Tighten typing in inline rendering
authorantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 29 Sep 2013 20:50:01 +0000 (20:50 +0000)
committerantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 29 Sep 2013 20:50:01 +0000 (20:50 +0000)
https://bugs.webkit.org/show_bug.cgi?id=122076

Reviewed by Andreas Kling.

More RenderElement, const, &, etc.

* dom/Position.cpp:
(WebCore::Position::hasRenderedNonAnonymousDescendantsWithHeight):
* rendering/InlineFlowBox.cpp:
(WebCore::isLastChildForRenderer):
(WebCore::isAncestorAndWithinBlock):
(WebCore::InlineFlowBox::determineSpacingForFlowBoxes):
(WebCore::InlineFlowBox::nodeAtPoint):
* rendering/InlineIterator.h:
(WebCore::InlineIterator::InlineIterator):
(WebCore::InlineIterator::root):
(WebCore::isEmptyInline):
(WebCore::bidiNextShared):
(WebCore::bidiNextSkippingEmptyInlines):
(WebCore::bidiNextIncludingEmptyInlines):
(WebCore::bidiFirstSkippingEmptyInlines):
* rendering/RenderBlockLineLayout.cpp:
(WebCore::inlineLogicalWidth):
(WebCore::alwaysRequiresLineBox):
(WebCore::requiresLineBox):
(WebCore::canBreakAtThisPosition):
(WebCore::LineBreaker::nextSegmentBreak):
* rendering/shapes/ShapeInsideInfo.h:
(WebCore::LineSegmentIterator::LineSegmentIterator):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@156618 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/dom/Position.cpp
Source/WebCore/rendering/InlineFlowBox.cpp
Source/WebCore/rendering/InlineIterator.h
Source/WebCore/rendering/RenderBlockLineLayout.cpp
Source/WebCore/rendering/shapes/ShapeInsideInfo.h

index d9d0435..2e871af 100644 (file)
@@ -1,3 +1,36 @@
+2013-09-29  Antti Koivisto  <antti@apple.com>
+
+        Tighten typing in inline rendering
+        https://bugs.webkit.org/show_bug.cgi?id=122076
+
+        Reviewed by Andreas Kling.
+        
+        More RenderElement, const, &, etc.
+
+        * dom/Position.cpp:
+        (WebCore::Position::hasRenderedNonAnonymousDescendantsWithHeight):
+        * rendering/InlineFlowBox.cpp:
+        (WebCore::isLastChildForRenderer):
+        (WebCore::isAncestorAndWithinBlock):
+        (WebCore::InlineFlowBox::determineSpacingForFlowBoxes):
+        (WebCore::InlineFlowBox::nodeAtPoint):
+        * rendering/InlineIterator.h:
+        (WebCore::InlineIterator::InlineIterator):
+        (WebCore::InlineIterator::root):
+        (WebCore::isEmptyInline):
+        (WebCore::bidiNextShared):
+        (WebCore::bidiNextSkippingEmptyInlines):
+        (WebCore::bidiNextIncludingEmptyInlines):
+        (WebCore::bidiFirstSkippingEmptyInlines):
+        * rendering/RenderBlockLineLayout.cpp:
+        (WebCore::inlineLogicalWidth):
+        (WebCore::alwaysRequiresLineBox):
+        (WebCore::requiresLineBox):
+        (WebCore::canBreakAtThisPosition):
+        (WebCore::LineBreaker::nextSegmentBreak):
+        * rendering/shapes/ShapeInsideInfo.h:
+        (WebCore::LineSegmentIterator::LineSegmentIterator):
+
 2013-09-28  Sam Weinig  <sam@webkit.org>
 
         Merge ScriptControllerBase into ScriptController
index 18402f0..1c4c0c6 100644 (file)
@@ -854,14 +854,31 @@ static int boundingBoxLogicalHeight(RenderObject *o, const IntRect &rect)
 bool Position::hasRenderedNonAnonymousDescendantsWithHeight(const RenderElement& renderer)
 {
     RenderObject* stop = renderer.nextInPreOrderAfterChildren();
-    for (RenderObject* o = renderer.firstChild(); o && o != stop; o = o->nextInPreOrder())
-        if (o->nonPseudoNode()) {
-            if ((o->isText() && boundingBoxLogicalHeight(o, toRenderText(o)->linesBoundingBox()))
-                || (o->isLineBreak() && boundingBoxLogicalHeight(o, toRenderLineBreak(o)->linesBoundingBox()))
-                || (o->isBox() && toRenderBox(o)->pixelSnappedLogicalHeight())
-                || (o->isRenderInline() && isEmptyInline(o) && boundingBoxLogicalHeight(o, toRenderInline(o)->linesBoundingBox())))
+    for (RenderObject* o = renderer.firstChild(); o && o != stop; o = o->nextInPreOrder()) {
+        if (!o->nonPseudoNode())
+            continue;
+        if (o->isText()) {
+            if (boundingBoxLogicalHeight(o, toRenderText(o)->linesBoundingBox()))
+                return true;
+            continue;
+        }
+        if (o->isLineBreak()) {
+            if (boundingBoxLogicalHeight(o, toRenderLineBreak(o)->linesBoundingBox()))
+                return true;
+            continue;
+        }
+        if (o->isBox()) {
+            if (toRenderBox(o)->pixelSnappedLogicalHeight())
+                return true;
+            continue;
+        }
+        if (o->isRenderInline()) {
+            const RenderInline& renderInline = toRenderInline(*o);
+            if (isEmptyInline(renderInline) && boundingBoxLogicalHeight(o, renderInline.linesBoundingBox()))
                 return true;
+            continue;
         }
+    }
     return false;
 }
 
index 6981885..e0a5f10 100644 (file)
@@ -269,20 +269,20 @@ RenderLineBoxList& InlineFlowBox::rendererLineBoxes() const
     return toRenderInline(renderer()).lineBoxes();
 }
 
-static inline bool isLastChildForRenderer(RenderElement* ancestor, RenderObject* child)
+static inline bool isLastChildForRenderer(const RenderElement& ancestor, const RenderObject* child)
 {
     if (!child)
         return false;
     
-    if (child == ancestor)
+    if (child == &ancestor)
         return true;
 
-    RenderObject* curr = child;
-    RenderElement* parent = curr->parent();
+    const RenderObject* curr = child;
+    const RenderElement* parent = curr->parent();
     while (parent && (!parent->isRenderBlock() || parent->isInline())) {
         if (parent->lastChild() != curr)
             return false;
-        if (parent == ancestor)
+        if (parent == &ancestor)
             return true;
             
         curr = parent;
@@ -292,9 +292,9 @@ static inline bool isLastChildForRenderer(RenderElement* ancestor, RenderObject*
     return true;
 }
 
-static bool isAncestorAndWithinBlock(RenderObject& ancestor, RenderObject* child)
+static bool isAncestorAndWithinBlock(const RenderInline& ancestor, const RenderObject* child)
 {
-    RenderObject* object = child;
+    const RenderObject* object = child;
     while (object && (!object->isRenderBlock() || object->isInline())) {
         if (object == &ancestor)
             return true;
@@ -331,7 +331,7 @@ void InlineFlowBox::determineSpacingForFlowBoxes(bool lastLine, bool isLogically
 
         if (!lineBoxList.lastLineBox()->isConstructed()) {
             RenderInline& inlineFlow = toRenderInline(renderer());
-            bool isLastObjectOnLine = !isAncestorAndWithinBlock(renderer(), logicallyLastRunRenderer) || (isLastChildForRenderer(&renderer(), logicallyLastRunRenderer) && !isLogicallyLastRunWrapped);
+            bool isLastObjectOnLine = !isAncestorAndWithinBlock(inlineFlow, logicallyLastRunRenderer) || (isLastChildForRenderer(renderer(), logicallyLastRunRenderer) && !isLogicallyLastRunWrapped);
 
             // We include the border under these conditions:
             // (1) The next line was not created, or it is constructed. We check the previous line for rtl.
@@ -1018,10 +1018,10 @@ bool InlineFlowBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& re
 
     // Check children first.
     // We need to account for culled inline parents of the hit-tested nodes, so that they may also get included in area-based hit-tests.
-    RenderObject* culledParent = 0;
+    RenderElement* culledParent = 0;
     for (InlineBox* curr = lastChild(); curr; curr = curr->prevOnLine()) {
         if (curr->renderer().isText() || !curr->boxModelObject()->hasSelfPaintingLayer()) {
-            RenderObject* newParent = 0;
+            RenderElement* newParent = 0;
             // Culled parents are only relevant for area-based hit-tests, so ignore it in point-based ones.
             if (locationInContainer.isRectBasedTest()) {
                 newParent = curr->renderer().parent();
index fc3e0d6..84f16d4 100644 (file)
@@ -25,6 +25,7 @@
 
 #include "BidiRun.h"
 #include "RenderBlock.h"
+#include "RenderInline.h"
 #include "RenderText.h"
 #include <wtf/StdLibExtras.h>
 
@@ -43,7 +44,7 @@ public:
     {
     }
 
-    InlineIterator(RenderObject* root, RenderObject* o, unsigned p)
+    InlineIterator(RenderElement* root, RenderObject* o, unsigned p)
         : m_root(root)
         , m_obj(o)
         , m_pos(p)
@@ -67,7 +68,7 @@ public:
 
     RenderObject* object() const { return m_obj; }
     unsigned offset() const { return m_pos; }
-    RenderObject* root() const { return m_root; }
+    RenderElement* root() const { return m_root; }
 
     void fastIncrementInTextNode();
     void increment(InlineBidiResolver* = 0);
@@ -90,7 +91,7 @@ public:
     ALWAYS_INLINE WTF::Unicode::Direction direction() const;
 
 private:
-    RenderObject* m_root;
+    RenderElement* m_root;
 
     // FIXME: These should be private.
 public:
@@ -175,18 +176,17 @@ enum EmptyInlineBehavior {
     IncludeEmptyInlines,
 };
 
-static bool isEmptyInline(RenderObject* object)
+static bool isEmptyInline(const RenderInline& renderer)
 {
-    if (!object->isRenderInline())
-        return false;
-
-    for (RenderObject* curr = toRenderElement(object)->firstChild(); curr; curr = curr->nextSibling()) {
+    for (RenderObject* curr = renderer.firstChild(); curr; curr = curr->nextSibling()) {
         if (curr->isFloatingOrOutOfFlowPositioned())
             continue;
-        if (curr->isText() && toRenderText(curr)->isAllCollapsibleWhitespace())
+        if (curr->isText()) {
+            if (!toRenderText(curr)->isAllCollapsibleWhitespace())
+                return false;
             continue;
-
-        if (!isEmptyInline(curr))
+        }
+        if (!curr->isRenderInline() || !isEmptyInline(toRenderInline(*curr)))
             return false;
     }
     return true;
@@ -196,7 +196,7 @@ static bool isEmptyInline(RenderObject* object)
 // This function will iterate over inlines within a block, optionally notifying
 // a bidi resolver as it enters/exits inlines (so it can push/pop embedding levels).
 template <class Observer>
-static inline RenderObject* bidiNextShared(RenderObject* root, RenderObject* current, Observer* observer = 0, EmptyInlineBehavior emptyInlineBehavior = SkipEmptyInlines, bool* endOfInlinePtr = 0)
+static inline RenderObject* bidiNextShared(RenderElement* root, RenderObject* current, Observer* observer = 0, EmptyInlineBehavior emptyInlineBehavior = SkipEmptyInlines, bool* endOfInlinePtr = 0)
 {
     RenderObject* next = 0;
     // oldEndOfInline denotes if when we last stopped iterating if we were at the end of an inline.
@@ -241,8 +241,7 @@ static inline RenderObject* bidiNextShared(RenderObject* root, RenderObject* cur
             break;
 
         if (isIteratorTarget(next)
-            || ((emptyInlineBehavior == IncludeEmptyInlines || isEmptyInline(next)) // Always return EMPTY inlines.
-                && next->isRenderInline()))
+            || (next->isRenderInline() && (emptyInlineBehavior == IncludeEmptyInlines || isEmptyInline(toRenderInline(*next)))))
             break;
         current = next;
     }
@@ -254,20 +253,20 @@ static inline RenderObject* bidiNextShared(RenderObject* root, RenderObject* cur
 }
 
 template <class Observer>
-static inline RenderObject* bidiNextSkippingEmptyInlines(RenderObject* root, RenderObject* current, Observer* observer)
+static inline RenderObject* bidiNextSkippingEmptyInlines(RenderElement* root, RenderObject* current, Observer* observer)
 {
     // The SkipEmptyInlines callers never care about endOfInlinePtr.
     return bidiNextShared(root, current, observer, SkipEmptyInlines);
 }
 
 // This makes callers cleaner as they don't have to specify a type for the observer when not providing one.
-static inline RenderObject* bidiNextSkippingEmptyInlines(RenderObject* root, RenderObject* current)
+static inline RenderObject* bidiNextSkippingEmptyInlines(RenderElement* root, RenderObject* current)
 {
     InlineBidiResolver* observer = 0;
     return bidiNextSkippingEmptyInlines(root, current, observer);
 }
 
-static inline RenderObject* bidiNextIncludingEmptyInlines(RenderObject* root, RenderObject* current, bool* endOfInlinePtr = 0)
+static inline RenderObject* bidiNextIncludingEmptyInlines(RenderElement* root, RenderObject* current, bool* endOfInlinePtr = 0)
 {
     InlineBidiResolver* observer = 0; // Callers who include empty inlines, never use an observer.
     return bidiNextShared(root, current, observer, IncludeEmptyInlines, endOfInlinePtr);
@@ -281,7 +280,7 @@ static inline RenderObject* bidiFirstSkippingEmptyInlines(RenderElement* root, I
 
     if (o->isRenderInline()) {
         notifyObserverEnteredObject(resolver, o);
-        if (!isEmptyInline(o))
+        if (!isEmptyInline(toRenderInline(*o)))
             o = bidiNextSkippingEmptyInlines(root, o, resolver);
         else {
             // Never skip empty inlines.
index f31485d..7540d06 100644 (file)
@@ -124,14 +124,14 @@ ShapeInsideInfo* RenderBlock::layoutShapeInsideInfo() const
 }
 #endif
 
-static inline LayoutUnit borderPaddingMarginStart(RenderInline* child)
+static inline LayoutUnit borderPaddingMarginStart(const RenderInline& child)
 {
-    return child->marginStart() + child->paddingStart() + child->borderStart();
+    return child.marginStart() + child.paddingStart() + child.borderStart();
 }
 
-static inline LayoutUnit borderPaddingMarginEnd(RenderInline* child)
+static inline LayoutUnit borderPaddingMarginEnd(const RenderInline& child)
 {
-    return child->marginEnd() + child->paddingEnd() + child->borderEnd();
+    return child.marginEnd() + child.paddingEnd() + child.borderEnd();
 }
 
 static inline bool shouldAddBorderPaddingMargin(RenderObject* child)
@@ -152,9 +152,9 @@ static LayoutUnit inlineLogicalWidth(RenderObject* child, bool checkStartEdge =
 {
     unsigned lineDepth = 1;
     LayoutUnit extraWidth = 0;
-    RenderObject* parent = child->parent();
+    RenderElement* parent = child->parent();
     while (parent->isRenderInline() && lineDepth++ < cMaxLineDepth) {
-        RenderInline* parentAsRenderInline = toRenderInline(parent);
+        const RenderInline& parentAsRenderInline = toRenderInline(*parent);
         if (!isEmptyInline(parentAsRenderInline)) {
             checkStartEdge = checkStartEdge && shouldAddBorderPaddingMargin(previousInFlowSibling(child));
             if (checkStartEdge)
@@ -622,7 +622,7 @@ public:
     HashSet<const SimpleFontData*> fallbackFonts;
 };
 
-static inline const RenderStyle& lineStyle(RenderElement& renderer, const LineInfo& lineInfo)
+static inline const RenderStyle& lineStyle(const RenderElement& renderer, const LineInfo& lineInfo)
 {
     return lineInfo.isFirstLine() ? *renderer.firstLineStyle() : *renderer.style();
 }
@@ -2216,11 +2216,11 @@ static inline bool shouldCollapseWhiteSpace(const RenderStyle* style, const Line
         || (whitespacePosition == TrailingWhitespace && style->whiteSpace() == PRE_WRAP && (!lineInfo.isEmpty() || !lineInfo.previousLineBrokeCleanly()));
 }
 
-static bool requiresLineBoxForContent(RenderInline* flow, const LineInfo& lineInfo)
+static bool requiresLineBoxForContent(const RenderInline& flow, const LineInfo& lineInfo)
 {
-    RenderElement* parent = flow->parent();
-    if (flow->document().inNoQuirksMode()) {
-        const RenderStyle& flowStyle = lineStyle(*flow, lineInfo);
+    RenderElement* parent = flow.parent();
+    if (flow.document().inNoQuirksMode()) {
+        const RenderStyle& flowStyle = lineStyle(flow, lineInfo);
         const RenderStyle& parentStyle = lineStyle(*parent, lineInfo);
         if (flowStyle.lineHeight() != parentStyle.lineHeight()
             || flowStyle.verticalAlign() != parentStyle.verticalAlign()
@@ -2230,24 +2230,24 @@ static bool requiresLineBoxForContent(RenderInline* flow, const LineInfo& lineIn
     return false;
 }
 
-static bool hasInlineDirectionBordersPaddingOrMargin(RenderInline* flow)
+static bool hasInlineDirectionBordersPaddingOrMargin(const RenderInline& flow)
 {
     // Where an empty inline is split across anonymous blocks we should only give lineboxes to the 'sides' of the
     // inline that have borders, padding or margin.
-    bool shouldApplyStartBorderPaddingOrMargin = !flow->parent()->isAnonymousBlock() || !flow->isInlineElementContinuation();
-    if (shouldApplyStartBorderPaddingOrMargin && (flow->borderStart() || flow->marginStart() || flow->paddingStart()))
+    bool shouldApplyStartBorderPaddingOrMargin = !flow.parent()->isAnonymousBlock() || !flow.isInlineElementContinuation();
+    if (shouldApplyStartBorderPaddingOrMargin && (flow.borderStart() || flow.marginStart() || flow.paddingStart()))
         return true;
 
-    bool shouldApplyEndBorderPaddingOrMargin = !flow->parent()->isAnonymousBlock() || flow->isInlineElementContinuation() || !flow->inlineElementContinuation();
-    return shouldApplyEndBorderPaddingOrMargin && (flow->borderEnd() || flow->marginEnd() || flow->paddingEnd());
+    bool shouldApplyEndBorderPaddingOrMargin = !flow.parent()->isAnonymousBlock() || flow.isInlineElementContinuation() || !flow.inlineElementContinuation();
+    return shouldApplyEndBorderPaddingOrMargin && (flow.borderEnd() || flow.marginEnd() || flow.paddingEnd());
 }
 
-static bool alwaysRequiresLineBox(RenderObject* flow)
+static bool alwaysRequiresLineBox(const RenderInline& flow)
 {
     // FIXME: Right now, we only allow line boxes for inlines that are truly empty.
     // We need to fix this, though, because at the very least, inlines containing only
     // ignorable whitespace should should also have line boxes.
-    return isEmptyInline(flow) && hasInlineDirectionBordersPaddingOrMargin(toRenderInline(flow));
+    return isEmptyInline(flow) && hasInlineDirectionBordersPaddingOrMargin(flow);
 }
 
 static bool requiresLineBox(const InlineIterator& it, const LineInfo& lineInfo = LineInfo(), WhitespacePosition whitespacePosition = LeadingWhitespace)
@@ -2258,15 +2258,20 @@ static bool requiresLineBox(const InlineIterator& it, const LineInfo& lineInfo =
     if (it.m_obj->isBR())
         return true;
 
-    if (it.m_obj->isRenderInline() && !alwaysRequiresLineBox(it.m_obj) && !requiresLineBoxForContent(toRenderInline(it.m_obj), lineInfo))
-        return false;
+    bool rendererIsEmptyInline = false;
+    if (it.m_obj->isRenderInline()) {
+        const RenderInline& inlineRenderer = toRenderInline(*it.m_obj);
+        if (!alwaysRequiresLineBox(inlineRenderer) && !requiresLineBoxForContent(inlineRenderer, lineInfo))
+            return false;
+        rendererIsEmptyInline = isEmptyInline(inlineRenderer);
+    }
 
     if (!shouldCollapseWhiteSpace(it.m_obj->style(), lineInfo, whitespacePosition))
         return true;
 
     UChar current = it.current();
     bool notJustWhitespace = current != ' ' && current != '\t' && current != softHyphen && (current != '\n' || it.m_obj->preservesNewline()) && !skipNonBreakingSpace(it, lineInfo);
-    return notJustWhitespace || isEmptyInline(it.m_obj);
+    return notJustWhitespace || rendererIsEmptyInline;
 }
 
 bool RenderBlock::generatesLineBoxesForInlineChild(RenderObject* inlineObj)
@@ -2607,7 +2612,7 @@ static bool canBreakAtThisPosition(bool autoWrap, LineWidth& width, InlineIterat
         return true;
 
     // Avoid breaking before empty inlines.
-    if (next && isEmptyInline(next))
+    if (next && next->isRenderInline() && isEmptyInline(toRenderInline(*next)))
         return false;
 
     // Return early if we autowrap and the current character is a space as we will always want to break at such a position.
@@ -2617,8 +2622,11 @@ static bool canBreakAtThisPosition(bool autoWrap, LineWidth& width, InlineIterat
     if (next && next->isLineBreakOpportunity())
         return autoWrap;
 
-    bool nextIsText = (next && (current.m_obj->isText() || isEmptyInline(current.m_obj)) && next->isText() && (autoWrap || next->style()->autoWrap()));
-    if (!nextIsText)
+    bool nextIsAutoWrappingText = (next && next->isText() && (autoWrap || next->style()->autoWrap()));
+    if (!nextIsAutoWrappingText)
+        return autoWrap;
+    bool currentIsTextOrEmptyInline = current.m_obj->isText() || (current.m_obj->isRenderInline() && isEmptyInline(toRenderInline(*current.m_obj)));
+    if (!currentIsTextOrEmptyInline)
         return autoWrap;
 
     bool canBreakHere = !currentCharacterIsSpace && textBeginsWithBreakablePosition(next);
@@ -2820,16 +2828,14 @@ InlineIterator LineBreaker::nextSegmentBreak(InlineBidiResolver& resolver, LineI
             // Update prior line break context characters, using U+FFFD (OBJECT REPLACEMENT CHARACTER) for floating element.
             renderTextInfo.m_lineBreakIterator.updatePriorContext(replacementCharacter);
         } else if (current.m_obj->isRenderInline()) {
+            RenderInline& flowBox = toRenderInline(*current.m_obj);
             // Right now, we should only encounter empty inlines here.
-            ASSERT(isEmptyInline(current.m_obj));
-
-            RenderInline* flowBox = toRenderInline(current.m_obj);
-
+            ASSERT(isEmptyInline(flowBox));
             // Now that some inline flows have line boxes, if we are already ignoring spaces, we need
             // to make sure that we stop to include this object and then start ignoring spaces again.
             // If this object is at the start of the line, we need to behave like list markers and
             // start ignoring spaces.
-            bool requiresLineBox = alwaysRequiresLineBox(current.m_obj);
+            bool requiresLineBox = alwaysRequiresLineBox(flowBox);
             if (requiresLineBox || requiresLineBoxForContent(flowBox, lineInfo)) {
                 // An empty inline that only has line-height, vertical-align or font-metrics will only get a
                 // line box to affect the height of the line if the rest of the line is not empty.
@@ -2846,7 +2852,7 @@ InlineIterator LineBreaker::nextSegmentBreak(InlineBidiResolver& resolver, LineI
                     currentCharacterIsWS = true;
                     ignoringSpaces = true;
                 } else {
-                    trailingObjects.appendBoxIfNeeded(flowBox);
+                    trailingObjects.appendBoxIfNeeded(&flowBox);
                 }
             }
 
index 4874265..83c635a 100644 (file)
@@ -40,13 +40,14 @@ namespace WebCore {
 
 class InlineIterator;
 class RenderBlock;
+class RenderElement;
 class RenderObject;
 
 struct LineSegmentIterator {
-    RenderObject* root;
+    RenderElement* root;
     RenderObject* object;
     unsigned offset;
-    LineSegmentIterator(RenderObject* root, RenderObject* object, unsigned offset)
+    LineSegmentIterator(RenderElement* root, RenderObject* object, unsigned offset)
         : root(root)
         , object(object)
         , offset(offset)