CmdParser.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "CommonCorePch.h"
  6. #include "Core/ICustomConfigFlags.h"
  7. #include "Core/CmdParser.h"
  8. using namespace Js;
  9. ///----------------------------------------------------------------------------
  10. ///
  11. /// CmdLineArgsParser::ParseString
  12. ///
  13. /// Parses a string token. There are 2 ways to specify it.
  14. /// 1. Quoted - " " Any character within quotes is parsed as string.
  15. /// if the quotes are not closed, its an error.
  16. /// 2. UnQuoted - End of string is indicated by a space/end of stream.
  17. /// If fTreatColonAsSeparator is mentioned, then we break
  18. /// at colon also.
  19. ///
  20. ///
  21. /// Empty string "" is treated as Exception()
  22. ///
  23. ///----------------------------------------------------------------------------
  24. LPWSTR
  25. CmdLineArgsParser::ParseString(__inout_ecount(ceBuffer) LPWSTR buffer, size_t ceBuffer, bool fTreatColonAsSeparator)
  26. {
  27. char16 *out = buffer;
  28. size_t len = 0;
  29. if('"' == CurChar())
  30. {
  31. NextChar();
  32. while('"' != CurChar())
  33. {
  34. if(0 == CurChar())
  35. {
  36. throw Exception(_u("Unmatched quote"));
  37. }
  38. //
  39. // MaxTokenSize - 1 because we need 1 extra position for null termination
  40. //
  41. if (len >= ceBuffer - 1)
  42. {
  43. throw Exception(_u("String token too large to parse"));
  44. }
  45. out[len++] = CurChar();
  46. NextChar();
  47. }
  48. NextChar();
  49. }
  50. else
  51. {
  52. bool fDone = false;
  53. while(!fDone)
  54. {
  55. switch(CurChar())
  56. {
  57. case ' ':
  58. case ',':
  59. case 0:
  60. fDone = true;
  61. break;
  62. case '-':
  63. case '=':
  64. case ':':
  65. if (fTreatColonAsSeparator)
  66. {
  67. fDone = true;
  68. break;
  69. }
  70. else
  71. {
  72. // Fallthrough
  73. }
  74. default:
  75. if (len >= MaxTokenSize - 1)
  76. {
  77. throw Exception(_u("String token too large to parse"));
  78. }
  79. out[len++] = CurChar();
  80. NextChar();
  81. }
  82. }
  83. }
  84. if(0 == len)
  85. {
  86. throw Exception(_u("String Token Expected"));
  87. }
  88. out[len] = '\0';
  89. return buffer;
  90. }
  91. ///----------------------------------------------------------------------------
  92. ///
  93. /// CmdLineArgsParser::ParseSourceFunctionIds
  94. ///
  95. /// Parses for sourceContextId and FunctionId pairs
  96. ///----------------------------------------------------------------------------
  97. Js::SourceFunctionNode
  98. CmdLineArgsParser::ParseSourceFunctionIds()
  99. {
  100. uint functionId, sourceId;
  101. if ('*' == CurChar())
  102. {
  103. sourceId = 1;
  104. functionId = (uint)-2;
  105. NextChar();
  106. }
  107. else if ('+' == CurChar())
  108. {
  109. sourceId = 1;
  110. functionId = (uint)-1;
  111. NextChar();
  112. }
  113. else
  114. {
  115. functionId = sourceId = ParseInteger();
  116. if ('.' == CurChar())
  117. {
  118. NextChar();
  119. if ('*' == CurChar())
  120. {
  121. functionId = (uint)-2;
  122. NextChar();
  123. }
  124. else if ('+' == CurChar())
  125. {
  126. functionId = (uint)-1;
  127. NextChar();
  128. }
  129. else
  130. {
  131. functionId = ParseInteger();
  132. }
  133. }
  134. else
  135. {
  136. sourceId = 1;
  137. }
  138. }
  139. return SourceFunctionNode(sourceId, functionId);
  140. }
  141. ///----------------------------------------------------------------------------
  142. ///
  143. /// CmdLineArgsParser::ParseInteger
  144. ///
  145. /// Parses signed integer. Checks for overflow and underflows.
  146. ///----------------------------------------------------------------------------
  147. int
  148. CmdLineArgsParser::ParseInteger()
  149. {
  150. int result = 0;
  151. int sign = 1;
  152. if('-' == CurChar())
  153. {
  154. sign = -1;
  155. NextChar();
  156. }
  157. if(!IsDigit())
  158. {
  159. throw Exception(_u("Integer Expected"));
  160. }
  161. int base = 10;
  162. if ('0' == CurChar())
  163. {
  164. NextChar();
  165. if (CurChar() == 'x')
  166. {
  167. NextChar();
  168. base = 16;
  169. }
  170. // Should the else case be parse as octal?
  171. }
  172. while(IsDigit() || (base == 16 && IsHexDigit()))
  173. {
  174. int currentDigit = (int)(CurChar() - '0');
  175. if (currentDigit > 9)
  176. {
  177. Assert(base == 16);
  178. if (CurChar() < 'F')
  179. {
  180. currentDigit = 10 + (int)(CurChar() - 'A');
  181. }
  182. else
  183. {
  184. currentDigit = 10 + (int)(CurChar() - 'a');
  185. }
  186. Assert(currentDigit < 16);
  187. }
  188. result = result * base + (int)(CurChar() - '0');
  189. if(result < 0)
  190. {
  191. // overflow or underflow in case sign = -1
  192. throw Exception(_u("Integer too large to parse"));
  193. }
  194. NextChar();
  195. }
  196. return result * sign;
  197. }
  198. ///----------------------------------------------------------------------------
  199. ///
  200. /// CmdLineArgsParser::ParseRange
  201. ///
  202. /// Parses :-
  203. /// range = int | int '-' int | range, range
  204. ///
  205. ///----------------------------------------------------------------------------
  206. void
  207. CmdLineArgsParser::ParseRange(Js::Range *pRange, Js::Range *oppositeRange)
  208. {
  209. SourceFunctionNode r1 = ParseSourceFunctionIds();
  210. SourceFunctionNode r2;
  211. switch(CurChar())
  212. {
  213. case '-':
  214. NextChar();
  215. r2 = ParseSourceFunctionIds();
  216. if (r1.sourceContextId > r2.sourceContextId)
  217. {
  218. throw Exception(_u("Left source index must be smaller than the Right source Index"));
  219. }
  220. if ((r1.sourceContextId == r2.sourceContextId) &&
  221. (r1.functionId > r2.functionId))
  222. {
  223. throw Exception(_u("Left functionId must be smaller than the Right functionId when Source file is the same"));
  224. }
  225. pRange->Add(r1, r2, oppositeRange);
  226. switch(CurChar())
  227. {
  228. case ',':
  229. NextChar();
  230. ParseRange(pRange, oppositeRange);
  231. break;
  232. case ' ':
  233. case 0:
  234. break;
  235. default:
  236. throw Exception(_u("Unexpected character while parsing Range"));
  237. }
  238. break;
  239. case ',':
  240. pRange->Add(r1, oppositeRange);
  241. NextChar();
  242. ParseRange(pRange, oppositeRange);
  243. break;
  244. case ' ':
  245. case 0:
  246. pRange->Add(r1, oppositeRange);
  247. break;
  248. default:
  249. throw Exception(_u("Unexpected character while parsing Range"));
  250. }
  251. }
  252. void
  253. CmdLineArgsParser::ParseNumberRange(Js::NumberRange *pRange)
  254. {
  255. int start = ParseInteger();
  256. int end;
  257. switch (CurChar())
  258. {
  259. case '-':
  260. NextChar();
  261. end = ParseInteger();
  262. if (start > end)
  263. {
  264. throw Exception(_u("Range start must be less than range end"));
  265. }
  266. pRange->Add(start, end);
  267. switch (CurChar())
  268. {
  269. case ',':
  270. NextChar();
  271. ParseNumberRange(pRange);
  272. break;
  273. case ' ':
  274. case 0:
  275. break;
  276. default:
  277. throw Exception(_u("Unexpected character while parsing Range"));
  278. }
  279. break;
  280. case ',':
  281. pRange->Add(start);
  282. NextChar();
  283. ParseNumberRange(pRange);
  284. break;
  285. case ' ':
  286. case 0:
  287. pRange->Add(start);
  288. break;
  289. default:
  290. throw Exception(_u("Unexpected character while parsing Range"));
  291. }
  292. }
  293. ///----------------------------------------------------------------------------
  294. ///
  295. /// CmdLineArgsParser::ParsePhase
  296. ///
  297. /// Parses comma separated list of:
  298. /// phase[:range]
  299. /// phase is a string defined in Js:PhaseNames.
  300. ///
  301. ///----------------------------------------------------------------------------
  302. void
  303. CmdLineArgsParser::ParsePhase(Js::Phases *pPhaseList, Js::Phases *oppositePhase)
  304. {
  305. char16 buffer[MaxTokenSize];
  306. ZeroMemory(buffer, sizeof(buffer));
  307. Phase phase = ConfigFlagsTable::GetPhase(ParseString(buffer));
  308. if(InvalidPhase == phase)
  309. {
  310. throw Exception(_u("Invalid phase :"));
  311. }
  312. pPhaseList->Enable(phase);
  313. switch(CurChar())
  314. {
  315. case ':':
  316. {
  317. NextChar();
  318. Js::Range* oppositeRange = nullptr;
  319. if (oppositePhase && oppositePhase->IsEnabled(phase))
  320. {
  321. oppositeRange = oppositePhase->GetRange(phase);
  322. }
  323. ParseRange(pPhaseList->GetRange(phase), oppositeRange);
  324. break;
  325. }
  326. case ',':
  327. NextChar();
  328. if (oppositePhase)
  329. {
  330. // The whole phase is turned on/off so disable the opposite
  331. oppositePhase->Disable(phase);
  332. }
  333. ParsePhase(pPhaseList, oppositePhase);
  334. break;
  335. default:
  336. if (oppositePhase)
  337. {
  338. // The whole phase is turned on/off so disable the opposite
  339. oppositePhase->Disable(phase);
  340. }
  341. pPhaseList->GetRange(phase)->Clear();
  342. break;
  343. }
  344. }
  345. void
  346. CmdLineArgsParser::ParseNumberSet(Js::NumberSet * numberPairSet)
  347. {
  348. while (true)
  349. {
  350. int x = ParseInteger();
  351. numberPairSet->Add(x);
  352. if (CurChar() != ';')
  353. {
  354. break;
  355. }
  356. NextChar();
  357. }
  358. }
  359. void
  360. CmdLineArgsParser::ParseNumberPairSet(Js::NumberPairSet * numberPairSet)
  361. {
  362. while (true)
  363. {
  364. int line = ParseInteger();
  365. int col = -1;
  366. if (CurChar() == ',')
  367. {
  368. NextChar();
  369. col = ParseInteger();
  370. }
  371. numberPairSet->Add(line, col);
  372. if (CurChar() != ';')
  373. {
  374. break;
  375. }
  376. NextChar();
  377. }
  378. }
  379. void
  380. CmdLineArgsParser::ParseNumberTrioSet(Js::NumberTrioSet * numberTrioSet)
  381. {
  382. while (true)
  383. {
  384. int line = ParseInteger();
  385. int col = -1;
  386. int stmt = -1;
  387. if (CurChar() == ',')
  388. {
  389. NextChar();
  390. col = ParseInteger();
  391. }
  392. if (CurChar() == ',')
  393. {
  394. NextChar();
  395. stmt = ParseInteger();
  396. }
  397. numberTrioSet->Add(line, col, stmt);
  398. if (CurChar() != ';')
  399. {
  400. break;
  401. }
  402. NextChar();
  403. }
  404. }
  405. bool
  406. CmdLineArgsParser::ParseBoolean()
  407. {
  408. if (CurChar() == ':')
  409. {
  410. throw Exception(_u("':' not expected with a boolean flag"));
  411. }
  412. else if (CurChar() != '-' && CurChar() != ' ' && CurChar() != 0)
  413. {
  414. throw Exception(_u("Invalid character after boolean flag"));
  415. }
  416. else
  417. {
  418. return (CurChar() != '-');
  419. }
  420. }
  421. BSTR
  422. CmdLineArgsParser::GetCurrentString()
  423. {
  424. char16 buffer[MaxTokenSize];
  425. ZeroMemory(buffer, sizeof(buffer));
  426. switch (CurChar())
  427. {
  428. case ':':
  429. NextChar();
  430. return SysAllocString(ParseString(buffer, MaxTokenSize, false));
  431. case ' ':
  432. case 0:
  433. NextChar();
  434. return nullptr;
  435. default:
  436. throw Exception(_u("Expected ':'"));
  437. }
  438. }
  439. ///----------------------------------------------------------------------------
  440. ///
  441. /// CmdLineArgsParser::ParseFlag
  442. ///
  443. /// Parses:
  444. /// flag[:parameter]
  445. /// Flag is a string defined in Js:FlagNames.
  446. /// The type of expected parameter depends upon the flag. It can be
  447. /// 1. String
  448. /// 2. Number
  449. /// 3. Boolean
  450. /// 4. Phase
  451. ///
  452. /// In case of boolean the presence no parameter is expected. the value of the
  453. /// boolean flag is set to 'true'
  454. ///
  455. ///----------------------------------------------------------------------------
  456. void
  457. CmdLineArgsParser::ParseFlag()
  458. {
  459. char16 buffer[MaxTokenSize];
  460. ZeroMemory(buffer, sizeof(buffer));
  461. LPWSTR flagString = ParseString(buffer);
  462. Flag flag = ConfigFlagsTable::GetFlag(flagString);
  463. if(InvalidFlag == flag)
  464. {
  465. if (pCustomConfigFlags != nullptr)
  466. {
  467. if (pCustomConfigFlags->ParseFlag(flagString, this))
  468. {
  469. return;
  470. }
  471. }
  472. throw Exception(_u("Invalid Flag"));
  473. }
  474. FlagTypes flagType = ConfigFlagsTable::GetFlagType(flag);
  475. AssertMsg(InvalidFlagType != flagType, "Invalid flag type");
  476. this->flagTable.Enable(flag);
  477. if(FlagBoolean == flagType)
  478. {
  479. Boolean boolValue = ParseBoolean();
  480. this->flagTable.SetAsBoolean(flag, boolValue);
  481. }
  482. else
  483. {
  484. switch(CurChar())
  485. {
  486. case ':':
  487. NextChar();
  488. switch(flagType)
  489. {
  490. case FlagPhases:
  491. {
  492. Flag oppositeFlag = this->flagTable.GetOppositePhaseFlag(flag);
  493. Phases* oppositePhase = nullptr;
  494. if (oppositeFlag != InvalidFlag)
  495. {
  496. this->flagTable.Enable(oppositeFlag);
  497. oppositePhase = this->flagTable.GetAsPhase(oppositeFlag);
  498. }
  499. ParsePhase(this->flagTable.GetAsPhase(flag), oppositePhase);
  500. break;
  501. }
  502. case FlagString:
  503. *this->flagTable.GetAsString(flag) = ParseString(buffer, MaxTokenSize, false);
  504. break;
  505. case FlagNumber:
  506. *this->flagTable.GetAsNumber(flag) = ParseInteger();
  507. break;
  508. case FlagNumberSet:
  509. ParseNumberSet(this->flagTable.GetAsNumberSet(flag));
  510. break;
  511. case FlagNumberPairSet:
  512. ParseNumberPairSet(this->flagTable.GetAsNumberPairSet(flag));
  513. break;
  514. case FlagNumberTrioSet:
  515. ParseNumberTrioSet(this->flagTable.GetAsNumberTrioSet(flag));
  516. break;
  517. case FlagNumberRange:
  518. ParseNumberRange(this->flagTable.GetAsNumberRange(flag));
  519. break;
  520. default:
  521. AssertMsg(0, "Flag not Handled");
  522. }
  523. break;
  524. case ' ':
  525. case 0:
  526. break;
  527. default:
  528. throw Exception(_u("Expected ':'"));
  529. }
  530. }
  531. }
  532. ///----------------------------------------------------------------------------
  533. ///
  534. /// CmdLineArgsParser::Parse
  535. ///
  536. /// The main loop which parses 1 flag at a time
  537. ///
  538. ///----------------------------------------------------------------------------
  539. int
  540. CmdLineArgsParser::Parse(int argc, __in_ecount(argc) LPWSTR argv[])
  541. {
  542. int err = 0;
  543. for(int i = 1; i < argc; i++)
  544. {
  545. if ((err = Parse(argv[i])) != 0)
  546. {
  547. break;
  548. }
  549. }
  550. if(this->flagTable.Filename == nullptr)
  551. {
  552. this->flagTable.Filename = _u("ttdSentinal.js");
  553. }
  554. return err;
  555. }
  556. int CmdLineArgsParser::Parse(__in LPWSTR oneArg) throw()
  557. {
  558. int err = 0;
  559. char16 buffer[MaxTokenSize];
  560. ZeroMemory(buffer, sizeof(buffer));
  561. this->pszCurrentArg = oneArg;
  562. AssertMsg(NULL != this->pszCurrentArg, "How can command line give NULL argv's");
  563. try
  564. {
  565. switch(CurChar())
  566. {
  567. case '-' :
  568. if ('-' == PeekChar())
  569. {
  570. //support --
  571. NextChar();
  572. }
  573. //fallthrough
  574. #ifdef _WIN32
  575. // Only support '/' as a command line switch start char on Windows
  576. // for legacy reason. Deprecate on xplat, as it starts a path on Unix.
  577. case '/':
  578. #endif
  579. NextChar();
  580. if('?' == CurChar())
  581. {
  582. PrintUsage();
  583. return -1;
  584. }
  585. ParseFlag();
  586. break;
  587. default:
  588. if(NULL != this->flagTable.Filename)
  589. {
  590. throw Exception(_u("Duplicate filename entry"));
  591. }
  592. this->flagTable.Filename = ParseString(buffer, MaxTokenSize, false);
  593. break;
  594. }
  595. }
  596. catch(Exception &exp)
  597. {
  598. Output::Print(_u("%s : %s\n"), (LPCWSTR)exp, oneArg);
  599. err = -1;
  600. }
  601. return err;
  602. }
  603. ///----------------------------------------------------------------------------
  604. ///
  605. /// CmdLineArgsParser::CmdLineArgsParser
  606. ///
  607. /// Constructor
  608. ///
  609. ///----------------------------------------------------------------------------
  610. CmdLineArgsParser::CmdLineArgsParser(ICustomConfigFlags * pCustomConfigFlags, Js::ConfigFlagsTable& flagTable) :
  611. flagTable(flagTable), pCustomConfigFlags(pCustomConfigFlags)
  612. {
  613. this->pszCurrentArg = NULL;
  614. }
  615. CmdLineArgsParser::~CmdLineArgsParser()
  616. {
  617. flagTable.FinalizeConfiguration();
  618. }
  619. void CmdLineArgsParser::PrintUsage()
  620. {
  621. if (pCustomConfigFlags)
  622. {
  623. pCustomConfigFlags->PrintUsage();
  624. return;
  625. }
  626. Js::ConfigFlagsTable::PrintUsageString();
  627. }