"Fossies" - the Fresh Open Source Software Archive

Member "angular-8.2.14/packages/compiler/src/output/abstract_emitter.ts" (13 Nov 2019, 16928 Bytes) of package /linux/www/angular-8.2.14.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) TypeScript source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /**
    2  * @license
    3  * Copyright Google Inc. All Rights Reserved.
    4  *
    5  * Use of this source code is governed by an MIT-style license that can be
    6  * found in the LICENSE file at https://angular.io/license
    7  */
    8 
    9 import {ParseSourceSpan} from '../parse_util';
   10 
   11 import * as o from './output_ast';
   12 import {SourceMapGenerator} from './source_map';
   13 
   14 const _SINGLE_QUOTE_ESCAPE_STRING_RE = /'|\\|\n|\r|\$/g;
   15 const _LEGAL_IDENTIFIER_RE = /^[$A-Z_][0-9A-Z_$]*$/i;
   16 const _INDENT_WITH = '  ';
   17 export const CATCH_ERROR_VAR = o.variable('error', null, null);
   18 export const CATCH_STACK_VAR = o.variable('stack', null, null);
   19 
   20 export interface OutputEmitter {
   21   emitStatements(genFilePath: string, stmts: o.Statement[], preamble?: string|null): string;
   22 }
   23 
   24 class _EmittedLine {
   25   partsLength = 0;
   26   parts: string[] = [];
   27   srcSpans: (ParseSourceSpan|null)[] = [];
   28   constructor(public indent: number) {}
   29 }
   30 
   31 export class EmitterVisitorContext {
   32   static createRoot(): EmitterVisitorContext { return new EmitterVisitorContext(0); }
   33 
   34   private _lines: _EmittedLine[];
   35   private _classes: o.ClassStmt[] = [];
   36   private _preambleLineCount = 0;
   37 
   38   constructor(private _indent: number) { this._lines = [new _EmittedLine(_indent)]; }
   39 
   40   private get _currentLine(): _EmittedLine { return this._lines[this._lines.length - 1]; }
   41 
   42   println(from?: {sourceSpan: ParseSourceSpan | null}|null, lastPart: string = ''): void {
   43     this.print(from || null, lastPart, true);
   44   }
   45 
   46   lineIsEmpty(): boolean { return this._currentLine.parts.length === 0; }
   47 
   48   lineLength(): number {
   49     return this._currentLine.indent * _INDENT_WITH.length + this._currentLine.partsLength;
   50   }
   51 
   52   print(from: {sourceSpan: ParseSourceSpan | null}|null, part: string, newLine: boolean = false) {
   53     if (part.length > 0) {
   54       this._currentLine.parts.push(part);
   55       this._currentLine.partsLength += part.length;
   56       this._currentLine.srcSpans.push(from && from.sourceSpan || null);
   57     }
   58     if (newLine) {
   59       this._lines.push(new _EmittedLine(this._indent));
   60     }
   61   }
   62 
   63   removeEmptyLastLine() {
   64     if (this.lineIsEmpty()) {
   65       this._lines.pop();
   66     }
   67   }
   68 
   69   incIndent() {
   70     this._indent++;
   71     if (this.lineIsEmpty()) {
   72       this._currentLine.indent = this._indent;
   73     }
   74   }
   75 
   76   decIndent() {
   77     this._indent--;
   78     if (this.lineIsEmpty()) {
   79       this._currentLine.indent = this._indent;
   80     }
   81   }
   82 
   83   pushClass(clazz: o.ClassStmt) { this._classes.push(clazz); }
   84 
   85   popClass(): o.ClassStmt { return this._classes.pop() !; }
   86 
   87   get currentClass(): o.ClassStmt|null {
   88     return this._classes.length > 0 ? this._classes[this._classes.length - 1] : null;
   89   }
   90 
   91   toSource(): string {
   92     return this.sourceLines
   93         .map(l => l.parts.length > 0 ? _createIndent(l.indent) + l.parts.join('') : '')
   94         .join('\n');
   95   }
   96 
   97   toSourceMapGenerator(genFilePath: string, startsAtLine: number = 0): SourceMapGenerator {
   98     const map = new SourceMapGenerator(genFilePath);
   99 
  100     let firstOffsetMapped = false;
  101     const mapFirstOffsetIfNeeded = () => {
  102       if (!firstOffsetMapped) {
  103         // Add a single space so that tools won't try to load the file from disk.
  104         // Note: We are using virtual urls like `ng:///`, so we have to
  105         // provide a content here.
  106         map.addSource(genFilePath, ' ').addMapping(0, genFilePath, 0, 0);
  107         firstOffsetMapped = true;
  108       }
  109     };
  110 
  111     for (let i = 0; i < startsAtLine; i++) {
  112       map.addLine();
  113       mapFirstOffsetIfNeeded();
  114     }
  115 
  116     this.sourceLines.forEach((line, lineIdx) => {
  117       map.addLine();
  118 
  119       const spans = line.srcSpans;
  120       const parts = line.parts;
  121       let col0 = line.indent * _INDENT_WITH.length;
  122       let spanIdx = 0;
  123       // skip leading parts without source spans
  124       while (spanIdx < spans.length && !spans[spanIdx]) {
  125         col0 += parts[spanIdx].length;
  126         spanIdx++;
  127       }
  128       if (spanIdx < spans.length && lineIdx === 0 && col0 === 0) {
  129         firstOffsetMapped = true;
  130       } else {
  131         mapFirstOffsetIfNeeded();
  132       }
  133 
  134       while (spanIdx < spans.length) {
  135         const span = spans[spanIdx] !;
  136         const source = span.start.file;
  137         const sourceLine = span.start.line;
  138         const sourceCol = span.start.col;
  139         map.addSource(source.url, source.content)
  140             .addMapping(col0, source.url, sourceLine, sourceCol);
  141 
  142         col0 += parts[spanIdx].length;
  143         spanIdx++;
  144 
  145         // assign parts without span or the same span to the previous segment
  146         while (spanIdx < spans.length && (span === spans[spanIdx] || !spans[spanIdx])) {
  147           col0 += parts[spanIdx].length;
  148           spanIdx++;
  149         }
  150       }
  151     });
  152 
  153     return map;
  154   }
  155 
  156   setPreambleLineCount(count: number) { return this._preambleLineCount = count; }
  157 
  158   spanOf(line: number, column: number): ParseSourceSpan|null {
  159     const emittedLine = this._lines[line - this._preambleLineCount];
  160     if (emittedLine) {
  161       let columnsLeft = column - _createIndent(emittedLine.indent).length;
  162       for (let partIndex = 0; partIndex < emittedLine.parts.length; partIndex++) {
  163         const part = emittedLine.parts[partIndex];
  164         if (part.length > columnsLeft) {
  165           return emittedLine.srcSpans[partIndex];
  166         }
  167         columnsLeft -= part.length;
  168       }
  169     }
  170     return null;
  171   }
  172 
  173   private get sourceLines(): _EmittedLine[] {
  174     if (this._lines.length && this._lines[this._lines.length - 1].parts.length === 0) {
  175       return this._lines.slice(0, -1);
  176     }
  177     return this._lines;
  178   }
  179 }
  180 
  181 export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.ExpressionVisitor {
  182   constructor(private _escapeDollarInStrings: boolean) {}
  183 
  184   visitExpressionStmt(stmt: o.ExpressionStatement, ctx: EmitterVisitorContext): any {
  185     stmt.expr.visitExpression(this, ctx);
  186     ctx.println(stmt, ';');
  187     return null;
  188   }
  189 
  190   visitReturnStmt(stmt: o.ReturnStatement, ctx: EmitterVisitorContext): any {
  191     ctx.print(stmt, `return `);
  192     stmt.value.visitExpression(this, ctx);
  193     ctx.println(stmt, ';');
  194     return null;
  195   }
  196 
  197   abstract visitCastExpr(ast: o.CastExpr, context: any): any;
  198 
  199   abstract visitDeclareClassStmt(stmt: o.ClassStmt, ctx: EmitterVisitorContext): any;
  200 
  201   visitIfStmt(stmt: o.IfStmt, ctx: EmitterVisitorContext): any {
  202     ctx.print(stmt, `if (`);
  203     stmt.condition.visitExpression(this, ctx);
  204     ctx.print(stmt, `) {`);
  205     const hasElseCase = stmt.falseCase != null && stmt.falseCase.length > 0;
  206     if (stmt.trueCase.length <= 1 && !hasElseCase) {
  207       ctx.print(stmt, ` `);
  208       this.visitAllStatements(stmt.trueCase, ctx);
  209       ctx.removeEmptyLastLine();
  210       ctx.print(stmt, ` `);
  211     } else {
  212       ctx.println();
  213       ctx.incIndent();
  214       this.visitAllStatements(stmt.trueCase, ctx);
  215       ctx.decIndent();
  216       if (hasElseCase) {
  217         ctx.println(stmt, `} else {`);
  218         ctx.incIndent();
  219         this.visitAllStatements(stmt.falseCase, ctx);
  220         ctx.decIndent();
  221       }
  222     }
  223     ctx.println(stmt, `}`);
  224     return null;
  225   }
  226 
  227   abstract visitTryCatchStmt(stmt: o.TryCatchStmt, ctx: EmitterVisitorContext): any;
  228 
  229   visitThrowStmt(stmt: o.ThrowStmt, ctx: EmitterVisitorContext): any {
  230     ctx.print(stmt, `throw `);
  231     stmt.error.visitExpression(this, ctx);
  232     ctx.println(stmt, `;`);
  233     return null;
  234   }
  235   visitCommentStmt(stmt: o.CommentStmt, ctx: EmitterVisitorContext): any {
  236     if (stmt.multiline) {
  237       ctx.println(stmt, `/* ${stmt.comment} */`);
  238     } else {
  239       stmt.comment.split('\n').forEach((line) => { ctx.println(stmt, `// ${line}`); });
  240     }
  241     return null;
  242   }
  243   visitJSDocCommentStmt(stmt: o.JSDocCommentStmt, ctx: EmitterVisitorContext) {
  244     ctx.println(stmt, `/*${stmt.toString()}*/`);
  245     return null;
  246   }
  247 
  248   abstract visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any;
  249 
  250   visitWriteVarExpr(expr: o.WriteVarExpr, ctx: EmitterVisitorContext): any {
  251     const lineWasEmpty = ctx.lineIsEmpty();
  252     if (!lineWasEmpty) {
  253       ctx.print(expr, '(');
  254     }
  255     ctx.print(expr, `${expr.name} = `);
  256     expr.value.visitExpression(this, ctx);
  257     if (!lineWasEmpty) {
  258       ctx.print(expr, ')');
  259     }
  260     return null;
  261   }
  262   visitWriteKeyExpr(expr: o.WriteKeyExpr, ctx: EmitterVisitorContext): any {
  263     const lineWasEmpty = ctx.lineIsEmpty();
  264     if (!lineWasEmpty) {
  265       ctx.print(expr, '(');
  266     }
  267     expr.receiver.visitExpression(this, ctx);
  268     ctx.print(expr, `[`);
  269     expr.index.visitExpression(this, ctx);
  270     ctx.print(expr, `] = `);
  271     expr.value.visitExpression(this, ctx);
  272     if (!lineWasEmpty) {
  273       ctx.print(expr, ')');
  274     }
  275     return null;
  276   }
  277   visitWritePropExpr(expr: o.WritePropExpr, ctx: EmitterVisitorContext): any {
  278     const lineWasEmpty = ctx.lineIsEmpty();
  279     if (!lineWasEmpty) {
  280       ctx.print(expr, '(');
  281     }
  282     expr.receiver.visitExpression(this, ctx);
  283     ctx.print(expr, `.${expr.name} = `);
  284     expr.value.visitExpression(this, ctx);
  285     if (!lineWasEmpty) {
  286       ctx.print(expr, ')');
  287     }
  288     return null;
  289   }
  290   visitInvokeMethodExpr(expr: o.InvokeMethodExpr, ctx: EmitterVisitorContext): any {
  291     expr.receiver.visitExpression(this, ctx);
  292     let name = expr.name;
  293     if (expr.builtin != null) {
  294       name = this.getBuiltinMethodName(expr.builtin);
  295       if (name == null) {
  296         // some builtins just mean to skip the call.
  297         return null;
  298       }
  299     }
  300     ctx.print(expr, `.${name}(`);
  301     this.visitAllExpressions(expr.args, ctx, `,`);
  302     ctx.print(expr, `)`);
  303     return null;
  304   }
  305 
  306   abstract getBuiltinMethodName(method: o.BuiltinMethod): string;
  307 
  308   visitInvokeFunctionExpr(expr: o.InvokeFunctionExpr, ctx: EmitterVisitorContext): any {
  309     expr.fn.visitExpression(this, ctx);
  310     ctx.print(expr, `(`);
  311     this.visitAllExpressions(expr.args, ctx, ',');
  312     ctx.print(expr, `)`);
  313     return null;
  314   }
  315   visitWrappedNodeExpr(ast: o.WrappedNodeExpr<any>, ctx: EmitterVisitorContext): any {
  316     throw new Error('Abstract emitter cannot visit WrappedNodeExpr.');
  317   }
  318   visitTypeofExpr(expr: o.TypeofExpr, ctx: EmitterVisitorContext): any {
  319     ctx.print(expr, 'typeof ');
  320     expr.expr.visitExpression(this, ctx);
  321   }
  322   visitReadVarExpr(ast: o.ReadVarExpr, ctx: EmitterVisitorContext): any {
  323     let varName = ast.name !;
  324     if (ast.builtin != null) {
  325       switch (ast.builtin) {
  326         case o.BuiltinVar.Super:
  327           varName = 'super';
  328           break;
  329         case o.BuiltinVar.This:
  330           varName = 'this';
  331           break;
  332         case o.BuiltinVar.CatchError:
  333           varName = CATCH_ERROR_VAR.name !;
  334           break;
  335         case o.BuiltinVar.CatchStack:
  336           varName = CATCH_STACK_VAR.name !;
  337           break;
  338         default:
  339           throw new Error(`Unknown builtin variable ${ast.builtin}`);
  340       }
  341     }
  342     ctx.print(ast, varName);
  343     return null;
  344   }
  345   visitInstantiateExpr(ast: o.InstantiateExpr, ctx: EmitterVisitorContext): any {
  346     ctx.print(ast, `new `);
  347     ast.classExpr.visitExpression(this, ctx);
  348     ctx.print(ast, `(`);
  349     this.visitAllExpressions(ast.args, ctx, ',');
  350     ctx.print(ast, `)`);
  351     return null;
  352   }
  353 
  354   visitLiteralExpr(ast: o.LiteralExpr, ctx: EmitterVisitorContext): any {
  355     const value = ast.value;
  356     if (typeof value === 'string') {
  357       ctx.print(ast, escapeIdentifier(value, this._escapeDollarInStrings));
  358     } else {
  359       ctx.print(ast, `${value}`);
  360     }
  361     return null;
  362   }
  363 
  364   abstract visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any;
  365 
  366   visitConditionalExpr(ast: o.ConditionalExpr, ctx: EmitterVisitorContext): any {
  367     ctx.print(ast, `(`);
  368     ast.condition.visitExpression(this, ctx);
  369     ctx.print(ast, '? ');
  370     ast.trueCase.visitExpression(this, ctx);
  371     ctx.print(ast, ': ');
  372     ast.falseCase !.visitExpression(this, ctx);
  373     ctx.print(ast, `)`);
  374     return null;
  375   }
  376   visitNotExpr(ast: o.NotExpr, ctx: EmitterVisitorContext): any {
  377     ctx.print(ast, '!');
  378     ast.condition.visitExpression(this, ctx);
  379     return null;
  380   }
  381   visitAssertNotNullExpr(ast: o.AssertNotNull, ctx: EmitterVisitorContext): any {
  382     ast.condition.visitExpression(this, ctx);
  383     return null;
  384   }
  385   abstract visitFunctionExpr(ast: o.FunctionExpr, ctx: EmitterVisitorContext): any;
  386   abstract visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, context: any): any;
  387 
  388   visitBinaryOperatorExpr(ast: o.BinaryOperatorExpr, ctx: EmitterVisitorContext): any {
  389     let opStr: string;
  390     switch (ast.operator) {
  391       case o.BinaryOperator.Equals:
  392         opStr = '==';
  393         break;
  394       case o.BinaryOperator.Identical:
  395         opStr = '===';
  396         break;
  397       case o.BinaryOperator.NotEquals:
  398         opStr = '!=';
  399         break;
  400       case o.BinaryOperator.NotIdentical:
  401         opStr = '!==';
  402         break;
  403       case o.BinaryOperator.And:
  404         opStr = '&&';
  405         break;
  406       case o.BinaryOperator.BitwiseAnd:
  407         opStr = '&';
  408         break;
  409       case o.BinaryOperator.Or:
  410         opStr = '||';
  411         break;
  412       case o.BinaryOperator.Plus:
  413         opStr = '+';
  414         break;
  415       case o.BinaryOperator.Minus:
  416         opStr = '-';
  417         break;
  418       case o.BinaryOperator.Divide:
  419         opStr = '/';
  420         break;
  421       case o.BinaryOperator.Multiply:
  422         opStr = '*';
  423         break;
  424       case o.BinaryOperator.Modulo:
  425         opStr = '%';
  426         break;
  427       case o.BinaryOperator.Lower:
  428         opStr = '<';
  429         break;
  430       case o.BinaryOperator.LowerEquals:
  431         opStr = '<=';
  432         break;
  433       case o.BinaryOperator.Bigger:
  434         opStr = '>';
  435         break;
  436       case o.BinaryOperator.BiggerEquals:
  437         opStr = '>=';
  438         break;
  439       default:
  440         throw new Error(`Unknown operator ${ast.operator}`);
  441     }
  442     if (ast.parens) ctx.print(ast, `(`);
  443     ast.lhs.visitExpression(this, ctx);
  444     ctx.print(ast, ` ${opStr} `);
  445     ast.rhs.visitExpression(this, ctx);
  446     if (ast.parens) ctx.print(ast, `)`);
  447     return null;
  448   }
  449 
  450   visitReadPropExpr(ast: o.ReadPropExpr, ctx: EmitterVisitorContext): any {
  451     ast.receiver.visitExpression(this, ctx);
  452     ctx.print(ast, `.`);
  453     ctx.print(ast, ast.name);
  454     return null;
  455   }
  456   visitReadKeyExpr(ast: o.ReadKeyExpr, ctx: EmitterVisitorContext): any {
  457     ast.receiver.visitExpression(this, ctx);
  458     ctx.print(ast, `[`);
  459     ast.index.visitExpression(this, ctx);
  460     ctx.print(ast, `]`);
  461     return null;
  462   }
  463   visitLiteralArrayExpr(ast: o.LiteralArrayExpr, ctx: EmitterVisitorContext): any {
  464     ctx.print(ast, `[`);
  465     this.visitAllExpressions(ast.entries, ctx, ',');
  466     ctx.print(ast, `]`);
  467     return null;
  468   }
  469   visitLiteralMapExpr(ast: o.LiteralMapExpr, ctx: EmitterVisitorContext): any {
  470     ctx.print(ast, `{`);
  471     this.visitAllObjects(entry => {
  472       ctx.print(ast, `${escapeIdentifier(entry.key, this._escapeDollarInStrings, entry.quoted)}:`);
  473       entry.value.visitExpression(this, ctx);
  474     }, ast.entries, ctx, ',');
  475     ctx.print(ast, `}`);
  476     return null;
  477   }
  478   visitCommaExpr(ast: o.CommaExpr, ctx: EmitterVisitorContext): any {
  479     ctx.print(ast, '(');
  480     this.visitAllExpressions(ast.parts, ctx, ',');
  481     ctx.print(ast, ')');
  482     return null;
  483   }
  484   visitAllExpressions(expressions: o.Expression[], ctx: EmitterVisitorContext, separator: string):
  485       void {
  486     this.visitAllObjects(expr => expr.visitExpression(this, ctx), expressions, ctx, separator);
  487   }
  488 
  489   visitAllObjects<T>(
  490       handler: (t: T) => void, expressions: T[], ctx: EmitterVisitorContext,
  491       separator: string): void {
  492     let incrementedIndent = false;
  493     for (let i = 0; i < expressions.length; i++) {
  494       if (i > 0) {
  495         if (ctx.lineLength() > 80) {
  496           ctx.print(null, separator, true);
  497           if (!incrementedIndent) {
  498             // continuation are marked with double indent.
  499             ctx.incIndent();
  500             ctx.incIndent();
  501             incrementedIndent = true;
  502           }
  503         } else {
  504           ctx.print(null, separator, false);
  505         }
  506       }
  507       handler(expressions[i]);
  508     }
  509     if (incrementedIndent) {
  510       // continuation are marked with double indent.
  511       ctx.decIndent();
  512       ctx.decIndent();
  513     }
  514   }
  515 
  516   visitAllStatements(statements: o.Statement[], ctx: EmitterVisitorContext): void {
  517     statements.forEach((stmt) => stmt.visitStatement(this, ctx));
  518   }
  519 }
  520 
  521 export function escapeIdentifier(
  522     input: string, escapeDollar: boolean, alwaysQuote: boolean = true): any {
  523   if (input == null) {
  524     return null;
  525   }
  526   const body = input.replace(_SINGLE_QUOTE_ESCAPE_STRING_RE, (...match: string[]) => {
  527     if (match[0] == '$') {
  528       return escapeDollar ? '\\$' : '$';
  529     } else if (match[0] == '\n') {
  530       return '\\n';
  531     } else if (match[0] == '\r') {
  532       return '\\r';
  533     } else {
  534       return `\\${match[0]}`;
  535     }
  536   });
  537   const requiresQuotes = alwaysQuote || !_LEGAL_IDENTIFIER_RE.test(body);
  538   return requiresQuotes ? `'${body}'` : body;
  539 }
  540 
  541 function _createIndent(count: number): string {
  542   let res = '';
  543   for (let i = 0; i < count; i++) {
  544     res += _INDENT_WITH;
  545   }
  546   return res;
  547 }