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

com.google.gwt.dev.js.rhino.Context Maven / Gradle / Ivy

There is a newer version: 2.10.0
Show newest version
/* -*- 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.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/NPL/
 *
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * The Original Code is Rhino code, released
 * May 6, 1999.
 *
 * The Initial Developer of the Original Code is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1997-2000 Netscape Communications Corporation. All
 * Rights Reserved.
 *
 * Contributor(s):
 *
 * Patrick Beard
 * Norris Boyd
 * Igor Bukanov
 * Brendan Eich
 * Roger Lawrence
 * Mike McCabe
 * Ian D. Stewart
 * Andi Vajda
 * Andrew Wason
 * Kemal Bayram
 *
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU Public License (the "GPL"), in which case the
 * provisions of the GPL are applicable instead of those above.
 * If you wish to allow use of your version of this file only
 * under the terms of the GPL and not to allow others to use your
 * version of this file under the NPL, indicate your decision by
 * deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL.  If you do not delete
 * the provisions above, a recipient may use your version of this
 * file under either the NPL or the GPL.
 */
// Modified by Google

// API class

package com.google.gwt.dev.js.rhino;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.Hashtable;
import java.util.Locale;
import java.util.ResourceBundle;

/**
 * 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 enter() method.

* * The behavior of the execution engine may be altered through methods * such as setErrorReporter.

* * 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 { public static final String languageVersionProperty = "language version"; public static final String errorReporterProperty = "error reporter"; /** * 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); } /** * 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. *

* Calling enter() 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 enter() * must have a matching call to exit(). For example, *

     *      Context cx = Context.enter();
     *      try {
     *          ...
     *          cx.evaluateString(...);
     *      }
     *      finally { Context.exit(); }
     * 
* @return a Context associated with the current thread * @see org.mozilla.javascript.Context#getCurrentContext * @see org.mozilla.javascript.Context#exit */ public static Context enter() { return enter(null); } /** * Get a Context associated with the current thread, using * the given Context if need be. *

* The same as enter() except that cx * is associated with the current thread and returned if * the current thread has no associated context and cx * is not associated with any other thread. * @param cx a Context to associate with the thread if possible * @return a Context associated with the current thread */ public static Context enter(Context cx) { Context old = getCurrentContext(); if (cx == null) { if (old != null) { cx = old; } else { cx = new Context(); setThreadContext(cx); } } else { if (cx.enterCount != 0) { // The supplied context must be the context for // the current thread if it is already entered if (cx != old) { throw new RuntimeException ("Cannot enter Context active on another thread"); } } else { if (old != null) { cx = old; } else { setThreadContext(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 * enter() 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 org.mozilla.javascript.Context#enter */ public static void exit() { boolean released = false; Context cx = getCurrentContext(); if (cx == null) { throw new RuntimeException ("Calling Context.exit without previous Context.enter"); } if (Context.check && cx.enterCount < 1) Context.codeBug(); --cx.enterCount; if (cx.enterCount == 0) { released = true; setThreadContext(null); } } /** * 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 org.mozilla.javascript.Context#enter * @see org.mozilla.javascript.Context#exit */ public static Context getCurrentContext() { if (threadLocalCx != null) { try { return (Context)threadLocalGet.invoke(threadLocalCx, (Object[]) null); } catch (Exception ex) { } } Thread t = Thread.currentThread(); return (Context) threadContexts.get(t); } private static void setThreadContext(Context cx) { if (threadLocalCx != null) { try { threadLocalSet.invoke(threadLocalCx, new Object[] { cx }); return; } catch (Exception ex) { } } Thread t = Thread.currentThread(); if (cx != null) { threadContexts.put(t, cx); } else { threadContexts.remove(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; /** * JavaScript 1.5 */ public static final int VERSION_1_5 = 150; /** * 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 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) { this.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 String getImplementationVersion() { return "Rhino 1.5 release 4.1 2003 04 21"; } /** * Get the current error reporter. * * @see org.mozilla.javascript.ErrorReporter */ public ErrorReporter getErrorReporter() { return errorReporter; } /** * Change the current error reporter. * * @return the previous error reporter * @see org.mozilla.javascript.ErrorReporter */ public ErrorReporter setErrorReporter(ErrorReporter reporter) { errorReporter = reporter; return reporter; } /** * 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; } /** * 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 newVale the new value */ void firePropertyChange(String property, Object oldValue, Object newValue) { Object[] array = listeners; if (array != null) { firePropertyChangeImpl(array, property, oldValue, newValue); } } private void firePropertyChangeImpl(Object[] array, String property, Object oldValue, Object newValue) { for (int i = array.length; i-- != 0;) { Object obj = array[i]; if (obj instanceof PropertyChangeListener) { PropertyChangeListener l = (PropertyChangeListener)obj; l.propertyChange(new PropertyChangeEvent( this, property, oldValue, newValue)); } } } /** * Report a warning using the error reporter for the current thread. * * @param message the warning message to report * @param sourceName a string describing the source, such as a filename * @param lineno the starting line number * @param lineSource the text of the line (may be null) * @param lineOffset the offset into lineSource where problem was detected * @see org.mozilla.javascript.ErrorReporter */ public static void reportWarning(String message, String sourceName, int lineno, String lineSource, int lineOffset) { Context cx = Context.getContext(); 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); } } static EvaluatorException reportRuntimeError0(String messageId) { return reportRuntimeError(getMessage0(messageId)); } static EvaluatorException reportRuntimeError1 (String messageId, Object arg1) { return reportRuntimeError(getMessage1(messageId, arg1)); } static EvaluatorException reportRuntimeError2 (String messageId, Object arg1, Object arg2) { return reportRuntimeError(getMessage2(messageId, arg1, arg2)); } static EvaluatorException reportRuntimeError3 (String messageId, Object arg1, Object arg2, Object arg3) { return reportRuntimeError(getMessage3(messageId, arg1, arg2, arg3)); } */ /** * 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); } */ /** * 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 retreived. Also, if private data is to be maintained * in this manner the key should be a java.lang.Object * whose reference is not divulged to untrusted code. * @param key the key used to lookup the value * @return a value previously stored using putThreadLocal. */ public final Object getThreadLocal(Object key) { if (hashtable == null) return null; return hashtable.get(key); } /** * Put a value that can later be retrieved using a given key. *

* @param key the key used to index the value * @param value the value to save */ public void putThreadLocal(Object key, Object value) { if (hashtable == null) hashtable = new Hashtable(); hashtable.put(key, value); } /** * Remove values from thread-local storage. * @param key the key for the entry to remove. * @since 1.5 release 2 */ public void removeThreadLocal(Object key) { if (hashtable == null) return; hashtable.remove(key); } /** * Return whether functions are compiled by this context using * dynamic scope. *

* If functions are compiled with dynamic scope, then they execute * in the scope of their caller, rather than in their parent scope. * This is useful for sharing functions across multiple scopes. * @since 1.5 Release 1 */ public final boolean hasCompileFunctionsWithDynamicScope() { return compileFunctionsWithDynamicScopeFlag; } /** * Set whether functions compiled by this context should use * dynamic scope. *

* @param flag if true, compile functions with dynamic scope * @since 1.5 Release 1 */ public void setCompileFunctionsWithDynamicScope(boolean flag) { compileFunctionsWithDynamicScopeFlag = flag; } /** * if hasFeature(FEATURE_NON_ECMA_GET_YEAR) returns true, * Date.prototype.getYear subtructs 1900 only if 1900 <= date < 2000 * in deviation with Ecma B.2.4 */ public static final int FEATURE_NON_ECMA_GET_YEAR = 1; /** * if hasFeature(FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME) returns true, * allow 'function (...) { ... }' to be syntax sugar for * ' = function(...) { ... }', when * is not simply identifier. * See Ecma-262, section 11.2 for definition of */ public static final int FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME = 2; /** * 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 */ public static final int FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER = 3; /** * if hasFeature(FEATURE_TO_STRING_AS_SOURCE) returns true, * calling toString on JS objects gives JS source with code to create an * object with all enumeratable fields of the original object instead of * printing "[object ]". * 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; /** * Controls certain aspects of script semantics. * Should be overwritten to alter default behavior. * @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 */ public boolean hasFeature(int featureIndex) { switch (featureIndex) { case FEATURE_NON_ECMA_GET_YEAR: /* * During the great date rewrite of 1.3, we tried to track the * evolving ECMA standard, which then had a definition of * getYear which always subtracted 1900. Which we * implemented, not realizing that it was incompatible with * the old behavior... now, rather than thrash the behavior * yet again, we've decided to leave it with the - 1900 * behavior and point people to the getFullYear method. But * we try to protect existing scripts that have specified a * version... */ return (version == Context.VERSION_1_0 || version == Context.VERSION_1_1 || version == Context.VERSION_1_2); case FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME: return false; case FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER: return false; case FEATURE_TO_STRING_AS_SOURCE: return version == VERSION_1_2; } // It is a bug to call the method with unknown featureIndex throw new IllegalArgumentException(); } /********** end of API **********/ static String getMessage0(String messageId) { return getMessage(messageId, null); } static String getMessage1(String messageId, Object arg1) { Object[] arguments = {arg1}; return getMessage(messageId, arguments); } static String getMessage2(String messageId, Object arg1, Object arg2) { Object[] arguments = {arg1, arg2}; return getMessage(messageId, arguments); } static String getMessage3 (String messageId, Object arg1, Object arg2, Object arg3) { Object[] arguments = {arg1, arg2, arg3}; return getMessage(messageId, arguments); } /** * 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; } /* 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. */ // bruce: removed referenced to the initial "java" package name // that used to be there due to a build artifact static final String defaultResource = "com.google.gwt.dev.js.rhino.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 MessageFormat formatter = new MessageFormat(formatString); return formatter.format(arguments); } // debug flags static final boolean printTrees = true; static final boolean printICode = true; final boolean isVersionECMA1() { return version == VERSION_DEFAULT || version >= VERSION_1_3; } // Rudimentary support for Design-by-Contract static void codeBug() { throw new RuntimeException("FAILED ASSERTION"); } static final boolean check = true; private static Hashtable threadContexts = new Hashtable(11); private static Object threadLocalCx; private static Method threadLocalGet; private static Method threadLocalSet; int version; int errorCount; private ErrorReporter errorReporter; private Locale locale; private boolean generatingDebug; private boolean generatingDebugChanged; private boolean generatingSource=true; private boolean compileFunctionsWithDynamicScopeFlag; private int enterCount; private Object[] listeners; private Hashtable hashtable; private ClassLoader applicationClassLoader; /** * This is the list of names of objects forcing the creation of * function activation records. */ private Hashtable activationNames; // For instruction counting (interpreter only) int instructionCount; int instructionThreshold; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy