| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928 |
- /*
- MIT License http://www.opensource.org/licenses/mit-license.php
- Author Tobias Koppers @sokra
- */
- "use strict";
- /** @import { Node } from "estree" */
- /**
- * @import {
- * Range,
- * VariableInfo,
- * Members,
- * MembersOptionals,
- * MemberRanges
- * } from "./JavascriptParser"
- */
- 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;
- /** @typedef {() => Members} GetMembers */
- /** @typedef {() => MembersOptionals} GetMembersOptionals */
- /** @typedef {() => MemberRanges} GetMemberRanges */
- // _flags bit layout: bits 0-3 type, then the boolean facts
- const FLAG_TYPE_MASK = 0b1111;
- const FLAG_TRUTHY = 0b10000;
- const FLAG_FALSY = 0b100000;
- const FLAG_NULLISH_KNOWN = 0b1000000;
- const FLAG_NULLISH = 0b10000000;
- const FLAG_SIDE_EFFECTS = 0b100000000;
- /**
- * An instance is allocated for a large share of walked expressions, so the
- * boolean facts live in one packed flags slot and the scalar/identifier
- * fields in five slots shared by type, with accessors keeping every public
- * field readable. This shrinks instances from 224 to 144 bytes on V8.
- */
- class BasicEvaluatedExpression {
- constructor() {
- this._flags = FLAG_SIDE_EFFECTS;
- /** @type {Range | undefined} */
- this._range = undefined;
- /** @type {Node | undefined} */
- this.expression = undefined;
- // _v1..._v5 hold the scalar type-specific fields, the accessors narrow
- // them by type; expression-valued fields get typed slots below
- /** @type {unknown} */
- this._v1 = undefined;
- /** @type {unknown} */
- this._v2 = undefined;
- /** @type {unknown} */
- this._v3 = undefined;
- /** @type {unknown} */
- this._v4 = undefined;
- /** @type {unknown} */
- this._v5 = undefined;
- // expression-valued fields stay plain data properties: a type that
- // references the class inside one of its own accessors makes tsc fork
- // the class into two unrelated identities
- /** @type {BasicEvaluatedExpression[] | undefined} */
- this.quasis = undefined;
- /** @type {BasicEvaluatedExpression[] | undefined} */
- this.parts = 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;
- }
- /**
- * Served lazily from the attached expression when not set explicitly:
- * `evaluateExpression`'s plain results share the expression's range, and
- * most evaluations never read it, so the node's range array is only
- * materialized on demand.
- * @returns {Range | undefined} range of the evaluated expression
- */
- get range() {
- const range = this._range;
- if (range !== undefined) return range;
- const expression = this.expression;
- return expression === undefined
- ? undefined
- : /** @type {Range} */ (
- /** @type {{ range?: Range }} */ (expression).range
- );
- }
- /**
- * @param {Range | undefined} value range of the evaluated expression
- */
- set range(value) {
- this._range = value;
- }
- /**
- * @returns {number} expression type
- */
- get type() {
- return this._flags & FLAG_TYPE_MASK;
- }
- /**
- * @param {number} value expression type
- */
- set type(value) {
- this._flags = (this._flags & ~FLAG_TYPE_MASK) | value;
- }
- /**
- * @returns {boolean} true when the expression is known truthy
- */
- get truthy() {
- return (this._flags & FLAG_TRUTHY) !== 0;
- }
- /**
- * @param {boolean} value known truthy
- */
- set truthy(value) {
- this._flags = value
- ? this._flags | FLAG_TRUTHY
- : this._flags & ~FLAG_TRUTHY;
- }
- /**
- * @returns {boolean} true when the expression is known falsy
- */
- get falsy() {
- return (this._flags & FLAG_FALSY) !== 0;
- }
- /**
- * @param {boolean} value known falsy
- */
- set falsy(value) {
- this._flags = value ? this._flags | FLAG_FALSY : this._flags & ~FLAG_FALSY;
- }
- /**
- * @returns {boolean | undefined} whether the value is nullish, when known
- */
- get nullish() {
- return (this._flags & FLAG_NULLISH_KNOWN) === 0
- ? undefined
- : (this._flags & FLAG_NULLISH) !== 0;
- }
- /**
- * @param {boolean | undefined} value whether the value is nullish
- */
- set nullish(value) {
- this._flags =
- value === undefined
- ? this._flags & ~(FLAG_NULLISH_KNOWN | FLAG_NULLISH)
- : value
- ? this._flags | FLAG_NULLISH_KNOWN | FLAG_NULLISH
- : (this._flags | FLAG_NULLISH_KNOWN) & ~FLAG_NULLISH;
- }
- /**
- * @returns {boolean} true when the expression could have side effects
- */
- get sideEffects() {
- return (this._flags & FLAG_SIDE_EFFECTS) !== 0;
- }
- /**
- * @param {boolean} value could have side effects
- */
- set sideEffects(value) {
- this._flags = value
- ? this._flags | FLAG_SIDE_EFFECTS
- : this._flags & ~FLAG_SIDE_EFFECTS;
- }
- /**
- * @returns {boolean | undefined} boolean value when boolean-typed
- */
- get bool() {
- return this.type === TypeBoolean
- ? /** @type {boolean} */ (this._v1)
- : undefined;
- }
- /**
- * @param {boolean | undefined} value boolean value
- */
- set bool(value) {
- this._v1 = value;
- }
- /**
- * @returns {number | undefined} number value when number-typed
- */
- get number() {
- return this.type === TypeNumber
- ? /** @type {number} */ (this._v1)
- : undefined;
- }
- /**
- * @param {number | undefined} value number value
- */
- set number(value) {
- this._v1 = value;
- }
- /**
- * @returns {bigint | undefined} bigint value when bigint-typed
- */
- get bigint() {
- return this.type === TypeBigInt
- ? /** @type {bigint} */ (this._v1)
- : undefined;
- }
- /**
- * @param {bigint | undefined} value bigint value
- */
- set bigint(value) {
- this._v1 = value;
- }
- /**
- * @returns {RegExp | undefined} regexp value when regexp-typed
- */
- get regExp() {
- return this.type === TypeRegExp
- ? /** @type {RegExp} */ (this._v1)
- : undefined;
- }
- /**
- * @param {RegExp | undefined} value regexp value
- */
- set regExp(value) {
- this._v1 = value;
- }
- /**
- * @returns {string | undefined} string value when string-typed
- */
- get string() {
- return this.type === TypeString
- ? /** @type {string} */ (this._v1)
- : undefined;
- }
- /**
- * @param {string | undefined} value string value
- */
- set string(value) {
- this._v1 = value;
- }
- /**
- * @returns {"cooked" | "raw" | undefined} template string kind
- */
- get templateStringKind() {
- return this.type === TypeTemplateString
- ? /** @type {"cooked" | "raw"} */ (this._v3)
- : undefined;
- }
- /**
- * @param {"cooked" | "raw" | undefined} value template string kind
- */
- set templateStringKind(value) {
- this._v3 = value;
- }
- /**
- * @returns {EXPECTED_ANY[] | undefined} const array values
- */
- get array() {
- return this.type === TypeConstArray
- ? /** @type {unknown[]} */ (this._v1)
- : undefined;
- }
- /**
- * @param {EXPECTED_ANY[] | undefined} value const array values
- */
- set array(value) {
- this._v1 = value;
- }
- /**
- * @returns {string | InstanceType<VariableInfo> | undefined} identifier
- */
- get identifier() {
- return this.type === TypeIdentifier
- ? /** @type {string | InstanceType<VariableInfo>} */ (this._v1)
- : undefined;
- }
- /**
- * @param {string | InstanceType<VariableInfo> | undefined} value identifier
- */
- set identifier(value) {
- this._v1 = value;
- }
- /**
- * @returns {string | InstanceType<VariableInfo> | undefined} root info
- */
- get rootInfo() {
- return this.type === TypeIdentifier
- ? /** @type {string | InstanceType<VariableInfo>} */ (this._v2)
- : undefined;
- }
- /**
- * @param {string | InstanceType<VariableInfo> | undefined} value root info
- */
- set rootInfo(value) {
- this._v2 = value;
- }
- /**
- * @returns {GetMembers | undefined} members getter
- */
- get getMembers() {
- return this.type === TypeIdentifier
- ? /** @type {GetMembers} */ (this._v3)
- : undefined;
- }
- /**
- * @param {GetMembers | undefined} value members getter
- */
- set getMembers(value) {
- this._v3 = value;
- }
- /**
- * @returns {GetMembersOptionals | undefined} members optionals getter
- */
- get getMembersOptionals() {
- return this.type === TypeIdentifier
- ? /** @type {GetMembersOptionals} */ (this._v4)
- : undefined;
- }
- /**
- * @param {GetMembersOptionals | undefined} value members optionals getter
- */
- set getMembersOptionals(value) {
- this._v4 = value;
- }
- /**
- * @returns {GetMemberRanges | undefined} member ranges getter
- */
- get getMemberRanges() {
- return this.type === TypeIdentifier
- ? /** @type {GetMemberRanges} */ (this._v5)
- : undefined;
- }
- /**
- * @param {GetMemberRanges | undefined} value member ranges getter
- */
- set getMemberRanges(value) {
- this._v5 = value;
- }
- 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;
- }
- }
- /**
- * As compile time value.
- * @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()) {
- /** @type {string[]} */
- 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;
- }
- }
- /**
- * Updates string using the provided string.
- * @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 | InstanceType<VariableInfo>} identifier identifier to set
- * @param {string | InstanceType<VariableInfo>} rootInfo root info
- * @param {GetMembers} getMembers members
- * @param {GetMembersOptionals=} getMembersOptionals optional members
- * @param {GetMemberRanges=} 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 the provided basic evaluated expression to the basic evaluated 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;
- }
- }
- /**
- * Returns is valid flags.
- * @param {string} flags regexp flags
- * @returns {boolean} is valid flags
- */
- BasicEvaluatedExpression.isValidRegExpFlags = (flags) => {
- const len = flags.length;
- if (len === 0) return true;
- // 8 standard flags: d g i m s u v y
- if (len > 8) return false;
- const D = 1; // d hasIndices
- const G = 2; // g global
- const I = 4; // i ignoreCase
- const M = 8; // m multiline
- const S = 16; // s dotAll
- const U = 32; // u unicode
- const V = 64; // v unicodeSets
- const Y = 128; // y sticky
- let seen = 0;
- for (let i = 0; i < len; i++) {
- let bit;
- switch (flags.charCodeAt(i)) {
- case 100 /* d */:
- bit = D;
- break;
- case 103 /* g */:
- bit = G;
- break;
- case 105 /* i */:
- bit = I;
- break;
- case 109 /* m */:
- bit = M;
- break;
- case 115 /* s */:
- bit = S;
- break;
- case 117 /* u */:
- bit = U;
- break;
- case 118 /* v */:
- bit = V;
- break;
- case 121 /* y */:
- bit = Y;
- break;
- default:
- return false;
- }
- if (seen & bit) return false; // duplicate flag
- seen |= bit;
- }
- // `u` and `v` are mutually exclusive
- if (seen & U && seen & V) return false;
- return true;
- };
- module.exports = BasicEvaluatedExpression;
|