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 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 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 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 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 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 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 414 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 575 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 596 bool Definition::crossCheck(const Definition& includeToken) const { 597 return crossCheckInside(fContentStart, fContentEnd, includeToken); 598 } 599 600 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 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 626 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 698 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 804 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 825 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 837 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 853 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 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 887 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 899 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 909 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 943 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 971 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 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 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 1115 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 1149 void Definition::trimEnd() { 1150 while (fContentEnd > fContentStart && ' ' >= fContentEnd[-1]) { 1151 --fContentEnd; 1152 } 1153 } 1154 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 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 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