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