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: // API class
047: package com.google.gwt.dev.js.rhino;
048:
049: import java.beans.PropertyChangeEvent;
050: import java.beans.PropertyChangeListener;
051: import java.lang.reflect.Method;
052: import java.text.MessageFormat;
053: import java.util.Hashtable;
054: import java.util.Locale;
055: import java.util.ResourceBundle;
056:
057: /**
058: * This class represents the runtime context of an executing script.
059: *
060: * Before executing a script, an instance of Context must be created
061: * and associated with the thread that will be executing the script.
062: * The Context will be used to store information about the executing
063: * of the script such as the call stack. Contexts are associated with
064: * the current thread using the <a href="#enter()">enter()</a> method.<p>
065: *
066: * The behavior of the execution engine may be altered through methods
067: * such as <a href="#setLanguageVersion>setLanguageVersion</a> and
068: * <a href="#setErrorReporter>setErrorReporter</a>.<p>
069: *
070: * Different forms of script execution are supported. Scripts may be
071: * evaluated from the source directly, or first compiled and then later
072: * executed. Interactive execution is also supported.<p>
073: *
074: * Some aspects of script execution, such as type conversions and
075: * object creation, may be accessed directly through methods of
076: * Context.
077: *
078: * @see Scriptable
079: * @author Norris Boyd
080: * @author Brendan Eich
081: */
082:
083: public class Context {
084: public static final String languageVersionProperty = "language version";
085: public static final String errorReporterProperty = "error reporter";
086:
087: /**
088: * Create a new Context.
089: *
090: * Note that the Context must be associated with a thread before
091: * it can be used to execute a script.
092: *
093: * @see org.mozilla.javascript.Context#enter
094: */
095: public Context() {
096: setLanguageVersion(VERSION_DEFAULT);
097: }
098:
099: /**
100: * Get a context associated with the current thread, creating
101: * one if need be.
102: *
103: * The Context stores the execution state of the JavaScript
104: * engine, so it is required that the context be entered
105: * before execution may begin. Once a thread has entered
106: * a Context, then getCurrentContext() may be called to find
107: * the context that is associated with the current thread.
108: * <p>
109: * Calling <code>enter()</code> will
110: * return either the Context currently associated with the
111: * thread, or will create a new context and associate it
112: * with the current thread. Each call to <code>enter()</code>
113: * must have a matching call to <code>exit()</code>. For example,
114: * <pre>
115: * Context cx = Context.enter();
116: * try {
117: * ...
118: * cx.evaluateString(...);
119: * }
120: * finally { Context.exit(); }
121: * </pre>
122: * @return a Context associated with the current thread
123: * @see org.mozilla.javascript.Context#getCurrentContext
124: * @see org.mozilla.javascript.Context#exit
125: */
126: public static Context enter() {
127: return enter(null);
128: }
129:
130: /**
131: * Get a Context associated with the current thread, using
132: * the given Context if need be.
133: * <p>
134: * The same as <code>enter()</code> except that <code>cx</code>
135: * is associated with the current thread and returned if
136: * the current thread has no associated context and <code>cx</code>
137: * is not associated with any other thread.
138: * @param cx a Context to associate with the thread if possible
139: * @return a Context associated with the current thread
140: */
141: public static Context enter(Context cx) {
142:
143: Context old = getCurrentContext();
144:
145: if (cx == null) {
146: if (old != null) {
147: cx = old;
148: } else {
149: cx = new Context();
150: setThreadContext(cx);
151: }
152: } else {
153: if (cx.enterCount != 0) {
154: // The suplied context must be the context for
155: // the current thread if it is already entered
156: if (cx != old) {
157: throw new RuntimeException(
158: "Cannot enter Context active on another thread");
159: }
160: } else {
161: if (old != null) {
162: cx = old;
163: } else {
164: setThreadContext(cx);
165: }
166: }
167: }
168:
169: ++cx.enterCount;
170:
171: return cx;
172: }
173:
174: /**
175: * Exit a block of code requiring a Context.
176: *
177: * Calling <code>exit()</code> will remove the association between
178: * the current thread and a Context if the prior call to
179: * <code>enter()</code> on this thread newly associated a Context
180: * with this thread.
181: * Once the current thread no longer has an associated Context,
182: * it cannot be used to execute JavaScript until it is again associated
183: * with a Context.
184: *
185: * @see org.mozilla.javascript.Context#enter
186: */
187: public static void exit() {
188: boolean released = false;
189: Context cx = getCurrentContext();
190: if (cx == null) {
191: throw new RuntimeException(
192: "Calling Context.exit without previous Context.enter");
193: }
194: if (Context.check && cx.enterCount < 1)
195: Context.codeBug();
196: --cx.enterCount;
197: if (cx.enterCount == 0) {
198: released = true;
199: setThreadContext(null);
200: }
201: }
202:
203: /**
204: * Get the current Context.
205: *
206: * The current Context is per-thread; this method looks up
207: * the Context associated with the current thread. <p>
208: *
209: * @return the Context associated with the current thread, or
210: * null if no context is associated with the current
211: * thread.
212: * @see org.mozilla.javascript.Context#enter
213: * @see org.mozilla.javascript.Context#exit
214: */
215: public static Context getCurrentContext() {
216: if (threadLocalCx != null) {
217: try {
218: return (Context) threadLocalGet.invoke(threadLocalCx,
219: (Object[]) null);
220: } catch (Exception ex) {
221: }
222: }
223: Thread t = Thread.currentThread();
224: return (Context) threadContexts.get(t);
225: }
226:
227: private static void setThreadContext(Context cx) {
228: if (threadLocalCx != null) {
229: try {
230: threadLocalSet.invoke(threadLocalCx,
231: new Object[] { cx });
232: return;
233: } catch (Exception ex) {
234: }
235: }
236: Thread t = Thread.currentThread();
237: if (cx != null) {
238: threadContexts.put(t, cx);
239: } else {
240: threadContexts.remove(t);
241: }
242: }
243:
244: /**
245: * Language versions
246: *
247: * All integral values are reserved for future version numbers.
248: */
249:
250: /**
251: * The unknown version.
252: */
253: public static final int VERSION_UNKNOWN = -1;
254:
255: /**
256: * The default version.
257: */
258: public static final int VERSION_DEFAULT = 0;
259:
260: /**
261: * JavaScript 1.0
262: */
263: public static final int VERSION_1_0 = 100;
264:
265: /**
266: * JavaScript 1.1
267: */
268: public static final int VERSION_1_1 = 110;
269:
270: /**
271: * JavaScript 1.2
272: */
273: public static final int VERSION_1_2 = 120;
274:
275: /**
276: * JavaScript 1.3
277: */
278: public static final int VERSION_1_3 = 130;
279:
280: /**
281: * JavaScript 1.4
282: */
283: public static final int VERSION_1_4 = 140;
284:
285: /**
286: * JavaScript 1.5
287: */
288: public static final int VERSION_1_5 = 150;
289:
290: /**
291: * Get the current language version.
292: * <p>
293: * The language version number affects JavaScript semantics as detailed
294: * in the overview documentation.
295: *
296: * @return an integer that is one of VERSION_1_0, VERSION_1_1, etc.
297: */
298: public int getLanguageVersion() {
299: return version;
300: }
301:
302: /**
303: * Set the language version.
304: *
305: * <p>
306: * Setting the language version will affect functions and scripts compiled
307: * subsequently. See the overview documentation for version-specific
308: * behavior.
309: *
310: * @param version the version as specified by VERSION_1_0, VERSION_1_1, etc.
311: */
312: public void setLanguageVersion(int version) {
313: this .version = version;
314: }
315:
316: /**
317: * Get the implementation version.
318: *
319: * <p>
320: * The implementation version is of the form
321: * <pre>
322: * "<i>name langVer</i> <code>release</code> <i>relNum date</i>"
323: * </pre>
324: * where <i>name</i> is the name of the product, <i>langVer</i> is
325: * the language version, <i>relNum</i> is the release number, and
326: * <i>date</i> is the release date for that specific
327: * release in the form "yyyy mm dd".
328: *
329: * @return a string that encodes the product, language version, release
330: * number, and date.
331: */
332: public String getImplementationVersion() {
333: return "Rhino 1.5 release 4.1 2003 04 21";
334: }
335:
336: /**
337: * Get the current error reporter.
338: *
339: * @see org.mozilla.javascript.ErrorReporter
340: */
341: public ErrorReporter getErrorReporter() {
342: return errorReporter;
343: }
344:
345: /**
346: * Change the current error reporter.
347: *
348: * @return the previous error reporter
349: * @see org.mozilla.javascript.ErrorReporter
350: */
351: public ErrorReporter setErrorReporter(ErrorReporter reporter) {
352: errorReporter = reporter;
353: return reporter;
354: }
355:
356: /**
357: * Get the current locale. Returns the default locale if none has
358: * been set.
359: *
360: * @see java.util.Locale
361: */
362:
363: public Locale getLocale() {
364: if (locale == null)
365: locale = Locale.getDefault();
366: return locale;
367: }
368:
369: /**
370: * Set the current locale.
371: *
372: * @see java.util.Locale
373: */
374: public Locale setLocale(Locale loc) {
375: Locale result = locale;
376: locale = loc;
377: return result;
378: }
379:
380: /**
381: * Notify any registered listeners that a bounded property has changed
382: * @see #addPropertyChangeListener(java.beans.PropertyChangeListener)
383: * @see #removePropertyChangeListener(java.beans.PropertyChangeListener)
384: * @see java.beans.PropertyChangeListener
385: * @see java.beans.PropertyChangeEvent
386: * @param property the bound property
387: * @param oldValue the old value
388: * @param newVale the new value
389: */
390: void firePropertyChange(String property, Object oldValue,
391: Object newValue) {
392: Object[] array = listeners;
393: if (array != null) {
394: firePropertyChangeImpl(array, property, oldValue, newValue);
395: }
396: }
397:
398: private void firePropertyChangeImpl(Object[] array,
399: String property, Object oldValue, Object newValue) {
400: for (int i = array.length; i-- != 0;) {
401: Object obj = array[i];
402: if (obj instanceof PropertyChangeListener) {
403: PropertyChangeListener l = (PropertyChangeListener) obj;
404: l.propertyChange(new PropertyChangeEvent(this ,
405: property, oldValue, newValue));
406: }
407: }
408: }
409:
410: /**
411: * Report a warning using the error reporter for the current thread.
412: *
413: * @param message the warning message to report
414: * @param sourceName a string describing the source, such as a filename
415: * @param lineno the starting line number
416: * @param lineSource the text of the line (may be null)
417: * @param lineOffset the offset into lineSource where problem was detected
418: * @see org.mozilla.javascript.ErrorReporter
419: */
420: public static void reportWarning(String message, String sourceName,
421: int lineno, String lineSource, int lineOffset) {
422: Context cx = Context.getContext();
423: cx.getErrorReporter().warning(message, sourceName, lineno,
424: lineSource, lineOffset);
425: }
426:
427: /**
428: * Report a warning using the error reporter for the current thread.
429: *
430: * @param message the warning message to report
431: * @see org.mozilla.javascript.ErrorReporter
432: */
433: /*
434: public static void reportWarning(String message) {
435: int[] linep = { 0 };
436: String filename = getSourcePositionFromStack(linep);
437: Context.reportWarning(message, filename, linep[0], null, 0);
438: }
439: */
440:
441: /**
442: * Report an error using the error reporter for the current thread.
443: *
444: * @param message the error message to report
445: * @param sourceName a string describing the source, such as a filename
446: * @param lineno the starting line number
447: * @param lineSource the text of the line (may be null)
448: * @param lineOffset the offset into lineSource where problem was detected
449: * @see org.mozilla.javascript.ErrorReporter
450: */
451: public static void reportError(String message, String sourceName,
452: int lineno, String lineSource, int lineOffset) {
453: Context cx = getCurrentContext();
454: if (cx != null) {
455: cx.errorCount++;
456: cx.getErrorReporter().error(message, sourceName, lineno,
457: lineSource, lineOffset);
458: } else {
459: throw new EvaluatorException(message);
460: }
461: }
462:
463: /**
464: * Report an error using the error reporter for the current thread.
465: *
466: * @param message the error message to report
467: * @see org.mozilla.javascript.ErrorReporter
468: */
469: /*
470: public static void reportError(String message) {
471: int[] linep = { 0 };
472: String filename = getSourcePositionFromStack(linep);
473: Context.reportError(message, filename, linep[0], null, 0);
474: }
475: */
476:
477: /**
478: * Report a runtime error using the error reporter for the current thread.
479: *
480: * @param message the error message to report
481: * @param sourceName a string describing the source, such as a filename
482: * @param lineno the starting line number
483: * @param lineSource the text of the line (may be null)
484: * @param lineOffset the offset into lineSource where problem was detected
485: * @return a runtime exception that will be thrown to terminate the
486: * execution of the script
487: * @see org.mozilla.javascript.ErrorReporter
488: */
489: /*
490: public static EvaluatorException reportRuntimeError(String message,
491: String sourceName,
492: int lineno,
493: String lineSource,
494: int lineOffset)
495: {
496: Context cx = getCurrentContext();
497: if (cx != null) {
498: cx.errorCount++;
499: return cx.getErrorReporter().
500: runtimeError(message, sourceName, lineno,
501: lineSource, lineOffset);
502: } else {
503: throw new EvaluatorException(message);
504: }
505: }
506:
507: static EvaluatorException reportRuntimeError0(String messageId) {
508: return reportRuntimeError(getMessage0(messageId));
509: }
510:
511: static EvaluatorException reportRuntimeError1
512: (String messageId, Object arg1)
513: {
514: return reportRuntimeError(getMessage1(messageId, arg1));
515: }
516:
517: static EvaluatorException reportRuntimeError2
518: (String messageId, Object arg1, Object arg2)
519: {
520: return reportRuntimeError(getMessage2(messageId, arg1, arg2));
521: }
522:
523: static EvaluatorException reportRuntimeError3
524: (String messageId, Object arg1, Object arg2, Object arg3)
525: {
526: return reportRuntimeError(getMessage3(messageId, arg1, arg2, arg3));
527: }
528: */
529:
530: /**
531: * Report a runtime error using the error reporter for the current thread.
532: *
533: * @param message the error message to report
534: * @see org.mozilla.javascript.ErrorReporter
535: */
536: /*
537: public static EvaluatorException reportRuntimeError(String message) {
538: int[] linep = { 0 };
539: String filename = getSourcePositionFromStack(linep);
540: return Context.reportRuntimeError(message, filename, linep[0], null, 0);
541: }
542: */
543:
544: /**
545: * Get a value corresponding to a key.
546: * <p>
547: * Since the Context is associated with a thread it can be
548: * used to maintain values that can be later retrieved using
549: * the current thread.
550: * <p>
551: * Note that the values are maintained with the Context, so
552: * if the Context is disassociated from the thread the values
553: * cannot be retreived. Also, if private data is to be maintained
554: * in this manner the key should be a java.lang.Object
555: * whose reference is not divulged to untrusted code.
556: * @param key the key used to lookup the value
557: * @return a value previously stored using putThreadLocal.
558: */
559: public final Object getThreadLocal(Object key) {
560: if (hashtable == null)
561: return null;
562: return hashtable.get(key);
563: }
564:
565: /**
566: * Put a value that can later be retrieved using a given key.
567: * <p>
568: * @param key the key used to index the value
569: * @param value the value to save
570: */
571: public void putThreadLocal(Object key, Object value) {
572: if (hashtable == null)
573: hashtable = new Hashtable();
574: hashtable.put(key, value);
575: }
576:
577: /**
578: * Remove values from thread-local storage.
579: * @param key the key for the entry to remove.
580: * @since 1.5 release 2
581: */
582: public void removeThreadLocal(Object key) {
583: if (hashtable == null)
584: return;
585: hashtable.remove(key);
586: }
587:
588: /**
589: * Return whether functions are compiled by this context using
590: * dynamic scope.
591: * <p>
592: * If functions are compiled with dynamic scope, then they execute
593: * in the scope of their caller, rather than in their parent scope.
594: * This is useful for sharing functions across multiple scopes.
595: * @since 1.5 Release 1
596: */
597: public final boolean hasCompileFunctionsWithDynamicScope() {
598: return compileFunctionsWithDynamicScopeFlag;
599: }
600:
601: /**
602: * Set whether functions compiled by this context should use
603: * dynamic scope.
604: * <p>
605: * @param flag if true, compile functions with dynamic scope
606: * @since 1.5 Release 1
607: */
608: public void setCompileFunctionsWithDynamicScope(boolean flag) {
609: compileFunctionsWithDynamicScopeFlag = flag;
610: }
611:
612: /**
613: * if hasFeature(FEATURE_NON_ECMA_GET_YEAR) returns true,
614: * Date.prototype.getYear subtructs 1900 only if 1900 <= date < 2000
615: * in deviation with Ecma B.2.4
616: */
617: public static final int FEATURE_NON_ECMA_GET_YEAR = 1;
618:
619: /**
620: * if hasFeature(FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME) returns true,
621: * allow 'function <MemberExpression>(...) { ... }' to be syntax sugar for
622: * '<MemberExpression> = function(...) { ... }', when <MemberExpression>
623: * is not simply identifier.
624: * See Ecma-262, section 11.2 for definition of <MemberExpression>
625: */
626: public static final int FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME = 2;
627:
628: /**
629: * if hasFeature(RESERVED_KEYWORD_AS_IDENTIFIER) returns true,
630: * treat future reserved keyword (see Ecma-262, section 7.5.3) as ordinary
631: * identifiers but warn about this usage
632: */
633: public static final int FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER = 3;
634:
635: /**
636: * if hasFeature(FEATURE_TO_STRING_AS_SOURCE) returns true,
637: * calling toString on JS objects gives JS source with code to create an
638: * object with all enumeratable fields of the original object instead of
639: * printing "[object <object-type>]".
640: * By default {@link #hasFeature(int)} returns true only if
641: * the current JS version is set to {@link #VERSION_1_2}.
642: */
643: public static final int FEATURE_TO_STRING_AS_SOURCE = 4;
644:
645: /**
646: * Controls certain aspects of script semantics.
647: * Should be overwritten to alter default behavior.
648: * @param featureIndex feature index to check
649: * @return true if the <code>featureIndex</code> feature is turned on
650: * @see #FEATURE_NON_ECMA_GET_YEAR
651: * @see #FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME
652: * @see #FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER
653: * @see #FEATURE_TO_STRING_AS_SOURCE
654: */
655: public boolean hasFeature(int featureIndex) {
656: switch (featureIndex) {
657: case FEATURE_NON_ECMA_GET_YEAR:
658: /*
659: * During the great date rewrite of 1.3, we tried to track the
660: * evolving ECMA standard, which then had a definition of
661: * getYear which always subtracted 1900. Which we
662: * implemented, not realizing that it was incompatible with
663: * the old behavior... now, rather than thrash the behavior
664: * yet again, we've decided to leave it with the - 1900
665: * behavior and point people to the getFullYear method. But
666: * we try to protect existing scripts that have specified a
667: * version...
668: */
669: return (version == Context.VERSION_1_0
670: || version == Context.VERSION_1_1 || version == Context.VERSION_1_2);
671:
672: case FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME:
673: return false;
674:
675: case FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER:
676: return false;
677:
678: case FEATURE_TO_STRING_AS_SOURCE:
679: return version == VERSION_1_2;
680: }
681: // It is a bug to call the method with unknown featureIndex
682: throw new IllegalArgumentException();
683: }
684:
685: /********** end of API **********/
686:
687: static String getMessage0(String messageId) {
688: return getMessage(messageId, null);
689: }
690:
691: static String getMessage1(String messageId, Object arg1) {
692: Object[] arguments = { arg1 };
693: return getMessage(messageId, arguments);
694: }
695:
696: static String getMessage2(String messageId, Object arg1, Object arg2) {
697: Object[] arguments = { arg1, arg2 };
698: return getMessage(messageId, arguments);
699: }
700:
701: static String getMessage3(String messageId, Object arg1,
702: Object arg2, Object arg3) {
703: Object[] arguments = { arg1, arg2, arg3 };
704: return getMessage(messageId, arguments);
705: }
706:
707: /**
708: * Internal method that reports an error for missing calls to
709: * enter().
710: */
711: static Context getContext() {
712: Context cx = getCurrentContext();
713: if (cx == null) {
714: throw new RuntimeException(
715: "No Context associated with current Thread");
716: }
717: return cx;
718: }
719:
720: /* OPT there's a noticable delay for the first error! Maybe it'd
721: * make sense to use a ListResourceBundle instead of a properties
722: * file to avoid (synchronized) text parsing.
723: */
724: // bruce: removed referenced to the initial "java" package name
725: // that used to be there due to a build artifact
726: static final String defaultResource = "com.google.gwt.dev.js.rhino.Messages";
727:
728: static String getMessage(String messageId, Object[] arguments) {
729: Context cx = getCurrentContext();
730: Locale locale = cx != null ? cx.getLocale() : Locale
731: .getDefault();
732:
733: // ResourceBundle does cacheing.
734: ResourceBundle rb = ResourceBundle.getBundle(defaultResource,
735: locale);
736:
737: String formatString;
738: try {
739: formatString = rb.getString(messageId);
740: } catch (java.util.MissingResourceException mre) {
741: throw new RuntimeException(
742: "no message resource found for message property "
743: + messageId);
744: }
745:
746: /*
747: * It's OK to format the string, even if 'arguments' is null;
748: * we need to format it anyway, to make double ''s collapse to
749: * single 's.
750: */
751: // TODO: MessageFormat is not available on pJava
752: MessageFormat formatter = new MessageFormat(formatString);
753: return formatter.format(arguments);
754: }
755:
756: // debug flags
757: static final boolean printTrees = true;
758: static final boolean printICode = true;
759:
760: final boolean isVersionECMA1() {
761: return version == VERSION_DEFAULT || version >= VERSION_1_3;
762: }
763:
764: // Rudimentary support for Design-by-Contract
765: static void codeBug() {
766: throw new RuntimeException("FAILED ASSERTION");
767: }
768:
769: static final boolean check = true;
770:
771: private static Hashtable threadContexts = new Hashtable(11);
772: private static Object threadLocalCx;
773: private static Method threadLocalGet;
774: private static Method threadLocalSet;
775:
776: int version;
777: int errorCount;
778:
779: private ErrorReporter errorReporter;
780: private Locale locale;
781: private boolean generatingDebug;
782: private boolean generatingDebugChanged;
783: private boolean generatingSource = true;
784: private boolean compileFunctionsWithDynamicScopeFlag;
785: private int enterCount;
786: private Object[] listeners;
787: private Hashtable hashtable;
788: private ClassLoader applicationClassLoader;
789:
790: /**
791: * This is the list of names of objects forcing the creation of
792: * function activation records.
793: */
794: private Hashtable activationNames;
795:
796: // For instruction counting (interpreter only)
797: int instructionCount;
798: int instructionThreshold;
799: }
|