/[jscoverage]/trunk/js/jscntxt.cpp
ViewVC logotype

Diff of /trunk/js/jscntxt.cpp

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 459 by siliconforks, Tue Dec 9 03:37:47 2008 UTC revision 460 by siliconforks, Sat Sep 26 23:15:22 2009 UTC
# Line 1  Line 1 
1  /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-  /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2   * vim: set ts=8 sw=4 et tw=80:   * vim: set ts=8 sw=4 et tw=80:
3   *   *
4   * ***** BEGIN LICENSE BLOCK *****   * ***** BEGIN LICENSE BLOCK *****
# Line 61  Line 61 
61  #include "jsnum.h"  #include "jsnum.h"
62  #include "jsobj.h"  #include "jsobj.h"
63  #include "jsopcode.h"  #include "jsopcode.h"
64    #include "jspubtd.h"
65  #include "jsscan.h"  #include "jsscan.h"
66  #include "jsscope.h"  #include "jsscope.h"
67  #include "jsscript.h"  #include "jsscript.h"
68    #include "jsstaticcheck.h"
69  #include "jsstr.h"  #include "jsstr.h"
70  #include "jstracer.h"  #include "jstracer.h"
71    
72  #ifdef JS_THREADSAFE  static void
73  #include "prtypes.h"  FreeContext(JSContext *cx);
   
 /*  
  * The index for JSThread info, returned by PR_NewThreadPrivateIndex.  The  
  * index value is visible and shared by all threads, but the data associated  
  * with it is private to each thread.  
  */  
 static PRUintn threadTPIndex;  
 static JSBool  tpIndexInited = JS_FALSE;  
74    
75  JS_BEGIN_EXTERN_C  static void
76  JSBool  InitThreadData(JSThreadData *data)
 js_InitThreadPrivateIndex(void (*ptr)(void *))  
77  {  {
78      PRStatus status;  #ifdef DEBUG
79        /* The data must be already zeroed. */
80      if (tpIndexInited)      for (size_t i = 0; i != sizeof(*data); ++i)
81          return JS_TRUE;          JS_ASSERT(reinterpret_cast<uint8*>(data)[i] == 0);
82    #endif
83    #ifdef JS_TRACER
84        js_InitJIT(&data->traceMonitor);
85    #endif
86    }
87    
88      status = PR_NewThreadPrivateIndex(&threadTPIndex, ptr);  static void
89    FinishThreadData(JSThreadData *data)
90    {
91    #ifdef DEBUG
92        /* All GC-related things must be already removed at this point. */
93        for (size_t i = 0; i != JS_ARRAY_LENGTH(data->scriptsToGC); ++i)
94            JS_ASSERT(!data->scriptsToGC[i]);
95    #endif
96    
97      if (status == PR_SUCCESS)      js_FinishGSNCache(&data->gsnCache);
98          tpIndexInited = JS_TRUE;      js_FinishPropertyCache(&data->propertyCache);
99      return status == PR_SUCCESS;  #if defined JS_TRACER
100        js_FinishJIT(&data->traceMonitor);
101    #endif
102  }  }
 JS_END_EXTERN_C  
103    
104  /*  static void
105   * Callback function to delete a JSThread info when the thread that owns it  PurgeThreadData(JSContext *cx, JSThreadData *data)
  * is destroyed.  
  */  
 void  
 js_ThreadDestructorCB(void *ptr)  
106  {  {
107      JSThread *thread = (JSThread *)ptr;  # ifdef JS_TRACER
108        JSTraceMonitor *tm = &data->traceMonitor;
109        tm->reservedDoublePoolPtr = tm->reservedDoublePool;
110        tm->needFlush = JS_TRUE;
111    
112      if (!thread)      if (tm->recorder)
113          return;          tm->recorder->deepAbort();
114    
115      /*      /*
116       * Check that this thread properly called either JS_DestroyContext or       * We want to keep tm->reservedObjects after the GC. So, unless we are
117       * JS_ClearContextThread on each JSContext it created or used.       * shutting down, we don't purge them here and rather mark them during
118         * the GC, see MarkReservedObjects in jsgc.cpp.
119       */       */
120      JS_ASSERT(JS_CLIST_IS_EMPTY(&thread->contextList));      if (cx->runtime->state == JSRTS_LANDING)
121      GSN_CACHE_CLEAR(&thread->gsnCache);          tm->reservedObjects = NULL;
122  #if defined JS_TRACER  # endif
     js_FinishJIT(&thread->traceMonitor);  
 #endif  
     free(thread);  
 }  
123    
124  /*      /* Destroy eval'ed scripts. */
125   * Get current thread-local JSThread info, creating one if it doesn't exist.      js_DestroyScriptsToGC(cx, data);
  * Each thread has a unique JSThread pointer.  
  *  
  * Since we are dealing with thread-local data, no lock is needed.  
  *  
  * Return a pointer to the thread local info, NULL if the system runs out  
  * of memory, or it failed to set thread private data (neither case is very  
  * likely; both are probably due to out-of-memory).  It is up to the caller  
  * to report an error, if possible.  
  */  
 JSThread *  
 js_GetCurrentThread(JSRuntime *rt)  
 {  
     JSThread *thread;  
126    
127      thread = (JSThread *)PR_GetThreadPrivate(threadTPIndex);      js_PurgeGSNCache(&data->gsnCache);
128      if (!thread) {      js_PurgePropertyCache(cx, &data->propertyCache);
129          thread = (JSThread *) malloc(sizeof(JSThread));  }
         if (!thread)  
             return NULL;  
 #ifdef DEBUG  
         memset(thread, JS_FREE_PATTERN, sizeof(JSThread));  
 #endif  
         if (PR_FAILURE == PR_SetThreadPrivate(threadTPIndex, thread)) {  
             free(thread);  
             return NULL;  
         }  
130    
131          JS_INIT_CLIST(&thread->contextList);  #ifdef JS_THREADSAFE
         thread->id = js_CurrentThreadId();  
         thread->gcMallocBytes = 0;  
 #ifdef JS_TRACER  
         memset(&thread->traceMonitor, 0, sizeof(thread->traceMonitor));  
         js_InitJIT(&thread->traceMonitor);  
 #endif  
         thread->scriptsToGC = NULL;  
132    
133          /*  static JSThread *
134           * js_SetContextThread initializes the remaining fields as necessary.  NewThread(jsword id)
135           */  {
136      }      JS_ASSERT(js_CurrentThreadId() == id);
137        JSThread *thread = (JSThread *) calloc(1, sizeof(JSThread));
138        if (!thread)
139            return NULL;
140        JS_INIT_CLIST(&thread->contextList);
141        thread->id = id;
142        InitThreadData(&thread->data);
143      return thread;      return thread;
144  }  }
145    
146  /*  static void
147   * Sets current thread as owning thread of a context by assigning the  DestroyThread(JSThread *thread)
  * thread-private info to the context. If the current thread doesn't have  
  * private JSThread info, create one.  
  */  
 JSBool  
 js_SetContextThread(JSContext *cx)  
148  {  {
149      JSThread *thread = js_GetCurrentThread(cx->runtime);      /* The thread must have zero contexts. */
150        JS_ASSERT(JS_CLIST_IS_EMPTY(&thread->contextList));
151        JS_ASSERT(!thread->titleToShare);
152        FinishThreadData(&thread->data);
153        free(thread);
154    }
155    
156      if (!thread) {  JSBool
157          JS_ReportOutOfMemory(cx);  js_InitContextThread(JSContext *cx)
158          return JS_FALSE;  {
159      }      JS_ASSERT(!cx->thread);
160        jsword id = js_CurrentThreadId();
161        JSRuntime *rt = cx->runtime;
162        JS_LOCK_GC(rt);
163    
164      /*      /*
165       * Clear caches on each transition from 0 to 1 context active on the       * We must not race with a GC that accesses cx->thread for JSContext
166       * current thread. See bug 425828.       * instances on all threads, see bug 476934.
167       */       */
168      if (JS_CLIST_IS_EMPTY(&thread->contextList)) {      js_WaitForGC(rt);
169          memset(&thread->gsnCache, 0, sizeof(thread->gsnCache));      JSThreadsHashEntry *entry = (JSThreadsHashEntry *)
170          memset(&thread->propertyCache, 0, sizeof(thread->propertyCache));                                  JS_DHashTableOperate(&rt->threads,
171                                                         (const void *) id,
172                                                         JS_DHASH_LOOKUP);
173        JSThread *thread;
174        if (JS_DHASH_ENTRY_IS_BUSY(&entry->base)) {
175            thread = entry->thread;
176            JS_ASSERT(thread->id == id);
177        } else {
178            JS_UNLOCK_GC(rt);
179            thread = NewThread(id);
180            if (!thread)
181                return false;
182            JS_LOCK_GC(rt);
183            js_WaitForGC(rt);
184            entry = (JSThreadsHashEntry *)
185                    JS_DHashTableOperate(&rt->threads, (const void *) id,
186                                         JS_DHASH_ADD);
187            if (!entry) {
188                JS_UNLOCK_GC(rt);
189                DestroyThread(thread);
190                return false;
191            }
192    
193            /* Another thread cannot initialize entry->thread. */
194            JS_ASSERT(!entry->thread);
195            entry->thread = thread;
196      }      }
197    
198      /* Assert that the previous cx->thread called JS_ClearContextThread(). */      JS_APPEND_LINK(&cx->threadLinks, &thread->contextList);
     JS_ASSERT(!cx->thread || cx->thread == thread);  
     if (!cx->thread)  
         JS_APPEND_LINK(&cx->threadLinks, &thread->contextList);  
199      cx->thread = thread;      cx->thread = thread;
200      return JS_TRUE;      return true;
201  }  }
202    
 /* Remove the owning thread info of a context. */  
203  void  void
204  js_ClearContextThread(JSContext *cx)  js_ClearContextThread(JSContext *cx)
205  {  {
206      /*      JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread));
      * If cx is associated with a thread, this must be called only from that  
      * thread.  If not, this is a harmless no-op.  
      */  
     JS_ASSERT(cx->thread == js_GetCurrentThread(cx->runtime) || !cx->thread);  
207      JS_REMOVE_AND_INIT_LINK(&cx->threadLinks);      JS_REMOVE_AND_INIT_LINK(&cx->threadLinks);
208      cx->thread = NULL;      cx->thread = NULL;
209  }  }
210    
211    static JSBool
212    thread_matchEntry(JSDHashTable *table,
213                      const JSDHashEntryHdr *hdr,
214                      const void *key)
215    {
216        const JSThreadsHashEntry *entry = (const JSThreadsHashEntry *) hdr;
217    
218        return entry->thread->id == (jsword) key;
219    }
220    
221    static const JSDHashTableOps threads_ops = {
222        JS_DHashAllocTable,
223        JS_DHashFreeTable,
224        JS_DHashVoidPtrKeyStub,
225        thread_matchEntry,
226        JS_DHashMoveEntryStub,
227        JS_DHashClearEntryStub,
228        JS_DHashFinalizeStub,
229        NULL
230    };
231    
232    static JSDHashOperator
233    thread_destroyer(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 /* index */,
234                     void * /* arg */)
235    {
236        JSThreadsHashEntry *entry = (JSThreadsHashEntry *) hdr;
237        JSThread *thread = entry->thread;
238    
239        JS_ASSERT(JS_CLIST_IS_EMPTY(&thread->contextList));
240        DestroyThread(thread);
241        return JS_DHASH_REMOVE;
242    }
243    
244    static JSDHashOperator
245    thread_purger(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 /* index */,
246                  void *arg)
247    {
248        JSContext* cx = (JSContext *) arg;
249        JSThread *thread = ((JSThreadsHashEntry *) hdr)->thread;
250    
251        if (JS_CLIST_IS_EMPTY(&thread->contextList)) {
252            JS_ASSERT(cx->thread != thread);
253            js_DestroyScriptsToGC(cx, &thread->data);
254            DestroyThread(thread);
255            return JS_DHASH_REMOVE;
256        }
257        PurgeThreadData(cx, &thread->data);
258        return JS_DHASH_NEXT;
259    }
260    
261  #endif /* JS_THREADSAFE */  #endif /* JS_THREADSAFE */
262    
263    JSBool
264    js_InitThreads(JSRuntime *rt)
265    {
266    #ifdef JS_THREADSAFE
267        if (!JS_DHashTableInit(&rt->threads, &threads_ops, NULL,
268                               sizeof(JSThreadsHashEntry), 4)) {
269            rt->threads.ops = NULL;
270            return false;
271        }
272    #else
273        InitThreadData(&rt->threadData);
274    #endif
275        return true;
276    }
277    
278    void
279    js_FinishThreads(JSRuntime *rt)
280    {
281    #ifdef JS_THREADSAFE
282        if (!rt->threads.ops)
283            return;
284        JS_DHashTableEnumerate(&rt->threads, thread_destroyer, NULL);
285        JS_DHashTableFinish(&rt->threads);
286        rt->threads.ops = NULL;
287    #else
288        FinishThreadData(&rt->threadData);
289    #endif
290    }
291    
292    void
293    js_PurgeThreads(JSContext *cx)
294    {
295    #ifdef JS_THREADSAFE
296        JS_DHashTableEnumerate(&cx->runtime->threads, thread_purger, cx);
297    #else
298        PurgeThreadData(cx, &cx->runtime->threadData);
299    #endif
300    }
301    
302    /*
303     * JSOPTION_XML and JSOPTION_ANONFUNFIX must be part of the JS version
304     * associated with scripts, so in addition to storing them in cx->options we
305     * duplicate them in cx->version (script->version, etc.) and ensure each bit
306     * remains synchronized between the two through these two functions.
307     */
308    void
309    js_SyncOptionsToVersion(JSContext* cx)
310    {
311        if (cx->options & JSOPTION_XML)
312            cx->version |= JSVERSION_HAS_XML;
313        else
314            cx->version &= ~JSVERSION_HAS_XML;
315        if (cx->options & JSOPTION_ANONFUNFIX)
316            cx->version |= JSVERSION_ANONFUNFIX;
317        else
318            cx->version &= ~JSVERSION_ANONFUNFIX;
319    }
320    
321    inline void
322    js_SyncVersionToOptions(JSContext* cx)
323    {
324        if (cx->version & JSVERSION_HAS_XML)
325            cx->options |= JSOPTION_XML;
326        else
327            cx->options &= ~JSOPTION_XML;
328        if (cx->version & JSVERSION_ANONFUNFIX)
329            cx->options |= JSOPTION_ANONFUNFIX;
330        else
331            cx->options &= ~JSOPTION_ANONFUNFIX;
332    }
333    
334  void  void
335  js_OnVersionChange(JSContext *cx)  js_OnVersionChange(JSContext *cx)
336  {  {
# Line 225  Line 345 
345  js_SetVersion(JSContext *cx, JSVersion version)  js_SetVersion(JSContext *cx, JSVersion version)
346  {  {
347      cx->version = version;      cx->version = version;
348        js_SyncVersionToOptions(cx);
349      js_OnVersionChange(cx);      js_OnVersionChange(cx);
350  }  }
351    
# Line 235  Line 356 
356      JSBool ok, first;      JSBool ok, first;
357      JSContextCallback cxCallback;      JSContextCallback cxCallback;
358    
359      cx = (JSContext *) malloc(sizeof *cx);      /*
360         * We need to initialize the new context fully before adding it to the
361         * runtime list. After that it can be accessed from another thread via
362         * js_ContextIterator.
363         */
364        cx = (JSContext *) calloc(1, sizeof *cx);
365      if (!cx)      if (!cx)
366          return NULL;          return NULL;
     memset(cx, 0, sizeof *cx);  
367    
368      cx->runtime = rt;      cx->runtime = rt;
     JS_ClearOperationCallback(cx);  
369      cx->debugHooks = &rt->globalDebugHooks;      cx->debugHooks = &rt->globalDebugHooks;
370  #if JS_STACK_GROWTH_DIRECTION > 0  #if JS_STACK_GROWTH_DIRECTION > 0
371      cx->stackLimit = (jsuword)-1;      cx->stackLimit = (jsuword) -1;
372  #endif  #endif
373      cx->scriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA;      cx->scriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA;
374  #ifdef JS_THREADSAFE  #ifdef JS_THREADSAFE
375      cx->gcLocalFreeLists = (JSGCFreeListSet *) &js_GCEmptyFreeListSet;      cx->gcLocalFreeLists = (JSGCFreeListSet *) &js_GCEmptyFreeListSet;
     JS_INIT_CLIST(&cx->threadLinks);  
     js_SetContextThread(cx);  
376  #endif  #endif
377        JS_STATIC_ASSERT(JSVERSION_DEFAULT == 0);
378        JS_ASSERT(cx->version == JSVERSION_DEFAULT);
379        VOUCH_DOES_NOT_REQUIRE_STACK();
380        JS_INIT_ARENA_POOL(&cx->stackPool, "stack", stackChunkSize, sizeof(jsval),
381                           &cx->scriptStackQuota);
382    
383      JS_LOCK_GC(rt);      JS_INIT_ARENA_POOL(&cx->tempPool, "temp",
384                           1024,  /* FIXME: bug 421435 */
385                           sizeof(jsdouble), &cx->scriptStackQuota);
386    
387        js_InitRegExpStatics(cx);
388        JS_ASSERT(cx->resolveFlags == 0);
389    
390    #ifdef JS_THREADSAFE
391        if (!js_InitContextThread(cx)) {
392            FreeContext(cx);
393            return NULL;
394        }
395    #endif
396    
397        /*
398         * Here the GC lock is still held after js_InitContextThread took it and
399         * the GC is not running on another thread.
400         */
401      for (;;) {      for (;;) {
         first = (rt->contextList.next == &rt->contextList);  
402          if (rt->state == JSRTS_UP) {          if (rt->state == JSRTS_UP) {
403              JS_ASSERT(!first);              JS_ASSERT(!JS_CLIST_IS_EMPTY(&rt->contextList));
404                first = JS_FALSE;
405              break;              break;
406          }          }
407          if (rt->state == JSRTS_DOWN) {          if (rt->state == JSRTS_DOWN) {
408              JS_ASSERT(first);              JS_ASSERT(JS_CLIST_IS_EMPTY(&rt->contextList));
409                first = JS_TRUE;
410              rt->state = JSRTS_LAUNCHING;              rt->state = JSRTS_LAUNCHING;
411              break;              break;
412          }          }
413          JS_WAIT_CONDVAR(rt->stateChange, JS_NO_TIMEOUT);          JS_WAIT_CONDVAR(rt->stateChange, JS_NO_TIMEOUT);
     }  
     JS_APPEND_LINK(&cx->links, &rt->contextList);  
     JS_UNLOCK_GC(rt);  
   
     /*  
      * First we do the infallible, every-time per-context initializations.  
      * Should a later, fallible initialization (js_InitRegExpStatics, e.g.,  
      * or the stuff under 'if (first)' below) fail, at least the version  
      * and arena-pools will be valid and safe to use (say, from the last GC  
      * done by js_DestroyContext).  
      */  
     cx->version = JSVERSION_DEFAULT;  
     JS_INIT_ARENA_POOL(&cx->stackPool, "stack", stackChunkSize, sizeof(jsval),  
                        &cx->scriptStackQuota);  
414    
415      JS_INIT_ARENA_POOL(&cx->tempPool, "temp",          /*
416                         1024,  /* FIXME: bug 421435 */           * During the above wait after we are notified about the state change
417                         sizeof(jsdouble), &cx->scriptStackQuota);           * but before we wake up, another thread could enter the GC from
418             * js_DestroyContext, bug 478336. So we must wait here to ensure that
419      /*           * when we exit the loop with the first flag set to true, that GC is
420       * To avoid multiple allocations in InitMatch() (in jsregexp.c), the arena           * finished.
421       * size parameter should be at least as big as:           */
422       *   INITIAL_BACKTRACK          js_WaitForGC(rt);
      *   + (sizeof(REProgState) * INITIAL_STATESTACK)  
      *   + (offsetof(REMatchState, parens) + avgParanSize * sizeof(RECapture))  
      */  
     JS_INIT_ARENA_POOL(&cx->regexpPool, "regexp",  
                        12 * 1024 - 40,  /* FIXME: bug 421435 */  
                        sizeof(void *), &cx->scriptStackQuota);  
   
     if (!js_InitRegExpStatics(cx, &cx->regExpStatics)) {  
         js_DestroyContext(cx, JSDCM_NEW_FAILED);  
         return NULL;  
423      }      }
424        JS_APPEND_LINK(&cx->link, &rt->contextList);
425      cx->resolveFlags = 0;      JS_UNLOCK_GC(rt);
426    
427      /*      /*
428       * If cx is the first context on this runtime, initialize well-known atoms,       * If cx is the first context on this runtime, initialize well-known atoms,
# Line 351  Line 472 
472      return cx;      return cx;
473  }  }
474    
475    #if defined DEBUG && defined XP_UNIX
476    # include <stdio.h>
477    
478    class JSAutoFile {
479    public:
480        JSAutoFile() : mFile(NULL) {}
481    
482        ~JSAutoFile() {
483            if (mFile)
484                fclose(mFile);
485        }
486    
487        FILE *open(const char *fname, const char *mode) {
488            return mFile = fopen(fname, mode);
489        }
490        operator FILE *() {
491            return mFile;
492        }
493    
494    private:
495        FILE *mFile;
496    };
497    
498    #ifdef JS_EVAL_CACHE_METERING
499    static void
500    DumpEvalCacheMeter(JSContext *cx)
501    {
502        struct {
503            const char *name;
504            ptrdiff_t  offset;
505        } table[] = {
506    #define frob(x) { #x, offsetof(JSEvalCacheMeter, x) }
507            EVAL_CACHE_METER_LIST(frob)
508    #undef frob
509        };
510        JSEvalCacheMeter *ecm = &JS_THREAD_DATA(cx)->evalCacheMeter;
511    
512        static JSAutoFile fp;
513        if (!fp) {
514            fp.open("/tmp/evalcache.stats", "w");
515            if (!fp)
516                return;
517        }
518    
519        fprintf(fp, "eval cache meter (%p):\n",
520    #ifdef JS_THREADSAFE
521                (void *) cx->thread
522    #else
523                (void *) cx->runtime
524    #endif
525                );
526        for (uintN i = 0; i < JS_ARRAY_LENGTH(table); ++i) {
527            fprintf(fp, "%-8.8s  %llu\n",
528                    table[i].name, *(uint64 *)((uint8 *)ecm + table[i].offset));
529        }
530        fprintf(fp, "hit ratio %g%%\n", ecm->hit * 100. / ecm->probe);
531        fprintf(fp, "avg steps %g\n", double(ecm->step) / ecm->probe);
532        fflush(fp);
533    }
534    # define DUMP_EVAL_CACHE_METER(cx) DumpEvalCacheMeter(cx)
535    #endif
536    
537    #ifdef JS_FUNCTION_METERING
538    static void
539    DumpFunctionMeter(JSContext *cx)
540    {
541        struct {
542            const char *name;
543            ptrdiff_t  offset;
544        } table[] = {
545    #define frob(x) { #x, offsetof(JSFunctionMeter, x) }
546            FUNCTION_KIND_METER_LIST(frob)
547    #undef frob
548        };
549        JSFunctionMeter *fm = &cx->runtime->functionMeter;
550    
551        static JSAutoFile fp;
552        if (!fp) {
553            fp.open("/tmp/function.stats", "a");
554            if (!fp)
555                return;
556        }
557    
558        fprintf(fp, "function meter (%s):\n", cx->runtime->lastScriptFilename);
559        for (uintN i = 0; i < JS_ARRAY_LENGTH(table); ++i) {
560            fprintf(fp, "%-11.11s %d\n",
561                    table[i].name, *(int32 *)((uint8 *)fm + table[i].offset));
562        }
563        fflush(fp);
564    }
565    # define DUMP_FUNCTION_METER(cx)   DumpFunctionMeter(cx)
566    #endif
567    
568    #endif /* DEBUG && XP_UNIX */
569    
570    #ifndef DUMP_EVAL_CACHE_METER
571    # define DUMP_EVAL_CACHE_METER(cx) ((void) 0)
572    #endif
573    
574    #ifndef DUMP_FUNCTION_METER
575    # define DUMP_FUNCTION_METER(cx)   ((void) 0)
576    #endif
577    
578  void  void
579  js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)  js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
580  {  {
581      JSRuntime *rt;      JSRuntime *rt;
582      JSContextCallback cxCallback;      JSContextCallback cxCallback;
583      JSBool last;      JSBool last;
     JSArgumentFormatMap *map;  
     JSLocalRootStack *lrs;  
     JSLocalRootChunk *lrc;  
584    
585      rt = cx->runtime;      rt = cx->runtime;
586    #ifdef JS_THREADSAFE
587        /*
588         * For API compatibility we allow to destroy contexts without a thread in
589         * optimized builds. We assume that the embedding knows that an OOM error
590         * cannot happen in JS_SetContextThread.
591         */
592        JS_ASSERT(cx->thread && CURRENT_THREAD_IS_ME(cx->thread));
593        if (!cx->thread)
594            JS_SetContextThread(cx);
595    
596        JS_ASSERT_IF(rt->gcRunning, cx->outstandingRequests == 0);
597    #endif
598    
599      if (mode != JSDCM_NEW_FAILED) {      if (mode != JSDCM_NEW_FAILED) {
600          cxCallback = rt->cxCallback;          cxCallback = rt->cxCallback;
# Line 378  Line 611 
611          }          }
612      }      }
613    
     /* Remove cx from context list first. */  
614      JS_LOCK_GC(rt);      JS_LOCK_GC(rt);
615      JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING);      JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING);
616      JS_REMOVE_LINK(&cx->links);  #ifdef JS_THREADSAFE
617        /*
618         * Typically we are called outside a request, so ensure that the GC is not
619         * running before removing the context from rt->contextList, see bug 477021.
620         */
621        if (cx->requestDepth == 0)
622            js_WaitForGC(rt);
623        js_RevokeGCLocalFreeLists(cx);
624    #endif
625        JS_REMOVE_LINK(&cx->link);
626      last = (rt->contextList.next == &rt->contextList);      last = (rt->contextList.next == &rt->contextList);
627      if (last)      if (last)
628          rt->state = JSRTS_LANDING;          rt->state = JSRTS_LANDING;
629        if (last || mode == JSDCM_FORCE_GC || mode == JSDCM_MAYBE_GC
630  #ifdef JS_THREADSAFE  #ifdef JS_THREADSAFE
631      js_RevokeGCLocalFreeLists(cx);          || cx->requestDepth != 0
632  #endif  #endif
633      JS_UNLOCK_GC(rt);          ) {
634            JS_ASSERT(!rt->gcRunning);
635    
636            JS_UNLOCK_GC(rt);
637    
638            if (last) {
639    #ifdef JS_THREADSAFE
640                /*
641                 * If cx is not in a request already, begin one now so that we wait
642                 * for any racing GC started on a not-last context to finish, before
643                 * we plow ahead and unpin atoms.  Note that even though we begin a
644                 * request here if necessary, we end all requests on cx below before
645                 * forcing a final GC.  This lets any not-last context destruction
646                 * racing in another thread try to force or maybe run the GC, but by
647                 * that point, rt->state will not be JSRTS_UP, and that GC attempt
648                 * will return early.
649                 */
650                if (cx->requestDepth == 0)
651                    JS_BeginRequest(cx);
652    #endif
653    
654                /* Unlock and clear GC things held by runtime pointers. */
655                js_FinishRuntimeNumberState(cx);
656                js_FinishRuntimeStringState(cx);
657    
658                /* Unpin all common atoms before final GC. */
659                js_FinishCommonAtoms(cx);
660    
661                /* Clear debugging state to remove GC roots. */
662                JS_ClearAllTraps(cx);
663                JS_ClearAllWatchPoints(cx);
664            }
665    
666            /* Remove more GC roots in regExpStatics, then collect garbage. */
667            JS_ClearRegExpRoots(cx);
668    
     if (last) {  
669  #ifdef JS_THREADSAFE  #ifdef JS_THREADSAFE
670          /*          /*
671           * If cx is not in a request already, begin one now so that we wait           * Destroying a context implicitly calls JS_EndRequest().  Also, we must
672           * for any racing GC started on a not-last context to finish, before           * end our request here in case we are "last" -- in that event, another
673           * we plow ahead and unpin atoms.  Note that even though we begin a           * js_DestroyContext that was not last might be waiting in the GC for our
674           * request here if necessary, we end all requests on cx below before           * request to end.  We'll let it run below, just before we do the truly
675           * forcing a final GC.  This lets any not-last context destruction           * final GC and then free atom state.
          * racing in another thread try to force or maybe run the GC, but by  
          * that point, rt->state will not be JSRTS_UP, and that GC attempt  
          * will return early.  
676           */           */
677          if (cx->requestDepth == 0)          while (cx->requestDepth != 0)
678              JS_BeginRequest(cx);              JS_EndRequest(cx);
679  #endif  #endif
680    
681          /* Unlock and clear GC things held by runtime pointers. */          if (last) {
682          js_FinishRuntimeNumberState(cx);              /* Clear builtin functions, which are recreated on demand. */
683          js_FinishRuntimeStringState(cx);              memset(rt->builtinFunctions, 0, sizeof rt->builtinFunctions);
684    
685                js_GC(cx, GC_LAST_CONTEXT);
686                DUMP_EVAL_CACHE_METER(cx);
687                DUMP_FUNCTION_METER(cx);
688    
689          /* Unpin all common atoms before final GC. */              /*
690          js_FinishCommonAtoms(cx);               * Free the script filename table if it exists and is empty. Do this
691                 * after the last GC to avoid finalizers tripping on free memory.
692                 */
693                if (rt->scriptFilenameTable &&
694                    rt->scriptFilenameTable->nentries == 0) {
695                    js_FinishRuntimeScriptState(rt);
696                }
697    
698          /* Clear debugging state to remove GC roots. */              /* Take the runtime down, now that it has no contexts or atoms. */
699          JS_ClearAllTraps(cx);              JS_LOCK_GC(rt);
700          JS_ClearAllWatchPoints(cx);              rt->state = JSRTS_DOWN;
701                JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
702            } else {
703                if (mode == JSDCM_FORCE_GC)
704                    js_GC(cx, GC_NORMAL);
705                else if (mode == JSDCM_MAYBE_GC)
706                    JS_MaybeGC(cx);
707                JS_LOCK_GC(rt);
708                js_WaitForGC(rt);
709            }
710      }      }
   
     /*  
      * Remove more GC roots in regExpStatics, then collect garbage.  
      * XXX anti-modularity alert: we rely on the call to js_RemoveRoot within  
      * XXX this function call to wait for any racing GC to complete, in the  
      * XXX case where JS_DestroyContext is called outside of a request on cx  
      */  
     js_FreeRegExpStatics(cx, &cx->regExpStatics);  
   
711  #ifdef JS_THREADSAFE  #ifdef JS_THREADSAFE
712      /*      js_ClearContextThread(cx);
      * Destroying a context implicitly calls JS_EndRequest().  Also, we must  
      * end our request here in case we are "last" -- in that event, another  
      * js_DestroyContext that was not last might be waiting in the GC for our  
      * request to end.  We'll let it run below, just before we do the truly  
      * final GC and then free atom state.  
      *  
      * At this point, cx must be inaccessible to other threads.  It's off the  
      * rt->contextList, and it should not be reachable via any object private  
      * data structure.  
      */  
     while (cx->requestDepth != 0)  
         JS_EndRequest(cx);  
713  #endif  #endif
714        JS_UNLOCK_GC(rt);
715        FreeContext(cx);
716    }
717    
718      if (last) {  static void
719          js_GC(cx, GC_LAST_CONTEXT);  FreeContext(JSContext *cx)
720    {
721          /*      JSArgumentFormatMap *map;
722           * Free the script filename table if it exists and is empty. Do this      JSLocalRootStack *lrs;
723           * after the last GC to avoid finalizers tripping on free memory.      JSLocalRootChunk *lrc;
          */  
         if (rt->scriptFilenameTable && rt->scriptFilenameTable->nentries == 0)  
             js_FinishRuntimeScriptState(rt);  
724    
725          /* Take the runtime down, now that it has no contexts or atoms. */  #ifdef JS_THREADSAFE
726          JS_LOCK_GC(rt);      JS_ASSERT(!cx->thread);
727          rt->state = JSRTS_DOWN;  #endif
         JS_NOTIFY_ALL_CONDVAR(rt->stateChange);  
         JS_UNLOCK_GC(rt);  
     } else {  
         if (mode == JSDCM_FORCE_GC)  
             js_GC(cx, GC_NORMAL);  
         else if (mode == JSDCM_MAYBE_GC)  
             JS_MaybeGC(cx);  
     }  
728    
729      /* Free the stuff hanging off of cx. */      /* Free the stuff hanging off of cx. */
730        js_FreeRegExpStatics(cx);
731        VOUCH_DOES_NOT_REQUIRE_STACK();
732      JS_FinishArenaPool(&cx->stackPool);      JS_FinishArenaPool(&cx->stackPool);
733      JS_FinishArenaPool(&cx->tempPool);      JS_FinishArenaPool(&cx->tempPool);
     JS_FinishArenaPool(&cx->regexpPool);  
734    
735      if (cx->lastMessage)      if (cx->lastMessage)
736          free(cx->lastMessage);          free(cx->lastMessage);
# Line 495  Line 758 
758          JS_free(cx, lrs);          JS_free(cx, lrs);
759      }      }
760    
 #ifdef JS_THREADSAFE  
     js_ClearContextThread(cx);  
 #endif  
   
761      /* Finally, free cx itself. */      /* Finally, free cx itself. */
762      free(cx);      free(cx);
763  }  }
# Line 509  Line 768 
768      JSCList *cl;      JSCList *cl;
769    
770      for (cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next) {      for (cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next) {
771          if (cl == &cx->links)          if (cl == &cx->link)
772              return JS_TRUE;              return JS_TRUE;
773      }      }
774      JS_RUNTIME_METER(rt, deadContexts);      JS_RUNTIME_METER(rt, deadContexts);
# Line 523  Line 782 
782    
783      if (unlocked)      if (unlocked)
784          JS_LOCK_GC(rt);          JS_LOCK_GC(rt);
785      cx = (JSContext *) (cx ? cx->links.next : rt->contextList.next);      cx = js_ContextFromLinkField(cx ? cx->link.next : rt->contextList.next);
786      if (&cx->links == &rt->contextList)      if (&cx->link == &rt->contextList)
787          cx = NULL;          cx = NULL;
788      *iterp = cx;      *iterp = cx;
789      if (unlocked)      if (unlocked)
# Line 532  Line 791 
791      return cx;      return cx;
792  }  }
793    
794    JS_FRIEND_API(JSContext *)
795    js_NextActiveContext(JSRuntime *rt, JSContext *cx)
796    {
797        JSContext *iter = cx;
798    #ifdef JS_THREADSAFE
799        while ((cx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) {
800            if (cx->requestDepth)
801                break;
802        }
803        return cx;
804    #else
805        return js_ContextIterator(rt, JS_FALSE, &iter);
806    #endif          
807    }
808    
809    #ifdef JS_THREADSAFE
810    
811    uint32
812    js_CountThreadRequests(JSContext *cx)
813    {
814        JSCList *head, *link;
815        uint32 nrequests;
816    
817        JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread));
818        head = &cx->thread->contextList;
819        nrequests = 0;
820        for (link = head->next; link != head; link = link->next) {
821            JSContext *acx = CX_FROM_THREAD_LINKS(link);
822            JS_ASSERT(acx->thread == cx->thread);
823            if (acx->requestDepth)
824                nrequests++;
825        }
826        return nrequests;
827    }
828    
829    /*
830     * If the GC is running and we're called on another thread, wait for this GC
831     * activation to finish. We can safely wait here without fear of deadlock (in
832     * the case where we are called within a request on another thread's context)
833     * because the GC doesn't set rt->gcRunning until after it has waited for all
834     * active requests to end.
835     *
836     * We call here js_CurrentThreadId() after checking for rt->gcRunning to avoid
837     * expensive calls when the GC is not running.
838     */
839    void
840    js_WaitForGC(JSRuntime *rt)
841    {
842        JS_ASSERT_IF(rt->gcRunning, rt->gcLevel > 0);
843        if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) {
844            do {
845                JS_AWAIT_GC_DONE(rt);
846            } while (rt->gcRunning);
847        }
848    }
849    
850    uint32
851    js_DiscountRequestsForGC(JSContext *cx)
852    {
853        uint32 requestDebit;
854    
855        JS_ASSERT(cx->thread);
856        JS_ASSERT(cx->runtime->gcThread != cx->thread);
857    
858    #ifdef JS_TRACER
859        if (JS_ON_TRACE(cx)) {
860            JS_UNLOCK_GC(cx->runtime);
861            js_LeaveTrace(cx);
862            JS_LOCK_GC(cx->runtime);
863        }
864    #endif
865    
866        requestDebit = js_CountThreadRequests(cx);
867        if (requestDebit != 0) {
868            JSRuntime *rt = cx->runtime;
869            JS_ASSERT(requestDebit <= rt->requestCount);
870            rt->requestCount -= requestDebit;
871            if (rt->requestCount == 0)
872                JS_NOTIFY_REQUEST_DONE(rt);
873        }
874        return requestDebit;
875    }
876    
877    void
878    js_RecountRequestsAfterGC(JSRuntime *rt, uint32 requestDebit)
879    {
880        while (rt->gcLevel > 0) {
881            JS_ASSERT(rt->gcThread);
882            JS_AWAIT_GC_DONE(rt);
883        }
884        if (requestDebit != 0)
885            rt->requestCount += requestDebit;
886    }
887    
888    #endif
889    
890  static JSDHashNumber  static JSDHashNumber
891  resolving_HashKey(JSDHashTable *table, const void *ptr)  resolving_HashKey(JSDHashTable *table, const void *ptr)
892  {  {
# Line 879  Line 1234 
1234       * propagates out of scope.  This is needed for compatability       * propagates out of scope.  This is needed for compatability
1235       * with the old scheme.       * with the old scheme.
1236       */       */
1237      if (!cx->fp || !js_ErrorToException(cx, message, reportp)) {      if (!JS_IsRunning(cx) || !js_ErrorToException(cx, message, reportp)) {
1238          js_ReportErrorAgain(cx, message, reportp);          js_ReportErrorAgain(cx, message, reportp);
1239      } else if (cx->debugHooks->debugErrorHook && cx->errorReporter) {      } else if (cx->debugHooks->debugErrorHook && cx->errorReporter) {
1240          JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;          JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
# Line 889  Line 1244 
1244      }      }
1245  }  }
1246    
1247    /* The report must be initially zeroed. */
1248    static void
1249    PopulateReportBlame(JSContext *cx, JSErrorReport *report)
1250    {
1251        JSStackFrame *fp;
1252    
1253        /*
1254         * Walk stack until we find a frame that is associated with some script
1255         * rather than a native frame.
1256         */
1257        for (fp = js_GetTopStackFrame(cx); fp; fp = fp->down) {
1258            if (fp->regs) {
1259                report->filename = fp->script->filename;
1260                report->lineno = js_FramePCToLineNumber(cx, fp);
1261                break;
1262            }
1263        }
1264    }
1265    
1266  /*  /*
1267   * We don't post an exception in this case, since doing so runs into   * We don't post an exception in this case, since doing so runs into
1268   * complications of pre-allocating an exception object which required   * complications of pre-allocating an exception object which required
# Line 899  Line 1273 
1273  void  void
1274  js_ReportOutOfMemory(JSContext *cx)  js_ReportOutOfMemory(JSContext *cx)
1275  {  {
1276      JSStackFrame *fp;  #ifdef JS_TRACER
1277        /*
1278         * If we are in a builtin called directly from trace, don't report an
1279         * error. We will retry in the interpreter instead.
1280         */
1281        if (JS_ON_TRACE(cx) && !cx->bailExit)
1282            return;
1283    #endif
1284    
1285      JSErrorReport report;      JSErrorReport report;
1286      JSErrorReporter onError = cx->errorReporter;      JSErrorReporter onError = cx->errorReporter;
1287    
# Line 912  Line 1294 
1294      memset(&report, 0, sizeof (struct JSErrorReport));      memset(&report, 0, sizeof (struct JSErrorReport));
1295      report.flags = JSREPORT_ERROR;      report.flags = JSREPORT_ERROR;
1296      report.errorNumber = JSMSG_OUT_OF_MEMORY;      report.errorNumber = JSMSG_OUT_OF_MEMORY;
1297        PopulateReportBlame(cx, &report);
     /*  
      * Walk stack until we find a frame that is associated with some script  
      * rather than a native frame.  
      */  
     for (fp = cx->fp; fp; fp = fp->down) {  
         if (fp->regs) {  
             report.filename = fp->script->filename;  
             report.lineno = js_FramePCToLineNumber(cx, fp);  
             break;  
         }  
     }  
1298    
1299      /*      /*
1300       * If debugErrorHook is present then we give it a chance to veto sending       * If debugErrorHook is present then we give it a chance to veto sending
# Line 969  Line 1340 
1340      char *message;      char *message;
1341      jschar *ucmessage;      jschar *ucmessage;
1342      size_t messagelen;      size_t messagelen;
     JSStackFrame *fp;  
1343      JSErrorReport report;      JSErrorReport report;
1344      JSBool warning;      JSBool warning;
1345    
# Line 985  Line 1355 
1355      report.flags = flags;      report.flags = flags;
1356      report.errorNumber = JSMSG_USER_DEFINED_ERROR;      report.errorNumber = JSMSG_USER_DEFINED_ERROR;
1357      report.ucmessage = ucmessage = js_InflateString(cx, message, &messagelen);      report.ucmessage = ucmessage = js_InflateString(cx, message, &messagelen);
1358        PopulateReportBlame(cx, &report);
     /* Find the top-most active script frame, for best line number blame. */  
     for (fp = cx->fp; fp; fp = fp->down) {  
         if (fp->regs) {  
             report.filename = fp->script->filename;  
             report.lineno = js_FramePCToLineNumber(cx, fp);  
             break;  
         }  
     }  
1359    
1360      warning = JSREPORT_IS_WARNING(report.flags);      warning = JSREPORT_IS_WARNING(report.flags);
1361      if (warning && JS_HAS_WERROR_OPTION(cx)) {      if (warning && JS_HAS_WERROR_OPTION(cx)) {
# Line 1183  Line 1545 
1545                         void *userRef, const uintN errorNumber,                         void *userRef, const uintN errorNumber,
1546                         JSBool charArgs, va_list ap)                         JSBool charArgs, va_list ap)
1547  {  {
     JSStackFrame *fp;  
1548      JSErrorReport report;      JSErrorReport report;
1549      char *message;      char *message;
1550      JSBool warning;      JSBool warning;
# Line 1194  Line 1555 
1555      memset(&report, 0, sizeof (struct JSErrorReport));      memset(&report, 0, sizeof (struct JSErrorReport));
1556      report.flags = flags;      report.flags = flags;
1557      report.errorNumber = errorNumber;      report.errorNumber = errorNumber;
1558        PopulateReportBlame(cx, &report);
     /*  
      * If we can't find out where the error was based on the current frame,  
      * see if the next frame has a script/pc combo we can use.  
      */  
     for (fp = cx->fp; fp; fp = fp->down) {  
         if (fp->regs) {  
             report.filename = fp->script->filename;  
             report.lineno = js_FramePCToLineNumber(cx, fp);  
             break;  
         }  
     }  
1559    
1560      if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber,      if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber,
1561                                   &message, &report, &warning, charArgs, ap)) {                                   &message, &report, &warning, charArgs, ap)) {
# Line 1349  Line 1699 
1699    
1700  #if defined DEBUG && defined XP_UNIX  #if defined DEBUG && defined XP_UNIX
1701  /* For gdb usage. */  /* For gdb usage. */
1702  void js_traceon(JSContext *cx)  { cx->tracefp = stderr; }  void js_traceon(JSContext *cx)  { cx->tracefp = stderr; cx->tracePrevPc = NULL; }
1703  void js_traceoff(JSContext *cx) { cx->tracefp = NULL; }  void js_traceoff(JSContext *cx) { cx->tracefp = NULL; }
1704  #endif  #endif
1705    
# Line 1369  Line 1719 
1719  }  }
1720    
1721  JSBool  JSBool
1722  js_ResetOperationCount(JSContext *cx)  js_InvokeOperationCallback(JSContext *cx)
1723  {  {
1724      JSScript *script;      JS_ASSERT(cx->operationCallbackFlag);
1725        
1726        /*
1727         * Reset the callback flag first, then yield. If another thread is racing
1728         * us here we will accumulate another callback request which will be
1729         * serviced at the next opportunity.
1730         */
1731        cx->operationCallbackFlag = 0;
1732    
1733        /*
1734         * Unless we are going to run the GC, we automatically yield the current
1735         * context every time the operation callback is hit since we might be
1736         * called as a result of an impending GC, which would deadlock if we do
1737         * not yield. Operation callbacks are supposed to happen rarely (seconds,
1738         * not milliseconds) so it is acceptable to yield at every callback.
1739         */
1740        if (cx->runtime->gcIsNeeded)
1741            js_GC(cx, GC_NORMAL);
1742    #ifdef JS_THREADSAFE    
1743        else
1744            JS_YieldRequest(cx);
1745    #endif
1746    
1747        JSOperationCallback cb = cx->operationCallback;
1748    
1749      JS_ASSERT(cx->operationCount <= 0);      /*
1750      JS_ASSERT(cx->operationLimit > 0);       * Important: Additional callbacks can occur inside the callback handler
1751         * if it re-enters the JS engine. The embedding must ensure that the
1752         * callback is disconnected before attempting such re-entry.
1753         */
1754    
1755      cx->operationCount = (int32) cx->operationLimit;      return !cb || cb(cx);
1756      if (cx->operationCallbackIsSet)  }
         return cx->operationCallback(cx);  
1757    
1758      if (cx->operationCallback) {  void
1759          /*  js_TriggerAllOperationCallbacks(JSRuntime *rt, JSBool gcLocked)
1760           * Invoke the deprecated branch callback. It may be called only when  {
1761           * the top-most frame is scripted or JSOPTION_NATIVE_BRANCH_CALLBACK      JSContext *acx, *iter;
1762           * is set.  #ifdef JS_THREADSAFE
1763           */      if (!gcLocked)
1764          script = cx->fp ? cx->fp->script : NULL;          JS_LOCK_GC(rt);
1765          if (script || JS_HAS_OPTION(cx, JSOPTION_NATIVE_BRANCH_CALLBACK))  #endif
1766              return ((JSBranchCallback) cx->operationCallback)(cx, script);      iter = NULL;
1767        while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)))
1768            JS_TriggerOperationCallback(acx);
1769    #ifdef JS_THREADSAFE
1770        if (!gcLocked)
1771            JS_UNLOCK_GC(rt);
1772    #endif
1773    }
1774    
1775    JSStackFrame *
1776    js_GetScriptedCaller(JSContext *cx, JSStackFrame *fp)
1777    {
1778        if (!fp)
1779            fp = js_GetTopStackFrame(cx);
1780        while (fp) {
1781            if (fp->script)
1782                return fp;
1783            fp = fp->down;
1784      }      }
1785      return JS_TRUE;      return NULL;
1786    }
1787    
1788    jsbytecode*
1789    js_GetCurrentBytecodePC(JSContext* cx)
1790    {
1791        jsbytecode *pc, *imacpc;
1792    
1793    #ifdef JS_TRACER
1794        if (JS_ON_TRACE(cx)) {
1795            pc = cx->bailExit->pc;
1796            imacpc = cx->bailExit->imacpc;
1797        } else
1798    #endif
1799        {
1800            JS_ASSERT_NOT_ON_TRACE(cx);  /* for static analysis */
1801            JSStackFrame* fp = cx->fp;
1802            if (fp && fp->regs) {
1803                pc = fp->regs->pc;
1804                imacpc = fp->imacpc;
1805            } else {
1806                return NULL;
1807            }
1808        }
1809    
1810        /*
1811         * If we are inside GetProperty_tn or similar, return a pointer to the
1812         * current instruction in the script, not the CALL instruction in the
1813         * imacro, for the benefit of callers doing bytecode inspection.
1814         */
1815        return (*pc == JSOP_CALL && imacpc) ? imacpc : pc;
1816  }  }

Legend:
Removed from v.459  
changed lines
  Added in v.460

  ViewVC Help
Powered by ViewVC 1.1.24