/* * During incremental GC, we return from drainMarkStack without having processed * the entire stack. At that point, JS code can run and reallocate slot arrays * that are stored on the stack. To prevent this from happening, we replace all * ValueArrayTag stack items with SavedValueArrayTag. In the latter, slots * pointers are replaced with slot indexes. * * We also replace the slot array end pointer (which can be derived from the obj * pointer) with the object's class. During JS executation, array slowification * can cause the layout of slots to change. We can observe that slowification * happened if the class changed; in that case, we completely rescan the array. */ void GCMarker::saveValueRanges() { for (uintptr_t *p = stack.tos; p > stack.stack; ) { uintptr_t tag = *--p & StackTagMask; if (tag == ValueArrayTag) { p -= 2; SlotArrayLayout *arr = reinterpret_cast<SlotArrayLayout *>(p); JSObject *obj = arr->obj; if (obj->getClass() == &ArrayClass) { HeapSlot *vp = obj->getDenseArrayElements(); JS_ASSERT(arr->start >= vp && arr->end == vp + obj->getDenseArrayInitializedLength()); arr->index = arr->start - vp; } else { HeapSlot *vp = obj->fixedSlots(); unsigned nfixed = obj->numFixedSlots(); if (arr->start == arr->end) { arr->index = obj->slotSpan(); } else if (arr->start >= vp && arr->start < vp + nfixed) { JS_ASSERT(arr->end == vp + Min(nfixed, obj->slotSpan())); arr->index = arr->start - vp; } else { JS_ASSERT(arr->start >= obj->slots && arr->end == obj->slots + obj->slotSpan() - nfixed); arr->index = (arr->start - obj->slots) + nfixed; } } arr->clasp = obj->getClass(); p[2] |= SavedValueArrayTag; } else if (tag == SavedValueArrayTag) { p -= 2; } } }
inline void GCMarker::processMarkStackTop(SliceBudget &budget) { /* * The function uses explicit goto and implements the scanning of the * object directly. It allows to eliminate the tail recursion and * significantly improve the marking performance, see bug 641025. */ HeapSlot *vp, *end; JSObject *obj; uintptr_t addr = stack.pop(); uintptr_t tag = addr & StackTagMask; addr &= ~StackTagMask; if (tag == ValueArrayTag) { JS_STATIC_ASSERT(ValueArrayTag == 0); JS_ASSERT(!(addr & Cell::CellMask)); obj = reinterpret_cast<JSObject *>(addr); uintptr_t addr2 = stack.pop(); uintptr_t addr3 = stack.pop(); JS_ASSERT(addr2 <= addr3); JS_ASSERT((addr3 - addr2) % sizeof(Value) == 0); vp = reinterpret_cast<HeapSlot *>(addr2); end = reinterpret_cast<HeapSlot *>(addr3); goto scan_value_array; } if (tag == ObjectTag) { obj = reinterpret_cast<JSObject *>(addr); JS_COMPARTMENT_ASSERT(runtime, obj); goto scan_obj; } processMarkStackOther(budget, tag, addr); return; scan_value_array: JS_ASSERT(vp <= end); while (vp != end) { const Value &v = *vp++; if (v.isString()) { JSString *str = v.toString(); JS_COMPARTMENT_ASSERT_STR(runtime, str); JS_ASSERT(str->compartment() == runtime->atomsCompartment || str->compartment() == obj->compartment()); if (str->markIfUnmarked()) ScanString(this, str); } else if (v.isObject()) { JSObject *obj2 = &v.toObject(); JS_COMPARTMENT_ASSERT(runtime, obj2); JS_ASSERT(obj->compartment() == obj2->compartment()); if (obj2->markIfUnmarked(getMarkColor())) { pushValueArray(obj, vp, end); obj = obj2; goto scan_obj; } } } return; scan_obj: { JS_COMPARTMENT_ASSERT(runtime, obj); budget.step(); if (budget.isOverBudget()) { pushObject(obj); return; } types::TypeObject *type = obj->typeFromGC(); PushMarkStack(this, type); Shape *shape = obj->lastProperty(); PushMarkStack(this, shape); /* Call the trace hook if necessary. */ Class *clasp = shape->getObjectClass(); if (clasp->trace) { if (clasp == &ArrayClass) { JS_ASSERT(!shape->isNative()); vp = obj->getDenseArrayElements(); end = vp + obj->getDenseArrayInitializedLength(); goto scan_value_array; } else { JS_ASSERT_IF(runtime->gcMode == JSGC_MODE_INCREMENTAL && runtime->gcIncrementalEnabled, clasp->flags & JSCLASS_IMPLEMENTS_BARRIERS); } clasp->trace(this, obj); } if (!shape->isNative()) return; unsigned nslots = obj->slotSpan(); vp = obj->fixedSlots(); if (obj->slots) { unsigned nfixed = obj->numFixedSlots(); if (nslots > nfixed) { pushValueArray(obj, vp, vp + nfixed); vp = obj->slots; end = vp + (nslots - nfixed); goto scan_value_array; } } JS_ASSERT(nslots <= obj->numFixedSlots()); end = vp + nslots; goto scan_value_array; } }