blob: 4f841e4a36dd5bc87f913c3619016a292d29141c [file] [log] [blame]
Cary Clarka560c472017-11-27 10:44:06 -05001/*
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"
Cary Clark2d4bf5f2018-04-16 08:37:38 -04009#include "SkOSPath.h"
Cary Clarka560c472017-11-27 10:44:06 -050010
11#ifdef CONST
12#undef CONST
13#endif
14
15#ifdef FRIEND
16#undef FRIEND
17#endif
18
19#ifdef BLANK
20#undef BLANK
21#endif
22
23#ifdef ANY
24#undef ANY
25#endif
26
27#ifdef DEFOP
28#undef DEFOP
29#endif
30
31#define CONST 1
32#define STATIC 2
33#define BLANK 0
34#define ANY -1
35#define DEFOP Definition::Operator
36
37enum class OpType : int8_t {
38 kNone,
39 kVoid,
40 kBool,
41 kChar,
Cary Clarka560c472017-11-27 10:44:06 -050042 kInt,
43 kScalar,
44 kSizeT,
45 kThis,
46 kAny,
47};
48
49enum class OpMod : int8_t {
50 kNone,
51 kArray,
52 kMove,
53 kPointer,
54 kReference,
55 kAny,
56};
57
58const struct OperatorParser {
59 DEFOP fOperator;
60 const char* fSymbol;
61 const char* fName;
62 int8_t fFriend;
63 OpType fReturnType;
64 OpMod fReturnMod;
65 int8_t fConstMethod;
66 struct Param {
67 int8_t fConst;
68 OpType fType;
69 OpMod fMod;
70 } fParams[2];
71} opData[] = {
72 { DEFOP::kUnknown, "??", "???", BLANK, OpType::kNone, OpMod::kNone, BLANK,
73 { } },
74 { DEFOP::kAdd, "+", "add", BLANK, OpType::kThis, OpMod::kNone, BLANK,
75 {{ CONST, OpType::kThis, OpMod::kReference, },
76 { CONST, OpType::kThis, OpMod::kReference, }}},
77 { DEFOP::kAddTo, "+=", "addto", BLANK, OpType::kVoid, OpMod::kNone, BLANK,
78 {{ CONST, OpType::kThis, OpMod::kReference, }}},
79 { DEFOP::kAddTo, "+=", "addto1", BLANK, OpType::kThis, OpMod::kReference, BLANK,
80 {{ CONST, OpType::kThis, OpMod::kReference, }}},
81 { DEFOP::kAddTo, "+=", "addto2", BLANK, OpType::kThis, OpMod::kReference, BLANK,
82 {{ CONST, OpType::kChar, OpMod::kArray, }}},
83 { DEFOP::kAddTo, "+=", "addto3", BLANK, OpType::kThis, OpMod::kReference, BLANK,
84 {{ CONST, OpType::kChar, OpMod::kNone, }}},
85 { DEFOP::kArray, "[]", "array", BLANK, OpType::kScalar, OpMod::kNone, CONST,
86 {{ BLANK, OpType::kInt, OpMod::kNone, }}},
87 { DEFOP::kArray, "[]", "array1", BLANK, OpType::kScalar, OpMod::kReference, BLANK,
88 {{ BLANK, OpType::kInt, OpMod::kNone, }}},
89 { DEFOP::kArray, "[]", "array2", BLANK, OpType::kChar, OpMod::kNone, CONST,
90 {{ BLANK, OpType::kSizeT, OpMod::kNone, }}},
91 { DEFOP::kArray, "[]", "array3", BLANK, OpType::kChar, OpMod::kReference, BLANK,
92 {{ BLANK, OpType::kSizeT, OpMod::kNone, }}},
93 { DEFOP::kCast, "()", "cast", BLANK, OpType::kAny, OpMod::kAny, ANY,
94 {{ ANY, OpType::kAny, OpMod::kAny, }}},
95 { DEFOP::kCopy, "=", "copy", BLANK, OpType::kThis, OpMod::kReference, BLANK,
96 {{ CONST, OpType::kThis, OpMod::kReference, }}},
97 { DEFOP::kCopy, "=", "copy1", BLANK, OpType::kThis, OpMod::kReference, BLANK,
98 {{ CONST, OpType::kChar, OpMod::kArray, }}},
99 { DEFOP::kDelete, "delete", "delete", BLANK, OpType::kVoid, OpMod::kNone, BLANK,
100 {{ BLANK, OpType::kVoid, OpMod::kPointer, }}},
101 { DEFOP::kDereference, "->", "deref", ANY, OpType::kThis, OpMod::kPointer, CONST,
102 { } },
103 { DEFOP::kDereference, "*", "deref", BLANK, OpType::kThis, OpMod::kReference, CONST,
104 { } },
105 { DEFOP::kEqual, "==", "equal", BLANK, OpType::kBool, OpMod::kNone, BLANK,
106 {{ CONST, OpType::kThis, OpMod::kReference, },
107 { CONST, OpType::kThis, OpMod::kReference, }}},
108 { DEFOP::kEqual, "==", "equal1", BLANK, OpType::kBool, OpMod::kNone, CONST,
109 {{ CONST, OpType::kThis, OpMod::kReference, }}},
110 { DEFOP::kEqual, "==", "equal2", ANY, OpType::kBool, OpMod::kNone, BLANK,
111 {{ CONST, OpType::kThis, OpMod::kReference, },
112 { CONST, OpType::kThis, OpMod::kReference, }}},
113 { DEFOP::kMinus, "-", "minus", BLANK, OpType::kThis, OpMod::kNone, CONST,
114 { } },
115 { DEFOP::kMove, "=", "move", BLANK, OpType::kThis, OpMod::kReference, BLANK,
116 {{ BLANK, OpType::kThis, OpMod::kMove, }}},
117 { DEFOP::kMultiply, "*", "multiply", BLANK, OpType::kThis, OpMod::kNone, CONST,
118 {{ BLANK, OpType::kScalar, OpMod::kNone, }}},
Cary Clark61313f32018-10-08 14:57:48 -0400119 { DEFOP::kMultiply, "*", "multiply1", BLANK, OpType::kThis, OpMod::kNone, CONST,
120 {{ CONST, OpType::kThis, OpMod::kReference, }}},
121 { DEFOP::kMultiply, "*", "multiply2", BLANK, OpType::kThis, OpMod::kNone, BLANK,
Cary Clarka560c472017-11-27 10:44:06 -0500122 {{ CONST, OpType::kThis, OpMod::kReference, },
123 { CONST, OpType::kThis, OpMod::kReference, }}},
124 { DEFOP::kMultiplyBy, "*=", "multiplyby", BLANK, OpType::kThis, OpMod::kReference, BLANK,
125 {{ BLANK, OpType::kScalar, OpMod::kNone, }}},
126 { DEFOP::kNew, "new", "new", BLANK, OpType::kVoid, OpMod::kPointer, BLANK,
127 {{ BLANK, OpType::kSizeT, OpMod::kNone, }}},
128 { DEFOP::kNotEqual, "!=", "notequal", BLANK, OpType::kBool, OpMod::kNone, BLANK,
129 {{ CONST, OpType::kThis, OpMod::kReference, },
130 { CONST, OpType::kThis, OpMod::kReference, }}},
131 { DEFOP::kNotEqual, "!=", "notequal1", BLANK, OpType::kBool, OpMod::kNone, CONST,
132 {{ CONST, OpType::kThis, OpMod::kReference, }}},
133 { DEFOP::kNotEqual, "!=", "notequal2", ANY, OpType::kBool, OpMod::kNone, BLANK,
134 {{ CONST, OpType::kThis, OpMod::kReference, },
135 { CONST, OpType::kThis, OpMod::kReference, }}},
136 { DEFOP::kSubtract, "-", "subtract", BLANK, OpType::kThis, OpMod::kNone, BLANK,
137 {{ CONST, OpType::kThis, OpMod::kReference, },
138 { CONST, OpType::kThis, OpMod::kReference, }}},
139 { DEFOP::kSubtractFrom, "-=", "subtractfrom", BLANK, OpType::kVoid, OpMod::kNone, BLANK,
140 {{ CONST, OpType::kThis, OpMod::kReference, }}},
141};
142
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400143OpType lookup_type(string typeWord, string name) {
Cary Clarka560c472017-11-27 10:44:06 -0500144 if (typeWord == name || (typeWord == "SkIVector" && name == "SkIPoint")
145 || (typeWord == "SkVector" && name == "SkPoint")) {
146 return OpType::kThis;
147 }
Cary Clark61313f32018-10-08 14:57:48 -0400148 if ("float" == typeWord || "double" == typeWord) {
149 return OpType::kScalar;
150 }
151 const char* keyWords[] = { "void", "bool", "char", "int", "SkScalar", "size_t" };
Cary Clarka560c472017-11-27 10:44:06 -0500152 for (unsigned i = 0; i < SK_ARRAY_COUNT(keyWords); ++i) {
153 if (typeWord == keyWords[i]) {
154 return (OpType) (i + 1);
155 }
156 }
157 return OpType::kNone;
158}
159
160OpMod lookup_mod(TextParser& iParser) {
161 OpMod mod = OpMod::kNone;
162 if ('&' == iParser.peek()) {
163 mod = OpMod::kReference;
164 iParser.next();
165 if ('&' == iParser.peek()) {
166 mod = OpMod::kMove;
167 iParser.next();
168 }
169 } else if ('*' == iParser.peek()) {
170 mod = OpMod::kPointer;
171 iParser.next();
172 }
173 iParser.skipWhiteSpace();
174 return mod;
175}
176
177bool Definition::parseOperator(size_t doubleColons, string& result) {
178 const char operatorStr[] = "operator";
179 size_t opPos = fName.find(operatorStr, doubleColons);
180 if (string::npos == opPos) {
181 return false;
182 }
183 string className(fName, 0, doubleColons - 2);
184 TextParser iParser(fFileName, fStart, fContentStart, fLineCount);
185 SkAssertResult(iParser.skipWord("#Method"));
186 iParser.skipExact("SK_API");
187 iParser.skipWhiteSpace();
188 bool isStatic = iParser.skipExact("static");
189 iParser.skipWhiteSpace();
190 iParser.skipExact("SK_API");
191 iParser.skipWhiteSpace();
192 bool returnsConst = iParser.skipExact("const");
193 if (returnsConst) {
194 SkASSERT(0); // incomplete
195 }
196 SkASSERT(isStatic == false || returnsConst == false);
197 iParser.skipWhiteSpace();
198 const char* returnTypeStart = iParser.fChar;
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400199 iParser.skipToNonName();
Cary Clarka560c472017-11-27 10:44:06 -0500200 SkASSERT(iParser.fChar > returnTypeStart);
201 string returnType(returnTypeStart, iParser.fChar - returnTypeStart);
202 OpType returnOpType = lookup_type(returnType, className);
203 iParser.skipWhiteSpace();
204 OpMod returnMod = lookup_mod(iParser);
205 SkAssertResult(iParser.skipExact("operator"));
206 iParser.skipWhiteSpace();
207 fMethodType = Definition::MethodType::kOperator;
Cary Clark186d08f2018-04-03 08:43:27 -0400208 TextParserSave save(&iParser);
Cary Clarka560c472017-11-27 10:44:06 -0500209 for (auto parser : opData) {
210 save.restore();
211 if (!iParser.skipExact(parser.fSymbol)) {
212 continue;
213 }
214 iParser.skipWhiteSpace();
215 if ('(' != iParser.peek()) {
216 continue;
217 }
218 if (parser.fFriend != ANY && (parser.fFriend == STATIC) != isStatic) {
219 continue;
220 }
221 if (parser.fReturnType != OpType::kAny && parser.fReturnType != returnOpType) {
222 continue;
223 }
224 if (parser.fReturnMod != OpMod::kAny && parser.fReturnMod != returnMod) {
225 continue;
226 }
227 iParser.next(); // skip '('
228 iParser.skipWhiteSpace();
229 int parserCount = (parser.fParams[0].fType != OpType::kNone) +
230 (parser.fParams[1].fType != OpType::kNone);
231 bool countsMatch = true;
232 for (int pIndex = 0; pIndex < 2; ++pIndex) {
233 if (')' == iParser.peek()) {
234 countsMatch = pIndex == parserCount;
235 break;
236 }
237 if (',' == iParser.peek()) {
238 iParser.next();
239 iParser.skipWhiteSpace();
240 }
241 bool paramConst = iParser.skipExact("const");
242 if (parser.fParams[pIndex].fConst != ANY &&
243 paramConst != (parser.fParams[pIndex].fConst == CONST)) {
244 countsMatch = false;
245 break;
246 }
247 iParser.skipWhiteSpace();
248 const char* paramStart = iParser.fChar;
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400249 iParser.skipToNonName();
Cary Clarka560c472017-11-27 10:44:06 -0500250 SkASSERT(iParser.fChar > paramStart);
251 string paramType(paramStart, iParser.fChar - paramStart);
252 OpType paramOpType = lookup_type(paramType, className);
253 if (parser.fParams[pIndex].fType != OpType::kAny &&
254 parser.fParams[pIndex].fType != paramOpType) {
255 countsMatch = false;
256 break;
257 }
258 iParser.skipWhiteSpace();
259 OpMod paramMod = lookup_mod(iParser);
260 if (parser.fParams[pIndex].fMod != OpMod::kAny &&
261 parser.fParams[pIndex].fMod != paramMod) {
262 countsMatch = false;
263 break;
264 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400265 iParser.skipToNonName();
Cary Clarka560c472017-11-27 10:44:06 -0500266 if ('[' == iParser.peek()) {
267 paramMod = OpMod::kArray;
268 SkAssertResult(iParser.skipExact("[]"));
269 }
270 iParser.skipWhiteSpace();
271 }
272 if (!countsMatch) {
273 continue;
274 }
275 if (')' != iParser.peek()) {
276 continue;
277 }
278 iParser.next();
279 bool constMethod = iParser.skipExact("_const");
280 if (parser.fConstMethod != ANY && (parser.fConstMethod == CONST) != constMethod) {
281 continue;
282 }
283 result += parser.fName;
284 result += "_operator";
285 fOperator = parser.fOperator;
286 fOperatorConst = constMethod;
287 return true;
288 }
289 SkASSERT(0); // incomplete
290 return false;
291#if 0
292 if ('!' == fName[opPos]) {
293 SkASSERT('=' == fName[opPos + 1]);
294 result += "not_equal_operator";
295 } else if ('=' == fName[opPos]) {
296 if ('(' == fName[opPos + 1]) {
297 result += isMove ? "move_" : "copy_";
298 result += "assignment_operator";
299 } else {
300 SkASSERT('=' == fName[opPos + 1]);
301 result += "equal_operator";
302 }
303 } else if ('[' == fName[opPos]) {
304 result += "subscript_operator";
305 const char* end = fContentStart;
306 while (end > fStart && ' ' >= end[-1]) {
307 --end;
308 }
309 string constCheck(fStart, end - fStart);
310 size_t constPos = constCheck.rfind("const");
311 if (constCheck.length() == constPos + 5) {
312 result += "_const";
313 }
314 } else if ('*' == fName[opPos]) {
315 result += "multiply_operator";
316 } else if ('-' == fName[opPos]) {
317 result += "subtract_operator";
318 } else if ('+' == fName[opPos]) {
319 result += "add_operator";
320 } else {
321 SkASSERT(0); // todo: incomplete
322 }
323#endif
324 return true;
325}
326
327#undef CONST
328#undef FRIEND
329#undef BLANK
330#undef DEFOP
331
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400332bool Definition::boilerplateIfDef() {
Cary Clarka560c472017-11-27 10:44:06 -0500333 const Definition& label = fTokens.front();
334 if (Type::kWord != label.fType) {
335 return false;
336 }
337 fName = string(label.fContentStart, label.fContentEnd - label.fContentStart);
338 return true;
339}
340
Cary Clarka560c472017-11-27 10:44:06 -0500341
342// fixme: this will need to be more complicated to handle all of Skia
343// for now, just handle paint -- maybe fiddle will loosen naming restrictions
344void Definition::setCanonicalFiddle() {
345 fMethodType = Definition::MethodType::kNone;
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400346 size_t doubleColons = fName.rfind("::");
Cary Clarka560c472017-11-27 10:44:06 -0500347 SkASSERT(string::npos != doubleColons);
348 string base = fName.substr(0, doubleColons);
349 string result = base + "_";
350 doubleColons += 2;
351 if (string::npos != fName.find('~', doubleColons)) {
352 fMethodType = Definition::MethodType::kDestructor;
353 result += "destructor";
354 } else if (!this->parseOperator(doubleColons, result)) {
355 bool isMove = string::npos != fName.find("&&", doubleColons);
356 size_t parens = fName.find("()", doubleColons);
357 if (string::npos != parens) {
358 string methodName = fName.substr(doubleColons, parens - doubleColons);
359 do {
360 size_t nextDouble = methodName.find("::");
361 if (string::npos == nextDouble) {
362 break;
363 }
364 base = methodName.substr(0, nextDouble);
365 result += base + '_';
366 methodName = methodName.substr(nextDouble + 2);
367 doubleColons += nextDouble + 2;
368 } while (true);
369 if (base == methodName) {
370 fMethodType = Definition::MethodType::kConstructor;
371 result += "empty_constructor";
372 } else {
373 result += fName.substr(doubleColons, fName.length() - doubleColons - 2);
374 }
375 } else {
376 size_t openParen = fName.find('(', doubleColons);
377 if (string::npos == openParen) {
378 result += fName.substr(doubleColons);
Cary Clark682c58d2018-05-16 07:07:07 -0400379 // see if it is a constructor -- if second to last delimited name equals last
380 size_t nextColons = fName.find("::", doubleColons);
381 if (string::npos != nextColons) {
382 nextColons += 2;
383 if (!strncmp(&fName[doubleColons], &fName[nextColons],
384 nextColons - doubleColons - 2)) {
385 fMethodType = Definition::MethodType::kConstructor;
386 }
387 }
Cary Clarka560c472017-11-27 10:44:06 -0500388 } else {
389 size_t comma = fName.find(',', doubleColons);
390 if (string::npos == comma) {
391 result += isMove ? "move_" : "copy_";
392 }
393 fMethodType = Definition::MethodType::kConstructor;
394 // name them by their param types,
395 // e.g. SkCanvas__int_int_const_SkSurfaceProps_star
396 // TODO: move forward until parens are balanced and terminator =,)
397 TextParser params("", &fName[openParen] + 1, &*fName.end(), 0);
398 bool underline = false;
399 while (!params.eof()) {
400// SkDEBUGCODE(const char* end = params.anyOf("(),=")); // unused for now
401// SkASSERT(end[0] != '('); // fixme: put off handling nested parentheseses
402 if (params.startsWith("const") || params.startsWith("int")
403 || params.startsWith("Sk")) {
404 const char* wordStart = params.fChar;
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400405 params.skipToNonName();
Cary Clarka560c472017-11-27 10:44:06 -0500406 if (underline) {
407 result += '_';
408 } else {
409 underline = true;
410 }
411 result += string(wordStart, params.fChar - wordStart);
412 } else {
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400413 params.skipToNonName();
Cary Clarka560c472017-11-27 10:44:06 -0500414 }
415 if (!params.eof() && '*' == params.peek()) {
416 if (underline) {
417 result += '_';
418 } else {
419 underline = true;
420 }
421 result += "star";
422 params.next();
423 params.skipSpace();
424 }
425 params.skipToAlpha();
426 }
427 }
428 }
429 }
430 fFiddle = Definition::NormalizedName(result);
431}
432
Cary Clarka560c472017-11-27 10:44:06 -0500433static void space_pad(string* str) {
434 size_t len = str->length();
435 if (len == 0) {
436 return;
437 }
438 char last = (*str)[len - 1];
439 if ('~' == last || ' ' >= last) {
440 return;
441 }
442 *str += ' ';
443}
444
445//start here;
446// see if it possible to abstract this a little bit so it can
447// additionally be used to find params and return in method prototype that
448// does not have corresponding doxygen comments
449bool Definition::checkMethod() const {
450 SkASSERT(MarkType::kMethod == fMarkType);
451 // if method returns a value, look for a return child
452 // for each parameter, look for a corresponding child
453 const char* end = fContentStart;
454 while (end > fStart && ' ' >= end[-1]) {
455 --end;
456 }
457 TextParser methodParser(fFileName, fStart, end, fLineCount);
458 methodParser.skipWhiteSpace();
459 SkASSERT(methodParser.startsWith("#Method"));
460 methodParser.skipName("#Method");
461 methodParser.skipSpace();
462 string name = this->methodName();
463 if (MethodType::kNone == fMethodType && name.length() > 2 &&
464 "()" == name.substr(name.length() - 2)) {
465 name = name.substr(0, name.length() - 2);
466 }
467 bool expectReturn = this->methodHasReturn(name, &methodParser);
468 bool foundReturn = false;
469 bool foundException = false;
470 for (auto& child : fChildren) {
471 foundException |= MarkType::kDeprecated == child->fMarkType
472 || MarkType::kExperimental == child->fMarkType;
473 if (MarkType::kReturn != child->fMarkType) {
474 if (MarkType::kParam == child->fMarkType) {
475 child->fVisited = false;
476 }
477 continue;
478 }
479 if (!expectReturn) {
480 return methodParser.reportError<bool>("no #Return expected");
481 }
482 if (foundReturn) {
483 return methodParser.reportError<bool>("multiple #Return markers");
484 }
485 foundReturn = true;
486 }
487 if (expectReturn && !foundReturn && !foundException) {
488 return methodParser.reportError<bool>("missing #Return marker");
489 }
490 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
491 if (!paren) {
492 return methodParser.reportError<bool>("missing #Method function definition");
493 }
494 const char* nextEnd = paren;
495 do {
496 string paramName;
497 methodParser.fChar = nextEnd + 1;
498 methodParser.skipSpace();
499 if (!this->nextMethodParam(&methodParser, &nextEnd, &paramName)) {
500 continue;
501 }
502 bool foundParam = false;
503 for (auto& child : fChildren) {
504 if (MarkType::kParam != child->fMarkType) {
505 continue;
506 }
507 if (paramName != child->fName) {
508 continue;
509 }
510 if (child->fVisited) {
511 return methodParser.reportError<bool>("multiple #Method param with same name");
512 }
513 child->fVisited = true;
514 if (foundParam) {
515 TextParser paramError(child);
516 return methodParser.reportError<bool>("multiple #Param with same name");
517 }
518 foundParam = true;
519
520 }
521 if (!foundParam && !foundException) {
522 return methodParser.reportError<bool>("no #Param found");
523 }
524 if (')' == nextEnd[0]) {
525 break;
526 }
527 } while (')' != nextEnd[0]);
528 for (auto& child : fChildren) {
529 if (MarkType::kParam != child->fMarkType) {
530 continue;
531 }
532 if (!child->fVisited) {
533 TextParser paramError(child);
534 return paramError.reportError<bool>("#Param without param in #Method");
535 }
536 }
Cary Clark80247e52018-07-11 16:18:41 -0400537 // check after end of #Line and before next child for description
538 const char* descStart = fContentStart;
539 const char* descEnd = nullptr;
Cary Clarkab5c9af2018-07-12 16:24:53 -0400540 const Definition* defEnd = nullptr;
541 const Definition* priorDef = nullptr;
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400542 bool incomplete = false;
Cary Clark80247e52018-07-11 16:18:41 -0400543 for (auto& child : fChildren) {
544 if (MarkType::kAnchor == child->fMarkType) {
545 continue;
546 }
547 if (MarkType::kCode == child->fMarkType) {
Cary Clarkab5c9af2018-07-12 16:24:53 -0400548 priorDef = child;
Cary Clark80247e52018-07-11 16:18:41 -0400549 continue;
550 }
551 if (MarkType::kDeprecated == child->fMarkType) {
552 return true;
553 }
554 if (MarkType::kExperimental == child->fMarkType) {
555 return true;
556 }
557 if (MarkType::kFormula == child->fMarkType) {
558 continue;
559 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400560 if (MarkType::kLine == child->fMarkType) {
561 SkASSERT(child->fChildren.size() > 0);
562 TextParser childDesc(child->fChildren[0]);
563 incomplete |= childDesc.startsWith("incomplete");
564 }
Cary Clark80247e52018-07-11 16:18:41 -0400565 if (MarkType::kList == child->fMarkType) {
Cary Clarkab5c9af2018-07-12 16:24:53 -0400566 priorDef = child;
Cary Clark80247e52018-07-11 16:18:41 -0400567 continue;
568 }
569 if (MarkType::kMarkChar == child->fMarkType) {
570 continue;
571 }
572 if (MarkType::kPhraseRef == child->fMarkType) {
573 continue;
574 }
575 if (MarkType::kPrivate == child->fMarkType) {
576 return true;
577 }
578 TextParser emptyCheck(fFileName, descStart, child->fStart, child->fLineCount);
579 if (!emptyCheck.eof() && emptyCheck.skipWhiteSpace()) {
580 descStart = emptyCheck.fChar;
581 emptyCheck.trimEnd();
Cary Clarkab5c9af2018-07-12 16:24:53 -0400582 defEnd = priorDef;
Cary Clark80247e52018-07-11 16:18:41 -0400583 descEnd = emptyCheck.fEnd;
584 break;
585 }
586 descStart = child->fTerminator;
Cary Clarkab5c9af2018-07-12 16:24:53 -0400587 priorDef = nullptr;
Cary Clark80247e52018-07-11 16:18:41 -0400588 }
589 if (!descEnd) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400590 return incomplete ? true : methodParser.reportError<bool>("missing description");
Cary Clark80247e52018-07-11 16:18:41 -0400591 }
592 TextParser description(fFileName, descStart, descEnd, fLineCount);
593 // expect first word capitalized and pluralized. expect a trailing period
594 SkASSERT(descStart < descEnd);
595 if (!isupper(descStart[0])) {
596 description.reportWarning("expected capital");
597 } else if ('.' != descEnd[-1]) {
Cary Clarkab5c9af2018-07-12 16:24:53 -0400598 if (!defEnd || defEnd->fTerminator != descEnd) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400599 if (!incomplete) {
600 description.reportWarning("expected period");
601 }
Cary Clarkab5c9af2018-07-12 16:24:53 -0400602 }
Cary Clark80247e52018-07-11 16:18:41 -0400603 } else {
604 if (!description.startsWith("For use by Android")) {
605 description.skipToSpace();
606 if (',' == description.fChar[-1]) {
607 --description.fChar;
608 }
609 if ('s' != description.fChar[-1]) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400610 if (!incomplete) {
611 description.reportWarning("expected plural");
612 }
Cary Clark80247e52018-07-11 16:18:41 -0400613 }
614 }
615 }
Cary Clarka560c472017-11-27 10:44:06 -0500616 return true;
617}
618
619bool Definition::crossCheck2(const Definition& includeToken) const {
620 TextParser parser(fFileName, fStart, fContentStart, fLineCount);
621 parser.skipExact("#");
622 bool isMethod = parser.skipName("Method");
623 const char* contentEnd;
624 if (isMethod) {
625 contentEnd = fContentStart;
626 } else if (parser.skipName("DefinedBy")) {
627 contentEnd = fContentEnd;
628 while (parser.fChar < contentEnd && ' ' >= contentEnd[-1]) {
629 --contentEnd;
630 }
631 if (parser.fChar < contentEnd - 1 && ')' == contentEnd[-1] && '(' == contentEnd[-2]) {
632 contentEnd -= 2;
633 }
634 } else {
635 return parser.reportError<bool>("unexpected crosscheck marktype");
636 }
637 return crossCheckInside(parser.fChar, contentEnd, includeToken);
638}
639
640bool Definition::crossCheck(const Definition& includeToken) const {
641 return crossCheckInside(fContentStart, fContentEnd, includeToken);
642}
643
Cary Clarkab5c9af2018-07-12 16:24:53 -0400644const char* Definition::methodEnd() const {
645 const char defaultTag[] = " = default";
646 size_t tagSize = sizeof(defaultTag) - 1;
647 const char* tokenEnd = fContentEnd - tagSize;
648 if (tokenEnd <= fContentStart || strncmp(tokenEnd, defaultTag, tagSize)) {
649 tokenEnd = fContentEnd;
650 }
651 return tokenEnd;
652}
653
Cary Clarka560c472017-11-27 10:44:06 -0500654bool Definition::crossCheckInside(const char* start, const char* end,
655 const Definition& includeToken) const {
656 TextParser def(fFileName, start, end, fLineCount);
Cary Clarkab5c9af2018-07-12 16:24:53 -0400657 const char* tokenEnd = includeToken.methodEnd();
658 TextParser inc("", includeToken.fContentStart, tokenEnd, 0);
Cary Clarka560c472017-11-27 10:44:06 -0500659 if (inc.startsWith("SK_API")) {
660 inc.skipWord("SK_API");
661 }
Cary Clark61313f32018-10-08 14:57:48 -0400662 if (inc.startsWith("inline")) {
663 inc.skipWord("inline");
664 }
Cary Clarka560c472017-11-27 10:44:06 -0500665 if (inc.startsWith("friend")) {
666 inc.skipWord("friend");
667 }
668 if (inc.startsWith("SK_API")) {
669 inc.skipWord("SK_API");
670 }
671 inc.skipExact("SkDEBUGCODE(");
672 do {
673 bool defEof;
674 bool incEof;
675 do {
676 defEof = def.eof() || !def.skipWhiteSpace();
677 incEof = inc.eof() || !inc.skipWhiteSpace();
678 if (!incEof && '/' == inc.peek() && (defEof || '/' != def.peek())) {
679 inc.next();
680 if ('*' == inc.peek()) {
681 inc.skipToEndBracket("*/");
682 inc.next();
683 } else if ('/' == inc.peek()) {
684 inc.skipToEndBracket('\n');
685 }
686 } else if (!incEof && '#' == inc.peek() && (defEof || '#' != def.peek())) {
687 inc.next();
688 if (inc.startsWith("if")) {
689 inc.skipToEndBracket("\n");
690 } else if (inc.startsWith("endif")) {
691 inc.skipToEndBracket("\n");
692 } else {
693 SkASSERT(0); // incomplete
694 return false;
695 }
696 } else {
697 break;
698 }
699 inc.next();
700 } while (true);
701 if (defEof || incEof) {
702 if (defEof == incEof || (!defEof && ';' == def.peek())) {
703 return true;
704 }
705 return false; // allow setting breakpoint on failure
706 }
707 char defCh;
708 do {
709 defCh = def.next();
Cary Clark61313f32018-10-08 14:57:48 -0400710 if (inc.skipExact("SK_WARN_UNUSED_RESULT")) {
711 inc.skipSpace();
712 }
Cary Clarka560c472017-11-27 10:44:06 -0500713 char incCh = inc.next();
714 if (' ' >= defCh && ' ' >= incCh) {
715 break;
716 }
717 if (defCh != incCh) {
718 if ('_' != defCh || ' ' != incCh || !fOperatorConst || !def.startsWith("const")) {
719 return false;
720 }
721 }
722 if (';' == defCh) {
723 return true;
724 }
725 } while (!def.eof() && !inc.eof());
726 } while (true);
727 return false;
728}
729
Cary Clark78de7512018-02-07 07:27:09 -0500730string Definition::formatFunction(Format format) const {
Cary Clarka560c472017-11-27 10:44:06 -0500731 const char* end = fContentStart;
732 while (end > fStart && ' ' >= end[-1]) {
733 --end;
734 }
735 TextParser methodParser(fFileName, fStart, end, fLineCount);
736 methodParser.skipWhiteSpace();
737 SkASSERT(methodParser.startsWith("#Method"));
738 methodParser.skipName("#Method");
739 methodParser.skipSpace();
740 const char* lastStart = methodParser.fChar;
741 const int limit = 100; // todo: allow this to be set by caller or in global or something
742 string name = this->methodName();
Cary Clarka560c472017-11-27 10:44:06 -0500743 const char* nameInParser = methodParser.strnstr(name.c_str(), methodParser.fEnd);
744 methodParser.skipTo(nameInParser);
745 const char* lastEnd = methodParser.fChar;
Cary Clark78de7512018-02-07 07:27:09 -0500746 if (Format::kOmitReturn == format) {
747 lastStart = lastEnd;
748 }
Cary Clarka560c472017-11-27 10:44:06 -0500749 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
750 size_t indent;
751 if (paren) {
752 indent = (size_t) (paren - lastStart) + 1;
753 } else {
754 indent = (size_t) (lastEnd - lastStart);
755 }
756 // trim indent so longest line doesn't exceed box width
Cary Clark186d08f2018-04-03 08:43:27 -0400757 TextParserSave savePlace(&methodParser);
Cary Clarka560c472017-11-27 10:44:06 -0500758 const char* saveStart = lastStart;
759 ptrdiff_t maxLine = 0;
760 do {
761 const char* nextStart = lastEnd;
762 const char* delimiter = methodParser.anyOf(",)");
763 const char* nextEnd = delimiter ? delimiter : methodParser.fEnd;
764 if (delimiter) {
765 while (nextStart < nextEnd && ' ' >= nextStart[0]) {
766 ++nextStart;
767 }
768 }
769 while (nextEnd > nextStart && ' ' >= nextEnd[-1]) {
770 --nextEnd;
771 }
772 if (delimiter) {
773 nextEnd += 1;
774 delimiter += 1;
775 }
776 if (lastEnd > lastStart) {
777 maxLine = SkTMax(maxLine, lastEnd - lastStart);
778 }
779 if (delimiter) {
780 methodParser.skipTo(delimiter);
781 }
782 lastStart = nextStart;
783 lastEnd = nextEnd;
784 } while (lastStart < lastEnd);
785 savePlace.restore();
786 lastStart = saveStart;
787 lastEnd = methodParser.fChar;
788 indent = SkTMin(indent, (size_t) (limit - maxLine));
Cary Clark682c58d2018-05-16 07:07:07 -0400789 // write string with trimmmed indent
Cary Clarka560c472017-11-27 10:44:06 -0500790 string methodStr;
791 int written = 0;
792 do {
793 const char* nextStart = lastEnd;
Cary Clark390495e2018-07-13 07:52:44 -0400794// SkASSERT(written < limit);
Cary Clarka560c472017-11-27 10:44:06 -0500795 const char* delimiter = methodParser.anyOf(",)");
796 const char* nextEnd = delimiter ? delimiter : methodParser.fEnd;
797 if (delimiter) {
798 while (nextStart < nextEnd && ' ' >= nextStart[0]) {
799 ++nextStart;
800 }
801 }
802 while (nextEnd > nextStart && ' ' >= nextEnd[-1]) {
803 --nextEnd;
804 }
805 if (delimiter) {
806 nextEnd += 1;
807 delimiter += 1;
808 }
809 if (lastEnd > lastStart) {
810 if (lastStart[0] != ' ') {
811 space_pad(&methodStr);
812 }
Cary Clark61313f32018-10-08 14:57:48 -0400813 string addon(lastStart, (size_t) (lastEnd - lastStart));
814 if ("_const" == addon) {
815 addon = "const";
816 }
817 methodStr += addon;
818 written += addon.length();
Cary Clarka560c472017-11-27 10:44:06 -0500819 }
820 if (delimiter) {
821 if (nextEnd - nextStart >= (ptrdiff_t) (limit - written)) {
822 written = indent;
Cary Clark78de7512018-02-07 07:27:09 -0500823 if (Format::kIncludeReturn == format) {
824 methodStr += '\n';
825 methodStr += string(indent, ' ');
826 }
Cary Clarka560c472017-11-27 10:44:06 -0500827 }
828 methodParser.skipTo(delimiter);
829 }
830 lastStart = nextStart;
831 lastEnd = nextEnd;
832 } while (lastStart < lastEnd);
833 return methodStr;
834}
835
836string Definition::fiddleName() const {
837 string result;
838 size_t start = 0;
839 string parent;
840 const Definition* parentDef = this;
841 while ((parentDef = parentDef->fParent)) {
842 if (MarkType::kClass == parentDef->fMarkType || MarkType::kStruct == parentDef->fMarkType) {
843 parent = parentDef->fFiddle;
844 break;
845 }
846 }
847 if (parent.length() && 0 == fFiddle.compare(0, parent.length(), parent)) {
848 start = parent.length();
849 while (start < fFiddle.length() && '_' == fFiddle[start]) {
850 ++start;
851 }
852 }
853 size_t end = fFiddle.find_first_of('(', start);
854 return fFiddle.substr(start, end - start);
855}
856
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400857string Definition::fileName() const {
858 size_t nameStart = fFileName.rfind(SkOSPath::SEPARATOR);
859 if (SkOSPath::SEPARATOR != '/') {
860 size_t altNameStart = fFileName.rfind('/');
861 nameStart = string::npos == nameStart ? altNameStart :
862 string::npos != altNameStart && altNameStart > nameStart ? altNameStart : nameStart;
863 }
864 SkASSERT(string::npos != nameStart);
865 string baseFile = fFileName.substr(nameStart + 1);
866 return baseFile;
867}
868
Cary Clark4855f782018-02-06 09:41:53 -0500869const Definition* Definition::findClone(string match) const {
870 for (auto child : fChildren) {
871 if (!child->fClone) {
872 continue;
873 }
874 if (match == child->fName) {
875 return child;
876 }
877 auto inner = child->findClone(match);
878 if (inner) {
879 return inner;
880 }
881 }
882 return nullptr;
883}
884
Cary Clarka560c472017-11-27 10:44:06 -0500885const Definition* Definition::hasChild(MarkType markType) const {
886 for (auto iter : fChildren) {
887 if (markType == iter->fMarkType) {
888 return iter;
889 }
890 }
891 return nullptr;
892}
893
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400894const Definition* Definition::hasParam(string ref) const {
Cary Clarka560c472017-11-27 10:44:06 -0500895 SkASSERT(MarkType::kMethod == fMarkType);
896 for (auto iter : fChildren) {
897 if (MarkType::kParam != iter->fMarkType) {
898 continue;
899 }
900 if (iter->fName == ref) {
901 return &*iter;
902 }
903
904 }
905 return nullptr;
906}
907
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400908bool Definition::hasMatch(string name) const {
Cary Clarkac47b882018-01-11 10:35:44 -0500909 for (auto child : fChildren) {
910 if (name == child->fName) {
911 return true;
912 }
913 if (child->hasMatch(name)) {
914 return true;
915 }
916 }
917 return false;
918}
919
Cary Clark682c58d2018-05-16 07:07:07 -0400920string Definition::incompleteMessage(DetailsType detailsType) const {
Cary Clark137b8742018-05-30 09:21:49 -0400921 SkASSERT(!IncompleteAllowed(fMarkType));
922 auto iter = std::find_if(fChildren.begin(), fChildren.end(),
923 [](const Definition* test) { return IncompleteAllowed(test->fMarkType); });
924 SkASSERT(fChildren.end() != iter);
925 SkASSERT(Details::kNone == (*iter)->fDetails);
926 string message = MarkType::kExperimental == (*iter)->fMarkType ?
Cary Clark682c58d2018-05-16 07:07:07 -0400927 "Experimental." : "Deprecated.";
Cary Clark137b8742018-05-30 09:21:49 -0400928 if (Details::kDoNotUse_Experiment == fDetails) {
Cary Clark682c58d2018-05-16 07:07:07 -0400929 message += " Do not use.";
Cary Clark137b8742018-05-30 09:21:49 -0400930 } else if (Details::kNotReady_Experiment == fDetails) {
Cary Clark682c58d2018-05-16 07:07:07 -0400931 message += " Not ready for general use.";
Cary Clark137b8742018-05-30 09:21:49 -0400932 } else if (Details::kSoonToBe_Deprecated == fDetails) {
933 message = "To be deprecated soon.";
934 } else if (Details::kTestingOnly_Experiment == fDetails) {
Cary Clark682c58d2018-05-16 07:07:07 -0400935 message += " For testing only.";
936 }
937 if (DetailsType::kPhrase == detailsType) {
938 message = message.substr(0, message.length() - 1); // remove trailing period
939 std::replace(message.begin(), message.end(), '.', ':');
940 std::transform(message.begin(), message.end(), message.begin(), ::tolower);
941 }
942 return message;
943}
944
Cary Clarkab2621d2018-01-30 10:08:57 -0500945bool Definition::isStructOrClass() const {
946 if (MarkType::kStruct != fMarkType && MarkType::kClass != fMarkType) {
947 return false;
948 }
949 if (string::npos != fFileName.find("undocumented.bmh")) {
950 return false;
951 }
952 return true;
953}
954
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400955bool Definition::methodHasReturn(string name, TextParser* methodParser) const {
Cary Clarka560c472017-11-27 10:44:06 -0500956 if (methodParser->skipExact("static")) {
957 methodParser->skipWhiteSpace();
958 }
Cary Clark80247e52018-07-11 16:18:41 -0400959 if (methodParser->skipExact("virtual")) {
960 methodParser->skipWhiteSpace();
961 }
Cary Clarka560c472017-11-27 10:44:06 -0500962 const char* lastStart = methodParser->fChar;
963 const char* nameInParser = methodParser->strnstr(name.c_str(), methodParser->fEnd);
964 methodParser->skipTo(nameInParser);
965 const char* lastEnd = methodParser->fChar;
966 const char* returnEnd = lastEnd;
967 while (returnEnd > lastStart && ' ' == returnEnd[-1]) {
968 --returnEnd;
969 }
970 bool expectReturn = 4 != returnEnd - lastStart || strncmp("void", lastStart, 4);
971 if (MethodType::kNone != fMethodType && MethodType::kOperator != fMethodType && !expectReturn) {
972 return methodParser->reportError<bool>("unexpected void");
973 }
974 switch (fMethodType) {
975 case MethodType::kNone:
976 case MethodType::kOperator:
977 // either is fine
978 break;
979 case MethodType::kConstructor:
980 expectReturn = true;
981 break;
982 case MethodType::kDestructor:
983 expectReturn = false;
984 break;
985 }
986 return expectReturn;
987}
988
989string Definition::methodName() const {
990 string result;
991 size_t start = 0;
992 string parent;
993 const Definition* parentDef = this;
994 while ((parentDef = parentDef->fParent)) {
995 if (MarkType::kClass == parentDef->fMarkType || MarkType::kStruct == parentDef->fMarkType) {
996 parent = parentDef->fName;
997 break;
998 }
999 }
1000 if (parent.length() && 0 == fName.compare(0, parent.length(), parent)) {
1001 start = parent.length();
1002 while (start < fName.length() && ':' == fName[start]) {
1003 ++start;
1004 }
1005 }
1006 if (fClone) {
1007 int lastUnder = fName.rfind('_');
1008 return fName.substr(start, (size_t) (lastUnder - start));
1009 }
1010 size_t end = fName.find_first_of('(', start);
1011 if (string::npos == end) {
1012 return fName.substr(start);
1013 }
1014 return fName.substr(start, end - start);
1015}
1016
1017bool Definition::nextMethodParam(TextParser* methodParser, const char** nextEndPtr,
1018 string* paramName) const {
1019 int parenCount = 0;
Cary Clark186d08f2018-04-03 08:43:27 -04001020 TextParserSave saveState(methodParser);
Cary Clarka560c472017-11-27 10:44:06 -05001021 while (true) {
1022 if (methodParser->eof()) {
1023 return methodParser->reportError<bool>("#Method function missing close paren");
1024 }
1025 char ch = methodParser->peek();
1026 if ('(' == ch) {
1027 ++parenCount;
1028 }
1029 if (parenCount == 0 && (')' == ch || ',' == ch)) {
1030 *nextEndPtr = methodParser->fChar;
1031 break;
1032 }
1033 if (')' == ch) {
1034 if (0 > --parenCount) {
1035 return this->reportError<bool>("mismatched parentheses");
1036 }
1037 }
1038 methodParser->next();
1039 }
1040 saveState.restore();
1041 const char* nextEnd = *nextEndPtr;
1042 const char* paramEnd = nextEnd;
1043 const char* assign = methodParser->strnstr(" = ", paramEnd);
1044 if (assign) {
1045 paramEnd = assign;
1046 }
1047 const char* closeBracket = methodParser->strnstr("]", paramEnd);
1048 if (closeBracket) {
1049 const char* openBracket = methodParser->strnstr("[", paramEnd);
1050 if (openBracket && openBracket < closeBracket) {
1051 while (openBracket < --closeBracket && isdigit(closeBracket[0]))
1052 ;
1053 if (openBracket == closeBracket) {
1054 paramEnd = openBracket;
1055 }
1056 }
1057 }
1058 const char* function = methodParser->strnstr(")(", paramEnd);
1059 if (function) {
1060 paramEnd = function;
1061 }
1062 while (paramEnd > methodParser->fChar && ' ' == paramEnd[-1]) {
1063 --paramEnd;
1064 }
1065 const char* paramStart = paramEnd;
1066 while (paramStart > methodParser->fChar && isalnum(paramStart[-1])) {
1067 --paramStart;
1068 }
1069 if (paramStart > methodParser->fChar && paramStart >= paramEnd) {
1070 return methodParser->reportError<bool>("#Method missing param name");
1071 }
1072 *paramName = string(paramStart, paramEnd - paramStart);
1073 if (!paramName->length()) {
1074 if (')' != nextEnd[0]) {
1075 return methodParser->reportError<bool>("#Method malformed param");
1076 }
1077 return false;
1078 }
1079 return true;
1080}
1081
1082string Definition::NormalizedName(string name) {
1083 string normalizedName = name;
1084 std::replace(normalizedName.begin(), normalizedName.end(), '-', '_');
1085 do {
1086 size_t doubleColon = normalizedName.find("::", 0);
1087 if (string::npos == doubleColon) {
1088 break;
1089 }
1090 normalizedName = normalizedName.substr(0, doubleColon)
1091 + '_' + normalizedName.substr(doubleColon + 2);
1092 } while (true);
1093 return normalizedName;
1094}
1095
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001096static string unpreformat(string orig) {
Cary Clark78de7512018-02-07 07:27:09 -05001097 string result;
1098 int amp = 0;
1099 for (auto c : orig) {
1100 switch (amp) {
1101 case 0:
1102 if ('&' == c) {
1103 amp = 1;
1104 } else {
1105 amp = 0;
1106 result += c;
1107 }
1108 break;
1109 case 1:
1110 if ('l' == c) {
1111 amp = 2;
1112 } else if ('g' == c) {
1113 amp = 3;
1114 } else {
1115 amp = 0;
1116 result += "&";
1117 result += c;
1118 }
1119 break;
1120 case 2:
1121 if ('t' == c) {
1122 amp = 4;
1123 } else {
1124 amp = 0;
1125 result += "&l";
1126 result += c;
1127 }
1128 break;
1129 case 3:
1130 if ('t' == c) {
1131 amp = 5;
1132 } else {
1133 amp = 0;
1134 result += "&g";
1135 result += c;
1136 }
1137 break;
1138 case 4:
1139 if (';' == c) {
1140 result += '<';
1141 } else {
1142 result += "&lt";
1143 result += c;
1144 }
1145 amp = 0;
1146 break;
1147 case 5:
1148 if (';' == c) {
1149 result += '>';
1150 } else {
1151 result += "&gt";
1152 result += c;
1153 }
1154 amp = 0;
1155 break;
1156 }
1157 }
1158 return result;
1159}
1160
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001161bool Definition::paramsMatch(string matchFormatted, string name) const {
Cary Clark78de7512018-02-07 07:27:09 -05001162 string match = unpreformat(matchFormatted);
Cary Clarka560c472017-11-27 10:44:06 -05001163 TextParser def(fFileName, fStart, fContentStart, fLineCount);
1164 const char* dName = def.strnstr(name.c_str(), fContentStart);
1165 if (!dName) {
1166 return false;
1167 }
1168 def.skipTo(dName);
1169 TextParser m(fFileName, &match.front(), &match.back() + 1, fLineCount);
1170 const char* mName = m.strnstr(name.c_str(), m.fEnd);
1171 if (!mName) {
1172 return false;
1173 }
1174 m.skipTo(mName);
1175 while (!def.eof() && ')' != def.peek() && !m.eof() && ')' != m.peek()) {
1176 const char* ds = def.fChar;
1177 const char* ms = m.fChar;
1178 const char* de = def.anyOf(") \n");
1179 const char* me = m.anyOf(") \n");
1180 def.skipTo(de);
1181 m.skipTo(me);
1182 if (def.fChar - ds != m.fChar - ms) {
1183 return false;
1184 }
1185 if (strncmp(ds, ms, (int) (def.fChar - ds))) {
1186 return false;
1187 }
1188 def.skipWhiteSpace();
1189 m.skipWhiteSpace();
1190 }
1191 return !def.eof() && ')' == def.peek() && !m.eof() && ')' == m.peek();
1192}
1193
Cary Clark2be81cf2018-09-13 12:04:30 -04001194
1195void Definition::trimEnd() {
1196 while (fContentEnd > fContentStart && ' ' >= fContentEnd[-1]) {
1197 --fContentEnd;
1198 }
1199}
1200
Cary Clarka560c472017-11-27 10:44:06 -05001201void RootDefinition::clearVisited() {
1202 fVisited = false;
1203 for (auto& leaf : fLeaves) {
1204 leaf.second.fVisited = false;
1205 }
1206 for (auto& branch : fBranches) {
1207 branch.second->clearVisited();
1208 }
1209}
1210
Cary Clarkf5404bb2018-01-05 12:10:09 -05001211bool RootDefinition::dumpUnVisited() {
1212 bool success = true;
Cary Clarka560c472017-11-27 10:44:06 -05001213 for (auto& leaf : fLeaves) {
1214 if (!leaf.second.fVisited) {
Cary Clarka560c472017-11-27 10:44:06 -05001215 // FIXME: bugs requiring long tail fixes, suppressed here:
1216 // SkBitmap::validate() is wrapped in SkDEBUGCODE in .h and not parsed
1217 if ("SkBitmap::validate()" == leaf.first) {
1218 continue;
1219 }
Cary Clarka560c472017-11-27 10:44:06 -05001220 // SkPath::pathRefIsValid in #ifdef ; prefer to remove chrome dependency to fix
1221 if ("SkPath::pathRefIsValid" == leaf.first) {
1222 continue;
1223 }
1224 // FIXME: end of long tail bugs
1225 SkDebugf("defined in bmh but missing in include: %s\n", leaf.first.c_str());
Cary Clarkf5404bb2018-01-05 12:10:09 -05001226 success = false;
Cary Clarka560c472017-11-27 10:44:06 -05001227 }
1228 }
1229 for (auto& branch : fBranches) {
Cary Clarkf5404bb2018-01-05 12:10:09 -05001230 success &= branch.second->dumpUnVisited();
Cary Clarka560c472017-11-27 10:44:06 -05001231 }
Cary Clarkf5404bb2018-01-05 12:10:09 -05001232 return success;
Cary Clarka560c472017-11-27 10:44:06 -05001233}
1234
Cary Clark682c58d2018-05-16 07:07:07 -04001235Definition* RootDefinition::find(string ref, AllowParens allowParens) {
Cary Clarka560c472017-11-27 10:44:06 -05001236 const auto leafIter = fLeaves.find(ref);
1237 if (leafIter != fLeaves.end()) {
1238 return &leafIter->second;
1239 }
Cary Clarka8cdc172018-09-07 14:17:08 -04001240 if (AllowParens::kYes == allowParens) {
1241 size_t leftParen = ref.find('(');
1242 if (string::npos == leftParen
1243 || (leftParen + 1 < ref.length() && ')' != ref[leftParen + 1])) {
1244 string withParens = ref + "()";
1245 const auto parensIter = fLeaves.find(withParens);
1246 if (parensIter != fLeaves.end()) {
1247 return &parensIter->second;
1248 }
1249 }
1250 if (string::npos != leftParen) {
1251 string name = ref.substr(0, leftParen);
1252 size_t posInDefName = fName.find(name);
1253 if (string::npos != posInDefName && posInDefName > 2
1254 && "::" == fName.substr(posInDefName - 2, 2)) {
1255 string fullRef = fName + "::" + ref;
1256 const auto fullIter = fLeaves.find(fullRef);
1257 if (fullIter != fLeaves.end()) {
1258 return &fullIter->second;
1259 }
1260 }
Cary Clarka560c472017-11-27 10:44:06 -05001261 }
1262 }
1263 const auto branchIter = fBranches.find(ref);
1264 if (branchIter != fBranches.end()) {
Cary Clark682c58d2018-05-16 07:07:07 -04001265 RootDefinition* rootDef = branchIter->second;
Cary Clarka560c472017-11-27 10:44:06 -05001266 return rootDef;
1267 }
Cary Clark682c58d2018-05-16 07:07:07 -04001268 Definition* result = nullptr;
Cary Clarka560c472017-11-27 10:44:06 -05001269 for (const auto& branch : fBranches) {
Cary Clark682c58d2018-05-16 07:07:07 -04001270 RootDefinition* rootDef = branch.second;
Cary Clarka560c472017-11-27 10:44:06 -05001271 result = rootDef->find(ref, allowParens);
1272 if (result) {
1273 break;
1274 }
1275 }
1276 return result;
1277}