source: imaps-frontend/node_modules/webpack/lib/javascript/BasicEvaluatedExpression.js

main
Last change on this file was 79a0317, checked in by stefan toskovski <stefantoska84@…>, 4 days ago

F4 Finalna Verzija

  • Property mode set to 100644
File size: 14.2 KB
RevLine 
[79a0317]1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8/** @typedef {import("estree").Node} Node */
9/** @typedef {import("./JavascriptParser").Range} Range */
10/** @typedef {import("./JavascriptParser").VariableInfoInterface} VariableInfoInterface */
11
12const TypeUnknown = 0;
13const TypeUndefined = 1;
14const TypeNull = 2;
15const TypeString = 3;
16const TypeNumber = 4;
17const TypeBoolean = 5;
18const TypeRegExp = 6;
19const TypeConditional = 7;
20const TypeArray = 8;
21const TypeConstArray = 9;
22const TypeIdentifier = 10;
23const TypeWrapped = 11;
24const TypeTemplateString = 12;
25const TypeBigInt = 13;
26
27class BasicEvaluatedExpression {
28 constructor() {
29 this.type = TypeUnknown;
30 /** @type {Range | undefined} */
31 this.range = undefined;
32 /** @type {boolean} */
33 this.falsy = false;
34 /** @type {boolean} */
35 this.truthy = false;
36 /** @type {boolean | undefined} */
37 this.nullish = undefined;
38 /** @type {boolean} */
39 this.sideEffects = true;
40 /** @type {boolean | undefined} */
41 this.bool = undefined;
42 /** @type {number | undefined} */
43 this.number = undefined;
44 /** @type {bigint | undefined} */
45 this.bigint = undefined;
46 /** @type {RegExp | undefined} */
47 this.regExp = undefined;
48 /** @type {string | undefined} */
49 this.string = undefined;
50 /** @type {BasicEvaluatedExpression[] | undefined} */
51 this.quasis = undefined;
52 /** @type {BasicEvaluatedExpression[] | undefined} */
53 this.parts = undefined;
54 /** @type {any[] | undefined} */
55 this.array = undefined;
56 /** @type {BasicEvaluatedExpression[] | undefined} */
57 this.items = undefined;
58 /** @type {BasicEvaluatedExpression[] | undefined} */
59 this.options = undefined;
60 /** @type {BasicEvaluatedExpression | undefined | null} */
61 this.prefix = undefined;
62 /** @type {BasicEvaluatedExpression | undefined | null} */
63 this.postfix = undefined;
64 /** @type {BasicEvaluatedExpression[] | undefined} */
65 this.wrappedInnerExpressions = undefined;
66 /** @type {string | VariableInfoInterface | undefined} */
67 this.identifier = undefined;
68 /** @type {string | VariableInfoInterface | undefined} */
69 this.rootInfo = undefined;
70 /** @type {(() => string[]) | undefined} */
71 this.getMembers = undefined;
72 /** @type {(() => boolean[]) | undefined} */
73 this.getMembersOptionals = undefined;
74 /** @type {(() => Range[]) | undefined} */
75 this.getMemberRanges = undefined;
76 /** @type {Node | undefined} */
77 this.expression = undefined;
78 }
79
80 isUnknown() {
81 return this.type === TypeUnknown;
82 }
83
84 isNull() {
85 return this.type === TypeNull;
86 }
87
88 isUndefined() {
89 return this.type === TypeUndefined;
90 }
91
92 isString() {
93 return this.type === TypeString;
94 }
95
96 isNumber() {
97 return this.type === TypeNumber;
98 }
99
100 isBigInt() {
101 return this.type === TypeBigInt;
102 }
103
104 isBoolean() {
105 return this.type === TypeBoolean;
106 }
107
108 isRegExp() {
109 return this.type === TypeRegExp;
110 }
111
112 isConditional() {
113 return this.type === TypeConditional;
114 }
115
116 isArray() {
117 return this.type === TypeArray;
118 }
119
120 isConstArray() {
121 return this.type === TypeConstArray;
122 }
123
124 isIdentifier() {
125 return this.type === TypeIdentifier;
126 }
127
128 isWrapped() {
129 return this.type === TypeWrapped;
130 }
131
132 isTemplateString() {
133 return this.type === TypeTemplateString;
134 }
135
136 /**
137 * Is expression a primitive or an object type value?
138 * @returns {boolean | undefined} true: primitive type, false: object type, undefined: unknown/runtime-defined
139 */
140 isPrimitiveType() {
141 switch (this.type) {
142 case TypeUndefined:
143 case TypeNull:
144 case TypeString:
145 case TypeNumber:
146 case TypeBoolean:
147 case TypeBigInt:
148 case TypeWrapped:
149 case TypeTemplateString:
150 return true;
151 case TypeRegExp:
152 case TypeArray:
153 case TypeConstArray:
154 return false;
155 default:
156 return undefined;
157 }
158 }
159
160 /**
161 * Is expression a runtime or compile-time value?
162 * @returns {boolean} true: compile time value, false: runtime value
163 */
164 isCompileTimeValue() {
165 switch (this.type) {
166 case TypeUndefined:
167 case TypeNull:
168 case TypeString:
169 case TypeNumber:
170 case TypeBoolean:
171 case TypeRegExp:
172 case TypeConstArray:
173 case TypeBigInt:
174 return true;
175 default:
176 return false;
177 }
178 }
179
180 /**
181 * Gets the compile-time value of the expression
182 * @returns {any} the javascript value
183 */
184 asCompileTimeValue() {
185 switch (this.type) {
186 case TypeUndefined:
187 return;
188 case TypeNull:
189 return null;
190 case TypeString:
191 return this.string;
192 case TypeNumber:
193 return this.number;
194 case TypeBoolean:
195 return this.bool;
196 case TypeRegExp:
197 return this.regExp;
198 case TypeConstArray:
199 return this.array;
200 case TypeBigInt:
201 return this.bigint;
202 default:
203 throw new Error(
204 "asCompileTimeValue must only be called for compile-time values"
205 );
206 }
207 }
208
209 isTruthy() {
210 return this.truthy;
211 }
212
213 isFalsy() {
214 return this.falsy;
215 }
216
217 isNullish() {
218 return this.nullish;
219 }
220
221 /**
222 * Can this expression have side effects?
223 * @returns {boolean} false: never has side effects
224 */
225 couldHaveSideEffects() {
226 return this.sideEffects;
227 }
228
229 /**
230 * Creates a boolean representation of this evaluated expression.
231 * @returns {boolean | undefined} true: truthy, false: falsy, undefined: unknown
232 */
233 asBool() {
234 if (this.truthy) return true;
235 if (this.falsy || this.nullish) return false;
236 if (this.isBoolean()) return this.bool;
237 if (this.isNull()) return false;
238 if (this.isUndefined()) return false;
239 if (this.isString()) return this.string !== "";
240 if (this.isNumber()) return this.number !== 0;
241 if (this.isBigInt()) return this.bigint !== BigInt(0);
242 if (this.isRegExp()) return true;
243 if (this.isArray()) return true;
244 if (this.isConstArray()) return true;
245 if (this.isWrapped()) {
246 return (this.prefix && this.prefix.asBool()) ||
247 (this.postfix && this.postfix.asBool())
248 ? true
249 : undefined;
250 }
251 if (this.isTemplateString()) {
252 const str = this.asString();
253 if (typeof str === "string") return str !== "";
254 }
255 }
256
257 /**
258 * Creates a nullish coalescing representation of this evaluated expression.
259 * @returns {boolean | undefined} true: nullish, false: not nullish, undefined: unknown
260 */
261 asNullish() {
262 const nullish = this.isNullish();
263
264 if (nullish === true || this.isNull() || this.isUndefined()) return true;
265
266 if (nullish === false) return false;
267 if (this.isTruthy()) return false;
268 if (this.isBoolean()) return false;
269 if (this.isString()) return false;
270 if (this.isNumber()) return false;
271 if (this.isBigInt()) return false;
272 if (this.isRegExp()) return false;
273 if (this.isArray()) return false;
274 if (this.isConstArray()) return false;
275 if (this.isTemplateString()) return false;
276 if (this.isRegExp()) return false;
277 }
278
279 /**
280 * Creates a string representation of this evaluated expression.
281 * @returns {string | undefined} the string representation or undefined if not possible
282 */
283 asString() {
284 if (this.isBoolean()) return `${this.bool}`;
285 if (this.isNull()) return "null";
286 if (this.isUndefined()) return "undefined";
287 if (this.isString()) return this.string;
288 if (this.isNumber()) return `${this.number}`;
289 if (this.isBigInt()) return `${this.bigint}`;
290 if (this.isRegExp()) return `${this.regExp}`;
291 if (this.isArray()) {
292 const array = [];
293 for (const item of /** @type {BasicEvaluatedExpression[]} */ (
294 this.items
295 )) {
296 const itemStr = item.asString();
297 if (itemStr === undefined) return;
298 array.push(itemStr);
299 }
300 return `${array}`;
301 }
302 if (this.isConstArray()) return `${this.array}`;
303 if (this.isTemplateString()) {
304 let str = "";
305 for (const part of /** @type {BasicEvaluatedExpression[]} */ (
306 this.parts
307 )) {
308 const partStr = part.asString();
309 if (partStr === undefined) return;
310 str += partStr;
311 }
312 return str;
313 }
314 }
315
316 /**
317 * @param {string} string value
318 * @returns {BasicEvaluatedExpression} basic evaluated expression
319 */
320 setString(string) {
321 this.type = TypeString;
322 this.string = string;
323 this.sideEffects = false;
324 return this;
325 }
326
327 setUndefined() {
328 this.type = TypeUndefined;
329 this.sideEffects = false;
330 return this;
331 }
332
333 setNull() {
334 this.type = TypeNull;
335 this.sideEffects = false;
336 return this;
337 }
338
339 /**
340 * Set's the value of this expression to a number
341 * @param {number} number number to set
342 * @returns {this} this
343 */
344 setNumber(number) {
345 this.type = TypeNumber;
346 this.number = number;
347 this.sideEffects = false;
348 return this;
349 }
350
351 /**
352 * Set's the value of this expression to a BigInt
353 * @param {bigint} bigint bigint to set
354 * @returns {this} this
355 */
356 setBigInt(bigint) {
357 this.type = TypeBigInt;
358 this.bigint = bigint;
359 this.sideEffects = false;
360 return this;
361 }
362
363 /**
364 * Set's the value of this expression to a boolean
365 * @param {boolean} bool boolean to set
366 * @returns {this} this
367 */
368 setBoolean(bool) {
369 this.type = TypeBoolean;
370 this.bool = bool;
371 this.sideEffects = false;
372 return this;
373 }
374
375 /**
376 * Set's the value of this expression to a regular expression
377 * @param {RegExp} regExp regular expression to set
378 * @returns {this} this
379 */
380 setRegExp(regExp) {
381 this.type = TypeRegExp;
382 this.regExp = regExp;
383 this.sideEffects = false;
384 return this;
385 }
386
387 /**
388 * Set's the value of this expression to a particular identifier and its members.
389 * @param {string | VariableInfoInterface} identifier identifier to set
390 * @param {string | VariableInfoInterface} rootInfo root info
391 * @param {() => string[]} getMembers members
392 * @param {() => boolean[]=} getMembersOptionals optional members
393 * @param {() => Range[]=} getMemberRanges ranges of progressively increasing sub-expressions
394 * @returns {this} this
395 */
396 setIdentifier(
397 identifier,
398 rootInfo,
399 getMembers,
400 getMembersOptionals,
401 getMemberRanges
402 ) {
403 this.type = TypeIdentifier;
404 this.identifier = identifier;
405 this.rootInfo = rootInfo;
406 this.getMembers = getMembers;
407 this.getMembersOptionals = getMembersOptionals;
408 this.getMemberRanges = getMemberRanges;
409 this.sideEffects = true;
410 return this;
411 }
412
413 /**
414 * Wraps an array of expressions with a prefix and postfix expression.
415 * @param {BasicEvaluatedExpression | null | undefined} prefix Expression to be added before the innerExpressions
416 * @param {BasicEvaluatedExpression | null | undefined} postfix Expression to be added after the innerExpressions
417 * @param {BasicEvaluatedExpression[] | undefined} innerExpressions Expressions to be wrapped
418 * @returns {this} this
419 */
420 setWrapped(prefix, postfix, innerExpressions) {
421 this.type = TypeWrapped;
422 this.prefix = prefix;
423 this.postfix = postfix;
424 this.wrappedInnerExpressions = innerExpressions;
425 this.sideEffects = true;
426 return this;
427 }
428
429 /**
430 * Stores the options of a conditional expression.
431 * @param {BasicEvaluatedExpression[]} options optional (consequent/alternate) expressions to be set
432 * @returns {this} this
433 */
434 setOptions(options) {
435 this.type = TypeConditional;
436 this.options = options;
437 this.sideEffects = true;
438 return this;
439 }
440
441 /**
442 * Adds options to a conditional expression.
443 * @param {BasicEvaluatedExpression[]} options optional (consequent/alternate) expressions to be added
444 * @returns {this} this
445 */
446 addOptions(options) {
447 if (!this.options) {
448 this.type = TypeConditional;
449 this.options = [];
450 this.sideEffects = true;
451 }
452 for (const item of options) {
453 this.options.push(item);
454 }
455 return this;
456 }
457
458 /**
459 * Set's the value of this expression to an array of expressions.
460 * @param {BasicEvaluatedExpression[]} items expressions to set
461 * @returns {this} this
462 */
463 setItems(items) {
464 this.type = TypeArray;
465 this.items = items;
466 this.sideEffects = items.some(i => i.couldHaveSideEffects());
467 return this;
468 }
469
470 /**
471 * Set's the value of this expression to an array of strings.
472 * @param {string[]} array array to set
473 * @returns {this} this
474 */
475 setArray(array) {
476 this.type = TypeConstArray;
477 this.array = array;
478 this.sideEffects = false;
479 return this;
480 }
481
482 /**
483 * Set's the value of this expression to a processed/unprocessed template string. Used
484 * for evaluating TemplateLiteral expressions in the JavaScript Parser.
485 * @param {BasicEvaluatedExpression[]} quasis template string quasis
486 * @param {BasicEvaluatedExpression[]} parts template string parts
487 * @param {"cooked" | "raw"} kind template string kind
488 * @returns {this} this
489 */
490 setTemplateString(quasis, parts, kind) {
491 this.type = TypeTemplateString;
492 this.quasis = quasis;
493 this.parts = parts;
494 this.templateStringKind = kind;
495 this.sideEffects = parts.some(p => p.sideEffects);
496 return this;
497 }
498
499 setTruthy() {
500 this.falsy = false;
501 this.truthy = true;
502 this.nullish = false;
503 return this;
504 }
505
506 setFalsy() {
507 this.falsy = true;
508 this.truthy = false;
509 return this;
510 }
511
512 /**
513 * Set's the value of the expression to nullish.
514 * @param {boolean} value true, if the expression is nullish
515 * @returns {this} this
516 */
517 setNullish(value) {
518 this.nullish = value;
519
520 if (value) return this.setFalsy();
521
522 return this;
523 }
524
525 /**
526 * Set's the range for the expression.
527 * @param {[number, number]} range range to set
528 * @returns {this} this
529 */
530 setRange(range) {
531 this.range = range;
532 return this;
533 }
534
535 /**
536 * Set whether or not the expression has side effects.
537 * @param {boolean} sideEffects true, if the expression has side effects
538 * @returns {this} this
539 */
540 setSideEffects(sideEffects = true) {
541 this.sideEffects = sideEffects;
542 return this;
543 }
544
545 /**
546 * Set the expression node for the expression.
547 * @param {Node | undefined} expression expression
548 * @returns {this} this
549 */
550 setExpression(expression) {
551 this.expression = expression;
552 return this;
553 }
554}
555
556/**
557 * @param {string} flags regexp flags
558 * @returns {boolean} is valid flags
559 */
560BasicEvaluatedExpression.isValidRegExpFlags = flags => {
561 const len = flags.length;
562
563 if (len === 0) return true;
564 if (len > 4) return false;
565
566 // cspell:word gimy
567 let remaining = 0b0000; // bit per RegExp flag: gimy
568
569 for (let i = 0; i < len; i++)
570 switch (flags.charCodeAt(i)) {
571 case 103 /* g */:
572 if (remaining & 0b1000) return false;
573 remaining |= 0b1000;
574 break;
575 case 105 /* i */:
576 if (remaining & 0b0100) return false;
577 remaining |= 0b0100;
578 break;
579 case 109 /* m */:
580 if (remaining & 0b0010) return false;
581 remaining |= 0b0010;
582 break;
583 case 121 /* y */:
584 if (remaining & 0b0001) return false;
585 remaining |= 0b0001;
586 break;
587 default:
588 return false;
589 }
590
591 return true;
592};
593
594module.exports = BasicEvaluatedExpression;
Note: See TracBrowser for help on using the repository browser.