norris%netscape.com 1967c50fd1 If the security resource isn't present, catch the exception and proceed.
git-svn-id: svn://10.0.0.236/trunk@28920 18797224-902f-48f8-a5cc-f745e15eee43
1999-04-23 19:54:28 +00:00

1689 lines
58 KiB
Java

/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
// API class
package org.mozilla.javascript;
import java.io.*;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import java.util.Locale;
import java.util.ResourceBundle;
import java.text.Format;
import java.text.MessageFormat;
import java.lang.reflect.*;
/**
* 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 <a href="#enter()">enter()</a> method.<p>
*
* The behavior of the execution engine may be altered through methods
* such as <a href="#setLanguageVersion>setLanguageVersion</a> and
* <a href="#setErrorReporter>setErrorReporter</a>.<p>
*
* 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.<p>
*
* 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 final class Context {
/**
* Create a new Context.
*
* Note that the Context must be associated with a thread before
* it can be used to execute a script.
*
* @see org.mozilla.javascript.Context#enter
*/
public Context() {
setLanguageVersion(VERSION_DEFAULT);
this.generatingDebug = true;
optimizationLevel = codegenClass != null ? 0 : -1;
}
/**
* Create a new context with the associated security support.
*
* @param securitySupport an encapsulation of the functionality
* needed to support security for scripts.
* @see org.mozilla.javascript.SecuritySupport
*/
public Context(SecuritySupport securitySupport) {
this();
this.securitySupport = securitySupport;
}
/**
* Get a context associated with the current thread, creating
* one if need be.
*
* The Context stores the execution state of the JavaScript
* engine, so it is required that the context be entered
* before execution may begin. Once a thread has entered
* a Context, then getCurrentContext() may be called to find
* the context that is associated with the current thread.
* <p>
* Calling <code>enter()</code> will
* return either the Context currently associated with the
* thread, or will create a new context and associate it
* with the current thread. Each call to <code>enter()</code>
* must have a matching call to <code>exit()</code>. For example,
* <pre>
* Context cx = Context.enter();
* ...
* cx.evaluateString(...);
* cx.exit();
* </pre>
* @return a Context associated with the current thread
* @see org.mozilla.javascript.Context#getCurrentContext
* @see org.mozilla.javascript.Context#exit
*/
public static Context enter() {
Thread t = Thread.currentThread();
Context cx = (Context) threadContexts.get(t);
if (cx == null) {
cx = new Context();
cx.currentThread = t;
threadContexts.put(t, cx);
}
synchronized (cx) {
cx.enterCount++;
}
return cx;
}
/**
* Exit a block of code requiring a Context.
*
* Calling <code>exit()</code> will disassociate the Context with the
* current thread if the matching call to <code>enter()</code>
* had created a new Context.
* 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 org.mozilla.javascript.Context#enter
*/
public synchronized void exit() {
if (--enterCount == 0) {
threadContexts.remove(currentThread);
currentThread = null;
}
}
/**
* Get the current Context.
*
* The current Context is per-thread; this method looks up
* the Context associated with the current thread. <p>
*
* @return the Context associated with the current thread, or
* null if no context is associated with the current
* thread.
* @see org.mozilla.javascript.Context#enter
* @see org.mozilla.javascript.Context#exit
*/
public static Context getCurrentContext() {
Thread t = Thread.currentThread();
return (Context) threadContexts.get(t);
}
/**
* 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;
/**
* Get the current language version.
* <p>
* 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 int getLanguageVersion() {
return version;
}
/**
* Set the language version.
*
* <p>
* 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) {
this.version = version;
}
/**
* Get the implementation version.
*
* <p>
* The implementation version is of the form
* <pre>
* "<i>name langVer</i> <code>release</code> <i>relNum date</i>"
* </pre>
* where <i>name</i> is the name of the product, <i>langVer</i> is
* the language version, <i>relNum</i> is the release number, and
* <i>date</i> 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 String getImplementationVersion() {
return "JavaScript-Java 1.4 release 2 1998 12 18";
}
/**
* Get the current error reporter.
*
* @see org.mozilla.javascript.ErrorReporter
*/
public ErrorReporter getErrorReporter() {
if (null != debug_errorReporterHook)
return debug_errorReporterHook;
if (errorReporter == null)
errorReporter = new DefaultErrorReporter();
return errorReporter;
}
/**
* Change the current error reporter.
*
* @return the previous error reporter
* @see org.mozilla.javascript.ErrorReporter
*/
public ErrorReporter setErrorReporter(ErrorReporter reporter) {
if (null != debug_errorReporterHook)
return debug_errorReporterHook.setErrorReporter(reporter);
ErrorReporter result = errorReporter;
errorReporter = reporter;
return result;
}
/**
* Get the current locale. Returns the default locale if none has
* been set.
*
* @see java.util.Locale
*/
public Locale getLocale() {
if (locale == null)
locale = Locale.getDefault();
return locale;
}
/**
* Set the current locale.
*
* @see java.util.Locale
*/
public Locale setLocale(Locale loc) {
Locale result = locale;
locale = loc;
return result;
}
/**
* 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();
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);
}
/**
* 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.errorCount++;
cx.getErrorReporter().error(message, sourceName, lineno,
lineSource, lineOffset);
} else {
throw new EvaluatorException(message);
}
}
/**
* 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) {
cx.errorCount++;
return cx.getErrorReporter().
runtimeError(message, sourceName, lineno,
lineSource, lineOffset);
} else {
throw new EvaluatorException(message);
}
}
/**
* 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.<p>
*
* This method must be called to initialize a scope before scripts
* can be evaluated in that scope.
*
* @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
*/
public 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.<p>
*
* This method must be called to initialize a scope before scripts
* can be evaluated in that scope.<p>
*
* This form of the method also allows for creating "sealed" standard
* objects. An object that is sealed cannot have properties added 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
* @since 1.4R3
*/
public ScriptableObject initStandardObjects(ScriptableObject scope,
boolean sealed)
{
try {
if (scope == null)
scope = new NativeObject();
ScriptableObject.defineClass(scope, NativeFunction.class, sealed);
ScriptableObject.defineClass(scope, NativeObject.class, sealed);
Scriptable objectProto = ScriptableObject.
getObjectPrototype(scope);
// Function.prototype.__proto__ should be Object.prototype
Scriptable functionProto = ScriptableObject.
getFunctionPrototype(scope);
functionProto.setPrototype(objectProto);
// Set the prototype of the object passed in if need be
if (scope.getPrototype() == null)
scope.setPrototype(objectProto);
String[] classes = { "NativeGlobal", "NativeArray",
"NativeString", "NativeBoolean",
"NativeNumber", "NativeDate",
"NativeMath", "NativeCall",
"NativeClosure", "NativeWith",
"regexp.NativeRegExp", "NativeScript"
};
for (int i=0; i < classes.length; i++) {
try {
Class c = Class.forName("org.mozilla.javascript." +
classes[i]);
ScriptableObject.defineClass(scope, c, sealed);
} catch (ClassNotFoundException e) {
continue;
}
}
// This creates the Packages and java package roots.
NativeJavaPackage.init(scope);
// Define JavaAdapter class if possible.
getCompiler().defineJavaAdapter(scope);
}
// All of these exceptions should not occur since we are initializing
// from known classes
catch (IllegalAccessException e) {
throw WrappedException.wrapException(e);
}
catch (InstantiationException e) {
throw WrappedException.wrapException(e);
}
catch (InvocationTargetException e) {
throw WrappedException.wrapException(e);
}
catch (ClassDefinitionException e) {
throw WrappedException.wrapException(e);
}
catch (PropertyException e) {
throw WrappedException.wrapException(e);
}
return scope;
}
/**
* 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
* @exception JavaScriptException if an uncaught JavaScript exception
* occurred while evaluating the source string
* @see org.mozilla.javascript.SecuritySupport
*/
public Object evaluateString(Scriptable scope, String source,
String sourceName, int lineno,
Object securityDomain)
throws JavaScriptException
{
try {
Reader in = new StringReader(source);
return evaluateReader(scope, in, sourceName, lineno,
securityDomain);
}
catch (IOException ioe) {
// Should never occur because we just made the reader from a String
throw new RuntimeException();
}
}
/**
* 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
* @exception JavaScriptException if an uncaught JavaScript exception
* occurred while evaluating the Reader
*/
public Object evaluateReader(Scriptable scope, Reader in,
String sourceName, int lineno,
Object securityDomain)
throws IOException, JavaScriptException
{
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.
* <p>
* 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.
* <p>
* 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
*/
synchronized public boolean stringIsCompilableUnit(String source)
{
Reader in = new StringReader(source);
// no source name or source text manager, because we're just
// going to throw away the result.
TokenStream ts = new TokenStream(in, null, 1);
// Temporarily set error reporter to always be the exception-throwing
// DefaultErrorReporter. (This is why the method is synchronized...)
DeepErrorReporterHook hook = setErrorReporterHook(null);
ErrorReporter currentReporter =
setErrorReporter(new DefaultErrorReporter());
boolean errorseen = false;
try {
IRFactory irf = new IRFactory(ts);
Parser p = new Parser(irf);
p.parse(ts);
} catch (IOException ioe) {
errorseen = true;
} catch (EvaluatorException ee) {
errorseen = true;
} finally {
// Restore the old error reporter.
setErrorReporter(currentReporter);
setErrorReporterHook(hook);
}
// 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 && ts.eof())
return false;
else
return true;
}
/**
* Compiles the source in the given reader.
* <p>
* Returns a script that may later be executed.
* Will consume all the source in the reader.
*
* @param scope if nonnull, will be the scope in which the script object
* is created. The script object will be a valid JavaScript object
* as if it were created using the JavaScript1.3 Script constructor
* @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
* @see org.mozilla.javascript.Script#exec
* @exception IOException if an IOException was generated by the Reader
*/
public Script compileReader(Scriptable scope, Reader in, String sourceName,
int lineno, Object securityDomain)
throws IOException
{
return (Script) compile(scope, in, sourceName, lineno, securityDomain,
false);
}
/**
* Compile a JavaScript function.
* <p>
* The function source must be a function definition as defined by
* ECMA (e.g., "function f(a) { return a; }"). As an extension to the
* ECMA grammar, the function name is optional.
*
* @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 Function compileFunction(Scriptable scope, String source,
String sourceName, int lineno,
Object securityDomain)
{
Reader in = new StringReader(source);
try {
return (Function) compile(scope, in, sourceName, lineno,
securityDomain, true);
}
catch (IOException ioe) {
// Should never happen because we just made the reader
// from a String
throw new RuntimeException();
}
}
/**
* Decompile the script.
* <p>
* The canonical source of the script is returned.
*
* @param script the script to decompile
* @param scope the scope under which to decompile
* @param indent the number of spaces to indent the result
* @return a string representing the script source
*/
public String decompileScript(Script script, Scriptable scope,
int indent)
{
NativeScript ns = (NativeScript) script;
ns.initScript(scope);
return ns.decompile(indent, true, false);
}
/**
* Decompile a JavaScript Function.
* <p>
* Decompiles a previously compiled JavaScript function object to
* canonical source.
* <p>
* 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 String decompileFunction(Function fun, int indent) {
if (fun instanceof NativeFunction)
return ((NativeFunction)fun).decompile(indent, true, false);
else
return "function " + fun.getClassName() +
"() {\n\t[native code]\n}\n";
}
/**
* Decompile the body of a JavaScript Function.
* <p>
* 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 String decompileFunctionBody(Function fun, int indent) {
if (fun instanceof NativeFunction)
return ((NativeFunction)fun).decompile(indent, true, true);
else
// not sure what the right response here is. JSRef currently
// dumps core.
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
* @exception PropertyException if "Object" cannot be found in
* the scope
* @exception NotAFunctionException if the "Object" found in the scope
* is not a function
* @exception JavaScriptException if an uncaught JavaScript exception
* occurred while creating the object
*/
public Scriptable newObject(Scriptable scope)
throws PropertyException,
NotAFunctionException,
JavaScriptException
{
return newObject(scope, "Object", null);
}
/**
* Create a new JavaScript object by executing the named constructor.
*
* The call <code>newObject("Foo")</code> 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
* @exception PropertyException if a property with the constructor
* name cannot be found in the scope
* @exception NotAFunctionException if the property found in the scope
* is not a function
* @exception JavaScriptException if an uncaught JavaScript exception
* occurred while creating the object
*/
public Scriptable newObject(Scriptable scope, String constructorName)
throws PropertyException,
NotAFunctionException,
JavaScriptException
{
return newObject(scope, constructorName, null);
}
/**
* Creates a new JavaScript object by executing the named constructor.
*
* Searches <code>scope</code> for the named constructor, calls it with
* the given arguments, and returns the result.<p>
*
* The code
* <pre>
* Object[] args = { "a", "b" };
* newObject(scope, "Foo", args)</pre>
* is equivalent to evaluating "new Foo('a', 'b')", assuming that the Foo
* constructor has been defined in <code>scope</code>.
*
* @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
* @exception PropertyException if a property with the constructor
* name cannot be found in the scope
* @exception NotAFunctionException if the property found in the scope
* is not a function
* @exception JavaScriptException if an uncaught JavaScript exception
* occurs while creating the object
*/
public Scriptable newObject(Scriptable scope, String constructorName,
Object[] args)
throws PropertyException,
NotAFunctionException,
JavaScriptException
{
Object ctorVal = ScriptRuntime.getTopLevelProp(scope, constructorName);
if (ctorVal == Scriptable.NOT_FOUND) {
Object[] errArgs = { constructorName };
String message = getMessage("msg.ctor.not.found", errArgs);
throw new PropertyException(message);
}
if (!(ctorVal instanceof Function)) {
Object[] errArgs = { constructorName };
String message = getMessage("msg.not.ctor", errArgs);
throw new NotAFunctionException(message);
}
Function ctor = (Function) ctorVal;
return ctor.construct(this, ctor.getParentScope(),
(args == null) ? ScriptRuntime.emptyArgs : args);
}
/**
* Create an array with a specified initial length.
* <p>
* @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 Scriptable newArray(Scriptable scope, int length) {
Scriptable result = new NativeArray(length);
newArrayHelper(scope, result);
return result;
}
/**
* Create an array with a set of initial elements.
* <p>
* @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.
* @return the new array object
*/
public Scriptable newArray(Scriptable scope, Object[] elements) {
Scriptable result = new NativeArray(elements);
newArrayHelper(scope, result);
return result;
}
/**
* Get the elements of a JavaScript array.
* <p>
* If the object defines a length property, a Java array with that
* length is created and 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, null is returned.
* @param object the JavaScript array or array-like object
* @return a Java array of objects
* @since 1.4 release 2
*/
public Object[] getElements(Scriptable object) {
double doubleLen = NativeArray.getLengthProperty(object);
if (doubleLen != doubleLen)
return null;
int len = (int) doubleLen;
Object[] result = new Object[len];
for (int i=0; i < len; i++) {
Object elem = object.get(i, object);
result[i] = elem == Scriptable.NOT_FOUND ? Undefined.instance
: elem;
}
return result;
}
/**
* Convert the value to a JavaScript boolean value.
* <p>
* 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.
* <p>
* Returns a Java double for the JavaScript Number.
* <p>
* 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.
* <p>
* See ECMA 9.8.
* <p>
* @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.
* <p>
* Note that a scope must be provided to look up the constructors
* for Number, Boolean, and String.
* <p>
* See ECMA 9.9.
* <p>
* 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);
}
/**
* Tell whether debug information is being generated.
* @since 1.3
*/
public boolean isGeneratingDebug() {
return generatingDebug;
}
/**
* Specify whether or not debug information should be generated.
* <p>
* Setting the generation of debug information on will set the
* optimization level to zero.
* @since 1.3
*/
public void setGeneratingDebug(boolean generatingDebug) {
if (generatingDebug)
setOptimizationLevel(0);
this.generatingDebug = generatingDebug;
}
/**
* Tell whether source information is being generated.
* @since 1.3
*/
public boolean isGeneratingSource() {
return generatingSource;
}
/**
* Specify whether or not source information should be generated.
* <p>
* 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 void setGeneratingSource(boolean generatingSource) {
this.generatingSource = generatingSource;
}
/**
* Get the current optimization level.
* <p>
* The optimization level is expressed as an integer between -1 and
* 9.
* @since 1.3
*
*/
public int getOptimizationLevel() {
return optimizationLevel;
}
/**
* Set the current optimization level.
* <p>
* 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 void setOptimizationLevel(int optimizationLevel) {
if (optimizationLevel < 0) {
optimizationLevel = -1;
} else if (optimizationLevel > 9) {
optimizationLevel = 9;
}
if (codegenClass == null)
optimizationLevel = -1;
this.optimizationLevel = optimizationLevel;
}
/**
* Get the current target class file name.
* <p>
* If nonnull, requests to compile source will result in one or
* more class files being generated.
* @since 1.3
*/
public String getTargetClassFileName() {
return nameHelper == null
? null
: nameHelper.getTargetClassFileName();
}
/**
* Set the current target class file name.
* <p>
* If nonnull, requests to compile source will result in one or
* more class files being generated. If null, classes will only
* be generated in memory.
*
* @since 1.3
*/
public void setTargetClassFileName(String classFileName) {
if (nameHelper != null)
nameHelper.setTargetClassFileName(classFileName);
}
/**
* Get the current package to generate classes into.
*
* @since 1.3
*/
public String getTargetPackage() {
return (nameHelper == null) ? null : nameHelper.getTargetPackage();
}
/**
* Set the package to generate classes into.
*
* @since 1.3
*/
public void setTargetPackage(String targetPackage) {
if (nameHelper != null)
nameHelper.setTargetPackage(targetPackage);
}
/**
* Return true if a security domain is required on calls to
* compile and evaluate scripts.
*
* @since 1.4 Release 2
*/
public static boolean isSecurityDomainRequired() {
return requireSecurityDomain;
}
/**
* Returns the security context associated with the innermost
* script or function being executed by the interpreter.
* @since 1.4 release 2
*/
public Object getInterpreterSecurityDomain() {
return interpreterSecurityDomain;
}
/**
* Returns true if the class parameter is a class in the
* interpreter. Typically used by embeddings that get a class
* context to check security. These embeddings must know
* whether to get the security context associated with the
* interpreter or not.
*
* @param cl a class to test whether or not it is an interpreter
* class
* @return true if cl is an interpreter class
* @since 1.4 release 2
*/
public boolean isInterpreterClass(Class cl) {
return cl == Interpreter.class;
}
/**** debugger oriented portion of API ****/
/**
* Get the current source text hook (for debugging).
*
* @return the current hook
* @see org.mozilla.javascript.SourceTextManager
* @see org.mozilla.javascript.Context#setSourceTextManager
*/
public SourceTextManager getSourceTextManager() {
return debug_stm;
}
/**
* Set the current source text hook (for debugging).
* <p>
* When using the org.mozilla.javascript.debug system to debug within the
* context of a particular embedding if the Context has this hook set
* then all parsed JavaScript source will be passed to the hook. In
* some embeddings of JavaScript it may be better to not use this
* low level hook and instead have the embedding itself feed the
* source text to the SourceTextManager.
*
* @param debug_stm new hook
* @return the previous hook
* @see org.mozilla.javascript.SourceTextManager
*/
public SourceTextManager setSourceTextManager(SourceTextManager debug_stm) {
SourceTextManager result = this.debug_stm;
this.debug_stm = debug_stm;
return result;
}
/**
* Get the current script hook (for debugging).
*
* @return the current hook
* @see org.mozilla.javascript.DeepScriptHook
* @see org.mozilla.javascript.Context#setScriptHook
*/
public DeepScriptHook getScriptHook() {
return debug_scriptHook;
}
/**
* Set the current script hook (for debugging).
* <p>
* At debugLevel >= 3 the script hook is called when
* compiled scripts (and functions) are loaded and unloaded.
*
* @param hook new hook
* @return the previous hook
* @see org.mozilla.javascript.DeepScriptHook
*/
public DeepScriptHook setScriptHook(DeepScriptHook hook) {
DeepScriptHook result = debug_scriptHook;
debug_scriptHook = hook;
return result;
}
/**
* Get the current call hook (for debugging).
*
* @return the current hook
* @see org.mozilla.javascript.DeepCallHook
* @see org.mozilla.javascript.Context#setCallHook
*/
public DeepCallHook getCallHook() {
return debug_callHook;
}
/**
* Set the current call hook (for debugging).
* <p>
* At debugLevel >= 3 the call hook is called when
* compiled scripts and functions make function calls.
*
* @param hook new hook
* @return the previous hook
* @see org.mozilla.javascript.DeepCallHook
*/
public DeepCallHook setCallHook(DeepCallHook hook) {
DeepCallHook result = debug_callHook;
debug_callHook = hook;
return result;
}
/**
* Get the current execute hook (for debugging).
*
* @return the current hook
* @see org.mozilla.javascript.DeepExecuteHook
* @see org.mozilla.javascript.Context#setExecuteHook
*/
public DeepExecuteHook getExecuteHook() {
return debug_executeHook;
}
/**
* Set the current execute hook (for debugging).
* <p>
* At debugLevel >= 3 the execute hook is called when
* top level compiled scripts (non-functions) are executed.
*
* @param hook new hook
* @return the previous hook
* @see org.mozilla.javascript.DeepExecuteHook
*/
public DeepExecuteHook setExecuteHook(DeepExecuteHook hook) {
DeepExecuteHook result = debug_executeHook;
debug_executeHook = hook;
return result;
}
/**
* Get the current new object hook (for debugging).
*
* @return the current hook
* @see org.mozilla.javascript.DeepNewObjectHook
* @see org.mozilla.javascript.Context#setNewObjectHook
*/
public DeepNewObjectHook getNewObjectHook() {
return debug_newObjectHook;
}
/**
* Set the current new object hook (for debugging).
* <p>
* At debugLevel >= 3 the new object hook is called when
* JavaScript objects are created by compiled scripts
* and functions; i.e. when constructor functions run.
*
* @param hook new hook
* @return the previous hook
* @see org.mozilla.javascript.DeepNewObjectHook
*/
public DeepNewObjectHook setNewObjectHook(DeepNewObjectHook hook) {
DeepNewObjectHook result = debug_newObjectHook;
debug_newObjectHook = hook;
return result;
}
/**
* Get the current byte code hook (for debugging).
*
* @return the current hook
* @see org.mozilla.javascript.DeepBytecodeHook
* @see org.mozilla.javascript.Context#setBytecodeHook
*/
public DeepBytecodeHook getBytecodeHook() {
return debug_bytecodeHook;
}
/**
* Set the current byte code hook (for debugging).
* <p>
* At debugLevel >= 6 generated scripts and functions
* support setting traps and interrupts on a per statement
* basis. If a trap or interrupt is encountered while
* running in this Context, then this hook is called to
* handle it.
*
* @param hook new hook
* @return the previous hook
* @see org.mozilla.javascript.DeepBytecodeHook
*/
public DeepBytecodeHook setBytecodeHook(DeepBytecodeHook hook) {
DeepBytecodeHook result = debug_bytecodeHook;
debug_bytecodeHook = hook;
return result;
}
/**
* Get the current error reporter hook (for debugging).
*
* @return the current hook
* @see org.mozilla.javascript.DeepErrorReporterHook
* @see org.mozilla.javascript.Context#setErrorReporter
*/
public DeepErrorReporterHook getErrorReporterHook() {
return debug_errorReporterHook;
}
/**
* Set the current error reporter hook (for debugging).
* <p>
* This hook allows a debugger to trap error reports before
* there are sent to the error reporter. This is not meant to
* be used in place of the normal error reporting system.
*
* @param hook new hook
* @return the previous hook
* @see org.mozilla.javascript.DeepErrorReporterHook
* @see org.mozilla.javascript.ErrorReporter
* @see org.mozilla.javascript.Context#setErrorReporter
*/
public DeepErrorReporterHook setErrorReporterHook(DeepErrorReporterHook hook) {
DeepErrorReporterHook result = debug_errorReporterHook;
debug_errorReporterHook = hook;
return result;
}
/**
* Get the current debug level (for debugging).
*
* @return the current debug level
* @see org.mozilla.javascript.Context#setDebugLevel
*/
public int getDebugLevel() {
return debugLevel;
}
/**
* Set the current debug level (for debugging).
* <p>
* Set the debug level. Note that a non-zero debug level will
* force the optimization level to 0.
* <p>
* Currently supported debug levels:
* <pre>
* debugLevel == 0 - all debug support off (except error reporter hooks)
* debugLevel >= 1 - name of source file stored in NativeFunction
* debugLevel >= 3 - load/unload hooks called
* - base/end lineno info stored in NativeFunction
* - call, new object, and execute hooks called
* debugLevel >= 6 - interrupts and traps supported
*
* </pre>
*
* @param debugLevel new debugLevel
* @return the previous debug level
*/
public int setDebugLevel(int debugLevel) {
int result = this.debugLevel;
if (debugLevel < 0)
debugLevel = 0;
else if (debugLevel > 9)
debugLevel = 9;
if(debugLevel > 0)
setOptimizationLevel(0);
this.debugLevel = (byte) debugLevel;
return result;
}
/********** end of API **********/
/**
* Internal method that reports an error for missing calls to
* enter().
*/
static Context getContext() {
Thread t = Thread.currentThread();
Context cx = (Context) threadContexts.get(t);
if (cx == null) {
throw new RuntimeException(
"No Context associated with current Thread");
}
return cx;
}
/* OPT there's a noticable delay for the first error! Maybe it'd
* make sense to use a ListResourceBundle instead of a properties
* file to avoid (synchronized) text parsing.
*/
static final String defaultResource =
"org.mozilla.javascript.resources.Messages";
static String getMessage(String messageId, Object[] arguments) {
Context cx = getCurrentContext();
Locale locale = cx != null ? cx.getLocale() : Locale.getDefault();
// ResourceBundle does cacheing.
ResourceBundle rb = ResourceBundle.getBundle(defaultResource, locale);
String formatString;
try {
formatString = rb.getString(messageId);
} catch (java.util.MissingResourceException mre) {
throw new RuntimeException
("no message resource found for message property "+ messageId);
}
/*
* It's OK to format the string, even if 'arguments' is null;
* we need to format it anyway, to make double ''s collapse to
* single 's.
*/
// TODO: MessageFormat is not available on pJava
Format formatter = new MessageFormat(formatString);
return formatter.format(arguments);
}
// debug flags
static final boolean printTrees = false;
/**
* Compile a script.
*
* Reads script source from the reader and compiles it, returning
* a class for either the script or the function depending on the
* value of <code>returnFunction</code>.
*
* @param scope the scope to compile relative to
* @param in the Reader to read source from
* @param sourceName the name of the origin of the source (usually
* a file or URL)
* @param lineno the line number of the start of the source
* @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.
* @param returnFunction if true, will expect the source to contain
* a function; return value is assumed to
* then be a org.mozilla.javascript.Function
* @return a class for the script or function
* @see org.mozilla.javascript.Context#compileReader
*/
private Object compile(Scriptable scope, Reader in, String sourceName,
int lineno, Object securityDomain,
boolean returnFunction)
throws IOException
{
TokenStream ts = new TokenStream(in, sourceName, lineno);
return compile(scope, ts, securityDomain, returnFunction);
}
private static Class codegenClass;
private static ClassNameHelper nameHelper;
static {
try {
codegenClass = Class.forName(
"com.netscape.javascript.optimizer.Codegen");
Class nameHelperClass = Class.forName(
"com.netscape.javascript.optimizer.OptClassNameHelper");
nameHelper = (ClassNameHelper)nameHelperClass.newInstance();
} catch (ClassNotFoundException x) {
// ...must be running lite, that's ok
codegenClass = null;
} catch (IllegalAccessException x) {
codegenClass = null;
} catch (InstantiationException x) {
codegenClass = null;
}
}
private Interpreter getCompiler() {
if (codegenClass == null) {
return new Interpreter();
} else {
try {
return (Interpreter) codegenClass.newInstance();
}
catch (SecurityException x) {
}
catch (IllegalArgumentException x) {
}
catch (InstantiationException x) {
}
catch (IllegalAccessException x) {
}
throw new RuntimeException("Malformed optimizer package");
}
}
private Object compile(Scriptable scope, TokenStream ts,
Object securityDomain, boolean returnFunction)
throws IOException
{
Interpreter compiler = optimizationLevel == -1
? new Interpreter()
: getCompiler();
errorCount = 0;
IRFactory irf = compiler.createIRFactory(ts, nameHelper);
Parser p = new Parser(irf);
Node tree = (Node) p.parse(ts);
if (tree == null)
return null;
tree = compiler.transform(tree, ts);
if (printTrees)
System.out.println(tree.toStringTree());
if (returnFunction) {
Node first = tree.getFirstChild();
if (first == null)
return null;
tree = (Node) first.getProp(Node.FUNCTION_PROP);
if (tree == null)
return null;
}
Object result = compiler.compile(this, scope, tree, securityDomain,
securitySupport, nameHelper);
return errorCount == 0 ? result : null;
}
/**
* A bit of a hack, but the only way to get filename and line
* number from an enclosing frame.
*/
static String getSourcePositionFromStack(int[] linep) {
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) {
String fileStr = s.substring(open + 1, colon);
if (fileStr.endsWith(".js")) {
String lineStr = s.substring(colon + 1, close);
try {
linep[0] = Integer.parseInt(lineStr);
return fileStr;
}
catch (NumberFormatException e) {
// fall through
}
}
open = close = colon = -1;
}
}
return null;
}
RegExpProxy getRegExpProxy() {
if (regExpProxy == null) {
try {
Class c = Class.forName(
"org.mozilla.javascript.regexp.RegExpImpl");
regExpProxy = (RegExpProxy) c.newInstance();
return regExpProxy;
} catch (ClassNotFoundException e) {
} catch (InstantiationException e) {
} catch (IllegalAccessException e) {
}
}
return regExpProxy;
}
private void newArrayHelper(Scriptable scope, Scriptable array) {
array.setParentScope(scope);
Object ctor = ScriptRuntime.getTopLevelProp(scope, "Array");
if (ctor != null && ctor instanceof Scriptable) {
Scriptable s = (Scriptable) ctor;
array.setPrototype((Scriptable) s.get("prototype", s));
}
}
final boolean isVersionECMA1() {
return version == VERSION_DEFAULT || version >= VERSION_1_3;
}
/**
* Get the security context from the given class.
* <p>
* When some form of security check needs to be done, the class context
* must retrieved from the security manager to determine what class is
* requesting some form of privileged access.
* @since 1.4 release 2
*/
Object getSecurityDomainFromClass(Class cl) {
if (cl == Interpreter.class)
return interpreterSecurityDomain;
return securitySupport.getSecurityDomain(cl);
}
SecuritySupport getSecuritySupport() {
return securitySupport;
}
Object getSecurityDomainForStackDepth(int depth) {
Object result = null;
if (securitySupport != null) {
Class[] classes = securitySupport.getClassContext();
int depth1 = depth + 1;
if (0 <= depth1 && depth1 < classes.length) {
result = getSecurityDomainFromClass(classes[depth1]);
}
}
if (result != null)
return result;
if (requireSecurityDomain)
throw new SecurityException("Required security context not found");
return null;
}
private static boolean requireSecurityDomain = true;
static {
try {
ResourceBundle rb = ResourceBundle.getBundle(
"org.mozilla.javascript.resources.Security");
String s = rb.getString("security.requireSecurityDomain");
requireSecurityDomain = s.equals("true");
} catch (java.util.MissingResourceException mre) {
// Assume stricter policy.
requireSecurityDomain = true;
}
}
static final boolean useJSObject = false;
/**
* The activation of the currently executing function or script.
*/
NativeCall currentActivation;
// for Objects, Arrays to tag themselves as being printed out,
// so they don't print themselves out recursively.
Hashtable iterating;
Object interpreterSecurityDomain;
int version;
int errorCount;
private SecuritySupport securitySupport;
private ErrorReporter errorReporter;
private Thread currentThread;
private static Hashtable threadContexts = new Hashtable(11);
private RegExpProxy regExpProxy;
private Locale locale;
private boolean generatingDebug;
private boolean generatingSource=true;
private int optimizationLevel;
private SourceTextManager debug_stm;
private DeepScriptHook debug_scriptHook;
private DeepCallHook debug_callHook;
private DeepExecuteHook debug_executeHook;
private DeepNewObjectHook debug_newObjectHook;
private DeepBytecodeHook debug_bytecodeHook;
private DeepErrorReporterHook debug_errorReporterHook;
private byte debugLevel;
private int enterCount;
}