1 //===--- PPCallbacksTracker.cpp - Preprocessor tracker -*--*-------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===--------------------------------------------------------------------===// 9 /// 10 /// \file 11 /// \brief Implementations for preprocessor tracking. 12 /// 13 /// See the header for details. 14 /// 15 //===--------------------------------------------------------------------===// 16 17 #include "PPCallbacksTracker.h" 18 #include "clang/Lex/MacroArgs.h" 19 #include "llvm/Support/raw_ostream.h" 20 #include <stdarg.h> 21 #include <stdio.h> 22 23 // Utility functions. 24 25 // Get a "file:line:column" source location string. 26 static std::string getSourceLocationString(clang::Preprocessor &PP, 27 clang::SourceLocation Loc) { 28 if (Loc.isInvalid()) 29 return std::string("(none)"); 30 31 if (Loc.isFileID()) { 32 clang::PresumedLoc PLoc = PP.getSourceManager().getPresumedLoc(Loc); 33 34 if (PLoc.isInvalid()) { 35 return std::string("(invalid)"); 36 } 37 38 std::string Str; 39 llvm::raw_string_ostream SS(Str); 40 41 // The macro expansion and spelling pos is identical for file locs. 42 SS << "\"" << PLoc.getFilename() << ':' << PLoc.getLine() << ':' 43 << PLoc.getColumn() << "\""; 44 45 std::string Result = SS.str(); 46 47 // YAML treats backslash as escape, so use forward slashes. 48 std::replace(Result.begin(), Result.end(), '\\', '/'); 49 50 return Result; 51 } 52 53 return std::string("(nonfile)"); 54 } 55 56 // Enum string tables. 57 58 // FileChangeReason strings. 59 static const char *FileChangeReasonStrings[] = { 60 "EnterFile", "ExitFile", "SystemHeaderPragma", "RenameFile" 61 }; 62 63 // CharacteristicKind strings. 64 static const char *CharacteristicKindStrings[] = { "C_User", "C_System", 65 "C_ExternCSystem" }; 66 67 // MacroDirective::Kind strings. 68 static const char *MacroDirectiveKindStrings[] = { "MD_Define", "MD_Undefine", 69 "MD_Visibility" }; 70 71 // PragmaIntroducerKind strings. 72 static const char *PragmaIntroducerKindStrings[] = { "PIK_HashPragma", 73 "PIK__Pragma", 74 "PIK___pragma" }; 75 76 // PragmaMessageKind strings. 77 static const char *PragmaMessageKindStrings[] = { "PMK_Message", "PMK_Warning", 78 "PMK_Error" }; 79 80 // ConditionValueKind strings. 81 const char * 82 ConditionValueKindStrings[] = { 83 "CVK_NotEvaluated", "CVK_False", "CVK_True" 84 }; 85 86 // Mapping strings. 87 static const char *MappingStrings[] = { "0", "MAP_IGNORE", 88 "MAP_REMARK", "MAP_WARNING", 89 "MAP_ERROR", "MAP_FATAL" }; 90 91 // PPCallbacksTracker functions. 92 93 PPCallbacksTracker::PPCallbacksTracker(llvm::SmallSet<std::string, 4> &Ignore, 94 std::vector<CallbackCall> &CallbackCalls, 95 clang::Preprocessor &PP) 96 : CallbackCalls(CallbackCalls), Ignore(Ignore), PP(PP) {} 97 98 PPCallbacksTracker::~PPCallbacksTracker() {} 99 100 // Callback functions. 101 102 // Callback invoked whenever a source file is entered or exited. 103 void PPCallbacksTracker::FileChanged( 104 clang::SourceLocation Loc, clang::PPCallbacks::FileChangeReason Reason, 105 clang::SrcMgr::CharacteristicKind FileType, clang::FileID PrevFID) { 106 beginCallback("FileChanged"); 107 appendArgument("Loc", Loc); 108 appendArgument("Reason", Reason, FileChangeReasonStrings); 109 appendArgument("FileType", FileType, CharacteristicKindStrings); 110 appendArgument("PrevFID", PrevFID); 111 } 112 113 // Callback invoked whenever a source file is skipped as the result 114 // of header guard optimization. 115 void 116 PPCallbacksTracker::FileSkipped(const clang::FileEntry &ParentFile, 117 const clang::Token &FilenameTok, 118 clang::SrcMgr::CharacteristicKind FileType) { 119 beginCallback("FileSkipped"); 120 appendArgument("ParentFile", &ParentFile); 121 appendArgument("FilenameTok", FilenameTok); 122 appendArgument("FileType", FileType, CharacteristicKindStrings); 123 } 124 125 // Callback invoked whenever an inclusion directive results in a 126 // file-not-found error. 127 bool 128 PPCallbacksTracker::FileNotFound(llvm::StringRef FileName, 129 llvm::SmallVectorImpl<char> &RecoveryPath) { 130 beginCallback("FileNotFound"); 131 appendFilePathArgument("FileName", FileName); 132 return false; 133 } 134 135 // Callback invoked whenever an inclusion directive of 136 // any kind (#include, #import, etc.) has been processed, regardless 137 // of whether the inclusion will actually result in an inclusion. 138 void PPCallbacksTracker::InclusionDirective( 139 clang::SourceLocation HashLoc, const clang::Token &IncludeTok, 140 llvm::StringRef FileName, bool IsAngled, 141 clang::CharSourceRange FilenameRange, const clang::FileEntry *File, 142 llvm::StringRef SearchPath, llvm::StringRef RelativePath, 143 const clang::Module *Imported) { 144 beginCallback("InclusionDirective"); 145 appendArgument("IncludeTok", IncludeTok); 146 appendFilePathArgument("FileName", FileName); 147 appendArgument("IsAngled", IsAngled); 148 appendArgument("FilenameRange", FilenameRange); 149 appendArgument("File", File); 150 appendFilePathArgument("SearchPath", SearchPath); 151 appendFilePathArgument("RelativePath", RelativePath); 152 appendArgument("Imported", Imported); 153 } 154 155 // Callback invoked whenever there was an explicit module-import 156 // syntax. 157 void PPCallbacksTracker::moduleImport(clang::SourceLocation ImportLoc, 158 clang::ModuleIdPath Path, 159 const clang::Module *Imported) { 160 beginCallback("moduleImport"); 161 appendArgument("ImportLoc", ImportLoc); 162 appendArgument("Path", Path); 163 appendArgument("Imported", Imported); 164 } 165 166 // Callback invoked when the end of the main file is reached. 167 // No subsequent callbacks will be made. 168 void PPCallbacksTracker::EndOfMainFile() { beginCallback("EndOfMainFile"); } 169 170 // Callback invoked when a #ident or #sccs directive is read. 171 void PPCallbacksTracker::Ident(clang::SourceLocation Loc, 172 const std::string &Str) { 173 beginCallback("Ident"); 174 appendArgument("Loc", Loc); 175 appendArgument("Str", Str); 176 } 177 178 // Callback invoked when start reading any pragma directive. 179 void 180 PPCallbacksTracker::PragmaDirective(clang::SourceLocation Loc, 181 clang::PragmaIntroducerKind Introducer) { 182 beginCallback("PragmaDirective"); 183 appendArgument("Loc", Loc); 184 appendArgument("Introducer", Introducer, PragmaIntroducerKindStrings); 185 } 186 187 // Callback invoked when a #pragma comment directive is read. 188 void PPCallbacksTracker::PragmaComment(clang::SourceLocation Loc, 189 const clang::IdentifierInfo *Kind, 190 const std::string &Str) { 191 beginCallback("PragmaComment"); 192 appendArgument("Loc", Loc); 193 appendArgument("Kind", Kind); 194 appendArgument("Str", Str); 195 } 196 197 // Callback invoked when a #pragma detect_mismatch directive is 198 // read. 199 void PPCallbacksTracker::PragmaDetectMismatch(clang::SourceLocation Loc, 200 const std::string &Name, 201 const std::string &Value) { 202 beginCallback("PragmaDetectMismatch"); 203 appendArgument("Loc", Loc); 204 appendArgument("Name", Name); 205 appendArgument("Value", Value); 206 } 207 208 // Callback invoked when a #pragma clang __debug directive is read. 209 void PPCallbacksTracker::PragmaDebug(clang::SourceLocation Loc, 210 llvm::StringRef DebugType) { 211 beginCallback("PragmaDebug"); 212 appendArgument("Loc", Loc); 213 appendArgument("DebugType", DebugType); 214 } 215 216 // Callback invoked when a #pragma message directive is read. 217 void PPCallbacksTracker::PragmaMessage( 218 clang::SourceLocation Loc, llvm::StringRef Namespace, 219 clang::PPCallbacks::PragmaMessageKind Kind, llvm::StringRef Str) { 220 beginCallback("PragmaMessage"); 221 appendArgument("Loc", Loc); 222 appendArgument("Namespace", Namespace); 223 appendArgument("Kind", Kind, PragmaMessageKindStrings); 224 appendArgument("Str", Str); 225 } 226 227 // Callback invoked when a #pragma gcc dianostic push directive 228 // is read. 229 void PPCallbacksTracker::PragmaDiagnosticPush(clang::SourceLocation Loc, 230 llvm::StringRef Namespace) { 231 beginCallback("PragmaDiagnosticPush"); 232 appendArgument("Loc", Loc); 233 appendArgument("Namespace", Namespace); 234 } 235 236 // Callback invoked when a #pragma gcc dianostic pop directive 237 // is read. 238 void PPCallbacksTracker::PragmaDiagnosticPop(clang::SourceLocation Loc, 239 llvm::StringRef Namespace) { 240 beginCallback("PragmaDiagnosticPop"); 241 appendArgument("Loc", Loc); 242 appendArgument("Namespace", Namespace); 243 } 244 245 // Callback invoked when a #pragma gcc dianostic directive is read. 246 void PPCallbacksTracker::PragmaDiagnostic(clang::SourceLocation Loc, 247 llvm::StringRef Namespace, 248 clang::diag::Severity Mapping, 249 llvm::StringRef Str) { 250 beginCallback("PragmaDiagnostic"); 251 appendArgument("Loc", Loc); 252 appendArgument("Namespace", Namespace); 253 appendArgument("Mapping", (unsigned)Mapping, MappingStrings); 254 appendArgument("Str", Str); 255 } 256 257 // Called when an OpenCL extension is either disabled or 258 // enabled with a pragma. 259 void PPCallbacksTracker::PragmaOpenCLExtension( 260 clang::SourceLocation NameLoc, const clang::IdentifierInfo *Name, 261 clang::SourceLocation StateLoc, unsigned State) { 262 beginCallback("PragmaOpenCLExtension"); 263 appendArgument("NameLoc", NameLoc); 264 appendArgument("Name", Name); 265 appendArgument("StateLoc", StateLoc); 266 appendArgument("State", (int)State); 267 } 268 269 // Callback invoked when a #pragma warning directive is read. 270 void PPCallbacksTracker::PragmaWarning(clang::SourceLocation Loc, 271 llvm::StringRef WarningSpec, 272 llvm::ArrayRef<int> Ids) { 273 beginCallback("PragmaWarning"); 274 appendArgument("Loc", Loc); 275 appendArgument("WarningSpec", WarningSpec); 276 277 std::string Str; 278 llvm::raw_string_ostream SS(Str); 279 SS << "["; 280 for (int i = 0, e = Ids.size(); i != e; ++i) { 281 if (i) 282 SS << ", "; 283 SS << Ids[i]; 284 } 285 SS << "]"; 286 appendArgument("Ids", SS.str()); 287 } 288 289 // Callback invoked when a #pragma warning(push) directive is read. 290 void PPCallbacksTracker::PragmaWarningPush(clang::SourceLocation Loc, 291 int Level) { 292 beginCallback("PragmaWarningPush"); 293 appendArgument("Loc", Loc); 294 appendArgument("Level", Level); 295 } 296 297 // Callback invoked when a #pragma warning(pop) directive is read. 298 void PPCallbacksTracker::PragmaWarningPop(clang::SourceLocation Loc) { 299 beginCallback("PragmaWarningPop"); 300 appendArgument("Loc", Loc); 301 } 302 303 // Called by Preprocessor::HandleMacroExpandedIdentifier when a 304 // macro invocation is found. 305 void 306 PPCallbacksTracker::MacroExpands(const clang::Token &MacroNameTok, 307 const clang::MacroDirective *MacroDirective, 308 clang::SourceRange Range, 309 const clang::MacroArgs *Args) { 310 beginCallback("MacroExpands"); 311 appendArgument("MacroNameTok", MacroNameTok); 312 appendArgument("MacroDirective", MacroDirective); 313 appendArgument("Range", Range); 314 appendArgument("Args", Args); 315 } 316 317 // Hook called whenever a macro definition is seen. 318 void 319 PPCallbacksTracker::MacroDefined(const clang::Token &MacroNameTok, 320 const clang::MacroDirective *MacroDirective) { 321 beginCallback("MacroDefined"); 322 appendArgument("MacroNameTok", MacroNameTok); 323 appendArgument("MacroDirective", MacroDirective); 324 } 325 326 // Hook called whenever a macro #undef is seen. 327 void PPCallbacksTracker::MacroUndefined( 328 const clang::Token &MacroNameTok, 329 const clang::MacroDirective *MacroDirective) { 330 beginCallback("MacroUndefined"); 331 appendArgument("MacroNameTok", MacroNameTok); 332 appendArgument("MacroDirective", MacroDirective); 333 } 334 335 // Hook called whenever the 'defined' operator is seen. 336 void PPCallbacksTracker::Defined(const clang::Token &MacroNameTok, 337 const clang::MacroDirective *MacroDirective, 338 clang::SourceRange Range) { 339 beginCallback("Defined"); 340 appendArgument("MacroNameTok", MacroNameTok); 341 appendArgument("MacroDirective", MacroDirective); 342 appendArgument("Range", Range); 343 } 344 345 // Hook called when a source range is skipped. 346 void PPCallbacksTracker::SourceRangeSkipped(clang::SourceRange Range) { 347 beginCallback("SourceRangeSkipped"); 348 appendArgument("Range", Range); 349 } 350 351 // Hook called whenever an #if is seen. 352 void PPCallbacksTracker::If(clang::SourceLocation Loc, 353 clang::SourceRange ConditionRange, 354 ConditionValueKind ConditionValue) { 355 beginCallback("If"); 356 appendArgument("Loc", Loc); 357 appendArgument("ConditionRange", ConditionRange); 358 appendArgument("ConditionValue", ConditionValue, ConditionValueKindStrings); 359 } 360 361 // Hook called whenever an #elif is seen. 362 void PPCallbacksTracker::Elif(clang::SourceLocation Loc, 363 clang::SourceRange ConditionRange, 364 ConditionValueKind ConditionValue, 365 clang::SourceLocation IfLoc) { 366 beginCallback("Elif"); 367 appendArgument("Loc", Loc); 368 appendArgument("ConditionRange", ConditionRange); 369 appendArgument("ConditionValue", ConditionValue, ConditionValueKindStrings); 370 appendArgument("IfLoc", IfLoc); 371 } 372 373 // Hook called whenever an #ifdef is seen. 374 void PPCallbacksTracker::Ifdef(clang::SourceLocation Loc, 375 const clang::Token &MacroNameTok, 376 const clang::MacroDirective *MacroDirective) { 377 beginCallback("Ifdef"); 378 appendArgument("Loc", Loc); 379 appendArgument("MacroNameTok", MacroNameTok); 380 appendArgument("MacroDirective", MacroDirective); 381 } 382 383 // Hook called whenever an #ifndef is seen. 384 void PPCallbacksTracker::Ifndef(clang::SourceLocation Loc, 385 const clang::Token &MacroNameTok, 386 const clang::MacroDirective *MacroDirective) { 387 beginCallback("Ifndef"); 388 appendArgument("Loc", Loc); 389 appendArgument("MacroNameTok", MacroNameTok); 390 appendArgument("MacroDirective", MacroDirective); 391 } 392 393 // Hook called whenever an #else is seen. 394 void PPCallbacksTracker::Else(clang::SourceLocation Loc, 395 clang::SourceLocation IfLoc) { 396 beginCallback("Else"); 397 appendArgument("Loc", Loc); 398 appendArgument("IfLoc", IfLoc); 399 } 400 401 // Hook called whenever an #endif is seen. 402 void PPCallbacksTracker::Endif(clang::SourceLocation Loc, 403 clang::SourceLocation IfLoc) { 404 beginCallback("Endif"); 405 appendArgument("Loc", Loc); 406 appendArgument("IfLoc", IfLoc); 407 } 408 409 // Helper functions. 410 411 // Start a new callback. 412 void PPCallbacksTracker::beginCallback(const char *Name) { 413 DisableTrace = Ignore.count(std::string(Name)); 414 if (DisableTrace) 415 return; 416 CallbackCalls.push_back(CallbackCall(Name)); 417 } 418 419 // Append a bool argument to the top trace item. 420 void PPCallbacksTracker::appendArgument(const char *Name, bool Value) { 421 appendArgument(Name, (Value ? "true" : "false")); 422 } 423 424 // Append an int argument to the top trace item. 425 void PPCallbacksTracker::appendArgument(const char *Name, int Value) { 426 std::string Str; 427 llvm::raw_string_ostream SS(Str); 428 SS << Value; 429 appendArgument(Name, SS.str()); 430 } 431 432 // Append a string argument to the top trace item. 433 void PPCallbacksTracker::appendArgument(const char *Name, const char *Value) { 434 if (DisableTrace) 435 return; 436 CallbackCalls.back().Arguments.push_back(Argument(Name, Value)); 437 } 438 439 // Append a string object argument to the top trace item. 440 void PPCallbacksTracker::appendArgument(const char *Name, 441 llvm::StringRef Value) { 442 appendArgument(Name, Value.str()); 443 } 444 445 // Append a string object argument to the top trace item. 446 void PPCallbacksTracker::appendArgument(const char *Name, 447 const std::string &Value) { 448 appendArgument(Name, Value.c_str()); 449 } 450 451 // Append a token argument to the top trace item. 452 void PPCallbacksTracker::appendArgument(const char *Name, 453 const clang::Token &Value) { 454 appendArgument(Name, PP.getSpelling(Value)); 455 } 456 457 // Append an enum argument to the top trace item. 458 void PPCallbacksTracker::appendArgument(const char *Name, int Value, 459 const char *Strings[]) { 460 appendArgument(Name, Strings[Value]); 461 } 462 463 // Append a FileID argument to the top trace item. 464 void PPCallbacksTracker::appendArgument(const char *Name, clang::FileID Value) { 465 if (Value.isInvalid()) { 466 appendArgument(Name, "(invalid)"); 467 return; 468 } 469 const clang::FileEntry *FileEntry = 470 PP.getSourceManager().getFileEntryForID(Value); 471 if (!FileEntry) { 472 appendArgument(Name, "(getFileEntryForID failed)"); 473 return; 474 } 475 appendFilePathArgument(Name, FileEntry->getName()); 476 } 477 478 // Append a FileEntry argument to the top trace item. 479 void PPCallbacksTracker::appendArgument(const char *Name, 480 const clang::FileEntry *Value) { 481 if (!Value) { 482 appendArgument(Name, "(null)"); 483 return; 484 } 485 appendFilePathArgument(Name, Value->getName()); 486 } 487 488 // Append a SourceLocation argument to the top trace item. 489 void PPCallbacksTracker::appendArgument(const char *Name, 490 clang::SourceLocation Value) { 491 if (Value.isInvalid()) { 492 appendArgument(Name, "(invalid)"); 493 return; 494 } 495 appendArgument(Name, getSourceLocationString(PP, Value).c_str()); 496 } 497 498 // Append a SourceRange argument to the top trace item. 499 void PPCallbacksTracker::appendArgument(const char *Name, 500 clang::SourceRange Value) { 501 if (DisableTrace) 502 return; 503 if (Value.isInvalid()) { 504 appendArgument(Name, "(invalid)"); 505 return; 506 } 507 std::string Str; 508 llvm::raw_string_ostream SS(Str); 509 SS << "[" << getSourceLocationString(PP, Value.getBegin()) << ", " 510 << getSourceLocationString(PP, Value.getEnd()) << "]"; 511 appendArgument(Name, SS.str()); 512 } 513 514 // Append a CharSourceRange argument to the top trace item. 515 void PPCallbacksTracker::appendArgument(const char *Name, 516 clang::CharSourceRange Value) { 517 if (Value.isInvalid()) { 518 appendArgument(Name, "(invalid)"); 519 return; 520 } 521 appendArgument(Name, getSourceString(Value).str().c_str()); 522 } 523 524 // Append a SourceLocation argument to the top trace item. 525 void PPCallbacksTracker::appendArgument(const char *Name, 526 clang::ModuleIdPath Value) { 527 if (DisableTrace) 528 return; 529 std::string Str; 530 llvm::raw_string_ostream SS(Str); 531 SS << "["; 532 for (int I = 0, E = Value.size(); I != E; ++I) { 533 if (I) 534 SS << ", "; 535 SS << "{" 536 << "Name: " << Value[I].first->getName() << ", " 537 << "Loc: " << getSourceLocationString(PP, Value[I].second) << "}"; 538 } 539 SS << "]"; 540 appendArgument(Name, SS.str()); 541 } 542 543 // Append an IdentifierInfo argument to the top trace item. 544 void PPCallbacksTracker::appendArgument(const char *Name, 545 const clang::IdentifierInfo *Value) { 546 if (!Value) { 547 appendArgument(Name, "(null)"); 548 return; 549 } 550 appendArgument(Name, Value->getName().str().c_str()); 551 } 552 553 // Append a MacroDirective argument to the top trace item. 554 void PPCallbacksTracker::appendArgument(const char *Name, 555 const clang::MacroDirective *Value) { 556 if (!Value) { 557 appendArgument(Name, "(null)"); 558 return; 559 } 560 appendArgument(Name, MacroDirectiveKindStrings[Value->getKind()]); 561 } 562 563 // Append a MacroArgs argument to the top trace item. 564 void PPCallbacksTracker::appendArgument(const char *Name, 565 const clang::MacroArgs *Value) { 566 if (!Value) { 567 appendArgument(Name, "(null)"); 568 return; 569 } 570 std::string Str; 571 llvm::raw_string_ostream SS(Str); 572 SS << "["; 573 // The argument tokens might include end tokens, so we reflect how 574 // how getUnexpArgument provides the arguments. 575 for (int I = 0, E = Value->getNumArguments(); I < E; ++I) { 576 const clang::Token *Current = Value->getUnexpArgument(I); 577 int TokenCount = Value->getArgLength(Current) + 1; // include EOF 578 E -= TokenCount; 579 if (I) 580 SS << ", "; 581 // We're assuming tokens are contiguous, as otherwise we have no 582 // other way to get at them. 583 --TokenCount; 584 for (int TokenIndex = 0; TokenIndex < TokenCount; ++TokenIndex, ++Current) { 585 if (TokenIndex) 586 SS << " "; 587 // We need to be careful here because the arguments might not be legal in 588 // YAML, so we use the token name for anything but identifiers and 589 // numeric literals. 590 if (Current->isAnyIdentifier() || 591 Current->is(clang::tok::numeric_constant)) { 592 SS << PP.getSpelling(*Current); 593 } else { 594 SS << "<" << Current->getName() << ">"; 595 } 596 } 597 } 598 SS << "]"; 599 appendArgument(Name, SS.str()); 600 } 601 602 // Append a Module argument to the top trace item. 603 void PPCallbacksTracker::appendArgument(const char *Name, 604 const clang::Module *Value) { 605 if (!Value) { 606 appendArgument(Name, "(null)"); 607 return; 608 } 609 appendArgument(Name, Value->Name.c_str()); 610 } 611 612 // Append a double-quoted argument to the top trace item. 613 void PPCallbacksTracker::appendQuotedArgument(const char *Name, 614 const std::string &Value) { 615 std::string Str; 616 llvm::raw_string_ostream SS(Str); 617 SS << "\"" << Value << "\""; 618 appendArgument(Name, SS.str()); 619 } 620 621 // Append a double-quoted file path argument to the top trace item. 622 void PPCallbacksTracker::appendFilePathArgument(const char *Name, 623 llvm::StringRef Value) { 624 std::string Path(Value); 625 // YAML treats backslash as escape, so use forward slashes. 626 std::replace(Path.begin(), Path.end(), '\\', '/'); 627 appendQuotedArgument(Name, Path); 628 } 629 630 // Get the raw source string of the range. 631 llvm::StringRef 632 PPCallbacksTracker::getSourceString(clang::CharSourceRange Range) { 633 const char *B = PP.getSourceManager().getCharacterData(Range.getBegin()); 634 const char *E = PP.getSourceManager().getCharacterData(Range.getEnd()); 635 return llvm::StringRef(B, E - B); 636 } 637