001    /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
002     *
003     * The contents of this file are subject to the Netscape Public
004     * License Version 1.1 (the "License"); you may not use this file
005     * except in compliance with the License. You may obtain a copy of
006     * the License at http://www.mozilla.org/NPL/
007     *
008     * Software distributed under the License is distributed on an "AS
009     * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
010     * implied. See the License for the specific language governing
011     * rights and limitations under the License.
012     *
013     * The Original Code is Rhino code, released
014     * May 6, 1999.
015     *
016     * The Initial Developer of the Original Code is Netscape
017     * Communications Corporation.  Portions created by Netscape are
018     * Copyright (C) 1997-2000 Netscape Communications Corporation. All
019     * Rights Reserved.
020     *
021     * Contributor(s):
022     *
023     * Patrick Beard
024     * Norris Boyd
025     * Igor Bukanov
026     * Brendan Eich
027     * Roger Lawrence
028     * Mike McCabe
029     * Ian D. Stewart
030     * Andi Vajda
031     * Andrew Wason
032     * Kemal Bayram
033     *
034     * Alternatively, the contents of this file may be used under the
035     * terms of the GNU Public License (the "GPL"), in which case the
036     * provisions of the GPL are applicable instead of those above.
037     * If you wish to allow use of your version of this file only
038     * under the terms of the GPL and not to allow others to use your
039     * version of this file under the NPL, indicate your decision by
040     * deleting the provisions above and replace them with the notice
041     * and other provisions required by the GPL.  If you do not delete
042     * the provisions above, a recipient may use your version of this
043     * file under either the NPL or the GPL.
044     */
045    // Modified by Google
046    
047    // API class
048    
049    package com.google.gwt.dev.js.rhino;
050    
051    import org.jetbrains.annotations.NotNull;
052    
053    import java.beans.PropertyChangeEvent;
054    import java.beans.PropertyChangeListener;
055    import java.lang.reflect.Method;
056    import java.text.MessageFormat;
057    import java.util.Hashtable;
058    import java.util.Locale;
059    import java.util.MissingResourceException;
060    
061    /**
062     * This class represents the runtime context of an executing script.
063     *
064     * Before executing a script, an instance of Context must be created
065     * and associated with the thread that will be executing the script.
066     * The Context will be used to store information about the executing
067     * of the script such as the call stack. Contexts are associated with
068     * the current thread  using the <a href="#enter()">enter()</a> method.<p>
069     *
070     * The behavior of the execution engine may be altered through methods
071     * such as <a href="#setLanguageVersion>setLanguageVersion</a> and
072     * <a href="#setErrorReporter>setErrorReporter</a>.<p>
073     *
074     * Different forms of script execution are supported. Scripts may be
075     * evaluated from the source directly, or first compiled and then later
076     * executed. Interactive execution is also supported.<p>
077     *
078     * Some aspects of script execution, such as type conversions and
079     * object creation, may be accessed directly through methods of
080     * Context.
081     */
082    public class Context {
083    
084        /**
085         * Create a new Context.
086         *
087         * Note that the Context must be associated with a thread before
088         * it can be used to execute a script.
089         */
090        public Context() {
091            setLanguageVersion(VERSION_DEFAULT);
092        }
093    
094        /**
095         * Get a context associated with the current thread, creating
096         * one if need be.
097         *
098         * The Context stores the execution state of the JavaScript
099         * engine, so it is required that the context be entered
100         * before execution may begin. Once a thread has entered
101         * a Context, then getCurrentContext() may be called to find
102         * the context that is associated with the current thread.
103         * <p>
104         * Calling <code>enter()</code> will
105         * return either the Context currently associated with the
106         * thread, or will create a new context and associate it
107         * with the current thread. Each call to <code>enter()</code>
108         * must have a matching call to <code>exit()</code>. For example,
109         * <pre>
110         *      Context cx = Context.enter();
111         *      try {
112         *          ...
113         *          cx.evaluateString(...);
114         *      }
115         *      finally { Context.exit(); }
116         * </pre>
117         * @return a Context associated with the current thread
118         */
119        public static Context enter() {
120            return enter(null);
121        }
122    
123        /**
124         * Get a Context associated with the current thread, using
125         * the given Context if need be.
126         * <p>
127         * The same as <code>enter()</code> except that <code>cx</code>
128         * is associated with the current thread and returned if
129         * the current thread has no associated context and <code>cx</code>
130         * is not associated with any other thread.
131         * @param cx a Context to associate with the thread if possible
132         * @return a Context associated with the current thread
133         */
134        public static Context enter(Context cx) {
135    
136            Context old = getCurrentContext();
137    
138            if (cx == null) {
139                if (old != null) {
140                    cx = old;
141                } else {
142                    cx = new Context();
143                    setThreadContext(cx);
144                }
145            } else {
146                if (cx.enterCount != 0) {
147                    // The suplied context must be the context for
148                    // the current thread if it is already entered
149                    if (cx != old) {
150                        throw new RuntimeException
151                            ("Cannot enter Context active on another thread");
152                    }
153                } else {
154                    if (old != null) {
155                        cx = old;
156                    } else {
157                        setThreadContext(cx);
158                    }
159                }
160            }
161    
162            ++cx.enterCount;
163    
164            return cx;
165         }
166    
167        /**
168         * Exit a block of code requiring a Context.
169         *
170         * Calling <code>exit()</code> will remove the association between
171         * the current thread and a Context if the prior call to
172         * <code>enter()</code> on this thread newly associated a Context
173         * with this thread.
174         * Once the current thread no longer has an associated Context,
175         * it cannot be used to execute JavaScript until it is again associated
176         * with a Context.
177         */
178        public static void exit() {
179            boolean released = false;
180            Context cx = getCurrentContext();
181            if (cx == null) {
182                throw new RuntimeException
183                    ("Calling Context.exit without previous Context.enter");
184            }
185            if (Context.check && cx.enterCount < 1) Context.codeBug();
186            --cx.enterCount;
187            if (cx.enterCount == 0) {
188                released = true;
189                setThreadContext(null);
190            }
191        }
192    
193        /**
194         * Get the current Context.
195         *
196         * The current Context is per-thread; this method looks up
197         * the Context associated with the current thread. <p>
198         *
199         * @return the Context associated with the current thread, or
200         *         null if no context is associated with the current
201         *         thread.
202         */
203        public static Context getCurrentContext() {
204            if (threadLocalCx != null) {
205                try {
206                    return (Context)threadLocalGet.invoke(threadLocalCx, (Object[]) null);
207                } catch (Exception ex) { }
208            }
209            Thread t = Thread.currentThread();
210            return (Context) threadContexts.get(t);
211        }
212    
213        private static void setThreadContext(Context cx) {
214            if (threadLocalCx != null) {
215                try {
216                    threadLocalSet.invoke(threadLocalCx, new Object[] { cx });
217                    return;
218                } catch (Exception ex) { }
219            }
220            Thread t = Thread.currentThread();
221            if (cx != null) {
222                threadContexts.put(t, cx);
223            } else {
224                threadContexts.remove(t);
225            }
226        }
227    
228        /**
229         * Language versions
230         *
231         * All integral values are reserved for future version numbers.
232         */
233    
234        /**
235         * The unknown version.
236         */
237        public static final int VERSION_UNKNOWN =   -1;
238    
239        /**
240         * The default version.
241         */
242        public static final int VERSION_DEFAULT =    0;
243    
244        /**
245         * JavaScript 1.0
246         */
247        public static final int VERSION_1_0 =      100;
248    
249        /**
250         * JavaScript 1.1
251         */
252        public static final int VERSION_1_1 =      110;
253    
254        /**
255         * JavaScript 1.2
256         */
257        public static final int VERSION_1_2 =      120;
258    
259        /**
260         * JavaScript 1.3
261         */
262        public static final int VERSION_1_3 =      130;
263    
264        /**
265         * JavaScript 1.4
266         */
267        public static final int VERSION_1_4 =      140;
268    
269        /**
270         * JavaScript 1.5
271         */
272        public static final int VERSION_1_5 =      150;
273    
274        /**
275         * Get the current language version.
276         * <p>
277         * The language version number affects JavaScript semantics as detailed
278         * in the overview documentation.
279         *
280         * @return an integer that is one of VERSION_1_0, VERSION_1_1, etc.
281         */
282        public int getLanguageVersion() {
283           return version;
284        }
285    
286        /**
287         * Set the language version.
288         *
289         * <p>
290         * Setting the language version will affect functions and scripts compiled
291         * subsequently. See the overview documentation for version-specific
292         * behavior.
293         *
294         * @param version the version as specified by VERSION_1_0, VERSION_1_1, etc.
295         */
296        public void setLanguageVersion(int version) {
297            this.version = version;
298        }
299    
300        /**
301         * Get the current error reporter.
302         */
303        public ErrorReporter getErrorReporter() {
304            return errorReporter;
305        }
306    
307        /**
308         * Change the current error reporter.
309         *
310         * @return the previous error reporter
311         */
312        public ErrorReporter setErrorReporter(ErrorReporter reporter) {
313            errorReporter = reporter;
314            return reporter;
315        }
316    
317        /**
318         * Get the current locale.  Returns the default locale if none has
319         * been set.
320         *
321         * @see java.util.Locale
322         */
323    
324        public Locale getLocale() {
325            if (locale == null)
326                locale = Locale.getDefault();
327            return locale;
328        }
329    
330        public static void reportWarning(@NotNull String message, @NotNull CodePosition startPosition, @NotNull CodePosition endPosition)
331        {
332            Context cx = Context.getContext();
333            cx.getErrorReporter().warning(message, startPosition, endPosition);
334        }
335    
336        public static void reportError(@NotNull String message, @NotNull CodePosition startPosition, @NotNull CodePosition endPosition)
337        {
338            Context cx = getCurrentContext();
339            if (cx != null) {
340                cx.errorCount++;
341                cx.getErrorReporter().error(message, startPosition, endPosition);
342            } else {
343                throw new EvaluatorException(message);
344            }
345        }
346    
347        /**
348         * if hasFeature(FEATURE_NON_ECMA_GET_YEAR) returns true,
349         * Date.prototype.getYear subtructs 1900 only if 1900 <= date < 2000
350         * in deviation with Ecma B.2.4
351         */
352        public static final int FEATURE_NON_ECMA_GET_YEAR = 1;
353    
354        /**
355         * if hasFeature(FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME) returns true,
356         * allow 'function <MemberExpression>(...) { ... }' to be syntax sugar for
357         * '<MemberExpression> = function(...) { ... }', when <MemberExpression>
358         * is not simply identifier.
359         * See Ecma-262, section 11.2 for definition of <MemberExpression>
360         */
361        public static final int FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME = 2;
362    
363        /**
364         * if hasFeature(RESERVED_KEYWORD_AS_IDENTIFIER) returns true,
365         * treat future reserved keyword (see  Ecma-262, section 7.5.3) as ordinary
366         * identifiers but warn about this usage
367         */
368        public static final int FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER = 3;
369    
370        /**
371         * if hasFeature(FEATURE_TO_STRING_AS_SOURCE) returns true,
372         * calling toString on JS objects gives JS source with code to create an
373         * object with all enumeratable fields of the original object instead of
374         * printing "[object <object-type>]".
375         * By default {@link #hasFeature(int)} returns true only if
376         * the current JS version is set to {@link #VERSION_1_2}.
377         */
378        public static final int FEATURE_TO_STRING_AS_SOURCE = 4;
379    
380        /**
381         * Controls certain aspects of script semantics.
382         * Should be overwritten to alter default behavior.
383         * @param featureIndex feature index to check
384         * @return true if the <code>featureIndex</code> feature is turned on
385         * @see #FEATURE_NON_ECMA_GET_YEAR
386         * @see #FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME
387         * @see #FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER
388         * @see #FEATURE_TO_STRING_AS_SOURCE
389         */
390        public boolean hasFeature(int featureIndex) {
391            switch (featureIndex) {
392                case FEATURE_NON_ECMA_GET_YEAR:
393                   /*
394                    * During the great date rewrite of 1.3, we tried to track the
395                    * evolving ECMA standard, which then had a definition of
396                    * getYear which always subtracted 1900.  Which we
397                    * implemented, not realizing that it was incompatible with
398                    * the old behavior...  now, rather than thrash the behavior
399                    * yet again, we've decided to leave it with the - 1900
400                    * behavior and point people to the getFullYear method.  But
401                    * we try to protect existing scripts that have specified a
402                    * version...
403                    */
404                    return (version == Context.VERSION_1_0
405                            || version == Context.VERSION_1_1
406                            || version == Context.VERSION_1_2);
407    
408                case FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME:
409                    return false;
410    
411                case FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER:
412                    return false;
413    
414                case FEATURE_TO_STRING_AS_SOURCE:
415                    return version == VERSION_1_2;
416            }
417            // It is a bug to call the method with unknown featureIndex
418            throw new IllegalArgumentException();
419        }
420    
421        /********** end of API **********/
422    
423        static String getMessage0(String messageId) {
424            return getMessage(messageId, null);
425        }
426    
427        static String getMessage2(String messageId, Object arg1, Object arg2) {
428            Object[] arguments = {arg1, arg2};
429            return getMessage(messageId, arguments);
430        }
431    
432        /**
433         * Internal method that reports an error for missing calls to
434         * enter().
435         */
436        static Context getContext() {
437            Context cx = getCurrentContext();
438            if (cx == null) {
439                throw new RuntimeException(
440                    "No Context associated with current Thread");
441            }
442            return cx;
443        }
444    
445        static String getMessage(String messageId, Object[] arguments) {
446            String formatString;
447            try {
448                formatString = messages.getString(messageId);
449            } catch (MissingResourceException mre) {
450                throw new RuntimeException("No message resource found for message property " + messageId);
451            }
452    
453            MessageFormat formatter = new MessageFormat(formatString);
454            return formatter.format(arguments);
455        }
456    
457        // debug flags
458        static final boolean printTrees = true;
459        static final boolean printICode = true;
460    
461    // Rudimentary support for Design-by-Contract
462        static void codeBug() {
463            throw new RuntimeException("FAILED ASSERTION");
464        }
465    
466        static final boolean check = true;
467    
468        private static MessagesBundle messages = new MessagesBundle();
469        private static Hashtable threadContexts = new Hashtable(11);
470        private static Object threadLocalCx;
471        private static Method threadLocalGet;
472        private static Method threadLocalSet;
473    
474        int version;
475        int errorCount;
476    
477        private ErrorReporter errorReporter;
478        private Locale locale;
479        private boolean generatingDebug;
480        private boolean generatingDebugChanged;
481        private boolean generatingSource=true;
482        private boolean compileFunctionsWithDynamicScopeFlag;
483        private int enterCount;
484        private Object[] listeners;
485        private Hashtable hashtable;
486        private ClassLoader applicationClassLoader;
487    
488        /**
489         * This is the list of names of objects forcing the creation of
490         * function activation records.
491         */
492        private Hashtable activationNames;
493    
494        // For instruction counting (interpreter only)
495        int instructionCount;
496        int instructionThreshold;
497    }