All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.htmlunit.corejs.javascript.Context Maven / Gradle / Ivy

/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// API class

package org.htmlunit.corejs.javascript;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.Closeable;
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.ArrayDeque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.function.UnaryOperator;
import org.htmlunit.corejs.classfile.ClassFileWriter.ClassFileFormatException;
import org.htmlunit.corejs.javascript.ast.AstRoot;
import org.htmlunit.corejs.javascript.ast.ScriptNode;
import org.htmlunit.corejs.javascript.debug.DebuggableScript;
import org.htmlunit.corejs.javascript.debug.Debugger;
import org.htmlunit.corejs.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 implements Closeable { /** * Language versions. * *

All integral values are reserved for future version numbers. */ /** * The unknown version. * *

Be aware, this version will not support many of the newer language features and will not * change in the future. * *

Please use one of the other constants like VERSION_ES6 to get support for recent language * features. */ 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; /** JavaScript 1.8 */ public static final int VERSION_1_8 = 180; /** ECMAScript 6. */ public static final int VERSION_ES6 = 200; /** * 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. */ @Deprecated 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 create a 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 behavior 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; /** * Enables access to JavaScript features from ECMAscript 6 that are present in JavaScript * engines that do not yet support version 6, such as V8. This includes support for typed * arrays. Default is true. * * @since 1.7 Release 3 */ public static final int FEATURE_V8_EXTENSIONS = 14; /** * Defines how an undefined "this" parameter is handled in certain calls. Previously Rhino would * convert an undefined "this" to null, whereas recent specs call for it to be treated * differently. Default is to be set if language version <= 1.7. * * @since 1.7.7 */ public static final int FEATURE_OLD_UNDEF_NULL_THIS = 15; /** * If set, then the order of property key enumeration will be first numeric keys in numeric * order, followed by string keys in order of creation, and finally Symbol keys, as specified in * ES6. Default is true for language version >= "ES6" and false otherwise. * * @since 1.7.7.1 */ public static final int FEATURE_ENUMERATE_IDS_FIRST = 16; /** * If set, then all objects will have a thread-safe property map. (Note that this doesn't make * everything else that they do thread-safe -- that depends on the specific implementation. If * not set, users should not share Rhino objects between threads, unless the "sync" function is * used to wrap them with an explicit synchronizer. The default is false, which means that by * default, individual objects are not thread-safe. * * @since 1.7.8 */ public static final int FEATURE_THREAD_SAFE_OBJECTS = 17; /** * If set, then all integer numbers will be returned without decimal place. For instance assume * there is a function like this: function foo() {return 5;} 5 will be returned if * feature is set, 5.0 otherwise. */ public static final int FEATURE_INTEGER_WITHOUT_DECIMAL_PLACE = 18; /** * TypedArray buffer uses little/big endian depending on the platform. The default is big endian * for Rhino. * * @since 1.7 Release 11 */ public static final int FEATURE_LITTLE_ENDIAN = 19; /** * Configure the XMLProcessor to parse XML with security features or not. Security features * include not fetching remote entity references and disabling XIncludes * * @since 1.7 Release 12 */ public static final int FEATURE_ENABLE_XML_SECURE_PARSING = 20; /** * Special to HtmlUnit's Rhino fork. * *

Whether the "someFunc.arguments" is a read-only view of the function argument or the real * arguments. * *

By default {@link #hasFeature(int)} returns false. */ public static final int FEATURE_HTMLUNIT_FN_ARGUMENTS_IS_RO_VIEW = 101; /** * Special to HtmlUnit's Rhino fork. * *

Indicates that function is defined even before its declaration, inside a block. * *

By default {@link #hasFeature(int)} returns false. */ public static final int FEATURE_HTMLUNIT_FUNCTION_DECLARED_FORWARD_IN_BLOCK = 103; /** * Special to HtmlUnit's Rhino fork. * *

Indicates that {@code MemberBox.toString()} contains property name. * *

By default {@link #hasFeature(int)} returns true. */ public static final int FEATURE_HTMLUNIT_MEMBERBOX_NAME = 105; /** * Special to HtmlUnit's Rhino fork. * *

Indicates that {@code MemberBox.toString()} starts with new line. * *

By default {@link #hasFeature(int)} returns true. */ public static final int FEATURE_HTMLUNIT_MEMBERBOX_NEWLINE = 106; /** * Configure whether the entries in a Java Map can be accessed by properties. * *

Not enabled: * *

var map = new java.util.HashMap(); map.put('foo', 1); map.foo; // undefined * *

Enabled: * *

var map = new java.util.HashMap(); map.put('foo', 1); map.foo; // 1 * *

WARNING: This feature is similar to the one in Nashorn, but incomplete. * *

1. A entry has priority over method. * *

map.put("put", "abc"); map.put; // abc map.put("put", "efg"); // ERROR * *

2. The distinction between numeric keys and string keys is ambiguous. * *

map.put('1', 123); map['1']; // Not found. This means `map[1]`. * * @since 1.7 Release 14 */ public static final int FEATURE_ENABLE_JAVA_MAP_ACCESS = 21; /** * Internationalization API implementation (see https://tc39.github.io/ecma402) can be activated * using this feature. * * @since 1.7 Release 15 */ public static final int FEATURE_INTL_402 = 22; 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; /** * Creates a new Context. The context will be associated with the {@link * ContextFactory#getGlobal() global context factory}. * *

Note that the Context must be associated with a thread before it can be used to execute a * script. * * @deprecated this constructor is deprecated because it creates a dependency on a static * singleton context factory. Use {@link ContextFactory#enter()} or {@link * ContextFactory#call(ContextAction)} instead. If you subclass this class, consider using * {@link #Context(ContextFactory)} constructor instead in the subclasses' constructors. */ @Deprecated public Context() { this(ContextFactory.getGlobal()); } /** * Creates a new context. Provided as a preferred super constructor for subclasses in place of * the deprecated default public constructor. * * @param factory the context factory associated with this context (most likely, the one that * created the context). Can not be null. The context features are inherited from the * factory, and the context will also otherwise use its factory's services. * @throws IllegalArgumentException if factory parameter is null. */ protected Context(ContextFactory factory) { if (factory == null) { throw new IllegalArgumentException("factory == null"); } this.factory = factory; version = 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. * * @return a Context associated with the current thread * @see #getCurrentContext() * @see #exit() * @see #call(ContextAction) */ public static Context enter() { return enter(null, ContextFactory.getGlobal()); } /** * 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) */ @Deprecated 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); } } @Override public void close() { if (enterCount < 1) Kit.codeBug(); if (--enterCount == 0) { Object helper = VMBridge.instance.getThreadContextHelper(); VMBridge.instance.setContext(helper, null); factory.onContextReleased(this); } } /** * 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)}. */ @Deprecated public static T 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, cx -> callable.call(cx, scope, thisObj, args)); } /** The method implements {@link ContextFactory#call(ContextAction)} logic. */ static T call(ContextFactory factory, ContextAction action) { try (Context cx = enter(null, factory)) { return action.run(cx); } } /** * @deprecated * @see ContextFactory#addListener(org.htmlunit.corejs.javascript.ContextFactory.Listener) * @see ContextFactory#getGlobal() */ @Deprecated public static void addContextListener(ContextListener listener) { // Special workaround for the debugger String DBG = "org.htmlunit.corejs.javascript.tools.debugger.Main"; if (DBG.equals(listener.getClass().getName())) { Class cl = listener.getClass(); Class factoryClass = Kit.classOrNull("org.htmlunit.corejs.javascript.ContextFactory"); Class[] sig = {factoryClass}; Object[] args = {ContextFactory.getGlobal()}; try { Method m = cl.getMethod("attachTo", sig); m.invoke(listener, args); } catch (Exception ex) { throw new RuntimeException(ex); } return; } ContextFactory.getGlobal().addListener(listener); } /** * @deprecated * @see ContextFactory#removeListener(org.htmlunit.corejs.javascript.ContextFactory.Listener) * @see ContextFactory#getGlobal() */ @Deprecated 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, Integer.valueOf(this.version), Integer.valueOf(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: case VERSION_1_8: case VERSION_ES6: 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() { return ImplementationVersion.get(); } /** * Get the current error reporter. * * @see org.htmlunit.corejs.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.htmlunit.corejs.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. * * @return the old value of the locale * @see java.util.Locale */ public final Locale setLocale(Locale loc) { if (sealed) onSealedMutation(); Locale result = locale; locale = loc; return result; } /** * Get the current timezone. Returns the default timezone if none has been set. * * @return the old value of the timezone * @see java.util.TimeZone */ public final TimeZone getTimeZone() { if (timezone == null) timezone = TimeZone.getDefault(); return timezone; } /** * Set the current timezone. * * @see java.util.TimeZone */ public final TimeZone setTimeZone(TimeZone tz) { if (sealed) onSealedMutation(); TimeZone result = timezone; timezone = tz; 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.htmlunit.corejs.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.htmlunit.corejs.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.htmlunit.corejs.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.htmlunit.corejs.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.htmlunit.corejs.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); } throw new EvaluatorException(message, sourceName, lineno, lineSource, lineOffset); } static EvaluatorException reportRuntimeErrorById(String messageId, Object... args) { String msg = ScriptRuntime.getMessageById(messageId, args); return reportRuntimeError(msg); } /** @deprecated Use {@link #reportRuntimeErrorById(String messageId, Object... args)} instead */ @Deprecated static EvaluatorException reportRuntimeError0(String messageId) { String msg = ScriptRuntime.getMessageById(messageId); return reportRuntimeError(msg); } /** @deprecated Use {@link #reportRuntimeErrorById(String messageId, Object... args)} instead */ @Deprecated static EvaluatorException reportRuntimeError1(String messageId, Object arg1) { String msg = ScriptRuntime.getMessageById(messageId, arg1); return reportRuntimeError(msg); } /** @deprecated Use {@link #reportRuntimeErrorById(String messageId, Object... args)} instead */ @Deprecated static EvaluatorException reportRuntimeError2(String messageId, Object arg1, Object arg2) { String msg = ScriptRuntime.getMessageById(messageId, arg1, arg2); return reportRuntimeError(msg); } /** @deprecated Use {@link #reportRuntimeErrorById(String messageId, Object... args)} instead */ @Deprecated static EvaluatorException reportRuntimeError3( String messageId, Object arg1, Object arg2, Object arg3) { String msg = ScriptRuntime.getMessageById(messageId, arg1, arg2, arg3); return reportRuntimeError(msg); } /** @deprecated Use {@link #reportRuntimeErrorById(String messageId, Object... args)} instead */ @Deprecated static EvaluatorException reportRuntimeError4( String messageId, Object arg1, Object arg2, Object arg3, Object arg4) { String msg = ScriptRuntime.getMessageById(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.htmlunit.corejs.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, leaving out those that offer access directly to Java * classes. This sets up "scope" to have access to all the standard JavaScript classes, but does * not create global objects for any top-level Java packages. In addition, the "Packages," * "JavaAdapter," and "JavaImporter" classes, and the "getClass" function, are not initialized. * *

The result of this function is a scope that may be safely used in a "sandbox" environment * where it is not desirable to give access to Java code from JavaScript. * *

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 initSafeStandardObjects() { return initSafeStandardObjects(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, leaving out those that offer access directly to Java * classes. This sets up "scope" to have access to all the standard JavaScript classes, but does * not create global objects for any top-level Java packages. In addition, the "Packages," * "JavaAdapter," and "JavaImporter" classes, and the "getClass" function, are not initialized. * *

The result of this function is a scope that may be safely used in a "sandbox" environment * where it is not desirable to give access to Java code from JavaScript. * *

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 initSafeStandardObjects(ScriptableObject scope) { return initSafeStandardObjects(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); } /** * Initialize the standard objects, leaving out those that offer access directly to Java * classes. This sets up "scope" to have access to all the standard JavaScript classes, but does * not create global objects for any top-level Java packages. In addition, the "Packages," * "JavaAdapter," and "JavaImporter" classes, and the "getClass" function, are not initialized. * *

The result of this function is a scope that may be safely used in a "sandbox" environment * where it is not desirable to give access to Java code from JavaScript. * *

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.7.6 */ public ScriptableObject initSafeStandardObjects(ScriptableObject scope, boolean sealed) { return ScriptRuntime.initSafeStandardObjects(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.htmlunit.corejs.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); } 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(in, sourceName, lineno, securityDomain); if (script != null) { return script.exec(this, scope); } return null; } /** * Execute script that may pause execution by capturing a continuation. Caller must be prepared * to catch a ContinuationPending exception and resume execution by calling {@link * #resumeContinuation(Object, Scriptable, Object)}. * * @param script The script to execute. Script must have been compiled with interpreted mode * (optimization level -1) * @param scope The scope to execute the script against * @throws ContinuationPending if the script calls a function that results in a call to {@link * #captureContinuation()} * @since 1.7 Release 2 */ public Object executeScriptWithContinuations(Script script, Scriptable scope) throws ContinuationPending { if (!(script instanceof InterpretedFunction) || !((InterpretedFunction) script).isScript()) { // Can only be applied to scripts throw new IllegalArgumentException( "Script argument was not" + " a script or was not created by interpreted mode "); } return callFunctionWithContinuations( (InterpretedFunction) script, scope, ScriptRuntime.emptyArgs); } /** * Call function that may pause execution by capturing a continuation. Caller must be prepared * to catch a ContinuationPending exception and resume execution by calling {@link * #resumeContinuation(Object, Scriptable, Object)}. * * @param function The function to call. The function must have been compiled with interpreted * mode (optimization level -1) * @param scope The scope to execute the script against * @param args The arguments for the function * @throws ContinuationPending if the script calls a function that results in a call to {@link * #captureContinuation()} * @since 1.7 Release 2 */ public Object callFunctionWithContinuations(Callable function, Scriptable scope, Object[] args) throws ContinuationPending { if (!(function instanceof InterpretedFunction)) { // Can only be applied to scripts throw new IllegalArgumentException( "Function argument was not" + " created by interpreted mode "); } if (ScriptRuntime.hasTopCall(this)) { throw new IllegalStateException( "Cannot have any pending top " + "calls when executing a script with continuations"); } // Annotate so we can check later to ensure no java code in // intervening frames isContinuationsTopCall = true; return ScriptRuntime.doTopCall(function, this, scope, scope, args, isTopLevelStrict); } /** * Capture a continuation from the current execution. The execution must have been started via a * call to {@link #executeScriptWithContinuations(Script, Scriptable)} or {@link * #callFunctionWithContinuations(Callable, Scriptable, Object[])}. This implies that the code * calling this method must have been called as a function from the JavaScript script. Also, * there cannot be any non-JavaScript code between the JavaScript frames (e.g., a call to * eval()). The ContinuationPending exception returned must be thrown. * * @return A ContinuationPending exception that must be thrown * @since 1.7 Release 2 */ public ContinuationPending captureContinuation() { return new ContinuationPending(Interpreter.captureContinuation(this)); } /** * Restarts execution of the JavaScript suspended at the call to {@link #captureContinuation()}. * Execution of the code will resume with the functionResult as the result of the call that * captured the continuation. Execution of the script will either conclude normally and the * result returned, another continuation will be captured and thrown, or the script will * terminate abnormally and throw an exception. * * @param continuation The value returned by {@link ContinuationPending#getContinuation()} * @param functionResult This value will appear to the code being resumed as the result of the * function that captured the continuation * @throws ContinuationPending if another continuation is captured before the code terminates * @since 1.7 Release 2 */ public Object resumeContinuation(Object continuation, Scriptable scope, Object functionResult) throws ContinuationPending { Object[] args = {functionResult}; return Interpreter.restartContinuation( (org.htmlunit.corejs.javascript.NativeContinuation) continuation, this, scope, args); } /** * 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); Parser p = new Parser(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. return !(errorseen && p.eof()); } /** * @deprecated * @see #compileReader(Reader in, String sourceName, int lineno, Object securityDomain) */ @Deprecated 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.htmlunit.corejs.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, Kit.readReader(in), 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. Use 0 if the line number is * unknown. * @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.htmlunit.corejs.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); } protected Script compileString( String source, Evaluator compiler, ErrorReporter compilationErrorReporter, String sourceName, int lineno, Object securityDomain) { try { return (Script) compileImpl( null, source, sourceName, lineno, securityDomain, false, compiler, compilationErrorReporter); } catch (IOException ioe) { // Should not happen when dealing with source as string throw new RuntimeException(ioe); } } /** * 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.htmlunit.corejs.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); } protected Function compileFunction( Scriptable scope, String source, Evaluator compiler, ErrorReporter compilationErrorReporter, String sourceName, int lineno, Object securityDomain) { try { return (Function) compileImpl( scope, 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(ioe); } } /** * 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); 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 Scriptable newObject(Scriptable scope) { NativeObject result = new NativeObject(); ScriptRuntime.setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.Object); return result; } /** * 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 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 Scriptable newObject(Scriptable scope, String constructorName, Object[] args) { return ScriptRuntime.newObject(this, scope, constructorName, 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 Scriptable newArray(Scriptable scope, int length) { NativeArray result = new NativeArray(length); ScriptRuntime.setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.Array); 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 Scriptable newArray(Scriptable scope, Object[] elements) { if (elements.getClass().getComponentType() != ScriptRuntime.ObjectClass) throw new IllegalArgumentException(); NativeArray result = new NativeArray(elements); ScriptRuntime.setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.Array); 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) */ @Deprecated 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) { return javaToJS(value, scope, null); } /** * 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: * *

     *    return cx.getWrapFactory().wrap(cx, scope, value, null);
     * 
* * @param value any Java object * @param scope top scope object * @param cx context to use for wrapping LiveConnect objects * @return value suitable to pass to any API that takes JavaScript values. */ public static Object javaToJS(Object value, Scriptable scope, Context cx) { 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 { if (cx == null) { 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. */ @Deprecated public static Object toType(Object value, Class desiredType) throws IllegalArgumentException { try { return jsToJava(value, desiredType); } catch (EvaluatorException ex) { throw new IllegalArgumentException(ex.getMessage(), ex); } } /** * Returns the javaToJSONConverter for this Context. * *

The converter is used by the JSON.stringify method for Java objects other than instances * of {@link java.util.Map Map}, {@link java.util.Collection Collection}, or {@link * java.lang.Object Object[]}. * *

The default converter if unset will convert Java Objects to their toString() value. * * @return javaToJSONConverter for this Context */ public UnaryOperator getJavaToJSONConverter() { if (javaToJSONConverter == null) { return JavaToJSONConverters.STRING; } return javaToJSONConverter; } /** * Sets the javaToJSONConverter for this Context. * *

The converter is used by the JSON.stringify method for Java objects other than instances * of {@link java.util.Map Map}, {@link java.util.Collection Collection}, or {@link * java.lang.Object Object[]}. * *

Objects returned by the converter will converted with {@link #javaToJS(Object, * Scriptable)} and then stringified themselves. * * @param javaToJSONConverter * @throws IllegalArgumentException if javaToJSONConverter is null */ public void setJavaToJSONConverter(UnaryOperator javaToJSONConverter) throws IllegalArgumentException { if (javaToJSONConverter == null) { throw new IllegalArgumentException("javaToJSONConverter == null"); } this.javaToJSONConverter = javaToJSONConverter; } /** * 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 synchronized void setClassShutter(ClassShutter shutter) { if (sealed) onSealedMutation(); if (shutter == null) throw new IllegalArgumentException(); if (hasClassShutter) { throw new SecurityException("Cannot overwrite existing " + "ClassShutter object"); } classShutter = shutter; hasClassShutter = true; } final synchronized ClassShutter getClassShutter() { return classShutter; } public interface ClassShutterSetter { public void setClassShutter(ClassShutter shutter); public ClassShutter getClassShutter(); } public final synchronized ClassShutterSetter getClassShutterSetter() { if (hasClassShutter) return null; hasClassShutter = true; return new ClassShutterSetter() { @Override public void setClassShutter(ClassShutter shutter) { classShutter = shutter; } @Override public 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 (threadLocalMap == null) return null; return threadLocalMap.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 synchronized void putThreadLocal(Object key, Object value) { if (sealed) onSealedMutation(); if (threadLocalMap == null) threadLocalMap = new HashMap<>(); threadLocalMap.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 (threadLocalMap == null) return; threadLocalMap.remove(key); } /** * @deprecated * @see ClassCache#get(Scriptable) * @see ClassCache#setCachingEnabled(boolean) */ @Deprecated 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.
* Note that the meaning of "instruction" is not guaranteed to be consistent between compiled * and interpretive modes: executing a given script or function in the different modes will * result in different instruction counts against the threshold. {@link * #setGenerateObserverCount} is called with true if 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 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 = Thread.currentThread().getContextClassLoader(); 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; } /** * Add a task that will be executed at the end of the current operation. The various "evaluate" * functions will all call this before exiting to ensure that all microtasks run to completion. * Otherwise, callers should call "processMicrotasks" to run them all. This feature is primarily * used to implement Promises. The microtask queue is not thread-safe. */ public void enqueueMicrotask(Runnable task) { microtasks.add(task); } /** * Run all the microtasks for the current context to completion. This is called by the various * "evaluate" functions. Frameworks that call Function objects directly should call this * function to ensure that everything completes if they want all Promises to eventually resolve. * This function is idempotent, but the microtask queue is not thread-safe. */ public void processMicrotasks() { Runnable head; do { head = microtasks.poll(); if (head != null) { head.run(); } } while (head != null); } /** * Control whether to track unhandled promise rejections. If "track" is set to true, then the * tracker returned by "getUnhandledPromiseTracker" must be periodically used to process the * queue of unhandled promise rejections, or a memory leak may result. * * @param track if true, then track unhandled promise rejections */ public void setTrackUnhandledPromiseRejections(boolean track) { unhandledPromises.enable(track); } /** * Return the object used to track unhandled promise rejections. * * @return the tracker object */ public UnhandledRejectionTracker getUnhandledPromiseTracker() { return unhandledPromises; } /* ******** 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; } protected Object compileImpl( Scriptable scope, 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"); } // 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(); } ScriptNode tree = parse( sourceString, sourceName, lineno, compilerEnv, compilationErrorReporter, returnFunction); Object bytecode; try { if (compiler == null) { compiler = createCompiler(); } bytecode = compiler.compile(compilerEnv, tree, tree.getEncodedSource(), returnFunction); } catch (ClassFileFormatException e) { // we hit some class file limit, fall back to interpreter or report // we have to recreate the tree because the compile call might have changed the tree // already tree = parse( sourceString, sourceName, lineno, compilerEnv, compilationErrorReporter, returnFunction); compiler = createInterpreter(); bytecode = compiler.compile(compilerEnv, tree, tree.getEncodedSource(), 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 ScriptNode parse( String sourceString, String sourceName, int lineno, CompilerEnvirons compilerEnv, ErrorReporter compilationErrorReporter, boolean returnFunction) throws IOException { Parser p = new Parser(compilerEnv, compilationErrorReporter); if (returnFunction) { p.calledByCompileFunction = true; } if (isStrictMode()) { p.setDefaultUseStrictDirective(true); } AstRoot ast = p.parse(sourceString, sourceName, lineno); if (returnFunction) { // parser no longer adds function to script node if (!(ast.getFirstChild() != null && ast.getFirstChild().getType() == Token.FUNCTION)) { // XXX: the check just looks 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); } } IRFactory irf = new IRFactory(compilerEnv, compilationErrorReporter); //HtmlUnit ScriptNode tree = irf.transformTree(ast); ScriptNode tree = irf.transformTree(ast, sourceString); return tree; } 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.htmlunit.corejs.javascript.optimizer.Codegen"); private static Class interpreterClass = Kit.classOrNull("org.htmlunit.corejs.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. */ StackTraceElement[] stackTrace = new Throwable().getStackTrace(); for (StackTraceElement st : stackTrace) { String file = st.getFileName(); if (!(file == null || file.endsWith(".java"))) { int line = st.getLineNumber(); if (line >= 0) { linep[0] = line; return file; } } } return null; } RegExpProxy getRegExpProxy() { if (regExpProxy == null) { Class cl = Kit.classOrNull("org.htmlunit.corejs.javascript.regexp.RegExpImpl"); if (cl != null) { regExpProxy = (RegExpProxy) Kit.newInstanceOrNull(cl); } } return regExpProxy; } final boolean isVersionECMA1() { return /* HtmlUnit version == #VERSION_DEFAULT || version >= #VERSION_1_3 */ true; } // 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 HashSet<>(); activationNames.add(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.contains(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); } public final boolean isStrictMode() { return isTopLevelStrict || (currentActivationCall != null && currentActivationCall.isStrict); } public static boolean isCurrentContextStrict() { Context cx = getCurrentContext(); if (cx == null) { return false; } return cx.isStrictMode(); } private final ContextFactory factory; private boolean sealed; private Object sealKey; Scriptable topCallScope; boolean isContinuationsTopCall; NativeCall currentActivationCall; XMLLib cachedXMLLib; BaseFunction typeErrorThrower; // 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 boolean hasClassShutter; private ClassShutter classShutter; private ErrorReporter errorReporter; RegExpProxy regExpProxy; private Locale locale; private TimeZone timezone; private boolean generatingDebug; private boolean generatingDebugChanged; private boolean generatingSource = true; boolean useDynamicScope; private int optimizationLevel; private int maximumInterpreterStackDepth; private WrapFactory wrapFactory; Debugger debugger; private Object debuggerData; private int enterCount; private Object propertyListeners; private Map threadLocalMap; private ClassLoader applicationClassLoader; private UnaryOperator javaToJSONConverter; private final ArrayDeque microtasks = new ArrayDeque<>(); private final UnhandledRejectionTracker unhandledPromises = new UnhandledRejectionTracker(); /** This is the list of names of objects forcing the creation of function activation records. */ Set 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 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; boolean isTopLevelStrict; }