@angular/compiler
Version:
Angular - the compiler library
1,320 lines • 213 kB
JavaScript
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
//// Types
export var TypeModifier;
(function (TypeModifier) {
TypeModifier[TypeModifier["Const"] = 0] = "Const";
})(TypeModifier || (TypeModifier = {}));
export class Type {
constructor(modifiers = []) {
this.modifiers = modifiers;
}
hasModifier(modifier) {
return this.modifiers.indexOf(modifier) !== -1;
}
}
export var BuiltinTypeName;
(function (BuiltinTypeName) {
BuiltinTypeName[BuiltinTypeName["Dynamic"] = 0] = "Dynamic";
BuiltinTypeName[BuiltinTypeName["Bool"] = 1] = "Bool";
BuiltinTypeName[BuiltinTypeName["String"] = 2] = "String";
BuiltinTypeName[BuiltinTypeName["Int"] = 3] = "Int";
BuiltinTypeName[BuiltinTypeName["Number"] = 4] = "Number";
BuiltinTypeName[BuiltinTypeName["Function"] = 5] = "Function";
BuiltinTypeName[BuiltinTypeName["Inferred"] = 6] = "Inferred";
BuiltinTypeName[BuiltinTypeName["None"] = 7] = "None";
})(BuiltinTypeName || (BuiltinTypeName = {}));
export class BuiltinType extends Type {
constructor(name, modifiers) {
super(modifiers);
this.name = name;
}
visitType(visitor, context) {
return visitor.visitBuiltinType(this, context);
}
}
export class ExpressionType extends Type {
constructor(value, modifiers, typeParams = null) {
super(modifiers);
this.value = value;
this.typeParams = typeParams;
}
visitType(visitor, context) {
return visitor.visitExpressionType(this, context);
}
}
export class ArrayType extends Type {
constructor(of, modifiers) {
super(modifiers);
this.of = of;
}
visitType(visitor, context) {
return visitor.visitArrayType(this, context);
}
}
export class MapType extends Type {
constructor(valueType, modifiers) {
super(modifiers);
this.valueType = valueType || null;
}
visitType(visitor, context) {
return visitor.visitMapType(this, context);
}
}
export const DYNAMIC_TYPE = new BuiltinType(BuiltinTypeName.Dynamic);
export const INFERRED_TYPE = new BuiltinType(BuiltinTypeName.Inferred);
export const BOOL_TYPE = new BuiltinType(BuiltinTypeName.Bool);
export const INT_TYPE = new BuiltinType(BuiltinTypeName.Int);
export const NUMBER_TYPE = new BuiltinType(BuiltinTypeName.Number);
export const STRING_TYPE = new BuiltinType(BuiltinTypeName.String);
export const FUNCTION_TYPE = new BuiltinType(BuiltinTypeName.Function);
export const NONE_TYPE = new BuiltinType(BuiltinTypeName.None);
///// Expressions
export var UnaryOperator;
(function (UnaryOperator) {
UnaryOperator[UnaryOperator["Minus"] = 0] = "Minus";
UnaryOperator[UnaryOperator["Plus"] = 1] = "Plus";
})(UnaryOperator || (UnaryOperator = {}));
export var BinaryOperator;
(function (BinaryOperator) {
BinaryOperator[BinaryOperator["Equals"] = 0] = "Equals";
BinaryOperator[BinaryOperator["NotEquals"] = 1] = "NotEquals";
BinaryOperator[BinaryOperator["Identical"] = 2] = "Identical";
BinaryOperator[BinaryOperator["NotIdentical"] = 3] = "NotIdentical";
BinaryOperator[BinaryOperator["Minus"] = 4] = "Minus";
BinaryOperator[BinaryOperator["Plus"] = 5] = "Plus";
BinaryOperator[BinaryOperator["Divide"] = 6] = "Divide";
BinaryOperator[BinaryOperator["Multiply"] = 7] = "Multiply";
BinaryOperator[BinaryOperator["Modulo"] = 8] = "Modulo";
BinaryOperator[BinaryOperator["And"] = 9] = "And";
BinaryOperator[BinaryOperator["Or"] = 10] = "Or";
BinaryOperator[BinaryOperator["BitwiseAnd"] = 11] = "BitwiseAnd";
BinaryOperator[BinaryOperator["Lower"] = 12] = "Lower";
BinaryOperator[BinaryOperator["LowerEquals"] = 13] = "LowerEquals";
BinaryOperator[BinaryOperator["Bigger"] = 14] = "Bigger";
BinaryOperator[BinaryOperator["BiggerEquals"] = 15] = "BiggerEquals";
BinaryOperator[BinaryOperator["NullishCoalesce"] = 16] = "NullishCoalesce";
})(BinaryOperator || (BinaryOperator = {}));
export function nullSafeIsEquivalent(base, other) {
if (base == null || other == null) {
return base == other;
}
return base.isEquivalent(other);
}
function areAllEquivalentPredicate(base, other, equivalentPredicate) {
const len = base.length;
if (len !== other.length) {
return false;
}
for (let i = 0; i < len; i++) {
if (!equivalentPredicate(base[i], other[i])) {
return false;
}
}
return true;
}
export function areAllEquivalent(base, other) {
return areAllEquivalentPredicate(base, other, (baseElement, otherElement) => baseElement.isEquivalent(otherElement));
}
export class Expression {
constructor(type, sourceSpan) {
this.type = type || null;
this.sourceSpan = sourceSpan || null;
}
prop(name, sourceSpan) {
return new ReadPropExpr(this, name, null, sourceSpan);
}
key(index, type, sourceSpan) {
return new ReadKeyExpr(this, index, type, sourceSpan);
}
callFn(params, sourceSpan, pure) {
return new InvokeFunctionExpr(this, params, null, sourceSpan, pure);
}
instantiate(params, type, sourceSpan) {
return new InstantiateExpr(this, params, type, sourceSpan);
}
conditional(trueCase, falseCase = null, sourceSpan) {
return new ConditionalExpr(this, trueCase, falseCase, null, sourceSpan);
}
equals(rhs, sourceSpan) {
return new BinaryOperatorExpr(BinaryOperator.Equals, this, rhs, null, sourceSpan);
}
notEquals(rhs, sourceSpan) {
return new BinaryOperatorExpr(BinaryOperator.NotEquals, this, rhs, null, sourceSpan);
}
identical(rhs, sourceSpan) {
return new BinaryOperatorExpr(BinaryOperator.Identical, this, rhs, null, sourceSpan);
}
notIdentical(rhs, sourceSpan) {
return new BinaryOperatorExpr(BinaryOperator.NotIdentical, this, rhs, null, sourceSpan);
}
minus(rhs, sourceSpan) {
return new BinaryOperatorExpr(BinaryOperator.Minus, this, rhs, null, sourceSpan);
}
plus(rhs, sourceSpan) {
return new BinaryOperatorExpr(BinaryOperator.Plus, this, rhs, null, sourceSpan);
}
divide(rhs, sourceSpan) {
return new BinaryOperatorExpr(BinaryOperator.Divide, this, rhs, null, sourceSpan);
}
multiply(rhs, sourceSpan) {
return new BinaryOperatorExpr(BinaryOperator.Multiply, this, rhs, null, sourceSpan);
}
modulo(rhs, sourceSpan) {
return new BinaryOperatorExpr(BinaryOperator.Modulo, this, rhs, null, sourceSpan);
}
and(rhs, sourceSpan) {
return new BinaryOperatorExpr(BinaryOperator.And, this, rhs, null, sourceSpan);
}
bitwiseAnd(rhs, sourceSpan, parens = true) {
return new BinaryOperatorExpr(BinaryOperator.BitwiseAnd, this, rhs, null, sourceSpan, parens);
}
or(rhs, sourceSpan) {
return new BinaryOperatorExpr(BinaryOperator.Or, this, rhs, null, sourceSpan);
}
lower(rhs, sourceSpan) {
return new BinaryOperatorExpr(BinaryOperator.Lower, this, rhs, null, sourceSpan);
}
lowerEquals(rhs, sourceSpan) {
return new BinaryOperatorExpr(BinaryOperator.LowerEquals, this, rhs, null, sourceSpan);
}
bigger(rhs, sourceSpan) {
return new BinaryOperatorExpr(BinaryOperator.Bigger, this, rhs, null, sourceSpan);
}
biggerEquals(rhs, sourceSpan) {
return new BinaryOperatorExpr(BinaryOperator.BiggerEquals, this, rhs, null, sourceSpan);
}
isBlank(sourceSpan) {
// Note: We use equals by purpose here to compare to null and undefined in JS.
// We use the typed null to allow strictNullChecks to narrow types.
return this.equals(TYPED_NULL_EXPR, sourceSpan);
}
cast(type, sourceSpan) {
return new CastExpr(this, type, sourceSpan);
}
nullishCoalesce(rhs, sourceSpan) {
return new BinaryOperatorExpr(BinaryOperator.NullishCoalesce, this, rhs, null, sourceSpan);
}
toStmt() {
return new ExpressionStatement(this, null);
}
}
export var BuiltinVar;
(function (BuiltinVar) {
BuiltinVar[BuiltinVar["This"] = 0] = "This";
BuiltinVar[BuiltinVar["Super"] = 1] = "Super";
BuiltinVar[BuiltinVar["CatchError"] = 2] = "CatchError";
BuiltinVar[BuiltinVar["CatchStack"] = 3] = "CatchStack";
})(BuiltinVar || (BuiltinVar = {}));
export class ReadVarExpr extends Expression {
constructor(name, type, sourceSpan) {
super(type, sourceSpan);
if (typeof name === 'string') {
this.name = name;
this.builtin = null;
}
else {
this.name = null;
this.builtin = name;
}
}
isEquivalent(e) {
return e instanceof ReadVarExpr && this.name === e.name && this.builtin === e.builtin;
}
isConstant() {
return false;
}
visitExpression(visitor, context) {
return visitor.visitReadVarExpr(this, context);
}
set(value) {
if (!this.name) {
throw new Error(`Built in variable ${this.builtin} can not be assigned to.`);
}
return new WriteVarExpr(this.name, value, null, this.sourceSpan);
}
}
export class TypeofExpr extends Expression {
constructor(expr, type, sourceSpan) {
super(type, sourceSpan);
this.expr = expr;
}
visitExpression(visitor, context) {
return visitor.visitTypeofExpr(this, context);
}
isEquivalent(e) {
return e instanceof TypeofExpr && e.expr.isEquivalent(this.expr);
}
isConstant() {
return this.expr.isConstant();
}
}
export class WrappedNodeExpr extends Expression {
constructor(node, type, sourceSpan) {
super(type, sourceSpan);
this.node = node;
}
isEquivalent(e) {
return e instanceof WrappedNodeExpr && this.node === e.node;
}
isConstant() {
return false;
}
visitExpression(visitor, context) {
return visitor.visitWrappedNodeExpr(this, context);
}
}
export class WriteVarExpr extends Expression {
constructor(name, value, type, sourceSpan) {
super(type || value.type, sourceSpan);
this.name = name;
this.value = value;
}
isEquivalent(e) {
return e instanceof WriteVarExpr && this.name === e.name && this.value.isEquivalent(e.value);
}
isConstant() {
return false;
}
visitExpression(visitor, context) {
return visitor.visitWriteVarExpr(this, context);
}
toDeclStmt(type, modifiers) {
return new DeclareVarStmt(this.name, this.value, type, modifiers, this.sourceSpan);
}
toConstDecl() {
return this.toDeclStmt(INFERRED_TYPE, [StmtModifier.Final]);
}
}
export class WriteKeyExpr extends Expression {
constructor(receiver, index, value, type, sourceSpan) {
super(type || value.type, sourceSpan);
this.receiver = receiver;
this.index = index;
this.value = value;
}
isEquivalent(e) {
return e instanceof WriteKeyExpr && this.receiver.isEquivalent(e.receiver) &&
this.index.isEquivalent(e.index) && this.value.isEquivalent(e.value);
}
isConstant() {
return false;
}
visitExpression(visitor, context) {
return visitor.visitWriteKeyExpr(this, context);
}
}
export class WritePropExpr extends Expression {
constructor(receiver, name, value, type, sourceSpan) {
super(type || value.type, sourceSpan);
this.receiver = receiver;
this.name = name;
this.value = value;
}
isEquivalent(e) {
return e instanceof WritePropExpr && this.receiver.isEquivalent(e.receiver) &&
this.name === e.name && this.value.isEquivalent(e.value);
}
isConstant() {
return false;
}
visitExpression(visitor, context) {
return visitor.visitWritePropExpr(this, context);
}
}
export var BuiltinMethod;
(function (BuiltinMethod) {
BuiltinMethod[BuiltinMethod["ConcatArray"] = 0] = "ConcatArray";
BuiltinMethod[BuiltinMethod["SubscribeObservable"] = 1] = "SubscribeObservable";
BuiltinMethod[BuiltinMethod["Bind"] = 2] = "Bind";
})(BuiltinMethod || (BuiltinMethod = {}));
export class InvokeFunctionExpr extends Expression {
constructor(fn, args, type, sourceSpan, pure = false) {
super(type, sourceSpan);
this.fn = fn;
this.args = args;
this.pure = pure;
}
isEquivalent(e) {
return e instanceof InvokeFunctionExpr && this.fn.isEquivalent(e.fn) &&
areAllEquivalent(this.args, e.args) && this.pure === e.pure;
}
isConstant() {
return false;
}
visitExpression(visitor, context) {
return visitor.visitInvokeFunctionExpr(this, context);
}
}
export class TaggedTemplateExpr extends Expression {
constructor(tag, template, type, sourceSpan) {
super(type, sourceSpan);
this.tag = tag;
this.template = template;
}
isEquivalent(e) {
return e instanceof TaggedTemplateExpr && this.tag.isEquivalent(e.tag) &&
areAllEquivalentPredicate(this.template.elements, e.template.elements, (a, b) => a.text === b.text) &&
areAllEquivalent(this.template.expressions, e.template.expressions);
}
isConstant() {
return false;
}
visitExpression(visitor, context) {
return visitor.visitTaggedTemplateExpr(this, context);
}
}
export class InstantiateExpr extends Expression {
constructor(classExpr, args, type, sourceSpan) {
super(type, sourceSpan);
this.classExpr = classExpr;
this.args = args;
}
isEquivalent(e) {
return e instanceof InstantiateExpr && this.classExpr.isEquivalent(e.classExpr) &&
areAllEquivalent(this.args, e.args);
}
isConstant() {
return false;
}
visitExpression(visitor, context) {
return visitor.visitInstantiateExpr(this, context);
}
}
export class LiteralExpr extends Expression {
constructor(value, type, sourceSpan) {
super(type, sourceSpan);
this.value = value;
}
isEquivalent(e) {
return e instanceof LiteralExpr && this.value === e.value;
}
isConstant() {
return true;
}
visitExpression(visitor, context) {
return visitor.visitLiteralExpr(this, context);
}
}
export class TemplateLiteral {
constructor(elements, expressions) {
this.elements = elements;
this.expressions = expressions;
}
}
export class TemplateLiteralElement {
constructor(text, sourceSpan, rawText) {
this.text = text;
this.sourceSpan = sourceSpan;
// If `rawText` is not provided, try to extract the raw string from its
// associated `sourceSpan`. If that is also not available, "fake" the raw
// string instead by escaping the following control sequences:
// - "\" would otherwise indicate that the next character is a control character.
// - "`" and "${" are template string control sequences that would otherwise prematurely
// indicate the end of the template literal element.
this.rawText =
rawText ?? sourceSpan?.toString() ?? escapeForTemplateLiteral(escapeSlashes(text));
}
}
export class MessagePiece {
constructor(text, sourceSpan) {
this.text = text;
this.sourceSpan = sourceSpan;
}
}
export class LiteralPiece extends MessagePiece {
}
export class PlaceholderPiece extends MessagePiece {
}
export class LocalizedString extends Expression {
constructor(metaBlock, messageParts, placeHolderNames, expressions, sourceSpan) {
super(STRING_TYPE, sourceSpan);
this.metaBlock = metaBlock;
this.messageParts = messageParts;
this.placeHolderNames = placeHolderNames;
this.expressions = expressions;
}
isEquivalent(e) {
// return e instanceof LocalizedString && this.message === e.message;
return false;
}
isConstant() {
return false;
}
visitExpression(visitor, context) {
return visitor.visitLocalizedString(this, context);
}
/**
* Serialize the given `meta` and `messagePart` into "cooked" and "raw" strings that can be used
* in a `$localize` tagged string. The format of the metadata is the same as that parsed by
* `parseI18nMeta()`.
*
* @param meta The metadata to serialize
* @param messagePart The first part of the tagged string
*/
serializeI18nHead() {
const MEANING_SEPARATOR = '|';
const ID_SEPARATOR = '@@';
const LEGACY_ID_INDICATOR = '␟';
let metaBlock = this.metaBlock.description || '';
if (this.metaBlock.meaning) {
metaBlock = `${this.metaBlock.meaning}${MEANING_SEPARATOR}${metaBlock}`;
}
if (this.metaBlock.customId) {
metaBlock = `${metaBlock}${ID_SEPARATOR}${this.metaBlock.customId}`;
}
if (this.metaBlock.legacyIds) {
this.metaBlock.legacyIds.forEach(legacyId => {
metaBlock = `${metaBlock}${LEGACY_ID_INDICATOR}${legacyId}`;
});
}
return createCookedRawString(metaBlock, this.messageParts[0].text, this.getMessagePartSourceSpan(0));
}
getMessagePartSourceSpan(i) {
return this.messageParts[i]?.sourceSpan ?? this.sourceSpan;
}
getPlaceholderSourceSpan(i) {
return this.placeHolderNames[i]?.sourceSpan ?? this.expressions[i]?.sourceSpan ??
this.sourceSpan;
}
/**
* Serialize the given `placeholderName` and `messagePart` into "cooked" and "raw" strings that
* can be used in a `$localize` tagged string.
*
* @param placeholderName The placeholder name to serialize
* @param messagePart The following message string after this placeholder
*/
serializeI18nTemplatePart(partIndex) {
const placeholderName = this.placeHolderNames[partIndex - 1].text;
const messagePart = this.messageParts[partIndex];
return createCookedRawString(placeholderName, messagePart.text, this.getMessagePartSourceSpan(partIndex));
}
}
const escapeSlashes = (str) => str.replace(/\\/g, '\\\\');
const escapeStartingColon = (str) => str.replace(/^:/, '\\:');
const escapeColons = (str) => str.replace(/:/g, '\\:');
const escapeForTemplateLiteral = (str) => str.replace(/`/g, '\\`').replace(/\${/g, '$\\{');
/**
* Creates a `{cooked, raw}` object from the `metaBlock` and `messagePart`.
*
* The `raw` text must have various character sequences escaped:
* * "\" would otherwise indicate that the next character is a control character.
* * "`" and "${" are template string control sequences that would otherwise prematurely indicate
* the end of a message part.
* * ":" inside a metablock would prematurely indicate the end of the metablock.
* * ":" at the start of a messagePart with no metablock would erroneously indicate the start of a
* metablock.
*
* @param metaBlock Any metadata that should be prepended to the string
* @param messagePart The message part of the string
*/
function createCookedRawString(metaBlock, messagePart, range) {
if (metaBlock === '') {
return {
cooked: messagePart,
raw: escapeForTemplateLiteral(escapeStartingColon(escapeSlashes(messagePart))),
range,
};
}
else {
return {
cooked: `:${metaBlock}:${messagePart}`,
raw: escapeForTemplateLiteral(`:${escapeColons(escapeSlashes(metaBlock))}:${escapeSlashes(messagePart)}`),
range,
};
}
}
export class ExternalExpr extends Expression {
constructor(value, type, typeParams = null, sourceSpan) {
super(type, sourceSpan);
this.value = value;
this.typeParams = typeParams;
}
isEquivalent(e) {
return e instanceof ExternalExpr && this.value.name === e.value.name &&
this.value.moduleName === e.value.moduleName && this.value.runtime === e.value.runtime;
}
isConstant() {
return false;
}
visitExpression(visitor, context) {
return visitor.visitExternalExpr(this, context);
}
}
export class ExternalReference {
constructor(moduleName, name, runtime) {
this.moduleName = moduleName;
this.name = name;
this.runtime = runtime;
}
}
export class ConditionalExpr extends Expression {
constructor(condition, trueCase, falseCase = null, type, sourceSpan) {
super(type || trueCase.type, sourceSpan);
this.condition = condition;
this.falseCase = falseCase;
this.trueCase = trueCase;
}
isEquivalent(e) {
return e instanceof ConditionalExpr && this.condition.isEquivalent(e.condition) &&
this.trueCase.isEquivalent(e.trueCase) && nullSafeIsEquivalent(this.falseCase, e.falseCase);
}
isConstant() {
return false;
}
visitExpression(visitor, context) {
return visitor.visitConditionalExpr(this, context);
}
}
export class NotExpr extends Expression {
constructor(condition, sourceSpan) {
super(BOOL_TYPE, sourceSpan);
this.condition = condition;
}
isEquivalent(e) {
return e instanceof NotExpr && this.condition.isEquivalent(e.condition);
}
isConstant() {
return false;
}
visitExpression(visitor, context) {
return visitor.visitNotExpr(this, context);
}
}
export class AssertNotNull extends Expression {
constructor(condition, sourceSpan) {
super(condition.type, sourceSpan);
this.condition = condition;
}
isEquivalent(e) {
return e instanceof AssertNotNull && this.condition.isEquivalent(e.condition);
}
isConstant() {
return false;
}
visitExpression(visitor, context) {
return visitor.visitAssertNotNullExpr(this, context);
}
}
export class CastExpr extends Expression {
constructor(value, type, sourceSpan) {
super(type, sourceSpan);
this.value = value;
}
isEquivalent(e) {
return e instanceof CastExpr && this.value.isEquivalent(e.value);
}
isConstant() {
return false;
}
visitExpression(visitor, context) {
return visitor.visitCastExpr(this, context);
}
}
export class FnParam {
constructor(name, type = null) {
this.name = name;
this.type = type;
}
isEquivalent(param) {
return this.name === param.name;
}
}
export class FunctionExpr extends Expression {
constructor(params, statements, type, sourceSpan, name) {
super(type, sourceSpan);
this.params = params;
this.statements = statements;
this.name = name;
}
isEquivalent(e) {
return e instanceof FunctionExpr && areAllEquivalent(this.params, e.params) &&
areAllEquivalent(this.statements, e.statements);
}
isConstant() {
return false;
}
visitExpression(visitor, context) {
return visitor.visitFunctionExpr(this, context);
}
toDeclStmt(name, modifiers) {
return new DeclareFunctionStmt(name, this.params, this.statements, this.type, modifiers, this.sourceSpan);
}
}
export class UnaryOperatorExpr extends Expression {
constructor(operator, expr, type, sourceSpan, parens = true) {
super(type || NUMBER_TYPE, sourceSpan);
this.operator = operator;
this.expr = expr;
this.parens = parens;
}
isEquivalent(e) {
return e instanceof UnaryOperatorExpr && this.operator === e.operator &&
this.expr.isEquivalent(e.expr);
}
isConstant() {
return false;
}
visitExpression(visitor, context) {
return visitor.visitUnaryOperatorExpr(this, context);
}
}
export class BinaryOperatorExpr extends Expression {
constructor(operator, lhs, rhs, type, sourceSpan, parens = true) {
super(type || lhs.type, sourceSpan);
this.operator = operator;
this.rhs = rhs;
this.parens = parens;
this.lhs = lhs;
}
isEquivalent(e) {
return e instanceof BinaryOperatorExpr && this.operator === e.operator &&
this.lhs.isEquivalent(e.lhs) && this.rhs.isEquivalent(e.rhs);
}
isConstant() {
return false;
}
visitExpression(visitor, context) {
return visitor.visitBinaryOperatorExpr(this, context);
}
}
export class ReadPropExpr extends Expression {
constructor(receiver, name, type, sourceSpan) {
super(type, sourceSpan);
this.receiver = receiver;
this.name = name;
}
isEquivalent(e) {
return e instanceof ReadPropExpr && this.receiver.isEquivalent(e.receiver) &&
this.name === e.name;
}
isConstant() {
return false;
}
visitExpression(visitor, context) {
return visitor.visitReadPropExpr(this, context);
}
set(value) {
return new WritePropExpr(this.receiver, this.name, value, null, this.sourceSpan);
}
}
export class ReadKeyExpr extends Expression {
constructor(receiver, index, type, sourceSpan) {
super(type, sourceSpan);
this.receiver = receiver;
this.index = index;
}
isEquivalent(e) {
return e instanceof ReadKeyExpr && this.receiver.isEquivalent(e.receiver) &&
this.index.isEquivalent(e.index);
}
isConstant() {
return false;
}
visitExpression(visitor, context) {
return visitor.visitReadKeyExpr(this, context);
}
set(value) {
return new WriteKeyExpr(this.receiver, this.index, value, null, this.sourceSpan);
}
}
export class LiteralArrayExpr extends Expression {
constructor(entries, type, sourceSpan) {
super(type, sourceSpan);
this.entries = entries;
}
isConstant() {
return this.entries.every(e => e.isConstant());
}
isEquivalent(e) {
return e instanceof LiteralArrayExpr && areAllEquivalent(this.entries, e.entries);
}
visitExpression(visitor, context) {
return visitor.visitLiteralArrayExpr(this, context);
}
}
export class LiteralMapEntry {
constructor(key, value, quoted) {
this.key = key;
this.value = value;
this.quoted = quoted;
}
isEquivalent(e) {
return this.key === e.key && this.value.isEquivalent(e.value);
}
}
export class LiteralMapExpr extends Expression {
constructor(entries, type, sourceSpan) {
super(type, sourceSpan);
this.entries = entries;
this.valueType = null;
if (type) {
this.valueType = type.valueType;
}
}
isEquivalent(e) {
return e instanceof LiteralMapExpr && areAllEquivalent(this.entries, e.entries);
}
isConstant() {
return this.entries.every(e => e.value.isConstant());
}
visitExpression(visitor, context) {
return visitor.visitLiteralMapExpr(this, context);
}
}
export class CommaExpr extends Expression {
constructor(parts, sourceSpan) {
super(parts[parts.length - 1].type, sourceSpan);
this.parts = parts;
}
isEquivalent(e) {
return e instanceof CommaExpr && areAllEquivalent(this.parts, e.parts);
}
isConstant() {
return false;
}
visitExpression(visitor, context) {
return visitor.visitCommaExpr(this, context);
}
}
export const THIS_EXPR = new ReadVarExpr(BuiltinVar.This, null, null);
export const SUPER_EXPR = new ReadVarExpr(BuiltinVar.Super, null, null);
export const CATCH_ERROR_VAR = new ReadVarExpr(BuiltinVar.CatchError, null, null);
export const CATCH_STACK_VAR = new ReadVarExpr(BuiltinVar.CatchStack, null, null);
export const NULL_EXPR = new LiteralExpr(null, null, null);
export const TYPED_NULL_EXPR = new LiteralExpr(null, INFERRED_TYPE, null);
//// Statements
export var StmtModifier;
(function (StmtModifier) {
StmtModifier[StmtModifier["Final"] = 0] = "Final";
StmtModifier[StmtModifier["Private"] = 1] = "Private";
StmtModifier[StmtModifier["Exported"] = 2] = "Exported";
StmtModifier[StmtModifier["Static"] = 3] = "Static";
})(StmtModifier || (StmtModifier = {}));
export class LeadingComment {
constructor(text, multiline, trailingNewline) {
this.text = text;
this.multiline = multiline;
this.trailingNewline = trailingNewline;
}
toString() {
return this.multiline ? ` ${this.text} ` : this.text;
}
}
export class JSDocComment extends LeadingComment {
constructor(tags) {
super('', /* multiline */ true, /* trailingNewline */ true);
this.tags = tags;
}
toString() {
return serializeTags(this.tags);
}
}
export class Statement {
constructor(modifiers = [], sourceSpan = null, leadingComments) {
this.modifiers = modifiers;
this.sourceSpan = sourceSpan;
this.leadingComments = leadingComments;
}
hasModifier(modifier) {
return this.modifiers.indexOf(modifier) !== -1;
}
addLeadingComment(leadingComment) {
this.leadingComments = this.leadingComments ?? [];
this.leadingComments.push(leadingComment);
}
}
export class DeclareVarStmt extends Statement {
constructor(name, value, type, modifiers, sourceSpan, leadingComments) {
super(modifiers, sourceSpan, leadingComments);
this.name = name;
this.value = value;
this.type = type || (value && value.type) || null;
}
isEquivalent(stmt) {
return stmt instanceof DeclareVarStmt && this.name === stmt.name &&
(this.value ? !!stmt.value && this.value.isEquivalent(stmt.value) : !stmt.value);
}
visitStatement(visitor, context) {
return visitor.visitDeclareVarStmt(this, context);
}
}
export class DeclareFunctionStmt extends Statement {
constructor(name, params, statements, type, modifiers, sourceSpan, leadingComments) {
super(modifiers, sourceSpan, leadingComments);
this.name = name;
this.params = params;
this.statements = statements;
this.type = type || null;
}
isEquivalent(stmt) {
return stmt instanceof DeclareFunctionStmt && areAllEquivalent(this.params, stmt.params) &&
areAllEquivalent(this.statements, stmt.statements);
}
visitStatement(visitor, context) {
return visitor.visitDeclareFunctionStmt(this, context);
}
}
export class ExpressionStatement extends Statement {
constructor(expr, sourceSpan, leadingComments) {
super([], sourceSpan, leadingComments);
this.expr = expr;
}
isEquivalent(stmt) {
return stmt instanceof ExpressionStatement && this.expr.isEquivalent(stmt.expr);
}
visitStatement(visitor, context) {
return visitor.visitExpressionStmt(this, context);
}
}
export class ReturnStatement extends Statement {
constructor(value, sourceSpan = null, leadingComments) {
super([], sourceSpan, leadingComments);
this.value = value;
}
isEquivalent(stmt) {
return stmt instanceof ReturnStatement && this.value.isEquivalent(stmt.value);
}
visitStatement(visitor, context) {
return visitor.visitReturnStmt(this, context);
}
}
export class AbstractClassPart {
constructor(type = null, modifiers = []) {
this.type = type;
this.modifiers = modifiers;
}
hasModifier(modifier) {
return this.modifiers.indexOf(modifier) !== -1;
}
}
export class ClassField extends AbstractClassPart {
constructor(name, type, modifiers, initializer) {
super(type, modifiers);
this.name = name;
this.initializer = initializer;
}
isEquivalent(f) {
return this.name === f.name;
}
}
export class ClassMethod extends AbstractClassPart {
constructor(name, params, body, type, modifiers) {
super(type, modifiers);
this.name = name;
this.params = params;
this.body = body;
}
isEquivalent(m) {
return this.name === m.name && areAllEquivalent(this.body, m.body);
}
}
export class ClassGetter extends AbstractClassPart {
constructor(name, body, type, modifiers) {
super(type, modifiers);
this.name = name;
this.body = body;
}
isEquivalent(m) {
return this.name === m.name && areAllEquivalent(this.body, m.body);
}
}
export class ClassStmt extends Statement {
constructor(name, parent, fields, getters, constructorMethod, methods, modifiers, sourceSpan, leadingComments) {
super(modifiers, sourceSpan, leadingComments);
this.name = name;
this.parent = parent;
this.fields = fields;
this.getters = getters;
this.constructorMethod = constructorMethod;
this.methods = methods;
}
isEquivalent(stmt) {
return stmt instanceof ClassStmt && this.name === stmt.name &&
nullSafeIsEquivalent(this.parent, stmt.parent) &&
areAllEquivalent(this.fields, stmt.fields) &&
areAllEquivalent(this.getters, stmt.getters) &&
this.constructorMethod.isEquivalent(stmt.constructorMethod) &&
areAllEquivalent(this.methods, stmt.methods);
}
visitStatement(visitor, context) {
return visitor.visitDeclareClassStmt(this, context);
}
}
export class IfStmt extends Statement {
constructor(condition, trueCase, falseCase = [], sourceSpan, leadingComments) {
super([], sourceSpan, leadingComments);
this.condition = condition;
this.trueCase = trueCase;
this.falseCase = falseCase;
}
isEquivalent(stmt) {
return stmt instanceof IfStmt && this.condition.isEquivalent(stmt.condition) &&
areAllEquivalent(this.trueCase, stmt.trueCase) &&
areAllEquivalent(this.falseCase, stmt.falseCase);
}
visitStatement(visitor, context) {
return visitor.visitIfStmt(this, context);
}
}
export class TryCatchStmt extends Statement {
constructor(bodyStmts, catchStmts, sourceSpan = null, leadingComments) {
super([], sourceSpan, leadingComments);
this.bodyStmts = bodyStmts;
this.catchStmts = catchStmts;
}
isEquivalent(stmt) {
return stmt instanceof TryCatchStmt && areAllEquivalent(this.bodyStmts, stmt.bodyStmts) &&
areAllEquivalent(this.catchStmts, stmt.catchStmts);
}
visitStatement(visitor, context) {
return visitor.visitTryCatchStmt(this, context);
}
}
export class ThrowStmt extends Statement {
constructor(error, sourceSpan = null, leadingComments) {
super([], sourceSpan, leadingComments);
this.error = error;
}
isEquivalent(stmt) {
return stmt instanceof TryCatchStmt && this.error.isEquivalent(stmt.error);
}
visitStatement(visitor, context) {
return visitor.visitThrowStmt(this, context);
}
}
export class AstTransformer {
transformExpr(expr, context) {
return expr;
}
transformStmt(stmt, context) {
return stmt;
}
visitReadVarExpr(ast, context) {
return this.transformExpr(ast, context);
}
visitWrappedNodeExpr(ast, context) {
return this.transformExpr(ast, context);
}
visitTypeofExpr(expr, context) {
return this.transformExpr(new TypeofExpr(expr.expr.visitExpression(this, context), expr.type, expr.sourceSpan), context);
}
visitWriteVarExpr(expr, context) {
return this.transformExpr(new WriteVarExpr(expr.name, expr.value.visitExpression(this, context), expr.type, expr.sourceSpan), context);
}
visitWriteKeyExpr(expr, context) {
return this.transformExpr(new WriteKeyExpr(expr.receiver.visitExpression(this, context), expr.index.visitExpression(this, context), expr.value.visitExpression(this, context), expr.type, expr.sourceSpan), context);
}
visitWritePropExpr(expr, context) {
return this.transformExpr(new WritePropExpr(expr.receiver.visitExpression(this, context), expr.name, expr.value.visitExpression(this, context), expr.type, expr.sourceSpan), context);
}
visitInvokeFunctionExpr(ast, context) {
return this.transformExpr(new InvokeFunctionExpr(ast.fn.visitExpression(this, context), this.visitAllExpressions(ast.args, context), ast.type, ast.sourceSpan), context);
}
visitTaggedTemplateExpr(ast, context) {
return this.transformExpr(new TaggedTemplateExpr(ast.tag.visitExpression(this, context), new TemplateLiteral(ast.template.elements, ast.template.expressions.map((e) => e.visitExpression(this, context))), ast.type, ast.sourceSpan), context);
}
visitInstantiateExpr(ast, context) {
return this.transformExpr(new InstantiateExpr(ast.classExpr.visitExpression(this, context), this.visitAllExpressions(ast.args, context), ast.type, ast.sourceSpan), context);
}
visitLiteralExpr(ast, context) {
return this.transformExpr(ast, context);
}
visitLocalizedString(ast, context) {
return this.transformExpr(new LocalizedString(ast.metaBlock, ast.messageParts, ast.placeHolderNames, this.visitAllExpressions(ast.expressions, context), ast.sourceSpan), context);
}
visitExternalExpr(ast, context) {
return this.transformExpr(ast, context);
}
visitConditionalExpr(ast, context) {
return this.transformExpr(new ConditionalExpr(ast.condition.visitExpression(this, context), ast.trueCase.visitExpression(this, context), ast.falseCase.visitExpression(this, context), ast.type, ast.sourceSpan), context);
}
visitNotExpr(ast, context) {
return this.transformExpr(new NotExpr(ast.condition.visitExpression(this, context), ast.sourceSpan), context);
}
visitAssertNotNullExpr(ast, context) {
return this.transformExpr(new AssertNotNull(ast.condition.visitExpression(this, context), ast.sourceSpan), context);
}
visitCastExpr(ast, context) {
return this.transformExpr(new CastExpr(ast.value.visitExpression(this, context), ast.type, ast.sourceSpan), context);
}
visitFunctionExpr(ast, context) {
return this.transformExpr(new FunctionExpr(ast.params, this.visitAllStatements(ast.statements, context), ast.type, ast.sourceSpan), context);
}
visitUnaryOperatorExpr(ast, context) {
return this.transformExpr(new UnaryOperatorExpr(ast.operator, ast.expr.visitExpression(this, context), ast.type, ast.sourceSpan), context);
}
visitBinaryOperatorExpr(ast, context) {
return this.transformExpr(new BinaryOperatorExpr(ast.operator, ast.lhs.visitExpression(this, context), ast.rhs.visitExpression(this, context), ast.type, ast.sourceSpan), context);
}
visitReadPropExpr(ast, context) {
return this.transformExpr(new ReadPropExpr(ast.receiver.visitExpression(this, context), ast.name, ast.type, ast.sourceSpan), context);
}
visitReadKeyExpr(ast, context) {
return this.transformExpr(new ReadKeyExpr(ast.receiver.visitExpression(this, context), ast.index.visitExpression(this, context), ast.type, ast.sourceSpan), context);
}
visitLiteralArrayExpr(ast, context) {
return this.transformExpr(new LiteralArrayExpr(this.visitAllExpressions(ast.entries, context), ast.type, ast.sourceSpan), context);
}
visitLiteralMapExpr(ast, context) {
const entries = ast.entries.map((entry) => new LiteralMapEntry(entry.key, entry.value.visitExpression(this, context), entry.quoted));
const mapType = new MapType(ast.valueType);
return this.transformExpr(new LiteralMapExpr(entries, mapType, ast.sourceSpan), context);
}
visitCommaExpr(ast, context) {
return this.transformExpr(new CommaExpr(this.visitAllExpressions(ast.parts, context), ast.sourceSpan), context);
}
visitAllExpressions(exprs, context) {
return exprs.map(expr => expr.visitExpression(this, context));
}
visitDeclareVarStmt(stmt, context) {
const value = stmt.value && stmt.value.visitExpression(this, context);
return this.transformStmt(new DeclareVarStmt(stmt.name, value, stmt.type, stmt.modifiers, stmt.sourceSpan, stmt.leadingComments), context);
}
visitDeclareFunctionStmt(stmt, context) {
return this.transformStmt(new DeclareFunctionStmt(stmt.name, stmt.params, this.visitAllStatements(stmt.statements, context), stmt.type, stmt.modifiers, stmt.sourceSpan, stmt.leadingComments), context);
}
visitExpressionStmt(stmt, context) {
return this.transformStmt(new ExpressionStatement(stmt.expr.visitExpression(this, context), stmt.sourceSpan, stmt.leadingComments), context);
}
visitReturnStmt(stmt, context) {
return this.transformStmt(new ReturnStatement(stmt.value.visitExpression(this, context), stmt.sourceSpan, stmt.leadingComments), context);
}
visitDeclareClassStmt(stmt, context) {
const parent = stmt.parent.visitExpression(this, context);
const getters = stmt.getters.map(getter => new ClassGetter(getter.name, this.visitAllStatements(getter.body, context), getter.type, getter.modifiers));
const ctorMethod = stmt.constructorMethod &&
new ClassMethod(stmt.constructorMethod.name, stmt.constructorMethod.params, this.visitAllStatements(stmt.constructorMethod.body, context), stmt.constructorMethod.type, stmt.constructorMethod.modifiers);
const methods = stmt.methods.map(method => new ClassMethod(method.name, method.params, this.visitAllStatements(method.body, context), method.type, method.modifiers));
return this.transformStmt(new ClassStmt(stmt.name, parent, stmt.fields, getters, ctorMethod, methods, stmt.modifiers, stmt.sourceSpan), context);
}
visitIfStmt(stmt, context) {
return this.transformStmt(new IfStmt(stmt.condition.visitExpression(this, context), this.visitAllStatements(stmt.trueCase, context), this.visitAllStatements(stmt.falseCase, context), stmt.sourceSpan, stmt.leadingComments), context);
}
visitTryCatchStmt(stmt, context) {
return this.transformStmt(new TryCatchStmt(this.visitAllStatements(stmt.bodyStmts, context), this.visitAllStatements(stmt.catchStmts, context), stmt.sourceSpan, stmt.leadingComments), context);
}
visitThrowStmt(stmt, context) {
return this.transformStmt(new ThrowStmt(stmt.error.visitExpression(this, context), stmt.sourceSpan, stmt.leadingComments), context);
}
visitAllStatements(stmts, context) {
return stmts.map(stmt => stmt.visitStatement(this, context));
}
}
export class RecursiveAstVisitor {
visitType(ast, context) {
return ast;
}
visitExpression(ast, context) {
if (ast.type) {
ast.type.visitType(this, context);
}
return ast;
}
visitBuiltinType(type, context) {
return this.visitType(type, context);
}
visitExpressionType(type, context) {
type.value.visitExpression(this, context);
if (type.typeParams !== null) {
type.typeParams.forEach(param => this.visitType(param, context));
}
return this.visitType(type, context);
}
visitArrayType(type, context) {
return this.visitType(type, context);
}
visitMapType(type, context) {
return this.visitType(type, context);
}
visitWrappedNodeExpr(ast, context) {
return ast;
}
visitTypeofExpr(ast, context) {
return this.visitExpression(ast, context);
}
visitReadVarExpr(ast, context) {
return this.visitExpression(ast, context);
}
visitWriteVarExpr(ast, context) {
ast.value.visitExpression(this, context);
return this.visitExpression(ast, context);
}
visitWriteKeyExpr(ast, context) {
ast.receiver.visitExpression(this, context);
ast.index.visitExpression(this, context);
ast.value.visitExpression(this, context);
return this.visitExpression(ast, context);
}
visitWritePropExpr(ast, context) {
ast.receiver.visitExpression(this, context);
ast.value.visitExpression(this, context);
return this.visitExpression(ast, context);
}
visitInvokeFunctionExpr(ast, context) {
ast.fn.visitExpression(this, context);
this.visitAllExpressions(ast.args, context);
return this.visitExpression(ast, context);
}
visitTaggedTemplateExpr(ast, context) {
ast.tag.visitExpression(this, context);
this.visitAllExpressions(ast.template.expressions, context);
return this.visitExpression(ast, context);
}
visitInstantiateExpr(ast, context) {
ast.classExpr.visitExpression(this, context);
this.visitAllExpressions(ast.args, context);
return this.visitExpression(ast, context);
}
visitLiteralExpr(ast, context) {
return this.visitExpression(ast, context);
}
visitLocalizedString(ast, context) {
return this.visitExpression(ast, context);
}
visitExternalExpr(ast, context) {
if (ast.typeParams) {
ast.typeParams.forEach(type => type.visitType(this, context));
}
return this.visitExpression(ast, context);
}
visitConditionalExpr(ast, context) {
ast.condition.visitExpression(this, context);
ast.trueCase.visitExpression(this, context);
ast.falseCase.visitExpression(this, context);
return this.visitExpression(ast, context);
}
visitNotExpr(ast, context) {
ast.condition.visitExpression(this, context);
return this.visitExpression(ast, context);
}
visitAssertNotNullExpr(ast, context) {
ast.condition.visitExpression(this, context);
return this.visitExpression(ast, context);
}
visitCastExpr(ast, context) {
ast.value.visitExpression(this, context);
return this.visitExpression(ast, context);
}
visitFunctionExpr(ast, context) {
this.visitAllStatements(ast.statements, context);
return this.visitExpression(ast, context);
}
visitUnaryOperatorExpr(ast, context) {
ast.expr.visitExpression(this, context);
return this.visitExpression(ast, context);
}
visitBinaryOperatorExpr(ast, context) {
ast.lhs.visitExpression(this, context);
ast.rhs.visitExpression(this, context);
return this.visitExpression(ast, context);
}
visitReadPropExpr(ast, context) {
ast.receiver.visitExpression(this, context);
return this.visitExpression(ast, context);
}
visitReadKeyExpr(ast, context) {
ast.receiver.visitExpression(this, context);
ast.index.visitExpression(this, context);
return this.visitExpression(ast, context);
}
visitLiteralArrayExpr(ast, context) {
this.visitAllExpressions(ast.entries, context);
return this.visitExpression(ast, context);
}
visitLiteralMapExpr(ast, context) {
ast.entries.forEach((entry) => entry.value.visitExpression(this, context));
return this.visitExpression(ast, context);
}
visitCommaExpr(ast, context) {
this.visitAllExpressions(ast.parts, context);
return this.visitExpression(ast, context);
}
visitAllExpressions(exprs, context) {
exprs.forEach(expr => expr.visitExpression(this, context));
}
visitDeclareVarStmt(stmt, context) {
if (stmt.value) {
stmt.value.visitExpression(this, context);
}
if (stmt.type) {
stmt.type.visitType(this, context);
}
return stmt;
}
visitDeclareFunctionStmt(stmt, context) {
this.visitAllStatements(stmt.statements, context);
if (stmt.type) {
stmt.type.visitType(this, context);
}
return stmt;
}
visitExpressionStmt(stmt, context) {
stmt.expr.visitExpression(this, context);
return stmt;
}
visitReturnStmt(stmt, context) {
stmt.value.visitExpression(this, context);
return stmt;
}
visitDeclareClassStmt(stmt, context) {
stmt.parent.visitExpression(this, context);
stmt.getters.forEach(getter => this.visitAllStatements(getter.body, context));
if (stmt.constructorMethod) {
this.visitAllStatements(stmt.constructorMethod.body, context);
}
stmt.methods.forEach(method => this.visitAllStatements(method.body, context));
return stmt;
}
visitIfStmt(stmt, context) {
stmt.condition.visitExpression(this, context);
this.visitAllStatements(stmt.trueCase, context);
this.visitAllStatements(stmt.falseCase, context);
return stmt;
}
visitTryCatchStmt(stmt, context) {
this.visitAllStatements(stmt.bodyStmts, context);
this.visitAllStatements(stmt.catchStmts, context);
return stmt;
}
visitThrowStmt(stmt, context) {
stmt.error.visitExpression(this, context);
return stmt;
}
visitAllStatements(stmts, context) {
stmts.forEach(stmt => stmt.visitStatement(this, context));
}
}
export function findReadVarNames(stmts) {
const visitor = new _ReadVarVisitor();
visitor.visitAllStatements(stmts, null);
return visitor.varNames;
}
class _ReadVarVisitor extends RecursiveAstVisitor {
constructor() {
super(...arguments);
this.varNames = new Set();
}
visitDeclareFunctionStmt(stmt, context) {
// Don't descend into nested functions
return stmt;
}
visitDeclareClassStmt(stmt, context) {
// Don't descend into nested classes
return stmt;
}
visitReadVarExpr(ast, context) {
if (ast.name) {
this.varNames.add(ast.name);
}
return null;
}
}
export function collectExternalReferences(stmts) {
const visitor = new _FindExternalReferencesVisitor();
visit