Bug 807480 - Add Rooted<T> roots to PerThread state. r=luke

Currently there are a number of global fields in JSRuntime* which are basically
tracking per-thread state.  This makes sense on the current trunk since there
is only ever a single thread associated with a runtime, but as Parallel JS (nee
Rivertrail) starts to land this assumption no longer holds.

This patch makes a struct, currently called |JS::PerThreadData|, that stores
per-thread data from the runtime.  There is one instance of this struct
embedded in "Runtime" itself (the field |mainThread|).  For now I have only
migrated the debug GC fields into |PerThread|, those are the ones causing me
immediate pain.  Eventually more fields will want to move into there.

The eventual goal is to distinguish thread-safe code, which will take as
argument a |JS::PerThread*|, from non-thread-safe code, which will take a
|JSRuntime*| or |JSContext*|.
This commit is contained in:
Nicholas D. Matsakis 2012-11-06 22:32:01 -05:00
parent 6240239870
commit 0f88896f1a
11 changed files with 190 additions and 76 deletions

View File

@ -519,27 +519,23 @@ class Rooted : public RootedBase<T>
{ {
#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING) #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
ContextFriendFields *cx = ContextFriendFields::get(cxArg); ContextFriendFields *cx = ContextFriendFields::get(cxArg);
commonInit(cx->thingGCRooters);
ThingRootKind kind = RootMethods<T>::kind();
this->stack = reinterpret_cast<Rooted<T>**>(&cx->thingGCRooters[kind]);
this->prev = *stack;
*stack = this;
JS_ASSERT(!RootMethods<T>::poisoned(ptr));
#endif #endif
} }
void init(JSRuntime *rtArg) void init(JSRuntime *rtArg)
{ {
#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING) #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
RuntimeFriendFields *rt = const_cast<RuntimeFriendFields *>(RuntimeFriendFields::get(rtArg)); PerThreadDataFriendFields *pt = PerThreadDataFriendFields::getMainThread(rtArg);
commonInit(pt->thingGCRooters);
#endif
}
ThingRootKind kind = RootMethods<T>::kind(); void init(js::PerThreadData *ptArg)
this->stack = reinterpret_cast<Rooted<T>**>(&rt->thingGCRooters[kind]); {
this->prev = *stack; #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
*stack = this; PerThreadDataFriendFields *pt = PerThreadDataFriendFields::get(ptArg);
commonInit(pt->thingGCRooters);
JS_ASSERT(!RootMethods<T>::poisoned(ptr));
#endif #endif
} }
@ -576,6 +572,22 @@ class Rooted : public RootedBase<T>
init(cx); init(cx);
} }
Rooted(js::PerThreadData *pt
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: ptr(RootMethods<T>::initial())
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
init(pt);
}
Rooted(js::PerThreadData *pt, T initial
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: ptr(initial)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
init(pt);
}
template <typename S> template <typename S>
Rooted(JSContext *cx, const Return<S> &initial Rooted(JSContext *cx, const Return<S> &initial
MOZ_GUARD_OBJECT_NOTIFIER_PARAM) MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
@ -585,6 +597,15 @@ class Rooted : public RootedBase<T>
init(cx); init(cx);
} }
template <typename S>
Rooted(js::PerThreadData *pt, const Return<S> &initial
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: ptr(initial.ptr_)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
init(pt);
}
~Rooted() ~Rooted()
{ {
#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING) #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
@ -625,6 +646,17 @@ class Rooted : public RootedBase<T>
} }
private: private:
void commonInit(Rooted<void*> **thingGCRooters) {
#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
ThingRootKind kind = RootMethods<T>::kind();
this->stack = reinterpret_cast<Rooted<T>**>(&thingGCRooters[kind]);
this->prev = *stack;
*stack = this;
JS_ASSERT(!RootMethods<T>::poisoned(ptr));
#endif
}
#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING) #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
Rooted<T> **stack, *prev; Rooted<T> **stack, *prev;
#endif #endif

View File

@ -676,33 +676,36 @@ static JSBool js_NewRuntimeWasCalled = JS_FALSE;
/* /*
* Thread Local Storage slot for storing the runtime for a thread. * Thread Local Storage slot for storing the runtime for a thread.
*/ */
namespace js {
mozilla::ThreadLocal<PerThreadData *> TlsPerThreadData;
}
namespace JS { namespace JS {
mozilla::ThreadLocal<JSRuntime *> TlsRuntime;
#ifdef DEBUG #ifdef DEBUG
JS_FRIEND_API(void) JS_FRIEND_API(void)
EnterAssertNoGCScope() EnterAssertNoGCScope()
{ {
++TlsRuntime.get()->gcAssertNoGCDepth; ++TlsPerThreadData.get()->gcAssertNoGCDepth;
} }
JS_FRIEND_API(void) JS_FRIEND_API(void)
LeaveAssertNoGCScope() LeaveAssertNoGCScope()
{ {
--TlsRuntime.get()->gcAssertNoGCDepth; --TlsPerThreadData.get()->gcAssertNoGCDepth;
JS_ASSERT(TlsRuntime.get()->gcAssertNoGCDepth >= 0); JS_ASSERT(TlsPerThreadData.get()->gcAssertNoGCDepth >= 0);
} }
JS_FRIEND_API(bool) JS_FRIEND_API(bool)
InNoGCScope() InNoGCScope()
{ {
return TlsRuntime.get()->gcAssertNoGCDepth > 0; return TlsPerThreadData.get()->gcAssertNoGCDepth > 0;
} }
JS_FRIEND_API(bool) JS_FRIEND_API(bool)
NeedRelaxedRootChecks() NeedRelaxedRootChecks()
{ {
return TlsRuntime.get()->gcRelaxRootChecks; return TlsPerThreadData.get()->gcRelaxRootChecks;
} }
#else #else
JS_FRIEND_API(void) EnterAssertNoGCScope() {} JS_FRIEND_API(void) EnterAssertNoGCScope() {}
@ -715,8 +718,17 @@ JS_FRIEND_API(bool) NeedRelaxedRootChecks() { return false; }
static const JSSecurityCallbacks NullSecurityCallbacks = { }; static const JSSecurityCallbacks NullSecurityCallbacks = { };
js::PerThreadData::PerThreadData(JSRuntime *runtime)
: runtime_(runtime)
#ifdef DEBUG
, gcRelaxRootChecks(false)
, gcAssertNoGCDepth(0)
#endif
{}
JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads) JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
: atomsCompartment(NULL), : mainThread(this),
atomsCompartment(NULL),
#ifdef JS_THREADSAFE #ifdef JS_THREADSAFE
ownerThread_(NULL), ownerThread_(NULL),
#endif #endif
@ -788,10 +800,6 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
gcSliceBudget(SliceBudget::Unlimited), gcSliceBudget(SliceBudget::Unlimited),
gcIncrementalEnabled(true), gcIncrementalEnabled(true),
gcExactScanningEnabled(true), gcExactScanningEnabled(true),
#ifdef DEBUG
gcRelaxRootChecks(false),
gcAssertNoGCDepth(0),
#endif
gcPoke(false), gcPoke(false),
heapState(Idle), heapState(Idle),
#ifdef JS_GC_ZEAL #ifdef JS_GC_ZEAL
@ -885,16 +893,12 @@ JSRuntime::init(uint32_t maxbytes)
ownerThread_ = PR_GetCurrentThread(); ownerThread_ = PR_GetCurrentThread();
#endif #endif
JS::TlsRuntime.set(this); js::TlsPerThreadData.set(&mainThread);
#ifdef JS_METHODJIT_SPEW #ifdef JS_METHODJIT_SPEW
JMCheckLogging(); JMCheckLogging();
#endif #endif
#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
PodArrayZero(thingGCRooters);
#endif
if (!js_InitGC(this, maxbytes)) if (!js_InitGC(this, maxbytes))
return false; return false;
@ -1018,9 +1022,9 @@ JSRuntime::setOwnerThread()
JS_ASSERT(ownerThread_ == (void *)0xc1ea12); /* "clear" */ JS_ASSERT(ownerThread_ == (void *)0xc1ea12); /* "clear" */
JS_ASSERT(requestDepth == 0); JS_ASSERT(requestDepth == 0);
JS_ASSERT(js_NewRuntimeWasCalled); JS_ASSERT(js_NewRuntimeWasCalled);
JS_ASSERT(JS::TlsRuntime.get() == NULL); JS_ASSERT(js::TlsPerThreadData.get() == NULL);
ownerThread_ = PR_GetCurrentThread(); ownerThread_ = PR_GetCurrentThread();
JS::TlsRuntime.set(this); js::TlsPerThreadData.set(&mainThread);
nativeStackBase = GetNativeStackBase(); nativeStackBase = GetNativeStackBase();
if (nativeStackQuota) if (nativeStackQuota)
JS_SetNativeStackQuota(this, nativeStackQuota); JS_SetNativeStackQuota(this, nativeStackQuota);
@ -1033,7 +1037,7 @@ JSRuntime::clearOwnerThread()
JS_ASSERT(requestDepth == 0); JS_ASSERT(requestDepth == 0);
JS_ASSERT(js_NewRuntimeWasCalled); JS_ASSERT(js_NewRuntimeWasCalled);
ownerThread_ = (void *)0xc1ea12; /* "clear" */ ownerThread_ = (void *)0xc1ea12; /* "clear" */
JS::TlsRuntime.set(NULL); js::TlsPerThreadData.set(NULL);
nativeStackBase = 0; nativeStackBase = 0;
#if JS_STACK_GROWTH_DIRECTION > 0 #if JS_STACK_GROWTH_DIRECTION > 0
nativeStackLimit = UINTPTR_MAX; nativeStackLimit = UINTPTR_MAX;
@ -1047,7 +1051,7 @@ JSRuntime::abortIfWrongThread() const
{ {
if (ownerThread_ != PR_GetCurrentThread()) if (ownerThread_ != PR_GetCurrentThread())
MOZ_CRASH(); MOZ_CRASH();
if (this != JS::TlsRuntime.get()) if (!js::TlsPerThreadData.get()->associatedWith(this))
MOZ_CRASH(); MOZ_CRASH();
} }
@ -1055,7 +1059,7 @@ JS_FRIEND_API(void)
JSRuntime::assertValidThread() const JSRuntime::assertValidThread() const
{ {
JS_ASSERT(ownerThread_ == PR_GetCurrentThread()); JS_ASSERT(ownerThread_ == PR_GetCurrentThread());
JS_ASSERT(this == JS::TlsRuntime.get()); JS_ASSERT(js::TlsPerThreadData.get()->associatedWith(this));
} }
#endif /* JS_THREADSAFE */ #endif /* JS_THREADSAFE */
@ -1092,7 +1096,7 @@ JS_NewRuntime(uint32_t maxbytes, JSUseHelperThreads useHelperThreads)
InitMemorySubsystem(); InitMemorySubsystem();
if (!JS::TlsRuntime.init()) if (!js::TlsPerThreadData.init())
return NULL; return NULL;
js_NewRuntimeWasCalled = JS_TRUE; js_NewRuntimeWasCalled = JS_TRUE;

View File

@ -2889,8 +2889,6 @@ JS_IsInRequest(JSRuntime *rt);
namespace JS { namespace JS {
extern mozilla::ThreadLocal<JSRuntime *> TlsRuntime;
inline bool inline bool
IsPoisonedId(jsid iden) IsPoisonedId(jsid iden)
{ {

View File

@ -389,8 +389,70 @@ struct JSAtomState
#define NAME_OFFSET(name) offsetof(JSAtomState, name) #define NAME_OFFSET(name) offsetof(JSAtomState, name)
#define OFFSET_TO_NAME(rt,off) (*(js::FixedHeapPtr<js::PropertyName>*)((char*)&(rt)->atomState + (off))) #define OFFSET_TO_NAME(rt,off) (*(js::FixedHeapPtr<js::PropertyName>*)((char*)&(rt)->atomState + (off)))
namespace js {
/*
* Encapsulates portions of the runtime/context that are tied to a
* single active thread. Normally, as most JS is single-threaded,
* there is only one instance of this struct, embedded in the
* JSRuntime as the field |mainThread|. During Parallel JS sections,
* however, there will be one instance per worker thread.
*
* The eventual plan is to designate thread-safe portions of the
* interpreter and runtime by having them take |PerThreadData*|
* arguments instead of |JSContext*| or |JSRuntime*|.
*/
class PerThreadData : public js::PerThreadDataFriendFields
{
/*
* Backpointer to the full shared JSRuntime* with which this
* thread is associaed. This is private because accessing the
* fields of this runtime can provoke race conditions, so the
* intention is that access will be mediated through safe
* functions like |associatedWith()| below.
*/
JSRuntime *runtime_;
public:
/*
* We save all conservative scanned roots in this vector so that
* conservative scanning can be "replayed" deterministically. In DEBUG mode,
* this allows us to run a non-incremental GC after every incremental GC to
* ensure that no objects were missed.
*/
#ifdef DEBUG
struct SavedGCRoot {
void *thing;
JSGCTraceKind kind;
SavedGCRoot(void *thing, JSGCTraceKind kind) : thing(thing), kind(kind) {}
};
js::Vector<SavedGCRoot, 0, js::SystemAllocPolicy> gcSavedRoots;
bool gcRelaxRootChecks;
int gcAssertNoGCDepth;
#endif
PerThreadData(JSRuntime *runtime);
bool associatedWith(const JSRuntime *rt) { return runtime_ == rt; }
};
} // namespace js
struct JSRuntime : js::RuntimeFriendFields struct JSRuntime : js::RuntimeFriendFields
{ {
/* Per-thread data for the main thread that is associated with
* this JSRuntime, as opposed to any worker threads used in
* parallel sections. See definition of |PerThreadData| struct
* above for more details.
*
* NB: This field is statically asserted to be at offset
* sizeof(RuntimeFriendFields). See
* PerThreadDataFriendFields::getMainThread.
*/
js::PerThreadData mainThread;
/* Default compartment. */ /* Default compartment. */
JSCompartment *atomsCompartment; JSCompartment *atomsCompartment;
@ -654,25 +716,6 @@ struct JSRuntime : js::RuntimeFriendFields
*/ */
bool gcExactScanningEnabled; bool gcExactScanningEnabled;
/*
* We save all conservative scanned roots in this vector so that
* conservative scanning can be "replayed" deterministically. In DEBUG mode,
* this allows us to run a non-incremental GC after every incremental GC to
* ensure that no objects were missed.
*/
#ifdef DEBUG
struct SavedGCRoot {
void *thing;
JSGCTraceKind kind;
SavedGCRoot(void *thing, JSGCTraceKind kind) : thing(thing), kind(kind) {}
};
js::Vector<SavedGCRoot, 0, js::SystemAllocPolicy> gcSavedRoots;
bool gcRelaxRootChecks;
int gcAssertNoGCDepth;
#endif
bool gcPoke; bool gcPoke;
enum HeapState { enum HeapState {

View File

@ -22,6 +22,16 @@
using namespace js; using namespace js;
using namespace JS; using namespace JS;
// Required by PerThreadDataFriendFields::getMainThread()
JS_STATIC_ASSERT(offsetof(JSRuntime, mainThread) == sizeof(RuntimeFriendFields));
PerThreadDataFriendFields::PerThreadDataFriendFields()
{
#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
PodArrayZero(thingGCRooters);
#endif
}
JS_FRIEND_API(void) JS_FRIEND_API(void)
JS_SetSourceHook(JSRuntime *rt, JS_SourceHook hook) JS_SetSourceHook(JSRuntime *rt, JS_SourceHook hook)
{ {

View File

@ -183,6 +183,8 @@ JS_SetSourceHook(JSRuntime *rt, JS_SourceHook hook);
namespace js { namespace js {
extern mozilla::ThreadLocal<PerThreadData *> TlsPerThreadData;
inline JSRuntime * inline JSRuntime *
GetRuntime(const JSContext *cx) GetRuntime(const JSContext *cx)
{ {

View File

@ -992,7 +992,7 @@ MarkExactStackRoots(JSTracer *trc)
for (ContextIter cx(trc->runtime); !cx.done(); cx.next()) { for (ContextIter cx(trc->runtime); !cx.done(); cx.next()) {
MarkExactStackRooters(trc, cx->thingGCRooters[i], ThingRootKind(i)); MarkExactStackRooters(trc, cx->thingGCRooters[i], ThingRootKind(i));
} }
MarkExactStackRooters(trc, rt->thingGCRooters[i], ThingRootKind(i)); MarkExactStackRooters(trc, rt->mainThread.thingGCRooters[i], ThingRootKind(i));
} }
} }
#endif /* JSGC_USE_EXACT_ROOTING */ #endif /* JSGC_USE_EXACT_ROOTING */
@ -1125,7 +1125,8 @@ MarkIfGCThingWord(JSTracer *trc, uintptr_t w)
#ifdef DEBUG #ifdef DEBUG
if (trc->runtime->gcIncrementalState == MARK_ROOTS) if (trc->runtime->gcIncrementalState == MARK_ROOTS)
trc->runtime->gcSavedRoots.append(JSRuntime::SavedGCRoot(thing, traceKind)); trc->runtime->mainThread.gcSavedRoots.append(
PerThreadData::SavedGCRoot(thing, traceKind));
#endif #endif
return CGCT_VALID; return CGCT_VALID;
@ -1186,8 +1187,8 @@ MarkConservativeStackRoots(JSTracer *trc, bool useSavedRoots)
#ifdef DEBUG #ifdef DEBUG
if (useSavedRoots) { if (useSavedRoots) {
for (JSRuntime::SavedGCRoot *root = rt->gcSavedRoots.begin(); for (PerThreadData::SavedGCRoot *root = rt->mainThread.gcSavedRoots.begin();
root != rt->gcSavedRoots.end(); root != rt->mainThread.gcSavedRoots.end();
root++) root++)
{ {
JS_SET_TRACING_NAME(trc, "cstack"); JS_SET_TRACING_NAME(trc, "cstack");
@ -1197,7 +1198,7 @@ MarkConservativeStackRoots(JSTracer *trc, bool useSavedRoots)
} }
if (rt->gcIncrementalState == MARK_ROOTS) if (rt->gcIncrementalState == MARK_ROOTS)
rt->gcSavedRoots.clearAndFree(); rt->mainThread.gcSavedRoots.clearAndFree();
#endif #endif
ConservativeGCData *cgcd = &rt->conservativeGC; ConservativeGCData *cgcd = &rt->conservativeGC;
@ -4912,7 +4913,8 @@ CheckStackRoot(JSTracer *trc, uintptr_t *w)
bool matched = false; bool matched = false;
JSRuntime *rt = trc->runtime; JSRuntime *rt = trc->runtime;
for (unsigned i = 0; i < THING_ROOT_LIMIT; i++) { for (unsigned i = 0; i < THING_ROOT_LIMIT; i++) {
CheckStackRootThings(w, rt->thingGCRooters[i], ThingRootKind(i), &matched); CheckStackRootThings(w, rt->mainThread.thingGCRooters[i],
ThingRootKind(i), &matched);
for (ContextIter cx(rt); !cx.done(); cx.next()) { for (ContextIter cx(rt); !cx.done(); cx.next()) {
CheckStackRootThings(w, cx->thingGCRooters[i], ThingRootKind(i), &matched); CheckStackRootThings(w, cx->thingGCRooters[i], ThingRootKind(i), &matched);
SkipRoot *skip = cx->skipGCRooters; SkipRoot *skip = cx->skipGCRooters;

View File

@ -307,14 +307,6 @@ struct RuntimeFriendFields {
/* Limit pointer for checking native stack consumption. */ /* Limit pointer for checking native stack consumption. */
uintptr_t nativeStackLimit; uintptr_t nativeStackLimit;
#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
/*
* Stack allocated GC roots for stack GC heap pointers, which may be
* overwritten if moved during a GC.
*/
Rooted<void*> *thingGCRooters[THING_ROOT_LIMIT];
#endif
RuntimeFriendFields() RuntimeFriendFields()
: interrupt(0), : interrupt(0),
nativeStackLimit(0) { } nativeStackLimit(0) { }
@ -324,6 +316,32 @@ struct RuntimeFriendFields {
} }
}; };
class PerThreadData;
struct PerThreadDataFriendFields
{
PerThreadDataFriendFields();
#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
/*
* Stack allocated GC roots for stack GC heap pointers, which may be
* overwritten if moved during a GC.
*/
Rooted<void*> *thingGCRooters[THING_ROOT_LIMIT];
#endif
static PerThreadDataFriendFields *get(js::PerThreadData *pt) {
return reinterpret_cast<PerThreadDataFriendFields *>(pt);
}
static PerThreadDataFriendFields *getMainThread(JSRuntime *rt) {
// mainThread must always appear directly after |RuntimeFriendFields|.
// Tested by a JS_STATIC_ASSERT in |jsfriendapi.cpp|
return reinterpret_cast<PerThreadDataFriendFields *>(
reinterpret_cast<char*>(rt) + sizeof(RuntimeFriendFields));
}
};
} /* namespace js */ } /* namespace js */
#endif /* __cplusplus */ #endif /* __cplusplus */

View File

@ -3417,7 +3417,7 @@ RelaxRootChecks(JSContext *cx, unsigned argc, jsval *vp)
} }
#ifdef DEBUG #ifdef DEBUG
cx->runtime->gcRelaxRootChecks = true; cx->runtime->mainThread.gcRelaxRootChecks = true;
#endif #endif
return true; return true;

View File

@ -1402,7 +1402,8 @@ StackIter::settleOnNewState()
} }
StackIter::StackIter(JSContext *cx, SavedOption savedOption) StackIter::StackIter(JSContext *cx, SavedOption savedOption)
: maybecx_(cx), : perThread_(&cx->runtime->mainThread),
maybecx_(cx),
savedOption_(savedOption), savedOption_(savedOption),
script_(cx, NULL) script_(cx, NULL)
#ifdef JS_ION #ifdef JS_ION
@ -1426,7 +1427,9 @@ StackIter::StackIter(JSContext *cx, SavedOption savedOption)
} }
StackIter::StackIter(JSRuntime *rt, StackSegment &seg) StackIter::StackIter(JSRuntime *rt, StackSegment &seg)
: maybecx_(NULL), savedOption_(STOP_AT_SAVED), : perThread_(&rt->mainThread),
maybecx_(NULL),
savedOption_(STOP_AT_SAVED),
script_(rt, NULL) script_(rt, NULL)
#ifdef JS_ION #ifdef JS_ION
, ionActivations_(rt), , ionActivations_(rt),
@ -1444,14 +1447,15 @@ StackIter::StackIter(JSRuntime *rt, StackSegment &seg)
} }
StackIter::StackIter(const StackIter &other) StackIter::StackIter(const StackIter &other)
: maybecx_(other.maybecx_), : perThread_(other.perThread_),
maybecx_(other.maybecx_),
savedOption_(other.savedOption_), savedOption_(other.savedOption_),
state_(other.state_), state_(other.state_),
fp_(other.fp_), fp_(other.fp_),
calls_(other.calls_), calls_(other.calls_),
seg_(other.seg_), seg_(other.seg_),
pc_(other.pc_), pc_(other.pc_),
script_(other.maybecx_ ? other.maybecx_->runtime : TlsRuntime.get(), other.script_), script_(perThread_, other.script_),
args_(other.args_) args_(other.args_)
#ifdef JS_ION #ifdef JS_ION
, ionActivations_(other.ionActivations_), , ionActivations_(other.ionActivations_),

View File

@ -1712,6 +1712,7 @@ class GeneratorFrameGuard : public FrameGuard
class StackIter class StackIter
{ {
friend class ContextStack; friend class ContextStack;
PerThreadData *perThread_;
JSContext *maybecx_; JSContext *maybecx_;
public: public:
enum SavedOption { STOP_AT_SAVED, GO_THROUGH_SAVED }; enum SavedOption { STOP_AT_SAVED, GO_THROUGH_SAVED };