blob: f693fd83edcf1b214f1c7ee3bf6aa4e85ee383d0 [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
Cary Clark2d4bf5f2018-04-16 08:37:38 -04008#include "SkOSPath.h"
Cary Clarka560c472017-11-27 10:44:06 -05009
Cary Clark2da9fb82018-11-01 09:29:36 -040010#include "definition.h"
11#include "textParser.h"
12
Cary Clarka560c472017-11-27 10:44:06 -050013#ifdef CONST
14#undef CONST
15#endif
16
17#ifdef FRIEND
18#undef FRIEND
19#endif
20
21#ifdef BLANK
22#undef BLANK
23#endif
24
25#ifdef ANY
26#undef ANY
27#endif
28
29#ifdef DEFOP
30#undef DEFOP
31#endif
32
33#define CONST 1
34#define STATIC 2
35#define BLANK 0
36#define ANY -1
37#define DEFOP Definition::Operator
38
39enum class OpType : int8_t {
40 kNone,
41 kVoid,
42 kBool,
43 kChar,
Cary Clarka560c472017-11-27 10:44:06 -050044 kInt,
45 kScalar,
46 kSizeT,
47 kThis,
48 kAny,
49};
50
51enum class OpMod : int8_t {
52 kNone,
53 kArray,
54 kMove,
55 kPointer,
56 kReference,
57 kAny,
58};
59
60const struct OperatorParser {
61 DEFOP fOperator;
62 const char* fSymbol;
63 const char* fName;
64 int8_t fFriend;
65 OpType fReturnType;
66 OpMod fReturnMod;
67 int8_t fConstMethod;
68 struct Param {
69 int8_t fConst;
70 OpType fType;
71 OpMod fMod;
72 } fParams[2];
73} opData[] = {
74 { DEFOP::kUnknown, "??", "???", BLANK, OpType::kNone, OpMod::kNone, BLANK,
75 { } },
76 { DEFOP::kAdd, "+", "add", BLANK, OpType::kThis, OpMod::kNone, BLANK,
77 {{ CONST, OpType::kThis, OpMod::kReference, },
78 { CONST, OpType::kThis, OpMod::kReference, }}},
79 { DEFOP::kAddTo, "+=", "addto", BLANK, OpType::kVoid, OpMod::kNone, BLANK,
80 {{ CONST, OpType::kThis, OpMod::kReference, }}},
81 { DEFOP::kAddTo, "+=", "addto1", BLANK, OpType::kThis, OpMod::kReference, BLANK,
82 {{ CONST, OpType::kThis, OpMod::kReference, }}},
83 { DEFOP::kAddTo, "+=", "addto2", BLANK, OpType::kThis, OpMod::kReference, BLANK,
84 {{ CONST, OpType::kChar, OpMod::kArray, }}},
85 { DEFOP::kAddTo, "+=", "addto3", BLANK, OpType::kThis, OpMod::kReference, BLANK,
86 {{ CONST, OpType::kChar, OpMod::kNone, }}},
87 { DEFOP::kArray, "[]", "array", BLANK, OpType::kScalar, OpMod::kNone, CONST,
88 {{ BLANK, OpType::kInt, OpMod::kNone, }}},
89 { DEFOP::kArray, "[]", "array1", BLANK, OpType::kScalar, OpMod::kReference, BLANK,
90 {{ BLANK, OpType::kInt, OpMod::kNone, }}},
91 { DEFOP::kArray, "[]", "array2", BLANK, OpType::kChar, OpMod::kNone, CONST,
92 {{ BLANK, OpType::kSizeT, OpMod::kNone, }}},
93 { DEFOP::kArray, "[]", "array3", BLANK, OpType::kChar, OpMod::kReference, BLANK,
94 {{ BLANK, OpType::kSizeT, OpMod::kNone, }}},
95 { DEFOP::kCast, "()", "cast", BLANK, OpType::kAny, OpMod::kAny, ANY,
96 {{ ANY, OpType::kAny, OpMod::kAny, }}},
97 { DEFOP::kCopy, "=", "copy", BLANK, OpType::kThis, OpMod::kReference, BLANK,
98 {{ CONST, OpType::kThis, OpMod::kReference, }}},
99 { DEFOP::kCopy, "=", "copy1", BLANK, OpType::kThis, OpMod::kReference, BLANK,
100 {{ CONST, OpType::kChar, OpMod::kArray, }}},
101 { DEFOP::kDelete, "delete", "delete", BLANK, OpType::kVoid, OpMod::kNone, BLANK,
102 {{ BLANK, OpType::kVoid, OpMod::kPointer, }}},
103 { DEFOP::kDereference, "->", "deref", ANY, OpType::kThis, OpMod::kPointer, CONST,
104 { } },
105 { DEFOP::kDereference, "*", "deref", BLANK, OpType::kThis, OpMod::kReference, CONST,
106 { } },
107 { DEFOP::kEqual, "==", "equal", BLANK, OpType::kBool, OpMod::kNone, BLANK,
108 {{ CONST, OpType::kThis, OpMod::kReference, },
109 { CONST, OpType::kThis, OpMod::kReference, }}},
110 { DEFOP::kEqual, "==", "equal1", BLANK, OpType::kBool, OpMod::kNone, CONST,
111 {{ CONST, OpType::kThis, OpMod::kReference, }}},
112 { DEFOP::kEqual, "==", "equal2", ANY, OpType::kBool, OpMod::kNone, BLANK,
113 {{ CONST, OpType::kThis, OpMod::kReference, },
114 { CONST, OpType::kThis, OpMod::kReference, }}},
115 { DEFOP::kMinus, "-", "minus", BLANK, OpType::kThis, OpMod::kNone, CONST,
116 { } },
117 { DEFOP::kMove, "=", "move", BLANK, OpType::kThis, OpMod::kReference, BLANK,
118 {{ BLANK, OpType::kThis, OpMod::kMove, }}},
119 { DEFOP::kMultiply, "*", "multiply", BLANK, OpType::kThis, OpMod::kNone, CONST,
120 {{ BLANK, OpType::kScalar, OpMod::kNone, }}},
Cary Clark61313f32018-10-08 14:57:48 -0400121 { DEFOP::kMultiply, "*", "multiply1", BLANK, OpType::kThis, OpMod::kNone, CONST,
122 {{ CONST, OpType::kThis, OpMod::kReference, }}},
123 { DEFOP::kMultiply, "*", "multiply2", BLANK, OpType::kThis, OpMod::kNone, BLANK,
Cary Clarka560c472017-11-27 10:44:06 -0500124 {{ CONST, OpType::kThis, OpMod::kReference, },
125 { CONST, OpType::kThis, OpMod::kReference, }}},
126 { DEFOP::kMultiplyBy, "*=", "multiplyby", BLANK, OpType::kThis, OpMod::kReference, BLANK,
127 {{ BLANK, OpType::kScalar, OpMod::kNone, }}},
128 { DEFOP::kNew, "new", "new", BLANK, OpType::kVoid, OpMod::kPointer, BLANK,
129 {{ BLANK, OpType::kSizeT, OpMod::kNone, }}},
130 { DEFOP::kNotEqual, "!=", "notequal", BLANK, OpType::kBool, OpMod::kNone, BLANK,
131 {{ CONST, OpType::kThis, OpMod::kReference, },
132 { CONST, OpType::kThis, OpMod::kReference, }}},
133 { DEFOP::kNotEqual, "!=", "notequal1", BLANK, OpType::kBool, OpMod::kNone, CONST,
134 {{ CONST, OpType::kThis, OpMod::kReference, }}},
135 { DEFOP::kNotEqual, "!=", "notequal2", ANY, OpType::kBool, OpMod::kNone, BLANK,
136 {{ CONST, OpType::kThis, OpMod::kReference, },
137 { CONST, OpType::kThis, OpMod::kReference, }}},
138 { DEFOP::kSubtract, "-", "subtract", BLANK, OpType::kThis, OpMod::kNone, BLANK,
139 {{ CONST, OpType::kThis, OpMod::kReference, },
140 { CONST, OpType::kThis, OpMod::kReference, }}},
141 { DEFOP::kSubtractFrom, "-=", "subtractfrom", BLANK, OpType::kVoid, OpMod::kNone, BLANK,
142 {{ CONST, OpType::kThis, OpMod::kReference, }}},
143};
144
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400145OpType lookup_type(string typeWord, string name) {
Cary Clarka560c472017-11-27 10:44:06 -0500146 if (typeWord == name || (typeWord == "SkIVector" && name == "SkIPoint")
147 || (typeWord == "SkVector" && name == "SkPoint")) {
148 return OpType::kThis;
149 }
Cary Clark61313f32018-10-08 14:57:48 -0400150 if ("float" == typeWord || "double" == typeWord) {
151 return OpType::kScalar;
152 }
153 const char* keyWords[] = { "void", "bool", "char", "int", "SkScalar", "size_t" };
Cary Clarka560c472017-11-27 10:44:06 -0500154 for (unsigned i = 0; i < SK_ARRAY_COUNT(keyWords); ++i) {
155 if (typeWord == keyWords[i]) {
156 return (OpType) (i + 1);
157 }
158 }
159 return OpType::kNone;
160}
161
162OpMod lookup_mod(TextParser& iParser) {
163 OpMod mod = OpMod::kNone;
164 if ('&' == iParser.peek()) {
165 mod = OpMod::kReference;
166 iParser.next();
167 if ('&' == iParser.peek()) {
168 mod = OpMod::kMove;
169 iParser.next();
170 }
171 } else if ('*' == iParser.peek()) {
172 mod = OpMod::kPointer;
173 iParser.next();
174 }
175 iParser.skipWhiteSpace();
176 return mod;
177}
178
179bool Definition::parseOperator(size_t doubleColons, string& result) {
180 const char operatorStr[] = "operator";
181 size_t opPos = fName.find(operatorStr, doubleColons);
182 if (string::npos == opPos) {
183 return false;
184 }
185 string className(fName, 0, doubleColons - 2);
186 TextParser iParser(fFileName, fStart, fContentStart, fLineCount);
187 SkAssertResult(iParser.skipWord("#Method"));
Cary Clarka560c472017-11-27 10:44:06 -0500188 iParser.skipWhiteSpace();
189 bool isStatic = iParser.skipExact("static");
190 iParser.skipWhiteSpace();
Cary Clarka560c472017-11-27 10:44:06 -0500191 bool returnsConst = iParser.skipExact("const");
192 if (returnsConst) {
193 SkASSERT(0); // incomplete
194 }
195 SkASSERT(isStatic == false || returnsConst == false);
196 iParser.skipWhiteSpace();
197 const char* returnTypeStart = iParser.fChar;
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400198 iParser.skipToNonName();
Cary Clarka560c472017-11-27 10:44:06 -0500199 SkASSERT(iParser.fChar > returnTypeStart);
200 string returnType(returnTypeStart, iParser.fChar - returnTypeStart);
201 OpType returnOpType = lookup_type(returnType, className);
202 iParser.skipWhiteSpace();
203 OpMod returnMod = lookup_mod(iParser);
204 SkAssertResult(iParser.skipExact("operator"));
205 iParser.skipWhiteSpace();
206 fMethodType = Definition::MethodType::kOperator;
Cary Clark186d08f2018-04-03 08:43:27 -0400207 TextParserSave save(&iParser);
Cary Clarka560c472017-11-27 10:44:06 -0500208 for (auto parser : opData) {
209 save.restore();
210 if (!iParser.skipExact(parser.fSymbol)) {
211 continue;
212 }
213 iParser.skipWhiteSpace();
214 if ('(' != iParser.peek()) {
215 continue;
216 }
217 if (parser.fFriend != ANY && (parser.fFriend == STATIC) != isStatic) {
218 continue;
219 }
220 if (parser.fReturnType != OpType::kAny && parser.fReturnType != returnOpType) {
221 continue;
222 }
223 if (parser.fReturnMod != OpMod::kAny && parser.fReturnMod != returnMod) {
224 continue;
225 }
226 iParser.next(); // skip '('
227 iParser.skipWhiteSpace();
228 int parserCount = (parser.fParams[0].fType != OpType::kNone) +
229 (parser.fParams[1].fType != OpType::kNone);
230 bool countsMatch = true;
231 for (int pIndex = 0; pIndex < 2; ++pIndex) {
232 if (')' == iParser.peek()) {
233 countsMatch = pIndex == parserCount;
234 break;
235 }
236 if (',' == iParser.peek()) {
237 iParser.next();
238 iParser.skipWhiteSpace();
239 }
240 bool paramConst = iParser.skipExact("const");
241 if (parser.fParams[pIndex].fConst != ANY &&
242 paramConst != (parser.fParams[pIndex].fConst == CONST)) {
243 countsMatch = false;
244 break;
245 }
246 iParser.skipWhiteSpace();
247 const char* paramStart = iParser.fChar;
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400248 iParser.skipToNonName();
Cary Clarka560c472017-11-27 10:44:06 -0500249 SkASSERT(iParser.fChar > paramStart);
250 string paramType(paramStart, iParser.fChar - paramStart);
251 OpType paramOpType = lookup_type(paramType, className);
252 if (parser.fParams[pIndex].fType != OpType::kAny &&
253 parser.fParams[pIndex].fType != paramOpType) {
254 countsMatch = false;
255 break;
256 }
257 iParser.skipWhiteSpace();
258 OpMod paramMod = lookup_mod(iParser);
259 if (parser.fParams[pIndex].fMod != OpMod::kAny &&
260 parser.fParams[pIndex].fMod != paramMod) {
261 countsMatch = false;
262 break;
263 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400264 iParser.skipToNonName();
Cary Clarka560c472017-11-27 10:44:06 -0500265 if ('[' == iParser.peek()) {
266 paramMod = OpMod::kArray;
267 SkAssertResult(iParser.skipExact("[]"));
268 }
269 iParser.skipWhiteSpace();
270 }
271 if (!countsMatch) {
272 continue;
273 }
274 if (')' != iParser.peek()) {
275 continue;
276 }
277 iParser.next();
Cary Clarkcb6bef02018-11-29 12:05:25 -0500278 bool constMethod = iParser.skipExact(" const");
Cary Clarka560c472017-11-27 10:44:06 -0500279 if (parser.fConstMethod != ANY && (parser.fConstMethod == CONST) != constMethod) {
280 continue;
281 }
282 result += parser.fName;
283 result += "_operator";
284 fOperator = parser.fOperator;
285 fOperatorConst = constMethod;
286 return true;
287 }
288 SkASSERT(0); // incomplete
289 return false;
Cary Clarka560c472017-11-27 10:44:06 -0500290}
291
292#undef CONST
293#undef FRIEND
294#undef BLANK
295#undef DEFOP
296
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400297bool Definition::boilerplateIfDef() {
Cary Clarka560c472017-11-27 10:44:06 -0500298 const Definition& label = fTokens.front();
299 if (Type::kWord != label.fType) {
300 return false;
301 }
302 fName = string(label.fContentStart, label.fContentEnd - label.fContentStart);
303 return true;
304}
305
Cary Clarka560c472017-11-27 10:44:06 -0500306
307// fixme: this will need to be more complicated to handle all of Skia
308// for now, just handle paint -- maybe fiddle will loosen naming restrictions
309void Definition::setCanonicalFiddle() {
310 fMethodType = Definition::MethodType::kNone;
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400311 size_t doubleColons = fName.rfind("::");
Cary Clarka560c472017-11-27 10:44:06 -0500312 SkASSERT(string::npos != doubleColons);
313 string base = fName.substr(0, doubleColons);
314 string result = base + "_";
315 doubleColons += 2;
316 if (string::npos != fName.find('~', doubleColons)) {
317 fMethodType = Definition::MethodType::kDestructor;
318 result += "destructor";
319 } else if (!this->parseOperator(doubleColons, result)) {
320 bool isMove = string::npos != fName.find("&&", doubleColons);
321 size_t parens = fName.find("()", doubleColons);
322 if (string::npos != parens) {
323 string methodName = fName.substr(doubleColons, parens - doubleColons);
324 do {
325 size_t nextDouble = methodName.find("::");
326 if (string::npos == nextDouble) {
327 break;
328 }
329 base = methodName.substr(0, nextDouble);
330 result += base + '_';
331 methodName = methodName.substr(nextDouble + 2);
332 doubleColons += nextDouble + 2;
333 } while (true);
334 if (base == methodName) {
335 fMethodType = Definition::MethodType::kConstructor;
336 result += "empty_constructor";
337 } else {
338 result += fName.substr(doubleColons, fName.length() - doubleColons - 2);
339 }
340 } else {
341 size_t openParen = fName.find('(', doubleColons);
342 if (string::npos == openParen) {
343 result += fName.substr(doubleColons);
Cary Clark682c58d2018-05-16 07:07:07 -0400344 // see if it is a constructor -- if second to last delimited name equals last
345 size_t nextColons = fName.find("::", doubleColons);
346 if (string::npos != nextColons) {
347 nextColons += 2;
348 if (!strncmp(&fName[doubleColons], &fName[nextColons],
349 nextColons - doubleColons - 2)) {
350 fMethodType = Definition::MethodType::kConstructor;
351 }
352 }
Cary Clarka560c472017-11-27 10:44:06 -0500353 } else {
354 size_t comma = fName.find(',', doubleColons);
355 if (string::npos == comma) {
356 result += isMove ? "move_" : "copy_";
357 }
358 fMethodType = Definition::MethodType::kConstructor;
359 // name them by their param types,
360 // e.g. SkCanvas__int_int_const_SkSurfaceProps_star
361 // TODO: move forward until parens are balanced and terminator =,)
362 TextParser params("", &fName[openParen] + 1, &*fName.end(), 0);
363 bool underline = false;
364 while (!params.eof()) {
365// SkDEBUGCODE(const char* end = params.anyOf("(),=")); // unused for now
366// SkASSERT(end[0] != '('); // fixme: put off handling nested parentheseses
367 if (params.startsWith("const") || params.startsWith("int")
368 || params.startsWith("Sk")) {
369 const char* wordStart = params.fChar;
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400370 params.skipToNonName();
Cary Clarka560c472017-11-27 10:44:06 -0500371 if (underline) {
372 result += '_';
373 } else {
374 underline = true;
375 }
376 result += string(wordStart, params.fChar - wordStart);
377 } else {
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400378 params.skipToNonName();
Cary Clarka560c472017-11-27 10:44:06 -0500379 }
380 if (!params.eof() && '*' == params.peek()) {
381 if (underline) {
382 result += '_';
383 } else {
384 underline = true;
385 }
386 result += "star";
387 params.next();
388 params.skipSpace();
389 }
390 params.skipToAlpha();
391 }
392 }
393 }
394 }
395 fFiddle = Definition::NormalizedName(result);
396}
397
Cary Clarka560c472017-11-27 10:44:06 -0500398static void space_pad(string* str) {
399 size_t len = str->length();
400 if (len == 0) {
401 return;
402 }
403 char last = (*str)[len - 1];
404 if ('~' == last || ' ' >= last) {
405 return;
406 }
407 *str += ' ';
408}
409
410//start here;
411// see if it possible to abstract this a little bit so it can
412// additionally be used to find params and return in method prototype that
413// does not have corresponding doxygen comments
414bool Definition::checkMethod() const {
415 SkASSERT(MarkType::kMethod == fMarkType);
416 // if method returns a value, look for a return child
417 // for each parameter, look for a corresponding child
418 const char* end = fContentStart;
419 while (end > fStart && ' ' >= end[-1]) {
420 --end;
421 }
422 TextParser methodParser(fFileName, fStart, end, fLineCount);
423 methodParser.skipWhiteSpace();
424 SkASSERT(methodParser.startsWith("#Method"));
425 methodParser.skipName("#Method");
426 methodParser.skipSpace();
427 string name = this->methodName();
428 if (MethodType::kNone == fMethodType && name.length() > 2 &&
429 "()" == name.substr(name.length() - 2)) {
430 name = name.substr(0, name.length() - 2);
431 }
432 bool expectReturn = this->methodHasReturn(name, &methodParser);
433 bool foundReturn = false;
Cary Clarka64e4ee2018-10-18 08:30:34 -0400434 bool foundPopulate = false;
Cary Clarka560c472017-11-27 10:44:06 -0500435 for (auto& child : fChildren) {
Cary Clarka64e4ee2018-10-18 08:30:34 -0400436 foundPopulate |= MarkType::kPopulate == child->fMarkType;
Cary Clarka560c472017-11-27 10:44:06 -0500437 if (MarkType::kReturn != child->fMarkType) {
438 if (MarkType::kParam == child->fMarkType) {
439 child->fVisited = false;
440 }
441 continue;
442 }
443 if (!expectReturn) {
444 return methodParser.reportError<bool>("no #Return expected");
445 }
446 if (foundReturn) {
447 return methodParser.reportError<bool>("multiple #Return markers");
448 }
449 foundReturn = true;
450 }
Cary Clarkabaffd82018-11-15 08:25:12 -0500451 if (expectReturn && !foundReturn && !foundPopulate) {
Cary Clarka560c472017-11-27 10:44:06 -0500452 return methodParser.reportError<bool>("missing #Return marker");
453 }
454 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
455 if (!paren) {
456 return methodParser.reportError<bool>("missing #Method function definition");
457 }
458 const char* nextEnd = paren;
459 do {
460 string paramName;
461 methodParser.fChar = nextEnd + 1;
462 methodParser.skipSpace();
463 if (!this->nextMethodParam(&methodParser, &nextEnd, &paramName)) {
464 continue;
465 }
466 bool foundParam = false;
467 for (auto& child : fChildren) {
468 if (MarkType::kParam != child->fMarkType) {
469 continue;
470 }
471 if (paramName != child->fName) {
472 continue;
473 }
474 if (child->fVisited) {
475 return methodParser.reportError<bool>("multiple #Method param with same name");
476 }
477 child->fVisited = true;
478 if (foundParam) {
479 TextParser paramError(child);
480 return methodParser.reportError<bool>("multiple #Param with same name");
481 }
482 foundParam = true;
483
484 }
Cary Clarkabaffd82018-11-15 08:25:12 -0500485 if (!foundParam && !foundPopulate) {
Cary Clarka560c472017-11-27 10:44:06 -0500486 return methodParser.reportError<bool>("no #Param found");
487 }
488 if (')' == nextEnd[0]) {
489 break;
490 }
491 } while (')' != nextEnd[0]);
492 for (auto& child : fChildren) {
493 if (MarkType::kParam != child->fMarkType) {
494 continue;
495 }
496 if (!child->fVisited) {
497 TextParser paramError(child);
498 return paramError.reportError<bool>("#Param without param in #Method");
499 }
500 }
Cary Clark80247e52018-07-11 16:18:41 -0400501 // check after end of #Line and before next child for description
502 const char* descStart = fContentStart;
503 const char* descEnd = nullptr;
Cary Clarkab5c9af2018-07-12 16:24:53 -0400504 const Definition* defEnd = nullptr;
505 const Definition* priorDef = nullptr;
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400506 bool incomplete = false;
Cary Clark80247e52018-07-11 16:18:41 -0400507 for (auto& child : fChildren) {
508 if (MarkType::kAnchor == child->fMarkType) {
509 continue;
510 }
511 if (MarkType::kCode == child->fMarkType) {
Cary Clarkab5c9af2018-07-12 16:24:53 -0400512 priorDef = child;
Cary Clark80247e52018-07-11 16:18:41 -0400513 continue;
514 }
Cary Clark80247e52018-07-11 16:18:41 -0400515 if (MarkType::kFormula == child->fMarkType) {
516 continue;
517 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400518 if (MarkType::kLine == child->fMarkType) {
519 SkASSERT(child->fChildren.size() > 0);
520 TextParser childDesc(child->fChildren[0]);
521 incomplete |= childDesc.startsWith("incomplete");
522 }
Cary Clark80247e52018-07-11 16:18:41 -0400523 if (MarkType::kList == child->fMarkType) {
Cary Clarkab5c9af2018-07-12 16:24:53 -0400524 priorDef = child;
Cary Clark80247e52018-07-11 16:18:41 -0400525 continue;
526 }
527 if (MarkType::kMarkChar == child->fMarkType) {
528 continue;
529 }
530 if (MarkType::kPhraseRef == child->fMarkType) {
531 continue;
532 }
Cary Clark80247e52018-07-11 16:18:41 -0400533 TextParser emptyCheck(fFileName, descStart, child->fStart, child->fLineCount);
534 if (!emptyCheck.eof() && emptyCheck.skipWhiteSpace()) {
535 descStart = emptyCheck.fChar;
536 emptyCheck.trimEnd();
Cary Clarkab5c9af2018-07-12 16:24:53 -0400537 defEnd = priorDef;
Cary Clark80247e52018-07-11 16:18:41 -0400538 descEnd = emptyCheck.fEnd;
539 break;
540 }
541 descStart = child->fTerminator;
Cary Clarkab5c9af2018-07-12 16:24:53 -0400542 priorDef = nullptr;
Cary Clark80247e52018-07-11 16:18:41 -0400543 }
544 if (!descEnd) {
Cary Clarka64e4ee2018-10-18 08:30:34 -0400545 return incomplete || foundPopulate ? true :
546 methodParser.reportError<bool>("missing description");
Cary Clark80247e52018-07-11 16:18:41 -0400547 }
548 TextParser description(fFileName, descStart, descEnd, fLineCount);
549 // expect first word capitalized and pluralized. expect a trailing period
550 SkASSERT(descStart < descEnd);
551 if (!isupper(descStart[0])) {
552 description.reportWarning("expected capital");
553 } else if ('.' != descEnd[-1]) {
Cary Clarkab5c9af2018-07-12 16:24:53 -0400554 if (!defEnd || defEnd->fTerminator != descEnd) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400555 if (!incomplete) {
556 description.reportWarning("expected period");
557 }
Cary Clarkab5c9af2018-07-12 16:24:53 -0400558 }
Cary Clark80247e52018-07-11 16:18:41 -0400559 } else {
560 if (!description.startsWith("For use by Android")) {
561 description.skipToSpace();
562 if (',' == description.fChar[-1]) {
563 --description.fChar;
564 }
565 if ('s' != description.fChar[-1]) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400566 if (!incomplete) {
567 description.reportWarning("expected plural");
568 }
Cary Clark80247e52018-07-11 16:18:41 -0400569 }
570 }
571 }
Cary Clarka560c472017-11-27 10:44:06 -0500572 return true;
573}
574
575bool Definition::crossCheck2(const Definition& includeToken) const {
576 TextParser parser(fFileName, fStart, fContentStart, fLineCount);
577 parser.skipExact("#");
578 bool isMethod = parser.skipName("Method");
579 const char* contentEnd;
580 if (isMethod) {
581 contentEnd = fContentStart;
582 } else if (parser.skipName("DefinedBy")) {
583 contentEnd = fContentEnd;
584 while (parser.fChar < contentEnd && ' ' >= contentEnd[-1]) {
585 --contentEnd;
586 }
587 if (parser.fChar < contentEnd - 1 && ')' == contentEnd[-1] && '(' == contentEnd[-2]) {
588 contentEnd -= 2;
589 }
590 } else {
591 return parser.reportError<bool>("unexpected crosscheck marktype");
592 }
593 return crossCheckInside(parser.fChar, contentEnd, includeToken);
594}
595
596bool Definition::crossCheck(const Definition& includeToken) const {
597 return crossCheckInside(fContentStart, fContentEnd, includeToken);
598}
599
Cary Clarkab5c9af2018-07-12 16:24:53 -0400600const char* Definition::methodEnd() const {
601 const char defaultTag[] = " = default";
602 size_t tagSize = sizeof(defaultTag) - 1;
603 const char* tokenEnd = fContentEnd - tagSize;
604 if (tokenEnd <= fContentStart || strncmp(tokenEnd, defaultTag, tagSize)) {
605 tokenEnd = fContentEnd;
606 }
607 return tokenEnd;
608}
609
Cary Clarkfd32e722018-11-16 14:36:02 -0500610bool Definition::SkipImplementationWords(TextParser& inc) {
Cary Clarka560c472017-11-27 10:44:06 -0500611 if (inc.startsWith("SK_API")) {
612 inc.skipWord("SK_API");
613 }
Cary Clark61313f32018-10-08 14:57:48 -0400614 if (inc.startsWith("inline")) {
615 inc.skipWord("inline");
616 }
Cary Clarka560c472017-11-27 10:44:06 -0500617 if (inc.startsWith("friend")) {
618 inc.skipWord("friend");
619 }
620 if (inc.startsWith("SK_API")) {
621 inc.skipWord("SK_API");
622 }
Cary Clarkfd32e722018-11-16 14:36:02 -0500623 return inc.skipExact("SkDEBUGCODE(");
624}
625
626bool Definition::crossCheckInside(const char* start, const char* end,
627 const Definition& includeToken) const {
628 TextParser def(fFileName, start, end, fLineCount);
629 const char* tokenEnd = includeToken.methodEnd();
630 TextParser inc("", includeToken.fContentStart, tokenEnd, 0);
631 if (inc.startsWith("static")) {
632 def.skipWhiteSpace();
633 if (!def.startsWith("static")) {
634 return false;
635 }
636 inc.skipWord("static");
637 def.skipWord("static");
638 }
639 (void) Definition::SkipImplementationWords(inc);
Cary Clarka560c472017-11-27 10:44:06 -0500640 do {
641 bool defEof;
642 bool incEof;
643 do {
644 defEof = def.eof() || !def.skipWhiteSpace();
645 incEof = inc.eof() || !inc.skipWhiteSpace();
646 if (!incEof && '/' == inc.peek() && (defEof || '/' != def.peek())) {
647 inc.next();
648 if ('*' == inc.peek()) {
649 inc.skipToEndBracket("*/");
650 inc.next();
651 } else if ('/' == inc.peek()) {
652 inc.skipToEndBracket('\n');
653 }
654 } else if (!incEof && '#' == inc.peek() && (defEof || '#' != def.peek())) {
655 inc.next();
656 if (inc.startsWith("if")) {
657 inc.skipToEndBracket("\n");
658 } else if (inc.startsWith("endif")) {
659 inc.skipToEndBracket("\n");
660 } else {
661 SkASSERT(0); // incomplete
662 return false;
663 }
664 } else {
665 break;
666 }
667 inc.next();
668 } while (true);
669 if (defEof || incEof) {
670 if (defEof == incEof || (!defEof && ';' == def.peek())) {
671 return true;
672 }
673 return false; // allow setting breakpoint on failure
674 }
675 char defCh;
676 do {
677 defCh = def.next();
Cary Clark61313f32018-10-08 14:57:48 -0400678 if (inc.skipExact("SK_WARN_UNUSED_RESULT")) {
679 inc.skipSpace();
680 }
Cary Clarka560c472017-11-27 10:44:06 -0500681 char incCh = inc.next();
682 if (' ' >= defCh && ' ' >= incCh) {
683 break;
684 }
685 if (defCh != incCh) {
686 if ('_' != defCh || ' ' != incCh || !fOperatorConst || !def.startsWith("const")) {
687 return false;
688 }
689 }
690 if (';' == defCh) {
691 return true;
692 }
693 } while (!def.eof() && !inc.eof());
694 } while (true);
695 return false;
696}
697
Cary Clark78de7512018-02-07 07:27:09 -0500698string Definition::formatFunction(Format format) const {
Cary Clarka560c472017-11-27 10:44:06 -0500699 const char* end = fContentStart;
700 while (end > fStart && ' ' >= end[-1]) {
701 --end;
702 }
703 TextParser methodParser(fFileName, fStart, end, fLineCount);
704 methodParser.skipWhiteSpace();
705 SkASSERT(methodParser.startsWith("#Method"));
706 methodParser.skipName("#Method");
707 methodParser.skipSpace();
708 const char* lastStart = methodParser.fChar;
709 const int limit = 100; // todo: allow this to be set by caller or in global or something
710 string name = this->methodName();
Cary Clarka560c472017-11-27 10:44:06 -0500711 const char* nameInParser = methodParser.strnstr(name.c_str(), methodParser.fEnd);
712 methodParser.skipTo(nameInParser);
713 const char* lastEnd = methodParser.fChar;
Cary Clark78de7512018-02-07 07:27:09 -0500714 if (Format::kOmitReturn == format) {
715 lastStart = lastEnd;
716 }
Cary Clarka560c472017-11-27 10:44:06 -0500717 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
718 size_t indent;
719 if (paren) {
720 indent = (size_t) (paren - lastStart) + 1;
721 } else {
722 indent = (size_t) (lastEnd - lastStart);
723 }
724 // trim indent so longest line doesn't exceed box width
Cary Clark186d08f2018-04-03 08:43:27 -0400725 TextParserSave savePlace(&methodParser);
Cary Clarka560c472017-11-27 10:44:06 -0500726 const char* saveStart = lastStart;
727 ptrdiff_t maxLine = 0;
728 do {
729 const char* nextStart = lastEnd;
730 const char* delimiter = methodParser.anyOf(",)");
731 const char* nextEnd = delimiter ? delimiter : methodParser.fEnd;
732 if (delimiter) {
733 while (nextStart < nextEnd && ' ' >= nextStart[0]) {
734 ++nextStart;
735 }
736 }
737 while (nextEnd > nextStart && ' ' >= nextEnd[-1]) {
738 --nextEnd;
739 }
740 if (delimiter) {
741 nextEnd += 1;
742 delimiter += 1;
743 }
744 if (lastEnd > lastStart) {
745 maxLine = SkTMax(maxLine, lastEnd - lastStart);
746 }
747 if (delimiter) {
748 methodParser.skipTo(delimiter);
749 }
750 lastStart = nextStart;
751 lastEnd = nextEnd;
752 } while (lastStart < lastEnd);
753 savePlace.restore();
754 lastStart = saveStart;
755 lastEnd = methodParser.fChar;
756 indent = SkTMin(indent, (size_t) (limit - maxLine));
Cary Clark682c58d2018-05-16 07:07:07 -0400757 // write string with trimmmed indent
Cary Clarka560c472017-11-27 10:44:06 -0500758 string methodStr;
759 int written = 0;
760 do {
761 const char* nextStart = lastEnd;
Cary Clark390495e2018-07-13 07:52:44 -0400762// SkASSERT(written < limit);
Cary Clarka560c472017-11-27 10:44:06 -0500763 const char* delimiter = methodParser.anyOf(",)");
764 const char* nextEnd = delimiter ? delimiter : methodParser.fEnd;
765 if (delimiter) {
766 while (nextStart < nextEnd && ' ' >= nextStart[0]) {
767 ++nextStart;
768 }
769 }
770 while (nextEnd > nextStart && ' ' >= nextEnd[-1]) {
771 --nextEnd;
772 }
773 if (delimiter) {
774 nextEnd += 1;
775 delimiter += 1;
776 }
777 if (lastEnd > lastStart) {
778 if (lastStart[0] != ' ') {
779 space_pad(&methodStr);
780 }
Cary Clark61313f32018-10-08 14:57:48 -0400781 string addon(lastStart, (size_t) (lastEnd - lastStart));
Cary Clarkcb6bef02018-11-29 12:05:25 -0500782 if (" const" == addon) {
Cary Clark61313f32018-10-08 14:57:48 -0400783 addon = "const";
784 }
785 methodStr += addon;
786 written += addon.length();
Cary Clarka560c472017-11-27 10:44:06 -0500787 }
788 if (delimiter) {
789 if (nextEnd - nextStart >= (ptrdiff_t) (limit - written)) {
790 written = indent;
Cary Clark78de7512018-02-07 07:27:09 -0500791 if (Format::kIncludeReturn == format) {
792 methodStr += '\n';
793 methodStr += string(indent, ' ');
794 }
Cary Clarka560c472017-11-27 10:44:06 -0500795 }
796 methodParser.skipTo(delimiter);
797 }
798 lastStart = nextStart;
799 lastEnd = nextEnd;
800 } while (lastStart < lastEnd);
801 return methodStr;
802}
803
804string Definition::fiddleName() const {
805 string result;
806 size_t start = 0;
807 string parent;
808 const Definition* parentDef = this;
809 while ((parentDef = parentDef->fParent)) {
810 if (MarkType::kClass == parentDef->fMarkType || MarkType::kStruct == parentDef->fMarkType) {
811 parent = parentDef->fFiddle;
812 break;
813 }
814 }
815 if (parent.length() && 0 == fFiddle.compare(0, parent.length(), parent)) {
816 start = parent.length();
817 while (start < fFiddle.length() && '_' == fFiddle[start]) {
818 ++start;
819 }
820 }
821 size_t end = fFiddle.find_first_of('(', start);
822 return fFiddle.substr(start, end - start);
823}
824
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400825string Definition::fileName() const {
826 size_t nameStart = fFileName.rfind(SkOSPath::SEPARATOR);
827 if (SkOSPath::SEPARATOR != '/') {
828 size_t altNameStart = fFileName.rfind('/');
829 nameStart = string::npos == nameStart ? altNameStart :
830 string::npos != altNameStart && altNameStart > nameStart ? altNameStart : nameStart;
831 }
832 SkASSERT(string::npos != nameStart);
833 string baseFile = fFileName.substr(nameStart + 1);
834 return baseFile;
835}
836
Cary Clark4855f782018-02-06 09:41:53 -0500837const Definition* Definition::findClone(string match) const {
838 for (auto child : fChildren) {
839 if (!child->fClone) {
840 continue;
841 }
842 if (match == child->fName) {
843 return child;
844 }
845 auto inner = child->findClone(match);
846 if (inner) {
847 return inner;
848 }
849 }
850 return nullptr;
851}
852
Cary Clarka560c472017-11-27 10:44:06 -0500853const Definition* Definition::hasChild(MarkType markType) const {
854 for (auto iter : fChildren) {
855 if (markType == iter->fMarkType) {
856 return iter;
857 }
858 }
859 return nullptr;
860}
861
Cary Clarka64e4ee2018-10-18 08:30:34 -0400862Definition* Definition::hasParam(string ref) {
Cary Clarka560c472017-11-27 10:44:06 -0500863 SkASSERT(MarkType::kMethod == fMarkType);
864 for (auto iter : fChildren) {
865 if (MarkType::kParam != iter->fMarkType) {
866 continue;
867 }
868 if (iter->fName == ref) {
869 return &*iter;
870 }
Cary Clarka64e4ee2018-10-18 08:30:34 -0400871 }
872 for (auto& iter : fTokens) {
873 if (MarkType::kComment != iter.fMarkType) {
874 continue;
875 }
876 TextParser parser(&iter);
877 if (!parser.skipExact("@param ")) {
878 continue;
879 }
Cary Clark09d80c02018-10-31 12:14:03 -0400880 if (parser.skipExact(ref.c_str()) && ' ' == parser.peek()) {
Cary Clarka64e4ee2018-10-18 08:30:34 -0400881 return &iter;
882 }
Cary Clarka560c472017-11-27 10:44:06 -0500883 }
884 return nullptr;
885}
886
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400887bool Definition::hasMatch(string name) const {
Cary Clarkac47b882018-01-11 10:35:44 -0500888 for (auto child : fChildren) {
889 if (name == child->fName) {
890 return true;
891 }
892 if (child->hasMatch(name)) {
893 return true;
894 }
895 }
896 return false;
897}
898
Cary Clarkab2621d2018-01-30 10:08:57 -0500899bool Definition::isStructOrClass() const {
900 if (MarkType::kStruct != fMarkType && MarkType::kClass != fMarkType) {
901 return false;
902 }
903 if (string::npos != fFileName.find("undocumented.bmh")) {
904 return false;
905 }
906 return true;
907}
908
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400909bool Definition::methodHasReturn(string name, TextParser* methodParser) const {
Cary Clarka560c472017-11-27 10:44:06 -0500910 if (methodParser->skipExact("static")) {
911 methodParser->skipWhiteSpace();
912 }
Cary Clark80247e52018-07-11 16:18:41 -0400913 if (methodParser->skipExact("virtual")) {
914 methodParser->skipWhiteSpace();
915 }
Cary Clarka560c472017-11-27 10:44:06 -0500916 const char* lastStart = methodParser->fChar;
917 const char* nameInParser = methodParser->strnstr(name.c_str(), methodParser->fEnd);
918 methodParser->skipTo(nameInParser);
919 const char* lastEnd = methodParser->fChar;
920 const char* returnEnd = lastEnd;
921 while (returnEnd > lastStart && ' ' == returnEnd[-1]) {
922 --returnEnd;
923 }
924 bool expectReturn = 4 != returnEnd - lastStart || strncmp("void", lastStart, 4);
925 if (MethodType::kNone != fMethodType && MethodType::kOperator != fMethodType && !expectReturn) {
926 return methodParser->reportError<bool>("unexpected void");
927 }
928 switch (fMethodType) {
929 case MethodType::kNone:
930 case MethodType::kOperator:
931 // either is fine
932 break;
933 case MethodType::kConstructor:
934 expectReturn = true;
935 break;
936 case MethodType::kDestructor:
937 expectReturn = false;
938 break;
939 }
940 return expectReturn;
941}
942
943string Definition::methodName() const {
944 string result;
945 size_t start = 0;
946 string parent;
947 const Definition* parentDef = this;
948 while ((parentDef = parentDef->fParent)) {
949 if (MarkType::kClass == parentDef->fMarkType || MarkType::kStruct == parentDef->fMarkType) {
950 parent = parentDef->fName;
951 break;
952 }
953 }
954 if (parent.length() && 0 == fName.compare(0, parent.length(), parent)) {
955 start = parent.length();
956 while (start < fName.length() && ':' == fName[start]) {
957 ++start;
958 }
959 }
960 if (fClone) {
961 int lastUnder = fName.rfind('_');
962 return fName.substr(start, (size_t) (lastUnder - start));
963 }
964 size_t end = fName.find_first_of('(', start);
965 if (string::npos == end) {
966 return fName.substr(start);
967 }
968 return fName.substr(start, end - start);
969}
970
971bool Definition::nextMethodParam(TextParser* methodParser, const char** nextEndPtr,
972 string* paramName) const {
973 int parenCount = 0;
Cary Clark186d08f2018-04-03 08:43:27 -0400974 TextParserSave saveState(methodParser);
Cary Clarka560c472017-11-27 10:44:06 -0500975 while (true) {
976 if (methodParser->eof()) {
977 return methodParser->reportError<bool>("#Method function missing close paren");
978 }
979 char ch = methodParser->peek();
Cary Clarkcb6bef02018-11-29 12:05:25 -0500980 if ('(' == ch || '{' == ch) {
Cary Clarka560c472017-11-27 10:44:06 -0500981 ++parenCount;
982 }
983 if (parenCount == 0 && (')' == ch || ',' == ch)) {
984 *nextEndPtr = methodParser->fChar;
985 break;
986 }
Cary Clarkcb6bef02018-11-29 12:05:25 -0500987 if (')' == ch || '}' == ch) {
Cary Clarka560c472017-11-27 10:44:06 -0500988 if (0 > --parenCount) {
989 return this->reportError<bool>("mismatched parentheses");
990 }
991 }
992 methodParser->next();
993 }
994 saveState.restore();
995 const char* nextEnd = *nextEndPtr;
996 const char* paramEnd = nextEnd;
997 const char* assign = methodParser->strnstr(" = ", paramEnd);
998 if (assign) {
999 paramEnd = assign;
1000 }
1001 const char* closeBracket = methodParser->strnstr("]", paramEnd);
1002 if (closeBracket) {
1003 const char* openBracket = methodParser->strnstr("[", paramEnd);
1004 if (openBracket && openBracket < closeBracket) {
1005 while (openBracket < --closeBracket && isdigit(closeBracket[0]))
1006 ;
1007 if (openBracket == closeBracket) {
1008 paramEnd = openBracket;
1009 }
1010 }
1011 }
1012 const char* function = methodParser->strnstr(")(", paramEnd);
1013 if (function) {
1014 paramEnd = function;
1015 }
1016 while (paramEnd > methodParser->fChar && ' ' == paramEnd[-1]) {
1017 --paramEnd;
1018 }
1019 const char* paramStart = paramEnd;
1020 while (paramStart > methodParser->fChar && isalnum(paramStart[-1])) {
1021 --paramStart;
1022 }
1023 if (paramStart > methodParser->fChar && paramStart >= paramEnd) {
1024 return methodParser->reportError<bool>("#Method missing param name");
1025 }
1026 *paramName = string(paramStart, paramEnd - paramStart);
1027 if (!paramName->length()) {
1028 if (')' != nextEnd[0]) {
1029 return methodParser->reportError<bool>("#Method malformed param");
1030 }
1031 return false;
1032 }
1033 return true;
1034}
1035
1036string Definition::NormalizedName(string name) {
1037 string normalizedName = name;
1038 std::replace(normalizedName.begin(), normalizedName.end(), '-', '_');
1039 do {
1040 size_t doubleColon = normalizedName.find("::", 0);
1041 if (string::npos == doubleColon) {
1042 break;
1043 }
1044 normalizedName = normalizedName.substr(0, doubleColon)
1045 + '_' + normalizedName.substr(doubleColon + 2);
1046 } while (true);
1047 return normalizedName;
1048}
1049
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001050static string unpreformat(string orig) {
Cary Clark78de7512018-02-07 07:27:09 -05001051 string result;
1052 int amp = 0;
1053 for (auto c : orig) {
1054 switch (amp) {
1055 case 0:
1056 if ('&' == c) {
1057 amp = 1;
1058 } else {
1059 amp = 0;
1060 result += c;
1061 }
1062 break;
1063 case 1:
1064 if ('l' == c) {
1065 amp = 2;
1066 } else if ('g' == c) {
1067 amp = 3;
1068 } else {
1069 amp = 0;
1070 result += "&";
1071 result += c;
1072 }
1073 break;
1074 case 2:
1075 if ('t' == c) {
1076 amp = 4;
1077 } else {
1078 amp = 0;
1079 result += "&l";
1080 result += c;
1081 }
1082 break;
1083 case 3:
1084 if ('t' == c) {
1085 amp = 5;
1086 } else {
1087 amp = 0;
1088 result += "&g";
1089 result += c;
1090 }
1091 break;
1092 case 4:
1093 if (';' == c) {
1094 result += '<';
1095 } else {
1096 result += "&lt";
1097 result += c;
1098 }
1099 amp = 0;
1100 break;
1101 case 5:
1102 if (';' == c) {
1103 result += '>';
1104 } else {
1105 result += "&gt";
1106 result += c;
1107 }
1108 amp = 0;
1109 break;
1110 }
1111 }
1112 return result;
1113}
1114
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001115bool Definition::paramsMatch(string matchFormatted, string name) const {
Cary Clark78de7512018-02-07 07:27:09 -05001116 string match = unpreformat(matchFormatted);
Cary Clarka560c472017-11-27 10:44:06 -05001117 TextParser def(fFileName, fStart, fContentStart, fLineCount);
1118 const char* dName = def.strnstr(name.c_str(), fContentStart);
1119 if (!dName) {
1120 return false;
1121 }
1122 def.skipTo(dName);
1123 TextParser m(fFileName, &match.front(), &match.back() + 1, fLineCount);
1124 const char* mName = m.strnstr(name.c_str(), m.fEnd);
1125 if (!mName) {
1126 return false;
1127 }
1128 m.skipTo(mName);
1129 while (!def.eof() && ')' != def.peek() && !m.eof() && ')' != m.peek()) {
1130 const char* ds = def.fChar;
1131 const char* ms = m.fChar;
1132 const char* de = def.anyOf(") \n");
1133 const char* me = m.anyOf(") \n");
1134 def.skipTo(de);
1135 m.skipTo(me);
1136 if (def.fChar - ds != m.fChar - ms) {
1137 return false;
1138 }
1139 if (strncmp(ds, ms, (int) (def.fChar - ds))) {
1140 return false;
1141 }
1142 def.skipWhiteSpace();
1143 m.skipWhiteSpace();
1144 }
1145 return !def.eof() && ')' == def.peek() && !m.eof() && ')' == m.peek();
1146}
1147
Cary Clark2be81cf2018-09-13 12:04:30 -04001148
1149void Definition::trimEnd() {
1150 while (fContentEnd > fContentStart && ' ' >= fContentEnd[-1]) {
1151 --fContentEnd;
1152 }
1153}
1154
Cary Clarka560c472017-11-27 10:44:06 -05001155void RootDefinition::clearVisited() {
1156 fVisited = false;
1157 for (auto& leaf : fLeaves) {
1158 leaf.second.fVisited = false;
1159 }
1160 for (auto& branch : fBranches) {
1161 branch.second->clearVisited();
1162 }
1163}
1164
Cary Clarkf5404bb2018-01-05 12:10:09 -05001165bool RootDefinition::dumpUnVisited() {
1166 bool success = true;
Cary Clarka560c472017-11-27 10:44:06 -05001167 for (auto& leaf : fLeaves) {
1168 if (!leaf.second.fVisited) {
Cary Clarka560c472017-11-27 10:44:06 -05001169 // FIXME: bugs requiring long tail fixes, suppressed here:
1170 // SkBitmap::validate() is wrapped in SkDEBUGCODE in .h and not parsed
1171 if ("SkBitmap::validate()" == leaf.first) {
1172 continue;
1173 }
Cary Clarka560c472017-11-27 10:44:06 -05001174 // FIXME: end of long tail bugs
1175 SkDebugf("defined in bmh but missing in include: %s\n", leaf.first.c_str());
Cary Clarkf5404bb2018-01-05 12:10:09 -05001176 success = false;
Cary Clarka560c472017-11-27 10:44:06 -05001177 }
1178 }
1179 for (auto& branch : fBranches) {
Cary Clarkf5404bb2018-01-05 12:10:09 -05001180 success &= branch.second->dumpUnVisited();
Cary Clarka560c472017-11-27 10:44:06 -05001181 }
Cary Clarkf5404bb2018-01-05 12:10:09 -05001182 return success;
Cary Clarka560c472017-11-27 10:44:06 -05001183}
1184
Cary Clark682c58d2018-05-16 07:07:07 -04001185Definition* RootDefinition::find(string ref, AllowParens allowParens) {
Cary Clarka560c472017-11-27 10:44:06 -05001186 const auto leafIter = fLeaves.find(ref);
1187 if (leafIter != fLeaves.end()) {
1188 return &leafIter->second;
1189 }
Cary Clarka8cdc172018-09-07 14:17:08 -04001190 if (AllowParens::kYes == allowParens) {
1191 size_t leftParen = ref.find('(');
1192 if (string::npos == leftParen
1193 || (leftParen + 1 < ref.length() && ')' != ref[leftParen + 1])) {
1194 string withParens = ref + "()";
1195 const auto parensIter = fLeaves.find(withParens);
1196 if (parensIter != fLeaves.end()) {
1197 return &parensIter->second;
1198 }
1199 }
1200 if (string::npos != leftParen) {
1201 string name = ref.substr(0, leftParen);
1202 size_t posInDefName = fName.find(name);
1203 if (string::npos != posInDefName && posInDefName > 2
1204 && "::" == fName.substr(posInDefName - 2, 2)) {
1205 string fullRef = fName + "::" + ref;
1206 const auto fullIter = fLeaves.find(fullRef);
1207 if (fullIter != fLeaves.end()) {
1208 return &fullIter->second;
1209 }
1210 }
Cary Clarka560c472017-11-27 10:44:06 -05001211 }
1212 }
1213 const auto branchIter = fBranches.find(ref);
1214 if (branchIter != fBranches.end()) {
Cary Clark682c58d2018-05-16 07:07:07 -04001215 RootDefinition* rootDef = branchIter->second;
Cary Clarka560c472017-11-27 10:44:06 -05001216 return rootDef;
1217 }
Cary Clark682c58d2018-05-16 07:07:07 -04001218 Definition* result = nullptr;
Cary Clarka560c472017-11-27 10:44:06 -05001219 for (const auto& branch : fBranches) {
Cary Clark682c58d2018-05-16 07:07:07 -04001220 RootDefinition* rootDef = branch.second;
Cary Clarka560c472017-11-27 10:44:06 -05001221 result = rootDef->find(ref, allowParens);
1222 if (result) {
1223 break;
1224 }
1225 }
1226 return result;
1227}