@angular/compiler
Version:
Angular - the compiler library
886 lines • 128 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
*/
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define("@angular/compiler/src/compiler_util/expression_converter", ["require", "exports", "tslib", "@angular/compiler/src/expression_parser/ast", "@angular/compiler/src/identifiers", "@angular/compiler/src/output/output_ast", "@angular/compiler/src/parse_util"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BuiltinFunctionCall = exports.temporaryDeclaration = exports.convertUpdateArguments = exports.convertPropertyBinding = exports.BindingForm = exports.ConvertPropertyBindingResult = exports.convertPropertyBindingBuiltins = exports.convertActionBinding = exports.ConvertActionBindingResult = exports.EventHandlerVars = void 0;
var tslib_1 = require("tslib");
var cdAst = require("@angular/compiler/src/expression_parser/ast");
var identifiers_1 = require("@angular/compiler/src/identifiers");
var o = require("@angular/compiler/src/output/output_ast");
var parse_util_1 = require("@angular/compiler/src/parse_util");
var EventHandlerVars = /** @class */ (function () {
function EventHandlerVars() {
}
EventHandlerVars.event = o.variable('$event');
return EventHandlerVars;
}());
exports.EventHandlerVars = EventHandlerVars;
var ConvertActionBindingResult = /** @class */ (function () {
function ConvertActionBindingResult(
/**
* Render2 compatible statements,
*/
stmts,
/**
* Variable name used with render2 compatible statements.
*/
allowDefault) {
this.stmts = stmts;
this.allowDefault = allowDefault;
/**
* This is bit of a hack. It converts statements which render2 expects to statements which are
* expected by render3.
*
* Example: `<div click="doSomething($event)">` will generate:
*
* Render3:
* ```
* const pd_b:any = ((<any>ctx.doSomething($event)) !== false);
* return pd_b;
* ```
*
* but render2 expects:
* ```
* return ctx.doSomething($event);
* ```
*/
// TODO(misko): remove this hack once we no longer support ViewEngine.
this.render3Stmts = stmts.map(function (statement) {
if (statement instanceof o.DeclareVarStmt && statement.name == allowDefault.name &&
statement.value instanceof o.BinaryOperatorExpr) {
var lhs = statement.value.lhs;
return new o.ReturnStatement(lhs.value);
}
return statement;
});
}
return ConvertActionBindingResult;
}());
exports.ConvertActionBindingResult = ConvertActionBindingResult;
/**
* Converts the given expression AST into an executable output AST, assuming the expression is
* used in an action binding (e.g. an event handler).
*/
function convertActionBinding(localResolver, implicitReceiver, action, bindingId, interpolationFunction, baseSourceSpan, implicitReceiverAccesses, globals) {
if (!localResolver) {
localResolver = new DefaultLocalResolver(globals);
}
var actionWithoutBuiltins = convertPropertyBindingBuiltins({
createLiteralArrayConverter: function (argCount) {
// Note: no caching for literal arrays in actions.
return function (args) { return o.literalArr(args); };
},
createLiteralMapConverter: function (keys) {
// Note: no caching for literal maps in actions.
return function (values) {
var entries = keys.map(function (k, i) { return ({
key: k.key,
value: values[i],
quoted: k.quoted,
}); });
return o.literalMap(entries);
};
},
createPipeConverter: function (name) {
throw new Error("Illegal State: Actions are not allowed to contain pipes. Pipe: " + name);
}
}, action);
var visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, interpolationFunction, baseSourceSpan, implicitReceiverAccesses);
var actionStmts = [];
flattenStatements(actionWithoutBuiltins.visit(visitor, _Mode.Statement), actionStmts);
prependTemporaryDecls(visitor.temporaryCount, bindingId, actionStmts);
if (visitor.usesImplicitReceiver) {
localResolver.notifyImplicitReceiverUse();
}
var lastIndex = actionStmts.length - 1;
var preventDefaultVar = null;
if (lastIndex >= 0) {
var lastStatement = actionStmts[lastIndex];
var returnExpr = convertStmtIntoExpression(lastStatement);
if (returnExpr) {
// Note: We need to cast the result of the method call to dynamic,
// as it might be a void method!
preventDefaultVar = createPreventDefaultVar(bindingId);
actionStmts[lastIndex] =
preventDefaultVar.set(returnExpr.cast(o.DYNAMIC_TYPE).notIdentical(o.literal(false)))
.toDeclStmt(null, [o.StmtModifier.Final]);
}
}
return new ConvertActionBindingResult(actionStmts, preventDefaultVar);
}
exports.convertActionBinding = convertActionBinding;
function convertPropertyBindingBuiltins(converterFactory, ast) {
return convertBuiltins(converterFactory, ast);
}
exports.convertPropertyBindingBuiltins = convertPropertyBindingBuiltins;
var ConvertPropertyBindingResult = /** @class */ (function () {
function ConvertPropertyBindingResult(stmts, currValExpr) {
this.stmts = stmts;
this.currValExpr = currValExpr;
}
return ConvertPropertyBindingResult;
}());
exports.ConvertPropertyBindingResult = ConvertPropertyBindingResult;
var BindingForm;
(function (BindingForm) {
// The general form of binding expression, supports all expressions.
BindingForm[BindingForm["General"] = 0] = "General";
// Try to generate a simple binding (no temporaries or statements)
// otherwise generate a general binding
BindingForm[BindingForm["TrySimple"] = 1] = "TrySimple";
// Inlines assignment of temporaries into the generated expression. The result may still
// have statements attached for declarations of temporary variables.
// This is the only relevant form for Ivy, the other forms are only used in ViewEngine.
BindingForm[BindingForm["Expression"] = 2] = "Expression";
})(BindingForm = exports.BindingForm || (exports.BindingForm = {}));
/**
* Converts the given expression AST into an executable output AST, assuming the expression
* is used in property binding. The expression has to be preprocessed via
* `convertPropertyBindingBuiltins`.
*/
function convertPropertyBinding(localResolver, implicitReceiver, expressionWithoutBuiltins, bindingId, form, interpolationFunction) {
if (!localResolver) {
localResolver = new DefaultLocalResolver();
}
var visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, interpolationFunction);
var outputExpr = expressionWithoutBuiltins.visit(visitor, _Mode.Expression);
var stmts = getStatementsFromVisitor(visitor, bindingId);
if (visitor.usesImplicitReceiver) {
localResolver.notifyImplicitReceiverUse();
}
if (visitor.temporaryCount === 0 && form == BindingForm.TrySimple) {
return new ConvertPropertyBindingResult([], outputExpr);
}
else if (form === BindingForm.Expression) {
return new ConvertPropertyBindingResult(stmts, outputExpr);
}
var currValExpr = createCurrValueExpr(bindingId);
stmts.push(currValExpr.set(outputExpr).toDeclStmt(o.DYNAMIC_TYPE, [o.StmtModifier.Final]));
return new ConvertPropertyBindingResult(stmts, currValExpr);
}
exports.convertPropertyBinding = convertPropertyBinding;
/**
* Given some expression, such as a binding or interpolation expression, and a context expression to
* look values up on, visit each facet of the given expression resolving values from the context
* expression such that a list of arguments can be derived from the found values that can be used as
* arguments to an external update instruction.
*
* @param localResolver The resolver to use to look up expressions by name appropriately
* @param contextVariableExpression The expression representing the context variable used to create
* the final argument expressions
* @param expressionWithArgumentsToExtract The expression to visit to figure out what values need to
* be resolved and what arguments list to build.
* @param bindingId A name prefix used to create temporary variable names if they're needed for the
* arguments generated
* @returns An array of expressions that can be passed as arguments to instruction expressions like
* `o.importExpr(R3.propertyInterpolate).callFn(result)`
*/
function convertUpdateArguments(localResolver, contextVariableExpression, expressionWithArgumentsToExtract, bindingId) {
var visitor = new _AstToIrVisitor(localResolver, contextVariableExpression, bindingId, undefined);
var outputExpr = expressionWithArgumentsToExtract.visit(visitor, _Mode.Expression);
if (visitor.usesImplicitReceiver) {
localResolver.notifyImplicitReceiverUse();
}
var stmts = getStatementsFromVisitor(visitor, bindingId);
// Removing the first argument, because it was a length for ViewEngine, not Ivy.
var args = outputExpr.args.slice(1);
if (expressionWithArgumentsToExtract instanceof cdAst.Interpolation) {
// If we're dealing with an interpolation of 1 value with an empty prefix and suffix, reduce the
// args returned to just the value, because we're going to pass it to a special instruction.
var strings = expressionWithArgumentsToExtract.strings;
if (args.length === 3 && strings[0] === '' && strings[1] === '') {
// Single argument interpolate instructions.
args = [args[1]];
}
else if (args.length >= 19) {
// 19 or more arguments must be passed to the `interpolateV`-style instructions, which accept
// an array of arguments
args = [o.literalArr(args)];
}
}
return { stmts: stmts, args: args };
}
exports.convertUpdateArguments = convertUpdateArguments;
function getStatementsFromVisitor(visitor, bindingId) {
var stmts = [];
for (var i = 0; i < visitor.temporaryCount; i++) {
stmts.push(temporaryDeclaration(bindingId, i));
}
return stmts;
}
function convertBuiltins(converterFactory, ast) {
var visitor = new _BuiltinAstConverter(converterFactory);
return ast.visit(visitor);
}
function temporaryName(bindingId, temporaryNumber) {
return "tmp_" + bindingId + "_" + temporaryNumber;
}
function temporaryDeclaration(bindingId, temporaryNumber) {
return new o.DeclareVarStmt(temporaryName(bindingId, temporaryNumber), o.NULL_EXPR);
}
exports.temporaryDeclaration = temporaryDeclaration;
function prependTemporaryDecls(temporaryCount, bindingId, statements) {
for (var i = temporaryCount - 1; i >= 0; i--) {
statements.unshift(temporaryDeclaration(bindingId, i));
}
}
var _Mode;
(function (_Mode) {
_Mode[_Mode["Statement"] = 0] = "Statement";
_Mode[_Mode["Expression"] = 1] = "Expression";
})(_Mode || (_Mode = {}));
function ensureStatementMode(mode, ast) {
if (mode !== _Mode.Statement) {
throw new Error("Expected a statement, but saw " + ast);
}
}
function ensureExpressionMode(mode, ast) {
if (mode !== _Mode.Expression) {
throw new Error("Expected an expression, but saw " + ast);
}
}
function convertToStatementIfNeeded(mode, expr) {
if (mode === _Mode.Statement) {
return expr.toStmt();
}
else {
return expr;
}
}
var _BuiltinAstConverter = /** @class */ (function (_super) {
tslib_1.__extends(_BuiltinAstConverter, _super);
function _BuiltinAstConverter(_converterFactory) {
var _this = _super.call(this) || this;
_this._converterFactory = _converterFactory;
return _this;
}
_BuiltinAstConverter.prototype.visitPipe = function (ast, context) {
var _this = this;
var args = tslib_1.__spread([ast.exp], ast.args).map(function (ast) { return ast.visit(_this, context); });
return new BuiltinFunctionCall(ast.span, ast.sourceSpan, args, this._converterFactory.createPipeConverter(ast.name, args.length));
};
_BuiltinAstConverter.prototype.visitLiteralArray = function (ast, context) {
var _this = this;
var args = ast.expressions.map(function (ast) { return ast.visit(_this, context); });
return new BuiltinFunctionCall(ast.span, ast.sourceSpan, args, this._converterFactory.createLiteralArrayConverter(ast.expressions.length));
};
_BuiltinAstConverter.prototype.visitLiteralMap = function (ast, context) {
var _this = this;
var args = ast.values.map(function (ast) { return ast.visit(_this, context); });
return new BuiltinFunctionCall(ast.span, ast.sourceSpan, args, this._converterFactory.createLiteralMapConverter(ast.keys));
};
return _BuiltinAstConverter;
}(cdAst.AstTransformer));
var _AstToIrVisitor = /** @class */ (function () {
function _AstToIrVisitor(_localResolver, _implicitReceiver, bindingId, interpolationFunction, baseSourceSpan, implicitReceiverAccesses) {
this._localResolver = _localResolver;
this._implicitReceiver = _implicitReceiver;
this.bindingId = bindingId;
this.interpolationFunction = interpolationFunction;
this.baseSourceSpan = baseSourceSpan;
this.implicitReceiverAccesses = implicitReceiverAccesses;
this._nodeMap = new Map();
this._resultMap = new Map();
this._currentTemporary = 0;
this.temporaryCount = 0;
this.usesImplicitReceiver = false;
}
_AstToIrVisitor.prototype.visitUnary = function (ast, mode) {
var op;
switch (ast.operator) {
case '+':
op = o.UnaryOperator.Plus;
break;
case '-':
op = o.UnaryOperator.Minus;
break;
default:
throw new Error("Unsupported operator " + ast.operator);
}
return convertToStatementIfNeeded(mode, new o.UnaryOperatorExpr(op, this._visit(ast.expr, _Mode.Expression), undefined, this.convertSourceSpan(ast.span)));
};
_AstToIrVisitor.prototype.visitBinary = function (ast, mode) {
var op;
switch (ast.operation) {
case '+':
op = o.BinaryOperator.Plus;
break;
case '-':
op = o.BinaryOperator.Minus;
break;
case '*':
op = o.BinaryOperator.Multiply;
break;
case '/':
op = o.BinaryOperator.Divide;
break;
case '%':
op = o.BinaryOperator.Modulo;
break;
case '&&':
op = o.BinaryOperator.And;
break;
case '||':
op = o.BinaryOperator.Or;
break;
case '==':
op = o.BinaryOperator.Equals;
break;
case '!=':
op = o.BinaryOperator.NotEquals;
break;
case '===':
op = o.BinaryOperator.Identical;
break;
case '!==':
op = o.BinaryOperator.NotIdentical;
break;
case '<':
op = o.BinaryOperator.Lower;
break;
case '>':
op = o.BinaryOperator.Bigger;
break;
case '<=':
op = o.BinaryOperator.LowerEquals;
break;
case '>=':
op = o.BinaryOperator.BiggerEquals;
break;
default:
throw new Error("Unsupported operation " + ast.operation);
}
return convertToStatementIfNeeded(mode, new o.BinaryOperatorExpr(op, this._visit(ast.left, _Mode.Expression), this._visit(ast.right, _Mode.Expression), undefined, this.convertSourceSpan(ast.span)));
};
_AstToIrVisitor.prototype.visitChain = function (ast, mode) {
ensureStatementMode(mode, ast);
return this.visitAll(ast.expressions, mode);
};
_AstToIrVisitor.prototype.visitConditional = function (ast, mode) {
var value = this._visit(ast.condition, _Mode.Expression);
return convertToStatementIfNeeded(mode, value.conditional(this._visit(ast.trueExp, _Mode.Expression), this._visit(ast.falseExp, _Mode.Expression), this.convertSourceSpan(ast.span)));
};
_AstToIrVisitor.prototype.visitPipe = function (ast, mode) {
throw new Error("Illegal state: Pipes should have been converted into functions. Pipe: " + ast.name);
};
_AstToIrVisitor.prototype.visitFunctionCall = function (ast, mode) {
var convertedArgs = this.visitAll(ast.args, _Mode.Expression);
var fnResult;
if (ast instanceof BuiltinFunctionCall) {
fnResult = ast.converter(convertedArgs);
}
else {
fnResult = this._visit(ast.target, _Mode.Expression)
.callFn(convertedArgs, this.convertSourceSpan(ast.span));
}
return convertToStatementIfNeeded(mode, fnResult);
};
_AstToIrVisitor.prototype.visitImplicitReceiver = function (ast, mode) {
ensureExpressionMode(mode, ast);
this.usesImplicitReceiver = true;
return this._implicitReceiver;
};
_AstToIrVisitor.prototype.visitThisReceiver = function (ast, mode) {
return this.visitImplicitReceiver(ast, mode);
};
_AstToIrVisitor.prototype.visitInterpolation = function (ast, mode) {
ensureExpressionMode(mode, ast);
var args = [o.literal(ast.expressions.length)];
for (var i = 0; i < ast.strings.length - 1; i++) {
args.push(o.literal(ast.strings[i]));
args.push(this._visit(ast.expressions[i], _Mode.Expression));
}
args.push(o.literal(ast.strings[ast.strings.length - 1]));
if (this.interpolationFunction) {
return this.interpolationFunction(args);
}
return ast.expressions.length <= 9 ?
o.importExpr(identifiers_1.Identifiers.inlineInterpolate).callFn(args) :
o.importExpr(identifiers_1.Identifiers.interpolate).callFn([
args[0], o.literalArr(args.slice(1), undefined, this.convertSourceSpan(ast.span))
]);
};
_AstToIrVisitor.prototype.visitKeyedRead = function (ast, mode) {
var leftMostSafe = this.leftMostSafeNode(ast);
if (leftMostSafe) {
return this.convertSafeAccess(ast, leftMostSafe, mode);
}
else {
return convertToStatementIfNeeded(mode, this._visit(ast.obj, _Mode.Expression).key(this._visit(ast.key, _Mode.Expression)));
}
};
_AstToIrVisitor.prototype.visitKeyedWrite = function (ast, mode) {
var obj = this._visit(ast.obj, _Mode.Expression);
var key = this._visit(ast.key, _Mode.Expression);
var value = this._visit(ast.value, _Mode.Expression);
return convertToStatementIfNeeded(mode, obj.key(key).set(value));
};
_AstToIrVisitor.prototype.visitLiteralArray = function (ast, mode) {
throw new Error("Illegal State: literal arrays should have been converted into functions");
};
_AstToIrVisitor.prototype.visitLiteralMap = function (ast, mode) {
throw new Error("Illegal State: literal maps should have been converted into functions");
};
_AstToIrVisitor.prototype.visitLiteralPrimitive = function (ast, mode) {
// For literal values of null, undefined, true, or false allow type interference
// to infer the type.
var type = ast.value === null || ast.value === undefined || ast.value === true || ast.value === true ?
o.INFERRED_TYPE :
undefined;
return convertToStatementIfNeeded(mode, o.literal(ast.value, type, this.convertSourceSpan(ast.span)));
};
_AstToIrVisitor.prototype._getLocal = function (name, receiver) {
var _a;
if (((_a = this._localResolver.globals) === null || _a === void 0 ? void 0 : _a.has(name)) && receiver instanceof cdAst.ThisReceiver) {
return null;
}
return this._localResolver.getLocal(name);
};
_AstToIrVisitor.prototype.visitMethodCall = function (ast, mode) {
if (ast.receiver instanceof cdAst.ImplicitReceiver &&
!(ast.receiver instanceof cdAst.ThisReceiver) && ast.name === '$any') {
var args = this.visitAll(ast.args, _Mode.Expression);
if (args.length != 1) {
throw new Error("Invalid call to $any, expected 1 argument but received " + (args.length || 'none'));
}
return args[0].cast(o.DYNAMIC_TYPE, this.convertSourceSpan(ast.span));
}
var leftMostSafe = this.leftMostSafeNode(ast);
if (leftMostSafe) {
return this.convertSafeAccess(ast, leftMostSafe, mode);
}
else {
var args = this.visitAll(ast.args, _Mode.Expression);
var prevUsesImplicitReceiver = this.usesImplicitReceiver;
var result = null;
var receiver = this._visit(ast.receiver, _Mode.Expression);
if (receiver === this._implicitReceiver) {
var varExpr = this._getLocal(ast.name, ast.receiver);
if (varExpr) {
// Restore the previous "usesImplicitReceiver" state since the implicit
// receiver has been replaced with a resolved local expression.
this.usesImplicitReceiver = prevUsesImplicitReceiver;
result = varExpr.callFn(args);
this.addImplicitReceiverAccess(ast.name);
}
}
if (result == null) {
result = receiver.callMethod(ast.name, args, this.convertSourceSpan(ast.span));
}
return convertToStatementIfNeeded(mode, result);
}
};
_AstToIrVisitor.prototype.visitPrefixNot = function (ast, mode) {
return convertToStatementIfNeeded(mode, o.not(this._visit(ast.expression, _Mode.Expression)));
};
_AstToIrVisitor.prototype.visitNonNullAssert = function (ast, mode) {
return convertToStatementIfNeeded(mode, o.assertNotNull(this._visit(ast.expression, _Mode.Expression)));
};
_AstToIrVisitor.prototype.visitPropertyRead = function (ast, mode) {
var leftMostSafe = this.leftMostSafeNode(ast);
if (leftMostSafe) {
return this.convertSafeAccess(ast, leftMostSafe, mode);
}
else {
var result = null;
var prevUsesImplicitReceiver = this.usesImplicitReceiver;
var receiver = this._visit(ast.receiver, _Mode.Expression);
if (receiver === this._implicitReceiver) {
result = this._getLocal(ast.name, ast.receiver);
if (result) {
// Restore the previous "usesImplicitReceiver" state since the implicit
// receiver has been replaced with a resolved local expression.
this.usesImplicitReceiver = prevUsesImplicitReceiver;
this.addImplicitReceiverAccess(ast.name);
}
}
if (result == null) {
result = receiver.prop(ast.name);
}
return convertToStatementIfNeeded(mode, result);
}
};
_AstToIrVisitor.prototype.visitPropertyWrite = function (ast, mode) {
var receiver = this._visit(ast.receiver, _Mode.Expression);
var prevUsesImplicitReceiver = this.usesImplicitReceiver;
var varExpr = null;
if (receiver === this._implicitReceiver) {
var localExpr = this._getLocal(ast.name, ast.receiver);
if (localExpr) {
if (localExpr instanceof o.ReadPropExpr) {
// If the local variable is a property read expression, it's a reference
// to a 'context.property' value and will be used as the target of the
// write expression.
varExpr = localExpr;
// Restore the previous "usesImplicitReceiver" state since the implicit
// receiver has been replaced with a resolved local expression.
this.usesImplicitReceiver = prevUsesImplicitReceiver;
this.addImplicitReceiverAccess(ast.name);
}
else {
// Otherwise it's an error.
var receiver_1 = ast.name;
var value = (ast.value instanceof cdAst.PropertyRead) ? ast.value.name : undefined;
throw new Error("Cannot assign value \"" + value + "\" to template variable \"" + receiver_1 + "\". Template variables are read-only.");
}
}
}
// If no local expression could be produced, use the original receiver's
// property as the target.
if (varExpr === null) {
varExpr = receiver.prop(ast.name);
}
return convertToStatementIfNeeded(mode, varExpr.set(this._visit(ast.value, _Mode.Expression)));
};
_AstToIrVisitor.prototype.visitSafePropertyRead = function (ast, mode) {
return this.convertSafeAccess(ast, this.leftMostSafeNode(ast), mode);
};
_AstToIrVisitor.prototype.visitSafeMethodCall = function (ast, mode) {
return this.convertSafeAccess(ast, this.leftMostSafeNode(ast), mode);
};
_AstToIrVisitor.prototype.visitAll = function (asts, mode) {
var _this = this;
return asts.map(function (ast) { return _this._visit(ast, mode); });
};
_AstToIrVisitor.prototype.visitQuote = function (ast, mode) {
throw new Error("Quotes are not supported for evaluation!\n Statement: " + ast.uninterpretedExpression + " located at " + ast.location);
};
_AstToIrVisitor.prototype._visit = function (ast, mode) {
var result = this._resultMap.get(ast);
if (result)
return result;
return (this._nodeMap.get(ast) || ast).visit(this, mode);
};
_AstToIrVisitor.prototype.convertSafeAccess = function (ast, leftMostSafe, mode) {
// If the expression contains a safe access node on the left it needs to be converted to
// an expression that guards the access to the member by checking the receiver for blank. As
// execution proceeds from left to right, the left most part of the expression must be guarded
// first but, because member access is left associative, the right side of the expression is at
// the top of the AST. The desired result requires lifting a copy of the left part of the
// expression up to test it for blank before generating the unguarded version.
// Consider, for example the following expression: a?.b.c?.d.e
// This results in the ast:
// .
// / \
// ?. e
// / \
// . d
// / \
// ?. c
// / \
// a b
// The following tree should be generated:
//
// /---- ? ----\
// / | \
// a /--- ? ---\ null
// / | \
// . . null
// / \ / \
// . c . e
// / \ / \
// a b . d
// / \
// . c
// / \
// a b
//
// Notice that the first guard condition is the left hand of the left most safe access node
// which comes in as leftMostSafe to this routine.
var guardedExpression = this._visit(leftMostSafe.receiver, _Mode.Expression);
var temporary = undefined;
if (this.needsTemporary(leftMostSafe.receiver)) {
// If the expression has method calls or pipes then we need to save the result into a
// temporary variable to avoid calling stateful or impure code more than once.
temporary = this.allocateTemporary();
// Preserve the result in the temporary variable
guardedExpression = temporary.set(guardedExpression);
// Ensure all further references to the guarded expression refer to the temporary instead.
this._resultMap.set(leftMostSafe.receiver, temporary);
}
var condition = guardedExpression.isBlank();
// Convert the ast to an unguarded access to the receiver's member. The map will substitute
// leftMostNode with its unguarded version in the call to `this.visit()`.
if (leftMostSafe instanceof cdAst.SafeMethodCall) {
this._nodeMap.set(leftMostSafe, new cdAst.MethodCall(leftMostSafe.span, leftMostSafe.sourceSpan, leftMostSafe.nameSpan, leftMostSafe.receiver, leftMostSafe.name, leftMostSafe.args));
}
else {
this._nodeMap.set(leftMostSafe, new cdAst.PropertyRead(leftMostSafe.span, leftMostSafe.sourceSpan, leftMostSafe.nameSpan, leftMostSafe.receiver, leftMostSafe.name));
}
// Recursively convert the node now without the guarded member access.
var access = this._visit(ast, _Mode.Expression);
// Remove the mapping. This is not strictly required as the converter only traverses each node
// once but is safer if the conversion is changed to traverse the nodes more than once.
this._nodeMap.delete(leftMostSafe);
// If we allocated a temporary, release it.
if (temporary) {
this.releaseTemporary(temporary);
}
// Produce the conditional
return convertToStatementIfNeeded(mode, condition.conditional(o.literal(null), access));
};
// Given an expression of the form a?.b.c?.d.e then the left most safe node is
// the (a?.b). The . and ?. are left associative thus can be rewritten as:
// ((((a?.c).b).c)?.d).e. This returns the most deeply nested safe read or
// safe method call as this needs to be transformed initially to:
// a == null ? null : a.c.b.c?.d.e
// then to:
// a == null ? null : a.b.c == null ? null : a.b.c.d.e
_AstToIrVisitor.prototype.leftMostSafeNode = function (ast) {
var _this = this;
var visit = function (visitor, ast) {
return (_this._nodeMap.get(ast) || ast).visit(visitor);
};
return ast.visit({
visitUnary: function (ast) {
return null;
},
visitBinary: function (ast) {
return null;
},
visitChain: function (ast) {
return null;
},
visitConditional: function (ast) {
return null;
},
visitFunctionCall: function (ast) {
return null;
},
visitImplicitReceiver: function (ast) {
return null;
},
visitThisReceiver: function (ast) {
return null;
},
visitInterpolation: function (ast) {
return null;
},
visitKeyedRead: function (ast) {
return visit(this, ast.obj);
},
visitKeyedWrite: function (ast) {
return null;
},
visitLiteralArray: function (ast) {
return null;
},
visitLiteralMap: function (ast) {
return null;
},
visitLiteralPrimitive: function (ast) {
return null;
},
visitMethodCall: function (ast) {
return visit(this, ast.receiver);
},
visitPipe: function (ast) {
return null;
},
visitPrefixNot: function (ast) {
return null;
},
visitNonNullAssert: function (ast) {
return null;
},
visitPropertyRead: function (ast) {
return visit(this, ast.receiver);
},
visitPropertyWrite: function (ast) {
return null;
},
visitQuote: function (ast) {
return null;
},
visitSafeMethodCall: function (ast) {
return visit(this, ast.receiver) || ast;
},
visitSafePropertyRead: function (ast) {
return visit(this, ast.receiver) || ast;
}
});
};
// Returns true of the AST includes a method or a pipe indicating that, if the
// expression is used as the target of a safe property or method access then
// the expression should be stored into a temporary variable.
_AstToIrVisitor.prototype.needsTemporary = function (ast) {
var _this = this;
var visit = function (visitor, ast) {
return ast && (_this._nodeMap.get(ast) || ast).visit(visitor);
};
var visitSome = function (visitor, ast) {
return ast.some(function (ast) { return visit(visitor, ast); });
};
return ast.visit({
visitUnary: function (ast) {
return visit(this, ast.expr);
},
visitBinary: function (ast) {
return visit(this, ast.left) || visit(this, ast.right);
},
visitChain: function (ast) {
return false;
},
visitConditional: function (ast) {
return visit(this, ast.condition) || visit(this, ast.trueExp) || visit(this, ast.falseExp);
},
visitFunctionCall: function (ast) {
return true;
},
visitImplicitReceiver: function (ast) {
return false;
},
visitThisReceiver: function (ast) {
return false;
},
visitInterpolation: function (ast) {
return visitSome(this, ast.expressions);
},
visitKeyedRead: function (ast) {
return false;
},
visitKeyedWrite: function (ast) {
return false;
},
visitLiteralArray: function (ast) {
return true;
},
visitLiteralMap: function (ast) {
return true;
},
visitLiteralPrimitive: function (ast) {
return false;
},
visitMethodCall: function (ast) {
return true;
},
visitPipe: function (ast) {
return true;
},
visitPrefixNot: function (ast) {
return visit(this, ast.expression);
},
visitNonNullAssert: function (ast) {
return visit(this, ast.expression);
},
visitPropertyRead: function (ast) {
return false;
},
visitPropertyWrite: function (ast) {
return false;
},
visitQuote: function (ast) {
return false;
},
visitSafeMethodCall: function (ast) {
return true;
},
visitSafePropertyRead: function (ast) {
return false;
}
});
};
_AstToIrVisitor.prototype.allocateTemporary = function () {
var tempNumber = this._currentTemporary++;
this.temporaryCount = Math.max(this._currentTemporary, this.temporaryCount);
return new o.ReadVarExpr(temporaryName(this.bindingId, tempNumber));
};
_AstToIrVisitor.prototype.releaseTemporary = function (temporary) {
this._currentTemporary--;
if (temporary.name != temporaryName(this.bindingId, this._currentTemporary)) {
throw new Error("Temporary " + temporary.name + " released out of order");
}
};
/**
* Creates an absolute `ParseSourceSpan` from the relative `ParseSpan`.
*
* `ParseSpan` objects are relative to the start of the expression.
* This method converts these to full `ParseSourceSpan` objects that
* show where the span is within the overall source file.
*
* @param span the relative span to convert.
* @returns a `ParseSourceSpan` for the given span or null if no
* `baseSourceSpan` was provided to this class.
*/
_AstToIrVisitor.prototype.convertSourceSpan = function (span) {
if (this.baseSourceSpan) {
var start = this.baseSourceSpan.start.moveBy(span.start);
var end = this.baseSourceSpan.start.moveBy(span.end);
var fullStart = this.baseSourceSpan.fullStart.moveBy(span.start);
return new parse_util_1.ParseSourceSpan(start, end, fullStart);
}
else {
return null;
}
};
/** Adds the name of an AST to the list of implicit receiver accesses. */
_AstToIrVisitor.prototype.addImplicitReceiverAccess = function (name) {
if (this.implicitReceiverAccesses) {
this.implicitReceiverAccesses.add(name);
}
};
return _AstToIrVisitor;
}());
function flattenStatements(arg, output) {
if (Array.isArray(arg)) {
arg.forEach(function (entry) { return flattenStatements(entry, output); });
}
else {
output.push(arg);
}
}
var DefaultLocalResolver = /** @class */ (function () {
function DefaultLocalResolver(globals) {
this.globals = globals;
}
DefaultLocalResolver.prototype.notifyImplicitReceiverUse = function () { };
DefaultLocalResolver.prototype.getLocal = function (name) {
if (name === EventHandlerVars.event.name) {
return EventHandlerVars.event;
}
return null;
};
return DefaultLocalResolver;
}());
function createCurrValueExpr(bindingId) {
return o.variable("currVal_" + bindingId); // fix syntax highlighting: `
}
function createPreventDefaultVar(bindingId) {
return o.variable("pd_" + bindingId);
}
function convertStmtIntoExpression(stmt) {
if (stmt instanceof o.ExpressionStatement) {
return stmt.expr;
}
else if (stmt instanceof o.ReturnStatement) {
return stmt.value;
}
return null;
}
var BuiltinFunctionCall = /** @class */ (function (_super) {
tslib_1.__extends(BuiltinFunctionCall, _super);
function BuiltinFunctionCall(span, sourceSpan, args, converter) {
var _this = _super.call(this, span, sourceSpan, null, args) || this;
_this.args = args;
_this.converter = converter;
return _this;
}
return BuiltinFunctionCall;
}(cdAst.FunctionCall));
exports.BuiltinFunctionCall = BuiltinFunctionCall;
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXhwcmVzc2lvbl9jb252ZXJ0ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9wYWNrYWdlcy9jb21waWxlci9zcmMvY29tcGlsZXJfdXRpbC9leHByZXNzaW9uX2NvbnZlcnRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7O0dBTUc7Ozs7Ozs7Ozs7Ozs7O0lBRUgsbUVBQWtEO0lBQ2xELGlFQUEyQztJQUMzQywyREFBMEM7SUFDMUMsK0RBQThDO0lBRTlDO1FBQUE7UUFFQSxDQUFDO1FBRFEsc0JBQUssR0FBRyxDQUFDLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ3RDLHVCQUFDO0tBQUEsQUFGRCxJQUVDO0lBRlksNENBQWdCO0lBVTdCO1FBS0U7UUFDSTs7V0FFRztRQUNJLEtBQW9CO1FBQzNCOztXQUVHO1FBQ0ksWUFBMkI7WUFKM0IsVUFBSyxHQUFMLEtBQUssQ0FBZTtZQUlwQixpQkFBWSxHQUFaLFlBQVksQ0FBZTtZQUNwQzs7Ozs7Ozs7Ozs7Ozs7OztlQWdCRztZQUNILHNFQUFzRTtZQUN0RSxJQUFJLENBQUMsWUFBWSxHQUFHLEtBQUssQ0FBQyxHQUFHLENBQUMsVUFBQyxTQUFzQjtnQkFDbkQsSUFBSSxTQUFTLFlBQVksQ0FBQyxDQUFDLGNBQWMsSUFBSSxTQUFTLENBQUMsSUFBSSxJQUFJLFlBQVksQ0FBQyxJQUFJO29CQUM1RSxTQUFTLENBQUMsS0FBSyxZQUFZLENBQUMsQ0FBQyxrQkFBa0IsRUFBRTtvQkFDbkQsSUFBTSxHQUFHLEdBQUcsU0FBUyxDQUFDLEtBQUssQ0FBQyxHQUFpQixDQUFDO29CQUM5QyxPQUFPLElBQUksQ0FBQyxDQUFDLGVBQWUsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUM7aUJBQ3pDO2dCQUNELE9BQU8sU0FBUyxDQUFDO1lBQ25CLENBQUMsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztRQUNILGlDQUFDO0lBQUQsQ0FBQyxBQXpD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