• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2017 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "bookmaker.h"
9 #include "SkOSPath.h"
10 
count_indent(const string & text,size_t test,size_t end)11 static size_t count_indent(const string& text, size_t test, size_t end) {
12     size_t result = test;
13     while (test < end) {
14         if (' ' != text[test]) {
15             break;
16         }
17         ++test;
18     }
19     return test - result;
20 }
21 
add_code(const string & text,int pos,int end,size_t outIndent,size_t textIndent,string & example)22 static void add_code(const string& text, int pos, int end,
23         size_t outIndent, size_t textIndent, string& example) {
24     do {
25          // fix this to move whole paragraph in, out, but preserve doc indent
26         int nextIndent = count_indent(text, pos, end);
27         size_t len = text.find('\n', pos);
28         if (string::npos == len) {
29             len = end;
30         }
31         if ((size_t) (pos + nextIndent) < len) {
32             size_t indent = outIndent + nextIndent;
33             SkASSERT(indent >= textIndent);
34             indent -= textIndent;
35             for (size_t index = 0; index < indent; ++index) {
36                 example += ' ';
37             }
38             pos += nextIndent;
39             while ((size_t) pos < len) {
40                 example += '"' == text[pos] ? "\\\"" :
41                     '\\' == text[pos] ? "\\\\" :
42                     text.substr(pos, 1);
43                 ++pos;
44             }
45             example += "\\n";
46         } else {
47             pos += nextIndent;
48         }
49         if ('\n' == text[pos]) {
50             ++pos;
51         }
52     } while (pos < end);
53 }
54 
55 #ifdef CONST
56 #undef CONST
57 #endif
58 
59 #ifdef FRIEND
60 #undef FRIEND
61 #endif
62 
63 #ifdef BLANK
64 #undef BLANK
65 #endif
66 
67 #ifdef ANY
68 #undef ANY
69 #endif
70 
71 #ifdef DEFOP
72 #undef DEFOP
73 #endif
74 
75 #define CONST 1
76 #define STATIC 2
77 #define BLANK  0
78 #define ANY -1
79 #define DEFOP Definition::Operator
80 
81 enum class OpType : int8_t {
82     kNone,
83     kVoid,
84     kBool,
85     kChar,
86     kFloat,
87     kInt,
88     kScalar,
89     kSizeT,
90     kThis,
91     kAny,
92 };
93 
94 enum class OpMod : int8_t {
95     kNone,
96     kArray,
97     kMove,
98     kPointer,
99     kReference,
100     kAny,
101 };
102 
103 const struct OperatorParser {
104     DEFOP fOperator;
105     const char* fSymbol;
106     const char* fName;
107     int8_t fFriend;
108     OpType fReturnType;
109     OpMod fReturnMod;
110     int8_t fConstMethod;
111     struct Param {
112         int8_t fConst;
113         OpType fType;
114         OpMod fMod;
115     } fParams[2];
116 } opData[] = {
117     { DEFOP::kUnknown, "??", "???",    BLANK,  OpType::kNone,   OpMod::kNone,         BLANK,
118                                     { } },
119     { DEFOP::kAdd,     "+",  "add",    BLANK,  OpType::kThis,   OpMod::kNone,         BLANK,
120                                     {{ CONST,  OpType::kThis,   OpMod::kReference, },
121                                      { CONST,  OpType::kThis,   OpMod::kReference, }}},
122     { DEFOP::kAddTo,   "+=", "addto",  BLANK,  OpType::kVoid,   OpMod::kNone,         BLANK,
123                                     {{ CONST,  OpType::kThis,   OpMod::kReference, }}},
124     { DEFOP::kAddTo,   "+=", "addto1", BLANK,  OpType::kThis,   OpMod::kReference,    BLANK,
125                                     {{ CONST,  OpType::kThis,   OpMod::kReference, }}},
126     { DEFOP::kAddTo,   "+=", "addto2", BLANK,  OpType::kThis,   OpMod::kReference,    BLANK,
127                                     {{ CONST,  OpType::kChar,   OpMod::kArray, }}},
128     { DEFOP::kAddTo,   "+=", "addto3", BLANK,  OpType::kThis,   OpMod::kReference,    BLANK,
129                                     {{ CONST,  OpType::kChar,   OpMod::kNone, }}},
130     { DEFOP::kArray,   "[]", "array",  BLANK,  OpType::kScalar, OpMod::kNone,         CONST,
131                                     {{ BLANK,  OpType::kInt,    OpMod::kNone, }}},
132     { DEFOP::kArray,   "[]", "array1", BLANK,  OpType::kScalar, OpMod::kReference,    BLANK,
133                                     {{ BLANK,  OpType::kInt,    OpMod::kNone, }}},
134     { DEFOP::kArray,   "[]", "array2", BLANK,  OpType::kChar,   OpMod::kNone,         CONST,
135                                     {{ BLANK,  OpType::kSizeT,  OpMod::kNone, }}},
136     { DEFOP::kArray,   "[]", "array3", BLANK,  OpType::kChar,   OpMod::kReference,    BLANK,
137                                     {{ BLANK,  OpType::kSizeT,  OpMod::kNone, }}},
138     { DEFOP::kCast,    "()", "cast",   BLANK,  OpType::kAny,    OpMod::kAny,          ANY,
139                                     {{ ANY,    OpType::kAny,    OpMod::kAny,  }}},
140     { DEFOP::kCopy,    "=",  "copy",   BLANK,  OpType::kThis,   OpMod::kReference,    BLANK,
141                                     {{ CONST,  OpType::kThis,   OpMod::kReference, }}},
142     { DEFOP::kCopy,    "=",  "copy1",  BLANK,  OpType::kThis,   OpMod::kReference,    BLANK,
143                                     {{ CONST,  OpType::kChar,   OpMod::kArray, }}},
144     { DEFOP::kDelete,  "delete", "delete",  BLANK,  OpType::kVoid,   OpMod::kNone,    BLANK,
145                                     {{ BLANK,  OpType::kVoid,   OpMod::kPointer, }}},
146     { DEFOP::kDereference, "->", "deref",  ANY,  OpType::kThis, OpMod::kPointer,      CONST,
147                                     { } },
148     { DEFOP::kDereference, "*", "deref", BLANK,  OpType::kThis, OpMod::kReference,    CONST,
149                                     { } },
150     { DEFOP::kEqual,   "==", "equal",  BLANK,  OpType::kBool,   OpMod::kNone,         BLANK,
151                                     {{ CONST,  OpType::kThis,   OpMod::kReference, },
152                                      { CONST,  OpType::kThis,   OpMod::kReference, }}},
153     { DEFOP::kEqual,   "==", "equal1",  BLANK,  OpType::kBool,   OpMod::kNone,         CONST,
154                                     {{ CONST,  OpType::kThis,   OpMod::kReference, }}},
155     { DEFOP::kEqual,   "==", "equal2", ANY,    OpType::kBool,   OpMod::kNone,         BLANK,
156                                     {{ CONST,  OpType::kThis,   OpMod::kReference, },
157                                      { CONST,  OpType::kThis,   OpMod::kReference, }}},
158     { DEFOP::kMinus,   "-",  "minus",  BLANK,  OpType::kThis,   OpMod::kNone,         CONST,
159                                     { } },
160     { DEFOP::kMove,    "=",  "move",   BLANK,  OpType::kThis,   OpMod::kReference,    BLANK,
161                                     {{ BLANK,  OpType::kThis,   OpMod::kMove, }}},
162     { DEFOP::kMultiply, "*", "multiply", BLANK, OpType::kThis, OpMod::kNone,         CONST,
163                                     {{ BLANK,  OpType::kScalar, OpMod::kNone, }}},
164     { DEFOP::kMultiply, "*", "multiply1", BLANK, OpType::kThis, OpMod::kNone,         BLANK,
165                                     {{ CONST,  OpType::kThis,   OpMod::kReference, },
166                                      { CONST,  OpType::kThis,   OpMod::kReference, }}},
167     { DEFOP::kMultiplyBy, "*=", "multiplyby", BLANK,  OpType::kThis, OpMod::kReference, BLANK,
168                                     {{ BLANK,  OpType::kScalar, OpMod::kNone, }}},
169     { DEFOP::kNew,     "new", "new",   BLANK,  OpType::kVoid,   OpMod::kPointer,      BLANK,
170                                     {{ BLANK,  OpType::kSizeT,  OpMod::kNone, }}},
171     { DEFOP::kNotEqual, "!=", "notequal", BLANK, OpType::kBool,   OpMod::kNone,      BLANK,
172                                     {{ CONST,  OpType::kThis,   OpMod::kReference, },
173                                      { CONST,  OpType::kThis,   OpMod::kReference, }}},
174     { DEFOP::kNotEqual, "!=", "notequal1", BLANK,  OpType::kBool,   OpMod::kNone,     CONST,
175                                     {{ CONST,  OpType::kThis,   OpMod::kReference, }}},
176     { DEFOP::kNotEqual, "!=", "notequal2", ANY, OpType::kBool,   OpMod::kNone,     BLANK,
177                                     {{ CONST,  OpType::kThis,   OpMod::kReference, },
178                                      { CONST,  OpType::kThis,   OpMod::kReference, }}},
179     { DEFOP::kSubtract, "-", "subtract", BLANK, OpType::kThis, OpMod::kNone,         BLANK,
180                                     {{ CONST,  OpType::kThis,   OpMod::kReference, },
181                                      { CONST,  OpType::kThis,   OpMod::kReference, }}},
182     { DEFOP::kSubtractFrom, "-=", "subtractfrom",  BLANK,  OpType::kVoid,   OpMod::kNone, BLANK,
183                                     {{ CONST,  OpType::kThis,   OpMod::kReference, }}},
184 };
185 
lookup_type(const string & typeWord,const string & name)186 OpType lookup_type(const string& typeWord, const string& name) {
187     if (typeWord == name || (typeWord == "SkIVector" && name == "SkIPoint")
188                          || (typeWord == "SkVector" && name == "SkPoint")) {
189         return OpType::kThis;
190     }
191     const char* keyWords[] = { "void", "bool", "char", "float", "int", "SkScalar", "size_t" };
192     for (unsigned i = 0; i < SK_ARRAY_COUNT(keyWords); ++i) {
193         if (typeWord == keyWords[i]) {
194             return (OpType) (i + 1);
195         }
196     }
197     return OpType::kNone;
198 }
199 
lookup_mod(TextParser & iParser)200 OpMod lookup_mod(TextParser& iParser) {
201     OpMod mod = OpMod::kNone;
202     if ('&' == iParser.peek()) {
203         mod = OpMod::kReference;
204         iParser.next();
205         if ('&' == iParser.peek()) {
206             mod = OpMod::kMove;
207             iParser.next();
208         }
209     } else if ('*' == iParser.peek()) {
210         mod = OpMod::kPointer;
211         iParser.next();
212     }
213     iParser.skipWhiteSpace();
214     return mod;
215 }
216 
parseOperator(size_t doubleColons,string & result)217 bool Definition::parseOperator(size_t doubleColons, string& result) {
218     const char operatorStr[] = "operator";
219     size_t opPos = fName.find(operatorStr, doubleColons);
220     if (string::npos == opPos) {
221         return false;
222     }
223     string className(fName, 0, doubleColons - 2);
224     TextParser iParser(fFileName, fStart, fContentStart, fLineCount);
225     SkAssertResult(iParser.skipWord("#Method"));
226     iParser.skipExact("SK_API");
227     iParser.skipWhiteSpace();
228     bool isStatic = iParser.skipExact("static");
229     iParser.skipWhiteSpace();
230     iParser.skipExact("SK_API");
231     iParser.skipWhiteSpace();
232     bool returnsConst = iParser.skipExact("const");
233     if (returnsConst) {
234         SkASSERT(0);  // incomplete
235     }
236     SkASSERT(isStatic == false || returnsConst == false);
237     iParser.skipWhiteSpace();
238     const char* returnTypeStart = iParser.fChar;
239     iParser.skipToNonAlphaNum();
240     SkASSERT(iParser.fChar > returnTypeStart);
241     string returnType(returnTypeStart, iParser.fChar - returnTypeStart);
242     OpType returnOpType = lookup_type(returnType, className);
243     iParser.skipWhiteSpace();
244     OpMod returnMod = lookup_mod(iParser);
245     SkAssertResult(iParser.skipExact("operator"));
246     iParser.skipWhiteSpace();
247     fMethodType = Definition::MethodType::kOperator;
248     TextParser::Save save(&iParser);
249     for (auto parser : opData) {
250         save.restore();
251         if (!iParser.skipExact(parser.fSymbol)) {
252             continue;
253         }
254         iParser.skipWhiteSpace();
255         if ('(' != iParser.peek()) {
256             continue;
257         }
258         if (parser.fFriend != ANY && (parser.fFriend == STATIC) != isStatic) {
259             continue;
260         }
261         if (parser.fReturnType != OpType::kAny && parser.fReturnType != returnOpType) {
262             continue;
263         }
264         if (parser.fReturnMod != OpMod::kAny && parser.fReturnMod != returnMod) {
265             continue;
266         }
267         iParser.next();  // skip '('
268         iParser.skipWhiteSpace();
269         int parserCount = (parser.fParams[0].fType != OpType::kNone) +
270             (parser.fParams[1].fType != OpType::kNone);
271         bool countsMatch = true;
272         for (int pIndex = 0; pIndex < 2; ++pIndex) {
273             if (')' == iParser.peek()) {
274                 countsMatch = pIndex == parserCount;
275                 break;
276             }
277             if (',' == iParser.peek()) {
278                 iParser.next();
279                 iParser.skipWhiteSpace();
280             }
281             bool paramConst = iParser.skipExact("const");
282             if (parser.fParams[pIndex].fConst != ANY &&
283                     paramConst != (parser.fParams[pIndex].fConst == CONST)) {
284                 countsMatch = false;
285                 break;
286             }
287             iParser.skipWhiteSpace();
288             const char* paramStart = iParser.fChar;
289             iParser.skipToNonAlphaNum();
290             SkASSERT(iParser.fChar > paramStart);
291             string paramType(paramStart, iParser.fChar - paramStart);
292             OpType paramOpType = lookup_type(paramType, className);
293             if (parser.fParams[pIndex].fType != OpType::kAny &&
294                     parser.fParams[pIndex].fType != paramOpType) {
295                 countsMatch = false;
296                 break;
297             }
298             iParser.skipWhiteSpace();
299             OpMod paramMod = lookup_mod(iParser);
300             if (parser.fParams[pIndex].fMod != OpMod::kAny &&
301                     parser.fParams[pIndex].fMod != paramMod) {
302                 countsMatch = false;
303                 break;
304             }
305             iParser.skipToNonAlphaNum();
306             if ('[' == iParser.peek()) {
307                 paramMod = OpMod::kArray;
308                 SkAssertResult(iParser.skipExact("[]"));
309             }
310             iParser.skipWhiteSpace();
311         }
312         if (!countsMatch) {
313             continue;
314         }
315         if (')' != iParser.peek()) {
316             continue;
317         }
318         iParser.next();
319         bool constMethod = iParser.skipExact("_const");
320         if (parser.fConstMethod != ANY && (parser.fConstMethod == CONST) != constMethod) {
321             continue;
322         }
323         result += parser.fName;
324         result += "_operator";
325         fOperator = parser.fOperator;
326         fOperatorConst = constMethod;
327         return true;
328     }
329     SkASSERT(0); // incomplete
330     return false;
331 #if 0
332     if ('!' == fName[opPos]) {
333         SkASSERT('=' == fName[opPos + 1]);
334         result += "not_equal_operator";
335     } else if ('=' == fName[opPos]) {
336         if ('(' == fName[opPos + 1]) {
337             result += isMove ? "move_" : "copy_";
338             result += "assignment_operator";
339         } else {
340             SkASSERT('=' == fName[opPos + 1]);
341             result += "equal_operator";
342         }
343     } else if ('[' == fName[opPos]) {
344         result += "subscript_operator";
345         const char* end = fContentStart;
346         while (end > fStart && ' ' >= end[-1]) {
347             --end;
348         }
349         string constCheck(fStart, end - fStart);
350         size_t constPos = constCheck.rfind("const");
351         if (constCheck.length() == constPos + 5) {
352             result += "_const";
353         }
354     } else if ('*' == fName[opPos]) {
355         result += "multiply_operator";
356     } else if ('-' == fName[opPos]) {
357         result += "subtract_operator";
358     } else if ('+' == fName[opPos]) {
359         result += "add_operator";
360     } else {
361         SkASSERT(0);  // todo: incomplete
362     }
363 #endif
364     return true;
365 }
366 
367 #undef CONST
368 #undef FRIEND
369 #undef BLANK
370 #undef DEFOP
371 
boilerplateIfDef(Definition * parent)372 bool Definition::boilerplateIfDef(Definition* parent) {
373     const Definition& label = fTokens.front();
374     if (Type::kWord != label.fType) {
375         return false;
376     }
377     fName = string(label.fContentStart, label.fContentEnd - label.fContentStart);
378     return true;
379 }
380 
381 // todo: this is matching #ifndef SkXXX_DEFINED for no particular reason
382 // it doesn't do anything useful with arbitrary input, e.g. #ifdef SK_SUPPORT_LEGACY_CANVAS_HELPERS
383 // also doesn't know what to do with SK_REQUIRE_LOCAL_VAR()
boilerplateDef(Definition * parent)384 bool Definition::boilerplateDef(Definition* parent) {
385     if (!this->boilerplateIfDef(parent)) {
386         return false;
387     }
388     const char* s = fName.c_str();
389     const char* e = strchr(s, '_');
390     return true; // fixme: if this is trying to do something useful with define, do it here
391     if (!e) {
392         return false;
393     }
394     string prefix(s, e - s);
395     const char* inName = strstr(parent->fName.c_str(), prefix.c_str());
396     if (!inName) {
397         return false;
398     }
399     if ('/' != inName[-1] && '\\' != inName[-1]) {
400         return false;
401     }
402     if (strcmp(inName + prefix.size(), ".h")) {
403         return false;
404     }
405     return true;
406 }
407 
408 // fixme: this will need to be more complicated to handle all of Skia
409 // for now, just handle paint -- maybe fiddle will loosen naming restrictions
setCanonicalFiddle()410 void Definition::setCanonicalFiddle() {
411     fMethodType = Definition::MethodType::kNone;
412     size_t doubleColons = fName.find("::", 0);
413     SkASSERT(string::npos != doubleColons);
414     string base = fName.substr(0, doubleColons);
415     string result = base + "_";
416     doubleColons += 2;
417     if (string::npos != fName.find('~', doubleColons)) {
418         fMethodType = Definition::MethodType::kDestructor;
419         result += "destructor";
420     } else if (!this->parseOperator(doubleColons, result)) {
421         bool isMove = string::npos != fName.find("&&", doubleColons);
422         size_t parens = fName.find("()", doubleColons);
423         if (string::npos != parens) {
424             string methodName = fName.substr(doubleColons, parens - doubleColons);
425             do {
426                 size_t nextDouble = methodName.find("::");
427                 if (string::npos == nextDouble) {
428                     break;
429                 }
430                 base = methodName.substr(0, nextDouble);
431                 result += base + '_';
432                 methodName = methodName.substr(nextDouble + 2);
433                 doubleColons += nextDouble + 2;
434             } while (true);
435             if (base == methodName) {
436                 fMethodType = Definition::MethodType::kConstructor;
437                 result += "empty_constructor";
438             } else {
439                 result += fName.substr(doubleColons, fName.length() - doubleColons - 2);
440             }
441         } else {
442             size_t openParen = fName.find('(', doubleColons);
443             if (string::npos == openParen) {
444                 result += fName.substr(doubleColons);
445             } else {
446                 size_t comma = fName.find(',', doubleColons);
447                 if (string::npos == comma) {
448                     result += isMove ? "move_" : "copy_";
449                 }
450                 fMethodType = Definition::MethodType::kConstructor;
451                 // name them by their param types,
452                 //   e.g. SkCanvas__int_int_const_SkSurfaceProps_star
453                 // TODO: move forward until parens are balanced and terminator =,)
454                 TextParser params("", &fName[openParen] + 1, &*fName.end(), 0);
455                 bool underline = false;
456                 while (!params.eof()) {
457 //                    SkDEBUGCODE(const char* end = params.anyOf("(),="));  // unused for now
458 //                    SkASSERT(end[0] != '(');  // fixme: put off handling nested parentheseses
459                     if (params.startsWith("const") || params.startsWith("int")
460                             || params.startsWith("Sk")) {
461                         const char* wordStart = params.fChar;
462                         params.skipToNonAlphaNum();
463                         if (underline) {
464                             result += '_';
465                         } else {
466                             underline = true;
467                         }
468                         result += string(wordStart, params.fChar - wordStart);
469                     } else {
470                         params.skipToNonAlphaNum();
471                     }
472                     if (!params.eof() && '*' == params.peek()) {
473                         if (underline) {
474                             result += '_';
475                         } else {
476                             underline = true;
477                         }
478                         result += "star";
479                         params.next();
480                         params.skipSpace();
481                     }
482                     params.skipToAlpha();
483                 }
484             }
485         }
486     }
487     fFiddle = Definition::NormalizedName(result);
488 }
489 
setWrapper()490 void Definition::setWrapper() {
491     const char drawWrapper[] = "void draw(SkCanvas* canvas) {";
492     const char drawNoCanvas[] = "void draw(SkCanvas* ) {";
493     string text = this->extractText(Definition::TrimExtract::kNo);
494     size_t nonSpace = 0;
495     while (nonSpace < text.length() && ' ' >= text[nonSpace]) {
496         ++nonSpace;
497     }
498     bool hasFunc = !text.compare(nonSpace, sizeof(drawWrapper) - 1, drawWrapper);
499     bool noCanvas = !text.compare(nonSpace, sizeof(drawNoCanvas) - 1, drawNoCanvas);
500     bool hasCanvas = string::npos != text.find("SkCanvas canvas");
501     SkASSERT(!hasFunc || !noCanvas);
502     bool preprocessor = text[0] == '#';
503     bool wrapCode = !hasFunc && !noCanvas && !preprocessor;
504     if (wrapCode) {
505         fWrapper = hasCanvas ? string(drawNoCanvas) : string(drawWrapper);
506     }
507 }
508 
exampleToScript(string * result,ExampleOptions exampleOptions) const509 bool Definition::exampleToScript(string* result, ExampleOptions exampleOptions) const {
510     bool hasFiddle = true;
511     const Definition* platform = this->hasChild(MarkType::kPlatform);
512     if (platform) {
513         TextParser platParse(platform);
514         hasFiddle = !platParse.strnstr("!fiddle", platParse.fEnd);
515     }
516     if (!hasFiddle) {
517         *result = "";
518         return true;
519     }
520     string text = this->extractText(Definition::TrimExtract::kNo);
521     bool textOut = string::npos != text.find("SkDebugf(")
522             || string::npos != text.find("dump(")
523             || string::npos != text.find("dumpHex(");
524     string heightStr = "256";
525     string widthStr = "256";
526     string normalizedName(fFiddle);
527     string code;
528     string imageStr = "0";
529     string srgbStr = "false";
530     string durationStr = "0";
531     for (auto const& iter : fChildren) {
532         switch (iter->fMarkType) {
533             case MarkType::kDuration:
534                 durationStr = string(iter->fContentStart, iter->fContentEnd - iter->fContentStart);
535                 break;
536             case MarkType::kHeight:
537                 heightStr = string(iter->fContentStart, iter->fContentEnd - iter->fContentStart);
538                 break;
539             case MarkType::kWidth:
540                 widthStr = string(iter->fContentStart, iter->fContentEnd - iter->fContentStart);
541                 break;
542             case MarkType::kDescription:
543                 // ignore for now
544                 break;
545             case MarkType::kFunction: {
546                 // emit this, but don't wrap this in draw()
547                 string funcText(iter->fContentStart, iter->fContentEnd - iter->fContentStart);
548                 size_t pos = 0;
549                 while (pos < funcText.length() && ' ' > funcText[pos]) {
550                     ++pos;
551                 }
552                 size_t indent = count_indent(funcText, pos, funcText.length());
553                 add_code(funcText, pos, funcText.length(), 0, indent, code);
554                 code += "\\n";
555                 } break;
556             case MarkType::kComment:
557                 break;
558             case MarkType::kImage:
559                 imageStr = string(iter->fContentStart, iter->fContentEnd - iter->fContentStart);
560                 break;
561             case MarkType::kToDo:
562                 break;
563             case MarkType::kMarkChar:
564             case MarkType::kPlatform:
565                 // ignore for now
566                 break;
567             case MarkType::kSet:
568                 if ("sRGB" == string(iter->fContentStart,
569                                      iter->fContentEnd - iter->fContentStart)) {
570                     srgbStr = "true";
571                 } else {
572                     SkASSERT(0);   // more work to do
573                     return false;
574                 }
575                 break;
576             case MarkType::kStdOut:
577                 textOut = true;
578                 break;
579             default:
580                 SkASSERT(0);  // more coding to do
581         }
582     }
583     string animatedStr = "0" != durationStr ? "true" : "false";
584     string textOutStr = textOut ? "true" : "false";
585     size_t pos = 0;
586     while (pos < text.length() && ' ' > text[pos]) {
587         ++pos;
588     }
589     size_t end = text.length();
590     size_t outIndent = 0;
591     size_t textIndent = count_indent(text, pos, end);
592     if (fWrapper.length() > 0) {
593         code += fWrapper;
594         code += "\\n";
595         outIndent = 4;
596     }
597     add_code(text, pos, end, outIndent, textIndent, code);
598     if (fWrapper.length() > 0) {
599         code += "}";
600     }
601     string example = "\"" + normalizedName + "\": {\n";
602     size_t nameStart = fFileName.find(SkOSPath::SEPARATOR, 0);
603     SkASSERT(string::npos != nameStart);
604     string baseFile = fFileName.substr(nameStart + 1, fFileName.length() - nameStart - 5);
605     if (ExampleOptions::kText == exampleOptions) {
606         example += "    \"code\": \"" + code + "\",\n";
607         example += "    \"hash\": \"" + fHash + "\",\n";
608         example += "    \"file\": \"" + baseFile + "\",\n";
609         example += "    \"name\": \"" + fName + "\",";
610     } else {
611         example += "    \"code\": \"" + code + "\",\n";
612         if (ExampleOptions::kPng == exampleOptions) {
613             example += "    \"width\": " + widthStr + ",\n";
614             example += "    \"height\": " + heightStr + ",\n";
615             example += "    \"hash\": \"" + fHash + "\",\n";
616             example += "    \"file\": \"" + baseFile + "\",\n";
617             example += "    \"name\": \"" + fName + "\"\n";
618             example += "}";
619        } else {
620             example += "    \"options\": {\n";
621             example += "        \"width\": " + widthStr + ",\n";
622             example += "        \"height\": " + heightStr + ",\n";
623             example += "        \"source\": " + imageStr + ",\n";
624             example += "        \"srgb\": " + srgbStr + ",\n";
625             example += "        \"f16\": false,\n";
626             example += "        \"textOnly\": " + textOutStr + ",\n";
627             example += "        \"animated\": " + animatedStr + ",\n";
628             example += "        \"duration\": " + durationStr + "\n";
629             example += "    },\n";
630             example += "    \"fast\": true";
631         }
632     }
633     *result = example;
634     return true;
635 }
636 
extractText(TrimExtract trimExtract) const637 string Definition::extractText(TrimExtract trimExtract) const {
638     string result;
639     TextParser parser(fFileName, fContentStart, fContentEnd, fLineCount);
640     int childIndex = 0;
641     char mc = '#';
642     while (parser.fChar < parser.fEnd) {
643         if (TrimExtract::kYes == trimExtract && !parser.skipWhiteSpace()) {
644             break;
645         }
646         if (parser.next() == mc) {
647             if (parser.next() == mc) {
648                 if (parser.next() == mc) {
649                     mc = parser.next();
650                 }
651             } else {
652                 // fixme : more work to do if # style comment is in text
653                 // if in method definition, could be alternate method name
654                 --parser.fChar;
655                 if (' ' < parser.fChar[0]) {
656                     if (islower(parser.fChar[0])) {
657                         result += '\n';
658                         parser.skipLine();
659                     } else {
660                         SkASSERT(isupper(parser.fChar[0]));
661                         parser.skipTo(fChildren[childIndex]->fTerminator);
662                         if (mc == parser.fChar[0] && mc == parser.fChar[1]) {
663                             parser.next();
664                             parser.next();
665                         }
666                         childIndex++;
667                     }
668                 } else {
669                     parser.skipLine();
670                 }
671                 continue;
672             }
673         } else {
674             --parser.fChar;
675         }
676         const char* end = parser.fEnd;
677         const char* mark = parser.strnchr(mc, end);
678         if (mark) {
679             end = mark;
680         }
681         string fragment(parser.fChar, end - parser.fChar);
682         trim_end(fragment);
683         if (TrimExtract::kYes == trimExtract) {
684             trim_start(fragment);
685             if (result.length()) {
686                 result += '\n';
687                 result += '\n';
688             }
689         }
690         if (TrimExtract::kYes == trimExtract || has_nonwhitespace(fragment)) {
691             result += fragment;
692         }
693         parser.skipTo(end);
694     }
695     return result;
696 }
697 
space_pad(string * str)698 static void space_pad(string* str) {
699     size_t len = str->length();
700     if (len == 0) {
701         return;
702     }
703     char last = (*str)[len - 1];
704     if ('~' == last || ' ' >= last) {
705         return;
706     }
707     *str += ' ';
708 }
709 
710 //start here;
711 // see if it possible to abstract this a little bit so it can
712 // additionally be used to find params and return in method prototype that
713 // does not have corresponding doxygen comments
checkMethod() const714 bool Definition::checkMethod() const {
715     SkASSERT(MarkType::kMethod == fMarkType);
716     // if method returns a value, look for a return child
717     // for each parameter, look for a corresponding child
718     const char* end = fContentStart;
719     while (end > fStart && ' ' >= end[-1]) {
720         --end;
721     }
722     TextParser methodParser(fFileName, fStart, end, fLineCount);
723     methodParser.skipWhiteSpace();
724     SkASSERT(methodParser.startsWith("#Method"));
725     methodParser.skipName("#Method");
726     methodParser.skipSpace();
727     string name = this->methodName();
728     if (MethodType::kNone == fMethodType && name.length() > 2 &&
729             "()" == name.substr(name.length() - 2)) {
730         name = name.substr(0, name.length() - 2);
731     }
732     bool expectReturn = this->methodHasReturn(name, &methodParser);
733     bool foundReturn = false;
734     bool foundException = false;
735     for (auto& child : fChildren) {
736         foundException |= MarkType::kDeprecated == child->fMarkType
737                 || MarkType::kExperimental == child->fMarkType;
738         if (MarkType::kReturn != child->fMarkType) {
739             if (MarkType::kParam == child->fMarkType) {
740                 child->fVisited = false;
741             }
742             continue;
743         }
744         if (!expectReturn) {
745             return methodParser.reportError<bool>("no #Return expected");
746         }
747         if (foundReturn) {
748             return methodParser.reportError<bool>("multiple #Return markers");
749         }
750         foundReturn = true;
751     }
752     if (expectReturn && !foundReturn && !foundException) {
753         return methodParser.reportError<bool>("missing #Return marker");
754     }
755     const char* paren = methodParser.strnchr('(', methodParser.fEnd);
756     if (!paren) {
757         return methodParser.reportError<bool>("missing #Method function definition");
758     }
759     const char* nextEnd = paren;
760     do {
761         string paramName;
762         methodParser.fChar = nextEnd + 1;
763         methodParser.skipSpace();
764         if (!this->nextMethodParam(&methodParser, &nextEnd, &paramName)) {
765             continue;
766         }
767         bool foundParam = false;
768         for (auto& child : fChildren) {
769             if (MarkType::kParam != child->fMarkType) {
770                 continue;
771             }
772             if (paramName != child->fName) {
773                 continue;
774             }
775             if (child->fVisited) {
776                 return methodParser.reportError<bool>("multiple #Method param with same name");
777             }
778             child->fVisited = true;
779             if (foundParam) {
780                 TextParser paramError(child);
781                 return methodParser.reportError<bool>("multiple #Param with same name");
782             }
783             foundParam = true;
784 
785         }
786         if (!foundParam && !foundException) {
787             return methodParser.reportError<bool>("no #Param found");
788         }
789         if (')' == nextEnd[0]) {
790             break;
791         }
792     } while (')' != nextEnd[0]);
793     for (auto& child : fChildren) {
794         if (MarkType::kParam != child->fMarkType) {
795             continue;
796         }
797         if (!child->fVisited) {
798             TextParser paramError(child);
799             return paramError.reportError<bool>("#Param without param in #Method");
800         }
801     }
802     return true;
803 }
804 
crossCheck2(const Definition & includeToken) const805 bool Definition::crossCheck2(const Definition& includeToken) const {
806     TextParser parser(fFileName, fStart, fContentStart, fLineCount);
807     parser.skipExact("#");
808     bool isMethod = parser.skipName("Method");
809     const char* contentEnd;
810     if (isMethod) {
811         contentEnd = fContentStart;
812     } else if (parser.skipName("DefinedBy")) {
813         contentEnd = fContentEnd;
814         while (parser.fChar < contentEnd && ' ' >= contentEnd[-1]) {
815             --contentEnd;
816         }
817         if (parser.fChar < contentEnd - 1 && ')' == contentEnd[-1] && '(' == contentEnd[-2]) {
818             contentEnd -= 2;
819         }
820     } else {
821         return parser.reportError<bool>("unexpected crosscheck marktype");
822     }
823     return crossCheckInside(parser.fChar, contentEnd, includeToken);
824 }
825 
crossCheck(const Definition & includeToken) const826 bool Definition::crossCheck(const Definition& includeToken) const {
827     return crossCheckInside(fContentStart, fContentEnd, includeToken);
828 }
829 
crossCheckInside(const char * start,const char * end,const Definition & includeToken) const830 bool Definition::crossCheckInside(const char* start, const char* end,
831         const Definition& includeToken) const {
832     TextParser def(fFileName, start, end, fLineCount);
833     TextParser inc("", includeToken.fContentStart, includeToken.fContentEnd, 0);
834     if (inc.startsWith("SK_API")) {
835         inc.skipWord("SK_API");
836     }
837     if (inc.startsWith("friend")) {
838         inc.skipWord("friend");
839     }
840     if (inc.startsWith("SK_API")) {
841         inc.skipWord("SK_API");
842     }
843     inc.skipExact("SkDEBUGCODE(");
844     do {
845         bool defEof;
846         bool incEof;
847         do {
848             defEof = def.eof() || !def.skipWhiteSpace();
849             incEof = inc.eof() || !inc.skipWhiteSpace();
850             if (!incEof && '/' == inc.peek() && (defEof || '/' != def.peek())) {
851                 inc.next();
852                 if ('*' == inc.peek()) {
853                     inc.skipToEndBracket("*/");
854                     inc.next();
855                 } else if ('/' == inc.peek()) {
856                     inc.skipToEndBracket('\n');
857                 }
858             } else if (!incEof && '#' == inc.peek() && (defEof || '#' != def.peek())) {
859                 inc.next();
860                 if (inc.startsWith("if")) {
861                     inc.skipToEndBracket("\n");
862                 } else if (inc.startsWith("endif")) {
863                     inc.skipToEndBracket("\n");
864                 } else {
865                     SkASSERT(0); // incomplete
866                     return false;
867                 }
868             } else {
869                 break;
870             }
871             inc.next();
872         } while (true);
873         if (defEof || incEof) {
874             if (defEof == incEof || (!defEof && ';' == def.peek())) {
875                 return true;
876             }
877             return false;  // allow setting breakpoint on failure
878         }
879         char defCh;
880         do {
881             defCh = def.next();
882             char incCh = inc.next();
883             if (' ' >= defCh && ' ' >= incCh) {
884                 break;
885             }
886             if (defCh != incCh) {
887                 if ('_' != defCh || ' ' != incCh || !fOperatorConst || !def.startsWith("const")) {
888                     return false;
889                 }
890             }
891             if (';' == defCh) {
892                 return true;
893             }
894         } while (!def.eof() && !inc.eof());
895     } while (true);
896     return false;
897 }
898 
formatFunction() const899 string Definition::formatFunction() const {
900     const char* end = fContentStart;
901     while (end > fStart && ' ' >= end[-1]) {
902         --end;
903     }
904     TextParser methodParser(fFileName, fStart, end, fLineCount);
905     methodParser.skipWhiteSpace();
906     SkASSERT(methodParser.startsWith("#Method"));
907     methodParser.skipName("#Method");
908     methodParser.skipSpace();
909     const char* lastStart = methodParser.fChar;
910     const int limit = 100;  // todo: allow this to be set by caller or in global or something
911     string name = this->methodName();
912     const char* nameInParser = methodParser.strnstr(name.c_str(), methodParser.fEnd);
913     methodParser.skipTo(nameInParser);
914     const char* lastEnd = methodParser.fChar;
915     const char* paren = methodParser.strnchr('(', methodParser.fEnd);
916     size_t indent;
917     if (paren) {
918         indent = (size_t) (paren - lastStart) + 1;
919     } else {
920         indent = (size_t) (lastEnd - lastStart);
921     }
922     // trim indent so longest line doesn't exceed box width
923     TextParser::Save savePlace(&methodParser);
924     const char* saveStart = lastStart;
925     ptrdiff_t maxLine = 0;
926     do {
927         const char* nextStart = lastEnd;
928         const char* delimiter = methodParser.anyOf(",)");
929         const char* nextEnd = delimiter ? delimiter : methodParser.fEnd;
930         if (delimiter) {
931             while (nextStart < nextEnd && ' ' >= nextStart[0]) {
932                 ++nextStart;
933             }
934         }
935         while (nextEnd > nextStart && ' ' >= nextEnd[-1]) {
936             --nextEnd;
937         }
938         if (delimiter) {
939             nextEnd += 1;
940             delimiter += 1;
941         }
942         if (lastEnd > lastStart) {
943             maxLine = SkTMax(maxLine, lastEnd - lastStart);
944         }
945         if (delimiter) {
946             methodParser.skipTo(delimiter);
947         }
948         lastStart = nextStart;
949         lastEnd = nextEnd;
950     } while (lastStart < lastEnd);
951     savePlace.restore();
952     lastStart = saveStart;
953     lastEnd = methodParser.fChar;
954     indent = SkTMin(indent, (size_t) (limit - maxLine));
955     // write string wtih trimmmed indent
956     string methodStr;
957     int written = 0;
958     do {
959         const char* nextStart = lastEnd;
960         SkASSERT(written < limit);
961         const char* delimiter = methodParser.anyOf(",)");
962         const char* nextEnd = delimiter ? delimiter : methodParser.fEnd;
963         if (delimiter) {
964             while (nextStart < nextEnd && ' ' >= nextStart[0]) {
965                 ++nextStart;
966             }
967         }
968         while (nextEnd > nextStart && ' ' >= nextEnd[-1]) {
969             --nextEnd;
970         }
971         if (delimiter) {
972             nextEnd += 1;
973             delimiter += 1;
974         }
975         if (lastEnd > lastStart) {
976             if (lastStart[0] != ' ') {
977                 space_pad(&methodStr);
978             }
979             methodStr += string(lastStart, (size_t) (lastEnd - lastStart));
980             written += (size_t) (lastEnd - lastStart);
981         }
982         if (delimiter) {
983             if (nextEnd - nextStart >= (ptrdiff_t) (limit - written)) {
984                 written = indent;
985                 methodStr += '\n';
986                 methodStr += string(indent, ' ');
987             }
988             methodParser.skipTo(delimiter);
989         }
990         lastStart = nextStart;
991         lastEnd = nextEnd;
992     } while (lastStart < lastEnd);
993     return methodStr;
994 }
995 
fiddleName() const996 string Definition::fiddleName() const {
997     string result;
998     size_t start = 0;
999     string parent;
1000     const Definition* parentDef = this;
1001     while ((parentDef = parentDef->fParent)) {
1002         if (MarkType::kClass == parentDef->fMarkType || MarkType::kStruct == parentDef->fMarkType) {
1003             parent = parentDef->fFiddle;
1004             break;
1005         }
1006     }
1007     if (parent.length() && 0 == fFiddle.compare(0, parent.length(), parent)) {
1008         start = parent.length();
1009         while (start < fFiddle.length() && '_' == fFiddle[start]) {
1010             ++start;
1011         }
1012     }
1013     size_t end = fFiddle.find_first_of('(', start);
1014     return fFiddle.substr(start, end - start);
1015 }
1016 
hasChild(MarkType markType) const1017 const Definition* Definition::hasChild(MarkType markType) const {
1018     for (auto iter : fChildren) {
1019         if (markType == iter->fMarkType) {
1020             return iter;
1021         }
1022     }
1023     return nullptr;
1024 }
1025 
hasParam(const string & ref) const1026 const Definition* Definition::hasParam(const string& ref) const {
1027     SkASSERT(MarkType::kMethod == fMarkType);
1028     for (auto iter : fChildren) {
1029         if (MarkType::kParam != iter->fMarkType) {
1030             continue;
1031         }
1032         if (iter->fName == ref) {
1033             return &*iter;
1034         }
1035 
1036     }
1037     return nullptr;
1038 }
1039 
hasMatch(const string & name) const1040 bool Definition::hasMatch(const string& name) const {
1041     for (auto child : fChildren) {
1042         if (name == child->fName) {
1043             return true;
1044         }
1045         if (child->hasMatch(name)) {
1046             return true;
1047         }
1048     }
1049     return false;
1050 }
1051 
isStructOrClass() const1052 bool Definition::isStructOrClass() const {
1053     if (MarkType::kStruct != fMarkType && MarkType::kClass != fMarkType) {
1054         return false;
1055     }
1056     if (string::npos != fFileName.find("undocumented.bmh")) {
1057         return false;
1058     }
1059     return true;
1060 }
1061 
methodHasReturn(const string & name,TextParser * methodParser) const1062 bool Definition::methodHasReturn(const string& name, TextParser* methodParser) const {
1063     if (methodParser->skipExact("static")) {
1064         methodParser->skipWhiteSpace();
1065     }
1066     const char* lastStart = methodParser->fChar;
1067     const char* nameInParser = methodParser->strnstr(name.c_str(), methodParser->fEnd);
1068     methodParser->skipTo(nameInParser);
1069     const char* lastEnd = methodParser->fChar;
1070     const char* returnEnd = lastEnd;
1071     while (returnEnd > lastStart && ' ' == returnEnd[-1]) {
1072         --returnEnd;
1073     }
1074     bool expectReturn = 4 != returnEnd - lastStart || strncmp("void", lastStart, 4);
1075     if (MethodType::kNone != fMethodType && MethodType::kOperator != fMethodType && !expectReturn) {
1076         return methodParser->reportError<bool>("unexpected void");
1077     }
1078     switch (fMethodType) {
1079         case MethodType::kNone:
1080         case MethodType::kOperator:
1081             // either is fine
1082             break;
1083         case MethodType::kConstructor:
1084             expectReturn = true;
1085             break;
1086         case MethodType::kDestructor:
1087             expectReturn = false;
1088             break;
1089     }
1090     return expectReturn;
1091 }
1092 
methodName() const1093 string Definition::methodName() const {
1094     string result;
1095     size_t start = 0;
1096     string parent;
1097     const Definition* parentDef = this;
1098     while ((parentDef = parentDef->fParent)) {
1099         if (MarkType::kClass == parentDef->fMarkType || MarkType::kStruct == parentDef->fMarkType) {
1100             parent = parentDef->fName;
1101             break;
1102         }
1103     }
1104     if (parent.length() && 0 == fName.compare(0, parent.length(), parent)) {
1105         start = parent.length();
1106         while (start < fName.length() && ':' == fName[start]) {
1107             ++start;
1108         }
1109     }
1110     if (fClone) {
1111         int lastUnder = fName.rfind('_');
1112         return fName.substr(start, (size_t) (lastUnder - start));
1113     }
1114     size_t end = fName.find_first_of('(', start);
1115     if (string::npos == end) {
1116         return fName.substr(start);
1117     }
1118     return fName.substr(start, end - start);
1119 }
1120 
nextMethodParam(TextParser * methodParser,const char ** nextEndPtr,string * paramName) const1121 bool Definition::nextMethodParam(TextParser* methodParser, const char** nextEndPtr,
1122         string* paramName) const {
1123     int parenCount = 0;
1124     TextParser::Save saveState(methodParser);
1125     while (true) {
1126         if (methodParser->eof()) {
1127             return methodParser->reportError<bool>("#Method function missing close paren");
1128         }
1129         char ch = methodParser->peek();
1130         if ('(' == ch) {
1131             ++parenCount;
1132         }
1133         if (parenCount == 0 && (')' == ch || ',' == ch)) {
1134             *nextEndPtr = methodParser->fChar;
1135             break;
1136         }
1137         if (')' == ch) {
1138             if (0 > --parenCount) {
1139                 return this->reportError<bool>("mismatched parentheses");
1140             }
1141         }
1142         methodParser->next();
1143     }
1144     saveState.restore();
1145     const char* nextEnd = *nextEndPtr;
1146     const char* paramEnd = nextEnd;
1147     const char* assign = methodParser->strnstr(" = ", paramEnd);
1148     if (assign) {
1149         paramEnd = assign;
1150     }
1151     const char* closeBracket = methodParser->strnstr("]", paramEnd);
1152     if (closeBracket) {
1153         const char* openBracket = methodParser->strnstr("[", paramEnd);
1154         if (openBracket && openBracket < closeBracket) {
1155             while (openBracket < --closeBracket && isdigit(closeBracket[0]))
1156                 ;
1157             if (openBracket == closeBracket) {
1158                 paramEnd = openBracket;
1159             }
1160         }
1161     }
1162     const char* function = methodParser->strnstr(")(", paramEnd);
1163     if (function) {
1164         paramEnd = function;
1165     }
1166     while (paramEnd > methodParser->fChar && ' ' == paramEnd[-1]) {
1167         --paramEnd;
1168     }
1169     const char* paramStart = paramEnd;
1170     while (paramStart > methodParser->fChar && isalnum(paramStart[-1])) {
1171         --paramStart;
1172     }
1173     if (paramStart > methodParser->fChar && paramStart >= paramEnd) {
1174         return methodParser->reportError<bool>("#Method missing param name");
1175     }
1176     *paramName = string(paramStart, paramEnd - paramStart);
1177     if (!paramName->length()) {
1178         if (')' != nextEnd[0]) {
1179             return methodParser->reportError<bool>("#Method malformed param");
1180         }
1181         return false;
1182     }
1183     return true;
1184 }
1185 
NormalizedName(string name)1186 string Definition::NormalizedName(string name) {
1187     string normalizedName = name;
1188     std::replace(normalizedName.begin(), normalizedName.end(), '-', '_');
1189     do {
1190         size_t doubleColon = normalizedName.find("::", 0);
1191         if (string::npos == doubleColon) {
1192             break;
1193         }
1194         normalizedName = normalizedName.substr(0, doubleColon)
1195             + '_' + normalizedName.substr(doubleColon + 2);
1196     } while (true);
1197     return normalizedName;
1198 }
1199 
paramsMatch(const string & match,const string & name) const1200 bool Definition::paramsMatch(const string& match, const string& name) const {
1201     TextParser def(fFileName, fStart, fContentStart, fLineCount);
1202     const char* dName = def.strnstr(name.c_str(), fContentStart);
1203     if (!dName) {
1204         return false;
1205     }
1206     def.skipTo(dName);
1207     TextParser m(fFileName, &match.front(), &match.back() + 1, fLineCount);
1208     const char* mName = m.strnstr(name.c_str(), m.fEnd);
1209     if (!mName) {
1210         return false;
1211     }
1212     m.skipTo(mName);
1213     while (!def.eof() && ')' != def.peek() && !m.eof() && ')' != m.peek()) {
1214         const char* ds = def.fChar;
1215         const char* ms = m.fChar;
1216         const char* de = def.anyOf(") \n");
1217         const char* me = m.anyOf(") \n");
1218         def.skipTo(de);
1219         m.skipTo(me);
1220         if (def.fChar - ds != m.fChar - ms) {
1221             return false;
1222         }
1223         if (strncmp(ds, ms, (int) (def.fChar - ds))) {
1224             return false;
1225         }
1226         def.skipWhiteSpace();
1227         m.skipWhiteSpace();
1228     }
1229     return !def.eof() && ')' == def.peek() && !m.eof() && ')' == m.peek();
1230 }
1231 
clearVisited()1232 void RootDefinition::clearVisited() {
1233     fVisited = false;
1234     for (auto& leaf : fLeaves) {
1235         leaf.second.fVisited = false;
1236     }
1237     for (auto& branch : fBranches) {
1238         branch.second->clearVisited();
1239     }
1240 }
1241 
dumpUnVisited()1242 bool RootDefinition::dumpUnVisited() {
1243     bool success = true;
1244     for (auto& leaf : fLeaves) {
1245         if (!leaf.second.fVisited) {
1246             // FIXME: bugs requiring long tail fixes, suppressed here:
1247             // SkBitmap::validate() is wrapped in SkDEBUGCODE in .h and not parsed
1248             if ("SkBitmap::validate()" == leaf.first) {
1249                 continue;
1250             }
1251             // SkPath::pathRefIsValid in #ifdef ; prefer to remove chrome dependency to fix
1252             if ("SkPath::pathRefIsValid" == leaf.first) {
1253                 continue;
1254             }
1255             // FIXME: end of long tail bugs
1256             SkDebugf("defined in bmh but missing in include: %s\n", leaf.first.c_str());
1257             success = false;
1258         }
1259     }
1260     for (auto& branch : fBranches) {
1261         success &= branch.second->dumpUnVisited();
1262     }
1263     return success;
1264 }
1265 
find(const string & ref,AllowParens allowParens) const1266 const Definition* RootDefinition::find(const string& ref, AllowParens allowParens) const {
1267     const auto leafIter = fLeaves.find(ref);
1268     if (leafIter != fLeaves.end()) {
1269         return &leafIter->second;
1270     }
1271     if (AllowParens::kYes == allowParens && string::npos == ref.find("()")) {
1272         string withParens = ref + "()";
1273         const auto parensIter = fLeaves.find(withParens);
1274         if (parensIter != fLeaves.end()) {
1275             return &parensIter->second;
1276         }
1277     }
1278     const auto branchIter = fBranches.find(ref);
1279     if (branchIter != fBranches.end()) {
1280         const RootDefinition* rootDef = branchIter->second;
1281         return rootDef;
1282     }
1283     const Definition* result = nullptr;
1284     for (const auto& branch : fBranches) {
1285         const RootDefinition* rootDef = branch.second;
1286         result = rootDef->find(ref, allowParens);
1287         if (result) {
1288             break;
1289         }
1290     }
1291     return result;
1292 }
1293