| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595 | /*	MIT License http://www.opensource.org/licenses/mit-license.php	Author Tobias Koppers @sokra*/"use strict";/** @typedef {import("estree").Node} Node *//** @typedef {import("./JavascriptParser").Range} Range *//** @typedef {import("./JavascriptParser").VariableInfo} VariableInfo */const TypeUnknown = 0;const TypeUndefined = 1;const TypeNull = 2;const TypeString = 3;const TypeNumber = 4;const TypeBoolean = 5;const TypeRegExp = 6;const TypeConditional = 7;const TypeArray = 8;const TypeConstArray = 9;const TypeIdentifier = 10;const TypeWrapped = 11;const TypeTemplateString = 12;const TypeBigInt = 13;class BasicEvaluatedExpression {	constructor() {		this.type = TypeUnknown;		/** @type {Range | undefined} */		this.range = undefined;		/** @type {boolean} */		this.falsy = false;		/** @type {boolean} */		this.truthy = false;		/** @type {boolean | undefined} */		this.nullish = undefined;		/** @type {boolean} */		this.sideEffects = true;		/** @type {boolean | undefined} */		this.bool = undefined;		/** @type {number | undefined} */		this.number = undefined;		/** @type {bigint | undefined} */		this.bigint = undefined;		/** @type {RegExp | undefined} */		this.regExp = undefined;		/** @type {string | undefined} */		this.string = undefined;		/** @type {BasicEvaluatedExpression[] | undefined} */		this.quasis = undefined;		/** @type {BasicEvaluatedExpression[] | undefined} */		this.parts = undefined;		/** @type {EXPECTED_ANY[] | undefined} */		this.array = undefined;		/** @type {BasicEvaluatedExpression[] | undefined} */		this.items = undefined;		/** @type {BasicEvaluatedExpression[] | undefined} */		this.options = undefined;		/** @type {BasicEvaluatedExpression | undefined | null} */		this.prefix = undefined;		/** @type {BasicEvaluatedExpression | undefined | null} */		this.postfix = undefined;		/** @type {BasicEvaluatedExpression[] | undefined} */		this.wrappedInnerExpressions = undefined;		/** @type {string | VariableInfo | undefined} */		this.identifier = undefined;		/** @type {string | VariableInfo | undefined} */		this.rootInfo = undefined;		/** @type {(() => string[]) | undefined} */		this.getMembers = undefined;		/** @type {(() => boolean[]) | undefined} */		this.getMembersOptionals = undefined;		/** @type {(() => Range[]) | undefined} */		this.getMemberRanges = undefined;		/** @type {Node | undefined} */		this.expression = undefined;	}	isUnknown() {		return this.type === TypeUnknown;	}	isNull() {		return this.type === TypeNull;	}	isUndefined() {		return this.type === TypeUndefined;	}	isString() {		return this.type === TypeString;	}	isNumber() {		return this.type === TypeNumber;	}	isBigInt() {		return this.type === TypeBigInt;	}	isBoolean() {		return this.type === TypeBoolean;	}	isRegExp() {		return this.type === TypeRegExp;	}	isConditional() {		return this.type === TypeConditional;	}	isArray() {		return this.type === TypeArray;	}	isConstArray() {		return this.type === TypeConstArray;	}	isIdentifier() {		return this.type === TypeIdentifier;	}	isWrapped() {		return this.type === TypeWrapped;	}	isTemplateString() {		return this.type === TypeTemplateString;	}	/**	 * Is expression a primitive or an object type value?	 * @returns {boolean | undefined} true: primitive type, false: object type, undefined: unknown/runtime-defined	 */	isPrimitiveType() {		switch (this.type) {			case TypeUndefined:			case TypeNull:			case TypeString:			case TypeNumber:			case TypeBoolean:			case TypeBigInt:			case TypeWrapped:			case TypeTemplateString:				return true;			case TypeRegExp:			case TypeArray:			case TypeConstArray:				return false;			default:				return undefined;		}	}	/**	 * Is expression a runtime or compile-time value?	 * @returns {boolean} true: compile time value, false: runtime value	 */	isCompileTimeValue() {		switch (this.type) {			case TypeUndefined:			case TypeNull:			case TypeString:			case TypeNumber:			case TypeBoolean:			case TypeRegExp:			case TypeConstArray:			case TypeBigInt:				return true;			default:				return false;		}	}	/**	 * Gets the compile-time value of the expression	 * @returns {undefined | null | string | number | boolean | RegExp | EXPECTED_ANY[] | bigint} the javascript value	 */	asCompileTimeValue() {		switch (this.type) {			case TypeUndefined:				return;			case TypeNull:				return null;			case TypeString:				return this.string;			case TypeNumber:				return this.number;			case TypeBoolean:				return this.bool;			case TypeRegExp:				return this.regExp;			case TypeConstArray:				return this.array;			case TypeBigInt:				return this.bigint;			default:				throw new Error(					"asCompileTimeValue must only be called for compile-time values"				);		}	}	isTruthy() {		return this.truthy;	}	isFalsy() {		return this.falsy;	}	isNullish() {		return this.nullish;	}	/**	 * Can this expression have side effects?	 * @returns {boolean} false: never has side effects	 */	couldHaveSideEffects() {		return this.sideEffects;	}	/**	 * Creates a boolean representation of this evaluated expression.	 * @returns {boolean | undefined} true: truthy, false: falsy, undefined: unknown	 */	asBool() {		if (this.truthy) return true;		if (this.falsy || this.nullish) return false;		if (this.isBoolean()) return this.bool;		if (this.isNull()) return false;		if (this.isUndefined()) return false;		if (this.isString()) return this.string !== "";		if (this.isNumber()) return this.number !== 0;		if (this.isBigInt()) return this.bigint !== BigInt(0);		if (this.isRegExp()) return true;		if (this.isArray()) return true;		if (this.isConstArray()) return true;		if (this.isWrapped()) {			return (this.prefix && this.prefix.asBool()) ||				(this.postfix && this.postfix.asBool())				? true				: undefined;		}		if (this.isTemplateString()) {			const str = this.asString();			if (typeof str === "string") return str !== "";		}	}	/**	 * Creates a nullish coalescing representation of this evaluated expression.	 * @returns {boolean | undefined} true: nullish, false: not nullish, undefined: unknown	 */	asNullish() {		const nullish = this.isNullish();		if (nullish === true || this.isNull() || this.isUndefined()) return true;		if (nullish === false) return false;		if (this.isTruthy()) return false;		if (this.isBoolean()) return false;		if (this.isString()) return false;		if (this.isNumber()) return false;		if (this.isBigInt()) return false;		if (this.isRegExp()) return false;		if (this.isArray()) return false;		if (this.isConstArray()) return false;		if (this.isTemplateString()) return false;		if (this.isRegExp()) return false;	}	/**	 * Creates a string representation of this evaluated expression.	 * @returns {string | undefined} the string representation or undefined if not possible	 */	asString() {		if (this.isBoolean()) return `${this.bool}`;		if (this.isNull()) return "null";		if (this.isUndefined()) return "undefined";		if (this.isString()) return this.string;		if (this.isNumber()) return `${this.number}`;		if (this.isBigInt()) return `${this.bigint}`;		if (this.isRegExp()) return `${this.regExp}`;		if (this.isArray()) {			const array = [];			for (const item of /** @type {BasicEvaluatedExpression[]} */ (				this.items			)) {				const itemStr = item.asString();				if (itemStr === undefined) return;				array.push(itemStr);			}			return `${array}`;		}		if (this.isConstArray()) return `${this.array}`;		if (this.isTemplateString()) {			let str = "";			for (const part of /** @type {BasicEvaluatedExpression[]} */ (				this.parts			)) {				const partStr = part.asString();				if (partStr === undefined) return;				str += partStr;			}			return str;		}	}	/**	 * @param {string} string value	 * @returns {BasicEvaluatedExpression} basic evaluated expression	 */	setString(string) {		this.type = TypeString;		this.string = string;		this.sideEffects = false;		return this;	}	setUndefined() {		this.type = TypeUndefined;		this.sideEffects = false;		return this;	}	setNull() {		this.type = TypeNull;		this.sideEffects = false;		return this;	}	/**	 * Set's the value of this expression to a number	 * @param {number} number number to set	 * @returns {this} this	 */	setNumber(number) {		this.type = TypeNumber;		this.number = number;		this.sideEffects = false;		return this;	}	/**	 * Set's the value of this expression to a BigInt	 * @param {bigint} bigint bigint to set	 * @returns {this} this	 */	setBigInt(bigint) {		this.type = TypeBigInt;		this.bigint = bigint;		this.sideEffects = false;		return this;	}	/**	 * Set's the value of this expression to a boolean	 * @param {boolean} bool boolean to set	 * @returns {this} this	 */	setBoolean(bool) {		this.type = TypeBoolean;		this.bool = bool;		this.sideEffects = false;		return this;	}	/**	 * Set's the value of this expression to a regular expression	 * @param {RegExp} regExp regular expression to set	 * @returns {this} this	 */	setRegExp(regExp) {		this.type = TypeRegExp;		this.regExp = regExp;		this.sideEffects = false;		return this;	}	/**	 * Set's the value of this expression to a particular identifier and its members.	 * @param {string | VariableInfo} identifier identifier to set	 * @param {string | VariableInfo} rootInfo root info	 * @param {() => string[]} getMembers members	 * @param {() => boolean[]=} getMembersOptionals optional members	 * @param {() => Range[]=} getMemberRanges ranges of progressively increasing sub-expressions	 * @returns {this} this	 */	setIdentifier(		identifier,		rootInfo,		getMembers,		getMembersOptionals,		getMemberRanges	) {		this.type = TypeIdentifier;		this.identifier = identifier;		this.rootInfo = rootInfo;		this.getMembers = getMembers;		this.getMembersOptionals = getMembersOptionals;		this.getMemberRanges = getMemberRanges;		this.sideEffects = true;		return this;	}	/**	 * Wraps an array of expressions with a prefix and postfix expression.	 * @param {BasicEvaluatedExpression | null | undefined} prefix Expression to be added before the innerExpressions	 * @param {BasicEvaluatedExpression | null | undefined} postfix Expression to be added after the innerExpressions	 * @param {BasicEvaluatedExpression[] | undefined} innerExpressions Expressions to be wrapped	 * @returns {this} this	 */	setWrapped(prefix, postfix, innerExpressions) {		this.type = TypeWrapped;		this.prefix = prefix;		this.postfix = postfix;		this.wrappedInnerExpressions = innerExpressions;		this.sideEffects = true;		return this;	}	/**	 * Stores the options of a conditional expression.	 * @param {BasicEvaluatedExpression[]} options optional (consequent/alternate) expressions to be set	 * @returns {this} this	 */	setOptions(options) {		this.type = TypeConditional;		this.options = options;		this.sideEffects = true;		return this;	}	/**	 * Adds options to a conditional expression.	 * @param {BasicEvaluatedExpression[]} options optional (consequent/alternate) expressions to be added	 * @returns {this} this	 */	addOptions(options) {		if (!this.options) {			this.type = TypeConditional;			this.options = [];			this.sideEffects = true;		}		for (const item of options) {			this.options.push(item);		}		return this;	}	/**	 * Set's the value of this expression to an array of expressions.	 * @param {BasicEvaluatedExpression[]} items expressions to set	 * @returns {this} this	 */	setItems(items) {		this.type = TypeArray;		this.items = items;		this.sideEffects = items.some(i => i.couldHaveSideEffects());		return this;	}	/**	 * Set's the value of this expression to an array of strings.	 * @param {string[]} array array to set	 * @returns {this} this	 */	setArray(array) {		this.type = TypeConstArray;		this.array = array;		this.sideEffects = false;		return this;	}	/**	 * Set's the value of this expression to a processed/unprocessed template string. Used	 * for evaluating TemplateLiteral expressions in the JavaScript Parser.	 * @param {BasicEvaluatedExpression[]} quasis template string quasis	 * @param {BasicEvaluatedExpression[]} parts template string parts	 * @param {"cooked" | "raw"} kind template string kind	 * @returns {this} this	 */	setTemplateString(quasis, parts, kind) {		this.type = TypeTemplateString;		this.quasis = quasis;		this.parts = parts;		this.templateStringKind = kind;		this.sideEffects = parts.some(p => p.sideEffects);		return this;	}	setTruthy() {		this.falsy = false;		this.truthy = true;		this.nullish = false;		return this;	}	setFalsy() {		this.falsy = true;		this.truthy = false;		return this;	}	/**	 * Set's the value of the expression to nullish.	 * @param {boolean} value true, if the expression is nullish	 * @returns {this} this	 */	setNullish(value) {		this.nullish = value;		if (value) return this.setFalsy();		return this;	}	/**	 * Set's the range for the expression.	 * @param {Range} range range to set	 * @returns {this} this	 */	setRange(range) {		this.range = range;		return this;	}	/**	 * Set whether or not the expression has side effects.	 * @param {boolean} sideEffects true, if the expression has side effects	 * @returns {this} this	 */	setSideEffects(sideEffects = true) {		this.sideEffects = sideEffects;		return this;	}	/**	 * Set the expression node for the expression.	 * @param {Node | undefined} expression expression	 * @returns {this} this	 */	setExpression(expression) {		this.expression = expression;		return this;	}}/** * @param {string} flags regexp flags * @returns {boolean} is valid flags */BasicEvaluatedExpression.isValidRegExpFlags = flags => {	const len = flags.length;	if (len === 0) return true;	if (len > 4) return false;	// cspell:word gimy	let remaining = 0b0000; // bit per RegExp flag: gimy	for (let i = 0; i < len; i++) {		switch (flags.charCodeAt(i)) {			case 103 /* g */:				if (remaining & 0b1000) return false;				remaining |= 0b1000;				break;			case 105 /* i */:				if (remaining & 0b0100) return false;				remaining |= 0b0100;				break;			case 109 /* m */:				if (remaining & 0b0010) return false;				remaining |= 0b0010;				break;			case 121 /* y */:				if (remaining & 0b0001) return false;				remaining |= 0b0001;				break;			default:				return false;		}	}	return true;};module.exports = BasicEvaluatedExpression;
 |