feat(compiler): add support for source map generation (#14258)

fixes #14125

PR Close #14258
This commit is contained in:
Victor Berchet
2017-01-27 14:23:12 -08:00
committed by Miško Hevery
parent 53cf2ec573
commit 7ac38aa357
20 changed files with 1061 additions and 381 deletions
@@ -7,11 +7,14 @@
*/
import {isBlank, isPresent} from '../facade/lang';
import {ParseSourceSpan} from '../parse_util';
import * as o from './output_ast';
import {SourceMapGenerator} from './source_map';
const _SINGLE_QUOTE_ESCAPE_STRING_RE = /'|\\|\n|\r|\$/g;
const _LEGAL_IDENTIFIER_RE = /^[$A-Z_][0-9A-Z_$]*$/i;
const _INDENT_WITH = ' ';
export const CATCH_ERROR_VAR = o.variable('error');
export const CATCH_STACK_VAR = o.variable('stack');
@@ -21,6 +24,7 @@ export abstract class OutputEmitter {
class _EmittedLine {
parts: string[] = [];
srcSpans: ParseSourceSpan[] = [];
constructor(public indent: number) {}
}
@@ -40,13 +44,16 @@ export class EmitterVisitorContext {
isExportedVar(varName: string): boolean { return this._exportedVars.indexOf(varName) !== -1; }
println(lastPart: string = ''): void { this.print(lastPart, true); }
println(from?: {sourceSpan?: ParseSourceSpan}|null, lastPart: string = ''): void {
this.print(from, lastPart, true);
}
lineIsEmpty(): boolean { return this._currentLine.parts.length === 0; }
print(part: string, newLine: boolean = false) {
print(from: {sourceSpan?: ParseSourceSpan}|null, part: string, newLine: boolean = false) {
if (part.length > 0) {
this._currentLine.parts.push(part);
this._currentLine.srcSpans.push(from && from.sourceSpan || null);
}
if (newLine) {
this._lines.push(new _EmittedLine(this._indent));
@@ -77,21 +84,60 @@ export class EmitterVisitorContext {
return this._classes.length > 0 ? this._classes[this._classes.length - 1] : null;
}
toSource(): any {
let lines = this._lines;
if (lines[lines.length - 1].parts.length === 0) {
lines = lines.slice(0, lines.length - 1);
}
return lines
.map((line) => {
if (line.parts.length > 0) {
return _createIndent(line.indent) + line.parts.join('');
} else {
return '';
}
})
toSource(): string {
return this.sourceLines
.map(l => l.parts.length > 0 ? _createIndent(l.indent) + l.parts.join('') : '')
.join('\n');
}
toSourceMapGenerator(file: string|null = null, startsAtLine: number = 0): SourceMapGenerator {
const map = new SourceMapGenerator(file);
for (let i = 0; i < startsAtLine; i++) {
map.addLine();
}
this.sourceLines.forEach(line => {
map.addLine();
const spans = line.srcSpans;
const parts = line.parts;
let col0 = line.indent * _INDENT_WITH.length;
let spanIdx = 0;
// skip leading parts without source spans
while (spanIdx < spans.length && !spans[spanIdx]) {
col0 += parts[spanIdx].length;
spanIdx++;
}
while (spanIdx < spans.length) {
const span = spans[spanIdx];
const source = span.start.file;
const sourceLine = span.start.line;
const sourceCol = span.start.col;
map.addSource(source.url, source.content)
.addMapping(col0, source.url, sourceLine, sourceCol);
col0 += parts[spanIdx].length;
spanIdx++;
// assign parts without span or the same span to the previous segment
while (spanIdx < spans.length && (span === spans[spanIdx] || !spans[spanIdx])) {
col0 += parts[spanIdx].length;
spanIdx++;
}
}
});
return map;
}
private get sourceLines(): _EmittedLine[] {
if (this._lines.length && this._lines[this._lines.length - 1].parts.length === 0) {
return this._lines.slice(0, -1);
}
return this._lines;
}
}
export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.ExpressionVisitor {
@@ -99,14 +145,14 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
visitExpressionStmt(stmt: o.ExpressionStatement, ctx: EmitterVisitorContext): any {
stmt.expr.visitExpression(this, ctx);
ctx.println(';');
ctx.println(stmt, ';');
return null;
}
visitReturnStmt(stmt: o.ReturnStatement, ctx: EmitterVisitorContext): any {
ctx.print(`return `);
ctx.print(stmt, `return `);
stmt.value.visitExpression(this, ctx);
ctx.println(';');
ctx.println(stmt, ';');
return null;
}
@@ -115,82 +161,83 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
abstract visitDeclareClassStmt(stmt: o.ClassStmt, ctx: EmitterVisitorContext): any;
visitIfStmt(stmt: o.IfStmt, ctx: EmitterVisitorContext): any {
ctx.print(`if (`);
ctx.print(stmt, `if (`);
stmt.condition.visitExpression(this, ctx);
ctx.print(`) {`);
ctx.print(stmt, `) {`);
const hasElseCase = isPresent(stmt.falseCase) && stmt.falseCase.length > 0;
if (stmt.trueCase.length <= 1 && !hasElseCase) {
ctx.print(` `);
ctx.print(stmt, ` `);
this.visitAllStatements(stmt.trueCase, ctx);
ctx.removeEmptyLastLine();
ctx.print(` `);
ctx.print(stmt, ` `);
} else {
ctx.println();
ctx.incIndent();
this.visitAllStatements(stmt.trueCase, ctx);
ctx.decIndent();
if (hasElseCase) {
ctx.println(`} else {`);
ctx.println(stmt, `} else {`);
ctx.incIndent();
this.visitAllStatements(stmt.falseCase, ctx);
ctx.decIndent();
}
}
ctx.println(`}`);
ctx.println(stmt, `}`);
return null;
}
abstract visitTryCatchStmt(stmt: o.TryCatchStmt, ctx: EmitterVisitorContext): any;
visitThrowStmt(stmt: o.ThrowStmt, ctx: EmitterVisitorContext): any {
ctx.print(`throw `);
ctx.print(stmt, `throw `);
stmt.error.visitExpression(this, ctx);
ctx.println(`;`);
ctx.println(stmt, `;`);
return null;
}
visitCommentStmt(stmt: o.CommentStmt, ctx: EmitterVisitorContext): any {
const lines = stmt.comment.split('\n');
lines.forEach((line) => { ctx.println(`// ${line}`); });
lines.forEach((line) => { ctx.println(stmt, `// ${line}`); });
return null;
}
abstract visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any;
visitWriteVarExpr(expr: o.WriteVarExpr, ctx: EmitterVisitorContext): any {
const lineWasEmpty = ctx.lineIsEmpty();
if (!lineWasEmpty) {
ctx.print('(');
ctx.print(expr, '(');
}
ctx.print(`${expr.name} = `);
ctx.print(expr, `${expr.name} = `);
expr.value.visitExpression(this, ctx);
if (!lineWasEmpty) {
ctx.print(')');
ctx.print(expr, ')');
}
return null;
}
visitWriteKeyExpr(expr: o.WriteKeyExpr, ctx: EmitterVisitorContext): any {
const lineWasEmpty = ctx.lineIsEmpty();
if (!lineWasEmpty) {
ctx.print('(');
ctx.print(expr, '(');
}
expr.receiver.visitExpression(this, ctx);
ctx.print(`[`);
ctx.print(expr, `[`);
expr.index.visitExpression(this, ctx);
ctx.print(`] = `);
ctx.print(expr, `] = `);
expr.value.visitExpression(this, ctx);
if (!lineWasEmpty) {
ctx.print(')');
ctx.print(expr, ')');
}
return null;
}
visitWritePropExpr(expr: o.WritePropExpr, ctx: EmitterVisitorContext): any {
const lineWasEmpty = ctx.lineIsEmpty();
if (!lineWasEmpty) {
ctx.print('(');
ctx.print(expr, '(');
}
expr.receiver.visitExpression(this, ctx);
ctx.print(`.${expr.name} = `);
ctx.print(expr, `.${expr.name} = `);
expr.value.visitExpression(this, ctx);
if (!lineWasEmpty) {
ctx.print(')');
ctx.print(expr, ')');
}
return null;
}
@@ -204,9 +251,9 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
return null;
}
}
ctx.print(`.${name}(`);
ctx.print(expr, `.${name}(`);
this.visitAllExpressions(expr.args, ctx, `,`);
ctx.print(`)`);
ctx.print(expr, `)`);
return null;
}
@@ -214,9 +261,9 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
visitInvokeFunctionExpr(expr: o.InvokeFunctionExpr, ctx: EmitterVisitorContext): any {
expr.fn.visitExpression(this, ctx);
ctx.print(`(`);
ctx.print(expr, `(`);
this.visitAllExpressions(expr.args, ctx, ',');
ctx.print(`)`);
ctx.print(expr, `)`);
return null;
}
visitReadVarExpr(ast: o.ReadVarExpr, ctx: EmitterVisitorContext): any {
@@ -239,24 +286,24 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
throw new Error(`Unknown builtin variable ${ast.builtin}`);
}
}
ctx.print(varName);
ctx.print(ast, varName);
return null;
}
visitInstantiateExpr(ast: o.InstantiateExpr, ctx: EmitterVisitorContext): any {
ctx.print(`new `);
ctx.print(ast, `new `);
ast.classExpr.visitExpression(this, ctx);
ctx.print(`(`);
ctx.print(ast, `(`);
this.visitAllExpressions(ast.args, ctx, ',');
ctx.print(`)`);
ctx.print(ast, `)`);
return null;
}
visitLiteralExpr(ast: o.LiteralExpr, ctx: EmitterVisitorContext): any {
const value = ast.value;
if (typeof value === 'string') {
ctx.print(escapeIdentifier(value, this._escapeDollarInStrings));
ctx.print(ast, escapeIdentifier(value, this._escapeDollarInStrings));
} else {
ctx.print(`${value}`);
ctx.print(ast, `${value}`);
}
return null;
}
@@ -264,17 +311,17 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
abstract visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any;
visitConditionalExpr(ast: o.ConditionalExpr, ctx: EmitterVisitorContext): any {
ctx.print(`(`);
ctx.print(ast, `(`);
ast.condition.visitExpression(this, ctx);
ctx.print('? ');
ctx.print(ast, '? ');
ast.trueCase.visitExpression(this, ctx);
ctx.print(': ');
ctx.print(ast, ': ');
ast.falseCase.visitExpression(this, ctx);
ctx.print(`)`);
ctx.print(ast, `)`);
return null;
}
visitNotExpr(ast: o.NotExpr, ctx: EmitterVisitorContext): any {
ctx.print('!');
ctx.print(ast, '!');
ast.condition.visitExpression(this, ctx);
return null;
}
@@ -332,46 +379,46 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
default:
throw new Error(`Unknown operator ${ast.operator}`);
}
ctx.print(`(`);
ctx.print(ast, `(`);
ast.lhs.visitExpression(this, ctx);
ctx.print(` ${opStr} `);
ctx.print(ast, ` ${opStr} `);
ast.rhs.visitExpression(this, ctx);
ctx.print(`)`);
ctx.print(ast, `)`);
return null;
}
visitReadPropExpr(ast: o.ReadPropExpr, ctx: EmitterVisitorContext): any {
ast.receiver.visitExpression(this, ctx);
ctx.print(`.`);
ctx.print(ast.name);
ctx.print(ast, `.`);
ctx.print(ast, ast.name);
return null;
}
visitReadKeyExpr(ast: o.ReadKeyExpr, ctx: EmitterVisitorContext): any {
ast.receiver.visitExpression(this, ctx);
ctx.print(`[`);
ctx.print(ast, `[`);
ast.index.visitExpression(this, ctx);
ctx.print(`]`);
ctx.print(ast, `]`);
return null;
}
visitLiteralArrayExpr(ast: o.LiteralArrayExpr, ctx: EmitterVisitorContext): any {
const useNewLine = ast.entries.length > 1;
ctx.print(`[`, useNewLine);
ctx.print(ast, `[`, useNewLine);
ctx.incIndent();
this.visitAllExpressions(ast.entries, ctx, ',', useNewLine);
ctx.decIndent();
ctx.print(`]`, useNewLine);
ctx.print(ast, `]`, useNewLine);
return null;
}
visitLiteralMapExpr(ast: o.LiteralMapExpr, ctx: EmitterVisitorContext): any {
const useNewLine = ast.entries.length > 1;
ctx.print(`{`, useNewLine);
ctx.print(ast, `{`, useNewLine);
ctx.incIndent();
this.visitAllObjects(entry => {
ctx.print(`${escapeIdentifier(entry.key, this._escapeDollarInStrings, entry.quoted)}: `);
ctx.print(ast, `${escapeIdentifier(entry.key, this._escapeDollarInStrings, entry.quoted)}: `);
entry.value.visitExpression(this, ctx);
}, ast.entries, ctx, ',', useNewLine);
ctx.decIndent();
ctx.print(`}`, useNewLine);
ctx.print(ast, `}`, useNewLine);
return null;
}
@@ -387,7 +434,7 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
newLine: boolean = false): void {
for (let i = 0; i < expressions.length; i++) {
if (i > 0) {
ctx.print(separator, newLine);
ctx.print(null, separator, newLine);
}
handler(expressions[i]);
}
@@ -424,7 +471,7 @@ export function escapeIdentifier(
function _createIndent(count: number): string {
let res = '';
for (let i = 0; i < count; i++) {
res += ' ';
res += _INDENT_WITH;
}
return res;
}
@@ -18,9 +18,9 @@ export abstract class AbstractJsEmitterVisitor extends AbstractEmitterVisitor {
this._visitClassConstructor(stmt, ctx);
if (isPresent(stmt.parent)) {
ctx.print(`${stmt.name}.prototype = Object.create(`);
ctx.print(stmt, `${stmt.name}.prototype = Object.create(`);
stmt.parent.visitExpression(this, ctx);
ctx.println(`.prototype);`);
ctx.println(stmt, `.prototype);`);
}
stmt.getters.forEach((getter) => this._visitClassGetter(stmt, getter, ctx));
stmt.methods.forEach((method) => this._visitClassMethod(stmt, method, ctx));
@@ -29,50 +29,51 @@ export abstract class AbstractJsEmitterVisitor extends AbstractEmitterVisitor {
}
private _visitClassConstructor(stmt: o.ClassStmt, ctx: EmitterVisitorContext) {
ctx.print(`function ${stmt.name}(`);
ctx.print(stmt, `function ${stmt.name}(`);
if (isPresent(stmt.constructorMethod)) {
this._visitParams(stmt.constructorMethod.params, ctx);
}
ctx.println(`) {`);
ctx.println(stmt, `) {`);
ctx.incIndent();
if (isPresent(stmt.constructorMethod)) {
if (stmt.constructorMethod.body.length > 0) {
ctx.println(`var self = this;`);
ctx.println(stmt, `var self = this;`);
this.visitAllStatements(stmt.constructorMethod.body, ctx);
}
}
ctx.decIndent();
ctx.println(`}`);
ctx.println(stmt, `}`);
}
private _visitClassGetter(stmt: o.ClassStmt, getter: o.ClassGetter, ctx: EmitterVisitorContext) {
ctx.println(
stmt,
`Object.defineProperty(${stmt.name}.prototype, '${getter.name}', { get: function() {`);
ctx.incIndent();
if (getter.body.length > 0) {
ctx.println(`var self = this;`);
ctx.println(stmt, `var self = this;`);
this.visitAllStatements(getter.body, ctx);
}
ctx.decIndent();
ctx.println(`}});`);
ctx.println(stmt, `}});`);
}
private _visitClassMethod(stmt: o.ClassStmt, method: o.ClassMethod, ctx: EmitterVisitorContext) {
ctx.print(`${stmt.name}.prototype.${method.name} = function(`);
ctx.print(stmt, `${stmt.name}.prototype.${method.name} = function(`);
this._visitParams(method.params, ctx);
ctx.println(`) {`);
ctx.println(stmt, `) {`);
ctx.incIndent();
if (method.body.length > 0) {
ctx.println(`var self = this;`);
ctx.println(stmt, `var self = this;`);
this.visitAllStatements(method.body, ctx);
}
ctx.decIndent();
ctx.println(`};`);
ctx.println(stmt, `};`);
}
visitReadVarExpr(ast: o.ReadVarExpr, ctx: EmitterVisitorContext): string {
if (ast.builtin === o.BuiltinVar.This) {
ctx.print('self');
ctx.print(ast, 'self');
} else if (ast.builtin === o.BuiltinVar.Super) {
throw new Error(
`'super' needs to be handled at a parent ast node, not at the variable level!`);
@@ -82,9 +83,9 @@ export abstract class AbstractJsEmitterVisitor extends AbstractEmitterVisitor {
return null;
}
visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any {
ctx.print(`var ${stmt.name} = `);
ctx.print(stmt, `var ${stmt.name} = `);
stmt.value.visitExpression(this, ctx);
ctx.println(`;`);
ctx.println(stmt, `;`);
return null;
}
visitCastExpr(ast: o.CastExpr, ctx: EmitterVisitorContext): any {
@@ -95,43 +96,43 @@ export abstract class AbstractJsEmitterVisitor extends AbstractEmitterVisitor {
const fnExpr = expr.fn;
if (fnExpr instanceof o.ReadVarExpr && fnExpr.builtin === o.BuiltinVar.Super) {
ctx.currentClass.parent.visitExpression(this, ctx);
ctx.print(`.call(this`);
ctx.print(expr, `.call(this`);
if (expr.args.length > 0) {
ctx.print(`, `);
ctx.print(expr, `, `);
this.visitAllExpressions(expr.args, ctx, ',');
}
ctx.print(`)`);
ctx.print(expr, `)`);
} else {
super.visitInvokeFunctionExpr(expr, ctx);
}
return null;
}
visitFunctionExpr(ast: o.FunctionExpr, ctx: EmitterVisitorContext): any {
ctx.print(`function(`);
ctx.print(ast, `function(`);
this._visitParams(ast.params, ctx);
ctx.println(`) {`);
ctx.println(ast, `) {`);
ctx.incIndent();
this.visitAllStatements(ast.statements, ctx);
ctx.decIndent();
ctx.print(`}`);
ctx.print(ast, `}`);
return null;
}
visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, ctx: EmitterVisitorContext): any {
ctx.print(`function ${stmt.name}(`);
ctx.print(stmt, `function ${stmt.name}(`);
this._visitParams(stmt.params, ctx);
ctx.println(`) {`);
ctx.println(stmt, `) {`);
ctx.incIndent();
this.visitAllStatements(stmt.statements, ctx);
ctx.decIndent();
ctx.println(`}`);
ctx.println(stmt, `}`);
return null;
}
visitTryCatchStmt(stmt: o.TryCatchStmt, ctx: EmitterVisitorContext): any {
ctx.println(`try {`);
ctx.println(stmt, `try {`);
ctx.incIndent();
this.visitAllStatements(stmt.bodyStmts, ctx);
ctx.decIndent();
ctx.println(`} catch (${CATCH_ERROR_VAR.name}) {`);
ctx.println(stmt, `} catch (${CATCH_ERROR_VAR.name}) {`);
ctx.incIndent();
const catchStmts =
[<o.Statement>CATCH_STACK_VAR.set(CATCH_ERROR_VAR.prop('stack')).toDeclStmt(null, [
@@ -139,12 +140,12 @@ export abstract class AbstractJsEmitterVisitor extends AbstractEmitterVisitor {
])].concat(stmt.catchStmts);
this.visitAllStatements(catchStmts, ctx);
ctx.decIndent();
ctx.println(`}`);
ctx.println(stmt, `}`);
return null;
}
private _visitParams(params: o.FnParam[], ctx: EmitterVisitorContext): void {
this.visitAllObjects(param => ctx.print(param.name), params, ctx, ',');
this.visitAllObjects(param => ctx.print(null, param.name), params, ctx, ',');
}
getBuiltinMethodName(method: o.BuiltinMethod): string {
@@ -6,8 +6,11 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ParseSourceSpan} from '../parse_util';
import * as o from './output_ast';
/**
* Create a new class stmts based on the given data.
*/
@@ -16,7 +19,9 @@ export function createClassStmt(config: {
parent?: o.Expression,
parentArgs?: o.Expression[],
ctorParams?: o.FnParam[],
builders: ClassBuilderPart | ClassBuilderPart[], modifiers?: o.StmtModifier[]
builders: ClassBuilderPart | ClassBuilderPart[],
modifiers?: o.StmtModifier[],
sourceSpan?: ParseSourceSpan
}): o.ClassStmt {
const parentArgs = config.parentArgs || [];
const superCtorStmts = config.parent ? [o.SUPER_EXPR.callFn(parentArgs).toStmt()] : [];
@@ -27,7 +32,7 @@ export function createClassStmt(config: {
return new o.ClassStmt(
config.name, config.parent, builder.fields, builder.getters, ctor, builder.methods,
config.modifiers || []);
config.modifiers || [], config.sourceSpan);
}
function concatClassBuilderParts(builders: ClassBuilderPart[]) {
@@ -18,10 +18,12 @@ import {ImportResolver} from './path_util';
export class JavaScriptEmitter implements OutputEmitter {
constructor(private _importResolver: ImportResolver) {}
emitStatements(genFilePath: string, stmts: o.Statement[], exportedVars: string[]): string {
const converter = new JsEmitterVisitor(genFilePath, this._importResolver);
const ctx = EmitterVisitorContext.createRoot(exportedVars);
converter.visitAllStatements(stmts, ctx);
const srcParts: string[] = [];
converter.importsWithPrefixes.forEach((prefix, importedFilePath) => {
// Note: can't write the real word for import as it screws up system.js auto detection...
@@ -29,7 +31,15 @@ export class JavaScriptEmitter implements OutputEmitter {
`var ${prefix} = req` +
`uire('${this._importResolver.fileNameToModuleName(importedFilePath, genFilePath)}');`);
});
srcParts.push(ctx.toSource());
const prefixLines = converter.importsWithPrefixes.size;
const sm = ctx.toSourceMapGenerator(null, prefixLines).toJsComment();
if (sm) {
srcParts.push(sm);
}
return srcParts.join('\n');
}
}
@@ -55,29 +65,29 @@ class JsEmitterVisitor extends AbstractJsEmitterVisitor {
prefix = `import${this.importsWithPrefixes.size}`;
this.importsWithPrefixes.set(filePath, prefix);
}
ctx.print(`${prefix}.`);
ctx.print(ast, `${prefix}.`);
}
ctx.print(name);
ctx.print(ast, name);
return null;
}
visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any {
super.visitDeclareVarStmt(stmt, ctx);
if (ctx.isExportedVar(stmt.name)) {
ctx.println(exportVar(stmt.name));
ctx.println(stmt, exportVar(stmt.name));
}
return null;
}
visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, ctx: EmitterVisitorContext): any {
super.visitDeclareFunctionStmt(stmt, ctx);
if (ctx.isExportedVar(stmt.name)) {
ctx.println(exportVar(stmt.name));
ctx.println(stmt, exportVar(stmt.name));
}
return null;
}
visitDeclareClassStmt(stmt: o.ClassStmt, ctx: EmitterVisitorContext): any {
super.visitDeclareClassStmt(stmt, ctx);
if (ctx.isExportedVar(stmt.name)) {
ctx.println(exportVar(stmt.name));
ctx.println(stmt, exportVar(stmt.name));
}
return null;
}
+210 -130
View File
@@ -9,6 +9,7 @@
import {CompileIdentifierMetadata} from '../compile_metadata';
import {isPresent} from '../facade/lang';
import {ParseSourceSpan} from '../parse_util';
//// Types
export enum TypeModifier {
@@ -101,81 +102,91 @@ export enum BinaryOperator {
export abstract class Expression {
constructor(public type: Type) {}
constructor(public type: Type, public sourceSpan?: ParseSourceSpan) {}
abstract visitExpression(visitor: ExpressionVisitor, context: any): any;
prop(name: string): ReadPropExpr { return new ReadPropExpr(this, name); }
key(index: Expression, type: Type = null): ReadKeyExpr {
return new ReadKeyExpr(this, index, type);
prop(name: string, sourceSpan?: ParseSourceSpan): ReadPropExpr {
return new ReadPropExpr(this, name, null, sourceSpan);
}
callMethod(name: string|BuiltinMethod, params: Expression[]): InvokeMethodExpr {
return new InvokeMethodExpr(this, name, params);
key(index: Expression, type: Type = null, sourceSpan?: ParseSourceSpan): ReadKeyExpr {
return new ReadKeyExpr(this, index, type, sourceSpan);
}
callFn(params: Expression[]): InvokeFunctionExpr { return new InvokeFunctionExpr(this, params); }
instantiate(params: Expression[], type: Type = null): InstantiateExpr {
return new InstantiateExpr(this, params, type);
callMethod(name: string|BuiltinMethod, params: Expression[], sourceSpan?: ParseSourceSpan):
InvokeMethodExpr {
return new InvokeMethodExpr(this, name, params, null, sourceSpan);
}
conditional(trueCase: Expression, falseCase: Expression = null): ConditionalExpr {
return new ConditionalExpr(this, trueCase, falseCase);
callFn(params: Expression[], sourceSpan?: ParseSourceSpan): InvokeFunctionExpr {
return new InvokeFunctionExpr(this, params, null, sourceSpan);
}
equals(rhs: Expression): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Equals, this, rhs);
instantiate(params: Expression[], type: Type = null, sourceSpan?: ParseSourceSpan):
InstantiateExpr {
return new InstantiateExpr(this, params, type, sourceSpan);
}
notEquals(rhs: Expression): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.NotEquals, this, rhs);
conditional(trueCase: Expression, falseCase: Expression = null, sourceSpan?: ParseSourceSpan):
ConditionalExpr {
return new ConditionalExpr(this, trueCase, falseCase, null, sourceSpan);
}
identical(rhs: Expression): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Identical, this, rhs);
equals(rhs: Expression, sourceSpan?: ParseSourceSpan): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Equals, this, rhs, null, sourceSpan);
}
notIdentical(rhs: Expression): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.NotIdentical, this, rhs);
notEquals(rhs: Expression, sourceSpan?: ParseSourceSpan): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.NotEquals, this, rhs, null, sourceSpan);
}
minus(rhs: Expression): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Minus, this, rhs);
identical(rhs: Expression, sourceSpan?: ParseSourceSpan): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Identical, this, rhs, null, sourceSpan);
}
plus(rhs: Expression): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Plus, this, rhs);
notIdentical(rhs: Expression, sourceSpan?: ParseSourceSpan): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.NotIdentical, this, rhs, null, sourceSpan);
}
divide(rhs: Expression): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Divide, this, rhs);
minus(rhs: Expression, sourceSpan?: ParseSourceSpan): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Minus, this, rhs, null, sourceSpan);
}
multiply(rhs: Expression): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Multiply, this, rhs);
plus(rhs: Expression, sourceSpan?: ParseSourceSpan): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Plus, this, rhs, null, sourceSpan);
}
modulo(rhs: Expression): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Modulo, this, rhs);
divide(rhs: Expression, sourceSpan?: ParseSourceSpan): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Divide, this, rhs, null, sourceSpan);
}
and(rhs: Expression): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.And, this, rhs);
multiply(rhs: Expression, sourceSpan?: ParseSourceSpan): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Multiply, this, rhs, null, sourceSpan);
}
or(rhs: Expression): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Or, this, rhs);
modulo(rhs: Expression, sourceSpan?: ParseSourceSpan): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Modulo, this, rhs, null, sourceSpan);
}
lower(rhs: Expression): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Lower, this, rhs);
and(rhs: Expression, sourceSpan?: ParseSourceSpan): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.And, this, rhs, null, sourceSpan);
}
lowerEquals(rhs: Expression): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.LowerEquals, this, rhs);
or(rhs: Expression, sourceSpan?: ParseSourceSpan): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Or, this, rhs, null, sourceSpan);
}
bigger(rhs: Expression): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Bigger, this, rhs);
lower(rhs: Expression, sourceSpan?: ParseSourceSpan): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Lower, this, rhs, null, sourceSpan);
}
biggerEquals(rhs: Expression): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.BiggerEquals, this, rhs);
lowerEquals(rhs: Expression, sourceSpan?: ParseSourceSpan): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.LowerEquals, this, rhs, null, sourceSpan);
}
isBlank(): Expression {
bigger(rhs: Expression, sourceSpan?: ParseSourceSpan): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Bigger, this, rhs, null, sourceSpan);
}
biggerEquals(rhs: Expression, sourceSpan?: ParseSourceSpan): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.BiggerEquals, this, rhs, null, sourceSpan);
}
isBlank(sourceSpan?: ParseSourceSpan): Expression {
// 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);
return this.equals(TYPED_NULL_EXPR, sourceSpan);
}
cast(type: Type): Expression { return new CastExpr(this, type); }
cast(type: Type, sourceSpan?: ParseSourceSpan): Expression {
return new CastExpr(this, type, sourceSpan);
}
toStmt(): Statement { return new ExpressionStatement(this); }
}
@@ -190,8 +201,8 @@ export class ReadVarExpr extends Expression {
public name: string;
public builtin: BuiltinVar;
constructor(name: string|BuiltinVar, type: Type = null) {
super(type);
constructor(name: string|BuiltinVar, type: Type = null, sourceSpan?: ParseSourceSpan) {
super(type, sourceSpan);
if (typeof name === 'string') {
this.name = name;
this.builtin = null;
@@ -204,14 +215,17 @@ export class ReadVarExpr extends Expression {
return visitor.visitReadVarExpr(this, context);
}
set(value: Expression): WriteVarExpr { return new WriteVarExpr(this.name, value); }
set(value: Expression): WriteVarExpr {
return new WriteVarExpr(this.name, value, null, this.sourceSpan);
}
}
export class WriteVarExpr extends Expression {
public value: Expression;
constructor(public name: string, value: Expression, type: Type = null) {
super(type || value.type);
constructor(
public name: string, value: Expression, type: Type = null, sourceSpan?: ParseSourceSpan) {
super(type || value.type, sourceSpan);
this.value = value;
}
@@ -220,7 +234,7 @@ export class WriteVarExpr extends Expression {
}
toDeclStmt(type: Type = null, modifiers: StmtModifier[] = null): DeclareVarStmt {
return new DeclareVarStmt(this.name, this.value, type, modifiers);
return new DeclareVarStmt(this.name, this.value, type, modifiers, this.sourceSpan);
}
}
@@ -228,8 +242,9 @@ export class WriteVarExpr extends Expression {
export class WriteKeyExpr extends Expression {
public value: Expression;
constructor(
public receiver: Expression, public index: Expression, value: Expression, type: Type = null) {
super(type || value.type);
public receiver: Expression, public index: Expression, value: Expression, type: Type = null,
sourceSpan?: ParseSourceSpan) {
super(type || value.type, sourceSpan);
this.value = value;
}
visitExpression(visitor: ExpressionVisitor, context: any): any {
@@ -241,8 +256,9 @@ export class WriteKeyExpr extends Expression {
export class WritePropExpr extends Expression {
public value: Expression;
constructor(
public receiver: Expression, public name: string, value: Expression, type: Type = null) {
super(type || value.type);
public receiver: Expression, public name: string, value: Expression, type: Type = null,
sourceSpan?: ParseSourceSpan) {
super(type || value.type, sourceSpan);
this.value = value;
}
visitExpression(visitor: ExpressionVisitor, context: any): any {
@@ -261,8 +277,8 @@ export class InvokeMethodExpr extends Expression {
public builtin: BuiltinMethod;
constructor(
public receiver: Expression, method: string|BuiltinMethod, public args: Expression[],
type: Type = null) {
super(type);
type: Type = null, sourceSpan?: ParseSourceSpan) {
super(type, sourceSpan);
if (typeof method === 'string') {
this.name = method;
this.builtin = null;
@@ -278,7 +294,11 @@ export class InvokeMethodExpr extends Expression {
export class InvokeFunctionExpr extends Expression {
constructor(public fn: Expression, public args: Expression[], type: Type = null) { super(type); }
constructor(
public fn: Expression, public args: Expression[], type: Type = null,
sourceSpan?: ParseSourceSpan) {
super(type, sourceSpan);
}
visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitInvokeFunctionExpr(this, context);
}
@@ -286,7 +306,11 @@ export class InvokeFunctionExpr extends Expression {
export class InstantiateExpr extends Expression {
constructor(public classExpr: Expression, public args: Expression[], type?: Type) { super(type); }
constructor(
public classExpr: Expression, public args: Expression[], type?: Type,
sourceSpan?: ParseSourceSpan) {
super(type, sourceSpan);
}
visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitInstantiateExpr(this, context);
}
@@ -294,7 +318,9 @@ export class InstantiateExpr extends Expression {
export class LiteralExpr extends Expression {
constructor(public value: any, type: Type = null) { super(type); }
constructor(public value: any, type: Type = null, sourceSpan?: ParseSourceSpan) {
super(type, sourceSpan);
}
visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitLiteralExpr(this, context);
}
@@ -303,9 +329,9 @@ export class LiteralExpr extends Expression {
export class ExternalExpr extends Expression {
constructor(
public value: CompileIdentifierMetadata, type: Type = null,
public typeParams: Type[] = null) {
super(type);
public value: CompileIdentifierMetadata, type: Type = null, public typeParams: Type[] = null,
sourceSpan?: ParseSourceSpan) {
super(type, sourceSpan);
}
visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitExternalExpr(this, context);
@@ -317,8 +343,8 @@ export class ConditionalExpr extends Expression {
public trueCase: Expression;
constructor(
public condition: Expression, trueCase: Expression, public falseCase: Expression = null,
type: Type = null) {
super(type || trueCase.type);
type: Type = null, sourceSpan?: ParseSourceSpan) {
super(type || trueCase.type, sourceSpan);
this.trueCase = trueCase;
}
visitExpression(visitor: ExpressionVisitor, context: any): any {
@@ -328,14 +354,18 @@ export class ConditionalExpr extends Expression {
export class NotExpr extends Expression {
constructor(public condition: Expression) { super(BOOL_TYPE); }
constructor(public condition: Expression, sourceSpan?: ParseSourceSpan) {
super(BOOL_TYPE, sourceSpan);
}
visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitNotExpr(this, context);
}
}
export class CastExpr extends Expression {
constructor(public value: Expression, type: Type) { super(type); }
constructor(public value: Expression, type: Type, sourceSpan?: ParseSourceSpan) {
super(type, sourceSpan);
}
visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitCastExpr(this, context);
}
@@ -348,15 +378,18 @@ export class FnParam {
export class FunctionExpr extends Expression {
constructor(public params: FnParam[], public statements: Statement[], type: Type = null) {
super(type);
constructor(
public params: FnParam[], public statements: Statement[], type: Type = null,
sourceSpan?: ParseSourceSpan) {
super(type, sourceSpan);
}
visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitFunctionExpr(this, context);
}
toDeclStmt(name: string, modifiers: StmtModifier[] = null): DeclareFunctionStmt {
return new DeclareFunctionStmt(name, this.params, this.statements, this.type, modifiers);
return new DeclareFunctionStmt(
name, this.params, this.statements, this.type, modifiers, this.sourceSpan);
}
}
@@ -364,8 +397,9 @@ export class FunctionExpr extends Expression {
export class BinaryOperatorExpr extends Expression {
public lhs: Expression;
constructor(
public operator: BinaryOperator, lhs: Expression, public rhs: Expression, type: Type = null) {
super(type || lhs.type);
public operator: BinaryOperator, lhs: Expression, public rhs: Expression, type: Type = null,
sourceSpan?: ParseSourceSpan) {
super(type || lhs.type, sourceSpan);
this.lhs = lhs;
}
visitExpression(visitor: ExpressionVisitor, context: any): any {
@@ -375,33 +409,39 @@ export class BinaryOperatorExpr extends Expression {
export class ReadPropExpr extends Expression {
constructor(public receiver: Expression, public name: string, type: Type = null) { super(type); }
constructor(
public receiver: Expression, public name: string, type: Type = null,
sourceSpan?: ParseSourceSpan) {
super(type, sourceSpan);
}
visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitReadPropExpr(this, context);
}
set(value: Expression): WritePropExpr {
return new WritePropExpr(this.receiver, this.name, value);
return new WritePropExpr(this.receiver, this.name, value, null, this.sourceSpan);
}
}
export class ReadKeyExpr extends Expression {
constructor(public receiver: Expression, public index: Expression, type: Type = null) {
super(type);
constructor(
public receiver: Expression, public index: Expression, type: Type = null,
sourceSpan?: ParseSourceSpan) {
super(type, sourceSpan);
}
visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitReadKeyExpr(this, context);
}
set(value: Expression): WriteKeyExpr {
return new WriteKeyExpr(this.receiver, this.index, value);
return new WriteKeyExpr(this.receiver, this.index, value, null, this.sourceSpan);
}
}
export class LiteralArrayExpr extends Expression {
public entries: Expression[];
constructor(entries: Expression[], type: Type = null) {
super(type);
constructor(entries: Expression[], type: Type = null, sourceSpan?: ParseSourceSpan) {
super(type, sourceSpan);
this.entries = entries;
}
visitExpression(visitor: ExpressionVisitor, context: any): any {
@@ -415,9 +455,10 @@ export class LiteralMapEntry {
export class LiteralMapExpr extends Expression {
public valueType: Type = null;
constructor(public entries: LiteralMapEntry[], type: MapType = null) {
super(type);
if (isPresent(type)) {
constructor(
public entries: LiteralMapEntry[], type: MapType = null, sourceSpan?: ParseSourceSpan) {
super(type, sourceSpan);
if (type) {
this.valueType = type.valueType;
}
}
@@ -461,7 +502,7 @@ export enum StmtModifier {
}
export abstract class Statement {
constructor(public modifiers: StmtModifier[] = null) {
constructor(public modifiers: StmtModifier[] = null, public sourceSpan?: ParseSourceSpan) {
if (!modifiers) {
this.modifiers = [];
}
@@ -477,8 +518,8 @@ export class DeclareVarStmt extends Statement {
public type: Type;
constructor(
public name: string, public value: Expression, type: Type = null,
modifiers: StmtModifier[] = null) {
super(modifiers);
modifiers: StmtModifier[] = null, sourceSpan?: ParseSourceSpan) {
super(modifiers, sourceSpan);
this.type = type || value.type;
}
@@ -490,8 +531,8 @@ export class DeclareVarStmt extends Statement {
export class DeclareFunctionStmt extends Statement {
constructor(
public name: string, public params: FnParam[], public statements: Statement[],
public type: Type = null, modifiers: StmtModifier[] = null) {
super(modifiers);
public type: Type = null, modifiers: StmtModifier[] = null, sourceSpan?: ParseSourceSpan) {
super(modifiers, sourceSpan);
}
visitStatement(visitor: StatementVisitor, context: any): any {
@@ -500,7 +541,7 @@ export class DeclareFunctionStmt extends Statement {
}
export class ExpressionStatement extends Statement {
constructor(public expr: Expression) { super(); }
constructor(public expr: Expression, sourceSpan?: ParseSourceSpan) { super(null, sourceSpan); }
visitStatement(visitor: StatementVisitor, context: any): any {
return visitor.visitExpressionStmt(this, context);
@@ -509,7 +550,7 @@ export class ExpressionStatement extends Statement {
export class ReturnStatement extends Statement {
constructor(public value: Expression) { super(); }
constructor(public value: Expression, sourceSpan?: ParseSourceSpan) { super(null, sourceSpan); }
visitStatement(visitor: StatementVisitor, context: any): any {
return visitor.visitReturnStmt(this, context);
}
@@ -553,8 +594,9 @@ export class ClassStmt extends Statement {
constructor(
public name: string, public parent: Expression, public fields: ClassField[],
public getters: ClassGetter[], public constructorMethod: ClassMethod,
public methods: ClassMethod[], modifiers: StmtModifier[] = null) {
super(modifiers);
public methods: ClassMethod[], modifiers: StmtModifier[] = null,
sourceSpan?: ParseSourceSpan) {
super(modifiers, sourceSpan);
}
visitStatement(visitor: StatementVisitor, context: any): any {
return visitor.visitDeclareClassStmt(this, context);
@@ -565,8 +607,8 @@ export class ClassStmt extends Statement {
export class IfStmt extends Statement {
constructor(
public condition: Expression, public trueCase: Statement[],
public falseCase: Statement[] = []) {
super();
public falseCase: Statement[] = [], sourceSpan?: ParseSourceSpan) {
super(null, sourceSpan);
}
visitStatement(visitor: StatementVisitor, context: any): any {
return visitor.visitIfStmt(this, context);
@@ -575,7 +617,7 @@ export class IfStmt extends Statement {
export class CommentStmt extends Statement {
constructor(public comment: string) { super(); }
constructor(public comment: string, sourceSpan?: ParseSourceSpan) { super(null, sourceSpan); }
visitStatement(visitor: StatementVisitor, context: any): any {
return visitor.visitCommentStmt(this, context);
}
@@ -583,7 +625,10 @@ export class CommentStmt extends Statement {
export class TryCatchStmt extends Statement {
constructor(public bodyStmts: Statement[], public catchStmts: Statement[]) { super(); }
constructor(
public bodyStmts: Statement[], public catchStmts: Statement[], sourceSpan?: ParseSourceSpan) {
super(null, sourceSpan);
}
visitStatement(visitor: StatementVisitor, context: any): any {
return visitor.visitTryCatchStmt(this, context);
}
@@ -591,7 +636,7 @@ export class TryCatchStmt extends Statement {
export class ThrowStmt extends Statement {
constructor(public error: Expression) { super(); }
constructor(public error: Expression, sourceSpan?: ParseSourceSpan) { super(null, sourceSpan); }
visitStatement(visitor: StatementVisitor, context: any): any {
return visitor.visitThrowStmt(this, context);
}
@@ -611,74 +656,94 @@ export interface StatementVisitor {
export class ExpressionTransformer implements StatementVisitor, ExpressionVisitor {
visitReadVarExpr(ast: ReadVarExpr, context: any): any { return ast; }
visitWriteVarExpr(expr: WriteVarExpr, context: any): any {
return new WriteVarExpr(expr.name, expr.value.visitExpression(this, context));
return new WriteVarExpr(
expr.name, expr.value.visitExpression(this, context), expr.type, expr.sourceSpan);
}
visitWriteKeyExpr(expr: WriteKeyExpr, context: any): any {
return new WriteKeyExpr(
expr.receiver.visitExpression(this, context), expr.index.visitExpression(this, context),
expr.value.visitExpression(this, context));
expr.value.visitExpression(this, context), expr.type, expr.sourceSpan);
}
visitWritePropExpr(expr: WritePropExpr, context: any): any {
return new WritePropExpr(
expr.receiver.visitExpression(this, context), expr.name,
expr.value.visitExpression(this, context));
expr.value.visitExpression(this, context), expr.type, expr.sourceSpan);
}
visitInvokeMethodExpr(ast: InvokeMethodExpr, context: any): any {
const method = ast.builtin || ast.name;
return new InvokeMethodExpr(
ast.receiver.visitExpression(this, context), method,
this.visitAllExpressions(ast.args, context), ast.type);
this.visitAllExpressions(ast.args, context), ast.type, ast.sourceSpan);
}
visitInvokeFunctionExpr(ast: InvokeFunctionExpr, context: any): any {
return new InvokeFunctionExpr(
ast.fn.visitExpression(this, context), this.visitAllExpressions(ast.args, context),
ast.type);
ast.type, ast.sourceSpan);
}
visitInstantiateExpr(ast: InstantiateExpr, context: any): any {
return new InstantiateExpr(
ast.classExpr.visitExpression(this, context), this.visitAllExpressions(ast.args, context),
ast.type);
ast.type, ast.sourceSpan);
}
visitLiteralExpr(ast: LiteralExpr, context: any): any { return ast; }
visitExternalExpr(ast: ExternalExpr, context: any): any { return ast; }
visitConditionalExpr(ast: ConditionalExpr, context: any): any {
return new ConditionalExpr(
ast.condition.visitExpression(this, context), ast.trueCase.visitExpression(this, context),
ast.falseCase.visitExpression(this, context));
ast.falseCase.visitExpression(this, context), ast.type, ast.sourceSpan);
}
visitNotExpr(ast: NotExpr, context: any): any {
return new NotExpr(ast.condition.visitExpression(this, context));
return new NotExpr(ast.condition.visitExpression(this, context), ast.sourceSpan);
}
visitCastExpr(ast: CastExpr, context: any): any {
return new CastExpr(ast.value.visitExpression(this, context), context);
return new CastExpr(ast.value.visitExpression(this, context), context, ast.sourceSpan);
}
visitFunctionExpr(ast: FunctionExpr, context: any): any {
// Don't descend into nested functions
return ast;
}
visitBinaryOperatorExpr(ast: BinaryOperatorExpr, context: any): any {
return new BinaryOperatorExpr(
ast.operator, ast.lhs.visitExpression(this, context),
ast.rhs.visitExpression(this, context), ast.type);
ast.rhs.visitExpression(this, context), ast.type, ast.sourceSpan);
}
visitReadPropExpr(ast: ReadPropExpr, context: any): any {
return new ReadPropExpr(ast.receiver.visitExpression(this, context), ast.name, ast.type);
return new ReadPropExpr(
ast.receiver.visitExpression(this, context), ast.name, ast.type, ast.sourceSpan);
}
visitReadKeyExpr(ast: ReadKeyExpr, context: any): any {
return new ReadKeyExpr(
ast.receiver.visitExpression(this, context), ast.index.visitExpression(this, context),
ast.type);
ast.type, ast.sourceSpan);
}
visitLiteralArrayExpr(ast: LiteralArrayExpr, context: any): any {
return new LiteralArrayExpr(this.visitAllExpressions(ast.entries, context));
return new LiteralArrayExpr(
this.visitAllExpressions(ast.entries, context), ast.type, ast.sourceSpan);
}
visitLiteralMapExpr(ast: LiteralMapExpr, context: any): any {
const entries = ast.entries.map(
(entry): LiteralMapEntry => new LiteralMapEntry(
entry.key, entry.value.visitExpression(this, context), entry.quoted));
return new LiteralMapExpr(entries);
entry.key, entry.value.visitExpression(this, context), entry.quoted, ));
const mapType = new MapType(ast.valueType);
return new LiteralMapExpr(entries, mapType, ast.sourceSpan);
}
visitAllExpressions(exprs: Expression[], context: any): Expression[] {
return exprs.map(expr => expr.visitExpression(this, context));
@@ -686,37 +751,46 @@ export class ExpressionTransformer implements StatementVisitor, ExpressionVisito
visitDeclareVarStmt(stmt: DeclareVarStmt, context: any): any {
return new DeclareVarStmt(
stmt.name, stmt.value.visitExpression(this, context), stmt.type, stmt.modifiers);
stmt.name, stmt.value.visitExpression(this, context), stmt.type, stmt.modifiers,
stmt.sourceSpan);
}
visitDeclareFunctionStmt(stmt: DeclareFunctionStmt, context: any): any {
// Don't descend into nested functions
return stmt;
}
visitExpressionStmt(stmt: ExpressionStatement, context: any): any {
return new ExpressionStatement(stmt.expr.visitExpression(this, context));
return new ExpressionStatement(stmt.expr.visitExpression(this, context), stmt.sourceSpan);
}
visitReturnStmt(stmt: ReturnStatement, context: any): any {
return new ReturnStatement(stmt.value.visitExpression(this, context));
return new ReturnStatement(stmt.value.visitExpression(this, context), stmt.sourceSpan);
}
visitDeclareClassStmt(stmt: ClassStmt, context: any): any {
// Don't descend into nested functions
return stmt;
}
visitIfStmt(stmt: IfStmt, context: any): any {
return new IfStmt(
stmt.condition.visitExpression(this, context),
this.visitAllStatements(stmt.trueCase, context),
this.visitAllStatements(stmt.falseCase, context));
this.visitAllStatements(stmt.falseCase, context), stmt.sourceSpan);
}
visitTryCatchStmt(stmt: TryCatchStmt, context: any): any {
return new TryCatchStmt(
this.visitAllStatements(stmt.bodyStmts, context),
this.visitAllStatements(stmt.catchStmts, context));
this.visitAllStatements(stmt.catchStmts, context), stmt.sourceSpan);
}
visitThrowStmt(stmt: ThrowStmt, context: any): any {
return new ThrowStmt(stmt.error.visitExpression(this, context));
return new ThrowStmt(stmt.error.visitExpression(this, context), stmt.sourceSpan);
}
visitCommentStmt(stmt: CommentStmt, context: any): any { return stmt; }
visitAllStatements(stmts: Statement[], context: any): Statement[] {
return stmts.map(stmt => stmt.visitStatement(this, context));
}
@@ -866,12 +940,15 @@ class _VariableFinder extends RecursiveExpressionVisitor {
}
}
export function variable(name: string, type: Type = null): ReadVarExpr {
return new ReadVarExpr(name, type);
export function variable(
name: string, type: Type = null, sourceSpan?: ParseSourceSpan): ReadVarExpr {
return new ReadVarExpr(name, type, sourceSpan);
}
export function importExpr(id: CompileIdentifierMetadata, typeParams: Type[] = null): ExternalExpr {
return new ExternalExpr(id, null, typeParams);
export function importExpr(
id: CompileIdentifierMetadata, typeParams: Type[] = null,
sourceSpan?: ParseSourceSpan): ExternalExpr {
return new ExternalExpr(id, null, typeParams, sourceSpan);
}
export function importType(
@@ -885,8 +962,9 @@ export function expressionType(
return isPresent(expr) ? new ExpressionType(expr, typeModifiers) : null;
}
export function literalArr(values: Expression[], type: Type = null): LiteralArrayExpr {
return new LiteralArrayExpr(values, type);
export function literalArr(
values: Expression[], type: Type = null, sourceSpan?: ParseSourceSpan): LiteralArrayExpr {
return new LiteralArrayExpr(values, type, sourceSpan);
}
export function literalMap(
@@ -895,14 +973,16 @@ export function literalMap(
values.map(entry => new LiteralMapEntry(entry[0], entry[1], quoted)), type);
}
export function not(expr: Expression): NotExpr {
return new NotExpr(expr);
export function not(expr: Expression, sourceSpan?: ParseSourceSpan): NotExpr {
return new NotExpr(expr, sourceSpan);
}
export function fn(params: FnParam[], body: Statement[], type: Type = null): FunctionExpr {
return new FunctionExpr(params, body, type);
export function fn(
params: FnParam[], body: Statement[], type: Type = null,
sourceSpan?: ParseSourceSpan): FunctionExpr {
return new FunctionExpr(params, body, type, sourceSpan);
}
export function literal(value: any, type: Type = null): LiteralExpr {
return new LiteralExpr(value, type);
export function literal(value: any, type: Type = null, sourceSpan?: ParseSourceSpan): LiteralExpr {
return new LiteralExpr(value, type, sourceSpan);
}
@@ -28,7 +28,7 @@ function _executeFunctionStatements(
childCtx.vars.set(varNames[i], varValues[i]);
}
const result = visitor.visitAllStatements(statements, childCtx);
return isPresent(result) ? result.value : null;
return result ? result.value : null;
}
class _ExecutionContext {
@@ -7,15 +7,15 @@
*/
import {identifierName} from '../compile_metadata';
import {isPresent} from '../facade/lang';
import {EmitterVisitorContext} from './abstract_emitter';
import {AbstractJsEmitterVisitor} from './abstract_js_emitter';
import * as o from './output_ast';
function evalExpression(
sourceUrl: string, expr: string, declarations: string, vars: {[key: string]: any}): any {
const fnBody = `${declarations}\nreturn ${expr}\n//# sourceURL=${sourceUrl}`;
sourceUrl: string, expr: string, ctx: EmitterVisitorContext, vars: {[key: string]: any}): any {
const fnBody =
`${ctx.toSource()}\nreturn ${expr}\n//# sourceURL=${sourceUrl}\n${ctx.toSourceMapGenerator().toJsComment()}`;
const fnArgNames: string[] = [];
const fnArgValues: any[] = [];
for (const argName in vars) {
@@ -25,13 +25,12 @@ function evalExpression(
return new Function(...fnArgNames.concat(fnBody))(...fnArgValues);
}
export function jitStatements(
sourceUrl: string, statements: o.Statement[], resultVar: string): any {
const converter = new JitEmitterVisitor();
const ctx = EmitterVisitorContext.createRoot([resultVar]);
converter.visitAllStatements(statements, ctx);
return evalExpression(sourceUrl, resultVar, ctx.toSource(), converter.getArgs());
return evalExpression(sourceUrl, resultVar, ctx, converter.getArgs());
}
class JitEmitterVisitor extends AbstractJsEmitterVisitor {
@@ -55,7 +54,7 @@ class JitEmitterVisitor extends AbstractJsEmitterVisitor {
const name = identifierName(ast.value) || 'val';
this._evalArgNames.push(`jit_${name}${id}`);
}
ctx.print(this._evalArgNames[id]);
ctx.print(ast, this._evalArgNames[id]);
return null;
}
}
@@ -0,0 +1,184 @@
/**
* @license
* Copyright Google Inc. 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
*/
// https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit
const VERSION = 3;
const JS_B64_PREFIX = '# sourceMappingURL=data:application/json;base64,';
type Segment = {
col0: number,
sourceUrl?: string,
sourceLine0?: number,
sourceCol0?: number,
};
export type SourceMap = {
version: number,
file?: string,
sourceRoot: string,
sources: string[],
sourcesContent: string[],
mappings: string,
};
export class SourceMapGenerator {
private sourcesContent: Map<string, string> = new Map();
private lines: Segment[][] = [];
private lastCol0: number = 0;
private hasMappings = false;
constructor(private file: string|null = null) {}
// The content is `null` when the content is expected to be loaded using the URL
addSource(url: string, content: string|null = null): this {
if (!this.sourcesContent.has(url)) {
this.sourcesContent.set(url, content);
}
return this;
}
addLine(): this {
this.lines.push([]);
this.lastCol0 = 0;
return this;
}
addMapping(col0: number, sourceUrl?: string, sourceLine0?: number, sourceCol0?: number): this {
if (!this.currentLine) {
throw new Error(`A line must be added before mappings can be added`);
}
if (sourceUrl != null && !this.sourcesContent.has(sourceUrl)) {
throw new Error(`Unknown source file "${sourceUrl}"`);
}
if (col0 == null) {
throw new Error(`The column in the generated code must be provided`);
}
if (col0 < this.lastCol0) {
throw new Error(`Mapping should be added in output order`);
}
if (sourceUrl && (sourceLine0 == null || sourceCol0 == null)) {
throw new Error(`The source location must be provided when a source url is provided`);
}
this.hasMappings = true;
this.lastCol0 = col0;
this.currentLine.push({col0, sourceUrl, sourceLine0, sourceCol0});
return this;
}
private get currentLine(): Segment[]|null { return this.lines.slice(-1)[0]; }
toJSON(): SourceMap|null {
if (!this.hasMappings) {
return null;
}
const sourcesIndex = new Map<string, number>();
const sources: string[] = [];
const sourcesContent: string[] = [];
Array.from(this.sourcesContent.keys()).forEach((url: string, i: number) => {
sourcesIndex.set(url, i);
sources.push(url);
sourcesContent.push(this.sourcesContent.get(url) || null);
});
let mappings: string = '';
let lastCol0: number = 0;
let lastSourceIndex: number = 0;
let lastSourceLine0: number = 0;
let lastSourceCol0: number = 0;
this.lines.forEach(segments => {
lastCol0 = 0;
mappings += segments
.map(segment => {
// zero-based starting column of the line in the generated code
let segAsStr = toBase64VLQ(segment.col0 - lastCol0);
lastCol0 = segment.col0;
if (segment.sourceUrl != null) {
// zero-based index into the “sources” list
segAsStr +=
toBase64VLQ(sourcesIndex.get(segment.sourceUrl) - lastSourceIndex);
lastSourceIndex = sourcesIndex.get(segment.sourceUrl);
// the zero-based starting line in the original source
segAsStr += toBase64VLQ(segment.sourceLine0 - lastSourceLine0);
lastSourceLine0 = segment.sourceLine0;
// the zero-based starting column in the original source
segAsStr += toBase64VLQ(segment.sourceCol0 - lastSourceCol0);
lastSourceCol0 = segment.sourceCol0;
}
return segAsStr;
})
.join(',');
mappings += ';';
});
mappings = mappings.slice(0, -1);
return {
'file': this.file || '',
'version': VERSION,
'sourceRoot': '',
'sources': sources,
'sourcesContent': sourcesContent,
'mappings': mappings,
};
}
toJsComment(): string {
return this.hasMappings ? '//' + JS_B64_PREFIX + toBase64String(JSON.stringify(this, null, 0)) :
'';
}
}
export function toBase64String(value: string): string {
let b64 = '';
for (let i = 0; i < value.length;) {
const i1 = value.charCodeAt(i++);
const i2 = value.charCodeAt(i++);
const i3 = value.charCodeAt(i++);
b64 += toBase64Digit(i1 >> 2);
b64 += toBase64Digit(((i1 & 3) << 4) | (isNaN(i2) ? 0 : i2 >> 4));
b64 += isNaN(i2) ? '=' : toBase64Digit(((i2 & 15) << 2) | (i3 >> 6));
b64 += isNaN(i2) || isNaN(i3) ? '=' : toBase64Digit(i3 & 63);
}
return b64;
}
function toBase64VLQ(value: number): string {
value = value < 0 ? ((-value) << 1) + 1 : value << 1;
let out = '';
do {
let digit = value & 31;
value = value >> 5;
if (value > 0) {
digit = digit | 32;
}
out += toBase64Digit(digit);
} while (value > 0);
return out;
}
const B64_DIGITS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
function toBase64Digit(value: number): string {
if (value < 0 || value >= 64) {
throw new Error(`Can only encode value in the range [0, 63]`);
}
return B64_DIGITS[value];
}
@@ -41,26 +41,41 @@ export function debugOutputAstAsTypeScript(ast: o.Statement | o.Expression | o.T
return ctx.toSource();
}
export class TypeScriptEmitter implements OutputEmitter {
constructor(private _importResolver: ImportResolver) {}
emitStatements(genFilePath: string, stmts: o.Statement[], exportedVars: string[]): string {
const converter = new _TsEmitterVisitor(genFilePath, this._importResolver);
const ctx = EmitterVisitorContext.createRoot(exportedVars);
converter.visitAllStatements(stmts, ctx);
const srcParts: string[] = [];
converter.reexports.forEach((reexports, exportedFilePath) => {
const reexportsCode =
reexports.map(reexport => `${reexport.name} as ${reexport.as}`).join(',');
srcParts.push(
`export {${reexportsCode}} from '${this._importResolver.fileNameToModuleName(exportedFilePath, genFilePath)}';`);
});
converter.importsWithPrefixes.forEach((prefix, importedFilePath) => {
// Note: can't write the real word for import as it screws up system.js auto detection...
srcParts.push(
`imp` +
`ort * as ${prefix} from '${this._importResolver.fileNameToModuleName(importedFilePath, genFilePath)}';`);
});
srcParts.push(ctx.toSource());
const prefixLines = converter.reexports.size + converter.importsWithPrefixes.size;
const sm = ctx.toSourceMapGenerator(null, prefixLines).toJsComment();
if (sm) {
srcParts.push(sm);
}
return srcParts.join('\n');
}
}
@@ -81,14 +96,14 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
t.visitType(this, ctx);
this.typeExpression--;
} else {
ctx.print(defaultType);
ctx.print(null, defaultType);
}
}
visitLiteralExpr(ast: o.LiteralExpr, ctx: EmitterVisitorContext): any {
const value = ast.value;
if (isBlank(value) && ast.type != o.NULL_TYPE) {
ctx.print(`(${value} as any)`);
ctx.print(ast, `(${value} as any)`);
return null;
}
return super.visitLiteralExpr(ast, ctx);
@@ -101,11 +116,11 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
// start with [].concat. see https://github.com/angular/angular/pull/11846
visitLiteralArrayExpr(ast: o.LiteralArrayExpr, ctx: EmitterVisitorContext): any {
if (ast.entries.length === 0) {
ctx.print('(');
ctx.print(ast, '(');
}
const result = super.visitLiteralArrayExpr(ast, ctx);
if (ast.entries.length === 0) {
ctx.print(' as any[])');
ctx.print(ast, ' as any[])');
}
return result;
}
@@ -130,54 +145,54 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
}
}
if (ctx.isExportedVar(stmt.name)) {
ctx.print(`export `);
ctx.print(stmt, `export `);
}
if (stmt.hasModifier(o.StmtModifier.Final)) {
ctx.print(`const`);
ctx.print(stmt, `const`);
} else {
ctx.print(`var`);
ctx.print(stmt, `var`);
}
ctx.print(` ${stmt.name}:`);
ctx.print(stmt, ` ${stmt.name}:`);
this.visitType(stmt.type, ctx);
ctx.print(` = `);
ctx.print(stmt, ` = `);
stmt.value.visitExpression(this, ctx);
ctx.println(`;`);
ctx.println(stmt, `;`);
return null;
}
visitCastExpr(ast: o.CastExpr, ctx: EmitterVisitorContext): any {
ctx.print(`(<`);
ctx.print(ast, `(<`);
ast.type.visitType(this, ctx);
ctx.print(`>`);
ctx.print(ast, `>`);
ast.value.visitExpression(this, ctx);
ctx.print(`)`);
ctx.print(ast, `)`);
return null;
}
visitInstantiateExpr(ast: o.InstantiateExpr, ctx: EmitterVisitorContext): any {
ctx.print(`new `);
ctx.print(ast, `new `);
this.typeExpression++;
ast.classExpr.visitExpression(this, ctx);
this.typeExpression--;
ctx.print(`(`);
ctx.print(ast, `(`);
this.visitAllExpressions(ast.args, ctx, ',');
ctx.print(`)`);
ctx.print(ast, `)`);
return null;
}
visitDeclareClassStmt(stmt: o.ClassStmt, ctx: EmitterVisitorContext): any {
ctx.pushClass(stmt);
if (ctx.isExportedVar(stmt.name)) {
ctx.print(`export `);
ctx.print(stmt, `export `);
}
ctx.print(`class ${stmt.name}`);
ctx.print(stmt, `class ${stmt.name}`);
if (isPresent(stmt.parent)) {
ctx.print(` extends `);
ctx.print(stmt, ` extends `);
this.typeExpression++;
stmt.parent.visitExpression(this, ctx);
this.typeExpression--;
}
ctx.println(` {`);
ctx.println(stmt, ` {`);
ctx.incIndent();
stmt.fields.forEach((field) => this._visitClassField(field, ctx));
if (isPresent(stmt.constructorMethod)) {
@@ -186,7 +201,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
stmt.getters.forEach((getter) => this._visitClassGetter(getter, ctx));
stmt.methods.forEach((method) => this._visitClassMethod(method, ctx));
ctx.decIndent();
ctx.println(`}`);
ctx.println(stmt, `}`);
ctx.popClass();
return null;
}
@@ -194,88 +209,88 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
private _visitClassField(field: o.ClassField, ctx: EmitterVisitorContext) {
if (field.hasModifier(o.StmtModifier.Private)) {
// comment out as a workaround for #10967
ctx.print(`/*private*/ `);
ctx.print(null, `/*private*/ `);
}
ctx.print(field.name);
ctx.print(':');
ctx.print(null, field.name);
ctx.print(null, ':');
this.visitType(field.type, ctx);
ctx.println(`;`);
ctx.println(null, `;`);
}
private _visitClassGetter(getter: o.ClassGetter, ctx: EmitterVisitorContext) {
if (getter.hasModifier(o.StmtModifier.Private)) {
ctx.print(`private `);
ctx.print(null, `private `);
}
ctx.print(`get ${getter.name}()`);
ctx.print(':');
ctx.print(null, `get ${getter.name}()`);
ctx.print(null, ':');
this.visitType(getter.type, ctx);
ctx.println(` {`);
ctx.println(null, ` {`);
ctx.incIndent();
this.visitAllStatements(getter.body, ctx);
ctx.decIndent();
ctx.println(`}`);
ctx.println(null, `}`);
}
private _visitClassConstructor(stmt: o.ClassStmt, ctx: EmitterVisitorContext) {
ctx.print(`constructor(`);
ctx.print(stmt, `constructor(`);
this._visitParams(stmt.constructorMethod.params, ctx);
ctx.println(`) {`);
ctx.println(stmt, `) {`);
ctx.incIndent();
this.visitAllStatements(stmt.constructorMethod.body, ctx);
ctx.decIndent();
ctx.println(`}`);
ctx.println(stmt, `}`);
}
private _visitClassMethod(method: o.ClassMethod, ctx: EmitterVisitorContext) {
if (method.hasModifier(o.StmtModifier.Private)) {
ctx.print(`private `);
ctx.print(null, `private `);
}
ctx.print(`${method.name}(`);
ctx.print(null, `${method.name}(`);
this._visitParams(method.params, ctx);
ctx.print(`):`);
ctx.print(null, `):`);
this.visitType(method.type, ctx, 'void');
ctx.println(` {`);
ctx.println(null, ` {`);
ctx.incIndent();
this.visitAllStatements(method.body, ctx);
ctx.decIndent();
ctx.println(`}`);
ctx.println(null, `}`);
}
visitFunctionExpr(ast: o.FunctionExpr, ctx: EmitterVisitorContext): any {
ctx.print(`(`);
ctx.print(ast, `(`);
this._visitParams(ast.params, ctx);
ctx.print(`):`);
ctx.print(ast, `):`);
this.visitType(ast.type, ctx, 'void');
ctx.println(` => {`);
ctx.println(ast, ` => {`);
ctx.incIndent();
this.visitAllStatements(ast.statements, ctx);
ctx.decIndent();
ctx.print(`}`);
ctx.print(ast, `}`);
return null;
}
visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, ctx: EmitterVisitorContext): any {
if (ctx.isExportedVar(stmt.name)) {
ctx.print(`export `);
ctx.print(stmt, `export `);
}
ctx.print(`function ${stmt.name}(`);
ctx.print(stmt, `function ${stmt.name}(`);
this._visitParams(stmt.params, ctx);
ctx.print(`):`);
ctx.print(stmt, `):`);
this.visitType(stmt.type, ctx, 'void');
ctx.println(` {`);
ctx.println(stmt, ` {`);
ctx.incIndent();
this.visitAllStatements(stmt.statements, ctx);
ctx.decIndent();
ctx.println(`}`);
ctx.println(stmt, `}`);
return null;
}
visitTryCatchStmt(stmt: o.TryCatchStmt, ctx: EmitterVisitorContext): any {
ctx.println(`try {`);
ctx.println(stmt, `try {`);
ctx.incIndent();
this.visitAllStatements(stmt.bodyStmts, ctx);
ctx.decIndent();
ctx.println(`} catch (${CATCH_ERROR_VAR.name}) {`);
ctx.println(stmt, `} catch (${CATCH_ERROR_VAR.name}) {`);
ctx.incIndent();
const catchStmts =
[<o.Statement>CATCH_STACK_VAR.set(CATCH_ERROR_VAR.prop('stack')).toDeclStmt(null, [
@@ -283,7 +298,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
])].concat(stmt.catchStmts);
this.visitAllStatements(catchStmts, ctx);
ctx.decIndent();
ctx.println(`}`);
ctx.println(stmt, `}`);
return null;
}
@@ -311,7 +326,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
default:
throw new Error(`Unsupported builtin type ${type.name}`);
}
ctx.print(typeStr);
ctx.print(null, typeStr);
return null;
}
@@ -322,14 +337,14 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
visitArrayType(type: o.ArrayType, ctx: EmitterVisitorContext): any {
this.visitType(type.of, ctx);
ctx.print(`[]`);
ctx.print(null, `[]`);
return null;
}
visitMapType(type: o.MapType, ctx: EmitterVisitorContext): any {
ctx.print(`{[key: string]:`);
ctx.print(null, `{[key: string]:`);
this.visitType(type.valueType, ctx);
ctx.print(`}`);
ctx.print(null, `}`);
return null;
}
@@ -353,8 +368,8 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
private _visitParams(params: o.FnParam[], ctx: EmitterVisitorContext): void {
this.visitAllObjects(param => {
ctx.print(param.name);
ctx.print(':');
ctx.print(null, param.name);
ctx.print(null, ':');
this.visitType(param.type, ctx);
}, params, ctx, ',');
}
@@ -383,18 +398,18 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
prefix = `import${this.importsWithPrefixes.size}`;
this.importsWithPrefixes.set(filePath, prefix);
}
ctx.print(`${prefix}.`);
ctx.print(null, `${prefix}.`);
}
if (members.length) {
ctx.print(name);
ctx.print('.');
ctx.print(members.join('.'));
ctx.print(null, name);
ctx.print(null, '.');
ctx.print(null, members.join('.'));
} else {
ctx.print(name);
ctx.print(null, name);
}
if (this.typeExpression > 0) {
// If we are in a type expreession that refers to a generic type then supply
// If we are in a type expression that refers to a generic type then supply
// the required type parameters. If there were not enough type parameters
// supplied, supply any as the type. Outside a type expression the reference
// should not supply type parameters and be treated as a simple value reference
@@ -402,17 +417,17 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
const suppliedParameters = (typeParams && typeParams.length) || 0;
const additionalParameters = (arity || 0) - suppliedParameters;
if (suppliedParameters > 0 || additionalParameters > 0) {
ctx.print(`<`);
ctx.print(null, `<`);
if (suppliedParameters > 0) {
this.visitAllObjects(type => type.visitType(this, ctx), typeParams, ctx, ',');
}
if (additionalParameters > 0) {
for (let i = 0; i < additionalParameters; i++) {
if (i > 0 || suppliedParameters > 0) ctx.print(',');
ctx.print('any');
if (i > 0 || suppliedParameters > 0) ctx.print(null, ',');
ctx.print(null, 'any');
}
}
ctx.print(`>`);
ctx.print(null, `>`);
}
}
}