blob: 8055d698fbf794787ed6cbf884c49ee1e1e53178 [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"));
188 iParser.skipExact("SK_API");
189 iParser.skipWhiteSpace();
190 bool isStatic = iParser.skipExact("static");
191 iParser.skipWhiteSpace();
192 iParser.skipExact("SK_API");
193 iParser.skipWhiteSpace();
194 bool returnsConst = iParser.skipExact("const");
195 if (returnsConst) {
196 SkASSERT(0); // incomplete
197 }
198 SkASSERT(isStatic == false || returnsConst == false);
199 iParser.skipWhiteSpace();
200 const char* returnTypeStart = iParser.fChar;
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400201 iParser.skipToNonName();
Cary Clarka560c472017-11-27 10:44:06 -0500202 SkASSERT(iParser.fChar > returnTypeStart);
203 string returnType(returnTypeStart, iParser.fChar - returnTypeStart);
204 OpType returnOpType = lookup_type(returnType, className);
205 iParser.skipWhiteSpace();
206 OpMod returnMod = lookup_mod(iParser);
207 SkAssertResult(iParser.skipExact("operator"));
208 iParser.skipWhiteSpace();
209 fMethodType = Definition::MethodType::kOperator;
Cary Clark186d08f2018-04-03 08:43:27 -0400210 TextParserSave save(&iParser);
Cary Clarka560c472017-11-27 10:44:06 -0500211 for (auto parser : opData) {
212 save.restore();
213 if (!iParser.skipExact(parser.fSymbol)) {
214 continue;
215 }
216 iParser.skipWhiteSpace();
217 if ('(' != iParser.peek()) {
218 continue;
219 }
220 if (parser.fFriend != ANY && (parser.fFriend == STATIC) != isStatic) {
221 continue;
222 }
223 if (parser.fReturnType != OpType::kAny && parser.fReturnType != returnOpType) {
224 continue;
225 }
226 if (parser.fReturnMod != OpMod::kAny && parser.fReturnMod != returnMod) {
227 continue;
228 }
229 iParser.next(); // skip '('
230 iParser.skipWhiteSpace();
231 int parserCount = (parser.fParams[0].fType != OpType::kNone) +
232 (parser.fParams[1].fType != OpType::kNone);
233 bool countsMatch = true;
234 for (int pIndex = 0; pIndex < 2; ++pIndex) {
235 if (')' == iParser.peek()) {
236 countsMatch = pIndex == parserCount;
237 break;
238 }
239 if (',' == iParser.peek()) {
240 iParser.next();
241 iParser.skipWhiteSpace();
242 }
243 bool paramConst = iParser.skipExact("const");
244 if (parser.fParams[pIndex].fConst != ANY &&
245 paramConst != (parser.fParams[pIndex].fConst == CONST)) {
246 countsMatch = false;
247 break;
248 }
249 iParser.skipWhiteSpace();
250 const char* paramStart = iParser.fChar;
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400251 iParser.skipToNonName();
Cary Clarka560c472017-11-27 10:44:06 -0500252 SkASSERT(iParser.fChar > paramStart);
253 string paramType(paramStart, iParser.fChar - paramStart);
254 OpType paramOpType = lookup_type(paramType, className);
255 if (parser.fParams[pIndex].fType != OpType::kAny &&
256 parser.fParams[pIndex].fType != paramOpType) {
257 countsMatch = false;
258 break;
259 }
260 iParser.skipWhiteSpace();
261 OpMod paramMod = lookup_mod(iParser);
262 if (parser.fParams[pIndex].fMod != OpMod::kAny &&
263 parser.fParams[pIndex].fMod != paramMod) {
264 countsMatch = false;
265 break;
266 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400267 iParser.skipToNonName();
Cary Clarka560c472017-11-27 10:44:06 -0500268 if ('[' == iParser.peek()) {
269 paramMod = OpMod::kArray;
270 SkAssertResult(iParser.skipExact("[]"));
271 }
272 iParser.skipWhiteSpace();
273 }
274 if (!countsMatch) {
275 continue;
276 }
277 if (')' != iParser.peek()) {
278 continue;
279 }
280 iParser.next();
281 bool constMethod = iParser.skipExact("_const");
282 if (parser.fConstMethod != ANY && (parser.fConstMethod == CONST) != constMethod) {
283 continue;
284 }
285 result += parser.fName;
286 result += "_operator";
287 fOperator = parser.fOperator;
288 fOperatorConst = constMethod;
289 return true;
290 }
291 SkASSERT(0); // incomplete
292 return false;
293#if 0
294 if ('!' == fName[opPos]) {
295 SkASSERT('=' == fName[opPos + 1]);
296 result += "not_equal_operator";
297 } else if ('=' == fName[opPos]) {
298 if ('(' == fName[opPos + 1]) {
299 result += isMove ? "move_" : "copy_";
300 result += "assignment_operator";
301 } else {
302 SkASSERT('=' == fName[opPos + 1]);
303 result += "equal_operator";
304 }
305 } else if ('[' == fName[opPos]) {
306 result += "subscript_operator";
307 const char* end = fContentStart;
308 while (end > fStart && ' ' >= end[-1]) {
309 --end;
310 }
311 string constCheck(fStart, end - fStart);
312 size_t constPos = constCheck.rfind("const");
313 if (constCheck.length() == constPos + 5) {
314 result += "_const";
315 }
316 } else if ('*' == fName[opPos]) {
317 result += "multiply_operator";
318 } else if ('-' == fName[opPos]) {
319 result += "subtract_operator";
320 } else if ('+' == fName[opPos]) {
321 result += "add_operator";
322 } else {
323 SkASSERT(0); // todo: incomplete
324 }
325#endif
326 return true;
327}
328
329#undef CONST
330#undef FRIEND
331#undef BLANK
332#undef DEFOP
333
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400334bool Definition::boilerplateIfDef() {
Cary Clarka560c472017-11-27 10:44:06 -0500335 const Definition& label = fTokens.front();
336 if (Type::kWord != label.fType) {
337 return false;
338 }
339 fName = string(label.fContentStart, label.fContentEnd - label.fContentStart);
340 return true;
341}
342
Cary Clarka560c472017-11-27 10:44:06 -0500343
344// fixme: this will need to be more complicated to handle all of Skia
345// for now, just handle paint -- maybe fiddle will loosen naming restrictions
346void Definition::setCanonicalFiddle() {
347 fMethodType = Definition::MethodType::kNone;
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400348 size_t doubleColons = fName.rfind("::");
Cary Clarka560c472017-11-27 10:44:06 -0500349 SkASSERT(string::npos != doubleColons);
350 string base = fName.substr(0, doubleColons);
351 string result = base + "_";
352 doubleColons += 2;
353 if (string::npos != fName.find('~', doubleColons)) {
354 fMethodType = Definition::MethodType::kDestructor;
355 result += "destructor";
356 } else if (!this->parseOperator(doubleColons, result)) {
357 bool isMove = string::npos != fName.find("&&", doubleColons);
358 size_t parens = fName.find("()", doubleColons);
359 if (string::npos != parens) {
360 string methodName = fName.substr(doubleColons, parens - doubleColons);
361 do {
362 size_t nextDouble = methodName.find("::");
363 if (string::npos == nextDouble) {
364 break;
365 }
366 base = methodName.substr(0, nextDouble);
367 result += base + '_';
368 methodName = methodName.substr(nextDouble + 2);
369 doubleColons += nextDouble + 2;
370 } while (true);
371 if (base == methodName) {
372 fMethodType = Definition::MethodType::kConstructor;
373 result += "empty_constructor";
374 } else {
375 result += fName.substr(doubleColons, fName.length() - doubleColons - 2);
376 }
377 } else {
378 size_t openParen = fName.find('(', doubleColons);
379 if (string::npos == openParen) {
380 result += fName.substr(doubleColons);
Cary Clark682c58d2018-05-16 07:07:07 -0400381 // see if it is a constructor -- if second to last delimited name equals last
382 size_t nextColons = fName.find("::", doubleColons);
383 if (string::npos != nextColons) {
384 nextColons += 2;
385 if (!strncmp(&fName[doubleColons], &fName[nextColons],
386 nextColons - doubleColons - 2)) {
387 fMethodType = Definition::MethodType::kConstructor;
388 }
389 }
Cary Clarka560c472017-11-27 10:44:06 -0500390 } else {
391 size_t comma = fName.find(',', doubleColons);
392 if (string::npos == comma) {
393 result += isMove ? "move_" : "copy_";
394 }
395 fMethodType = Definition::MethodType::kConstructor;
396 // name them by their param types,
397 // e.g. SkCanvas__int_int_const_SkSurfaceProps_star
398 // TODO: move forward until parens are balanced and terminator =,)
399 TextParser params("", &fName[openParen] + 1, &*fName.end(), 0);
400 bool underline = false;
401 while (!params.eof()) {
402// SkDEBUGCODE(const char* end = params.anyOf("(),=")); // unused for now
403// SkASSERT(end[0] != '('); // fixme: put off handling nested parentheseses
404 if (params.startsWith("const") || params.startsWith("int")
405 || params.startsWith("Sk")) {
406 const char* wordStart = params.fChar;
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400407 params.skipToNonName();
Cary Clarka560c472017-11-27 10:44:06 -0500408 if (underline) {
409 result += '_';
410 } else {
411 underline = true;
412 }
413 result += string(wordStart, params.fChar - wordStart);
414 } else {
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400415 params.skipToNonName();
Cary Clarka560c472017-11-27 10:44:06 -0500416 }
417 if (!params.eof() && '*' == params.peek()) {
418 if (underline) {
419 result += '_';
420 } else {
421 underline = true;
422 }
423 result += "star";
424 params.next();
425 params.skipSpace();
426 }
427 params.skipToAlpha();
428 }
429 }
430 }
431 }
432 fFiddle = Definition::NormalizedName(result);
433}
434
Cary Clarka560c472017-11-27 10:44:06 -0500435static void space_pad(string* str) {
436 size_t len = str->length();
437 if (len == 0) {
438 return;
439 }
440 char last = (*str)[len - 1];
441 if ('~' == last || ' ' >= last) {
442 return;
443 }
444 *str += ' ';
445}
446
447//start here;
448// see if it possible to abstract this a little bit so it can
449// additionally be used to find params and return in method prototype that
450// does not have corresponding doxygen comments
451bool Definition::checkMethod() const {
452 SkASSERT(MarkType::kMethod == fMarkType);
453 // if method returns a value, look for a return child
454 // for each parameter, look for a corresponding child
455 const char* end = fContentStart;
456 while (end > fStart && ' ' >= end[-1]) {
457 --end;
458 }
459 TextParser methodParser(fFileName, fStart, end, fLineCount);
460 methodParser.skipWhiteSpace();
461 SkASSERT(methodParser.startsWith("#Method"));
462 methodParser.skipName("#Method");
463 methodParser.skipSpace();
464 string name = this->methodName();
465 if (MethodType::kNone == fMethodType && name.length() > 2 &&
466 "()" == name.substr(name.length() - 2)) {
467 name = name.substr(0, name.length() - 2);
468 }
469 bool expectReturn = this->methodHasReturn(name, &methodParser);
470 bool foundReturn = false;
Cary Clarka64e4ee2018-10-18 08:30:34 -0400471 bool foundPopulate = false;
Cary Clarka560c472017-11-27 10:44:06 -0500472 for (auto& child : fChildren) {
Cary Clarka64e4ee2018-10-18 08:30:34 -0400473 foundPopulate |= MarkType::kPopulate == child->fMarkType;
Cary Clarka560c472017-11-27 10:44:06 -0500474 if (MarkType::kReturn != child->fMarkType) {
475 if (MarkType::kParam == child->fMarkType) {
476 child->fVisited = false;
477 }
478 continue;
479 }
480 if (!expectReturn) {
481 return methodParser.reportError<bool>("no #Return expected");
482 }
483 if (foundReturn) {
484 return methodParser.reportError<bool>("multiple #Return markers");
485 }
486 foundReturn = true;
487 }
Cary Clarkabaffd82018-11-15 08:25:12 -0500488 if (expectReturn && !foundReturn && !foundPopulate) {
Cary Clarka560c472017-11-27 10:44:06 -0500489 return methodParser.reportError<bool>("missing #Return marker");
490 }
491 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
492 if (!paren) {
493 return methodParser.reportError<bool>("missing #Method function definition");
494 }
495 const char* nextEnd = paren;
496 do {
497 string paramName;
498 methodParser.fChar = nextEnd + 1;
499 methodParser.skipSpace();
500 if (!this->nextMethodParam(&methodParser, &nextEnd, &paramName)) {
501 continue;
502 }
503 bool foundParam = false;
504 for (auto& child : fChildren) {
505 if (MarkType::kParam != child->fMarkType) {
506 continue;
507 }
508 if (paramName != child->fName) {
509 continue;
510 }
511 if (child->fVisited) {
512 return methodParser.reportError<bool>("multiple #Method param with same name");
513 }
514 child->fVisited = true;
515 if (foundParam) {
516 TextParser paramError(child);
517 return methodParser.reportError<bool>("multiple #Param with same name");
518 }
519 foundParam = true;
520
521 }
Cary Clarkabaffd82018-11-15 08:25:12 -0500522 if (!foundParam && !foundPopulate) {
Cary Clarka560c472017-11-27 10:44:06 -0500523 return methodParser.reportError<bool>("no #Param found");
524 }
525 if (')' == nextEnd[0]) {
526 break;
527 }
528 } while (')' != nextEnd[0]);
529 for (auto& child : fChildren) {
530 if (MarkType::kParam != child->fMarkType) {
531 continue;
532 }
533 if (!child->fVisited) {
534 TextParser paramError(child);
535 return paramError.reportError<bool>("#Param without param in #Method");
536 }
537 }
Cary Clark80247e52018-07-11 16:18:41 -0400538 // check after end of #Line and before next child for description
539 const char* descStart = fContentStart;
540 const char* descEnd = nullptr;
Cary Clarkab5c9af2018-07-12 16:24:53 -0400541 const Definition* defEnd = nullptr;
542 const Definition* priorDef = nullptr;
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400543 bool incomplete = false;
Cary Clark80247e52018-07-11 16:18:41 -0400544 for (auto& child : fChildren) {
545 if (MarkType::kAnchor == child->fMarkType) {
546 continue;
547 }
548 if (MarkType::kCode == child->fMarkType) {
Cary Clarkab5c9af2018-07-12 16:24:53 -0400549 priorDef = child;
Cary Clark80247e52018-07-11 16:18:41 -0400550 continue;
551 }
Cary Clark80247e52018-07-11 16:18:41 -0400552 if (MarkType::kFormula == child->fMarkType) {
553 continue;
554 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400555 if (MarkType::kLine == child->fMarkType) {
556 SkASSERT(child->fChildren.size() > 0);
557 TextParser childDesc(child->fChildren[0]);
558 incomplete |= childDesc.startsWith("incomplete");
559 }
Cary Clark80247e52018-07-11 16:18:41 -0400560 if (MarkType::kList == child->fMarkType) {
Cary Clarkab5c9af2018-07-12 16:24:53 -0400561 priorDef = child;
Cary Clark80247e52018-07-11 16:18:41 -0400562 continue;
563 }
564 if (MarkType::kMarkChar == child->fMarkType) {
565 continue;
566 }
567 if (MarkType::kPhraseRef == child->fMarkType) {
568 continue;
569 }
Cary Clark80247e52018-07-11 16:18:41 -0400570 TextParser emptyCheck(fFileName, descStart, child->fStart, child->fLineCount);
571 if (!emptyCheck.eof() && emptyCheck.skipWhiteSpace()) {
572 descStart = emptyCheck.fChar;
573 emptyCheck.trimEnd();
Cary Clarkab5c9af2018-07-12 16:24:53 -0400574 defEnd = priorDef;
Cary Clark80247e52018-07-11 16:18:41 -0400575 descEnd = emptyCheck.fEnd;
576 break;
577 }
578 descStart = child->fTerminator;
Cary Clarkab5c9af2018-07-12 16:24:53 -0400579 priorDef = nullptr;
Cary Clark80247e52018-07-11 16:18:41 -0400580 }
581 if (!descEnd) {
Cary Clarka64e4ee2018-10-18 08:30:34 -0400582 return incomplete || foundPopulate ? true :
583 methodParser.reportError<bool>("missing description");
Cary Clark80247e52018-07-11 16:18:41 -0400584 }
585 TextParser description(fFileName, descStart, descEnd, fLineCount);
586 // expect first word capitalized and pluralized. expect a trailing period
587 SkASSERT(descStart < descEnd);
588 if (!isupper(descStart[0])) {
589 description.reportWarning("expected capital");
590 } else if ('.' != descEnd[-1]) {
Cary Clarkab5c9af2018-07-12 16:24:53 -0400591 if (!defEnd || defEnd->fTerminator != descEnd) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400592 if (!incomplete) {
593 description.reportWarning("expected period");
594 }
Cary Clarkab5c9af2018-07-12 16:24:53 -0400595 }
Cary Clark80247e52018-07-11 16:18:41 -0400596 } else {
597 if (!description.startsWith("For use by Android")) {
598 description.skipToSpace();
599 if (',' == description.fChar[-1]) {
600 --description.fChar;
601 }
602 if ('s' != description.fChar[-1]) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400603 if (!incomplete) {
604 description.reportWarning("expected plural");
605 }
Cary Clark80247e52018-07-11 16:18:41 -0400606 }
607 }
608 }
Cary Clarka560c472017-11-27 10:44:06 -0500609 return true;
610}
611
612bool Definition::crossCheck2(const Definition& includeToken) const {
613 TextParser parser(fFileName, fStart, fContentStart, fLineCount);
614 parser.skipExact("#");
615 bool isMethod = parser.skipName("Method");
616 const char* contentEnd;
617 if (isMethod) {
618 contentEnd = fContentStart;
619 } else if (parser.skipName("DefinedBy")) {
620 contentEnd = fContentEnd;
621 while (parser.fChar < contentEnd && ' ' >= contentEnd[-1]) {
622 --contentEnd;
623 }
624 if (parser.fChar < contentEnd - 1 && ')' == contentEnd[-1] && '(' == contentEnd[-2]) {
625 contentEnd -= 2;
626 }
627 } else {
628 return parser.reportError<bool>("unexpected crosscheck marktype");
629 }
630 return crossCheckInside(parser.fChar, contentEnd, includeToken);
631}
632
633bool Definition::crossCheck(const Definition& includeToken) const {
634 return crossCheckInside(fContentStart, fContentEnd, includeToken);
635}
636
Cary Clarkab5c9af2018-07-12 16:24:53 -0400637const char* Definition::methodEnd() const {
638 const char defaultTag[] = " = default";
639 size_t tagSize = sizeof(defaultTag) - 1;
640 const char* tokenEnd = fContentEnd - tagSize;
641 if (tokenEnd <= fContentStart || strncmp(tokenEnd, defaultTag, tagSize)) {
642 tokenEnd = fContentEnd;
643 }
644 return tokenEnd;
645}
646
Cary Clarka560c472017-11-27 10:44:06 -0500647bool Definition::crossCheckInside(const char* start, const char* end,
648 const Definition& includeToken) const {
649 TextParser def(fFileName, start, end, fLineCount);
Cary Clarkab5c9af2018-07-12 16:24:53 -0400650 const char* tokenEnd = includeToken.methodEnd();
651 TextParser inc("", includeToken.fContentStart, tokenEnd, 0);
Cary Clarka560c472017-11-27 10:44:06 -0500652 if (inc.startsWith("SK_API")) {
653 inc.skipWord("SK_API");
654 }
Cary Clark61313f32018-10-08 14:57:48 -0400655 if (inc.startsWith("inline")) {
656 inc.skipWord("inline");
657 }
Cary Clarka560c472017-11-27 10:44:06 -0500658 if (inc.startsWith("friend")) {
659 inc.skipWord("friend");
660 }
661 if (inc.startsWith("SK_API")) {
662 inc.skipWord("SK_API");
663 }
664 inc.skipExact("SkDEBUGCODE(");
665 do {
666 bool defEof;
667 bool incEof;
668 do {
669 defEof = def.eof() || !def.skipWhiteSpace();
670 incEof = inc.eof() || !inc.skipWhiteSpace();
671 if (!incEof && '/' == inc.peek() && (defEof || '/' != def.peek())) {
672 inc.next();
673 if ('*' == inc.peek()) {
674 inc.skipToEndBracket("*/");
675 inc.next();
676 } else if ('/' == inc.peek()) {
677 inc.skipToEndBracket('\n');
678 }
679 } else if (!incEof && '#' == inc.peek() && (defEof || '#' != def.peek())) {
680 inc.next();
681 if (inc.startsWith("if")) {
682 inc.skipToEndBracket("\n");
683 } else if (inc.startsWith("endif")) {
684 inc.skipToEndBracket("\n");
685 } else {
686 SkASSERT(0); // incomplete
687 return false;
688 }
689 } else {
690 break;
691 }
692 inc.next();
693 } while (true);
694 if (defEof || incEof) {
695 if (defEof == incEof || (!defEof && ';' == def.peek())) {
696 return true;
697 }
698 return false; // allow setting breakpoint on failure
699 }
700 char defCh;
701 do {
702 defCh = def.next();
Cary Clark61313f32018-10-08 14:57:48 -0400703 if (inc.skipExact("SK_WARN_UNUSED_RESULT")) {
704 inc.skipSpace();
705 }
Cary Clarka560c472017-11-27 10:44:06 -0500706 char incCh = inc.next();
707 if (' ' >= defCh && ' ' >= incCh) {
708 break;
709 }
710 if (defCh != incCh) {
711 if ('_' != defCh || ' ' != incCh || !fOperatorConst || !def.startsWith("const")) {
712 return false;
713 }
714 }
715 if (';' == defCh) {
716 return true;
717 }
718 } while (!def.eof() && !inc.eof());
719 } while (true);
720 return false;
721}
722
Cary Clark78de7512018-02-07 07:27:09 -0500723string Definition::formatFunction(Format format) const {
Cary Clarka560c472017-11-27 10:44:06 -0500724 const char* end = fContentStart;
725 while (end > fStart && ' ' >= end[-1]) {
726 --end;
727 }
728 TextParser methodParser(fFileName, fStart, end, fLineCount);
729 methodParser.skipWhiteSpace();
730 SkASSERT(methodParser.startsWith("#Method"));
731 methodParser.skipName("#Method");
732 methodParser.skipSpace();
733 const char* lastStart = methodParser.fChar;
734 const int limit = 100; // todo: allow this to be set by caller or in global or something
735 string name = this->methodName();
Cary Clarka560c472017-11-27 10:44:06 -0500736 const char* nameInParser = methodParser.strnstr(name.c_str(), methodParser.fEnd);
737 methodParser.skipTo(nameInParser);
738 const char* lastEnd = methodParser.fChar;
Cary Clark78de7512018-02-07 07:27:09 -0500739 if (Format::kOmitReturn == format) {
740 lastStart = lastEnd;
741 }
Cary Clarka560c472017-11-27 10:44:06 -0500742 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
743 size_t indent;
744 if (paren) {
745 indent = (size_t) (paren - lastStart) + 1;
746 } else {
747 indent = (size_t) (lastEnd - lastStart);
748 }
749 // trim indent so longest line doesn't exceed box width
Cary Clark186d08f2018-04-03 08:43:27 -0400750 TextParserSave savePlace(&methodParser);
Cary Clarka560c472017-11-27 10:44:06 -0500751 const char* saveStart = lastStart;
752 ptrdiff_t maxLine = 0;
753 do {
754 const char* nextStart = lastEnd;
755 const char* delimiter = methodParser.anyOf(",)");
756 const char* nextEnd = delimiter ? delimiter : methodParser.fEnd;
757 if (delimiter) {
758 while (nextStart < nextEnd && ' ' >= nextStart[0]) {
759 ++nextStart;
760 }
761 }
762 while (nextEnd > nextStart && ' ' >= nextEnd[-1]) {
763 --nextEnd;
764 }
765 if (delimiter) {
766 nextEnd += 1;
767 delimiter += 1;
768 }
769 if (lastEnd > lastStart) {
770 maxLine = SkTMax(maxLine, lastEnd - lastStart);
771 }
772 if (delimiter) {
773 methodParser.skipTo(delimiter);
774 }
775 lastStart = nextStart;
776 lastEnd = nextEnd;
777 } while (lastStart < lastEnd);
778 savePlace.restore();
779 lastStart = saveStart;
780 lastEnd = methodParser.fChar;
781 indent = SkTMin(indent, (size_t) (limit - maxLine));
Cary Clark682c58d2018-05-16 07:07:07 -0400782 // write string with trimmmed indent
Cary Clarka560c472017-11-27 10:44:06 -0500783 string methodStr;
784 int written = 0;
785 do {
786 const char* nextStart = lastEnd;
Cary Clark390495e2018-07-13 07:52:44 -0400787// SkASSERT(written < limit);
Cary Clarka560c472017-11-27 10:44:06 -0500788 const char* delimiter = methodParser.anyOf(",)");
789 const char* nextEnd = delimiter ? delimiter : methodParser.fEnd;
790 if (delimiter) {
791 while (nextStart < nextEnd && ' ' >= nextStart[0]) {
792 ++nextStart;
793 }
794 }
795 while (nextEnd > nextStart && ' ' >= nextEnd[-1]) {
796 --nextEnd;
797 }
798 if (delimiter) {
799 nextEnd += 1;
800 delimiter += 1;
801 }
802 if (lastEnd > lastStart) {
803 if (lastStart[0] != ' ') {
804 space_pad(&methodStr);
805 }
Cary Clark61313f32018-10-08 14:57:48 -0400806 string addon(lastStart, (size_t) (lastEnd - lastStart));
807 if ("_const" == addon) {
808 addon = "const";
809 }
810 methodStr += addon;
811 written += addon.length();
Cary Clarka560c472017-11-27 10:44:06 -0500812 }
813 if (delimiter) {
814 if (nextEnd - nextStart >= (ptrdiff_t) (limit - written)) {
815 written = indent;
Cary Clark78de7512018-02-07 07:27:09 -0500816 if (Format::kIncludeReturn == format) {
817 methodStr += '\n';
818 methodStr += string(indent, ' ');
819 }
Cary Clarka560c472017-11-27 10:44:06 -0500820 }
821 methodParser.skipTo(delimiter);
822 }
823 lastStart = nextStart;
824 lastEnd = nextEnd;
825 } while (lastStart < lastEnd);
826 return methodStr;
827}
828
829string Definition::fiddleName() const {
830 string result;
831 size_t start = 0;
832 string parent;
833 const Definition* parentDef = this;
834 while ((parentDef = parentDef->fParent)) {
835 if (MarkType::kClass == parentDef->fMarkType || MarkType::kStruct == parentDef->fMarkType) {
836 parent = parentDef->fFiddle;
837 break;
838 }
839 }
840 if (parent.length() && 0 == fFiddle.compare(0, parent.length(), parent)) {
841 start = parent.length();
842 while (start < fFiddle.length() && '_' == fFiddle[start]) {
843 ++start;
844 }
845 }
846 size_t end = fFiddle.find_first_of('(', start);
847 return fFiddle.substr(start, end - start);
848}
849
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400850string Definition::fileName() const {
851 size_t nameStart = fFileName.rfind(SkOSPath::SEPARATOR);
852 if (SkOSPath::SEPARATOR != '/') {
853 size_t altNameStart = fFileName.rfind('/');
854 nameStart = string::npos == nameStart ? altNameStart :
855 string::npos != altNameStart && altNameStart > nameStart ? altNameStart : nameStart;
856 }
857 SkASSERT(string::npos != nameStart);
858 string baseFile = fFileName.substr(nameStart + 1);
859 return baseFile;
860}
861
Cary Clark4855f782018-02-06 09:41:53 -0500862const Definition* Definition::findClone(string match) const {
863 for (auto child : fChildren) {
864 if (!child->fClone) {
865 continue;
866 }
867 if (match == child->fName) {
868 return child;
869 }
870 auto inner = child->findClone(match);
871 if (inner) {
872 return inner;
873 }
874 }
875 return nullptr;
876}
877
Cary Clarka560c472017-11-27 10:44:06 -0500878const Definition* Definition::hasChild(MarkType markType) const {
879 for (auto iter : fChildren) {
880 if (markType == iter->fMarkType) {
881 return iter;
882 }
883 }
884 return nullptr;
885}
886
Cary Clarka64e4ee2018-10-18 08:30:34 -0400887Definition* Definition::hasParam(string ref) {
Cary Clarka560c472017-11-27 10:44:06 -0500888 SkASSERT(MarkType::kMethod == fMarkType);
889 for (auto iter : fChildren) {
890 if (MarkType::kParam != iter->fMarkType) {
891 continue;
892 }
893 if (iter->fName == ref) {
894 return &*iter;
895 }
Cary Clarka64e4ee2018-10-18 08:30:34 -0400896 }
897 for (auto& iter : fTokens) {
898 if (MarkType::kComment != iter.fMarkType) {
899 continue;
900 }
901 TextParser parser(&iter);
902 if (!parser.skipExact("@param ")) {
903 continue;
904 }
Cary Clark09d80c02018-10-31 12:14:03 -0400905 if (parser.skipExact(ref.c_str()) && ' ' == parser.peek()) {
Cary Clarka64e4ee2018-10-18 08:30:34 -0400906 return &iter;
907 }
Cary Clarka560c472017-11-27 10:44:06 -0500908 }
909 return nullptr;
910}
911
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400912bool Definition::hasMatch(string name) const {
Cary Clarkac47b882018-01-11 10:35:44 -0500913 for (auto child : fChildren) {
914 if (name == child->fName) {
915 return true;
916 }
917 if (child->hasMatch(name)) {
918 return true;
919 }
920 }
921 return false;
922}
923
Cary Clarkab2621d2018-01-30 10:08:57 -0500924bool Definition::isStructOrClass() const {
925 if (MarkType::kStruct != fMarkType && MarkType::kClass != fMarkType) {
926 return false;
927 }
928 if (string::npos != fFileName.find("undocumented.bmh")) {
929 return false;
930 }
931 return true;
932}
933
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400934bool Definition::methodHasReturn(string name, TextParser* methodParser) const {
Cary Clarka560c472017-11-27 10:44:06 -0500935 if (methodParser->skipExact("static")) {
936 methodParser->skipWhiteSpace();
937 }
Cary Clark80247e52018-07-11 16:18:41 -0400938 if (methodParser->skipExact("virtual")) {
939 methodParser->skipWhiteSpace();
940 }
Cary Clarka560c472017-11-27 10:44:06 -0500941 const char* lastStart = methodParser->fChar;
942 const char* nameInParser = methodParser->strnstr(name.c_str(), methodParser->fEnd);
943 methodParser->skipTo(nameInParser);
944 const char* lastEnd = methodParser->fChar;
945 const char* returnEnd = lastEnd;
946 while (returnEnd > lastStart && ' ' == returnEnd[-1]) {
947 --returnEnd;
948 }
949 bool expectReturn = 4 != returnEnd - lastStart || strncmp("void", lastStart, 4);
950 if (MethodType::kNone != fMethodType && MethodType::kOperator != fMethodType && !expectReturn) {
951 return methodParser->reportError<bool>("unexpected void");
952 }
953 switch (fMethodType) {
954 case MethodType::kNone:
955 case MethodType::kOperator:
956 // either is fine
957 break;
958 case MethodType::kConstructor:
959 expectReturn = true;
960 break;
961 case MethodType::kDestructor:
962 expectReturn = false;
963 break;
964 }
965 return expectReturn;
966}
967
968string Definition::methodName() const {
969 string result;
970 size_t start = 0;
971 string parent;
972 const Definition* parentDef = this;
973 while ((parentDef = parentDef->fParent)) {
974 if (MarkType::kClass == parentDef->fMarkType || MarkType::kStruct == parentDef->fMarkType) {
975 parent = parentDef->fName;
976 break;
977 }
978 }
979 if (parent.length() && 0 == fName.compare(0, parent.length(), parent)) {
980 start = parent.length();
981 while (start < fName.length() && ':' == fName[start]) {
982 ++start;
983 }
984 }
985 if (fClone) {
986 int lastUnder = fName.rfind('_');
987 return fName.substr(start, (size_t) (lastUnder - start));
988 }
989 size_t end = fName.find_first_of('(', start);
990 if (string::npos == end) {
991 return fName.substr(start);
992 }
993 return fName.substr(start, end - start);
994}
995
996bool Definition::nextMethodParam(TextParser* methodParser, const char** nextEndPtr,
997 string* paramName) const {
998 int parenCount = 0;
Cary Clark186d08f2018-04-03 08:43:27 -0400999 TextParserSave saveState(methodParser);
Cary Clarka560c472017-11-27 10:44:06 -05001000 while (true) {
1001 if (methodParser->eof()) {
1002 return methodParser->reportError<bool>("#Method function missing close paren");
1003 }
1004 char ch = methodParser->peek();
1005 if ('(' == ch) {
1006 ++parenCount;
1007 }
1008 if (parenCount == 0 && (')' == ch || ',' == ch)) {
1009 *nextEndPtr = methodParser->fChar;
1010 break;
1011 }
1012 if (')' == ch) {
1013 if (0 > --parenCount) {
1014 return this->reportError<bool>("mismatched parentheses");
1015 }
1016 }
1017 methodParser->next();
1018 }
1019 saveState.restore();
1020 const char* nextEnd = *nextEndPtr;
1021 const char* paramEnd = nextEnd;
1022 const char* assign = methodParser->strnstr(" = ", paramEnd);
1023 if (assign) {
1024 paramEnd = assign;
1025 }
1026 const char* closeBracket = methodParser->strnstr("]", paramEnd);
1027 if (closeBracket) {
1028 const char* openBracket = methodParser->strnstr("[", paramEnd);
1029 if (openBracket && openBracket < closeBracket) {
1030 while (openBracket < --closeBracket && isdigit(closeBracket[0]))
1031 ;
1032 if (openBracket == closeBracket) {
1033 paramEnd = openBracket;
1034 }
1035 }
1036 }
1037 const char* function = methodParser->strnstr(")(", paramEnd);
1038 if (function) {
1039 paramEnd = function;
1040 }
1041 while (paramEnd > methodParser->fChar && ' ' == paramEnd[-1]) {
1042 --paramEnd;
1043 }
1044 const char* paramStart = paramEnd;
1045 while (paramStart > methodParser->fChar && isalnum(paramStart[-1])) {
1046 --paramStart;
1047 }
1048 if (paramStart > methodParser->fChar && paramStart >= paramEnd) {
1049 return methodParser->reportError<bool>("#Method missing param name");
1050 }
1051 *paramName = string(paramStart, paramEnd - paramStart);
1052 if (!paramName->length()) {
1053 if (')' != nextEnd[0]) {
1054 return methodParser->reportError<bool>("#Method malformed param");
1055 }
1056 return false;
1057 }
1058 return true;
1059}
1060
1061string Definition::NormalizedName(string name) {
1062 string normalizedName = name;
1063 std::replace(normalizedName.begin(), normalizedName.end(), '-', '_');
1064 do {
1065 size_t doubleColon = normalizedName.find("::", 0);
1066 if (string::npos == doubleColon) {
1067 break;
1068 }
1069 normalizedName = normalizedName.substr(0, doubleColon)
1070 + '_' + normalizedName.substr(doubleColon + 2);
1071 } while (true);
1072 return normalizedName;
1073}
1074
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001075static string unpreformat(string orig) {
Cary Clark78de7512018-02-07 07:27:09 -05001076 string result;
1077 int amp = 0;
1078 for (auto c : orig) {
1079 switch (amp) {
1080 case 0:
1081 if ('&' == c) {
1082 amp = 1;
1083 } else {
1084 amp = 0;
1085 result += c;
1086 }
1087 break;
1088 case 1:
1089 if ('l' == c) {
1090 amp = 2;
1091 } else if ('g' == c) {
1092 amp = 3;
1093 } else {
1094 amp = 0;
1095 result += "&";
1096 result += c;
1097 }
1098 break;
1099 case 2:
1100 if ('t' == c) {
1101 amp = 4;
1102 } else {
1103 amp = 0;
1104 result += "&l";
1105 result += c;
1106 }
1107 break;
1108 case 3:
1109 if ('t' == c) {
1110 amp = 5;
1111 } else {
1112 amp = 0;
1113 result += "&g";
1114 result += c;
1115 }
1116 break;
1117 case 4:
1118 if (';' == c) {
1119 result += '<';
1120 } else {
1121 result += "&lt";
1122 result += c;
1123 }
1124 amp = 0;
1125 break;
1126 case 5:
1127 if (';' == c) {
1128 result += '>';
1129 } else {
1130 result += "&gt";
1131 result += c;
1132 }
1133 amp = 0;
1134 break;
1135 }
1136 }
1137 return result;
1138}
1139
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001140bool Definition::paramsMatch(string matchFormatted, string name) const {
Cary Clark78de7512018-02-07 07:27:09 -05001141 string match = unpreformat(matchFormatted);
Cary Clarka560c472017-11-27 10:44:06 -05001142 TextParser def(fFileName, fStart, fContentStart, fLineCount);
1143 const char* dName = def.strnstr(name.c_str(), fContentStart);
1144 if (!dName) {
1145 return false;
1146 }
1147 def.skipTo(dName);
1148 TextParser m(fFileName, &match.front(), &match.back() + 1, fLineCount);
1149 const char* mName = m.strnstr(name.c_str(), m.fEnd);
1150 if (!mName) {
1151 return false;
1152 }
1153 m.skipTo(mName);
1154 while (!def.eof() && ')' != def.peek() && !m.eof() && ')' != m.peek()) {
1155 const char* ds = def.fChar;
1156 const char* ms = m.fChar;
1157 const char* de = def.anyOf(") \n");
1158 const char* me = m.anyOf(") \n");
1159 def.skipTo(de);
1160 m.skipTo(me);
1161 if (def.fChar - ds != m.fChar - ms) {
1162 return false;
1163 }
1164 if (strncmp(ds, ms, (int) (def.fChar - ds))) {
1165 return false;
1166 }
1167 def.skipWhiteSpace();
1168 m.skipWhiteSpace();
1169 }
1170 return !def.eof() && ')' == def.peek() && !m.eof() && ')' == m.peek();
1171}
1172
Cary Clark2be81cf2018-09-13 12:04:30 -04001173
1174void Definition::trimEnd() {
1175 while (fContentEnd > fContentStart && ' ' >= fContentEnd[-1]) {
1176 --fContentEnd;
1177 }
1178}
1179
Cary Clarka560c472017-11-27 10:44:06 -05001180void RootDefinition::clearVisited() {
1181 fVisited = false;
1182 for (auto& leaf : fLeaves) {
1183 leaf.second.fVisited = false;
1184 }
1185 for (auto& branch : fBranches) {
1186 branch.second->clearVisited();
1187 }
1188}
1189
Cary Clarkf5404bb2018-01-05 12:10:09 -05001190bool RootDefinition::dumpUnVisited() {
1191 bool success = true;
Cary Clarka560c472017-11-27 10:44:06 -05001192 for (auto& leaf : fLeaves) {
1193 if (!leaf.second.fVisited) {
Cary Clarka560c472017-11-27 10:44:06 -05001194 // FIXME: bugs requiring long tail fixes, suppressed here:
1195 // SkBitmap::validate() is wrapped in SkDEBUGCODE in .h and not parsed
1196 if ("SkBitmap::validate()" == leaf.first) {
1197 continue;
1198 }
Cary Clarka560c472017-11-27 10:44:06 -05001199 // SkPath::pathRefIsValid in #ifdef ; prefer to remove chrome dependency to fix
1200 if ("SkPath::pathRefIsValid" == leaf.first) {
1201 continue;
1202 }
1203 // FIXME: end of long tail bugs
1204 SkDebugf("defined in bmh but missing in include: %s\n", leaf.first.c_str());
Cary Clarkf5404bb2018-01-05 12:10:09 -05001205 success = false;
Cary Clarka560c472017-11-27 10:44:06 -05001206 }
1207 }
1208 for (auto& branch : fBranches) {
Cary Clarkf5404bb2018-01-05 12:10:09 -05001209 success &= branch.second->dumpUnVisited();
Cary Clarka560c472017-11-27 10:44:06 -05001210 }
Cary Clarkf5404bb2018-01-05 12:10:09 -05001211 return success;
Cary Clarka560c472017-11-27 10:44:06 -05001212}
1213
Cary Clark682c58d2018-05-16 07:07:07 -04001214Definition* RootDefinition::find(string ref, AllowParens allowParens) {
Cary Clarka560c472017-11-27 10:44:06 -05001215 const auto leafIter = fLeaves.find(ref);
1216 if (leafIter != fLeaves.end()) {
1217 return &leafIter->second;
1218 }
Cary Clarka8cdc172018-09-07 14:17:08 -04001219 if (AllowParens::kYes == allowParens) {
1220 size_t leftParen = ref.find('(');
1221 if (string::npos == leftParen
1222 || (leftParen + 1 < ref.length() && ')' != ref[leftParen + 1])) {
1223 string withParens = ref + "()";
1224 const auto parensIter = fLeaves.find(withParens);
1225 if (parensIter != fLeaves.end()) {
1226 return &parensIter->second;
1227 }
1228 }
1229 if (string::npos != leftParen) {
1230 string name = ref.substr(0, leftParen);
1231 size_t posInDefName = fName.find(name);
1232 if (string::npos != posInDefName && posInDefName > 2
1233 && "::" == fName.substr(posInDefName - 2, 2)) {
1234 string fullRef = fName + "::" + ref;
1235 const auto fullIter = fLeaves.find(fullRef);
1236 if (fullIter != fLeaves.end()) {
1237 return &fullIter->second;
1238 }
1239 }
Cary Clarka560c472017-11-27 10:44:06 -05001240 }
1241 }
1242 const auto branchIter = fBranches.find(ref);
1243 if (branchIter != fBranches.end()) {
Cary Clark682c58d2018-05-16 07:07:07 -04001244 RootDefinition* rootDef = branchIter->second;
Cary Clarka560c472017-11-27 10:44:06 -05001245 return rootDef;
1246 }
Cary Clark682c58d2018-05-16 07:07:07 -04001247 Definition* result = nullptr;
Cary Clarka560c472017-11-27 10:44:06 -05001248 for (const auto& branch : fBranches) {
Cary Clark682c58d2018-05-16 07:07:07 -04001249 RootDefinition* rootDef = branch.second;
Cary Clarka560c472017-11-27 10:44:06 -05001250 result = rootDef->find(ref, allowParens);
1251 if (result) {
1252 break;
1253 }
1254 }
1255 return result;
1256}