1 /*
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 "SkOSPath.h"
9
10 #include "definition.h"
11 #include "textParser.h"
12
13 #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
39 enum class OpType : int8_t {
40 kNone,
41 kVoid,
42 kBool,
43 kChar,
44 kInt,
45 kScalar,
46 kSizeT,
47 kThis,
48 kAny,
49 };
50
51 enum class OpMod : int8_t {
52 kNone,
53 kArray,
54 kMove,
55 kPointer,
56 kReference,
57 kAny,
58 };
59
60 const 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, }}},
121 { 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,
124 {{ 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
lookup_type(string typeWord,string name)145 OpType lookup_type(string typeWord, string name) {
146 if (typeWord == name || (typeWord == "SkIVector" && name == "SkIPoint")
147 || (typeWord == "SkVector" && name == "SkPoint")) {
148 return OpType::kThis;
149 }
150 if ("float" == typeWord || "double" == typeWord) {
151 return OpType::kScalar;
152 }
153 const char* keyWords[] = { "void", "bool", "char", "int", "SkScalar", "size_t" };
154 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
lookup_mod(TextParser & iParser)162 OpMod 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
parseOperator(size_t doubleColons,string & result)179 bool 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.skipWhiteSpace();
189 bool isStatic = iParser.skipExact("static");
190 iParser.skipWhiteSpace();
191 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;
198 iParser.skipToNonName();
199 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;
207 TextParserSave save(&iParser);
208 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;
248 iParser.skipToNonName();
249 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 }
264 iParser.skipToNonName();
265 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 }
291
292 #undef CONST
293 #undef FRIEND
294 #undef BLANK
295 #undef DEFOP
296
boilerplateIfDef()297 bool Definition::boilerplateIfDef() {
298 const Definition& label = fTokens.front();
299 if (Type::kWord != label.fType) {
300 return false;
301 }
302 fName = string(label.fContentStart, label.fContentEnd - label.fContentStart);
303 return true;
304 }
305
306
307 // fixme: this will need to be more complicated to handle all of Skia
308 // for now, just handle paint -- maybe fiddle will loosen naming restrictions
setCanonicalFiddle()309 void Definition::setCanonicalFiddle() {
310 fMethodType = Definition::MethodType::kNone;
311 size_t doubleColons = fName.rfind("::");
312 SkASSERT(string::npos != doubleColons);
313 string base = fName.substr(0, doubleColons);
314 string result = base + "_";
315 doubleColons += 2;
316 if (string::npos != fName.find('~', doubleColons)) {
317 fMethodType = Definition::MethodType::kDestructor;
318 result += "destructor";
319 } else if (!this->parseOperator(doubleColons, result)) {
320 bool isMove = string::npos != fName.find("&&", doubleColons);
321 size_t parens = fName.find("()", doubleColons);
322 if (string::npos != parens) {
323 string methodName = fName.substr(doubleColons, parens - doubleColons);
324 do {
325 size_t nextDouble = methodName.find("::");
326 if (string::npos == nextDouble) {
327 break;
328 }
329 base = methodName.substr(0, nextDouble);
330 result += base + '_';
331 methodName = methodName.substr(nextDouble + 2);
332 doubleColons += nextDouble + 2;
333 } while (true);
334 if (base == methodName) {
335 fMethodType = Definition::MethodType::kConstructor;
336 result += "empty_constructor";
337 } else {
338 result += fName.substr(doubleColons, fName.length() - doubleColons - 2);
339 }
340 } else {
341 size_t openParen = fName.find('(', doubleColons);
342 if (string::npos == openParen) {
343 result += fName.substr(doubleColons);
344 // see if it is a constructor -- if second to last delimited name equals last
345 size_t nextColons = fName.find("::", doubleColons);
346 if (string::npos != nextColons) {
347 nextColons += 2;
348 if (!strncmp(&fName[doubleColons], &fName[nextColons],
349 nextColons - doubleColons - 2)) {
350 fMethodType = Definition::MethodType::kConstructor;
351 }
352 }
353 } else {
354 size_t comma = fName.find(',', doubleColons);
355 if (string::npos == comma) {
356 result += isMove ? "move_" : "copy_";
357 }
358 fMethodType = Definition::MethodType::kConstructor;
359 // name them by their param types,
360 // e.g. SkCanvas__int_int_const_SkSurfaceProps_star
361 // TODO: move forward until parens are balanced and terminator =,)
362 TextParser params("", &fName[openParen] + 1, &*fName.end(), 0);
363 bool underline = false;
364 while (!params.eof()) {
365 // SkDEBUGCODE(const char* end = params.anyOf("(),=")); // unused for now
366 // SkASSERT(end[0] != '('); // fixme: put off handling nested parentheseses
367 if (params.startsWith("const") || params.startsWith("int")
368 || params.startsWith("Sk")) {
369 const char* wordStart = params.fChar;
370 params.skipToNonName();
371 if (underline) {
372 result += '_';
373 } else {
374 underline = true;
375 }
376 result += string(wordStart, params.fChar - wordStart);
377 } else {
378 params.skipToNonName();
379 }
380 if (!params.eof() && '*' == params.peek()) {
381 if (underline) {
382 result += '_';
383 } else {
384 underline = true;
385 }
386 result += "star";
387 params.next();
388 params.skipSpace();
389 }
390 params.skipToAlpha();
391 }
392 }
393 }
394 }
395 fFiddle = Definition::NormalizedName(result);
396 }
397
space_pad(string * str)398 static void space_pad(string* str) {
399 size_t len = str->length();
400 if (len == 0) {
401 return;
402 }
403 char last = (*str)[len - 1];
404 if ('~' == last || ' ' >= last) {
405 return;
406 }
407 *str += ' ';
408 }
409
410 //start here;
411 // see if it possible to abstract this a little bit so it can
412 // additionally be used to find params and return in method prototype that
413 // does not have corresponding doxygen comments
checkMethod() const414 bool Definition::checkMethod() const {
415 SkASSERT(MarkType::kMethod == fMarkType);
416 // if method returns a value, look for a return child
417 // for each parameter, look for a corresponding child
418 const char* end = fContentStart;
419 while (end > fStart && ' ' >= end[-1]) {
420 --end;
421 }
422 TextParser methodParser(fFileName, fStart, end, fLineCount);
423 methodParser.skipWhiteSpace();
424 SkASSERT(methodParser.startsWith("#Method"));
425 methodParser.skipName("#Method");
426 methodParser.skipSpace();
427 string name = this->methodName();
428 if (MethodType::kNone == fMethodType && name.length() > 2 &&
429 "()" == name.substr(name.length() - 2)) {
430 name = name.substr(0, name.length() - 2);
431 }
432 bool expectReturn = this->methodHasReturn(name, &methodParser);
433 bool foundReturn = false;
434 bool foundPopulate = false;
435 for (auto& child : fChildren) {
436 foundPopulate |= MarkType::kPopulate == child->fMarkType;
437 if (MarkType::kReturn != child->fMarkType) {
438 if (MarkType::kParam == child->fMarkType) {
439 child->fVisited = false;
440 }
441 continue;
442 }
443 if (!expectReturn) {
444 return methodParser.reportError<bool>("no #Return expected");
445 }
446 if (foundReturn) {
447 return methodParser.reportError<bool>("multiple #Return markers");
448 }
449 foundReturn = true;
450 }
451 if (expectReturn && !foundReturn && !foundPopulate) {
452 return methodParser.reportError<bool>("missing #Return marker");
453 }
454 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
455 if (!paren) {
456 return methodParser.reportError<bool>("missing #Method function definition");
457 }
458 const char* nextEnd = paren;
459 do {
460 string paramName;
461 methodParser.fChar = nextEnd + 1;
462 methodParser.skipSpace();
463 if (!this->nextMethodParam(&methodParser, &nextEnd, ¶mName)) {
464 continue;
465 }
466 bool foundParam = false;
467 for (auto& child : fChildren) {
468 if (MarkType::kParam != child->fMarkType) {
469 continue;
470 }
471 if (paramName != child->fName) {
472 continue;
473 }
474 if (child->fVisited) {
475 return methodParser.reportError<bool>("multiple #Method param with same name");
476 }
477 child->fVisited = true;
478 if (foundParam) {
479 TextParser paramError(child);
480 return methodParser.reportError<bool>("multiple #Param with same name");
481 }
482 foundParam = true;
483
484 }
485 if (!foundParam && !foundPopulate) {
486 return methodParser.reportError<bool>("no #Param found");
487 }
488 if (')' == nextEnd[0]) {
489 break;
490 }
491 } while (')' != nextEnd[0]);
492 for (auto& child : fChildren) {
493 if (MarkType::kParam != child->fMarkType) {
494 continue;
495 }
496 if (!child->fVisited) {
497 TextParser paramError(child);
498 return paramError.reportError<bool>("#Param without param in #Method");
499 }
500 }
501 // check after end of #Line and before next child for description
502 const char* descStart = fContentStart;
503 const char* descEnd = nullptr;
504 const Definition* defEnd = nullptr;
505 const Definition* priorDef = nullptr;
506 bool incomplete = false;
507 for (auto& child : fChildren) {
508 if (MarkType::kAnchor == child->fMarkType) {
509 continue;
510 }
511 if (MarkType::kCode == child->fMarkType) {
512 priorDef = child;
513 continue;
514 }
515 if (MarkType::kFormula == child->fMarkType) {
516 continue;
517 }
518 if (MarkType::kLine == child->fMarkType) {
519 SkASSERT(child->fChildren.size() > 0);
520 TextParser childDesc(child->fChildren[0]);
521 incomplete |= childDesc.startsWith("incomplete");
522 }
523 if (MarkType::kList == child->fMarkType) {
524 priorDef = child;
525 continue;
526 }
527 if (MarkType::kMarkChar == child->fMarkType) {
528 continue;
529 }
530 if (MarkType::kPhraseRef == child->fMarkType) {
531 continue;
532 }
533 TextParser emptyCheck(fFileName, descStart, child->fStart, child->fLineCount);
534 if (!emptyCheck.eof() && emptyCheck.skipWhiteSpace()) {
535 descStart = emptyCheck.fChar;
536 emptyCheck.trimEnd();
537 defEnd = priorDef;
538 descEnd = emptyCheck.fEnd;
539 break;
540 }
541 descStart = child->fTerminator;
542 priorDef = nullptr;
543 }
544 if (!descEnd) {
545 return incomplete || foundPopulate ? true :
546 methodParser.reportError<bool>("missing description");
547 }
548 TextParser description(fFileName, descStart, descEnd, fLineCount);
549 // expect first word capitalized and pluralized. expect a trailing period
550 SkASSERT(descStart < descEnd);
551 if (!isupper(descStart[0])) {
552 description.reportWarning("expected capital");
553 } else if ('.' != descEnd[-1]) {
554 if (!defEnd || defEnd->fTerminator != descEnd) {
555 if (!incomplete) {
556 description.reportWarning("expected period");
557 }
558 }
559 } else {
560 if (!description.startsWith("For use by Android")) {
561 description.skipToSpace();
562 if (',' == description.fChar[-1]) {
563 --description.fChar;
564 }
565 if ('s' != description.fChar[-1]) {
566 if (!incomplete) {
567 description.reportWarning("expected plural");
568 }
569 }
570 }
571 }
572 return true;
573 }
574
crossCheck2(const Definition & includeToken) const575 bool Definition::crossCheck2(const Definition& includeToken) const {
576 TextParser parser(fFileName, fStart, fContentStart, fLineCount);
577 parser.skipExact("#");
578 bool isMethod = parser.skipName("Method");
579 const char* contentEnd;
580 if (isMethod) {
581 contentEnd = fContentStart;
582 } else if (parser.skipName("DefinedBy")) {
583 contentEnd = fContentEnd;
584 while (parser.fChar < contentEnd && ' ' >= contentEnd[-1]) {
585 --contentEnd;
586 }
587 if (parser.fChar < contentEnd - 1 && ')' == contentEnd[-1] && '(' == contentEnd[-2]) {
588 contentEnd -= 2;
589 }
590 } else {
591 return parser.reportError<bool>("unexpected crosscheck marktype");
592 }
593 return crossCheckInside(parser.fChar, contentEnd, includeToken);
594 }
595
crossCheck(const Definition & includeToken) const596 bool Definition::crossCheck(const Definition& includeToken) const {
597 return crossCheckInside(fContentStart, fContentEnd, includeToken);
598 }
599
methodEnd() const600 const char* Definition::methodEnd() const {
601 const char defaultTag[] = " = default";
602 size_t tagSize = sizeof(defaultTag) - 1;
603 const char* tokenEnd = fContentEnd - tagSize;
604 if (tokenEnd <= fContentStart || strncmp(tokenEnd, defaultTag, tagSize)) {
605 tokenEnd = fContentEnd;
606 }
607 return tokenEnd;
608 }
609
SkipImplementationWords(TextParser & inc)610 bool Definition::SkipImplementationWords(TextParser& inc) {
611 if (inc.startsWith("SK_API")) {
612 inc.skipWord("SK_API");
613 }
614 if (inc.startsWith("inline")) {
615 inc.skipWord("inline");
616 }
617 if (inc.startsWith("friend")) {
618 inc.skipWord("friend");
619 }
620 if (inc.startsWith("SK_API")) {
621 inc.skipWord("SK_API");
622 }
623 return inc.skipExact("SkDEBUGCODE(");
624 }
625
crossCheckInside(const char * start,const char * end,const Definition & includeToken) const626 bool Definition::crossCheckInside(const char* start, const char* end,
627 const Definition& includeToken) const {
628 TextParser def(fFileName, start, end, fLineCount);
629 const char* tokenEnd = includeToken.methodEnd();
630 TextParser inc("", includeToken.fContentStart, tokenEnd, 0);
631 if (inc.startsWith("static")) {
632 def.skipWhiteSpace();
633 if (!def.startsWith("static")) {
634 return false;
635 }
636 inc.skipWord("static");
637 def.skipWord("static");
638 }
639 (void) Definition::SkipImplementationWords(inc);
640 do {
641 bool defEof;
642 bool incEof;
643 do {
644 defEof = def.eof() || !def.skipWhiteSpace();
645 incEof = inc.eof() || !inc.skipWhiteSpace();
646 if (!incEof && '/' == inc.peek() && (defEof || '/' != def.peek())) {
647 inc.next();
648 if ('*' == inc.peek()) {
649 inc.skipToEndBracket("*/");
650 inc.next();
651 } else if ('/' == inc.peek()) {
652 inc.skipToEndBracket('\n');
653 }
654 } else if (!incEof && '#' == inc.peek() && (defEof || '#' != def.peek())) {
655 inc.next();
656 if (inc.startsWith("if")) {
657 inc.skipToEndBracket("\n");
658 } else if (inc.startsWith("endif")) {
659 inc.skipToEndBracket("\n");
660 } else {
661 SkASSERT(0); // incomplete
662 return false;
663 }
664 } else {
665 break;
666 }
667 inc.next();
668 } while (true);
669 if (defEof || incEof) {
670 if (defEof == incEof || (!defEof && ';' == def.peek())) {
671 return true;
672 }
673 return false; // allow setting breakpoint on failure
674 }
675 char defCh;
676 do {
677 defCh = def.next();
678 if (inc.skipExact("SK_WARN_UNUSED_RESULT")) {
679 inc.skipSpace();
680 }
681 char incCh = inc.next();
682 if (' ' >= defCh && ' ' >= incCh) {
683 break;
684 }
685 if (defCh != incCh) {
686 if ('_' != defCh || ' ' != incCh || !fOperatorConst || !def.startsWith("const")) {
687 return false;
688 }
689 }
690 if (';' == defCh) {
691 return true;
692 }
693 } while (!def.eof() && !inc.eof());
694 } while (true);
695 return false;
696 }
697
formatFunction(Format format) const698 string Definition::formatFunction(Format format) const {
699 const char* end = fContentStart;
700 while (end > fStart && ' ' >= end[-1]) {
701 --end;
702 }
703 TextParser methodParser(fFileName, fStart, end, fLineCount);
704 methodParser.skipWhiteSpace();
705 SkASSERT(methodParser.startsWith("#Method"));
706 methodParser.skipName("#Method");
707 methodParser.skipSpace();
708 const char* lastStart = methodParser.fChar;
709 const int limit = 100; // todo: allow this to be set by caller or in global or something
710 string name = this->methodName();
711 const char* nameInParser = methodParser.strnstr(name.c_str(), methodParser.fEnd);
712 methodParser.skipTo(nameInParser);
713 const char* lastEnd = methodParser.fChar;
714 if (Format::kOmitReturn == format) {
715 lastStart = lastEnd;
716 }
717 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
718 size_t indent;
719 if (paren) {
720 indent = (size_t) (paren - lastStart) + 1;
721 } else {
722 indent = (size_t) (lastEnd - lastStart);
723 }
724 // trim indent so longest line doesn't exceed box width
725 TextParserSave savePlace(&methodParser);
726 const char* saveStart = lastStart;
727 ptrdiff_t maxLine = 0;
728 do {
729 const char* nextStart = lastEnd;
730 const char* delimiter = methodParser.anyOf(",)");
731 const char* nextEnd = delimiter ? delimiter : methodParser.fEnd;
732 if (delimiter) {
733 while (nextStart < nextEnd && ' ' >= nextStart[0]) {
734 ++nextStart;
735 }
736 }
737 while (nextEnd > nextStart && ' ' >= nextEnd[-1]) {
738 --nextEnd;
739 }
740 if (delimiter) {
741 nextEnd += 1;
742 delimiter += 1;
743 }
744 if (lastEnd > lastStart) {
745 maxLine = SkTMax(maxLine, lastEnd - lastStart);
746 }
747 if (delimiter) {
748 methodParser.skipTo(delimiter);
749 }
750 lastStart = nextStart;
751 lastEnd = nextEnd;
752 } while (lastStart < lastEnd);
753 savePlace.restore();
754 lastStart = saveStart;
755 lastEnd = methodParser.fChar;
756 indent = SkTMin(indent, (size_t) (limit - maxLine));
757 // write string with trimmmed indent
758 string methodStr;
759 int written = 0;
760 do {
761 const char* nextStart = lastEnd;
762 // SkASSERT(written < limit);
763 const char* delimiter = methodParser.anyOf(",)");
764 const char* nextEnd = delimiter ? delimiter : methodParser.fEnd;
765 if (delimiter) {
766 while (nextStart < nextEnd && ' ' >= nextStart[0]) {
767 ++nextStart;
768 }
769 }
770 while (nextEnd > nextStart && ' ' >= nextEnd[-1]) {
771 --nextEnd;
772 }
773 if (delimiter) {
774 nextEnd += 1;
775 delimiter += 1;
776 }
777 if (lastEnd > lastStart) {
778 if (lastStart[0] != ' ') {
779 space_pad(&methodStr);
780 }
781 string addon(lastStart, (size_t) (lastEnd - lastStart));
782 if (" const" == addon) {
783 addon = "const";
784 }
785 methodStr += addon;
786 written += addon.length();
787 }
788 if (delimiter) {
789 if (nextEnd - nextStart >= (ptrdiff_t) (limit - written)) {
790 written = indent;
791 if (Format::kIncludeReturn == format) {
792 methodStr += '\n';
793 methodStr += string(indent, ' ');
794 }
795 }
796 methodParser.skipTo(delimiter);
797 }
798 lastStart = nextStart;
799 lastEnd = nextEnd;
800 } while (lastStart < lastEnd);
801 return methodStr;
802 }
803
fiddleName() const804 string Definition::fiddleName() const {
805 string result;
806 size_t start = 0;
807 string parent;
808 const Definition* parentDef = this;
809 while ((parentDef = parentDef->fParent)) {
810 if (MarkType::kClass == parentDef->fMarkType || MarkType::kStruct == parentDef->fMarkType) {
811 parent = parentDef->fFiddle;
812 break;
813 }
814 }
815 if (parent.length() && 0 == fFiddle.compare(0, parent.length(), parent)) {
816 start = parent.length();
817 while (start < fFiddle.length() && '_' == fFiddle[start]) {
818 ++start;
819 }
820 }
821 size_t end = fFiddle.find_first_of('(', start);
822 return fFiddle.substr(start, end - start);
823 }
824
fileName() const825 string Definition::fileName() const {
826 size_t nameStart = fFileName.rfind(SkOSPath::SEPARATOR);
827 if (SkOSPath::SEPARATOR != '/') {
828 size_t altNameStart = fFileName.rfind('/');
829 nameStart = string::npos == nameStart ? altNameStart :
830 string::npos != altNameStart && altNameStart > nameStart ? altNameStart : nameStart;
831 }
832 SkASSERT(string::npos != nameStart);
833 string baseFile = fFileName.substr(nameStart + 1);
834 return baseFile;
835 }
836
findClone(string match) const837 const Definition* Definition::findClone(string match) const {
838 for (auto child : fChildren) {
839 if (!child->fClone) {
840 continue;
841 }
842 if (match == child->fName) {
843 return child;
844 }
845 auto inner = child->findClone(match);
846 if (inner) {
847 return inner;
848 }
849 }
850 return nullptr;
851 }
852
hasChild(MarkType markType) const853 const Definition* Definition::hasChild(MarkType markType) const {
854 for (auto iter : fChildren) {
855 if (markType == iter->fMarkType) {
856 return iter;
857 }
858 }
859 return nullptr;
860 }
861
hasParam(string ref)862 Definition* Definition::hasParam(string ref) {
863 SkASSERT(MarkType::kMethod == fMarkType);
864 for (auto iter : fChildren) {
865 if (MarkType::kParam != iter->fMarkType) {
866 continue;
867 }
868 if (iter->fName == ref) {
869 return &*iter;
870 }
871 }
872 for (auto& iter : fTokens) {
873 if (MarkType::kComment != iter.fMarkType) {
874 continue;
875 }
876 TextParser parser(&iter);
877 if (!parser.skipExact("@param ")) {
878 continue;
879 }
880 if (parser.skipExact(ref.c_str()) && ' ' == parser.peek()) {
881 return &iter;
882 }
883 }
884 return nullptr;
885 }
886
hasMatch(string name) const887 bool Definition::hasMatch(string name) const {
888 for (auto child : fChildren) {
889 if (name == child->fName) {
890 return true;
891 }
892 if (child->hasMatch(name)) {
893 return true;
894 }
895 }
896 return false;
897 }
898
isStructOrClass() const899 bool Definition::isStructOrClass() const {
900 if (MarkType::kStruct != fMarkType && MarkType::kClass != fMarkType) {
901 return false;
902 }
903 if (string::npos != fFileName.find("undocumented.bmh")) {
904 return false;
905 }
906 return true;
907 }
908
methodHasReturn(string name,TextParser * methodParser) const909 bool Definition::methodHasReturn(string name, TextParser* methodParser) const {
910 if (methodParser->skipExact("static")) {
911 methodParser->skipWhiteSpace();
912 }
913 if (methodParser->skipExact("virtual")) {
914 methodParser->skipWhiteSpace();
915 }
916 const char* lastStart = methodParser->fChar;
917 const char* nameInParser = methodParser->strnstr(name.c_str(), methodParser->fEnd);
918 methodParser->skipTo(nameInParser);
919 const char* lastEnd = methodParser->fChar;
920 const char* returnEnd = lastEnd;
921 while (returnEnd > lastStart && ' ' == returnEnd[-1]) {
922 --returnEnd;
923 }
924 bool expectReturn = 4 != returnEnd - lastStart || strncmp("void", lastStart, 4);
925 if (MethodType::kNone != fMethodType && MethodType::kOperator != fMethodType && !expectReturn) {
926 return methodParser->reportError<bool>("unexpected void");
927 }
928 switch (fMethodType) {
929 case MethodType::kNone:
930 case MethodType::kOperator:
931 // either is fine
932 break;
933 case MethodType::kConstructor:
934 expectReturn = true;
935 break;
936 case MethodType::kDestructor:
937 expectReturn = false;
938 break;
939 }
940 return expectReturn;
941 }
942
methodName() const943 string Definition::methodName() const {
944 string result;
945 size_t start = 0;
946 string parent;
947 const Definition* parentDef = this;
948 while ((parentDef = parentDef->fParent)) {
949 if (MarkType::kClass == parentDef->fMarkType || MarkType::kStruct == parentDef->fMarkType) {
950 parent = parentDef->fName;
951 break;
952 }
953 }
954 if (parent.length() && 0 == fName.compare(0, parent.length(), parent)) {
955 start = parent.length();
956 while (start < fName.length() && ':' == fName[start]) {
957 ++start;
958 }
959 }
960 if (fClone) {
961 int lastUnder = fName.rfind('_');
962 return fName.substr(start, (size_t) (lastUnder - start));
963 }
964 size_t end = fName.find_first_of('(', start);
965 if (string::npos == end) {
966 return fName.substr(start);
967 }
968 return fName.substr(start, end - start);
969 }
970
nextMethodParam(TextParser * methodParser,const char ** nextEndPtr,string * paramName) const971 bool Definition::nextMethodParam(TextParser* methodParser, const char** nextEndPtr,
972 string* paramName) const {
973 int parenCount = 0;
974 TextParserSave saveState(methodParser);
975 while (true) {
976 if (methodParser->eof()) {
977 return methodParser->reportError<bool>("#Method function missing close paren");
978 }
979 char ch = methodParser->peek();
980 if ('(' == ch || '{' == ch) {
981 ++parenCount;
982 }
983 if (parenCount == 0 && (')' == ch || ',' == ch)) {
984 *nextEndPtr = methodParser->fChar;
985 break;
986 }
987 if (')' == ch || '}' == ch) {
988 if (0 > --parenCount) {
989 return this->reportError<bool>("mismatched parentheses");
990 }
991 }
992 methodParser->next();
993 }
994 saveState.restore();
995 const char* nextEnd = *nextEndPtr;
996 const char* paramEnd = nextEnd;
997 const char* assign = methodParser->strnstr(" = ", paramEnd);
998 if (assign) {
999 paramEnd = assign;
1000 }
1001 const char* closeBracket = methodParser->strnstr("]", paramEnd);
1002 if (closeBracket) {
1003 const char* openBracket = methodParser->strnstr("[", paramEnd);
1004 if (openBracket && openBracket < closeBracket) {
1005 while (openBracket < --closeBracket && isdigit(closeBracket[0]))
1006 ;
1007 if (openBracket == closeBracket) {
1008 paramEnd = openBracket;
1009 }
1010 }
1011 }
1012 const char* function = methodParser->strnstr(")(", paramEnd);
1013 if (function) {
1014 paramEnd = function;
1015 }
1016 while (paramEnd > methodParser->fChar && ' ' == paramEnd[-1]) {
1017 --paramEnd;
1018 }
1019 const char* paramStart = paramEnd;
1020 while (paramStart > methodParser->fChar && isalnum(paramStart[-1])) {
1021 --paramStart;
1022 }
1023 if (paramStart > methodParser->fChar && paramStart >= paramEnd) {
1024 return methodParser->reportError<bool>("#Method missing param name");
1025 }
1026 *paramName = string(paramStart, paramEnd - paramStart);
1027 if (!paramName->length()) {
1028 if (')' != nextEnd[0]) {
1029 return methodParser->reportError<bool>("#Method malformed param");
1030 }
1031 return false;
1032 }
1033 return true;
1034 }
1035
NormalizedName(string name)1036 string Definition::NormalizedName(string name) {
1037 string normalizedName = name;
1038 std::replace(normalizedName.begin(), normalizedName.end(), '-', '_');
1039 do {
1040 size_t doubleColon = normalizedName.find("::", 0);
1041 if (string::npos == doubleColon) {
1042 break;
1043 }
1044 normalizedName = normalizedName.substr(0, doubleColon)
1045 + '_' + normalizedName.substr(doubleColon + 2);
1046 } while (true);
1047 return normalizedName;
1048 }
1049
unpreformat(string orig)1050 static string unpreformat(string orig) {
1051 string result;
1052 int amp = 0;
1053 for (auto c : orig) {
1054 switch (amp) {
1055 case 0:
1056 if ('&' == c) {
1057 amp = 1;
1058 } else {
1059 amp = 0;
1060 result += c;
1061 }
1062 break;
1063 case 1:
1064 if ('l' == c) {
1065 amp = 2;
1066 } else if ('g' == c) {
1067 amp = 3;
1068 } else {
1069 amp = 0;
1070 result += "&";
1071 result += c;
1072 }
1073 break;
1074 case 2:
1075 if ('t' == c) {
1076 amp = 4;
1077 } else {
1078 amp = 0;
1079 result += "&l";
1080 result += c;
1081 }
1082 break;
1083 case 3:
1084 if ('t' == c) {
1085 amp = 5;
1086 } else {
1087 amp = 0;
1088 result += "&g";
1089 result += c;
1090 }
1091 break;
1092 case 4:
1093 if (';' == c) {
1094 result += '<';
1095 } else {
1096 result += "<";
1097 result += c;
1098 }
1099 amp = 0;
1100 break;
1101 case 5:
1102 if (';' == c) {
1103 result += '>';
1104 } else {
1105 result += ">";
1106 result += c;
1107 }
1108 amp = 0;
1109 break;
1110 }
1111 }
1112 return result;
1113 }
1114
paramsMatch(string matchFormatted,string name) const1115 bool Definition::paramsMatch(string matchFormatted, string name) const {
1116 string match = unpreformat(matchFormatted);
1117 TextParser def(fFileName, fStart, fContentStart, fLineCount);
1118 const char* dName = def.strnstr(name.c_str(), fContentStart);
1119 if (!dName) {
1120 return false;
1121 }
1122 def.skipTo(dName);
1123 TextParser m(fFileName, &match.front(), &match.back() + 1, fLineCount);
1124 const char* mName = m.strnstr(name.c_str(), m.fEnd);
1125 if (!mName) {
1126 return false;
1127 }
1128 m.skipTo(mName);
1129 while (!def.eof() && ')' != def.peek() && !m.eof() && ')' != m.peek()) {
1130 const char* ds = def.fChar;
1131 const char* ms = m.fChar;
1132 const char* de = def.anyOf(") \n");
1133 const char* me = m.anyOf(") \n");
1134 def.skipTo(de);
1135 m.skipTo(me);
1136 if (def.fChar - ds != m.fChar - ms) {
1137 return false;
1138 }
1139 if (strncmp(ds, ms, (int) (def.fChar - ds))) {
1140 return false;
1141 }
1142 def.skipWhiteSpace();
1143 m.skipWhiteSpace();
1144 }
1145 return !def.eof() && ')' == def.peek() && !m.eof() && ')' == m.peek();
1146 }
1147
1148
trimEnd()1149 void Definition::trimEnd() {
1150 while (fContentEnd > fContentStart && ' ' >= fContentEnd[-1]) {
1151 --fContentEnd;
1152 }
1153 }
1154
clearVisited()1155 void RootDefinition::clearVisited() {
1156 fVisited = false;
1157 for (auto& leaf : fLeaves) {
1158 leaf.second.fVisited = false;
1159 }
1160 for (auto& branch : fBranches) {
1161 branch.second->clearVisited();
1162 }
1163 }
1164
dumpUnVisited()1165 bool RootDefinition::dumpUnVisited() {
1166 bool success = true;
1167 for (auto& leaf : fLeaves) {
1168 if (!leaf.second.fVisited) {
1169 // FIXME: bugs requiring long tail fixes, suppressed here:
1170 // SkBitmap::validate() is wrapped in SkDEBUGCODE in .h and not parsed
1171 if ("SkBitmap::validate()" == leaf.first) {
1172 continue;
1173 }
1174 // FIXME: end of long tail bugs
1175 SkDebugf("defined in bmh but missing in include: %s\n", leaf.first.c_str());
1176 success = false;
1177 }
1178 }
1179 for (auto& branch : fBranches) {
1180 success &= branch.second->dumpUnVisited();
1181 }
1182 return success;
1183 }
1184
find(string ref,AllowParens allowParens)1185 Definition* RootDefinition::find(string ref, AllowParens allowParens) {
1186 const auto leafIter = fLeaves.find(ref);
1187 if (leafIter != fLeaves.end()) {
1188 return &leafIter->second;
1189 }
1190 if (AllowParens::kYes == allowParens) {
1191 size_t leftParen = ref.find('(');
1192 if (string::npos == leftParen
1193 || (leftParen + 1 < ref.length() && ')' != ref[leftParen + 1])) {
1194 string withParens = ref + "()";
1195 const auto parensIter = fLeaves.find(withParens);
1196 if (parensIter != fLeaves.end()) {
1197 return &parensIter->second;
1198 }
1199 }
1200 if (string::npos != leftParen) {
1201 string name = ref.substr(0, leftParen);
1202 size_t posInDefName = fName.find(name);
1203 if (string::npos != posInDefName && posInDefName > 2
1204 && "::" == fName.substr(posInDefName - 2, 2)) {
1205 string fullRef = fName + "::" + ref;
1206 const auto fullIter = fLeaves.find(fullRef);
1207 if (fullIter != fLeaves.end()) {
1208 return &fullIter->second;
1209 }
1210 }
1211 }
1212 }
1213 const auto branchIter = fBranches.find(ref);
1214 if (branchIter != fBranches.end()) {
1215 RootDefinition* rootDef = branchIter->second;
1216 return rootDef;
1217 }
1218 Definition* result = nullptr;
1219 for (const auto& branch : fBranches) {
1220 RootDefinition* rootDef = branch.second;
1221 result = rootDef->find(ref, allowParens);
1222 if (result) {
1223 break;
1224 }
1225 }
1226 return result;
1227 }
1228