📄 jsgc.c
字号:
uint32 *leakedroots = (uint32 *)arg; JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr; (*leakedroots)++; fprintf(stderr, "JS engine warning: leaking GC root \'%s\' at %p\n", rhe->name ? (char *)rhe->name : "", rhe->root); return JS_DHASH_NEXT;}static voidCheckLeakedRoots(JSRuntime *rt){ uint32 leakedroots = 0; /* Warn (but don't assert) debug builds of any remaining roots. */ JS_DHashTableEnumerate(&rt->gcRootsHash, js_root_printer, &leakedroots); if (leakedroots > 0) { if (leakedroots == 1) { fprintf(stderr,"JS engine warning: 1 GC root remains after destroying the JSRuntime.\n"" This root may point to freed memory. Objects reachable\n"" through it have not been finalized.\n"); } else { fprintf(stderr,"JS engine warning: %lu GC roots remain after destroying the JSRuntime.\n"" These roots may point to freed memory. Objects reachable\n"" through them have not been finalized.\n", (unsigned long) leakedroots); } }}typedef struct NamedRootDumpArgs { void (*dump)(const char *name, void *rp, void *data); void *data;} NamedRootDumpArgs;JS_STATIC_DLL_CALLBACK(JSDHashOperator)js_named_root_dumper(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, void *arg){ NamedRootDumpArgs *args = (NamedRootDumpArgs *) arg; JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr; if (rhe->name) args->dump(rhe->name, rhe->root, args->data); return JS_DHASH_NEXT;}voidjs_DumpNamedRoots(JSRuntime *rt, void (*dump)(const char *name, void *rp, void *data), void *data){ NamedRootDumpArgs args; args.dump = dump; args.data = data; JS_DHashTableEnumerate(&rt->gcRootsHash, js_named_root_dumper, &args);}#endif /* DEBUG */typedef struct GCRootMapArgs { JSGCRootMapFun map; void *data;} GCRootMapArgs;JS_STATIC_DLL_CALLBACK(JSDHashOperator)js_gcroot_mapper(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, void *arg){ GCRootMapArgs *args = (GCRootMapArgs *) arg; JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr; intN mapflags; JSDHashOperator op; mapflags = args->map(rhe->root, rhe->name, args->data);#if JS_MAP_GCROOT_NEXT == JS_DHASH_NEXT && \ JS_MAP_GCROOT_STOP == JS_DHASH_STOP && \ JS_MAP_GCROOT_REMOVE == JS_DHASH_REMOVE op = (JSDHashOperator)mapflags;#else op = JS_DHASH_NEXT; if (mapflags & JS_MAP_GCROOT_STOP) op |= JS_DHASH_STOP; if (mapflags & JS_MAP_GCROOT_REMOVE) op |= JS_DHASH_REMOVE;#endif return op;}uint32js_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data){ GCRootMapArgs args; uint32 rv; args.map = map; args.data = data; JS_LOCK_GC(rt); rv = JS_DHashTableEnumerate(&rt->gcRootsHash, js_gcroot_mapper, &args); JS_UNLOCK_GC(rt); return rv;}JSBooljs_RegisterCloseableIterator(JSContext *cx, JSObject *obj){ JSRuntime *rt; JSBool ok; rt = cx->runtime; JS_ASSERT(!rt->gcRunning); JS_LOCK_GC(rt); ok = AddToPtrTable(cx, &rt->gcIteratorTable, &iteratorTableInfo, obj); JS_UNLOCK_GC(rt); return ok;}static voidCloseIteratorStates(JSContext *cx){ JSRuntime *rt; size_t count, newCount, i; void **array; JSObject *obj; rt = cx->runtime; count = rt->gcIteratorTable.count; array = rt->gcIteratorTable.array; newCount = 0; for (i = 0; i != count; ++i) { obj = (JSObject *)array[i]; if (js_IsAboutToBeFinalized(cx, obj)) js_CloseIteratorState(cx, obj); else array[newCount++] = obj; } ShrinkPtrTable(&rt->gcIteratorTable, &iteratorTableInfo, newCount);}#if JS_HAS_GENERATORSvoidjs_RegisterGenerator(JSContext *cx, JSGenerator *gen){ JSRuntime *rt; rt = cx->runtime; JS_ASSERT(!rt->gcRunning); JS_ASSERT(rt->state != JSRTS_LANDING); JS_ASSERT(gen->state == JSGEN_NEWBORN); JS_LOCK_GC(rt); gen->next = rt->gcCloseState.reachableList; rt->gcCloseState.reachableList = gen; METER(rt->gcStats.nclose++); METER(rt->gcStats.maxnclose = JS_MAX(rt->gcStats.maxnclose, rt->gcStats.nclose)); JS_UNLOCK_GC(rt);}/* * We do not run close hooks when the parent scope of the generator instance * becomes unreachable to prevent denial-of-service and resource leakage from * misbehaved generators. * * Called from the GC. */static JSBoolCanScheduleCloseHook(JSGenerator *gen){ JSObject *parent; JSBool canSchedule; /* Avoid OBJ_GET_PARENT overhead as we are in GC. */ parent = JSVAL_TO_OBJECT(gen->obj->slots[JSSLOT_PARENT]); canSchedule = *js_GetGCThingFlags(parent) & GCF_MARK;#ifdef DEBUG_igor if (!canSchedule) { fprintf(stderr, "GEN: Kill without schedule, gen=%p parent=%p\n", (void *)gen, (void *)parent); }#endif return canSchedule;}/* * Check if we should delay execution of the close hook. * * Called outside GC or any locks. * * XXX The current implementation is a hack that embeds the knowledge of the * browser embedding pending the resolution of bug 352788. In the browser we * must not close any generators that came from a page that is currently in * the browser history. We detect that using the fact in the browser the scope * is history if scope->outerObject->innerObject != scope. */static JSBoolShouldDeferCloseHook(JSContext *cx, JSGenerator *gen, JSBool *defer){ JSObject *parent, *obj; JSClass *clasp; JSExtendedClass *xclasp; /* * This is called outside any locks, so use thread-safe macros to access * parent and classes. */ *defer = JS_FALSE; parent = OBJ_GET_PARENT(cx, gen->obj); clasp = OBJ_GET_CLASS(cx, parent); if (clasp->flags & JSCLASS_IS_EXTENDED) { xclasp = (JSExtendedClass *)clasp; if (xclasp->outerObject) { obj = xclasp->outerObject(cx, parent); if (!obj) return JS_FALSE; OBJ_TO_INNER_OBJECT(cx, obj); if (!obj) return JS_FALSE; *defer = obj != parent; } }#ifdef DEBUG_igor if (*defer) { fprintf(stderr, "GEN: deferring, gen=%p parent=%p\n", (void *)gen, (void *)parent); }#endif return JS_TRUE;}/* * Find all unreachable generators and move them to the todo queue from * rt->gcCloseState.reachableList to execute thier close hooks after the GC * cycle completes. To ensure liveness during the sweep phase we mark all * generators we are going to close later. */static voidFindAndMarkObjectsToClose(JSContext *cx, JSGCInvocationKind gckind, JSGenerator **todoQueueTail){ JSRuntime *rt; JSGenerator *todo, **genp, *gen; rt = cx->runtime; todo = NULL; genp = &rt->gcCloseState.reachableList; while ((gen = *genp) != NULL) { if (*js_GetGCThingFlags(gen->obj) & GCF_MARK) { genp = &gen->next; } else { /* Generator must not be executing when it becomes unreachable. */ JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN || gen->state == JSGEN_CLOSED); *genp = gen->next; if (gen->state == JSGEN_OPEN && js_FindFinallyHandler(gen->frame.script, gen->frame.pc) && CanScheduleCloseHook(gen)) { /* * Generator yielded inside a try with a finally block. * Schedule it for closing. * * We keep generators that yielded outside try-with-finally * with gen->state == JSGEN_OPEN. The finalizer must deal with * open generators as we may skip the close hooks, see below. */ gen->next = NULL; *todoQueueTail = gen; todoQueueTail = &gen->next; if (!todo) todo = gen; METER(JS_ASSERT(rt->gcStats.nclose)); METER(rt->gcStats.nclose--); METER(rt->gcStats.closelater++); METER(rt->gcStats.maxcloselater = JS_MAX(rt->gcStats.maxcloselater, rt->gcStats.closelater)); } } } if (gckind == GC_LAST_CONTEXT) { /* * Remove scheduled hooks on shutdown as it is too late to run them: * we do not allow execution of arbitrary scripts at this point. */ rt->gcCloseState.todoQueue = NULL; } else { /* * Mark just-found unreachable generators *after* we scan the global * list to prevent a generator that refers to other unreachable * generators from keeping them on gcCloseState.reachableList. */ for (gen = todo; gen; gen = gen->next) GC_MARK(cx, gen->obj, "newly scheduled generator"); }}/* * Mark unreachable generators already scheduled to close and return the tail * pointer to JSGCCloseState.todoQueue. */static JSGenerator **MarkScheduledGenerators(JSContext *cx){ JSRuntime *rt; JSGenerator **genp, *gen; rt = cx->runtime; genp = &rt->gcCloseState.todoQueue; while ((gen = *genp) != NULL) { if (CanScheduleCloseHook(gen)) { GC_MARK(cx, gen->obj, "scheduled generator"); genp = &gen->next; } else { /* Discard the generator from the list if its schedule is over. */ *genp = gen->next; METER(JS_ASSERT(rt->gcStats.closelater > 0)); METER(rt->gcStats.closelater--); } } return genp;}#ifdef JS_THREADSAFE# define GC_RUNNING_CLOSE_HOOKS_PTR(cx) \ (&(cx)->thread->gcRunningCloseHooks)#else# define GC_RUNNING_CLOSE_HOOKS_PTR(cx) \ (&(cx)->runtime->gcCloseState.runningCloseHook)#endiftypedef struct JSTempCloseList { JSTempValueRooter tvr; JSGenerator *head;} JSTempCloseList;JS_STATIC_DLL_CALLBACK(void)mark_temp_close_list(JSContext *cx, JSTempValueRooter *tvr){ JSTempCloseList *list = (JSTempCloseList *)tvr; JSGenerator *gen; for (gen = list->head; gen; gen = gen->next) GC_MARK(cx, gen->obj, "temp list generator");}#define JS_PUSH_TEMP_CLOSE_LIST(cx, tempList) \ JS_PUSH_TEMP_ROOT_MARKER(cx, mark_temp_close_list, &(tempList)->tvr)#define JS_POP_TEMP_CLOSE_LIST(cx, tempList) \ JS_BEGIN_MACRO \ JS_ASSERT((tempList)->tvr.u.marker == mark_temp_close_list); \ JS_POP_TEMP_ROOT(cx, &(tempList)->tvr); \ JS_END_MACROJSBooljs_RunCloseHooks(JSContext *cx){ JSRuntime *rt; JSTempCloseList tempList; JSStackFrame *fp; JSGenerator **genp, *gen; JSBool ok, defer;#if JS_GCMETER uint32 deferCount;#endif rt = cx->runtime; /* * It is OK to access todoQueue outside the lock here. When many threads * update the todo list, accessing some older value of todoQueue in the * worst case just delays the excution of close hooks. */ if (!rt->gcCloseState.todoQueue) return JS_TRUE; /* * To prevent an infinite loop when a close hook creats more objects with * close hooks and then triggers GC we ignore recursive invocations of * js_RunCloseHooks and limit number of hooks to execute to the initial * size of the list. */ if (*GC_RUNNING_CLOSE_HOOKS_PTR(cx)) return JS_TRUE; *GC_RUNNING_CLOSE_HOOKS_PTR(cx) = JS_TRUE; JS_LOCK_GC(rt); tempList.head = rt->gcCloseState.todoQueue; JS_PUSH_TEMP_CLOSE_LIST(cx, &tempList); rt->gcCloseState.todoQueue = NULL; METER(rt->gcStats.closelater = 0); rt->gcPoke = JS_TRUE; JS_UNLOCK_GC(rt); /* * Set aside cx->fp since we do not want a close hook using caller or * other means to backtrace into whatever stack might be active when * running the hook. We store the current frame on the dormant list to
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -