blob: 41407f810e2a3621ba1410a4a06830ebd129cd25 [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();
278 bool constMethod = iParser.skipExact("_const");
279 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;
290#if 0
291 if ('!' == fName[opPos]) {
292 SkASSERT('=' == fName[opPos + 1]);
293 result += "not_equal_operator";
294 } else if ('=' == fName[opPos]) {
295 if ('(' == fName[opPos + 1]) {
296 result += isMove ? "move_" : "copy_";
297 result += "assignment_operator";
298 } else {
299 SkASSERT('=' == fName[opPos + 1]);
300 result += "equal_operator";
301 }
302 } else if ('[' == fName[opPos]) {
303 result += "subscript_operator";
304 const char* end = fContentStart;
305 while (end > fStart && ' ' >= end[-1]) {
306 --end;
307 }
308 string constCheck(fStart, end - fStart);
309 size_t constPos = constCheck.rfind("const");
310 if (constCheck.length() == constPos + 5) {
311 result += "_const";
312 }
313 } else if ('*' == fName[opPos]) {
314 result += "multiply_operator";
315 } else if ('-' == fName[opPos]) {
316 result += "subtract_operator";
317 } else if ('+' == fName[opPos]) {
318 result += "add_operator";
319 } else {
320 SkASSERT(0); // todo: incomplete
321 }
322#endif
323 return true;
324}
325
326#undef CONST
327#undef FRIEND
328#undef BLANK
329#undef DEFOP
330
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400331bool Definition::boilerplateIfDef() {
Cary Clarka560c472017-11-27 10:44:06 -0500332 const Definition& label = fTokens.front();
333 if (Type::kWord != label.fType) {
334 return false;
335 }
336 fName = string(label.fContentStart, label.fContentEnd - label.fContentStart);
337 return true;
338}
339
Cary Clarka560c472017-11-27 10:44:06 -0500340
341// fixme: this will need to be more complicated to handle all of Skia
342// for now, just handle paint -- maybe fiddle will loosen naming restrictions
343void Definition::setCanonicalFiddle() {
344 fMethodType = Definition::MethodType::kNone;
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400345 size_t doubleColons = fName.rfind("::");
Cary Clarka560c472017-11-27 10:44:06 -0500346 SkASSERT(string::npos != doubleColons);
347 string base = fName.substr(0, doubleColons);
348 string result = base + "_";
349 doubleColons += 2;
350 if (string::npos != fName.find('~', doubleColons)) {
351 fMethodType = Definition::MethodType::kDestructor;
352 result += "destructor";
353 } else if (!this->parseOperator(doubleColons, result)) {
354 bool isMove = string::npos != fName.find("&&", doubleColons);
355 size_t parens = fName.find("()", doubleColons);
356 if (string::npos != parens) {
357 string methodName = fName.substr(doubleColons, parens - doubleColons);
358 do {
359 size_t nextDouble = methodName.find("::");
360 if (string::npos == nextDouble) {
361 break;
362 }
363 base = methodName.substr(0, nextDouble);
364 result += base + '_';
365 methodName = methodName.substr(nextDouble + 2);
366 doubleColons += nextDouble + 2;
367 } while (true);
368 if (base == methodName) {
369 fMethodType = Definition::MethodType::kConstructor;
370 result += "empty_constructor";
371 } else {
372 result += fName.substr(doubleColons, fName.length() - doubleColons - 2);
373 }
374 } else {
375 size_t openParen = fName.find('(', doubleColons);
376 if (string::npos == openParen) {
377 result += fName.substr(doubleColons);
Cary Clark682c58d2018-05-16 07:07:07 -0400378 // see if it is a constructor -- if second to last delimited name equals last
379 size_t nextColons = fName.find("::", doubleColons);
380 if (string::npos != nextColons) {
381 nextColons += 2;
382 if (!strncmp(&fName[doubleColons], &fName[nextColons],
383 nextColons - doubleColons - 2)) {
384 fMethodType = Definition::MethodType::kConstructor;
385 }
386 }
Cary Clarka560c472017-11-27 10:44:06 -0500387 } else {
388 size_t comma = fName.find(',', doubleColons);
389 if (string::npos == comma) {
390 result += isMove ? "move_" : "copy_";
391 }
392 fMethodType = Definition::MethodType::kConstructor;
393 // name them by their param types,
394 // e.g. SkCanvas__int_int_const_SkSurfaceProps_star
395 // TODO: move forward until parens are balanced and terminator =,)
396 TextParser params("", &fName[openParen] + 1, &*fName.end(), 0);
397 bool underline = false;
398 while (!params.eof()) {
399// SkDEBUGCODE(const char* end = params.anyOf("(),=")); // unused for now
400// SkASSERT(end[0] != '('); // fixme: put off handling nested parentheseses
401 if (params.startsWith("const") || params.startsWith("int")
402 || params.startsWith("Sk")) {
403 const char* wordStart = params.fChar;
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400404 params.skipToNonName();
Cary Clarka560c472017-11-27 10:44:06 -0500405 if (underline) {
406 result += '_';
407 } else {
408 underline = true;
409 }
410 result += string(wordStart, params.fChar - wordStart);
411 } else {
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400412 params.skipToNonName();
Cary Clarka560c472017-11-27 10:44:06 -0500413 }
414 if (!params.eof() && '*' == params.peek()) {
415 if (underline) {
416 result += '_';
417 } else {
418 underline = true;
419 }
420 result += "star";
421 params.next();
422 params.skipSpace();
423 }
424 params.skipToAlpha();
425 }
426 }
427 }
428 }
429 fFiddle = Definition::NormalizedName(result);
430}
431
Cary Clarka560c472017-11-27 10:44:06 -0500432static void space_pad(string* str) {
433 size_t len = str->length();
434 if (len == 0) {
435 return;
436 }
437 char last = (*str)[len - 1];
438 if ('~' == last || ' ' >= last) {
439 return;
440 }
441 *str += ' ';
442}
443
444//start here;
445// see if it possible to abstract this a little bit so it can
446// additionally be used to find params and return in method prototype that
447// does not have corresponding doxygen comments
448bool Definition::checkMethod() const {
449 SkASSERT(MarkType::kMethod == fMarkType);
450 // if method returns a value, look for a return child
451 // for each parameter, look for a corresponding child
452 const char* end = fContentStart;
453 while (end > fStart && ' ' >= end[-1]) {
454 --end;
455 }
456 TextParser methodParser(fFileName, fStart, end, fLineCount);
457 methodParser.skipWhiteSpace();
458 SkASSERT(methodParser.startsWith("#Method"));
459 methodParser.skipName("#Method");
460 methodParser.skipSpace();
461 string name = this->methodName();
462 if (MethodType::kNone == fMethodType && name.length() > 2 &&
463 "()" == name.substr(name.length() - 2)) {
464 name = name.substr(0, name.length() - 2);
465 }
466 bool expectReturn = this->methodHasReturn(name, &methodParser);
467 bool foundReturn = false;
Cary Clarka64e4ee2018-10-18 08:30:34 -0400468 bool foundPopulate = false;
Cary Clarka560c472017-11-27 10:44:06 -0500469 for (auto& child : fChildren) {
Cary Clarka64e4ee2018-10-18 08:30:34 -0400470 foundPopulate |= MarkType::kPopulate == child->fMarkType;
Cary Clarka560c472017-11-27 10:44:06 -0500471 if (MarkType::kReturn != child->fMarkType) {
472 if (MarkType::kParam == child->fMarkType) {
473 child->fVisited = false;
474 }
475 continue;
476 }
477 if (!expectReturn) {
478 return methodParser.reportError<bool>("no #Return expected");
479 }
480 if (foundReturn) {
481 return methodParser.reportError<bool>("multiple #Return markers");
482 }
483 foundReturn = true;
484 }
Cary Clarkabaffd82018-11-15 08:25:12 -0500485 if (expectReturn && !foundReturn && !foundPopulate) {
Cary Clarka560c472017-11-27 10:44:06 -0500486 return methodParser.reportError<bool>("missing #Return marker");
487 }
488 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
489 if (!paren) {
490 return methodParser.reportError<bool>("missing #Method function definition");
491 }
492 const char* nextEnd = paren;
493 do {
494 string paramName;
495 methodParser.fChar = nextEnd + 1;
496 methodParser.skipSpace();
497 if (!this->nextMethodParam(&methodParser, &nextEnd, &paramName)) {
498 continue;
499 }
500 bool foundParam = false;
501 for (auto& child : fChildren) {
502 if (MarkType::kParam != child->fMarkType) {
503 continue;
504 }
505 if (paramName != child->fName) {
506 continue;
507 }
508 if (child->fVisited) {
509 return methodParser.reportError<bool>("multiple #Method param with same name");
510 }
511 child->fVisited = true;
512 if (foundParam) {
513 TextParser paramError(child);
514 return methodParser.reportError<bool>("multiple #Param with same name");
515 }
516 foundParam = true;
517
518 }
Cary Clarkabaffd82018-11-15 08:25:12 -0500519 if (!foundParam && !foundPopulate) {
Cary Clarka560c472017-11-27 10:44:06 -0500520 return methodParser.reportError<bool>("no #Param found");
521 }
522 if (')' == nextEnd[0]) {
523 break;
524 }
525 } while (')' != nextEnd[0]);
526 for (auto& child : fChildren) {
527 if (MarkType::kParam != child->fMarkType) {
528 continue;
529 }
530 if (!child->fVisited) {
531 TextParser paramError(child);
532 return paramError.reportError<bool>("#Param without param in #Method");
533 }
534 }
Cary Clark80247e52018-07-11 16:18:41 -0400535 // check after end of #Line and before next child for description
536 const char* descStart = fContentStart;
537 const char* descEnd = nullptr;
Cary Clarkab5c9af2018-07-12 16:24:53 -0400538 const Definition* defEnd = nullptr;
539 const Definition* priorDef = nullptr;
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400540 bool incomplete = false;
Cary Clark80247e52018-07-11 16:18:41 -0400541 for (auto& child : fChildren) {
542 if (MarkType::kAnchor == child->fMarkType) {
543 continue;
544 }
545 if (MarkType::kCode == child->fMarkType) {
Cary Clarkab5c9af2018-07-12 16:24:53 -0400546 priorDef = child;
Cary Clark80247e52018-07-11 16:18:41 -0400547 continue;
548 }
Cary Clark80247e52018-07-11 16:18:41 -0400549 if (MarkType::kFormula == child->fMarkType) {
550 continue;
551 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400552 if (MarkType::kLine == child->fMarkType) {
553 SkASSERT(child->fChildren.size() > 0);
554 TextParser childDesc(child->fChildren[0]);
555 incomplete |= childDesc.startsWith("incomplete");
556 }
Cary Clark80247e52018-07-11 16:18:41 -0400557 if (MarkType::kList == child->fMarkType) {
Cary Clarkab5c9af2018-07-12 16:24:53 -0400558 priorDef = child;
Cary Clark80247e52018-07-11 16:18:41 -0400559 continue;
560 }
561 if (MarkType::kMarkChar == child->fMarkType) {
562 continue;
563 }
564 if (MarkType::kPhraseRef == child->fMarkType) {
565 continue;
566 }
Cary Clark80247e52018-07-11 16:18:41 -0400567 TextParser emptyCheck(fFileName, descStart, child->fStart, child->fLineCount);
568 if (!emptyCheck.eof() && emptyCheck.skipWhiteSpace()) {
569 descStart = emptyCheck.fChar;
570 emptyCheck.trimEnd();
Cary Clarkab5c9af2018-07-12 16:24:53 -0400571 defEnd = priorDef;
Cary Clark80247e52018-07-11 16:18:41 -0400572 descEnd = emptyCheck.fEnd;
573 break;
574 }
575 descStart = child->fTerminator;
Cary Clarkab5c9af2018-07-12 16:24:53 -0400576 priorDef = nullptr;
Cary Clark80247e52018-07-11 16:18:41 -0400577 }
578 if (!descEnd) {
Cary Clarka64e4ee2018-10-18 08:30:34 -0400579 return incomplete || foundPopulate ? true :
580 methodParser.reportError<bool>("missing description");
Cary Clark80247e52018-07-11 16:18:41 -0400581 }
582 TextParser description(fFileName, descStart, descEnd, fLineCount);
583 // expect first word capitalized and pluralized. expect a trailing period
584 SkASSERT(descStart < descEnd);
585 if (!isupper(descStart[0])) {
586 description.reportWarning("expected capital");
587 } else if ('.' != descEnd[-1]) {
Cary Clarkab5c9af2018-07-12 16:24:53 -0400588 if (!defEnd || defEnd->fTerminator != descEnd) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400589 if (!incomplete) {
590 description.reportWarning("expected period");
591 }
Cary Clarkab5c9af2018-07-12 16:24:53 -0400592 }
Cary Clark80247e52018-07-11 16:18:41 -0400593 } else {
594 if (!description.startsWith("For use by Android")) {
595 description.skipToSpace();
596 if (',' == description.fChar[-1]) {
597 --description.fChar;
598 }
599 if ('s' != description.fChar[-1]) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400600 if (!incomplete) {
601 description.reportWarning("expected plural");
602 }
Cary Clark80247e52018-07-11 16:18:41 -0400603 }
604 }
605 }
Cary Clarka560c472017-11-27 10:44:06 -0500606 return true;
607}
608
609bool Definition::crossCheck2(const Definition& includeToken) const {
610 TextParser parser(fFileName, fStart, fContentStart, fLineCount);
611 parser.skipExact("#");
612 bool isMethod = parser.skipName("Method");
613 const char* contentEnd;
614 if (isMethod) {
615 contentEnd = fContentStart;
616 } else if (parser.skipName("DefinedBy")) {
617 contentEnd = fContentEnd;
618 while (parser.fChar < contentEnd && ' ' >= contentEnd[-1]) {
619 --contentEnd;
620 }
621 if (parser.fChar < contentEnd - 1 && ')' == contentEnd[-1] && '(' == contentEnd[-2]) {
622 contentEnd -= 2;
623 }
624 } else {
625 return parser.reportError<bool>("unexpected crosscheck marktype");
626 }
627 return crossCheckInside(parser.fChar, contentEnd, includeToken);
628}
629
630bool Definition::crossCheck(const Definition& includeToken) const {
631 return crossCheckInside(fContentStart, fContentEnd, includeToken);
632}
633
Cary Clarkab5c9af2018-07-12 16:24:53 -0400634const char* Definition::methodEnd() const {
635 const char defaultTag[] = " = default";
636 size_t tagSize = sizeof(defaultTag) - 1;
637 const char* tokenEnd = fContentEnd - tagSize;
638 if (tokenEnd <= fContentStart || strncmp(tokenEnd, defaultTag, tagSize)) {
639 tokenEnd = fContentEnd;
640 }
641 return tokenEnd;
642}
643
Cary Clarkfd32e722018-11-16 14:36:02 -0500644bool Definition::SkipImplementationWords(TextParser& inc) {
Cary Clarka560c472017-11-27 10:44:06 -0500645 if (inc.startsWith("SK_API")) {
646 inc.skipWord("SK_API");
647 }
Cary Clark61313f32018-10-08 14:57:48 -0400648 if (inc.startsWith("inline")) {
649 inc.skipWord("inline");
650 }
Cary Clarka560c472017-11-27 10:44:06 -0500651 if (inc.startsWith("friend")) {
652 inc.skipWord("friend");
653 }
654 if (inc.startsWith("SK_API")) {
655 inc.skipWord("SK_API");
656 }
Cary Clarkfd32e722018-11-16 14:36:02 -0500657 return inc.skipExact("SkDEBUGCODE(");
658}
659
660bool Definition::crossCheckInside(const char* start, const char* end,
661 const Definition& includeToken) const {
662 TextParser def(fFileName, start, end, fLineCount);
663 const char* tokenEnd = includeToken.methodEnd();
664 TextParser inc("", includeToken.fContentStart, tokenEnd, 0);
665 if (inc.startsWith("static")) {
666 def.skipWhiteSpace();
667 if (!def.startsWith("static")) {
668 return false;
669 }
670 inc.skipWord("static");
671 def.skipWord("static");
672 }
673 (void) Definition::SkipImplementationWords(inc);
Cary Clarka560c472017-11-27 10:44:06 -0500674 do {
675 bool defEof;
676 bool incEof;
677 do {
678 defEof = def.eof() || !def.skipWhiteSpace();
679 incEof = inc.eof() || !inc.skipWhiteSpace();
680 if (!incEof && '/' == inc.peek() && (defEof || '/' != def.peek())) {
681 inc.next();
682 if ('*' == inc.peek()) {
683 inc.skipToEndBracket("*/");
684 inc.next();
685 } else if ('/' == inc.peek()) {
686 inc.skipToEndBracket('\n');
687 }
688 } else if (!incEof && '#' == inc.peek() && (defEof || '#' != def.peek())) {
689 inc.next();
690 if (inc.startsWith("if")) {
691 inc.skipToEndBracket("\n");
692 } else if (inc.startsWith("endif")) {
693 inc.skipToEndBracket("\n");
694 } else {
695 SkASSERT(0); // incomplete
696 return false;
697 }
698 } else {
699 break;
700 }
701 inc.next();
702 } while (true);
703 if (defEof || incEof) {
704 if (defEof == incEof || (!defEof && ';' == def.peek())) {
705 return true;
706 }
707 return false; // allow setting breakpoint on failure
708 }
709 char defCh;
710 do {
711 defCh = def.next();
Cary Clark61313f32018-10-08 14:57:48 -0400712 if (inc.skipExact("SK_WARN_UNUSED_RESULT")) {
713 inc.skipSpace();
714 }
Cary Clarka560c472017-11-27 10:44:06 -0500715 char incCh = inc.next();
716 if (' ' >= defCh && ' ' >= incCh) {
717 break;
718 }
719 if (defCh != incCh) {
720 if ('_' != defCh || ' ' != incCh || !fOperatorConst || !def.startsWith("const")) {
721 return false;
722 }
723 }
724 if (';' == defCh) {
725 return true;
726 }
727 } while (!def.eof() && !inc.eof());
728 } while (true);
729 return false;
730}
731
Cary Clark78de7512018-02-07 07:27:09 -0500732string Definition::formatFunction(Format format) const {
Cary Clarka560c472017-11-27 10:44:06 -0500733 const char* end = fContentStart;
734 while (end > fStart && ' ' >= end[-1]) {
735 --end;
736 }
737 TextParser methodParser(fFileName, fStart, end, fLineCount);
738 methodParser.skipWhiteSpace();
739 SkASSERT(methodParser.startsWith("#Method"));
740 methodParser.skipName("#Method");
741 methodParser.skipSpace();
742 const char* lastStart = methodParser.fChar;
743 const int limit = 100; // todo: allow this to be set by caller or in global or something
744 string name = this->methodName();
Cary Clarka560c472017-11-27 10:44:06 -0500745 const char* nameInParser = methodParser.strnstr(name.c_str(), methodParser.fEnd);
746 methodParser.skipTo(nameInParser);
747 const char* lastEnd = methodParser.fChar;
Cary Clark78de7512018-02-07 07:27:09 -0500748 if (Format::kOmitReturn == format) {
749 lastStart = lastEnd;
750 }
Cary Clarka560c472017-11-27 10:44:06 -0500751 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
752 size_t indent;
753 if (paren) {
754 indent = (size_t) (paren - lastStart) + 1;
755 } else {
756 indent = (size_t) (lastEnd - lastStart);
757 }
758 // trim indent so longest line doesn't exceed box width
Cary Clark186d08f2018-04-03 08:43:27 -0400759 TextParserSave savePlace(&methodParser);
Cary Clarka560c472017-11-27 10:44:06 -0500760 const char* saveStart = lastStart;
761 ptrdiff_t maxLine = 0;
762 do {
763 const char* nextStart = lastEnd;
764 const char* delimiter = methodParser.anyOf(",)");
765 const char* nextEnd = delimiter ? delimiter : methodParser.fEnd;
766 if (delimiter) {
767 while (nextStart < nextEnd && ' ' >= nextStart[0]) {
768 ++nextStart;
769 }
770 }
771 while (nextEnd > nextStart && ' ' >= nextEnd[-1]) {
772 --nextEnd;
773 }
774 if (delimiter) {
775 nextEnd += 1;
776 delimiter += 1;
777 }
778 if (lastEnd > lastStart) {
779 maxLine = SkTMax(maxLine, lastEnd - lastStart);
780 }
781 if (delimiter) {
782 methodParser.skipTo(delimiter);
783 }
784 lastStart = nextStart;
785 lastEnd = nextEnd;
786 } while (lastStart < lastEnd);
787 savePlace.restore();
788 lastStart = saveStart;
789 lastEnd = methodParser.fChar;
790 indent = SkTMin(indent, (size_t) (limit - maxLine));
Cary Clark682c58d2018-05-16 07:07:07 -0400791 // write string with trimmmed indent
Cary Clarka560c472017-11-27 10:44:06 -0500792 string methodStr;
793 int written = 0;
794 do {
795 const char* nextStart = lastEnd;
Cary Clark390495e2018-07-13 07:52:44 -0400796// SkASSERT(written < limit);
Cary Clarka560c472017-11-27 10:44:06 -0500797 const char* delimiter = methodParser.anyOf(",)");
798 const char* nextEnd = delimiter ? delimiter : methodParser.fEnd;
799 if (delimiter) {
800 while (nextStart < nextEnd && ' ' >= nextStart[0]) {
801 ++nextStart;
802 }
803 }
804 while (nextEnd > nextStart && ' ' >= nextEnd[-1]) {
805 --nextEnd;
806 }
807 if (delimiter) {
808 nextEnd += 1;
809 delimiter += 1;
810 }
811 if (lastEnd > lastStart) {
812 if (lastStart[0] != ' ') {
813 space_pad(&methodStr);
814 }
Cary Clark61313f32018-10-08 14:57:48 -0400815 string addon(lastStart, (size_t) (lastEnd - lastStart));
816 if ("_const" == addon) {
817 addon = "const";
818 }
819 methodStr += addon;
820 written += addon.length();
Cary Clarka560c472017-11-27 10:44:06 -0500821 }
822 if (delimiter) {
823 if (nextEnd - nextStart >= (ptrdiff_t) (limit - written)) {
824 written = indent;
Cary Clark78de7512018-02-07 07:27:09 -0500825 if (Format::kIncludeReturn == format) {
826 methodStr += '\n';
827 methodStr += string(indent, ' ');
828 }
Cary Clarka560c472017-11-27 10:44:06 -0500829 }
830 methodParser.skipTo(delimiter);
831 }
832 lastStart = nextStart;
833 lastEnd = nextEnd;
834 } while (lastStart < lastEnd);
835 return methodStr;
836}
837
838string Definition::fiddleName() const {
839 string result;
840 size_t start = 0;
841 string parent;
842 const Definition* parentDef = this;
843 while ((parentDef = parentDef->fParent)) {
844 if (MarkType::kClass == parentDef->fMarkType || MarkType::kStruct == parentDef->fMarkType) {
845 parent = parentDef->fFiddle;
846 break;
847 }
848 }
849 if (parent.length() && 0 == fFiddle.compare(0, parent.length(), parent)) {
850 start = parent.length();
851 while (start < fFiddle.length() && '_' == fFiddle[start]) {
852 ++start;
853 }
854 }
855 size_t end = fFiddle.find_first_of('(', start);
856 return fFiddle.substr(start, end - start);
857}
858
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400859string Definition::fileName() const {
860 size_t nameStart = fFileName.rfind(SkOSPath::SEPARATOR);
861 if (SkOSPath::SEPARATOR != '/') {
862 size_t altNameStart = fFileName.rfind('/');
863 nameStart = string::npos == nameStart ? altNameStart :
864 string::npos != altNameStart && altNameStart > nameStart ? altNameStart : nameStart;
865 }
866 SkASSERT(string::npos != nameStart);
867 string baseFile = fFileName.substr(nameStart + 1);
868 return baseFile;
869}
870
Cary Clark4855f782018-02-06 09:41:53 -0500871const Definition* Definition::findClone(string match) const {
872 for (auto child : fChildren) {
873 if (!child->fClone) {
874 continue;
875 }
876 if (match == child->fName) {
877 return child;
878 }
879 auto inner = child->findClone(match);
880 if (inner) {
881 return inner;
882 }
883 }
884 return nullptr;
885}
886
Cary Clarka560c472017-11-27 10:44:06 -0500887const Definition* Definition::hasChild(MarkType markType) const {
888 for (auto iter : fChildren) {
889 if (markType == iter->fMarkType) {
890 return iter;
891 }
892 }
893 return nullptr;
894}
895
Cary Clarka64e4ee2018-10-18 08:30:34 -0400896Definition* Definition::hasParam(string ref) {
Cary Clarka560c472017-11-27 10:44:06 -0500897 SkASSERT(MarkType::kMethod == fMarkType);
898 for (auto iter : fChildren) {
899 if (MarkType::kParam != iter->fMarkType) {
900 continue;
901 }
902 if (iter->fName == ref) {
903 return &*iter;
904 }
Cary Clarka64e4ee2018-10-18 08:30:34 -0400905 }
906 for (auto& iter : fTokens) {
907 if (MarkType::kComment != iter.fMarkType) {
908 continue;
909 }
910 TextParser parser(&iter);
911 if (!parser.skipExact("@param ")) {
912 continue;
913 }
Cary Clark09d80c02018-10-31 12:14:03 -0400914 if (parser.skipExact(ref.c_str()) && ' ' == parser.peek()) {
Cary Clarka64e4ee2018-10-18 08:30:34 -0400915 return &iter;
916 }
Cary Clarka560c472017-11-27 10:44:06 -0500917 }
918 return nullptr;
919}
920
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400921bool Definition::hasMatch(string name) const {
Cary Clarkac47b882018-01-11 10:35:44 -0500922 for (auto child : fChildren) {
923 if (name == child->fName) {
924 return true;
925 }
926 if (child->hasMatch(name)) {
927 return true;
928 }
929 }
930 return false;
931}
932
Cary Clarkab2621d2018-01-30 10:08:57 -0500933bool Definition::isStructOrClass() const {
934 if (MarkType::kStruct != fMarkType && MarkType::kClass != fMarkType) {
935 return false;
936 }
937 if (string::npos != fFileName.find("undocumented.bmh")) {
938 return false;
939 }
940 return true;
941}
942
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400943bool Definition::methodHasReturn(string name, TextParser* methodParser) const {
Cary Clarka560c472017-11-27 10:44:06 -0500944 if (methodParser->skipExact("static")) {
945 methodParser->skipWhiteSpace();
946 }
Cary Clark80247e52018-07-11 16:18:41 -0400947 if (methodParser->skipExact("virtual")) {
948 methodParser->skipWhiteSpace();
949 }
Cary Clarka560c472017-11-27 10:44:06 -0500950 const char* lastStart = methodParser->fChar;
951 const char* nameInParser = methodParser->strnstr(name.c_str(), methodParser->fEnd);
952 methodParser->skipTo(nameInParser);
953 const char* lastEnd = methodParser->fChar;
954 const char* returnEnd = lastEnd;
955 while (returnEnd > lastStart && ' ' == returnEnd[-1]) {
956 --returnEnd;
957 }
958 bool expectReturn = 4 != returnEnd - lastStart || strncmp("void", lastStart, 4);
959 if (MethodType::kNone != fMethodType && MethodType::kOperator != fMethodType && !expectReturn) {
960 return methodParser->reportError<bool>("unexpected void");
961 }
962 switch (fMethodType) {
963 case MethodType::kNone:
964 case MethodType::kOperator:
965 // either is fine
966 break;
967 case MethodType::kConstructor:
968 expectReturn = true;
969 break;
970 case MethodType::kDestructor:
971 expectReturn = false;
972 break;
973 }
974 return expectReturn;
975}
976
977string Definition::methodName() const {
978 string result;
979 size_t start = 0;
980 string parent;
981 const Definition* parentDef = this;
982 while ((parentDef = parentDef->fParent)) {
983 if (MarkType::kClass == parentDef->fMarkType || MarkType::kStruct == parentDef->fMarkType) {
984 parent = parentDef->fName;
985 break;
986 }
987 }
988 if (parent.length() && 0 == fName.compare(0, parent.length(), parent)) {
989 start = parent.length();
990 while (start < fName.length() && ':' == fName[start]) {
991 ++start;
992 }
993 }
994 if (fClone) {
995 int lastUnder = fName.rfind('_');
996 return fName.substr(start, (size_t) (lastUnder - start));
997 }
998 size_t end = fName.find_first_of('(', start);
999 if (string::npos == end) {
1000 return fName.substr(start);
1001 }
1002 return fName.substr(start, end - start);
1003}
1004
1005bool Definition::nextMethodParam(TextParser* methodParser, const char** nextEndPtr,
1006 string* paramName) const {
1007 int parenCount = 0;
Cary Clark186d08f2018-04-03 08:43:27 -04001008 TextParserSave saveState(methodParser);
Cary Clarka560c472017-11-27 10:44:06 -05001009 while (true) {
1010 if (methodParser->eof()) {
1011 return methodParser->reportError<bool>("#Method function missing close paren");
1012 }
1013 char ch = methodParser->peek();
1014 if ('(' == ch) {
1015 ++parenCount;
1016 }
1017 if (parenCount == 0 && (')' == ch || ',' == ch)) {
1018 *nextEndPtr = methodParser->fChar;
1019 break;
1020 }
1021 if (')' == ch) {
1022 if (0 > --parenCount) {
1023 return this->reportError<bool>("mismatched parentheses");
1024 }
1025 }
1026 methodParser->next();
1027 }
1028 saveState.restore();
1029 const char* nextEnd = *nextEndPtr;
1030 const char* paramEnd = nextEnd;
1031 const char* assign = methodParser->strnstr(" = ", paramEnd);
1032 if (assign) {
1033 paramEnd = assign;
1034 }
1035 const char* closeBracket = methodParser->strnstr("]", paramEnd);
1036 if (closeBracket) {
1037 const char* openBracket = methodParser->strnstr("[", paramEnd);
1038 if (openBracket && openBracket < closeBracket) {
1039 while (openBracket < --closeBracket && isdigit(closeBracket[0]))
1040 ;
1041 if (openBracket == closeBracket) {
1042 paramEnd = openBracket;
1043 }
1044 }
1045 }
1046 const char* function = methodParser->strnstr(")(", paramEnd);
1047 if (function) {
1048 paramEnd = function;
1049 }
1050 while (paramEnd > methodParser->fChar && ' ' == paramEnd[-1]) {
1051 --paramEnd;
1052 }
1053 const char* paramStart = paramEnd;
1054 while (paramStart > methodParser->fChar && isalnum(paramStart[-1])) {
1055 --paramStart;
1056 }
1057 if (paramStart > methodParser->fChar && paramStart >= paramEnd) {
1058 return methodParser->reportError<bool>("#Method missing param name");
1059 }
1060 *paramName = string(paramStart, paramEnd - paramStart);
1061 if (!paramName->length()) {
1062 if (')' != nextEnd[0]) {
1063 return methodParser->reportError<bool>("#Method malformed param");
1064 }
1065 return false;
1066 }
1067 return true;
1068}
1069
1070string Definition::NormalizedName(string name) {
1071 string normalizedName = name;
1072 std::replace(normalizedName.begin(), normalizedName.end(), '-', '_');
1073 do {
1074 size_t doubleColon = normalizedName.find("::", 0);
1075 if (string::npos == doubleColon) {
1076 break;
1077 }
1078 normalizedName = normalizedName.substr(0, doubleColon)
1079 + '_' + normalizedName.substr(doubleColon + 2);
1080 } while (true);
1081 return normalizedName;
1082}
1083
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001084static string unpreformat(string orig) {
Cary Clark78de7512018-02-07 07:27:09 -05001085 string result;
1086 int amp = 0;
1087 for (auto c : orig) {
1088 switch (amp) {
1089 case 0:
1090 if ('&' == c) {
1091 amp = 1;
1092 } else {
1093 amp = 0;
1094 result += c;
1095 }
1096 break;
1097 case 1:
1098 if ('l' == c) {
1099 amp = 2;
1100 } else if ('g' == c) {
1101 amp = 3;
1102 } else {
1103 amp = 0;
1104 result += "&";
1105 result += c;
1106 }
1107 break;
1108 case 2:
1109 if ('t' == c) {
1110 amp = 4;
1111 } else {
1112 amp = 0;
1113 result += "&l";
1114 result += c;
1115 }
1116 break;
1117 case 3:
1118 if ('t' == c) {
1119 amp = 5;
1120 } else {
1121 amp = 0;
1122 result += "&g";
1123 result += c;
1124 }
1125 break;
1126 case 4:
1127 if (';' == c) {
1128 result += '<';
1129 } else {
1130 result += "&lt";
1131 result += c;
1132 }
1133 amp = 0;
1134 break;
1135 case 5:
1136 if (';' == c) {
1137 result += '>';
1138 } else {
1139 result += "&gt";
1140 result += c;
1141 }
1142 amp = 0;
1143 break;
1144 }
1145 }
1146 return result;
1147}
1148
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001149bool Definition::paramsMatch(string matchFormatted, string name) const {
Cary Clark78de7512018-02-07 07:27:09 -05001150 string match = unpreformat(matchFormatted);
Cary Clarka560c472017-11-27 10:44:06 -05001151 TextParser def(fFileName, fStart, fContentStart, fLineCount);
1152 const char* dName = def.strnstr(name.c_str(), fContentStart);
1153 if (!dName) {
1154 return false;
1155 }
1156 def.skipTo(dName);
1157 TextParser m(fFileName, &match.front(), &match.back() + 1, fLineCount);
1158 const char* mName = m.strnstr(name.c_str(), m.fEnd);
1159 if (!mName) {
1160 return false;
1161 }
1162 m.skipTo(mName);
1163 while (!def.eof() && ')' != def.peek() && !m.eof() && ')' != m.peek()) {
1164 const char* ds = def.fChar;
1165 const char* ms = m.fChar;
1166 const char* de = def.anyOf(") \n");
1167 const char* me = m.anyOf(") \n");
1168 def.skipTo(de);
1169 m.skipTo(me);
1170 if (def.fChar - ds != m.fChar - ms) {
1171 return false;
1172 }
1173 if (strncmp(ds, ms, (int) (def.fChar - ds))) {
1174 return false;
1175 }
1176 def.skipWhiteSpace();
1177 m.skipWhiteSpace();
1178 }
1179 return !def.eof() && ')' == def.peek() && !m.eof() && ')' == m.peek();
1180}
1181
Cary Clark2be81cf2018-09-13 12:04:30 -04001182
1183void Definition::trimEnd() {
1184 while (fContentEnd > fContentStart && ' ' >= fContentEnd[-1]) {
1185 --fContentEnd;
1186 }
1187}
1188
Cary Clarka560c472017-11-27 10:44:06 -05001189void RootDefinition::clearVisited() {
1190 fVisited = false;
1191 for (auto& leaf : fLeaves) {
1192 leaf.second.fVisited = false;
1193 }
1194 for (auto& branch : fBranches) {
1195 branch.second->clearVisited();
1196 }
1197}
1198
Cary Clarkf5404bb2018-01-05 12:10:09 -05001199bool RootDefinition::dumpUnVisited() {
1200 bool success = true;
Cary Clarka560c472017-11-27 10:44:06 -05001201 for (auto& leaf : fLeaves) {
1202 if (!leaf.second.fVisited) {
Cary Clarka560c472017-11-27 10:44:06 -05001203 // FIXME: bugs requiring long tail fixes, suppressed here:
1204 // SkBitmap::validate() is wrapped in SkDEBUGCODE in .h and not parsed
1205 if ("SkBitmap::validate()" == leaf.first) {
1206 continue;
1207 }
Cary Clarka560c472017-11-27 10:44:06 -05001208 // FIXME: end of long tail bugs
1209 SkDebugf("defined in bmh but missing in include: %s\n", leaf.first.c_str());
Cary Clarkf5404bb2018-01-05 12:10:09 -05001210 success = false;
Cary Clarka560c472017-11-27 10:44:06 -05001211 }
1212 }
1213 for (auto& branch : fBranches) {
Cary Clarkf5404bb2018-01-05 12:10:09 -05001214 success &= branch.second->dumpUnVisited();
Cary Clarka560c472017-11-27 10:44:06 -05001215 }
Cary Clarkf5404bb2018-01-05 12:10:09 -05001216 return success;
Cary Clarka560c472017-11-27 10:44:06 -05001217}
1218
Cary Clark682c58d2018-05-16 07:07:07 -04001219Definition* RootDefinition::find(string ref, AllowParens allowParens) {
Cary Clarka560c472017-11-27 10:44:06 -05001220 const auto leafIter = fLeaves.find(ref);
1221 if (leafIter != fLeaves.end()) {
1222 return &leafIter->second;
1223 }
Cary Clarka8cdc172018-09-07 14:17:08 -04001224 if (AllowParens::kYes == allowParens) {
1225 size_t leftParen = ref.find('(');
1226 if (string::npos == leftParen
1227 || (leftParen + 1 < ref.length() && ')' != ref[leftParen + 1])) {
1228 string withParens = ref + "()";
1229 const auto parensIter = fLeaves.find(withParens);
1230 if (parensIter != fLeaves.end()) {
1231 return &parensIter->second;
1232 }
1233 }
1234 if (string::npos != leftParen) {
1235 string name = ref.substr(0, leftParen);
1236 size_t posInDefName = fName.find(name);
1237 if (string::npos != posInDefName && posInDefName > 2
1238 && "::" == fName.substr(posInDefName - 2, 2)) {
1239 string fullRef = fName + "::" + ref;
1240 const auto fullIter = fLeaves.find(fullRef);
1241 if (fullIter != fLeaves.end()) {
1242 return &fullIter->second;
1243 }
1244 }
Cary Clarka560c472017-11-27 10:44:06 -05001245 }
1246 }
1247 const auto branchIter = fBranches.find(ref);
1248 if (branchIter != fBranches.end()) {
Cary Clark682c58d2018-05-16 07:07:07 -04001249 RootDefinition* rootDef = branchIter->second;
Cary Clarka560c472017-11-27 10:44:06 -05001250 return rootDef;
1251 }
Cary Clark682c58d2018-05-16 07:07:07 -04001252 Definition* result = nullptr;
Cary Clarka560c472017-11-27 10:44:06 -05001253 for (const auto& branch : fBranches) {
Cary Clark682c58d2018-05-16 07:07:07 -04001254 RootDefinition* rootDef = branch.second;
Cary Clarka560c472017-11-27 10:44:06 -05001255 result = rootDef->find(ref, allowParens);
1256 if (result) {
1257 break;
1258 }
1259 }
1260 return result;
1261}