blob: e6707f6c55986a30fc2b1c5f993f721161b6f2df [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;
471 bool foundException = false;
Cary Clarka64e4ee2018-10-18 08:30:34 -0400472 bool foundPopulate = false;
Cary Clarka560c472017-11-27 10:44:06 -0500473 for (auto& child : fChildren) {
474 foundException |= MarkType::kDeprecated == child->fMarkType
475 || MarkType::kExperimental == child->fMarkType;
Cary Clarka64e4ee2018-10-18 08:30:34 -0400476 foundPopulate |= MarkType::kPopulate == child->fMarkType;
Cary Clarka560c472017-11-27 10:44:06 -0500477 if (MarkType::kReturn != child->fMarkType) {
478 if (MarkType::kParam == child->fMarkType) {
479 child->fVisited = false;
480 }
481 continue;
482 }
483 if (!expectReturn) {
484 return methodParser.reportError<bool>("no #Return expected");
485 }
486 if (foundReturn) {
487 return methodParser.reportError<bool>("multiple #Return markers");
488 }
489 foundReturn = true;
490 }
Cary Clarka64e4ee2018-10-18 08:30:34 -0400491 if (expectReturn && !foundReturn && !foundException && !foundPopulate) {
Cary Clarka560c472017-11-27 10:44:06 -0500492 return methodParser.reportError<bool>("missing #Return marker");
493 }
494 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
495 if (!paren) {
496 return methodParser.reportError<bool>("missing #Method function definition");
497 }
498 const char* nextEnd = paren;
499 do {
500 string paramName;
501 methodParser.fChar = nextEnd + 1;
502 methodParser.skipSpace();
503 if (!this->nextMethodParam(&methodParser, &nextEnd, &paramName)) {
504 continue;
505 }
506 bool foundParam = false;
507 for (auto& child : fChildren) {
508 if (MarkType::kParam != child->fMarkType) {
509 continue;
510 }
511 if (paramName != child->fName) {
512 continue;
513 }
514 if (child->fVisited) {
515 return methodParser.reportError<bool>("multiple #Method param with same name");
516 }
517 child->fVisited = true;
518 if (foundParam) {
519 TextParser paramError(child);
520 return methodParser.reportError<bool>("multiple #Param with same name");
521 }
522 foundParam = true;
523
524 }
Cary Clarka64e4ee2018-10-18 08:30:34 -0400525 if (!foundParam && !foundException && !foundPopulate) {
Cary Clarka560c472017-11-27 10:44:06 -0500526 return methodParser.reportError<bool>("no #Param found");
527 }
528 if (')' == nextEnd[0]) {
529 break;
530 }
531 } while (')' != nextEnd[0]);
532 for (auto& child : fChildren) {
533 if (MarkType::kParam != child->fMarkType) {
534 continue;
535 }
536 if (!child->fVisited) {
537 TextParser paramError(child);
538 return paramError.reportError<bool>("#Param without param in #Method");
539 }
540 }
Cary Clark80247e52018-07-11 16:18:41 -0400541 // check after end of #Line and before next child for description
542 const char* descStart = fContentStart;
543 const char* descEnd = nullptr;
Cary Clarkab5c9af2018-07-12 16:24:53 -0400544 const Definition* defEnd = nullptr;
545 const Definition* priorDef = nullptr;
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400546 bool incomplete = false;
Cary Clark80247e52018-07-11 16:18:41 -0400547 for (auto& child : fChildren) {
548 if (MarkType::kAnchor == child->fMarkType) {
549 continue;
550 }
551 if (MarkType::kCode == child->fMarkType) {
Cary Clarkab5c9af2018-07-12 16:24:53 -0400552 priorDef = child;
Cary Clark80247e52018-07-11 16:18:41 -0400553 continue;
554 }
555 if (MarkType::kDeprecated == child->fMarkType) {
556 return true;
557 }
558 if (MarkType::kExperimental == child->fMarkType) {
559 return true;
560 }
561 if (MarkType::kFormula == child->fMarkType) {
562 continue;
563 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400564 if (MarkType::kLine == child->fMarkType) {
565 SkASSERT(child->fChildren.size() > 0);
566 TextParser childDesc(child->fChildren[0]);
567 incomplete |= childDesc.startsWith("incomplete");
568 }
Cary Clark80247e52018-07-11 16:18:41 -0400569 if (MarkType::kList == child->fMarkType) {
Cary Clarkab5c9af2018-07-12 16:24:53 -0400570 priorDef = child;
Cary Clark80247e52018-07-11 16:18:41 -0400571 continue;
572 }
573 if (MarkType::kMarkChar == child->fMarkType) {
574 continue;
575 }
576 if (MarkType::kPhraseRef == child->fMarkType) {
577 continue;
578 }
579 if (MarkType::kPrivate == child->fMarkType) {
580 return true;
581 }
582 TextParser emptyCheck(fFileName, descStart, child->fStart, child->fLineCount);
583 if (!emptyCheck.eof() && emptyCheck.skipWhiteSpace()) {
584 descStart = emptyCheck.fChar;
585 emptyCheck.trimEnd();
Cary Clarkab5c9af2018-07-12 16:24:53 -0400586 defEnd = priorDef;
Cary Clark80247e52018-07-11 16:18:41 -0400587 descEnd = emptyCheck.fEnd;
588 break;
589 }
590 descStart = child->fTerminator;
Cary Clarkab5c9af2018-07-12 16:24:53 -0400591 priorDef = nullptr;
Cary Clark80247e52018-07-11 16:18:41 -0400592 }
593 if (!descEnd) {
Cary Clarka64e4ee2018-10-18 08:30:34 -0400594 return incomplete || foundPopulate ? true :
595 methodParser.reportError<bool>("missing description");
Cary Clark80247e52018-07-11 16:18:41 -0400596 }
597 TextParser description(fFileName, descStart, descEnd, fLineCount);
598 // expect first word capitalized and pluralized. expect a trailing period
599 SkASSERT(descStart < descEnd);
600 if (!isupper(descStart[0])) {
601 description.reportWarning("expected capital");
602 } else if ('.' != descEnd[-1]) {
Cary Clarkab5c9af2018-07-12 16:24:53 -0400603 if (!defEnd || defEnd->fTerminator != descEnd) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400604 if (!incomplete) {
605 description.reportWarning("expected period");
606 }
Cary Clarkab5c9af2018-07-12 16:24:53 -0400607 }
Cary Clark80247e52018-07-11 16:18:41 -0400608 } else {
609 if (!description.startsWith("For use by Android")) {
610 description.skipToSpace();
611 if (',' == description.fChar[-1]) {
612 --description.fChar;
613 }
614 if ('s' != description.fChar[-1]) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400615 if (!incomplete) {
616 description.reportWarning("expected plural");
617 }
Cary Clark80247e52018-07-11 16:18:41 -0400618 }
619 }
620 }
Cary Clarka560c472017-11-27 10:44:06 -0500621 return true;
622}
623
624bool Definition::crossCheck2(const Definition& includeToken) const {
625 TextParser parser(fFileName, fStart, fContentStart, fLineCount);
626 parser.skipExact("#");
627 bool isMethod = parser.skipName("Method");
628 const char* contentEnd;
629 if (isMethod) {
630 contentEnd = fContentStart;
631 } else if (parser.skipName("DefinedBy")) {
632 contentEnd = fContentEnd;
633 while (parser.fChar < contentEnd && ' ' >= contentEnd[-1]) {
634 --contentEnd;
635 }
636 if (parser.fChar < contentEnd - 1 && ')' == contentEnd[-1] && '(' == contentEnd[-2]) {
637 contentEnd -= 2;
638 }
639 } else {
640 return parser.reportError<bool>("unexpected crosscheck marktype");
641 }
642 return crossCheckInside(parser.fChar, contentEnd, includeToken);
643}
644
645bool Definition::crossCheck(const Definition& includeToken) const {
646 return crossCheckInside(fContentStart, fContentEnd, includeToken);
647}
648
Cary Clarkab5c9af2018-07-12 16:24:53 -0400649const char* Definition::methodEnd() const {
650 const char defaultTag[] = " = default";
651 size_t tagSize = sizeof(defaultTag) - 1;
652 const char* tokenEnd = fContentEnd - tagSize;
653 if (tokenEnd <= fContentStart || strncmp(tokenEnd, defaultTag, tagSize)) {
654 tokenEnd = fContentEnd;
655 }
656 return tokenEnd;
657}
658
Cary Clarka560c472017-11-27 10:44:06 -0500659bool Definition::crossCheckInside(const char* start, const char* end,
660 const Definition& includeToken) const {
661 TextParser def(fFileName, start, end, fLineCount);
Cary Clarkab5c9af2018-07-12 16:24:53 -0400662 const char* tokenEnd = includeToken.methodEnd();
663 TextParser inc("", includeToken.fContentStart, tokenEnd, 0);
Cary Clarka560c472017-11-27 10:44:06 -0500664 if (inc.startsWith("SK_API")) {
665 inc.skipWord("SK_API");
666 }
Cary Clark61313f32018-10-08 14:57:48 -0400667 if (inc.startsWith("inline")) {
668 inc.skipWord("inline");
669 }
Cary Clarka560c472017-11-27 10:44:06 -0500670 if (inc.startsWith("friend")) {
671 inc.skipWord("friend");
672 }
673 if (inc.startsWith("SK_API")) {
674 inc.skipWord("SK_API");
675 }
676 inc.skipExact("SkDEBUGCODE(");
677 do {
678 bool defEof;
679 bool incEof;
680 do {
681 defEof = def.eof() || !def.skipWhiteSpace();
682 incEof = inc.eof() || !inc.skipWhiteSpace();
683 if (!incEof && '/' == inc.peek() && (defEof || '/' != def.peek())) {
684 inc.next();
685 if ('*' == inc.peek()) {
686 inc.skipToEndBracket("*/");
687 inc.next();
688 } else if ('/' == inc.peek()) {
689 inc.skipToEndBracket('\n');
690 }
691 } else if (!incEof && '#' == inc.peek() && (defEof || '#' != def.peek())) {
692 inc.next();
693 if (inc.startsWith("if")) {
694 inc.skipToEndBracket("\n");
695 } else if (inc.startsWith("endif")) {
696 inc.skipToEndBracket("\n");
697 } else {
698 SkASSERT(0); // incomplete
699 return false;
700 }
701 } else {
702 break;
703 }
704 inc.next();
705 } while (true);
706 if (defEof || incEof) {
707 if (defEof == incEof || (!defEof && ';' == def.peek())) {
708 return true;
709 }
710 return false; // allow setting breakpoint on failure
711 }
712 char defCh;
713 do {
714 defCh = def.next();
Cary Clark61313f32018-10-08 14:57:48 -0400715 if (inc.skipExact("SK_WARN_UNUSED_RESULT")) {
716 inc.skipSpace();
717 }
Cary Clarka560c472017-11-27 10:44:06 -0500718 char incCh = inc.next();
719 if (' ' >= defCh && ' ' >= incCh) {
720 break;
721 }
722 if (defCh != incCh) {
723 if ('_' != defCh || ' ' != incCh || !fOperatorConst || !def.startsWith("const")) {
724 return false;
725 }
726 }
727 if (';' == defCh) {
728 return true;
729 }
730 } while (!def.eof() && !inc.eof());
731 } while (true);
732 return false;
733}
734
Cary Clark78de7512018-02-07 07:27:09 -0500735string Definition::formatFunction(Format format) const {
Cary Clarka560c472017-11-27 10:44:06 -0500736 const char* end = fContentStart;
737 while (end > fStart && ' ' >= end[-1]) {
738 --end;
739 }
740 TextParser methodParser(fFileName, fStart, end, fLineCount);
741 methodParser.skipWhiteSpace();
742 SkASSERT(methodParser.startsWith("#Method"));
743 methodParser.skipName("#Method");
744 methodParser.skipSpace();
745 const char* lastStart = methodParser.fChar;
746 const int limit = 100; // todo: allow this to be set by caller or in global or something
747 string name = this->methodName();
Cary Clarka560c472017-11-27 10:44:06 -0500748 const char* nameInParser = methodParser.strnstr(name.c_str(), methodParser.fEnd);
749 methodParser.skipTo(nameInParser);
750 const char* lastEnd = methodParser.fChar;
Cary Clark78de7512018-02-07 07:27:09 -0500751 if (Format::kOmitReturn == format) {
752 lastStart = lastEnd;
753 }
Cary Clarka560c472017-11-27 10:44:06 -0500754 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
755 size_t indent;
756 if (paren) {
757 indent = (size_t) (paren - lastStart) + 1;
758 } else {
759 indent = (size_t) (lastEnd - lastStart);
760 }
761 // trim indent so longest line doesn't exceed box width
Cary Clark186d08f2018-04-03 08:43:27 -0400762 TextParserSave savePlace(&methodParser);
Cary Clarka560c472017-11-27 10:44:06 -0500763 const char* saveStart = lastStart;
764 ptrdiff_t maxLine = 0;
765 do {
766 const char* nextStart = lastEnd;
767 const char* delimiter = methodParser.anyOf(",)");
768 const char* nextEnd = delimiter ? delimiter : methodParser.fEnd;
769 if (delimiter) {
770 while (nextStart < nextEnd && ' ' >= nextStart[0]) {
771 ++nextStart;
772 }
773 }
774 while (nextEnd > nextStart && ' ' >= nextEnd[-1]) {
775 --nextEnd;
776 }
777 if (delimiter) {
778 nextEnd += 1;
779 delimiter += 1;
780 }
781 if (lastEnd > lastStart) {
782 maxLine = SkTMax(maxLine, lastEnd - lastStart);
783 }
784 if (delimiter) {
785 methodParser.skipTo(delimiter);
786 }
787 lastStart = nextStart;
788 lastEnd = nextEnd;
789 } while (lastStart < lastEnd);
790 savePlace.restore();
791 lastStart = saveStart;
792 lastEnd = methodParser.fChar;
793 indent = SkTMin(indent, (size_t) (limit - maxLine));
Cary Clark682c58d2018-05-16 07:07:07 -0400794 // write string with trimmmed indent
Cary Clarka560c472017-11-27 10:44:06 -0500795 string methodStr;
796 int written = 0;
797 do {
798 const char* nextStart = lastEnd;
Cary Clark390495e2018-07-13 07:52:44 -0400799// SkASSERT(written < limit);
Cary Clarka560c472017-11-27 10:44:06 -0500800 const char* delimiter = methodParser.anyOf(",)");
801 const char* nextEnd = delimiter ? delimiter : methodParser.fEnd;
802 if (delimiter) {
803 while (nextStart < nextEnd && ' ' >= nextStart[0]) {
804 ++nextStart;
805 }
806 }
807 while (nextEnd > nextStart && ' ' >= nextEnd[-1]) {
808 --nextEnd;
809 }
810 if (delimiter) {
811 nextEnd += 1;
812 delimiter += 1;
813 }
814 if (lastEnd > lastStart) {
815 if (lastStart[0] != ' ') {
816 space_pad(&methodStr);
817 }
Cary Clark61313f32018-10-08 14:57:48 -0400818 string addon(lastStart, (size_t) (lastEnd - lastStart));
819 if ("_const" == addon) {
820 addon = "const";
821 }
822 methodStr += addon;
823 written += addon.length();
Cary Clarka560c472017-11-27 10:44:06 -0500824 }
825 if (delimiter) {
826 if (nextEnd - nextStart >= (ptrdiff_t) (limit - written)) {
827 written = indent;
Cary Clark78de7512018-02-07 07:27:09 -0500828 if (Format::kIncludeReturn == format) {
829 methodStr += '\n';
830 methodStr += string(indent, ' ');
831 }
Cary Clarka560c472017-11-27 10:44:06 -0500832 }
833 methodParser.skipTo(delimiter);
834 }
835 lastStart = nextStart;
836 lastEnd = nextEnd;
837 } while (lastStart < lastEnd);
838 return methodStr;
839}
840
841string Definition::fiddleName() const {
842 string result;
843 size_t start = 0;
844 string parent;
845 const Definition* parentDef = this;
846 while ((parentDef = parentDef->fParent)) {
847 if (MarkType::kClass == parentDef->fMarkType || MarkType::kStruct == parentDef->fMarkType) {
848 parent = parentDef->fFiddle;
849 break;
850 }
851 }
852 if (parent.length() && 0 == fFiddle.compare(0, parent.length(), parent)) {
853 start = parent.length();
854 while (start < fFiddle.length() && '_' == fFiddle[start]) {
855 ++start;
856 }
857 }
858 size_t end = fFiddle.find_first_of('(', start);
859 return fFiddle.substr(start, end - start);
860}
861
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400862string Definition::fileName() const {
863 size_t nameStart = fFileName.rfind(SkOSPath::SEPARATOR);
864 if (SkOSPath::SEPARATOR != '/') {
865 size_t altNameStart = fFileName.rfind('/');
866 nameStart = string::npos == nameStart ? altNameStart :
867 string::npos != altNameStart && altNameStart > nameStart ? altNameStart : nameStart;
868 }
869 SkASSERT(string::npos != nameStart);
870 string baseFile = fFileName.substr(nameStart + 1);
871 return baseFile;
872}
873
Cary Clark4855f782018-02-06 09:41:53 -0500874const Definition* Definition::findClone(string match) const {
875 for (auto child : fChildren) {
876 if (!child->fClone) {
877 continue;
878 }
879 if (match == child->fName) {
880 return child;
881 }
882 auto inner = child->findClone(match);
883 if (inner) {
884 return inner;
885 }
886 }
887 return nullptr;
888}
889
Cary Clarka560c472017-11-27 10:44:06 -0500890const Definition* Definition::hasChild(MarkType markType) const {
891 for (auto iter : fChildren) {
892 if (markType == iter->fMarkType) {
893 return iter;
894 }
895 }
896 return nullptr;
897}
898
Cary Clarka64e4ee2018-10-18 08:30:34 -0400899Definition* Definition::hasParam(string ref) {
Cary Clarka560c472017-11-27 10:44:06 -0500900 SkASSERT(MarkType::kMethod == fMarkType);
901 for (auto iter : fChildren) {
902 if (MarkType::kParam != iter->fMarkType) {
903 continue;
904 }
905 if (iter->fName == ref) {
906 return &*iter;
907 }
Cary Clarka64e4ee2018-10-18 08:30:34 -0400908 }
909 for (auto& iter : fTokens) {
910 if (MarkType::kComment != iter.fMarkType) {
911 continue;
912 }
913 TextParser parser(&iter);
914 if (!parser.skipExact("@param ")) {
915 continue;
916 }
Cary Clark09d80c02018-10-31 12:14:03 -0400917 if (parser.skipExact(ref.c_str()) && ' ' == parser.peek()) {
Cary Clarka64e4ee2018-10-18 08:30:34 -0400918 return &iter;
919 }
Cary Clarka560c472017-11-27 10:44:06 -0500920 }
921 return nullptr;
922}
923
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400924bool Definition::hasMatch(string name) const {
Cary Clarkac47b882018-01-11 10:35:44 -0500925 for (auto child : fChildren) {
926 if (name == child->fName) {
927 return true;
928 }
929 if (child->hasMatch(name)) {
930 return true;
931 }
932 }
933 return false;
934}
935
Cary Clark682c58d2018-05-16 07:07:07 -0400936string Definition::incompleteMessage(DetailsType detailsType) const {
Cary Clark137b8742018-05-30 09:21:49 -0400937 SkASSERT(!IncompleteAllowed(fMarkType));
938 auto iter = std::find_if(fChildren.begin(), fChildren.end(),
939 [](const Definition* test) { return IncompleteAllowed(test->fMarkType); });
940 SkASSERT(fChildren.end() != iter);
941 SkASSERT(Details::kNone == (*iter)->fDetails);
942 string message = MarkType::kExperimental == (*iter)->fMarkType ?
Cary Clark682c58d2018-05-16 07:07:07 -0400943 "Experimental." : "Deprecated.";
Cary Clark137b8742018-05-30 09:21:49 -0400944 if (Details::kDoNotUse_Experiment == fDetails) {
Cary Clark682c58d2018-05-16 07:07:07 -0400945 message += " Do not use.";
Cary Clark137b8742018-05-30 09:21:49 -0400946 } else if (Details::kNotReady_Experiment == fDetails) {
Cary Clark682c58d2018-05-16 07:07:07 -0400947 message += " Not ready for general use.";
Cary Clark137b8742018-05-30 09:21:49 -0400948 } else if (Details::kSoonToBe_Deprecated == fDetails) {
949 message = "To be deprecated soon.";
950 } else if (Details::kTestingOnly_Experiment == fDetails) {
Cary Clark682c58d2018-05-16 07:07:07 -0400951 message += " For testing only.";
952 }
953 if (DetailsType::kPhrase == detailsType) {
954 message = message.substr(0, message.length() - 1); // remove trailing period
955 std::replace(message.begin(), message.end(), '.', ':');
956 std::transform(message.begin(), message.end(), message.begin(), ::tolower);
957 }
958 return message;
959}
960
Cary Clarkab2621d2018-01-30 10:08:57 -0500961bool Definition::isStructOrClass() const {
962 if (MarkType::kStruct != fMarkType && MarkType::kClass != fMarkType) {
963 return false;
964 }
965 if (string::npos != fFileName.find("undocumented.bmh")) {
966 return false;
967 }
968 return true;
969}
970
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400971bool Definition::methodHasReturn(string name, TextParser* methodParser) const {
Cary Clarka560c472017-11-27 10:44:06 -0500972 if (methodParser->skipExact("static")) {
973 methodParser->skipWhiteSpace();
974 }
Cary Clark80247e52018-07-11 16:18:41 -0400975 if (methodParser->skipExact("virtual")) {
976 methodParser->skipWhiteSpace();
977 }
Cary Clarka560c472017-11-27 10:44:06 -0500978 const char* lastStart = methodParser->fChar;
979 const char* nameInParser = methodParser->strnstr(name.c_str(), methodParser->fEnd);
980 methodParser->skipTo(nameInParser);
981 const char* lastEnd = methodParser->fChar;
982 const char* returnEnd = lastEnd;
983 while (returnEnd > lastStart && ' ' == returnEnd[-1]) {
984 --returnEnd;
985 }
986 bool expectReturn = 4 != returnEnd - lastStart || strncmp("void", lastStart, 4);
987 if (MethodType::kNone != fMethodType && MethodType::kOperator != fMethodType && !expectReturn) {
988 return methodParser->reportError<bool>("unexpected void");
989 }
990 switch (fMethodType) {
991 case MethodType::kNone:
992 case MethodType::kOperator:
993 // either is fine
994 break;
995 case MethodType::kConstructor:
996 expectReturn = true;
997 break;
998 case MethodType::kDestructor:
999 expectReturn = false;
1000 break;
1001 }
1002 return expectReturn;
1003}
1004
1005string Definition::methodName() const {
1006 string result;
1007 size_t start = 0;
1008 string parent;
1009 const Definition* parentDef = this;
1010 while ((parentDef = parentDef->fParent)) {
1011 if (MarkType::kClass == parentDef->fMarkType || MarkType::kStruct == parentDef->fMarkType) {
1012 parent = parentDef->fName;
1013 break;
1014 }
1015 }
1016 if (parent.length() && 0 == fName.compare(0, parent.length(), parent)) {
1017 start = parent.length();
1018 while (start < fName.length() && ':' == fName[start]) {
1019 ++start;
1020 }
1021 }
1022 if (fClone) {
1023 int lastUnder = fName.rfind('_');
1024 return fName.substr(start, (size_t) (lastUnder - start));
1025 }
1026 size_t end = fName.find_first_of('(', start);
1027 if (string::npos == end) {
1028 return fName.substr(start);
1029 }
1030 return fName.substr(start, end - start);
1031}
1032
1033bool Definition::nextMethodParam(TextParser* methodParser, const char** nextEndPtr,
1034 string* paramName) const {
1035 int parenCount = 0;
Cary Clark186d08f2018-04-03 08:43:27 -04001036 TextParserSave saveState(methodParser);
Cary Clarka560c472017-11-27 10:44:06 -05001037 while (true) {
1038 if (methodParser->eof()) {
1039 return methodParser->reportError<bool>("#Method function missing close paren");
1040 }
1041 char ch = methodParser->peek();
1042 if ('(' == ch) {
1043 ++parenCount;
1044 }
1045 if (parenCount == 0 && (')' == ch || ',' == ch)) {
1046 *nextEndPtr = methodParser->fChar;
1047 break;
1048 }
1049 if (')' == ch) {
1050 if (0 > --parenCount) {
1051 return this->reportError<bool>("mismatched parentheses");
1052 }
1053 }
1054 methodParser->next();
1055 }
1056 saveState.restore();
1057 const char* nextEnd = *nextEndPtr;
1058 const char* paramEnd = nextEnd;
1059 const char* assign = methodParser->strnstr(" = ", paramEnd);
1060 if (assign) {
1061 paramEnd = assign;
1062 }
1063 const char* closeBracket = methodParser->strnstr("]", paramEnd);
1064 if (closeBracket) {
1065 const char* openBracket = methodParser->strnstr("[", paramEnd);
1066 if (openBracket && openBracket < closeBracket) {
1067 while (openBracket < --closeBracket && isdigit(closeBracket[0]))
1068 ;
1069 if (openBracket == closeBracket) {
1070 paramEnd = openBracket;
1071 }
1072 }
1073 }
1074 const char* function = methodParser->strnstr(")(", paramEnd);
1075 if (function) {
1076 paramEnd = function;
1077 }
1078 while (paramEnd > methodParser->fChar && ' ' == paramEnd[-1]) {
1079 --paramEnd;
1080 }
1081 const char* paramStart = paramEnd;
1082 while (paramStart > methodParser->fChar && isalnum(paramStart[-1])) {
1083 --paramStart;
1084 }
1085 if (paramStart > methodParser->fChar && paramStart >= paramEnd) {
1086 return methodParser->reportError<bool>("#Method missing param name");
1087 }
1088 *paramName = string(paramStart, paramEnd - paramStart);
1089 if (!paramName->length()) {
1090 if (')' != nextEnd[0]) {
1091 return methodParser->reportError<bool>("#Method malformed param");
1092 }
1093 return false;
1094 }
1095 return true;
1096}
1097
1098string Definition::NormalizedName(string name) {
1099 string normalizedName = name;
1100 std::replace(normalizedName.begin(), normalizedName.end(), '-', '_');
1101 do {
1102 size_t doubleColon = normalizedName.find("::", 0);
1103 if (string::npos == doubleColon) {
1104 break;
1105 }
1106 normalizedName = normalizedName.substr(0, doubleColon)
1107 + '_' + normalizedName.substr(doubleColon + 2);
1108 } while (true);
1109 return normalizedName;
1110}
1111
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001112static string unpreformat(string orig) {
Cary Clark78de7512018-02-07 07:27:09 -05001113 string result;
1114 int amp = 0;
1115 for (auto c : orig) {
1116 switch (amp) {
1117 case 0:
1118 if ('&' == c) {
1119 amp = 1;
1120 } else {
1121 amp = 0;
1122 result += c;
1123 }
1124 break;
1125 case 1:
1126 if ('l' == c) {
1127 amp = 2;
1128 } else if ('g' == c) {
1129 amp = 3;
1130 } else {
1131 amp = 0;
1132 result += "&";
1133 result += c;
1134 }
1135 break;
1136 case 2:
1137 if ('t' == c) {
1138 amp = 4;
1139 } else {
1140 amp = 0;
1141 result += "&l";
1142 result += c;
1143 }
1144 break;
1145 case 3:
1146 if ('t' == c) {
1147 amp = 5;
1148 } else {
1149 amp = 0;
1150 result += "&g";
1151 result += c;
1152 }
1153 break;
1154 case 4:
1155 if (';' == c) {
1156 result += '<';
1157 } else {
1158 result += "&lt";
1159 result += c;
1160 }
1161 amp = 0;
1162 break;
1163 case 5:
1164 if (';' == c) {
1165 result += '>';
1166 } else {
1167 result += "&gt";
1168 result += c;
1169 }
1170 amp = 0;
1171 break;
1172 }
1173 }
1174 return result;
1175}
1176
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001177bool Definition::paramsMatch(string matchFormatted, string name) const {
Cary Clark78de7512018-02-07 07:27:09 -05001178 string match = unpreformat(matchFormatted);
Cary Clarka560c472017-11-27 10:44:06 -05001179 TextParser def(fFileName, fStart, fContentStart, fLineCount);
1180 const char* dName = def.strnstr(name.c_str(), fContentStart);
1181 if (!dName) {
1182 return false;
1183 }
1184 def.skipTo(dName);
1185 TextParser m(fFileName, &match.front(), &match.back() + 1, fLineCount);
1186 const char* mName = m.strnstr(name.c_str(), m.fEnd);
1187 if (!mName) {
1188 return false;
1189 }
1190 m.skipTo(mName);
1191 while (!def.eof() && ')' != def.peek() && !m.eof() && ')' != m.peek()) {
1192 const char* ds = def.fChar;
1193 const char* ms = m.fChar;
1194 const char* de = def.anyOf(") \n");
1195 const char* me = m.anyOf(") \n");
1196 def.skipTo(de);
1197 m.skipTo(me);
1198 if (def.fChar - ds != m.fChar - ms) {
1199 return false;
1200 }
1201 if (strncmp(ds, ms, (int) (def.fChar - ds))) {
1202 return false;
1203 }
1204 def.skipWhiteSpace();
1205 m.skipWhiteSpace();
1206 }
1207 return !def.eof() && ')' == def.peek() && !m.eof() && ')' == m.peek();
1208}
1209
Cary Clark2be81cf2018-09-13 12:04:30 -04001210
1211void Definition::trimEnd() {
1212 while (fContentEnd > fContentStart && ' ' >= fContentEnd[-1]) {
1213 --fContentEnd;
1214 }
1215}
1216
Cary Clarka560c472017-11-27 10:44:06 -05001217void RootDefinition::clearVisited() {
1218 fVisited = false;
1219 for (auto& leaf : fLeaves) {
1220 leaf.second.fVisited = false;
1221 }
1222 for (auto& branch : fBranches) {
1223 branch.second->clearVisited();
1224 }
1225}
1226
Cary Clarkf5404bb2018-01-05 12:10:09 -05001227bool RootDefinition::dumpUnVisited() {
1228 bool success = true;
Cary Clarka560c472017-11-27 10:44:06 -05001229 for (auto& leaf : fLeaves) {
1230 if (!leaf.second.fVisited) {
Cary Clarka560c472017-11-27 10:44:06 -05001231 // FIXME: bugs requiring long tail fixes, suppressed here:
1232 // SkBitmap::validate() is wrapped in SkDEBUGCODE in .h and not parsed
1233 if ("SkBitmap::validate()" == leaf.first) {
1234 continue;
1235 }
Cary Clarka560c472017-11-27 10:44:06 -05001236 // SkPath::pathRefIsValid in #ifdef ; prefer to remove chrome dependency to fix
1237 if ("SkPath::pathRefIsValid" == leaf.first) {
1238 continue;
1239 }
1240 // FIXME: end of long tail bugs
1241 SkDebugf("defined in bmh but missing in include: %s\n", leaf.first.c_str());
Cary Clarkf5404bb2018-01-05 12:10:09 -05001242 success = false;
Cary Clarka560c472017-11-27 10:44:06 -05001243 }
1244 }
1245 for (auto& branch : fBranches) {
Cary Clarkf5404bb2018-01-05 12:10:09 -05001246 success &= branch.second->dumpUnVisited();
Cary Clarka560c472017-11-27 10:44:06 -05001247 }
Cary Clarkf5404bb2018-01-05 12:10:09 -05001248 return success;
Cary Clarka560c472017-11-27 10:44:06 -05001249}
1250
Cary Clark682c58d2018-05-16 07:07:07 -04001251Definition* RootDefinition::find(string ref, AllowParens allowParens) {
Cary Clarka560c472017-11-27 10:44:06 -05001252 const auto leafIter = fLeaves.find(ref);
1253 if (leafIter != fLeaves.end()) {
1254 return &leafIter->second;
1255 }
Cary Clarka8cdc172018-09-07 14:17:08 -04001256 if (AllowParens::kYes == allowParens) {
1257 size_t leftParen = ref.find('(');
1258 if (string::npos == leftParen
1259 || (leftParen + 1 < ref.length() && ')' != ref[leftParen + 1])) {
1260 string withParens = ref + "()";
1261 const auto parensIter = fLeaves.find(withParens);
1262 if (parensIter != fLeaves.end()) {
1263 return &parensIter->second;
1264 }
1265 }
1266 if (string::npos != leftParen) {
1267 string name = ref.substr(0, leftParen);
1268 size_t posInDefName = fName.find(name);
1269 if (string::npos != posInDefName && posInDefName > 2
1270 && "::" == fName.substr(posInDefName - 2, 2)) {
1271 string fullRef = fName + "::" + ref;
1272 const auto fullIter = fLeaves.find(fullRef);
1273 if (fullIter != fLeaves.end()) {
1274 return &fullIter->second;
1275 }
1276 }
Cary Clarka560c472017-11-27 10:44:06 -05001277 }
1278 }
1279 const auto branchIter = fBranches.find(ref);
1280 if (branchIter != fBranches.end()) {
Cary Clark682c58d2018-05-16 07:07:07 -04001281 RootDefinition* rootDef = branchIter->second;
Cary Clarka560c472017-11-27 10:44:06 -05001282 return rootDef;
1283 }
Cary Clark682c58d2018-05-16 07:07:07 -04001284 Definition* result = nullptr;
Cary Clarka560c472017-11-27 10:44:06 -05001285 for (const auto& branch : fBranches) {
Cary Clark682c58d2018-05-16 07:07:07 -04001286 RootDefinition* rootDef = branch.second;
Cary Clarka560c472017-11-27 10:44:06 -05001287 result = rootDef->find(ref, allowParens);
1288 if (result) {
1289 break;
1290 }
1291 }
1292 return result;
1293}