UNPKG

@angular/compiler

Version:

Angular - the compiler library

886 lines • 128 kB
/** * @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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