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::kBug:
564 case MarkType::kMarkChar:
565 case MarkType::kPlatform:
566 // ignore for now
567 break;
568 case MarkType::kSet:
569 if ("sRGB" == string(iter->fContentStart,
570 iter->fContentEnd - iter->fContentStart)) {
571 srgbStr = "true";
572 } else {
573 SkASSERT(0); // more work to do
574 return false;
575 }
576 break;
577 case MarkType::kStdOut:
578 textOut = true;
579 break;
580 default:
581 SkASSERT(0); // more coding to do
582 }
583 }
584 string animatedStr = "0" != durationStr ? "true" : "false";
585 string textOutStr = textOut ? "true" : "false";
586 size_t pos = 0;
587 while (pos < text.length() && ' ' > text[pos]) {
588 ++pos;
589 }
590 size_t end = text.length();
591 size_t outIndent = 0;
592 size_t textIndent = count_indent(text, pos, end);
593 if (fWrapper.length() > 0) {
594 code += fWrapper;
595 code += "\\n";
596 outIndent = 4;
597 }
598 add_code(text, pos, end, outIndent, textIndent, code);
599 if (fWrapper.length() > 0) {
600 code += "}";
601 }
602 string example = "\"" + normalizedName + "\": {\n";
603 size_t nameStart = fFileName.find(SkOSPath::SEPARATOR, 0);
604 SkASSERT(string::npos != nameStart);
605 string baseFile = fFileName.substr(nameStart + 1, fFileName.length() - nameStart - 5);
606 if (ExampleOptions::kText == exampleOptions) {
607 example += " \"code\": \"" + code + "\",\n";
608 example += " \"hash\": \"" + fHash + "\",\n";
609 example += " \"file\": \"" + baseFile + "\",\n";
610 example += " \"name\": \"" + fName + "\",";
611 } else {
612 example += " \"code\": \"" + code + "\",\n";
613 if (ExampleOptions::kPng == exampleOptions) {
614 example += " \"width\": " + widthStr + ",\n";
615 example += " \"height\": " + heightStr + ",\n";
616 example += " \"hash\": \"" + fHash + "\",\n";
617 example += " \"file\": \"" + baseFile + "\",\n";
618 example += " \"name\": \"" + fName + "\"\n";
619 example += "}";
620 } else {
621 example += " \"options\": {\n";
622 example += " \"width\": " + widthStr + ",\n";
623 example += " \"height\": " + heightStr + ",\n";
624 example += " \"source\": " + imageStr + ",\n";
625 example += " \"srgb\": " + srgbStr + ",\n";
626 example += " \"f16\": false,\n";
627 example += " \"textOnly\": " + textOutStr + ",\n";
628 example += " \"animated\": " + animatedStr + ",\n";
629 example += " \"duration\": " + durationStr + "\n";
630 example += " },\n";
631 example += " \"fast\": true";
632 }
633 }
634 *result = example;
635 return true;
636 }
637
extractText(TrimExtract trimExtract) const638 string Definition::extractText(TrimExtract trimExtract) const {
639 string result;
640 TextParser parser(fFileName, fContentStart, fContentEnd, fLineCount);
641 int childIndex = 0;
642 char mc = '#';
643 while (parser.fChar < parser.fEnd) {
644 if (TrimExtract::kYes == trimExtract && !parser.skipWhiteSpace()) {
645 break;
646 }
647 if (parser.next() == mc) {
648 if (parser.next() == mc) {
649 if (parser.next() == mc) {
650 mc = parser.next();
651 }
652 } else {
653 // fixme : more work to do if # style comment is in text
654 // if in method definition, could be alternate method name
655 --parser.fChar;
656 if (' ' < parser.fChar[0]) {
657 if (islower(parser.fChar[0])) {
658 result += '\n';
659 parser.skipLine();
660 } else {
661 SkASSERT(isupper(parser.fChar[0]));
662 parser.skipTo(fChildren[childIndex]->fTerminator);
663 if (mc == parser.fChar[0] && mc == parser.fChar[1]) {
664 parser.next();
665 parser.next();
666 }
667 childIndex++;
668 }
669 } else {
670 parser.skipLine();
671 }
672 continue;
673 }
674 } else {
675 --parser.fChar;
676 }
677 const char* end = parser.fEnd;
678 const char* mark = parser.strnchr(mc, end);
679 if (mark) {
680 end = mark;
681 }
682 string fragment(parser.fChar, end - parser.fChar);
683 trim_end(fragment);
684 if (TrimExtract::kYes == trimExtract) {
685 trim_start(fragment);
686 if (result.length()) {
687 result += '\n';
688 result += '\n';
689 }
690 }
691 if (TrimExtract::kYes == trimExtract || has_nonwhitespace(fragment)) {
692 result += fragment;
693 }
694 parser.skipTo(end);
695 }
696 return result;
697 }
698
space_pad(string * str)699 static void space_pad(string* str) {
700 size_t len = str->length();
701 if (len == 0) {
702 return;
703 }
704 char last = (*str)[len - 1];
705 if ('~' == last || ' ' >= last) {
706 return;
707 }
708 *str += ' ';
709 }
710
711 //start here;
712 // see if it possible to abstract this a little bit so it can
713 // additionally be used to find params and return in method prototype that
714 // does not have corresponding doxygen comments
checkMethod() const715 bool Definition::checkMethod() const {
716 SkASSERT(MarkType::kMethod == fMarkType);
717 // if method returns a value, look for a return child
718 // for each parameter, look for a corresponding child
719 const char* end = fContentStart;
720 while (end > fStart && ' ' >= end[-1]) {
721 --end;
722 }
723 TextParser methodParser(fFileName, fStart, end, fLineCount);
724 methodParser.skipWhiteSpace();
725 SkASSERT(methodParser.startsWith("#Method"));
726 methodParser.skipName("#Method");
727 methodParser.skipSpace();
728 string name = this->methodName();
729 if (MethodType::kNone == fMethodType && name.length() > 2 &&
730 "()" == name.substr(name.length() - 2)) {
731 name = name.substr(0, name.length() - 2);
732 }
733 bool expectReturn = this->methodHasReturn(name, &methodParser);
734 bool foundReturn = false;
735 bool foundException = false;
736 for (auto& child : fChildren) {
737 foundException |= MarkType::kDeprecated == child->fMarkType
738 || MarkType::kExperimental == child->fMarkType;
739 if (MarkType::kReturn != child->fMarkType) {
740 if (MarkType::kParam == child->fMarkType) {
741 child->fVisited = false;
742 }
743 continue;
744 }
745 if (!expectReturn) {
746 return methodParser.reportError<bool>("no #Return expected");
747 }
748 if (foundReturn) {
749 return methodParser.reportError<bool>("multiple #Return markers");
750 }
751 foundReturn = true;
752 }
753 if (expectReturn && !foundReturn && !foundException) {
754 return methodParser.reportError<bool>("missing #Return marker");
755 }
756 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
757 if (!paren) {
758 return methodParser.reportError<bool>("missing #Method function definition");
759 }
760 const char* nextEnd = paren;
761 do {
762 string paramName;
763 methodParser.fChar = nextEnd + 1;
764 methodParser.skipSpace();
765 if (!this->nextMethodParam(&methodParser, &nextEnd, ¶mName)) {
766 continue;
767 }
768 bool foundParam = false;
769 for (auto& child : fChildren) {
770 if (MarkType::kParam != child->fMarkType) {
771 continue;
772 }
773 if (paramName != child->fName) {
774 continue;
775 }
776 if (child->fVisited) {
777 return methodParser.reportError<bool>("multiple #Method param with same name");
778 }
779 child->fVisited = true;
780 if (foundParam) {
781 TextParser paramError(child);
782 return methodParser.reportError<bool>("multiple #Param with same name");
783 }
784 foundParam = true;
785
786 }
787 if (!foundParam && !foundException) {
788 return methodParser.reportError<bool>("no #Param found");
789 }
790 if (')' == nextEnd[0]) {
791 break;
792 }
793 } while (')' != nextEnd[0]);
794 for (auto& child : fChildren) {
795 if (MarkType::kParam != child->fMarkType) {
796 continue;
797 }
798 if (!child->fVisited) {
799 TextParser paramError(child);
800 return paramError.reportError<bool>("#Param without param in #Method");
801 }
802 }
803 return true;
804 }
805
crossCheck2(const Definition & includeToken) const806 bool Definition::crossCheck2(const Definition& includeToken) const {
807 TextParser parser(fFileName, fStart, fContentStart, fLineCount);
808 parser.skipExact("#");
809 bool isMethod = parser.skipName("Method");
810 const char* contentEnd;
811 if (isMethod) {
812 contentEnd = fContentStart;
813 } else if (parser.skipName("DefinedBy")) {
814 contentEnd = fContentEnd;
815 while (parser.fChar < contentEnd && ' ' >= contentEnd[-1]) {
816 --contentEnd;
817 }
818 if (parser.fChar < contentEnd - 1 && ')' == contentEnd[-1] && '(' == contentEnd[-2]) {
819 contentEnd -= 2;
820 }
821 } else {
822 return parser.reportError<bool>("unexpected crosscheck marktype");
823 }
824 return crossCheckInside(parser.fChar, contentEnd, includeToken);
825 }
826
crossCheck(const Definition & includeToken) const827 bool Definition::crossCheck(const Definition& includeToken) const {
828 return crossCheckInside(fContentStart, fContentEnd, includeToken);
829 }
830
crossCheckInside(const char * start,const char * end,const Definition & includeToken) const831 bool Definition::crossCheckInside(const char* start, const char* end,
832 const Definition& includeToken) const {
833 TextParser def(fFileName, start, end, fLineCount);
834 TextParser inc("", includeToken.fContentStart, includeToken.fContentEnd, 0);
835 if (inc.startsWith("SK_API")) {
836 inc.skipWord("SK_API");
837 }
838 if (inc.startsWith("friend")) {
839 inc.skipWord("friend");
840 }
841 if (inc.startsWith("SK_API")) {
842 inc.skipWord("SK_API");
843 }
844 inc.skipExact("SkDEBUGCODE(");
845 do {
846 bool defEof;
847 bool incEof;
848 do {
849 defEof = def.eof() || !def.skipWhiteSpace();
850 incEof = inc.eof() || !inc.skipWhiteSpace();
851 if (!incEof && '/' == inc.peek() && (defEof || '/' != def.peek())) {
852 inc.next();
853 if ('*' == inc.peek()) {
854 inc.skipToEndBracket("*/");
855 inc.next();
856 } else if ('/' == inc.peek()) {
857 inc.skipToEndBracket('\n');
858 }
859 } else if (!incEof && '#' == inc.peek() && (defEof || '#' != def.peek())) {
860 inc.next();
861 if (inc.startsWith("if")) {
862 inc.skipToEndBracket("\n");
863 } else if (inc.startsWith("endif")) {
864 inc.skipToEndBracket("\n");
865 } else {
866 SkASSERT(0); // incomplete
867 return false;
868 }
869 } else {
870 break;
871 }
872 inc.next();
873 } while (true);
874 if (defEof || incEof) {
875 if (defEof == incEof || (!defEof && ';' == def.peek())) {
876 return true;
877 }
878 return false; // allow setting breakpoint on failure
879 }
880 char defCh;
881 do {
882 defCh = def.next();
883 char incCh = inc.next();
884 if (' ' >= defCh && ' ' >= incCh) {
885 break;
886 }
887 if (defCh != incCh) {
888 if ('_' != defCh || ' ' != incCh || !fOperatorConst || !def.startsWith("const")) {
889 return false;
890 }
891 }
892 if (';' == defCh) {
893 return true;
894 }
895 } while (!def.eof() && !inc.eof());
896 } while (true);
897 return false;
898 }
899
formatFunction(Format format) const900 string Definition::formatFunction(Format format) const {
901 const char* end = fContentStart;
902 while (end > fStart && ' ' >= end[-1]) {
903 --end;
904 }
905 TextParser methodParser(fFileName, fStart, end, fLineCount);
906 methodParser.skipWhiteSpace();
907 SkASSERT(methodParser.startsWith("#Method"));
908 methodParser.skipName("#Method");
909 methodParser.skipSpace();
910 const char* lastStart = methodParser.fChar;
911 const int limit = 100; // todo: allow this to be set by caller or in global or something
912 string name = this->methodName();
913 const char* nameInParser = methodParser.strnstr(name.c_str(), methodParser.fEnd);
914 methodParser.skipTo(nameInParser);
915 const char* lastEnd = methodParser.fChar;
916 if (Format::kOmitReturn == format) {
917 lastStart = lastEnd;
918 }
919 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
920 size_t indent;
921 if (paren) {
922 indent = (size_t) (paren - lastStart) + 1;
923 } else {
924 indent = (size_t) (lastEnd - lastStart);
925 }
926 // trim indent so longest line doesn't exceed box width
927 TextParser::Save savePlace(&methodParser);
928 const char* saveStart = lastStart;
929 ptrdiff_t maxLine = 0;
930 do {
931 const char* nextStart = lastEnd;
932 const char* delimiter = methodParser.anyOf(",)");
933 const char* nextEnd = delimiter ? delimiter : methodParser.fEnd;
934 if (delimiter) {
935 while (nextStart < nextEnd && ' ' >= nextStart[0]) {
936 ++nextStart;
937 }
938 }
939 while (nextEnd > nextStart && ' ' >= nextEnd[-1]) {
940 --nextEnd;
941 }
942 if (delimiter) {
943 nextEnd += 1;
944 delimiter += 1;
945 }
946 if (lastEnd > lastStart) {
947 maxLine = SkTMax(maxLine, lastEnd - lastStart);
948 }
949 if (delimiter) {
950 methodParser.skipTo(delimiter);
951 }
952 lastStart = nextStart;
953 lastEnd = nextEnd;
954 } while (lastStart < lastEnd);
955 savePlace.restore();
956 lastStart = saveStart;
957 lastEnd = methodParser.fChar;
958 indent = SkTMin(indent, (size_t) (limit - maxLine));
959 // write string wtih trimmmed indent
960 string methodStr;
961 int written = 0;
962 do {
963 const char* nextStart = lastEnd;
964 SkASSERT(written < limit);
965 const char* delimiter = methodParser.anyOf(",)");
966 const char* nextEnd = delimiter ? delimiter : methodParser.fEnd;
967 if (delimiter) {
968 while (nextStart < nextEnd && ' ' >= nextStart[0]) {
969 ++nextStart;
970 }
971 }
972 while (nextEnd > nextStart && ' ' >= nextEnd[-1]) {
973 --nextEnd;
974 }
975 if (delimiter) {
976 nextEnd += 1;
977 delimiter += 1;
978 }
979 if (lastEnd > lastStart) {
980 if (lastStart[0] != ' ') {
981 space_pad(&methodStr);
982 }
983 methodStr += string(lastStart, (size_t) (lastEnd - lastStart));
984 written += (size_t) (lastEnd - lastStart);
985 }
986 if (delimiter) {
987 if (nextEnd - nextStart >= (ptrdiff_t) (limit - written)) {
988 written = indent;
989 if (Format::kIncludeReturn == format) {
990 methodStr += '\n';
991 methodStr += string(indent, ' ');
992 }
993 }
994 methodParser.skipTo(delimiter);
995 }
996 lastStart = nextStart;
997 lastEnd = nextEnd;
998 } while (lastStart < lastEnd);
999 return methodStr;
1000 }
1001
fiddleName() const1002 string Definition::fiddleName() const {
1003 string result;
1004 size_t start = 0;
1005 string parent;
1006 const Definition* parentDef = this;
1007 while ((parentDef = parentDef->fParent)) {
1008 if (MarkType::kClass == parentDef->fMarkType || MarkType::kStruct == parentDef->fMarkType) {
1009 parent = parentDef->fFiddle;
1010 break;
1011 }
1012 }
1013 if (parent.length() && 0 == fFiddle.compare(0, parent.length(), parent)) {
1014 start = parent.length();
1015 while (start < fFiddle.length() && '_' == fFiddle[start]) {
1016 ++start;
1017 }
1018 }
1019 size_t end = fFiddle.find_first_of('(', start);
1020 return fFiddle.substr(start, end - start);
1021 }
1022
findClone(string match) const1023 const Definition* Definition::findClone(string match) const {
1024 for (auto child : fChildren) {
1025 if (!child->fClone) {
1026 continue;
1027 }
1028 if (match == child->fName) {
1029 return child;
1030 }
1031 auto inner = child->findClone(match);
1032 if (inner) {
1033 return inner;
1034 }
1035 }
1036 return nullptr;
1037 }
1038
hasChild(MarkType markType) const1039 const Definition* Definition::hasChild(MarkType markType) const {
1040 for (auto iter : fChildren) {
1041 if (markType == iter->fMarkType) {
1042 return iter;
1043 }
1044 }
1045 return nullptr;
1046 }
1047
hasParam(const string & ref) const1048 const Definition* Definition::hasParam(const string& ref) const {
1049 SkASSERT(MarkType::kMethod == fMarkType);
1050 for (auto iter : fChildren) {
1051 if (MarkType::kParam != iter->fMarkType) {
1052 continue;
1053 }
1054 if (iter->fName == ref) {
1055 return &*iter;
1056 }
1057
1058 }
1059 return nullptr;
1060 }
1061
hasMatch(const string & name) const1062 bool Definition::hasMatch(const string& name) const {
1063 for (auto child : fChildren) {
1064 if (name == child->fName) {
1065 return true;
1066 }
1067 if (child->hasMatch(name)) {
1068 return true;
1069 }
1070 }
1071 return false;
1072 }
1073
isStructOrClass() const1074 bool Definition::isStructOrClass() const {
1075 if (MarkType::kStruct != fMarkType && MarkType::kClass != fMarkType) {
1076 return false;
1077 }
1078 if (string::npos != fFileName.find("undocumented.bmh")) {
1079 return false;
1080 }
1081 return true;
1082 }
1083
methodHasReturn(const string & name,TextParser * methodParser) const1084 bool Definition::methodHasReturn(const string& name, TextParser* methodParser) const {
1085 if (methodParser->skipExact("static")) {
1086 methodParser->skipWhiteSpace();
1087 }
1088 const char* lastStart = methodParser->fChar;
1089 const char* nameInParser = methodParser->strnstr(name.c_str(), methodParser->fEnd);
1090 methodParser->skipTo(nameInParser);
1091 const char* lastEnd = methodParser->fChar;
1092 const char* returnEnd = lastEnd;
1093 while (returnEnd > lastStart && ' ' == returnEnd[-1]) {
1094 --returnEnd;
1095 }
1096 bool expectReturn = 4 != returnEnd - lastStart || strncmp("void", lastStart, 4);
1097 if (MethodType::kNone != fMethodType && MethodType::kOperator != fMethodType && !expectReturn) {
1098 return methodParser->reportError<bool>("unexpected void");
1099 }
1100 switch (fMethodType) {
1101 case MethodType::kNone:
1102 case MethodType::kOperator:
1103 // either is fine
1104 break;
1105 case MethodType::kConstructor:
1106 expectReturn = true;
1107 break;
1108 case MethodType::kDestructor:
1109 expectReturn = false;
1110 break;
1111 }
1112 return expectReturn;
1113 }
1114
methodName() const1115 string Definition::methodName() const {
1116 string result;
1117 size_t start = 0;
1118 string parent;
1119 const Definition* parentDef = this;
1120 while ((parentDef = parentDef->fParent)) {
1121 if (MarkType::kClass == parentDef->fMarkType || MarkType::kStruct == parentDef->fMarkType) {
1122 parent = parentDef->fName;
1123 break;
1124 }
1125 }
1126 if (parent.length() && 0 == fName.compare(0, parent.length(), parent)) {
1127 start = parent.length();
1128 while (start < fName.length() && ':' == fName[start]) {
1129 ++start;
1130 }
1131 }
1132 if (fClone) {
1133 int lastUnder = fName.rfind('_');
1134 return fName.substr(start, (size_t) (lastUnder - start));
1135 }
1136 size_t end = fName.find_first_of('(', start);
1137 if (string::npos == end) {
1138 return fName.substr(start);
1139 }
1140 return fName.substr(start, end - start);
1141 }
1142
nextMethodParam(TextParser * methodParser,const char ** nextEndPtr,string * paramName) const1143 bool Definition::nextMethodParam(TextParser* methodParser, const char** nextEndPtr,
1144 string* paramName) const {
1145 int parenCount = 0;
1146 TextParser::Save saveState(methodParser);
1147 while (true) {
1148 if (methodParser->eof()) {
1149 return methodParser->reportError<bool>("#Method function missing close paren");
1150 }
1151 char ch = methodParser->peek();
1152 if ('(' == ch) {
1153 ++parenCount;
1154 }
1155 if (parenCount == 0 && (')' == ch || ',' == ch)) {
1156 *nextEndPtr = methodParser->fChar;
1157 break;
1158 }
1159 if (')' == ch) {
1160 if (0 > --parenCount) {
1161 return this->reportError<bool>("mismatched parentheses");
1162 }
1163 }
1164 methodParser->next();
1165 }
1166 saveState.restore();
1167 const char* nextEnd = *nextEndPtr;
1168 const char* paramEnd = nextEnd;
1169 const char* assign = methodParser->strnstr(" = ", paramEnd);
1170 if (assign) {
1171 paramEnd = assign;
1172 }
1173 const char* closeBracket = methodParser->strnstr("]", paramEnd);
1174 if (closeBracket) {
1175 const char* openBracket = methodParser->strnstr("[", paramEnd);
1176 if (openBracket && openBracket < closeBracket) {
1177 while (openBracket < --closeBracket && isdigit(closeBracket[0]))
1178 ;
1179 if (openBracket == closeBracket) {
1180 paramEnd = openBracket;
1181 }
1182 }
1183 }
1184 const char* function = methodParser->strnstr(")(", paramEnd);
1185 if (function) {
1186 paramEnd = function;
1187 }
1188 while (paramEnd > methodParser->fChar && ' ' == paramEnd[-1]) {
1189 --paramEnd;
1190 }
1191 const char* paramStart = paramEnd;
1192 while (paramStart > methodParser->fChar && isalnum(paramStart[-1])) {
1193 --paramStart;
1194 }
1195 if (paramStart > methodParser->fChar && paramStart >= paramEnd) {
1196 return methodParser->reportError<bool>("#Method missing param name");
1197 }
1198 *paramName = string(paramStart, paramEnd - paramStart);
1199 if (!paramName->length()) {
1200 if (')' != nextEnd[0]) {
1201 return methodParser->reportError<bool>("#Method malformed param");
1202 }
1203 return false;
1204 }
1205 return true;
1206 }
1207
NormalizedName(string name)1208 string Definition::NormalizedName(string name) {
1209 string normalizedName = name;
1210 std::replace(normalizedName.begin(), normalizedName.end(), '-', '_');
1211 do {
1212 size_t doubleColon = normalizedName.find("::", 0);
1213 if (string::npos == doubleColon) {
1214 break;
1215 }
1216 normalizedName = normalizedName.substr(0, doubleColon)
1217 + '_' + normalizedName.substr(doubleColon + 2);
1218 } while (true);
1219 return normalizedName;
1220 }
1221
unpreformat(const string & orig)1222 static string unpreformat(const string& orig) {
1223 string result;
1224 int amp = 0;
1225 for (auto c : orig) {
1226 switch (amp) {
1227 case 0:
1228 if ('&' == c) {
1229 amp = 1;
1230 } else {
1231 amp = 0;
1232 result += c;
1233 }
1234 break;
1235 case 1:
1236 if ('l' == c) {
1237 amp = 2;
1238 } else if ('g' == c) {
1239 amp = 3;
1240 } else {
1241 amp = 0;
1242 result += "&";
1243 result += c;
1244 }
1245 break;
1246 case 2:
1247 if ('t' == c) {
1248 amp = 4;
1249 } else {
1250 amp = 0;
1251 result += "&l";
1252 result += c;
1253 }
1254 break;
1255 case 3:
1256 if ('t' == c) {
1257 amp = 5;
1258 } else {
1259 amp = 0;
1260 result += "&g";
1261 result += c;
1262 }
1263 break;
1264 case 4:
1265 if (';' == c) {
1266 result += '<';
1267 } else {
1268 result += "<";
1269 result += c;
1270 }
1271 amp = 0;
1272 break;
1273 case 5:
1274 if (';' == c) {
1275 result += '>';
1276 } else {
1277 result += ">";
1278 result += c;
1279 }
1280 amp = 0;
1281 break;
1282 }
1283 }
1284 return result;
1285 }
1286
paramsMatch(const string & matchFormatted,const string & name) const1287 bool Definition::paramsMatch(const string& matchFormatted, const string& name) const {
1288 string match = unpreformat(matchFormatted);
1289 TextParser def(fFileName, fStart, fContentStart, fLineCount);
1290 const char* dName = def.strnstr(name.c_str(), fContentStart);
1291 if (!dName) {
1292 return false;
1293 }
1294 def.skipTo(dName);
1295 TextParser m(fFileName, &match.front(), &match.back() + 1, fLineCount);
1296 const char* mName = m.strnstr(name.c_str(), m.fEnd);
1297 if (!mName) {
1298 return false;
1299 }
1300 m.skipTo(mName);
1301 while (!def.eof() && ')' != def.peek() && !m.eof() && ')' != m.peek()) {
1302 const char* ds = def.fChar;
1303 const char* ms = m.fChar;
1304 const char* de = def.anyOf(") \n");
1305 const char* me = m.anyOf(") \n");
1306 def.skipTo(de);
1307 m.skipTo(me);
1308 if (def.fChar - ds != m.fChar - ms) {
1309 return false;
1310 }
1311 if (strncmp(ds, ms, (int) (def.fChar - ds))) {
1312 return false;
1313 }
1314 def.skipWhiteSpace();
1315 m.skipWhiteSpace();
1316 }
1317 return !def.eof() && ')' == def.peek() && !m.eof() && ')' == m.peek();
1318 }
1319
clearVisited()1320 void RootDefinition::clearVisited() {
1321 fVisited = false;
1322 for (auto& leaf : fLeaves) {
1323 leaf.second.fVisited = false;
1324 }
1325 for (auto& branch : fBranches) {
1326 branch.second->clearVisited();
1327 }
1328 }
1329
dumpUnVisited()1330 bool RootDefinition::dumpUnVisited() {
1331 bool success = true;
1332 for (auto& leaf : fLeaves) {
1333 if (!leaf.second.fVisited) {
1334 // FIXME: bugs requiring long tail fixes, suppressed here:
1335 // SkBitmap::validate() is wrapped in SkDEBUGCODE in .h and not parsed
1336 if ("SkBitmap::validate()" == leaf.first) {
1337 continue;
1338 }
1339 // SkPath::pathRefIsValid in #ifdef ; prefer to remove chrome dependency to fix
1340 if ("SkPath::pathRefIsValid" == leaf.first) {
1341 continue;
1342 }
1343 // FIXME: end of long tail bugs
1344 SkDebugf("defined in bmh but missing in include: %s\n", leaf.first.c_str());
1345 success = false;
1346 }
1347 }
1348 for (auto& branch : fBranches) {
1349 success &= branch.second->dumpUnVisited();
1350 }
1351 return success;
1352 }
1353
find(const string & ref,AllowParens allowParens) const1354 const Definition* RootDefinition::find(const string& ref, AllowParens allowParens) const {
1355 const auto leafIter = fLeaves.find(ref);
1356 if (leafIter != fLeaves.end()) {
1357 return &leafIter->second;
1358 }
1359 if (AllowParens::kYes == allowParens && string::npos == ref.find("()")) {
1360 string withParens = ref + "()";
1361 const auto parensIter = fLeaves.find(withParens);
1362 if (parensIter != fLeaves.end()) {
1363 return &parensIter->second;
1364 }
1365 }
1366 const auto branchIter = fBranches.find(ref);
1367 if (branchIter != fBranches.end()) {
1368 const RootDefinition* rootDef = branchIter->second;
1369 return rootDef;
1370 }
1371 const Definition* result = nullptr;
1372 for (const auto& branch : fBranches) {
1373 const RootDefinition* rootDef = branch.second;
1374 result = rootDef->find(ref, allowParens);
1375 if (result) {
1376 break;
1377 }
1378 }
1379 return result;
1380 }
1381