fix fuzzers

Many old pathops-related fuzz failures have built up while
the codebase was under a state a flux. Now that the code
is stable, address these failures.

Most of the CL plumbs the debug global state to downstream
routines so that, if the data is not trusted (ala fuzzed)
the function can safely exit without asserting.

TBR=reed@google.com
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2426173002

Review-Url: https://chromiumcodereview.appspot.com/2426173002
diff --git a/src/pathops/SkAddIntersections.cpp b/src/pathops/SkAddIntersections.cpp
index b3a82cd..17bc9e2 100644
--- a/src/pathops/SkAddIntersections.cpp
+++ b/src/pathops/SkAddIntersections.cpp
@@ -450,8 +450,10 @@
                         }
                         case SkIntersectionHelper::kCubic_Segment: {
                             swap = true;
-                            pts = ts.intersect(cubic2.set(wn.pts()),
-                                    conic1.set(wt.pts(), wt.weight()));
+                            pts = ts.intersect(cubic2.set(wn.pts()
+                                    SkDEBUGPARAMS(ts.globalState())),
+                                    conic1.set(wt.pts(), wt.weight()
+                                    SkDEBUGPARAMS(ts.globalState())));
                             debugShowCubicConicIntersection(pts, wn, wt, ts);
                             break;
                         }
@@ -479,8 +481,10 @@
                             break;
                         }
                         case SkIntersectionHelper::kConic_Segment: {
-                            pts = ts.intersect(cubic1.set(wt.pts()),
-                                    conic2.set(wn.pts(), wn.weight()));
+                            pts = ts.intersect(cubic1.set(wt.pts()
+                                    SkDEBUGPARAMS(ts.globalState())),
+                                    conic2.set(wn.pts(), wn.weight()
+                                    SkDEBUGPARAMS(ts.globalState())));
                             debugShowCubicConicIntersection(pts, wt, wn, ts);
                             break;
                         }
diff --git a/src/pathops/SkDConicLineIntersection.cpp b/src/pathops/SkDConicLineIntersection.cpp
index eb32068..102a4c3 100644
--- a/src/pathops/SkDConicLineIntersection.cpp
+++ b/src/pathops/SkDConicLineIntersection.cpp
@@ -105,8 +105,8 @@
             double conicT = rootVals[index];
             double lineT = this->findLineT(conicT);
 #ifdef SK_DEBUG
-            if (!fIntersections->debugGlobalState()
-                    || !fIntersections->debugGlobalState()->debugSkipAssert()) {
+            if (!fIntersections->globalState()
+                    || !fIntersections->globalState()->debugSkipAssert()) {
                 SkDEBUGCODE(SkDPoint conicPt = fConic.ptAtT(conicT));
                 SkDEBUGCODE(SkDPoint linePt = fLine->ptAtT(lineT));
                 SkASSERT(conicPt.approximatelyDEqual(linePt));
diff --git a/src/pathops/SkDCubicLineIntersection.cpp b/src/pathops/SkDCubicLineIntersection.cpp
index fd060de..ceedce1 100644
--- a/src/pathops/SkDCubicLineIntersection.cpp
+++ b/src/pathops/SkDCubicLineIntersection.cpp
@@ -122,6 +122,7 @@
         double adj = fLine[1].fX - fLine[0].fX;
         double opp = fLine[1].fY - fLine[0].fY;
         SkDCubic c;
+        SkDEBUGCODE(c.fDebugGlobalState = fIntersections->globalState());
         for (int n = 0; n < 4; ++n) {
             c[n].fX = (fCubic[n].fY - fLine[0].fY) * adj - (fCubic[n].fX - fLine[0].fX) * opp;
         }
diff --git a/src/pathops/SkDLineIntersection.cpp b/src/pathops/SkDLineIntersection.cpp
index 71e2a06..082e298 100644
--- a/src/pathops/SkDLineIntersection.cpp
+++ b/src/pathops/SkDLineIntersection.cpp
@@ -152,7 +152,7 @@
                         continue;
                     }
                     SkASSERT(a[iA] != b[nearer]);
-                    SkASSERT(iA == (bNearA[nearer] > 0.5));
+                    SkOPASSERT(iA == (bNearA[nearer] > 0.5));
                     insertNear(iA, nearer, a[iA], b[nearer]);
                     aNearB[iA] = -1;
                     bNearA[nearer] = -1;
diff --git a/src/pathops/SkIntersections.h b/src/pathops/SkIntersections.h
index abc10e1..d5d217c 100644
--- a/src/pathops/SkIntersections.h
+++ b/src/pathops/SkIntersections.h
@@ -104,7 +104,7 @@
     }
 
 #ifdef SK_DEBUG
-    SkOpGlobalState* debugGlobalState() { return fDebugGlobalState; }
+    SkOpGlobalState* globalState() const { return fDebugGlobalState; }
 #endif
 
     bool hasT(double t) const {
@@ -308,9 +308,9 @@
     void cleanUpParallelLines(bool parallel);
     void computePoints(const SkDLine& line, int used);
 
-    SkDPoint fPt[12];  // FIXME: since scans store points as SkPoint, this should also
+    SkDPoint fPt[13];  // FIXME: since scans store points as SkPoint, this should also
     SkDPoint fPt2[2];  // used by nearly same to store alternate intersection point
-    double fT[2][12];
+    double fT[2][13];
     uint16_t fIsCoincident[2];  // bit set for each curve's coincident T
     bool fNearlySame[2];  // true if end points nearly match
     unsigned char fUsed;
diff --git a/src/pathops/SkOpAngle.cpp b/src/pathops/SkOpAngle.cpp
index 99f93dd..1223ac1 100644
--- a/src/pathops/SkOpAngle.cpp
+++ b/src/pathops/SkOpAngle.cpp
@@ -152,7 +152,7 @@
         // FIXME : once this is verified to work, remove one opposite angle call
         SkDEBUGCODE(bool lrOpposite = lh->oppositePlanes(rh));
         bool ltOpposite = lh->oppositePlanes(this);
-        SkASSERT(lrOpposite != ltOpposite);
+        SkOPASSERT(lrOpposite != ltOpposite);
         return COMPARE_RESULT(8, ltOpposite);
     } else if (ltOrder == 1 && trOrder == 0) {
         SkASSERT(lrOrder < 0);
diff --git a/src/pathops/SkOpCoincidence.cpp b/src/pathops/SkOpCoincidence.cpp
index aa5f5bf..d16f6f8 100755
--- a/src/pathops/SkOpCoincidence.cpp
+++ b/src/pathops/SkOpCoincidence.cpp
@@ -128,11 +128,12 @@
 // A coincident span is unordered if the pairs of points in the main and opposite curves'
 // t values do not ascend or descend. For instance, if a tightly arced quadratic is
 // coincident with another curve, it may intersect it out of order.
-bool SkCoincidentSpans::ordered() const {
+bool SkCoincidentSpans::ordered(bool* result) const {
     const SkOpSpanBase* start = this->coinPtTStart()->span();
     const SkOpSpanBase* end = this->coinPtTEnd()->span();
     const SkOpSpanBase* next = start->upCast()->next();
     if (next == end) {
+        *result = true;
         return true;
     }
     bool flipped = this->flipped();
@@ -141,21 +142,24 @@
     do {
         const SkOpPtT* opp = next->contains(oppSeg);
         if (!opp) {
-            SkASSERT(0);  // may assert if coincident span isn't fully processed
-            continue;
+            SkOPOBJASSERT(start, 0);  // may assert if coincident span isn't fully processed
+            return false;
         }
         if ((oppLastT > opp->fT) != flipped) {
-            return false;
+            *result = false;
+            return true;
         }
         oppLastT = opp->fT;
         if (next == end) {
             break;
         }
         if (!next->upCastable()) {
-            return false;
+            *result = false;
+            return true;
         }
         next = next->upCast()->next();
     } while (true);
+    *result = true;
     return true;
 }
 
@@ -234,7 +238,7 @@
     coinPtTEnd = coinPtTEnd->span()->ptT();
     oppPtTStart = oppPtTStart->span()->ptT();
     oppPtTEnd = oppPtTEnd->span()->ptT();
-    SkASSERT(coinPtTStart->fT < coinPtTEnd->fT);
+    SkOPASSERT(coinPtTStart->fT < coinPtTEnd->fT);
     SkASSERT(oppPtTStart->fT != oppPtTEnd->fT);
     SkOPASSERT(!coinPtTStart->deleted());
     SkOPASSERT(!coinPtTEnd->deleted());
@@ -754,7 +758,7 @@
     // save head so that walker can iterate over old data unperturbed
     // addifmissing adds to head freely then add saved head in the end
         const SkOpPtT* ocs = outer->coinPtTStart();
-        SkASSERT(!ocs->deleted());
+        FAIL_IF(ocs->deleted());
         const SkOpSegment* outerCoin = ocs->segment();
         SkASSERT(!outerCoin->done());  // if it's done, should have already been removed from list
         const SkOpPtT* oos = outer->oppPtTStart();
@@ -772,9 +776,9 @@
             const SkOpPtT* ics = inner->coinPtTStart();
             FAIL_IF(ics->deleted());
             const SkOpSegment* innerCoin = ics->segment();
-            SkASSERT(!innerCoin->done());
+            FAIL_IF(innerCoin->done());
             const SkOpPtT* ios = inner->oppPtTStart();
-            SkASSERT(!ios->deleted());
+            FAIL_IF(ios->deleted());
             const SkOpSegment* innerOpp = ios->segment();
             SkASSERT(!innerOpp->done());
             SkOpSegment* innerCoinWritable = const_cast<SkOpSegment*>(innerCoin);
@@ -855,9 +859,11 @@
     }
     const SkOpPtT* s2 = overS->find(seg2);
     const SkOpPtT* e2 = overE->find(seg2);
+    FAIL_IF(!e2);
     if (!s2->starter(e2)->span()->upCast()->windValue()) {
         s2 = overS->find(seg2o);
         e2 = overE->find(seg2o);
+        FAIL_IF(!s2);
         if (!s2->starter(e2)->span()->upCast()->windValue()) {
             return true;
         }
@@ -956,11 +962,11 @@
 }
 
 // walk span sets in parallel, moving winding from one to the other
-void SkOpCoincidence::apply(DEBUG_COIN_DECLARE_ONLY_PARAMS()) {
+bool SkOpCoincidence::apply(DEBUG_COIN_DECLARE_ONLY_PARAMS()) {
     DEBUG_SET_PHASE();
     SkCoincidentSpans* coin = fHead;
     if (!coin) {
-        return;
+        return true;
     }
     do {
         SkOpSpan* start = coin->coinPtTStartWritable()->span()->upCast();
@@ -1055,6 +1061,7 @@
 #endif
             start->setWindValue(windValue);
             start->setOppValue(oppValue);
+            FAIL_IF(oWindValue == -1);
             oStart->setWindValue(oWindValue);
             oStart->setOppValue(oOppValue);
             if (!windValue && !oppValue) {
@@ -1076,6 +1083,7 @@
             oStart = oNext->upCast();
         } while (true);
     } while ((coin = coin->next()));
+    return true;
 }
 
 // Please keep this in sync with debugRelease()
@@ -1272,7 +1280,7 @@
         SkOpSpanBase* startBase = coin->coinPtTStartWritable()->span();
         FAIL_IF(!startBase->upCastable());
         SkOpSpan* start = startBase->upCast();
-        SkASSERT(!start->deleted());
+        FAIL_IF(start->deleted());
         SkOpSpanBase* end = coin->coinPtTEndWritable()->span();
         SkOPASSERT(!end->deleted());
         SkOpSpanBase* oStart = coin->oppPtTStartWritable()->span();
@@ -1291,7 +1299,8 @@
         const SkOpSegment* oSegment = oStart->segment();
         SkOpSpanBase* next = start;
         SkOpSpanBase* oNext = oStart;
-        bool ordered = coin->ordered();
+        bool ordered;
+        FAIL_IF(!coin->ordered(&ordered));
         while ((next = next->upCast()->next()) != end) {
             FAIL_IF(!next->upCastable());
             SkAssertResult(next->upCast()->insertCoincidence(oSegment, flipped, ordered));
diff --git a/src/pathops/SkOpCoincidence.h b/src/pathops/SkOpCoincidence.h
index e2188be..af84870 100644
--- a/src/pathops/SkOpCoincidence.h
+++ b/src/pathops/SkOpCoincidence.h
@@ -73,7 +73,7 @@
     // to a new span pair
     SkOpPtT* oppPtTStartWritable() const { return const_cast<SkOpPtT*>(fOppPtTStart); }
     SkOpPtT* oppPtTEndWritable() const { return const_cast<SkOpPtT*>(fOppPtTEnd); }
-    bool ordered() const;
+    bool ordered(bool* result) const;
 
     void set(SkCoincidentSpans* next, const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
             const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd);
@@ -108,7 +108,7 @@
     }
 
     void setOppPtTStart(const SkOpPtT* ptT) {
-        SkASSERT(ptT == ptT->span()->ptT());
+        SkOPASSERT(ptT == ptT->span()->ptT());
         SkOPASSERT(!fOppPtTEnd || ptT->fT != fOppPtTEnd->fT);
         SkASSERT(!fOppPtTEnd || fOppPtTEnd->segment() == ptT->segment());
         fOppPtTStart = ptT;
@@ -150,7 +150,7 @@
     bool addEndMovedSpans(DEBUG_COIN_DECLARE_ONLY_PARAMS());
     bool addExpanded(DEBUG_COIN_DECLARE_ONLY_PARAMS());
     bool addMissing(bool* added  DEBUG_COIN_DECLARE_PARAMS());
-    void apply(DEBUG_COIN_DECLARE_ONLY_PARAMS());
+    bool apply(DEBUG_COIN_DECLARE_ONLY_PARAMS());
     bool contains(const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
                   const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) const;
     void correctEnds(DEBUG_COIN_DECLARE_ONLY_PARAMS());
diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp
index 6012d83..439392f 100644
--- a/src/pathops/SkOpSegment.cpp
+++ b/src/pathops/SkOpSegment.cpp
@@ -169,18 +169,18 @@
     path->deferredMove(start->ptT());
     switch (verb) {
         case SkPath::kLine_Verb:
-            path->deferredLine(end->ptT());
+            FAIL_IF(!path->deferredLine(end->ptT()));
             break;
         case SkPath::kQuad_Verb:
-            path->quadTo(curvePart.fCurve.fQuad.fPts[1].asSkPoint(), end->ptT());
+            path->quadTo(curvePart.fCurve.fQuad[1].asSkPoint(), end->ptT());
             break;
         case SkPath::kConic_Verb:
-            path->conicTo(curvePart.fCurve.fConic.fPts[1].asSkPoint(), end->ptT(),
+            path->conicTo(curvePart.fCurve.fConic[1].asSkPoint(), end->ptT(),
                     curvePart.fCurve.fConic.fWeight);
             break;
         case SkPath::kCubic_Verb:
-            path->cubicTo(curvePart.fCurve.fCubic.fPts[1].asSkPoint(),
-                    curvePart.fCurve.fCubic.fPts[2].asSkPoint(), end->ptT());
+            path->cubicTo(curvePart.fCurve.fCubic[1].asSkPoint(),
+                    curvePart.fCurve.fCubic[2].asSkPoint(), end->ptT());
             break;
         default:
             SkASSERT(0);
@@ -225,6 +225,7 @@
         return true;
     }
     this->globalState()->resetAllocatedOpSpan();
+    FAIL_IF(!between(0, newT, 1));
     SkOpPtT* newPtT = this->addT(newT);
     *startOver |= this->globalState()->allocatedOpSpan();
     if (!newPtT) {
diff --git a/src/pathops/SkPathOpsCommon.cpp b/src/pathops/SkPathOpsCommon.cpp
index a1cd7bb..7387249 100644
--- a/src/pathops/SkPathOpsCommon.cpp
+++ b/src/pathops/SkPathOpsCommon.cpp
@@ -318,7 +318,9 @@
     do {
         SkOpCoincidence* pairs = overlaps.isEmpty() ? coincidence : &overlaps;
         // adjust the winding value to account for coincident edges
-        pairs->apply(DEBUG_ITER_ONLY_PARAMS(SAFETY_COUNT - safetyHatch));  
+        if (!pairs->apply(DEBUG_ITER_ONLY_PARAMS(SAFETY_COUNT - safetyHatch))) {
+            return false;
+        } 
         // For each coincident pair that overlaps another, when the receivers (the 1st of the pair)
         // are different, construct a new pair to resolve their mutual span
         if (!pairs->findOverlaps(&overlaps  DEBUG_ITER_PARAMS(SAFETY_COUNT - safetyHatch))) {
diff --git a/src/pathops/SkPathOpsConic.cpp b/src/pathops/SkPathOpsConic.cpp
index dd52321..82f3a7b 100644
--- a/src/pathops/SkPathOpsConic.cpp
+++ b/src/pathops/SkPathOpsConic.cpp
@@ -156,7 +156,8 @@
     double bx = 2 * dx - (ax + cx) / 2;
     double by = 2 * dy - (ay + cy) / 2;
     double bz = 2 * dz - (az + cz) / 2;
-    SkDConic dst = {{{{ax / az, ay / az}, {bx / bz, by / bz}, {cx / cz, cy / cz}}},
+    SkDConic dst = {{{{ax / az, ay / az}, {bx / bz, by / bz}, {cx / cz, cy / cz}}
+            SkDEBUGPARAMS(fPts.fDebugGlobalState) },
             SkDoubleToScalar(bz / sqrt(az * cz)) };
     return dst;
 }
diff --git a/src/pathops/SkPathOpsConic.h b/src/pathops/SkPathOpsConic.h
index 4cbe147..4236279 100644
--- a/src/pathops/SkPathOpsConic.h
+++ b/src/pathops/SkPathOpsConic.h
@@ -31,15 +31,23 @@
         fPts.debugInit();
     }
 
+    void debugSet(const SkDPoint* pts, SkScalar weight);
+
     SkDConic flip() const {
-        SkDConic result = {{{fPts[2], fPts[1], fPts[0]}}, fWeight};
+        SkDConic result = {{{fPts[2], fPts[1], fPts[0]}
+                SkDEBUGPARAMS(fPts.fDebugGlobalState) }, fWeight};
         return result;
     }
 
+#ifdef SK_DEBUG
+    SkOpGlobalState* globalState() const { return fPts.globalState(); }
+#endif
+
     static bool IsConic() { return true; }
 
-    const SkDConic& set(const SkPoint pts[kPointCount], SkScalar weight) {
-        fPts.set(pts);
+    const SkDConic& set(const SkPoint pts[kPointCount], SkScalar weight
+            SkDEBUGPARAMS(SkOpGlobalState* state = nullptr)) {
+        fPts.set(pts  SkDEBUGPARAMS(state));
         fWeight = weight;
         return *this;
     }
@@ -117,6 +125,7 @@
     void dump() const;
     void dumpID(int id) const;
     void dumpInner() const;
+
 };
 
 
diff --git a/src/pathops/SkPathOpsCubic.cpp b/src/pathops/SkPathOpsCubic.cpp
index bdae492..eaf9062 100644
--- a/src/pathops/SkPathOpsCubic.cpp
+++ b/src/pathops/SkPathOpsCubic.cpp
@@ -36,7 +36,7 @@
     double calcDist = calcPos - axisIntercept;
     do {
         double priorT = t - step;
-        SkASSERT(priorT >= min);
+        SkOPASSERT(priorT >= min);
         SkDPoint lessPt = ptAtT(priorT);
         if (approximately_equal_half(lessPt.fX, cubicAtT.fX)
                 && approximately_equal_half(lessPt.fY, cubicAtT.fY)) {
diff --git a/src/pathops/SkPathOpsCubic.h b/src/pathops/SkPathOpsCubic.h
index 16bca79..f868fbe 100644
--- a/src/pathops/SkPathOpsCubic.h
+++ b/src/pathops/SkPathOpsCubic.h
@@ -58,6 +58,8 @@
         sk_bzero(fPts, sizeof(fPts));
     }
 
+    void debugSet(const SkDPoint* pts);
+
     void dump() const;  // callable from the debugger when the implementation code is linked in
     void dumpID(int id) const;
     void dumpInner() const;
@@ -72,6 +74,11 @@
     }
 
     int findMaxCurvature(double tValues[]) const;
+
+#ifdef SK_DEBUG
+    SkOpGlobalState* globalState() const { return fDebugGlobalState; }
+#endif
+
     bool hullIntersects(const SkDCubic& c2, bool* isLinear) const;
     bool hullIntersects(const SkDConic& c, bool* isLinear) const;
     bool hullIntersects(const SkDQuad& c2, bool* isLinear) const;
@@ -98,11 +105,14 @@
      */
     int verticalIntersect(double xIntercept, double roots[3]) const;
 
-    const SkDCubic& set(const SkPoint pts[kPointCount]) {
+// add debug only global pointer so asserts can be skipped by fuzzers
+    const SkDCubic& set(const SkPoint pts[kPointCount]
+            SkDEBUGPARAMS(SkOpGlobalState* state = nullptr)) {
         fPts[0] = pts[0];
         fPts[1] = pts[1];
         fPts[2] = pts[2];
         fPts[3] = pts[3];
+        SkDEBUGCODE(fDebugGlobalState = state);
         return *this;
     }
 
@@ -125,8 +135,8 @@
     SkDQuad toQuad() const;
 
     static const int gPrecisionUnit;
-
     SkDPoint fPts[kPointCount];
+    SkDEBUGCODE(SkOpGlobalState* fDebugGlobalState);
 };
 
 /* Given the set [0, 1, 2, 3], and two of the four members, compute an XOR mask
diff --git a/src/pathops/SkPathOpsDebug.cpp b/src/pathops/SkPathOpsDebug.cpp
index a0fcff5..476fafb 100644
--- a/src/pathops/SkPathOpsDebug.cpp
+++ b/src/pathops/SkPathOpsDebug.cpp
@@ -691,8 +691,8 @@
 }
 #endif
 
+#include "SkPathOpsConic.h"
 #include "SkPathOpsCubic.h"
-#include "SkPathOpsQuad.h"
 
 SkDCubic SkDQuad::debugToCubic() const {
     SkDCubic cubic;
@@ -706,6 +706,21 @@
     return cubic;
 }
 
+void SkDQuad::debugSet(const SkDPoint* pts) {
+    memcpy(fPts, pts, sizeof(fPts));
+    SkDEBUGCODE(fDebugGlobalState = nullptr);
+}
+
+void SkDCubic::debugSet(const SkDPoint* pts) {
+    memcpy(fPts, pts, sizeof(fPts));
+    SkDEBUGCODE(fDebugGlobalState = nullptr);
+}
+
+void SkDConic::debugSet(const SkDPoint* pts, SkScalar weight) {
+    fPts.debugSet(pts);
+    fWeight = weight;
+}
+
 void SkDRect::debugInit() {
     fLeft = fTop = fRight = fBottom = SK_ScalarNaN;
 }
@@ -1595,6 +1610,7 @@
 // for each coincident pair, match the spans
 // if the spans don't match, add the mssing pt to the segment and loop it in the opposite span
 void SkOpCoincidence::debugAddExpanded(SkPathOpsDebug::GlitchLog* log) const {
+//    DEBUG_SET_PHASE();
     const SkCoincidentSpans* coin = this->fHead;
     if (!coin) {
         return;
@@ -1639,14 +1655,15 @@
                         walk = walk->upCast()->next();
                     } while (!(walkOpp = walk->ptT()->contains(oSeg))
                             && walk != coin->coinPtTEnd()->span());
+                    FAIL_IF(!walkOpp, coin);
                     nextT = walk->t();
                     oNextT = walkOpp->fT;
                 }
                 // use t ranges to guess which one is missing
-                double startRange = coin->coinPtTEnd()->fT - startPtT->fT;
+                double startRange = nextT - priorT;
                 FAIL_IF(!startRange, coin);
-                double startPart = (test->t() - startPtT->fT) / startRange;
-                double oStartRange = coin->oppPtTEnd()->fT - oStartPtT->fT;
+                double startPart = (test->t() - priorT) / startRange;
+                double oStartRange = oNextT - oPriorT;
                 FAIL_IF(!oStartRange, coin);
                 double oStartPart = (oTest->t() - oStartPtT->fT) / oStartRange;
                 FAIL_IF(startPart == oStartPart, coin);
@@ -2029,7 +2046,8 @@
         const SkOpSegment* oSegment = oStart->segment();
         const SkOpSpanBase* next = start;
         const SkOpSpanBase* oNext = oStart;
-        bool ordered = coin->ordered();
+        bool ordered;
+        FAIL_IF(!coin->ordered(&ordered), coin);
         while ((next = next->upCast()->next()) != end) {
             FAIL_IF(!next->upCastable(), coin);
             if (next->upCast()->debugInsertCoincidence(log, oSegment, flipped, ordered), false) {
diff --git a/src/pathops/SkPathOpsQuad.h b/src/pathops/SkPathOpsQuad.h
index 32cfe58..34740d6 100644
--- a/src/pathops/SkPathOpsQuad.h
+++ b/src/pathops/SkPathOpsQuad.h
@@ -40,17 +40,21 @@
         sk_bzero(fPts, sizeof(fPts));
     }
 
+    void debugSet(const SkDPoint* pts);
+
     SkDQuad flip() const {
-        SkDQuad result = {{fPts[2], fPts[1], fPts[0]}};
+        SkDQuad result = {{fPts[2], fPts[1], fPts[0]}  SkDEBUGPARAMS(fDebugGlobalState) };
         return result;
     }
 
     static bool IsConic() { return false; }
 
-    const SkDQuad& set(const SkPoint pts[kPointCount]) {
+    const SkDQuad& set(const SkPoint pts[kPointCount]
+            SkDEBUGPARAMS(SkOpGlobalState* state = nullptr)) {
         fPts[0] = pts[0];
         fPts[1] = pts[1];
         fPts[2] = pts[2];
+        SkDEBUGCODE(fDebugGlobalState = state);
         return *this;
     }
 
@@ -63,6 +67,10 @@
     SkDVector dxdyAtT(double t) const;
     static int FindExtrema(const double src[], double tValue[1]);
 
+#ifdef SK_DEBUG
+    SkOpGlobalState* globalState() const { return fDebugGlobalState; }
+#endif
+
     /**
      *  Return the number of valid roots (0 < root < 1) for this cubic intersecting the
      *  specified horizontal line.
@@ -106,8 +114,7 @@
     void dumpID(int id) const;
     void dumpInner() const;
 
-private:
-//  static double Tangent(const double* quadratic, double t);  // uncalled
+    SkDEBUGCODE(SkOpGlobalState* fDebugGlobalState);
 };
 
 #endif
diff --git a/src/pathops/SkPathOpsRect.h b/src/pathops/SkPathOpsRect.h
index d4e5f54..1efbb8c 100644
--- a/src/pathops/SkPathOpsRect.h
+++ b/src/pathops/SkPathOpsRect.h
@@ -64,6 +64,10 @@
     }
 
     void setBounds(const SkDQuad& curve, const SkDQuad& sub, double tStart, double tEnd);
+
+    bool valid() const {
+        return fLeft <= fRight && fTop <= fBottom;
+    }
 };
 
 #endif
diff --git a/src/pathops/SkPathOpsTSect.cpp b/src/pathops/SkPathOpsTSect.cpp
index 3e7817c..9bff5af 100644
--- a/src/pathops/SkPathOpsTSect.cpp
+++ b/src/pathops/SkPathOpsTSect.cpp
@@ -9,54 +9,54 @@
 
 int SkIntersections::intersect(const SkDQuad& quad1, const SkDQuad& quad2) {
     SkTSect<SkDQuad, SkDQuad> sect1(quad1 
-        SkDEBUGPARAMS(debugGlobalState())  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+        SkDEBUGPARAMS(globalState())  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
     SkTSect<SkDQuad, SkDQuad> sect2(quad2 
-        SkDEBUGPARAMS(debugGlobalState())  PATH_OPS_DEBUG_T_SECT_PARAMS(2));
+        SkDEBUGPARAMS(globalState())  PATH_OPS_DEBUG_T_SECT_PARAMS(2));
     SkTSect<SkDQuad, SkDQuad>::BinarySearch(&sect1, &sect2, this);
     return used();
 }
 
 int SkIntersections::intersect(const SkDConic& conic, const SkDQuad& quad) {
     SkTSect<SkDConic, SkDQuad> sect1(conic 
-        SkDEBUGPARAMS(debugGlobalState())  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+        SkDEBUGPARAMS(globalState())  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
     SkTSect<SkDQuad, SkDConic> sect2(quad 
-        SkDEBUGPARAMS(debugGlobalState())  PATH_OPS_DEBUG_T_SECT_PARAMS(2));
+        SkDEBUGPARAMS(globalState())  PATH_OPS_DEBUG_T_SECT_PARAMS(2));
     SkTSect<SkDConic, SkDQuad>::BinarySearch(&sect1, &sect2, this);
     return used();
 }
 
 int SkIntersections::intersect(const SkDConic& conic1, const SkDConic& conic2) {
     SkTSect<SkDConic, SkDConic> sect1(conic1 
-        SkDEBUGPARAMS(debugGlobalState())  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+        SkDEBUGPARAMS(globalState())  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
     SkTSect<SkDConic, SkDConic> sect2(conic2 
-        SkDEBUGPARAMS(debugGlobalState())  PATH_OPS_DEBUG_T_SECT_PARAMS(2));
+        SkDEBUGPARAMS(globalState())  PATH_OPS_DEBUG_T_SECT_PARAMS(2));
     SkTSect<SkDConic, SkDConic>::BinarySearch(&sect1, &sect2, this);
     return used();
 }
 
 int SkIntersections::intersect(const SkDCubic& cubic, const SkDQuad& quad) {
     SkTSect<SkDCubic, SkDQuad> sect1(cubic 
-        SkDEBUGPARAMS(debugGlobalState())  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+        SkDEBUGPARAMS(globalState())  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
     SkTSect<SkDQuad, SkDCubic> sect2(quad 
-        SkDEBUGPARAMS(debugGlobalState())  PATH_OPS_DEBUG_T_SECT_PARAMS(2));
+        SkDEBUGPARAMS(globalState())  PATH_OPS_DEBUG_T_SECT_PARAMS(2));
     SkTSect<SkDCubic, SkDQuad>::BinarySearch(&sect1, &sect2, this);
     return used();
 }
 
 int SkIntersections::intersect(const SkDCubic& cubic, const SkDConic& conic) {
     SkTSect<SkDCubic, SkDConic> sect1(cubic 
-        SkDEBUGPARAMS(debugGlobalState())  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+        SkDEBUGPARAMS(globalState())  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
     SkTSect<SkDConic, SkDCubic> sect2(conic 
-        SkDEBUGPARAMS(debugGlobalState())  PATH_OPS_DEBUG_T_SECT_PARAMS(2));
+        SkDEBUGPARAMS(globalState())  PATH_OPS_DEBUG_T_SECT_PARAMS(2));
     SkTSect<SkDCubic, SkDConic>::BinarySearch(&sect1, &sect2, this);
     return used();
 }
 
 int SkIntersections::intersect(const SkDCubic& cubic1, const SkDCubic& cubic2) {
     SkTSect<SkDCubic, SkDCubic> sect1(cubic1 
-        SkDEBUGPARAMS(debugGlobalState())  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+        SkDEBUGPARAMS(globalState())  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
     SkTSect<SkDCubic, SkDCubic> sect2(cubic2 
-        SkDEBUGPARAMS(debugGlobalState())  PATH_OPS_DEBUG_T_SECT_PARAMS(2));
+        SkDEBUGPARAMS(globalState())  PATH_OPS_DEBUG_T_SECT_PARAMS(2));
     SkTSect<SkDCubic, SkDCubic>::BinarySearch(&sect1, &sect2, this);
     return used();
 }
diff --git a/src/pathops/SkPathOpsTSect.h b/src/pathops/SkPathOpsTSect.h
index f22322b..51ea44a 100644
--- a/src/pathops/SkPathOpsTSect.h
+++ b/src/pathops/SkPathOpsTSect.h
@@ -137,7 +137,7 @@
 
     int hullsIntersect(SkTSpan<OppCurve, TCurve>* span, bool* start, bool* oppStart);
     void init(const TCurve& );
-    void initBounds(const TCurve& );
+    bool initBounds(const TCurve& );
 
     bool isBounded() const {
         return fBounded != nullptr;
@@ -317,7 +317,7 @@
     void removeSpans(SkTSpan<TCurve, OppCurve>* span, SkTSect<OppCurve, TCurve>* opp);
     SkTSpan<TCurve, OppCurve>* spanAtT(double t, SkTSpan<TCurve, OppCurve>** priorSpan);
     SkTSpan<TCurve, OppCurve>* tail();
-    void trim(SkTSpan<TCurve, OppCurve>* span, SkTSect<OppCurve, TCurve>* opp);
+    bool trim(SkTSpan<TCurve, OppCurve>* span, SkTSect<OppCurve, TCurve>* opp);
     void unlinkSpan(SkTSpan<TCurve, OppCurve>* span);
     bool updateBounded(SkTSpan<TCurve, OppCurve>* first, SkTSpan<TCurve, OppCurve>* last,
                        SkTSpan<OppCurve, TCurve>* oppFirst);
@@ -351,7 +351,7 @@
         const SkDPoint& cPt, const OppCurve& c2) {
     SkDVector dxdy = c1.dxdyAtT(t);
     SkDLine perp = {{ cPt, {cPt.fX + dxdy.fY, cPt.fY - dxdy.fX} }};
-    SkIntersections i;
+    SkIntersections i  SkDEBUGCODE((c1.globalState()));
     int used = i.intersectRay(c2, perp);
     // only keep closest
     if (used == 0 || used == 3) {
@@ -565,7 +565,7 @@
 }
 
 template<typename TCurve, typename OppCurve>
-void SkTSpan<TCurve, OppCurve>::initBounds(const TCurve& c) {
+bool SkTSpan<TCurve, OppCurve>::initBounds(const TCurve& c) {
     fPart = c.subDivide(fStartT, fEndT);
     fBounds.setBounds(fPart);
     fCoinStart.init();
@@ -579,6 +579,7 @@
         SkDebugf("");  // for convenient breakpoints
     }
 #endif
+    return fBounds.valid();
 }
 
 template<typename TCurve, typename OppCurve>
@@ -1206,6 +1207,7 @@
         }
     } else {
         SkDEBUGCODE(coinStart = first->fStartT);
+        FAIL_IF(!oppFirst);
         SkDEBUGCODE(oppStartT = oppMatched ? oppFirst->fStartT : oppFirst->fEndT);
     }
     // FIXME: incomplete : if we're not at the end, find end of coin
@@ -1286,7 +1288,7 @@
             work->validatePerpT(work->fCoinStart.perpT());
             work->validatePerpPt(work->fCoinStart.perpT(), work->fCoinStart.perpPt());
 #endif
-            SkASSERT(work->hasOppT(work->fCoinStart.perpT()));
+            SkOPASSERT(work->hasOppT(work->fCoinStart.perpT()));
             if (!work->fCoinEnd.isMatch()) {
                 break;
             }
@@ -1400,7 +1402,8 @@
 int SkTSect<TCurve, OppCurve>::linesIntersect(SkTSpan<TCurve, OppCurve>* span,
         SkTSect<OppCurve, TCurve>* opp,
         SkTSpan<OppCurve, TCurve>* oppSpan, SkIntersections* i) {
-    SkIntersections thisRayI, oppRayI;
+    SkIntersections thisRayI  SkDEBUGCODE((span->fDebugGlobalState));
+    SkIntersections oppRayI  SkDEBUGCODE((span->fDebugGlobalState));
     SkDLine thisLine = {{ span->fPart[0], span->fPart[TCurve::kPointLast] }};
     SkDLine oppLine = {{ oppSpan->fPart[0], oppSpan->fPart[OppCurve::kPointLast] }};
     int loopCount = 0;
@@ -1810,9 +1813,9 @@
 /* Each span has a range of opposite spans it intersects. After the span is split in two,
     adjust the range to its new size */
 template<typename TCurve, typename OppCurve>
-void SkTSect<TCurve, OppCurve>::trim(SkTSpan<TCurve, OppCurve>* span,
+bool SkTSect<TCurve, OppCurve>::trim(SkTSpan<TCurve, OppCurve>* span,
         SkTSect<OppCurve, TCurve>* opp) {
-    span->initBounds(fCurve);
+    FAIL_IF(!span->initBounds(fCurve));
     const SkTSpanBounded<OppCurve, TCurve>* testBounded = span->fBounded;
     while (testBounded) {
         SkTSpan<OppCurve, TCurve>* test = testBounded->fBounded;
@@ -1826,7 +1829,7 @@
             if (sects == 2) {
                 span->initBounds(fCurve);
                 this->removeAllBut(test, span, opp);
-                return;
+                return true;
             }
         } else {
             if (span->removeBounded(test)) {
@@ -1838,6 +1841,7 @@
         }
         testBounded = next;
     }
+    return true;
 }
 
 template<typename TCurve, typename OppCurve>
@@ -2112,7 +2116,7 @@
     SkDEBUGCODE(sect1->fOppSect = sect2);
     SkDEBUGCODE(sect2->fOppSect = sect1);
     intersections->reset();
-    intersections->setMax(TCurve::kMaxIntersections + 3);  // give extra for slop
+    intersections->setMax(TCurve::kMaxIntersections + 4);  // give extra for slop
     SkTSpan<TCurve, OppCurve>* span1 = sect1->fHead;
     SkTSpan<OppCurve, TCurve>* span2 = sect2->fHead;
     int oppSect, sect = sect1->intersects(span1, sect2, span2, &oppSect);
@@ -2151,8 +2155,14 @@
             if (!half1->split(largest1, &sect1->fHeap)) {
                 break;
             }
-            sect1->trim(largest1, sect2);
-            sect1->trim(half1, sect2);
+            if (!sect1->trim(largest1, sect2)) {
+                SkOPOBJASSERT(intersections, 0);
+                return;
+            }
+            if (!sect1->trim(half1, sect2)) {
+                SkOPOBJASSERT(intersections, 0);
+                return;
+            }
         } else {
             if (largest2->fCollapsed) {
                 break;
@@ -2163,8 +2173,14 @@
             if (!half2->split(largest2, &sect2->fHeap)) {
                 break;
             }
-            sect2->trim(largest2, sect1);
-            sect2->trim(half2, sect1);
+            if (!sect2->trim(largest2, sect1)) {
+                SkOPOBJASSERT(intersections, 0);
+                return;
+            }
+            if (!sect2->trim(half2, sect1)) {
+                SkOPOBJASSERT(intersections, 0);
+                return;
+            }
         }
         sect1->validate();
         sect2->validate();
@@ -2250,28 +2266,28 @@
         // if the final iteration contains an end (0 or 1),
         if (sect1->fRemovedStartT && !(zeroOneSet & kZeroS1Set)) {
             SkTCoincident<TCurve, OppCurve> perp;   // intersect perpendicular with opposite curve
-            perp.setPerp(sect1->fCurve, 0, sect1->fCurve.fPts[0], sect2->fCurve);
+            perp.setPerp(sect1->fCurve, 0, sect1->fCurve[0], sect2->fCurve);
             if (perp.isMatch()) {
                 intersections->insert(0, perp.perpT(), perp.perpPt());
             }
         }
         if (sect1->fRemovedEndT && !(zeroOneSet & kOneS1Set)) {
             SkTCoincident<TCurve, OppCurve> perp;
-            perp.setPerp(sect1->fCurve, 1, sect1->fCurve.fPts[TCurve::kPointLast], sect2->fCurve);
+            perp.setPerp(sect1->fCurve, 1, sect1->fCurve[TCurve::kPointLast], sect2->fCurve);
             if (perp.isMatch()) {
                 intersections->insert(1, perp.perpT(), perp.perpPt());
             }
         }
         if (sect2->fRemovedStartT && !(zeroOneSet & kZeroS2Set)) {
             SkTCoincident<OppCurve, TCurve> perp;
-            perp.setPerp(sect2->fCurve, 0, sect2->fCurve.fPts[0], sect1->fCurve);
+            perp.setPerp(sect2->fCurve, 0, sect2->fCurve[0], sect1->fCurve);
             if (perp.isMatch()) {
                 intersections->insert(perp.perpT(), 0, perp.perpPt());
             }
         }
         if (sect2->fRemovedEndT && !(zeroOneSet & kOneS2Set)) {
             SkTCoincident<OppCurve, TCurve> perp;
-            perp.setPerp(sect2->fCurve, 1, sect2->fCurve.fPts[OppCurve::kPointLast], sect1->fCurve);
+            perp.setPerp(sect2->fCurve, 1, sect2->fCurve[OppCurve::kPointLast], sect1->fCurve);
             if (perp.isMatch()) {
                 intersections->insert(perp.perpT(), 1, perp.perpPt());
             }
@@ -2365,7 +2381,7 @@
         }
         intersections->setCoincident(index);
     }
-    SkASSERT(intersections->used() <= TCurve::kMaxIntersections);
+    SkOPOBJASSERT(intersections, intersections->used() <= TCurve::kMaxIntersections);
 }
 
 #endif
diff --git a/src/pathops/SkPathOpsTypes.h b/src/pathops/SkPathOpsTypes.h
index 786eb22..e390b4b 100644
--- a/src/pathops/SkPathOpsTypes.h
+++ b/src/pathops/SkPathOpsTypes.h
@@ -221,8 +221,8 @@
 #define SkOPASSERT(cond) SkASSERT((this->globalState() && \
         this->globalState()->debugSkipAssert()) || (cond))
 #endif
-#define SkOPOBJASSERT(obj, cond) SkASSERT((obj->debugGlobalState() && \
-        obj->debugGlobalState()->debugSkipAssert()) || (cond))
+#define SkOPOBJASSERT(obj, cond) SkASSERT((obj->globalState() && \
+        obj->globalState()->debugSkipAssert()) || (cond))
 #else
 #define SkOPASSERT(cond)
 #define SkOPOBJASSERT(obj, cond)
diff --git a/src/pathops/SkPathWriter.cpp b/src/pathops/SkPathWriter.cpp
index 1f6dddd..c94809e 100644
--- a/src/pathops/SkPathWriter.cpp
+++ b/src/pathops/SkPathWriter.cpp
@@ -48,23 +48,26 @@
     fCurrent.cubicTo(pt1, pt2, pt3->fPt);
 }
 
-void SkPathWriter::deferredLine(const SkOpPtT* pt) {
+bool SkPathWriter::deferredLine(const SkOpPtT* pt) {
     SkASSERT(fFirstPtT);
     SkASSERT(fDefer[0]);
     if (fDefer[0] == pt) {
         // FIXME: why we're adding a degenerate line? Caller should have preflighted this.
-        return;
+        return true;
     }
     if (pt->contains(fDefer[0])) {
         // FIXME: why we're adding a degenerate line?
-        return;
+        return true;
     }
-    SkASSERT(!this->matchedLast(pt));
+    if (this->matchedLast(pt)) {
+        return false;
+    }
     if (fDefer[1] && this->changedSlopes(pt)) {
         this->lineTo();
         fDefer[0] = fDefer[1];
     }
     fDefer[1] = pt;
+    return true;
 }
 
 void SkPathWriter::deferredMove(const SkOpPtT* pt) {
diff --git a/src/pathops/SkPathWriter.h b/src/pathops/SkPathWriter.h
index bd13c71..5dd1bf6 100644
--- a/src/pathops/SkPathWriter.h
+++ b/src/pathops/SkPathWriter.h
@@ -23,7 +23,7 @@
     void assemble();
     void conicTo(const SkPoint& pt1, const SkOpPtT* pt2, SkScalar weight);
     void cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkOpPtT* pt3);
-    void deferredLine(const SkOpPtT* pt);
+    bool deferredLine(const SkOpPtT* pt);
     void deferredMove(const SkOpPtT* pt);
     void finishContour();
     bool hasMove() const { return !fFirstPtT; }