/* * Copyright (C) 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once namespace JSC { // Are you tired of waiting for all of WebKit to build because you changed the implementation of a // function in HeapInlines.h? Does it bother you that you're waiting on rebuilding the JS DOM // bindings even though your change is in a function called from only 2 .cpp files? Then HeapUtil.h // is for you! Everything in this class should be a static method that takes a Heap& if needed. // This is a friend of Heap, so you can access all of Heap's privates. // // This ends up being an issue because Heap exposes a lot of methods that ought to be inline for // performance or that must be inline because they are templates. This class ought to contain // methods that are used for the implementation of the collector, or for unusual clients that need // to reach deep into the collector for some reason. Don't put things in here that would cause you // to have to include it from more than a handful of places, since that would defeat the purpose. // This class isn't here to look pretty. It's to let us hack the GC more easily! class HeapUtil { public: // This function must be run after stopAllocation() is called and // before liveness data is cleared to be accurate. template static void findGCObjectPointersForMarking( Heap& heap, HeapVersion markingVersion, TinyBloomFilter filter, void* passedPointer, const Func& func) { const HashSet& set = heap.objectSpace().blocks().set(); ASSERT(heap.objectSpace().isMarking()); static const bool isMarking = true; char* pointer = static_cast(passedPointer); // It could point to a large allocation. if (heap.objectSpace().largeAllocationsForThisCollectionSize()) { if (heap.objectSpace().largeAllocationsForThisCollectionBegin()[0]->aboveLowerBound(pointer) && heap.objectSpace().largeAllocationsForThisCollectionEnd()[-1]->belowUpperBound(pointer)) { LargeAllocation** result = approximateBinarySearch( heap.objectSpace().largeAllocationsForThisCollectionBegin(), heap.objectSpace().largeAllocationsForThisCollectionSize(), LargeAllocation::fromCell(pointer), [] (LargeAllocation** ptr) -> LargeAllocation* { return *ptr; }); if (result) { if (result > heap.objectSpace().largeAllocationsForThisCollectionBegin() && result[-1]->contains(pointer)) func(result[-1]->cell()); if (result[0]->contains(pointer)) func(result[0]->cell()); if (result + 1 < heap.objectSpace().largeAllocationsForThisCollectionEnd() && result[1]->contains(pointer)) func(result[1]->cell()); } } } MarkedBlock* candidate = MarkedBlock::blockFor(pointer); // It's possible for a butterfly pointer to point past the end of a butterfly. Check this now. if (pointer <= bitwise_cast(candidate) + sizeof(IndexingHeader)) { // We may be interested in the last cell of the previous MarkedBlock. char* previousPointer = pointer - sizeof(IndexingHeader) - 1; MarkedBlock* previousCandidate = MarkedBlock::blockFor(previousPointer); if (!filter.ruleOut(bitwise_cast(previousCandidate)) && set.contains(previousCandidate) && previousCandidate->handle().cellKind() == HeapCell::Auxiliary) { previousPointer = static_cast(previousCandidate->handle().cellAlign(previousPointer)); if (previousCandidate->handle().isLiveCell(markingVersion, isMarking, previousPointer)) func(previousPointer); } } if (filter.ruleOut(bitwise_cast(candidate))) { ASSERT(!candidate || !set.contains(candidate)); return; } if (!set.contains(candidate)) return; auto tryPointer = [&] (void* pointer) { if (candidate->handle().isLiveCell(markingVersion, isMarking, pointer)) func(pointer); }; if (candidate->handle().cellKind() == HeapCell::JSCell) { if (!MarkedBlock::isAtomAligned(pointer)) return; tryPointer(pointer); return; } // A butterfly could point into the middle of an object. char* alignedPointer = static_cast(candidate->handle().cellAlign(pointer)); tryPointer(alignedPointer); // Also, a butterfly could point at the end of an object plus sizeof(IndexingHeader). In that // case, this is pointing to the object to the right of the one we should be marking. if (candidate->atomNumber(alignedPointer) > MarkedBlock::firstAtom() && pointer <= alignedPointer + sizeof(IndexingHeader)) tryPointer(alignedPointer - candidate->cellSize()); } static bool isPointerGCObjectJSCell( Heap& heap, TinyBloomFilter filter, const void* pointer) { // It could point to a large allocation. const Vector& largeAllocations = heap.objectSpace().largeAllocations(); if (!largeAllocations.isEmpty()) { if (largeAllocations[0]->aboveLowerBound(pointer) && largeAllocations.last()->belowUpperBound(pointer)) { LargeAllocation*const* result = approximateBinarySearch( largeAllocations.begin(), largeAllocations.size(), LargeAllocation::fromCell(pointer), [] (LargeAllocation*const* ptr) -> LargeAllocation* { return *ptr; }); if (result) { if (result > largeAllocations.begin() && result[-1]->cell() == pointer && result[-1]->attributes().cellKind == HeapCell::JSCell) return true; if (result[0]->cell() == pointer && result[0]->attributes().cellKind == HeapCell::JSCell) return true; if (result + 1 < largeAllocations.end() && result[1]->cell() == pointer && result[1]->attributes().cellKind == HeapCell::JSCell) return true; } } } const HashSet& set = heap.objectSpace().blocks().set(); MarkedBlock* candidate = MarkedBlock::blockFor(pointer); if (filter.ruleOut(bitwise_cast(candidate))) { ASSERT(!candidate || !set.contains(candidate)); return false; } if (!MarkedBlock::isAtomAligned(pointer)) return false; if (!set.contains(candidate)) return false; if (candidate->handle().cellKind() != HeapCell::JSCell) return false; if (!candidate->handle().isLiveCell(pointer)) return false; return true; } static bool isValueGCObject( Heap& heap, TinyBloomFilter filter, JSValue value) { if (!value.isCell()) return false; return isPointerGCObjectJSCell(heap, filter, static_cast(value.asCell())); } }; } // namespace JSC