BasicEvaluatedExpression.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. /** @import { Node } from "estree" */
  7. /**
  8. * @import {
  9. * Range,
  10. * VariableInfo,
  11. * Members,
  12. * MembersOptionals,
  13. * MemberRanges
  14. * } from "./JavascriptParser"
  15. */
  16. const TypeUnknown = 0;
  17. const TypeUndefined = 1;
  18. const TypeNull = 2;
  19. const TypeString = 3;
  20. const TypeNumber = 4;
  21. const TypeBoolean = 5;
  22. const TypeRegExp = 6;
  23. const TypeConditional = 7;
  24. const TypeArray = 8;
  25. const TypeConstArray = 9;
  26. const TypeIdentifier = 10;
  27. const TypeWrapped = 11;
  28. const TypeTemplateString = 12;
  29. const TypeBigInt = 13;
  30. /** @typedef {() => Members} GetMembers */
  31. /** @typedef {() => MembersOptionals} GetMembersOptionals */
  32. /** @typedef {() => MemberRanges} GetMemberRanges */
  33. // _flags bit layout: bits 0-3 type, then the boolean facts
  34. const FLAG_TYPE_MASK = 0b1111;
  35. const FLAG_TRUTHY = 0b10000;
  36. const FLAG_FALSY = 0b100000;
  37. const FLAG_NULLISH_KNOWN = 0b1000000;
  38. const FLAG_NULLISH = 0b10000000;
  39. const FLAG_SIDE_EFFECTS = 0b100000000;
  40. /**
  41. * An instance is allocated for a large share of walked expressions, so the
  42. * boolean facts live in one packed flags slot and the scalar/identifier
  43. * fields in five slots shared by type, with accessors keeping every public
  44. * field readable. This shrinks instances from 224 to 144 bytes on V8.
  45. */
  46. class BasicEvaluatedExpression {
  47. constructor() {
  48. this._flags = FLAG_SIDE_EFFECTS;
  49. /** @type {Range | undefined} */
  50. this._range = undefined;
  51. /** @type {Node | undefined} */
  52. this.expression = undefined;
  53. // _v1..._v5 hold the scalar type-specific fields, the accessors narrow
  54. // them by type; expression-valued fields get typed slots below
  55. /** @type {unknown} */
  56. this._v1 = undefined;
  57. /** @type {unknown} */
  58. this._v2 = undefined;
  59. /** @type {unknown} */
  60. this._v3 = undefined;
  61. /** @type {unknown} */
  62. this._v4 = undefined;
  63. /** @type {unknown} */
  64. this._v5 = undefined;
  65. // expression-valued fields stay plain data properties: a type that
  66. // references the class inside one of its own accessors makes tsc fork
  67. // the class into two unrelated identities
  68. /** @type {BasicEvaluatedExpression[] | undefined} */
  69. this.quasis = undefined;
  70. /** @type {BasicEvaluatedExpression[] | undefined} */
  71. this.parts = undefined;
  72. /** @type {BasicEvaluatedExpression[] | undefined} */
  73. this.items = undefined;
  74. /** @type {BasicEvaluatedExpression[] | undefined} */
  75. this.options = undefined;
  76. /** @type {BasicEvaluatedExpression | undefined | null} */
  77. this.prefix = undefined;
  78. /** @type {BasicEvaluatedExpression | undefined | null} */
  79. this.postfix = undefined;
  80. /** @type {BasicEvaluatedExpression[] | undefined} */
  81. this.wrappedInnerExpressions = undefined;
  82. }
  83. /**
  84. * Served lazily from the attached expression when not set explicitly:
  85. * `evaluateExpression`'s plain results share the expression's range, and
  86. * most evaluations never read it, so the node's range array is only
  87. * materialized on demand.
  88. * @returns {Range | undefined} range of the evaluated expression
  89. */
  90. get range() {
  91. const range = this._range;
  92. if (range !== undefined) return range;
  93. const expression = this.expression;
  94. return expression === undefined
  95. ? undefined
  96. : /** @type {Range} */ (
  97. /** @type {{ range?: Range }} */ (expression).range
  98. );
  99. }
  100. /**
  101. * @param {Range | undefined} value range of the evaluated expression
  102. */
  103. set range(value) {
  104. this._range = value;
  105. }
  106. /**
  107. * @returns {number} expression type
  108. */
  109. get type() {
  110. return this._flags & FLAG_TYPE_MASK;
  111. }
  112. /**
  113. * @param {number} value expression type
  114. */
  115. set type(value) {
  116. this._flags = (this._flags & ~FLAG_TYPE_MASK) | value;
  117. }
  118. /**
  119. * @returns {boolean} true when the expression is known truthy
  120. */
  121. get truthy() {
  122. return (this._flags & FLAG_TRUTHY) !== 0;
  123. }
  124. /**
  125. * @param {boolean} value known truthy
  126. */
  127. set truthy(value) {
  128. this._flags = value
  129. ? this._flags | FLAG_TRUTHY
  130. : this._flags & ~FLAG_TRUTHY;
  131. }
  132. /**
  133. * @returns {boolean} true when the expression is known falsy
  134. */
  135. get falsy() {
  136. return (this._flags & FLAG_FALSY) !== 0;
  137. }
  138. /**
  139. * @param {boolean} value known falsy
  140. */
  141. set falsy(value) {
  142. this._flags = value ? this._flags | FLAG_FALSY : this._flags & ~FLAG_FALSY;
  143. }
  144. /**
  145. * @returns {boolean | undefined} whether the value is nullish, when known
  146. */
  147. get nullish() {
  148. return (this._flags & FLAG_NULLISH_KNOWN) === 0
  149. ? undefined
  150. : (this._flags & FLAG_NULLISH) !== 0;
  151. }
  152. /**
  153. * @param {boolean | undefined} value whether the value is nullish
  154. */
  155. set nullish(value) {
  156. this._flags =
  157. value === undefined
  158. ? this._flags & ~(FLAG_NULLISH_KNOWN | FLAG_NULLISH)
  159. : value
  160. ? this._flags | FLAG_NULLISH_KNOWN | FLAG_NULLISH
  161. : (this._flags | FLAG_NULLISH_KNOWN) & ~FLAG_NULLISH;
  162. }
  163. /**
  164. * @returns {boolean} true when the expression could have side effects
  165. */
  166. get sideEffects() {
  167. return (this._flags & FLAG_SIDE_EFFECTS) !== 0;
  168. }
  169. /**
  170. * @param {boolean} value could have side effects
  171. */
  172. set sideEffects(value) {
  173. this._flags = value
  174. ? this._flags | FLAG_SIDE_EFFECTS
  175. : this._flags & ~FLAG_SIDE_EFFECTS;
  176. }
  177. /**
  178. * @returns {boolean | undefined} boolean value when boolean-typed
  179. */
  180. get bool() {
  181. return this.type === TypeBoolean
  182. ? /** @type {boolean} */ (this._v1)
  183. : undefined;
  184. }
  185. /**
  186. * @param {boolean | undefined} value boolean value
  187. */
  188. set bool(value) {
  189. this._v1 = value;
  190. }
  191. /**
  192. * @returns {number | undefined} number value when number-typed
  193. */
  194. get number() {
  195. return this.type === TypeNumber
  196. ? /** @type {number} */ (this._v1)
  197. : undefined;
  198. }
  199. /**
  200. * @param {number | undefined} value number value
  201. */
  202. set number(value) {
  203. this._v1 = value;
  204. }
  205. /**
  206. * @returns {bigint | undefined} bigint value when bigint-typed
  207. */
  208. get bigint() {
  209. return this.type === TypeBigInt
  210. ? /** @type {bigint} */ (this._v1)
  211. : undefined;
  212. }
  213. /**
  214. * @param {bigint | undefined} value bigint value
  215. */
  216. set bigint(value) {
  217. this._v1 = value;
  218. }
  219. /**
  220. * @returns {RegExp | undefined} regexp value when regexp-typed
  221. */
  222. get regExp() {
  223. return this.type === TypeRegExp
  224. ? /** @type {RegExp} */ (this._v1)
  225. : undefined;
  226. }
  227. /**
  228. * @param {RegExp | undefined} value regexp value
  229. */
  230. set regExp(value) {
  231. this._v1 = value;
  232. }
  233. /**
  234. * @returns {string | undefined} string value when string-typed
  235. */
  236. get string() {
  237. return this.type === TypeString
  238. ? /** @type {string} */ (this._v1)
  239. : undefined;
  240. }
  241. /**
  242. * @param {string | undefined} value string value
  243. */
  244. set string(value) {
  245. this._v1 = value;
  246. }
  247. /**
  248. * @returns {"cooked" | "raw" | undefined} template string kind
  249. */
  250. get templateStringKind() {
  251. return this.type === TypeTemplateString
  252. ? /** @type {"cooked" | "raw"} */ (this._v3)
  253. : undefined;
  254. }
  255. /**
  256. * @param {"cooked" | "raw" | undefined} value template string kind
  257. */
  258. set templateStringKind(value) {
  259. this._v3 = value;
  260. }
  261. /**
  262. * @returns {EXPECTED_ANY[] | undefined} const array values
  263. */
  264. get array() {
  265. return this.type === TypeConstArray
  266. ? /** @type {unknown[]} */ (this._v1)
  267. : undefined;
  268. }
  269. /**
  270. * @param {EXPECTED_ANY[] | undefined} value const array values
  271. */
  272. set array(value) {
  273. this._v1 = value;
  274. }
  275. /**
  276. * @returns {string | InstanceType<VariableInfo> | undefined} identifier
  277. */
  278. get identifier() {
  279. return this.type === TypeIdentifier
  280. ? /** @type {string | InstanceType<VariableInfo>} */ (this._v1)
  281. : undefined;
  282. }
  283. /**
  284. * @param {string | InstanceType<VariableInfo> | undefined} value identifier
  285. */
  286. set identifier(value) {
  287. this._v1 = value;
  288. }
  289. /**
  290. * @returns {string | InstanceType<VariableInfo> | undefined} root info
  291. */
  292. get rootInfo() {
  293. return this.type === TypeIdentifier
  294. ? /** @type {string | InstanceType<VariableInfo>} */ (this._v2)
  295. : undefined;
  296. }
  297. /**
  298. * @param {string | InstanceType<VariableInfo> | undefined} value root info
  299. */
  300. set rootInfo(value) {
  301. this._v2 = value;
  302. }
  303. /**
  304. * @returns {GetMembers | undefined} members getter
  305. */
  306. get getMembers() {
  307. return this.type === TypeIdentifier
  308. ? /** @type {GetMembers} */ (this._v3)
  309. : undefined;
  310. }
  311. /**
  312. * @param {GetMembers | undefined} value members getter
  313. */
  314. set getMembers(value) {
  315. this._v3 = value;
  316. }
  317. /**
  318. * @returns {GetMembersOptionals | undefined} members optionals getter
  319. */
  320. get getMembersOptionals() {
  321. return this.type === TypeIdentifier
  322. ? /** @type {GetMembersOptionals} */ (this._v4)
  323. : undefined;
  324. }
  325. /**
  326. * @param {GetMembersOptionals | undefined} value members optionals getter
  327. */
  328. set getMembersOptionals(value) {
  329. this._v4 = value;
  330. }
  331. /**
  332. * @returns {GetMemberRanges | undefined} member ranges getter
  333. */
  334. get getMemberRanges() {
  335. return this.type === TypeIdentifier
  336. ? /** @type {GetMemberRanges} */ (this._v5)
  337. : undefined;
  338. }
  339. /**
  340. * @param {GetMemberRanges | undefined} value member ranges getter
  341. */
  342. set getMemberRanges(value) {
  343. this._v5 = value;
  344. }
  345. isUnknown() {
  346. return this.type === TypeUnknown;
  347. }
  348. isNull() {
  349. return this.type === TypeNull;
  350. }
  351. isUndefined() {
  352. return this.type === TypeUndefined;
  353. }
  354. isString() {
  355. return this.type === TypeString;
  356. }
  357. isNumber() {
  358. return this.type === TypeNumber;
  359. }
  360. isBigInt() {
  361. return this.type === TypeBigInt;
  362. }
  363. isBoolean() {
  364. return this.type === TypeBoolean;
  365. }
  366. isRegExp() {
  367. return this.type === TypeRegExp;
  368. }
  369. isConditional() {
  370. return this.type === TypeConditional;
  371. }
  372. isArray() {
  373. return this.type === TypeArray;
  374. }
  375. isConstArray() {
  376. return this.type === TypeConstArray;
  377. }
  378. isIdentifier() {
  379. return this.type === TypeIdentifier;
  380. }
  381. isWrapped() {
  382. return this.type === TypeWrapped;
  383. }
  384. isTemplateString() {
  385. return this.type === TypeTemplateString;
  386. }
  387. /**
  388. * Is expression a primitive or an object type value?
  389. * @returns {boolean | undefined} true: primitive type, false: object type, undefined: unknown/runtime-defined
  390. */
  391. isPrimitiveType() {
  392. switch (this.type) {
  393. case TypeUndefined:
  394. case TypeNull:
  395. case TypeString:
  396. case TypeNumber:
  397. case TypeBoolean:
  398. case TypeBigInt:
  399. case TypeWrapped:
  400. case TypeTemplateString:
  401. return true;
  402. case TypeRegExp:
  403. case TypeArray:
  404. case TypeConstArray:
  405. return false;
  406. default:
  407. return undefined;
  408. }
  409. }
  410. /**
  411. * Is expression a runtime or compile-time value?
  412. * @returns {boolean} true: compile time value, false: runtime value
  413. */
  414. isCompileTimeValue() {
  415. switch (this.type) {
  416. case TypeUndefined:
  417. case TypeNull:
  418. case TypeString:
  419. case TypeNumber:
  420. case TypeBoolean:
  421. case TypeRegExp:
  422. case TypeConstArray:
  423. case TypeBigInt:
  424. return true;
  425. default:
  426. return false;
  427. }
  428. }
  429. /**
  430. * As compile time value.
  431. * @returns {undefined | null | string | number | boolean | RegExp | EXPECTED_ANY[] | bigint} the javascript value
  432. */
  433. asCompileTimeValue() {
  434. switch (this.type) {
  435. case TypeUndefined:
  436. return;
  437. case TypeNull:
  438. return null;
  439. case TypeString:
  440. return this.string;
  441. case TypeNumber:
  442. return this.number;
  443. case TypeBoolean:
  444. return this.bool;
  445. case TypeRegExp:
  446. return this.regExp;
  447. case TypeConstArray:
  448. return this.array;
  449. case TypeBigInt:
  450. return this.bigint;
  451. default:
  452. throw new Error(
  453. "asCompileTimeValue must only be called for compile-time values"
  454. );
  455. }
  456. }
  457. isTruthy() {
  458. return this.truthy;
  459. }
  460. isFalsy() {
  461. return this.falsy;
  462. }
  463. isNullish() {
  464. return this.nullish;
  465. }
  466. /**
  467. * Can this expression have side effects?
  468. * @returns {boolean} false: never has side effects
  469. */
  470. couldHaveSideEffects() {
  471. return this.sideEffects;
  472. }
  473. /**
  474. * Creates a boolean representation of this evaluated expression.
  475. * @returns {boolean | undefined} true: truthy, false: falsy, undefined: unknown
  476. */
  477. asBool() {
  478. if (this.truthy) return true;
  479. if (this.falsy || this.nullish) return false;
  480. if (this.isBoolean()) return this.bool;
  481. if (this.isNull()) return false;
  482. if (this.isUndefined()) return false;
  483. if (this.isString()) return this.string !== "";
  484. if (this.isNumber()) return this.number !== 0;
  485. if (this.isBigInt()) return this.bigint !== BigInt(0);
  486. if (this.isRegExp()) return true;
  487. if (this.isArray()) return true;
  488. if (this.isConstArray()) return true;
  489. if (this.isWrapped()) {
  490. return (this.prefix && this.prefix.asBool()) ||
  491. (this.postfix && this.postfix.asBool())
  492. ? true
  493. : undefined;
  494. }
  495. if (this.isTemplateString()) {
  496. const str = this.asString();
  497. if (typeof str === "string") return str !== "";
  498. }
  499. }
  500. /**
  501. * Creates a nullish coalescing representation of this evaluated expression.
  502. * @returns {boolean | undefined} true: nullish, false: not nullish, undefined: unknown
  503. */
  504. asNullish() {
  505. const nullish = this.isNullish();
  506. if (nullish === true || this.isNull() || this.isUndefined()) return true;
  507. if (nullish === false) return false;
  508. if (this.isTruthy()) return false;
  509. if (this.isBoolean()) return false;
  510. if (this.isString()) return false;
  511. if (this.isNumber()) return false;
  512. if (this.isBigInt()) return false;
  513. if (this.isRegExp()) return false;
  514. if (this.isArray()) return false;
  515. if (this.isConstArray()) return false;
  516. if (this.isTemplateString()) return false;
  517. if (this.isRegExp()) return false;
  518. }
  519. /**
  520. * Creates a string representation of this evaluated expression.
  521. * @returns {string | undefined} the string representation or undefined if not possible
  522. */
  523. asString() {
  524. if (this.isBoolean()) return `${this.bool}`;
  525. if (this.isNull()) return "null";
  526. if (this.isUndefined()) return "undefined";
  527. if (this.isString()) return this.string;
  528. if (this.isNumber()) return `${this.number}`;
  529. if (this.isBigInt()) return `${this.bigint}`;
  530. if (this.isRegExp()) return `${this.regExp}`;
  531. if (this.isArray()) {
  532. /** @type {string[]} */
  533. const array = [];
  534. for (const item of /** @type {BasicEvaluatedExpression[]} */ (
  535. this.items
  536. )) {
  537. const itemStr = item.asString();
  538. if (itemStr === undefined) return;
  539. array.push(itemStr);
  540. }
  541. return `${array}`;
  542. }
  543. if (this.isConstArray()) return `${this.array}`;
  544. if (this.isTemplateString()) {
  545. let str = "";
  546. for (const part of /** @type {BasicEvaluatedExpression[]} */ (
  547. this.parts
  548. )) {
  549. const partStr = part.asString();
  550. if (partStr === undefined) return;
  551. str += partStr;
  552. }
  553. return str;
  554. }
  555. }
  556. /**
  557. * Updates string using the provided string.
  558. * @param {string} string value
  559. * @returns {BasicEvaluatedExpression} basic evaluated expression
  560. */
  561. setString(string) {
  562. this.type = TypeString;
  563. this.string = string;
  564. this.sideEffects = false;
  565. return this;
  566. }
  567. setUndefined() {
  568. this.type = TypeUndefined;
  569. this.sideEffects = false;
  570. return this;
  571. }
  572. setNull() {
  573. this.type = TypeNull;
  574. this.sideEffects = false;
  575. return this;
  576. }
  577. /**
  578. * Set's the value of this expression to a number
  579. * @param {number} number number to set
  580. * @returns {this} this
  581. */
  582. setNumber(number) {
  583. this.type = TypeNumber;
  584. this.number = number;
  585. this.sideEffects = false;
  586. return this;
  587. }
  588. /**
  589. * Set's the value of this expression to a BigInt
  590. * @param {bigint} bigint bigint to set
  591. * @returns {this} this
  592. */
  593. setBigInt(bigint) {
  594. this.type = TypeBigInt;
  595. this.bigint = bigint;
  596. this.sideEffects = false;
  597. return this;
  598. }
  599. /**
  600. * Set's the value of this expression to a boolean
  601. * @param {boolean} bool boolean to set
  602. * @returns {this} this
  603. */
  604. setBoolean(bool) {
  605. this.type = TypeBoolean;
  606. this.bool = bool;
  607. this.sideEffects = false;
  608. return this;
  609. }
  610. /**
  611. * Set's the value of this expression to a regular expression
  612. * @param {RegExp} regExp regular expression to set
  613. * @returns {this} this
  614. */
  615. setRegExp(regExp) {
  616. this.type = TypeRegExp;
  617. this.regExp = regExp;
  618. this.sideEffects = false;
  619. return this;
  620. }
  621. /**
  622. * Set's the value of this expression to a particular identifier and its members.
  623. * @param {string | InstanceType<VariableInfo>} identifier identifier to set
  624. * @param {string | InstanceType<VariableInfo>} rootInfo root info
  625. * @param {GetMembers} getMembers members
  626. * @param {GetMembersOptionals=} getMembersOptionals optional members
  627. * @param {GetMemberRanges=} getMemberRanges ranges of progressively increasing sub-expressions
  628. * @returns {this} this
  629. */
  630. setIdentifier(
  631. identifier,
  632. rootInfo,
  633. getMembers,
  634. getMembersOptionals,
  635. getMemberRanges
  636. ) {
  637. this.type = TypeIdentifier;
  638. this.identifier = identifier;
  639. this.rootInfo = rootInfo;
  640. this.getMembers = getMembers;
  641. this.getMembersOptionals = getMembersOptionals;
  642. this.getMemberRanges = getMemberRanges;
  643. this.sideEffects = true;
  644. return this;
  645. }
  646. /**
  647. * Wraps an array of expressions with a prefix and postfix expression.
  648. * @param {BasicEvaluatedExpression | null | undefined} prefix Expression to be added before the innerExpressions
  649. * @param {BasicEvaluatedExpression | null | undefined} postfix Expression to be added after the innerExpressions
  650. * @param {BasicEvaluatedExpression[] | undefined} innerExpressions Expressions to be wrapped
  651. * @returns {this} this
  652. */
  653. setWrapped(prefix, postfix, innerExpressions) {
  654. this.type = TypeWrapped;
  655. this.prefix = prefix;
  656. this.postfix = postfix;
  657. this.wrappedInnerExpressions = innerExpressions;
  658. this.sideEffects = true;
  659. return this;
  660. }
  661. /**
  662. * Stores the options of a conditional expression.
  663. * @param {BasicEvaluatedExpression[]} options optional (consequent/alternate) expressions to be set
  664. * @returns {this} this
  665. */
  666. setOptions(options) {
  667. this.type = TypeConditional;
  668. this.options = options;
  669. this.sideEffects = true;
  670. return this;
  671. }
  672. /**
  673. * Adds the provided basic evaluated expression to the basic evaluated expression.
  674. * @param {BasicEvaluatedExpression[]} options optional (consequent/alternate) expressions to be added
  675. * @returns {this} this
  676. */
  677. addOptions(options) {
  678. if (!this.options) {
  679. this.type = TypeConditional;
  680. this.options = [];
  681. this.sideEffects = true;
  682. }
  683. for (const item of options) {
  684. this.options.push(item);
  685. }
  686. return this;
  687. }
  688. /**
  689. * Set's the value of this expression to an array of expressions.
  690. * @param {BasicEvaluatedExpression[]} items expressions to set
  691. * @returns {this} this
  692. */
  693. setItems(items) {
  694. this.type = TypeArray;
  695. this.items = items;
  696. this.sideEffects = items.some((i) => i.couldHaveSideEffects());
  697. return this;
  698. }
  699. /**
  700. * Set's the value of this expression to an array of strings.
  701. * @param {string[]} array array to set
  702. * @returns {this} this
  703. */
  704. setArray(array) {
  705. this.type = TypeConstArray;
  706. this.array = array;
  707. this.sideEffects = false;
  708. return this;
  709. }
  710. /**
  711. * Set's the value of this expression to a processed/unprocessed template string. Used
  712. * for evaluating TemplateLiteral expressions in the JavaScript Parser.
  713. * @param {BasicEvaluatedExpression[]} quasis template string quasis
  714. * @param {BasicEvaluatedExpression[]} parts template string parts
  715. * @param {"cooked" | "raw"} kind template string kind
  716. * @returns {this} this
  717. */
  718. setTemplateString(quasis, parts, kind) {
  719. this.type = TypeTemplateString;
  720. this.quasis = quasis;
  721. this.parts = parts;
  722. this.templateStringKind = kind;
  723. this.sideEffects = parts.some((p) => p.sideEffects);
  724. return this;
  725. }
  726. setTruthy() {
  727. this.falsy = false;
  728. this.truthy = true;
  729. this.nullish = false;
  730. return this;
  731. }
  732. setFalsy() {
  733. this.falsy = true;
  734. this.truthy = false;
  735. return this;
  736. }
  737. /**
  738. * Set's the value of the expression to nullish.
  739. * @param {boolean} value true, if the expression is nullish
  740. * @returns {this} this
  741. */
  742. setNullish(value) {
  743. this.nullish = value;
  744. if (value) return this.setFalsy();
  745. return this;
  746. }
  747. /**
  748. * Set's the range for the expression.
  749. * @param {Range} range range to set
  750. * @returns {this} this
  751. */
  752. setRange(range) {
  753. this._range = range;
  754. return this;
  755. }
  756. /**
  757. * Set whether or not the expression has side effects.
  758. * @param {boolean} sideEffects true, if the expression has side effects
  759. * @returns {this} this
  760. */
  761. setSideEffects(sideEffects = true) {
  762. this.sideEffects = sideEffects;
  763. return this;
  764. }
  765. /**
  766. * Set the expression node for the expression.
  767. * @param {Node | undefined} expression expression
  768. * @returns {this} this
  769. */
  770. setExpression(expression) {
  771. this.expression = expression;
  772. return this;
  773. }
  774. }
  775. /**
  776. * Returns is valid flags.
  777. * @param {string} flags regexp flags
  778. * @returns {boolean} is valid flags
  779. */
  780. BasicEvaluatedExpression.isValidRegExpFlags = (flags) => {
  781. const len = flags.length;
  782. if (len === 0) return true;
  783. // 8 standard flags: d g i m s u v y
  784. if (len > 8) return false;
  785. const D = 1; // d hasIndices
  786. const G = 2; // g global
  787. const I = 4; // i ignoreCase
  788. const M = 8; // m multiline
  789. const S = 16; // s dotAll
  790. const U = 32; // u unicode
  791. const V = 64; // v unicodeSets
  792. const Y = 128; // y sticky
  793. let seen = 0;
  794. for (let i = 0; i < len; i++) {
  795. let bit;
  796. switch (flags.charCodeAt(i)) {
  797. case 100 /* d */:
  798. bit = D;
  799. break;
  800. case 103 /* g */:
  801. bit = G;
  802. break;
  803. case 105 /* i */:
  804. bit = I;
  805. break;
  806. case 109 /* m */:
  807. bit = M;
  808. break;
  809. case 115 /* s */:
  810. bit = S;
  811. break;
  812. case 117 /* u */:
  813. bit = U;
  814. break;
  815. case 118 /* v */:
  816. bit = V;
  817. break;
  818. case 121 /* y */:
  819. bit = Y;
  820. break;
  821. default:
  822. return false;
  823. }
  824. if (seen & bit) return false; // duplicate flag
  825. seen |= bit;
  826. }
  827. // `u` and `v` are mutually exclusive
  828. if (seen & U && seen & V) return false;
  829. return true;
  830. };
  831. module.exports = BasicEvaluatedExpression;