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

View File

@ -7,11 +7,14 @@
*/ */
import {isBlank, isPresent} from '../facade/lang'; import {isBlank, isPresent} from '../facade/lang';
import {ParseSourceSpan} from '../parse_util';
import * as o from './output_ast'; import * as o from './output_ast';
import {SourceMapGenerator} from './source_map';
const _SINGLE_QUOTE_ESCAPE_STRING_RE = /'|\\|\n|\r|\$/g; const _SINGLE_QUOTE_ESCAPE_STRING_RE = /'|\\|\n|\r|\$/g;
const _LEGAL_IDENTIFIER_RE = /^[$A-Z_][0-9A-Z_$]*$/i; const _LEGAL_IDENTIFIER_RE = /^[$A-Z_][0-9A-Z_$]*$/i;
const _INDENT_WITH = ' ';
export const CATCH_ERROR_VAR = o.variable('error'); export const CATCH_ERROR_VAR = o.variable('error');
export const CATCH_STACK_VAR = o.variable('stack'); export const CATCH_STACK_VAR = o.variable('stack');
@ -21,6 +24,7 @@ export abstract class OutputEmitter {
class _EmittedLine { class _EmittedLine {
parts: string[] = []; parts: string[] = [];
srcSpans: ParseSourceSpan[] = [];
constructor(public indent: number) {} constructor(public indent: number) {}
} }
@ -40,13 +44,16 @@ export class EmitterVisitorContext {
isExportedVar(varName: string): boolean { return this._exportedVars.indexOf(varName) !== -1; } 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; } 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) { if (part.length > 0) {
this._currentLine.parts.push(part); this._currentLine.parts.push(part);
this._currentLine.srcSpans.push(from && from.sourceSpan || null);
} }
if (newLine) { if (newLine) {
this._lines.push(new _EmittedLine(this._indent)); 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; return this._classes.length > 0 ? this._classes[this._classes.length - 1] : null;
} }
toSource(): any { toSource(): string {
let lines = this._lines; return this.sourceLines
if (lines[lines.length - 1].parts.length === 0) { .map(l => l.parts.length > 0 ? _createIndent(l.indent) + l.parts.join('') : '')
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 '';
}
})
.join('\n'); .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 { 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 { visitExpressionStmt(stmt: o.ExpressionStatement, ctx: EmitterVisitorContext): any {
stmt.expr.visitExpression(this, ctx); stmt.expr.visitExpression(this, ctx);
ctx.println(';'); ctx.println(stmt, ';');
return null; return null;
} }
visitReturnStmt(stmt: o.ReturnStatement, ctx: EmitterVisitorContext): any { visitReturnStmt(stmt: o.ReturnStatement, ctx: EmitterVisitorContext): any {
ctx.print(`return `); ctx.print(stmt, `return `);
stmt.value.visitExpression(this, ctx); stmt.value.visitExpression(this, ctx);
ctx.println(';'); ctx.println(stmt, ';');
return null; return null;
} }
@ -115,82 +161,83 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
abstract visitDeclareClassStmt(stmt: o.ClassStmt, ctx: EmitterVisitorContext): any; abstract visitDeclareClassStmt(stmt: o.ClassStmt, ctx: EmitterVisitorContext): any;
visitIfStmt(stmt: o.IfStmt, ctx: EmitterVisitorContext): any { visitIfStmt(stmt: o.IfStmt, ctx: EmitterVisitorContext): any {
ctx.print(`if (`); ctx.print(stmt, `if (`);
stmt.condition.visitExpression(this, ctx); stmt.condition.visitExpression(this, ctx);
ctx.print(`) {`); ctx.print(stmt, `) {`);
const hasElseCase = isPresent(stmt.falseCase) && stmt.falseCase.length > 0; const hasElseCase = isPresent(stmt.falseCase) && stmt.falseCase.length > 0;
if (stmt.trueCase.length <= 1 && !hasElseCase) { if (stmt.trueCase.length <= 1 && !hasElseCase) {
ctx.print(` `); ctx.print(stmt, ` `);
this.visitAllStatements(stmt.trueCase, ctx); this.visitAllStatements(stmt.trueCase, ctx);
ctx.removeEmptyLastLine(); ctx.removeEmptyLastLine();
ctx.print(` `); ctx.print(stmt, ` `);
} else { } else {
ctx.println(); ctx.println();
ctx.incIndent(); ctx.incIndent();
this.visitAllStatements(stmt.trueCase, ctx); this.visitAllStatements(stmt.trueCase, ctx);
ctx.decIndent(); ctx.decIndent();
if (hasElseCase) { if (hasElseCase) {
ctx.println(`} else {`); ctx.println(stmt, `} else {`);
ctx.incIndent(); ctx.incIndent();
this.visitAllStatements(stmt.falseCase, ctx); this.visitAllStatements(stmt.falseCase, ctx);
ctx.decIndent(); ctx.decIndent();
} }
} }
ctx.println(`}`); ctx.println(stmt, `}`);
return null; return null;
} }
abstract visitTryCatchStmt(stmt: o.TryCatchStmt, ctx: EmitterVisitorContext): any; abstract visitTryCatchStmt(stmt: o.TryCatchStmt, ctx: EmitterVisitorContext): any;
visitThrowStmt(stmt: o.ThrowStmt, ctx: EmitterVisitorContext): any { visitThrowStmt(stmt: o.ThrowStmt, ctx: EmitterVisitorContext): any {
ctx.print(`throw `); ctx.print(stmt, `throw `);
stmt.error.visitExpression(this, ctx); stmt.error.visitExpression(this, ctx);
ctx.println(`;`); ctx.println(stmt, `;`);
return null; return null;
} }
visitCommentStmt(stmt: o.CommentStmt, ctx: EmitterVisitorContext): any { visitCommentStmt(stmt: o.CommentStmt, ctx: EmitterVisitorContext): any {
const lines = stmt.comment.split('\n'); const lines = stmt.comment.split('\n');
lines.forEach((line) => { ctx.println(`// ${line}`); }); lines.forEach((line) => { ctx.println(stmt, `// ${line}`); });
return null; return null;
} }
abstract visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any; abstract visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any;
visitWriteVarExpr(expr: o.WriteVarExpr, ctx: EmitterVisitorContext): any { visitWriteVarExpr(expr: o.WriteVarExpr, ctx: EmitterVisitorContext): any {
const lineWasEmpty = ctx.lineIsEmpty(); const lineWasEmpty = ctx.lineIsEmpty();
if (!lineWasEmpty) { if (!lineWasEmpty) {
ctx.print('('); ctx.print(expr, '(');
} }
ctx.print(`${expr.name} = `); ctx.print(expr, `${expr.name} = `);
expr.value.visitExpression(this, ctx); expr.value.visitExpression(this, ctx);
if (!lineWasEmpty) { if (!lineWasEmpty) {
ctx.print(')'); ctx.print(expr, ')');
} }
return null; return null;
} }
visitWriteKeyExpr(expr: o.WriteKeyExpr, ctx: EmitterVisitorContext): any { visitWriteKeyExpr(expr: o.WriteKeyExpr, ctx: EmitterVisitorContext): any {
const lineWasEmpty = ctx.lineIsEmpty(); const lineWasEmpty = ctx.lineIsEmpty();
if (!lineWasEmpty) { if (!lineWasEmpty) {
ctx.print('('); ctx.print(expr, '(');
} }
expr.receiver.visitExpression(this, ctx); expr.receiver.visitExpression(this, ctx);
ctx.print(`[`); ctx.print(expr, `[`);
expr.index.visitExpression(this, ctx); expr.index.visitExpression(this, ctx);
ctx.print(`] = `); ctx.print(expr, `] = `);
expr.value.visitExpression(this, ctx); expr.value.visitExpression(this, ctx);
if (!lineWasEmpty) { if (!lineWasEmpty) {
ctx.print(')'); ctx.print(expr, ')');
} }
return null; return null;
} }
visitWritePropExpr(expr: o.WritePropExpr, ctx: EmitterVisitorContext): any { visitWritePropExpr(expr: o.WritePropExpr, ctx: EmitterVisitorContext): any {
const lineWasEmpty = ctx.lineIsEmpty(); const lineWasEmpty = ctx.lineIsEmpty();
if (!lineWasEmpty) { if (!lineWasEmpty) {
ctx.print('('); ctx.print(expr, '(');
} }
expr.receiver.visitExpression(this, ctx); expr.receiver.visitExpression(this, ctx);
ctx.print(`.${expr.name} = `); ctx.print(expr, `.${expr.name} = `);
expr.value.visitExpression(this, ctx); expr.value.visitExpression(this, ctx);
if (!lineWasEmpty) { if (!lineWasEmpty) {
ctx.print(')'); ctx.print(expr, ')');
} }
return null; return null;
} }
@ -204,9 +251,9 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
return null; return null;
} }
} }
ctx.print(`.${name}(`); ctx.print(expr, `.${name}(`);
this.visitAllExpressions(expr.args, ctx, `,`); this.visitAllExpressions(expr.args, ctx, `,`);
ctx.print(`)`); ctx.print(expr, `)`);
return null; return null;
} }
@ -214,9 +261,9 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
visitInvokeFunctionExpr(expr: o.InvokeFunctionExpr, ctx: EmitterVisitorContext): any { visitInvokeFunctionExpr(expr: o.InvokeFunctionExpr, ctx: EmitterVisitorContext): any {
expr.fn.visitExpression(this, ctx); expr.fn.visitExpression(this, ctx);
ctx.print(`(`); ctx.print(expr, `(`);
this.visitAllExpressions(expr.args, ctx, ','); this.visitAllExpressions(expr.args, ctx, ',');
ctx.print(`)`); ctx.print(expr, `)`);
return null; return null;
} }
visitReadVarExpr(ast: o.ReadVarExpr, ctx: EmitterVisitorContext): any { 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}`); throw new Error(`Unknown builtin variable ${ast.builtin}`);
} }
} }
ctx.print(varName); ctx.print(ast, varName);
return null; return null;
} }
visitInstantiateExpr(ast: o.InstantiateExpr, ctx: EmitterVisitorContext): any { visitInstantiateExpr(ast: o.InstantiateExpr, ctx: EmitterVisitorContext): any {
ctx.print(`new `); ctx.print(ast, `new `);
ast.classExpr.visitExpression(this, ctx); ast.classExpr.visitExpression(this, ctx);
ctx.print(`(`); ctx.print(ast, `(`);
this.visitAllExpressions(ast.args, ctx, ','); this.visitAllExpressions(ast.args, ctx, ',');
ctx.print(`)`); ctx.print(ast, `)`);
return null; return null;
} }
visitLiteralExpr(ast: o.LiteralExpr, ctx: EmitterVisitorContext): any { visitLiteralExpr(ast: o.LiteralExpr, ctx: EmitterVisitorContext): any {
const value = ast.value; const value = ast.value;
if (typeof value === 'string') { if (typeof value === 'string') {
ctx.print(escapeIdentifier(value, this._escapeDollarInStrings)); ctx.print(ast, escapeIdentifier(value, this._escapeDollarInStrings));
} else { } else {
ctx.print(`${value}`); ctx.print(ast, `${value}`);
} }
return null; return null;
} }
@ -264,17 +311,17 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
abstract visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any; abstract visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any;
visitConditionalExpr(ast: o.ConditionalExpr, ctx: EmitterVisitorContext): any { visitConditionalExpr(ast: o.ConditionalExpr, ctx: EmitterVisitorContext): any {
ctx.print(`(`); ctx.print(ast, `(`);
ast.condition.visitExpression(this, ctx); ast.condition.visitExpression(this, ctx);
ctx.print('? '); ctx.print(ast, '? ');
ast.trueCase.visitExpression(this, ctx); ast.trueCase.visitExpression(this, ctx);
ctx.print(': '); ctx.print(ast, ': ');
ast.falseCase.visitExpression(this, ctx); ast.falseCase.visitExpression(this, ctx);
ctx.print(`)`); ctx.print(ast, `)`);
return null; return null;
} }
visitNotExpr(ast: o.NotExpr, ctx: EmitterVisitorContext): any { visitNotExpr(ast: o.NotExpr, ctx: EmitterVisitorContext): any {
ctx.print('!'); ctx.print(ast, '!');
ast.condition.visitExpression(this, ctx); ast.condition.visitExpression(this, ctx);
return null; return null;
} }
@ -332,46 +379,46 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
default: default:
throw new Error(`Unknown operator ${ast.operator}`); throw new Error(`Unknown operator ${ast.operator}`);
} }
ctx.print(`(`); ctx.print(ast, `(`);
ast.lhs.visitExpression(this, ctx); ast.lhs.visitExpression(this, ctx);
ctx.print(` ${opStr} `); ctx.print(ast, ` ${opStr} `);
ast.rhs.visitExpression(this, ctx); ast.rhs.visitExpression(this, ctx);
ctx.print(`)`); ctx.print(ast, `)`);
return null; return null;
} }
visitReadPropExpr(ast: o.ReadPropExpr, ctx: EmitterVisitorContext): any { visitReadPropExpr(ast: o.ReadPropExpr, ctx: EmitterVisitorContext): any {
ast.receiver.visitExpression(this, ctx); ast.receiver.visitExpression(this, ctx);
ctx.print(`.`); ctx.print(ast, `.`);
ctx.print(ast.name); ctx.print(ast, ast.name);
return null; return null;
} }
visitReadKeyExpr(ast: o.ReadKeyExpr, ctx: EmitterVisitorContext): any { visitReadKeyExpr(ast: o.ReadKeyExpr, ctx: EmitterVisitorContext): any {
ast.receiver.visitExpression(this, ctx); ast.receiver.visitExpression(this, ctx);
ctx.print(`[`); ctx.print(ast, `[`);
ast.index.visitExpression(this, ctx); ast.index.visitExpression(this, ctx);
ctx.print(`]`); ctx.print(ast, `]`);
return null; return null;
} }
visitLiteralArrayExpr(ast: o.LiteralArrayExpr, ctx: EmitterVisitorContext): any { visitLiteralArrayExpr(ast: o.LiteralArrayExpr, ctx: EmitterVisitorContext): any {
const useNewLine = ast.entries.length > 1; const useNewLine = ast.entries.length > 1;
ctx.print(`[`, useNewLine); ctx.print(ast, `[`, useNewLine);
ctx.incIndent(); ctx.incIndent();
this.visitAllExpressions(ast.entries, ctx, ',', useNewLine); this.visitAllExpressions(ast.entries, ctx, ',', useNewLine);
ctx.decIndent(); ctx.decIndent();
ctx.print(`]`, useNewLine); ctx.print(ast, `]`, useNewLine);
return null; return null;
} }
visitLiteralMapExpr(ast: o.LiteralMapExpr, ctx: EmitterVisitorContext): any { visitLiteralMapExpr(ast: o.LiteralMapExpr, ctx: EmitterVisitorContext): any {
const useNewLine = ast.entries.length > 1; const useNewLine = ast.entries.length > 1;
ctx.print(`{`, useNewLine); ctx.print(ast, `{`, useNewLine);
ctx.incIndent(); ctx.incIndent();
this.visitAllObjects(entry => { 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); entry.value.visitExpression(this, ctx);
}, ast.entries, ctx, ',', useNewLine); }, ast.entries, ctx, ',', useNewLine);
ctx.decIndent(); ctx.decIndent();
ctx.print(`}`, useNewLine); ctx.print(ast, `}`, useNewLine);
return null; return null;
} }
@ -387,7 +434,7 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
newLine: boolean = false): void { newLine: boolean = false): void {
for (let i = 0; i < expressions.length; i++) { for (let i = 0; i < expressions.length; i++) {
if (i > 0) { if (i > 0) {
ctx.print(separator, newLine); ctx.print(null, separator, newLine);
} }
handler(expressions[i]); handler(expressions[i]);
} }
@ -424,7 +471,7 @@ export function escapeIdentifier(
function _createIndent(count: number): string { function _createIndent(count: number): string {
let res = ''; let res = '';
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
res += ' '; res += _INDENT_WITH;
} }
return res; return res;
} }

View File

@ -18,9 +18,9 @@ export abstract class AbstractJsEmitterVisitor extends AbstractEmitterVisitor {
this._visitClassConstructor(stmt, ctx); this._visitClassConstructor(stmt, ctx);
if (isPresent(stmt.parent)) { if (isPresent(stmt.parent)) {
ctx.print(`${stmt.name}.prototype = Object.create(`); ctx.print(stmt, `${stmt.name}.prototype = Object.create(`);
stmt.parent.visitExpression(this, ctx); stmt.parent.visitExpression(this, ctx);
ctx.println(`.prototype);`); ctx.println(stmt, `.prototype);`);
} }
stmt.getters.forEach((getter) => this._visitClassGetter(stmt, getter, ctx)); stmt.getters.forEach((getter) => this._visitClassGetter(stmt, getter, ctx));
stmt.methods.forEach((method) => this._visitClassMethod(stmt, method, 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) { private _visitClassConstructor(stmt: o.ClassStmt, ctx: EmitterVisitorContext) {
ctx.print(`function ${stmt.name}(`); ctx.print(stmt, `function ${stmt.name}(`);
if (isPresent(stmt.constructorMethod)) { if (isPresent(stmt.constructorMethod)) {
this._visitParams(stmt.constructorMethod.params, ctx); this._visitParams(stmt.constructorMethod.params, ctx);
} }
ctx.println(`) {`); ctx.println(stmt, `) {`);
ctx.incIndent(); ctx.incIndent();
if (isPresent(stmt.constructorMethod)) { if (isPresent(stmt.constructorMethod)) {
if (stmt.constructorMethod.body.length > 0) { if (stmt.constructorMethod.body.length > 0) {
ctx.println(`var self = this;`); ctx.println(stmt, `var self = this;`);
this.visitAllStatements(stmt.constructorMethod.body, ctx); this.visitAllStatements(stmt.constructorMethod.body, ctx);
} }
} }
ctx.decIndent(); ctx.decIndent();
ctx.println(`}`); ctx.println(stmt, `}`);
} }
private _visitClassGetter(stmt: o.ClassStmt, getter: o.ClassGetter, ctx: EmitterVisitorContext) { private _visitClassGetter(stmt: o.ClassStmt, getter: o.ClassGetter, ctx: EmitterVisitorContext) {
ctx.println( ctx.println(
stmt,
`Object.defineProperty(${stmt.name}.prototype, '${getter.name}', { get: function() {`); `Object.defineProperty(${stmt.name}.prototype, '${getter.name}', { get: function() {`);
ctx.incIndent(); ctx.incIndent();
if (getter.body.length > 0) { if (getter.body.length > 0) {
ctx.println(`var self = this;`); ctx.println(stmt, `var self = this;`);
this.visitAllStatements(getter.body, ctx); this.visitAllStatements(getter.body, ctx);
} }
ctx.decIndent(); ctx.decIndent();
ctx.println(`}});`); ctx.println(stmt, `}});`);
} }
private _visitClassMethod(stmt: o.ClassStmt, method: o.ClassMethod, ctx: EmitterVisitorContext) { 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); this._visitParams(method.params, ctx);
ctx.println(`) {`); ctx.println(stmt, `) {`);
ctx.incIndent(); ctx.incIndent();
if (method.body.length > 0) { if (method.body.length > 0) {
ctx.println(`var self = this;`); ctx.println(stmt, `var self = this;`);
this.visitAllStatements(method.body, ctx); this.visitAllStatements(method.body, ctx);
} }
ctx.decIndent(); ctx.decIndent();
ctx.println(`};`); ctx.println(stmt, `};`);
} }
visitReadVarExpr(ast: o.ReadVarExpr, ctx: EmitterVisitorContext): string { visitReadVarExpr(ast: o.ReadVarExpr, ctx: EmitterVisitorContext): string {
if (ast.builtin === o.BuiltinVar.This) { if (ast.builtin === o.BuiltinVar.This) {
ctx.print('self'); ctx.print(ast, 'self');
} else if (ast.builtin === o.BuiltinVar.Super) { } else if (ast.builtin === o.BuiltinVar.Super) {
throw new Error( throw new Error(
`'super' needs to be handled at a parent ast node, not at the variable level!`); `'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; return null;
} }
visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any { visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any {
ctx.print(`var ${stmt.name} = `); ctx.print(stmt, `var ${stmt.name} = `);
stmt.value.visitExpression(this, ctx); stmt.value.visitExpression(this, ctx);
ctx.println(`;`); ctx.println(stmt, `;`);
return null; return null;
} }
visitCastExpr(ast: o.CastExpr, ctx: EmitterVisitorContext): any { visitCastExpr(ast: o.CastExpr, ctx: EmitterVisitorContext): any {
@ -95,43 +96,43 @@ export abstract class AbstractJsEmitterVisitor extends AbstractEmitterVisitor {
const fnExpr = expr.fn; const fnExpr = expr.fn;
if (fnExpr instanceof o.ReadVarExpr && fnExpr.builtin === o.BuiltinVar.Super) { if (fnExpr instanceof o.ReadVarExpr && fnExpr.builtin === o.BuiltinVar.Super) {
ctx.currentClass.parent.visitExpression(this, ctx); ctx.currentClass.parent.visitExpression(this, ctx);
ctx.print(`.call(this`); ctx.print(expr, `.call(this`);
if (expr.args.length > 0) { if (expr.args.length > 0) {
ctx.print(`, `); ctx.print(expr, `, `);
this.visitAllExpressions(expr.args, ctx, ','); this.visitAllExpressions(expr.args, ctx, ',');
} }
ctx.print(`)`); ctx.print(expr, `)`);
} else { } else {
super.visitInvokeFunctionExpr(expr, ctx); super.visitInvokeFunctionExpr(expr, ctx);
} }
return null; return null;
} }
visitFunctionExpr(ast: o.FunctionExpr, ctx: EmitterVisitorContext): any { visitFunctionExpr(ast: o.FunctionExpr, ctx: EmitterVisitorContext): any {
ctx.print(`function(`); ctx.print(ast, `function(`);
this._visitParams(ast.params, ctx); this._visitParams(ast.params, ctx);
ctx.println(`) {`); ctx.println(ast, `) {`);
ctx.incIndent(); ctx.incIndent();
this.visitAllStatements(ast.statements, ctx); this.visitAllStatements(ast.statements, ctx);
ctx.decIndent(); ctx.decIndent();
ctx.print(`}`); ctx.print(ast, `}`);
return null; return null;
} }
visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, ctx: EmitterVisitorContext): any { visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, ctx: EmitterVisitorContext): any {
ctx.print(`function ${stmt.name}(`); ctx.print(stmt, `function ${stmt.name}(`);
this._visitParams(stmt.params, ctx); this._visitParams(stmt.params, ctx);
ctx.println(`) {`); ctx.println(stmt, `) {`);
ctx.incIndent(); ctx.incIndent();
this.visitAllStatements(stmt.statements, ctx); this.visitAllStatements(stmt.statements, ctx);
ctx.decIndent(); ctx.decIndent();
ctx.println(`}`); ctx.println(stmt, `}`);
return null; return null;
} }
visitTryCatchStmt(stmt: o.TryCatchStmt, ctx: EmitterVisitorContext): any { visitTryCatchStmt(stmt: o.TryCatchStmt, ctx: EmitterVisitorContext): any {
ctx.println(`try {`); ctx.println(stmt, `try {`);
ctx.incIndent(); ctx.incIndent();
this.visitAllStatements(stmt.bodyStmts, ctx); this.visitAllStatements(stmt.bodyStmts, ctx);
ctx.decIndent(); ctx.decIndent();
ctx.println(`} catch (${CATCH_ERROR_VAR.name}) {`); ctx.println(stmt, `} catch (${CATCH_ERROR_VAR.name}) {`);
ctx.incIndent(); ctx.incIndent();
const catchStmts = const catchStmts =
[<o.Statement>CATCH_STACK_VAR.set(CATCH_ERROR_VAR.prop('stack')).toDeclStmt(null, [ [<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); ])].concat(stmt.catchStmts);
this.visitAllStatements(catchStmts, ctx); this.visitAllStatements(catchStmts, ctx);
ctx.decIndent(); ctx.decIndent();
ctx.println(`}`); ctx.println(stmt, `}`);
return null; return null;
} }
private _visitParams(params: o.FnParam[], ctx: EmitterVisitorContext): void { 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 { getBuiltinMethodName(method: o.BuiltinMethod): string {

View File

@ -6,8 +6,11 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {ParseSourceSpan} from '../parse_util';
import * as o from './output_ast'; import * as o from './output_ast';
/** /**
* Create a new class stmts based on the given data. * Create a new class stmts based on the given data.
*/ */
@ -16,7 +19,9 @@ export function createClassStmt(config: {
parent?: o.Expression, parent?: o.Expression,
parentArgs?: o.Expression[], parentArgs?: o.Expression[],
ctorParams?: o.FnParam[], ctorParams?: o.FnParam[],
builders: ClassBuilderPart | ClassBuilderPart[], modifiers?: o.StmtModifier[] builders: ClassBuilderPart | ClassBuilderPart[],
modifiers?: o.StmtModifier[],
sourceSpan?: ParseSourceSpan
}): o.ClassStmt { }): o.ClassStmt {
const parentArgs = config.parentArgs || []; const parentArgs = config.parentArgs || [];
const superCtorStmts = config.parent ? [o.SUPER_EXPR.callFn(parentArgs).toStmt()] : []; const superCtorStmts = config.parent ? [o.SUPER_EXPR.callFn(parentArgs).toStmt()] : [];
@ -27,7 +32,7 @@ export function createClassStmt(config: {
return new o.ClassStmt( return new o.ClassStmt(
config.name, config.parent, builder.fields, builder.getters, ctor, builder.methods, config.name, config.parent, builder.fields, builder.getters, ctor, builder.methods,
config.modifiers || []); config.modifiers || [], config.sourceSpan);
} }
function concatClassBuilderParts(builders: ClassBuilderPart[]) { function concatClassBuilderParts(builders: ClassBuilderPart[]) {

View File

@ -18,10 +18,12 @@ import {ImportResolver} from './path_util';
export class JavaScriptEmitter implements OutputEmitter { export class JavaScriptEmitter implements OutputEmitter {
constructor(private _importResolver: ImportResolver) {} constructor(private _importResolver: ImportResolver) {}
emitStatements(genFilePath: string, stmts: o.Statement[], exportedVars: string[]): string { emitStatements(genFilePath: string, stmts: o.Statement[], exportedVars: string[]): string {
const converter = new JsEmitterVisitor(genFilePath, this._importResolver); const converter = new JsEmitterVisitor(genFilePath, this._importResolver);
const ctx = EmitterVisitorContext.createRoot(exportedVars); const ctx = EmitterVisitorContext.createRoot(exportedVars);
converter.visitAllStatements(stmts, ctx); converter.visitAllStatements(stmts, ctx);
const srcParts: string[] = []; const srcParts: string[] = [];
converter.importsWithPrefixes.forEach((prefix, importedFilePath) => { converter.importsWithPrefixes.forEach((prefix, importedFilePath) => {
// Note: can't write the real word for import as it screws up system.js auto detection... // 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` + `var ${prefix} = req` +
`uire('${this._importResolver.fileNameToModuleName(importedFilePath, genFilePath)}');`); `uire('${this._importResolver.fileNameToModuleName(importedFilePath, genFilePath)}');`);
}); });
srcParts.push(ctx.toSource()); 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'); return srcParts.join('\n');
} }
} }
@ -55,29 +65,29 @@ class JsEmitterVisitor extends AbstractJsEmitterVisitor {
prefix = `import${this.importsWithPrefixes.size}`; prefix = `import${this.importsWithPrefixes.size}`;
this.importsWithPrefixes.set(filePath, prefix); this.importsWithPrefixes.set(filePath, prefix);
} }
ctx.print(`${prefix}.`); ctx.print(ast, `${prefix}.`);
} }
ctx.print(name); ctx.print(ast, name);
return null; return null;
} }
visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any { visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any {
super.visitDeclareVarStmt(stmt, ctx); super.visitDeclareVarStmt(stmt, ctx);
if (ctx.isExportedVar(stmt.name)) { if (ctx.isExportedVar(stmt.name)) {
ctx.println(exportVar(stmt.name)); ctx.println(stmt, exportVar(stmt.name));
} }
return null; return null;
} }
visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, ctx: EmitterVisitorContext): any { visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, ctx: EmitterVisitorContext): any {
super.visitDeclareFunctionStmt(stmt, ctx); super.visitDeclareFunctionStmt(stmt, ctx);
if (ctx.isExportedVar(stmt.name)) { if (ctx.isExportedVar(stmt.name)) {
ctx.println(exportVar(stmt.name)); ctx.println(stmt, exportVar(stmt.name));
} }
return null; return null;
} }
visitDeclareClassStmt(stmt: o.ClassStmt, ctx: EmitterVisitorContext): any { visitDeclareClassStmt(stmt: o.ClassStmt, ctx: EmitterVisitorContext): any {
super.visitDeclareClassStmt(stmt, ctx); super.visitDeclareClassStmt(stmt, ctx);
if (ctx.isExportedVar(stmt.name)) { if (ctx.isExportedVar(stmt.name)) {
ctx.println(exportVar(stmt.name)); ctx.println(stmt, exportVar(stmt.name));
} }
return null; return null;
} }

View File

@ -9,6 +9,7 @@
import {CompileIdentifierMetadata} from '../compile_metadata'; import {CompileIdentifierMetadata} from '../compile_metadata';
import {isPresent} from '../facade/lang'; import {isPresent} from '../facade/lang';
import {ParseSourceSpan} from '../parse_util';
//// Types //// Types
export enum TypeModifier { export enum TypeModifier {
@ -101,81 +102,91 @@ export enum BinaryOperator {
export abstract class Expression { export abstract class Expression {
constructor(public type: Type) {} constructor(public type: Type, public sourceSpan?: ParseSourceSpan) {}
abstract visitExpression(visitor: ExpressionVisitor, context: any): any; abstract visitExpression(visitor: ExpressionVisitor, context: any): any;
prop(name: string): ReadPropExpr { return new ReadPropExpr(this, name); } prop(name: string, sourceSpan?: ParseSourceSpan): ReadPropExpr {
return new ReadPropExpr(this, name, null, sourceSpan);
key(index: Expression, type: Type = null): ReadKeyExpr {
return new ReadKeyExpr(this, index, type);
} }
callMethod(name: string|BuiltinMethod, params: Expression[]): InvokeMethodExpr { key(index: Expression, type: Type = null, sourceSpan?: ParseSourceSpan): ReadKeyExpr {
return new InvokeMethodExpr(this, name, params); return new ReadKeyExpr(this, index, type, sourceSpan);
} }
callFn(params: Expression[]): InvokeFunctionExpr { return new InvokeFunctionExpr(this, params); } callMethod(name: string|BuiltinMethod, params: Expression[], sourceSpan?: ParseSourceSpan):
InvokeMethodExpr {
instantiate(params: Expression[], type: Type = null): InstantiateExpr { return new InvokeMethodExpr(this, name, params, null, sourceSpan);
return new InstantiateExpr(this, params, type);
} }
conditional(trueCase: Expression, falseCase: Expression = null): ConditionalExpr { callFn(params: Expression[], sourceSpan?: ParseSourceSpan): InvokeFunctionExpr {
return new ConditionalExpr(this, trueCase, falseCase); return new InvokeFunctionExpr(this, params, null, sourceSpan);
} }
equals(rhs: Expression): BinaryOperatorExpr { instantiate(params: Expression[], type: Type = null, sourceSpan?: ParseSourceSpan):
return new BinaryOperatorExpr(BinaryOperator.Equals, this, rhs); 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 { notEquals(rhs: Expression, sourceSpan?: ParseSourceSpan): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.NotIdentical, this, rhs); return new BinaryOperatorExpr(BinaryOperator.NotEquals, this, rhs, null, sourceSpan);
} }
minus(rhs: Expression): BinaryOperatorExpr { identical(rhs: Expression, sourceSpan?: ParseSourceSpan): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Minus, this, rhs); return new BinaryOperatorExpr(BinaryOperator.Identical, this, rhs, null, sourceSpan);
} }
plus(rhs: Expression): BinaryOperatorExpr { notIdentical(rhs: Expression, sourceSpan?: ParseSourceSpan): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Plus, this, rhs); return new BinaryOperatorExpr(BinaryOperator.NotIdentical, this, rhs, null, sourceSpan);
} }
divide(rhs: Expression): BinaryOperatorExpr { minus(rhs: Expression, sourceSpan?: ParseSourceSpan): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Divide, this, rhs); return new BinaryOperatorExpr(BinaryOperator.Minus, this, rhs, null, sourceSpan);
} }
multiply(rhs: Expression): BinaryOperatorExpr { plus(rhs: Expression, sourceSpan?: ParseSourceSpan): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Multiply, this, rhs); return new BinaryOperatorExpr(BinaryOperator.Plus, this, rhs, null, sourceSpan);
} }
modulo(rhs: Expression): BinaryOperatorExpr { divide(rhs: Expression, sourceSpan?: ParseSourceSpan): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Modulo, this, rhs); return new BinaryOperatorExpr(BinaryOperator.Divide, this, rhs, null, sourceSpan);
} }
and(rhs: Expression): BinaryOperatorExpr { multiply(rhs: Expression, sourceSpan?: ParseSourceSpan): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.And, this, rhs); return new BinaryOperatorExpr(BinaryOperator.Multiply, this, rhs, null, sourceSpan);
} }
or(rhs: Expression): BinaryOperatorExpr { modulo(rhs: Expression, sourceSpan?: ParseSourceSpan): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Or, this, rhs); return new BinaryOperatorExpr(BinaryOperator.Modulo, this, rhs, null, sourceSpan);
} }
lower(rhs: Expression): BinaryOperatorExpr { and(rhs: Expression, sourceSpan?: ParseSourceSpan): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Lower, this, rhs); return new BinaryOperatorExpr(BinaryOperator.And, this, rhs, null, sourceSpan);
} }
lowerEquals(rhs: Expression): BinaryOperatorExpr { or(rhs: Expression, sourceSpan?: ParseSourceSpan): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.LowerEquals, this, rhs); return new BinaryOperatorExpr(BinaryOperator.Or, this, rhs, null, sourceSpan);
} }
bigger(rhs: Expression): BinaryOperatorExpr { lower(rhs: Expression, sourceSpan?: ParseSourceSpan): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.Bigger, this, rhs); return new BinaryOperatorExpr(BinaryOperator.Lower, this, rhs, null, sourceSpan);
} }
biggerEquals(rhs: Expression): BinaryOperatorExpr { lowerEquals(rhs: Expression, sourceSpan?: ParseSourceSpan): BinaryOperatorExpr {
return new BinaryOperatorExpr(BinaryOperator.BiggerEquals, this, rhs); 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. // 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. // 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); } toStmt(): Statement { return new ExpressionStatement(this); }
} }
@ -190,8 +201,8 @@ export class ReadVarExpr extends Expression {
public name: string; public name: string;
public builtin: BuiltinVar; public builtin: BuiltinVar;
constructor(name: string|BuiltinVar, type: Type = null) { constructor(name: string|BuiltinVar, type: Type = null, sourceSpan?: ParseSourceSpan) {
super(type); super(type, sourceSpan);
if (typeof name === 'string') { if (typeof name === 'string') {
this.name = name; this.name = name;
this.builtin = null; this.builtin = null;
@ -204,14 +215,17 @@ export class ReadVarExpr extends Expression {
return visitor.visitReadVarExpr(this, context); 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 { export class WriteVarExpr extends Expression {
public value: Expression; public value: Expression;
constructor(public name: string, value: Expression, type: Type = null) { constructor(
super(type || value.type); public name: string, value: Expression, type: Type = null, sourceSpan?: ParseSourceSpan) {
super(type || value.type, sourceSpan);
this.value = value; this.value = value;
} }
@ -220,7 +234,7 @@ export class WriteVarExpr extends Expression {
} }
toDeclStmt(type: Type = null, modifiers: StmtModifier[] = null): DeclareVarStmt { 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 { export class WriteKeyExpr extends Expression {
public value: Expression; public value: Expression;
constructor( constructor(
public receiver: Expression, public index: Expression, value: Expression, type: Type = null) { public receiver: Expression, public index: Expression, value: Expression, type: Type = null,
super(type || value.type); sourceSpan?: ParseSourceSpan) {
super(type || value.type, sourceSpan);
this.value = value; this.value = value;
} }
visitExpression(visitor: ExpressionVisitor, context: any): any { visitExpression(visitor: ExpressionVisitor, context: any): any {
@ -241,8 +256,9 @@ export class WriteKeyExpr extends Expression {
export class WritePropExpr extends Expression { export class WritePropExpr extends Expression {
public value: Expression; public value: Expression;
constructor( constructor(
public receiver: Expression, public name: string, value: Expression, type: Type = null) { public receiver: Expression, public name: string, value: Expression, type: Type = null,
super(type || value.type); sourceSpan?: ParseSourceSpan) {
super(type || value.type, sourceSpan);
this.value = value; this.value = value;
} }
visitExpression(visitor: ExpressionVisitor, context: any): any { visitExpression(visitor: ExpressionVisitor, context: any): any {
@ -261,8 +277,8 @@ export class InvokeMethodExpr extends Expression {
public builtin: BuiltinMethod; public builtin: BuiltinMethod;
constructor( constructor(
public receiver: Expression, method: string|BuiltinMethod, public args: Expression[], public receiver: Expression, method: string|BuiltinMethod, public args: Expression[],
type: Type = null) { type: Type = null, sourceSpan?: ParseSourceSpan) {
super(type); super(type, sourceSpan);
if (typeof method === 'string') { if (typeof method === 'string') {
this.name = method; this.name = method;
this.builtin = null; this.builtin = null;
@ -278,7 +294,11 @@ export class InvokeMethodExpr extends Expression {
export class InvokeFunctionExpr 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 { visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitInvokeFunctionExpr(this, context); return visitor.visitInvokeFunctionExpr(this, context);
} }
@ -286,7 +306,11 @@ export class InvokeFunctionExpr extends Expression {
export class InstantiateExpr 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 { visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitInstantiateExpr(this, context); return visitor.visitInstantiateExpr(this, context);
} }
@ -294,7 +318,9 @@ export class InstantiateExpr extends Expression {
export class LiteralExpr 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 { visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitLiteralExpr(this, context); return visitor.visitLiteralExpr(this, context);
} }
@ -303,9 +329,9 @@ export class LiteralExpr extends Expression {
export class ExternalExpr extends Expression { export class ExternalExpr extends Expression {
constructor( constructor(
public value: CompileIdentifierMetadata, type: Type = null, public value: CompileIdentifierMetadata, type: Type = null, public typeParams: Type[] = null,
public typeParams: Type[] = null) { sourceSpan?: ParseSourceSpan) {
super(type); super(type, sourceSpan);
} }
visitExpression(visitor: ExpressionVisitor, context: any): any { visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitExternalExpr(this, context); return visitor.visitExternalExpr(this, context);
@ -317,8 +343,8 @@ export class ConditionalExpr extends Expression {
public trueCase: Expression; public trueCase: Expression;
constructor( constructor(
public condition: Expression, trueCase: Expression, public falseCase: Expression = null, public condition: Expression, trueCase: Expression, public falseCase: Expression = null,
type: Type = null) { type: Type = null, sourceSpan?: ParseSourceSpan) {
super(type || trueCase.type); super(type || trueCase.type, sourceSpan);
this.trueCase = trueCase; this.trueCase = trueCase;
} }
visitExpression(visitor: ExpressionVisitor, context: any): any { visitExpression(visitor: ExpressionVisitor, context: any): any {
@ -328,14 +354,18 @@ export class ConditionalExpr extends Expression {
export class NotExpr 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 { visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitNotExpr(this, context); return visitor.visitNotExpr(this, context);
} }
} }
export class CastExpr extends Expression { 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 { visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitCastExpr(this, context); return visitor.visitCastExpr(this, context);
} }
@ -348,15 +378,18 @@ export class FnParam {
export class FunctionExpr extends Expression { export class FunctionExpr extends Expression {
constructor(public params: FnParam[], public statements: Statement[], type: Type = null) { constructor(
super(type); public params: FnParam[], public statements: Statement[], type: Type = null,
sourceSpan?: ParseSourceSpan) {
super(type, sourceSpan);
} }
visitExpression(visitor: ExpressionVisitor, context: any): any { visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitFunctionExpr(this, context); return visitor.visitFunctionExpr(this, context);
} }
toDeclStmt(name: string, modifiers: StmtModifier[] = null): DeclareFunctionStmt { 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 { export class BinaryOperatorExpr extends Expression {
public lhs: Expression; public lhs: Expression;
constructor( constructor(
public operator: BinaryOperator, lhs: Expression, public rhs: Expression, type: Type = null) { public operator: BinaryOperator, lhs: Expression, public rhs: Expression, type: Type = null,
super(type || lhs.type); sourceSpan?: ParseSourceSpan) {
super(type || lhs.type, sourceSpan);
this.lhs = lhs; this.lhs = lhs;
} }
visitExpression(visitor: ExpressionVisitor, context: any): any { visitExpression(visitor: ExpressionVisitor, context: any): any {
@ -375,33 +409,39 @@ export class BinaryOperatorExpr extends Expression {
export class ReadPropExpr 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 { visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitReadPropExpr(this, context); return visitor.visitReadPropExpr(this, context);
} }
set(value: Expression): WritePropExpr { 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 { export class ReadKeyExpr extends Expression {
constructor(public receiver: Expression, public index: Expression, type: Type = null) { constructor(
super(type); public receiver: Expression, public index: Expression, type: Type = null,
sourceSpan?: ParseSourceSpan) {
super(type, sourceSpan);
} }
visitExpression(visitor: ExpressionVisitor, context: any): any { visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitReadKeyExpr(this, context); return visitor.visitReadKeyExpr(this, context);
} }
set(value: Expression): WriteKeyExpr { 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 { export class LiteralArrayExpr extends Expression {
public entries: Expression[]; public entries: Expression[];
constructor(entries: Expression[], type: Type = null) { constructor(entries: Expression[], type: Type = null, sourceSpan?: ParseSourceSpan) {
super(type); super(type, sourceSpan);
this.entries = entries; this.entries = entries;
} }
visitExpression(visitor: ExpressionVisitor, context: any): any { visitExpression(visitor: ExpressionVisitor, context: any): any {
@ -415,9 +455,10 @@ export class LiteralMapEntry {
export class LiteralMapExpr extends Expression { export class LiteralMapExpr extends Expression {
public valueType: Type = null; public valueType: Type = null;
constructor(public entries: LiteralMapEntry[], type: MapType = null) { constructor(
super(type); public entries: LiteralMapEntry[], type: MapType = null, sourceSpan?: ParseSourceSpan) {
if (isPresent(type)) { super(type, sourceSpan);
if (type) {
this.valueType = type.valueType; this.valueType = type.valueType;
} }
} }
@ -461,7 +502,7 @@ export enum StmtModifier {
} }
export abstract class Statement { export abstract class Statement {
constructor(public modifiers: StmtModifier[] = null) { constructor(public modifiers: StmtModifier[] = null, public sourceSpan?: ParseSourceSpan) {
if (!modifiers) { if (!modifiers) {
this.modifiers = []; this.modifiers = [];
} }
@ -477,8 +518,8 @@ export class DeclareVarStmt extends Statement {
public type: Type; public type: Type;
constructor( constructor(
public name: string, public value: Expression, type: Type = null, public name: string, public value: Expression, type: Type = null,
modifiers: StmtModifier[] = null) { modifiers: StmtModifier[] = null, sourceSpan?: ParseSourceSpan) {
super(modifiers); super(modifiers, sourceSpan);
this.type = type || value.type; this.type = type || value.type;
} }
@ -490,8 +531,8 @@ export class DeclareVarStmt extends Statement {
export class DeclareFunctionStmt extends Statement { export class DeclareFunctionStmt extends Statement {
constructor( constructor(
public name: string, public params: FnParam[], public statements: Statement[], public name: string, public params: FnParam[], public statements: Statement[],
public type: Type = null, modifiers: StmtModifier[] = null) { public type: Type = null, modifiers: StmtModifier[] = null, sourceSpan?: ParseSourceSpan) {
super(modifiers); super(modifiers, sourceSpan);
} }
visitStatement(visitor: StatementVisitor, context: any): any { visitStatement(visitor: StatementVisitor, context: any): any {
@ -500,7 +541,7 @@ export class DeclareFunctionStmt extends Statement {
} }
export class ExpressionStatement 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 { visitStatement(visitor: StatementVisitor, context: any): any {
return visitor.visitExpressionStmt(this, context); return visitor.visitExpressionStmt(this, context);
@ -509,7 +550,7 @@ export class ExpressionStatement extends Statement {
export class ReturnStatement 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 { visitStatement(visitor: StatementVisitor, context: any): any {
return visitor.visitReturnStmt(this, context); return visitor.visitReturnStmt(this, context);
} }
@ -553,8 +594,9 @@ export class ClassStmt extends Statement {
constructor( constructor(
public name: string, public parent: Expression, public fields: ClassField[], public name: string, public parent: Expression, public fields: ClassField[],
public getters: ClassGetter[], public constructorMethod: ClassMethod, public getters: ClassGetter[], public constructorMethod: ClassMethod,
public methods: ClassMethod[], modifiers: StmtModifier[] = null) { public methods: ClassMethod[], modifiers: StmtModifier[] = null,
super(modifiers); sourceSpan?: ParseSourceSpan) {
super(modifiers, sourceSpan);
} }
visitStatement(visitor: StatementVisitor, context: any): any { visitStatement(visitor: StatementVisitor, context: any): any {
return visitor.visitDeclareClassStmt(this, context); return visitor.visitDeclareClassStmt(this, context);
@ -565,8 +607,8 @@ export class ClassStmt extends Statement {
export class IfStmt extends Statement { export class IfStmt extends Statement {
constructor( constructor(
public condition: Expression, public trueCase: Statement[], public condition: Expression, public trueCase: Statement[],
public falseCase: Statement[] = []) { public falseCase: Statement[] = [], sourceSpan?: ParseSourceSpan) {
super(); super(null, sourceSpan);
} }
visitStatement(visitor: StatementVisitor, context: any): any { visitStatement(visitor: StatementVisitor, context: any): any {
return visitor.visitIfStmt(this, context); return visitor.visitIfStmt(this, context);
@ -575,7 +617,7 @@ export class IfStmt extends Statement {
export class CommentStmt 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 { visitStatement(visitor: StatementVisitor, context: any): any {
return visitor.visitCommentStmt(this, context); return visitor.visitCommentStmt(this, context);
} }
@ -583,7 +625,10 @@ export class CommentStmt extends Statement {
export class TryCatchStmt 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 { visitStatement(visitor: StatementVisitor, context: any): any {
return visitor.visitTryCatchStmt(this, context); return visitor.visitTryCatchStmt(this, context);
} }
@ -591,7 +636,7 @@ export class TryCatchStmt extends Statement {
export class ThrowStmt 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 { visitStatement(visitor: StatementVisitor, context: any): any {
return visitor.visitThrowStmt(this, context); return visitor.visitThrowStmt(this, context);
} }
@ -611,74 +656,94 @@ export interface StatementVisitor {
export class ExpressionTransformer implements StatementVisitor, ExpressionVisitor { export class ExpressionTransformer implements StatementVisitor, ExpressionVisitor {
visitReadVarExpr(ast: ReadVarExpr, context: any): any { return ast; } visitReadVarExpr(ast: ReadVarExpr, context: any): any { return ast; }
visitWriteVarExpr(expr: WriteVarExpr, context: any): any { 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 { visitWriteKeyExpr(expr: WriteKeyExpr, context: any): any {
return new WriteKeyExpr( return new WriteKeyExpr(
expr.receiver.visitExpression(this, context), expr.index.visitExpression(this, context), 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 { visitWritePropExpr(expr: WritePropExpr, context: any): any {
return new WritePropExpr( return new WritePropExpr(
expr.receiver.visitExpression(this, context), expr.name, 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 { visitInvokeMethodExpr(ast: InvokeMethodExpr, context: any): any {
const method = ast.builtin || ast.name; const method = ast.builtin || ast.name;
return new InvokeMethodExpr( return new InvokeMethodExpr(
ast.receiver.visitExpression(this, context), method, 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 { visitInvokeFunctionExpr(ast: InvokeFunctionExpr, context: any): any {
return new InvokeFunctionExpr( return new InvokeFunctionExpr(
ast.fn.visitExpression(this, context), this.visitAllExpressions(ast.args, context), ast.fn.visitExpression(this, context), this.visitAllExpressions(ast.args, context),
ast.type); ast.type, ast.sourceSpan);
} }
visitInstantiateExpr(ast: InstantiateExpr, context: any): any { visitInstantiateExpr(ast: InstantiateExpr, context: any): any {
return new InstantiateExpr( return new InstantiateExpr(
ast.classExpr.visitExpression(this, context), this.visitAllExpressions(ast.args, context), ast.classExpr.visitExpression(this, context), this.visitAllExpressions(ast.args, context),
ast.type); ast.type, ast.sourceSpan);
} }
visitLiteralExpr(ast: LiteralExpr, context: any): any { return ast; } visitLiteralExpr(ast: LiteralExpr, context: any): any { return ast; }
visitExternalExpr(ast: ExternalExpr, context: any): any { return ast; } visitExternalExpr(ast: ExternalExpr, context: any): any { return ast; }
visitConditionalExpr(ast: ConditionalExpr, context: any): any { visitConditionalExpr(ast: ConditionalExpr, context: any): any {
return new ConditionalExpr( return new ConditionalExpr(
ast.condition.visitExpression(this, context), ast.trueCase.visitExpression(this, context), 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 { 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 { 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 { visitFunctionExpr(ast: FunctionExpr, context: any): any {
// Don't descend into nested functions // Don't descend into nested functions
return ast; return ast;
} }
visitBinaryOperatorExpr(ast: BinaryOperatorExpr, context: any): any { visitBinaryOperatorExpr(ast: BinaryOperatorExpr, context: any): any {
return new BinaryOperatorExpr( return new BinaryOperatorExpr(
ast.operator, ast.lhs.visitExpression(this, context), 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 { 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 { visitReadKeyExpr(ast: ReadKeyExpr, context: any): any {
return new ReadKeyExpr( return new ReadKeyExpr(
ast.receiver.visitExpression(this, context), ast.index.visitExpression(this, context), ast.receiver.visitExpression(this, context), ast.index.visitExpression(this, context),
ast.type); ast.type, ast.sourceSpan);
} }
visitLiteralArrayExpr(ast: LiteralArrayExpr, context: any): any { 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 { visitLiteralMapExpr(ast: LiteralMapExpr, context: any): any {
const entries = ast.entries.map( const entries = ast.entries.map(
(entry): LiteralMapEntry => new LiteralMapEntry( (entry): LiteralMapEntry => new LiteralMapEntry(
entry.key, entry.value.visitExpression(this, context), entry.quoted)); entry.key, entry.value.visitExpression(this, context), entry.quoted, ));
return new LiteralMapExpr(entries); const mapType = new MapType(ast.valueType);
return new LiteralMapExpr(entries, mapType, ast.sourceSpan);
} }
visitAllExpressions(exprs: Expression[], context: any): Expression[] { visitAllExpressions(exprs: Expression[], context: any): Expression[] {
return exprs.map(expr => expr.visitExpression(this, context)); return exprs.map(expr => expr.visitExpression(this, context));
@ -686,37 +751,46 @@ export class ExpressionTransformer implements StatementVisitor, ExpressionVisito
visitDeclareVarStmt(stmt: DeclareVarStmt, context: any): any { visitDeclareVarStmt(stmt: DeclareVarStmt, context: any): any {
return new DeclareVarStmt( 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 { visitDeclareFunctionStmt(stmt: DeclareFunctionStmt, context: any): any {
// Don't descend into nested functions // Don't descend into nested functions
return stmt; return stmt;
} }
visitExpressionStmt(stmt: ExpressionStatement, context: any): any { 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 { 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 { visitDeclareClassStmt(stmt: ClassStmt, context: any): any {
// Don't descend into nested functions // Don't descend into nested functions
return stmt; return stmt;
} }
visitIfStmt(stmt: IfStmt, context: any): any { visitIfStmt(stmt: IfStmt, context: any): any {
return new IfStmt( return new IfStmt(
stmt.condition.visitExpression(this, context), stmt.condition.visitExpression(this, context),
this.visitAllStatements(stmt.trueCase, context), this.visitAllStatements(stmt.trueCase, context),
this.visitAllStatements(stmt.falseCase, context)); this.visitAllStatements(stmt.falseCase, context), stmt.sourceSpan);
} }
visitTryCatchStmt(stmt: TryCatchStmt, context: any): any { visitTryCatchStmt(stmt: TryCatchStmt, context: any): any {
return new TryCatchStmt( return new TryCatchStmt(
this.visitAllStatements(stmt.bodyStmts, context), this.visitAllStatements(stmt.bodyStmts, context),
this.visitAllStatements(stmt.catchStmts, context)); this.visitAllStatements(stmt.catchStmts, context), stmt.sourceSpan);
} }
visitThrowStmt(stmt: ThrowStmt, context: any): any { 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; } visitCommentStmt(stmt: CommentStmt, context: any): any { return stmt; }
visitAllStatements(stmts: Statement[], context: any): Statement[] { visitAllStatements(stmts: Statement[], context: any): Statement[] {
return stmts.map(stmt => stmt.visitStatement(this, context)); 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 { export function variable(
return new ReadVarExpr(name, type); name: string, type: Type = null, sourceSpan?: ParseSourceSpan): ReadVarExpr {
return new ReadVarExpr(name, type, sourceSpan);
} }
export function importExpr(id: CompileIdentifierMetadata, typeParams: Type[] = null): ExternalExpr { export function importExpr(
return new ExternalExpr(id, null, typeParams); id: CompileIdentifierMetadata, typeParams: Type[] = null,
sourceSpan?: ParseSourceSpan): ExternalExpr {
return new ExternalExpr(id, null, typeParams, sourceSpan);
} }
export function importType( export function importType(
@ -885,8 +962,9 @@ export function expressionType(
return isPresent(expr) ? new ExpressionType(expr, typeModifiers) : null; return isPresent(expr) ? new ExpressionType(expr, typeModifiers) : null;
} }
export function literalArr(values: Expression[], type: Type = null): LiteralArrayExpr { export function literalArr(
return new LiteralArrayExpr(values, type); values: Expression[], type: Type = null, sourceSpan?: ParseSourceSpan): LiteralArrayExpr {
return new LiteralArrayExpr(values, type, sourceSpan);
} }
export function literalMap( export function literalMap(
@ -895,14 +973,16 @@ export function literalMap(
values.map(entry => new LiteralMapEntry(entry[0], entry[1], quoted)), type); values.map(entry => new LiteralMapEntry(entry[0], entry[1], quoted)), type);
} }
export function not(expr: Expression): NotExpr { export function not(expr: Expression, sourceSpan?: ParseSourceSpan): NotExpr {
return new NotExpr(expr); return new NotExpr(expr, sourceSpan);
} }
export function fn(params: FnParam[], body: Statement[], type: Type = null): FunctionExpr { export function fn(
return new FunctionExpr(params, body, type); 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 { export function literal(value: any, type: Type = null, sourceSpan?: ParseSourceSpan): LiteralExpr {
return new LiteralExpr(value, type); return new LiteralExpr(value, type, sourceSpan);
} }

View File

@ -28,7 +28,7 @@ function _executeFunctionStatements(
childCtx.vars.set(varNames[i], varValues[i]); childCtx.vars.set(varNames[i], varValues[i]);
} }
const result = visitor.visitAllStatements(statements, childCtx); const result = visitor.visitAllStatements(statements, childCtx);
return isPresent(result) ? result.value : null; return result ? result.value : null;
} }
class _ExecutionContext { class _ExecutionContext {

View File

@ -7,15 +7,15 @@
*/ */
import {identifierName} from '../compile_metadata'; import {identifierName} from '../compile_metadata';
import {isPresent} from '../facade/lang';
import {EmitterVisitorContext} from './abstract_emitter'; import {EmitterVisitorContext} from './abstract_emitter';
import {AbstractJsEmitterVisitor} from './abstract_js_emitter'; import {AbstractJsEmitterVisitor} from './abstract_js_emitter';
import * as o from './output_ast'; import * as o from './output_ast';
function evalExpression( function evalExpression(
sourceUrl: string, expr: string, declarations: string, vars: {[key: string]: any}): any { sourceUrl: string, expr: string, ctx: EmitterVisitorContext, vars: {[key: string]: any}): any {
const fnBody = `${declarations}\nreturn ${expr}\n//# sourceURL=${sourceUrl}`; const fnBody =
`${ctx.toSource()}\nreturn ${expr}\n//# sourceURL=${sourceUrl}\n${ctx.toSourceMapGenerator().toJsComment()}`;
const fnArgNames: string[] = []; const fnArgNames: string[] = [];
const fnArgValues: any[] = []; const fnArgValues: any[] = [];
for (const argName in vars) { for (const argName in vars) {
@ -25,13 +25,12 @@ function evalExpression(
return new Function(...fnArgNames.concat(fnBody))(...fnArgValues); return new Function(...fnArgNames.concat(fnBody))(...fnArgValues);
} }
export function jitStatements( export function jitStatements(
sourceUrl: string, statements: o.Statement[], resultVar: string): any { sourceUrl: string, statements: o.Statement[], resultVar: string): any {
const converter = new JitEmitterVisitor(); const converter = new JitEmitterVisitor();
const ctx = EmitterVisitorContext.createRoot([resultVar]); const ctx = EmitterVisitorContext.createRoot([resultVar]);
converter.visitAllStatements(statements, ctx); converter.visitAllStatements(statements, ctx);
return evalExpression(sourceUrl, resultVar, ctx.toSource(), converter.getArgs()); return evalExpression(sourceUrl, resultVar, ctx, converter.getArgs());
} }
class JitEmitterVisitor extends AbstractJsEmitterVisitor { class JitEmitterVisitor extends AbstractJsEmitterVisitor {
@ -55,7 +54,7 @@ class JitEmitterVisitor extends AbstractJsEmitterVisitor {
const name = identifierName(ast.value) || 'val'; const name = identifierName(ast.value) || 'val';
this._evalArgNames.push(`jit_${name}${id}`); this._evalArgNames.push(`jit_${name}${id}`);
} }
ctx.print(this._evalArgNames[id]); ctx.print(ast, this._evalArgNames[id]);
return null; return null;
} }
} }

View File

@ -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];
}

View File

@ -41,26 +41,41 @@ export function debugOutputAstAsTypeScript(ast: o.Statement | o.Expression | o.T
return ctx.toSource(); return ctx.toSource();
} }
export class TypeScriptEmitter implements OutputEmitter { export class TypeScriptEmitter implements OutputEmitter {
constructor(private _importResolver: ImportResolver) {} constructor(private _importResolver: ImportResolver) {}
emitStatements(genFilePath: string, stmts: o.Statement[], exportedVars: string[]): string { emitStatements(genFilePath: string, stmts: o.Statement[], exportedVars: string[]): string {
const converter = new _TsEmitterVisitor(genFilePath, this._importResolver); const converter = new _TsEmitterVisitor(genFilePath, this._importResolver);
const ctx = EmitterVisitorContext.createRoot(exportedVars); const ctx = EmitterVisitorContext.createRoot(exportedVars);
converter.visitAllStatements(stmts, ctx); converter.visitAllStatements(stmts, ctx);
const srcParts: string[] = []; const srcParts: string[] = [];
converter.reexports.forEach((reexports, exportedFilePath) => { converter.reexports.forEach((reexports, exportedFilePath) => {
const reexportsCode = const reexportsCode =
reexports.map(reexport => `${reexport.name} as ${reexport.as}`).join(','); reexports.map(reexport => `${reexport.name} as ${reexport.as}`).join(',');
srcParts.push( srcParts.push(
`export {${reexportsCode}} from '${this._importResolver.fileNameToModuleName(exportedFilePath, genFilePath)}';`); `export {${reexportsCode}} from '${this._importResolver.fileNameToModuleName(exportedFilePath, genFilePath)}';`);
}); });
converter.importsWithPrefixes.forEach((prefix, importedFilePath) => { converter.importsWithPrefixes.forEach((prefix, importedFilePath) => {
// Note: can't write the real word for import as it screws up system.js auto detection... // Note: can't write the real word for import as it screws up system.js auto detection...
srcParts.push( srcParts.push(
`imp` + `imp` +
`ort * as ${prefix} from '${this._importResolver.fileNameToModuleName(importedFilePath, genFilePath)}';`); `ort * as ${prefix} from '${this._importResolver.fileNameToModuleName(importedFilePath, genFilePath)}';`);
}); });
srcParts.push(ctx.toSource()); 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'); return srcParts.join('\n');
} }
} }
@ -81,14 +96,14 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
t.visitType(this, ctx); t.visitType(this, ctx);
this.typeExpression--; this.typeExpression--;
} else { } else {
ctx.print(defaultType); ctx.print(null, defaultType);
} }
} }
visitLiteralExpr(ast: o.LiteralExpr, ctx: EmitterVisitorContext): any { visitLiteralExpr(ast: o.LiteralExpr, ctx: EmitterVisitorContext): any {
const value = ast.value; const value = ast.value;
if (isBlank(value) && ast.type != o.NULL_TYPE) { if (isBlank(value) && ast.type != o.NULL_TYPE) {
ctx.print(`(${value} as any)`); ctx.print(ast, `(${value} as any)`);
return null; return null;
} }
return super.visitLiteralExpr(ast, ctx); 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 // start with [].concat. see https://github.com/angular/angular/pull/11846
visitLiteralArrayExpr(ast: o.LiteralArrayExpr, ctx: EmitterVisitorContext): any { visitLiteralArrayExpr(ast: o.LiteralArrayExpr, ctx: EmitterVisitorContext): any {
if (ast.entries.length === 0) { if (ast.entries.length === 0) {
ctx.print('('); ctx.print(ast, '(');
} }
const result = super.visitLiteralArrayExpr(ast, ctx); const result = super.visitLiteralArrayExpr(ast, ctx);
if (ast.entries.length === 0) { if (ast.entries.length === 0) {
ctx.print(' as any[])'); ctx.print(ast, ' as any[])');
} }
return result; return result;
} }
@ -130,54 +145,54 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
} }
} }
if (ctx.isExportedVar(stmt.name)) { if (ctx.isExportedVar(stmt.name)) {
ctx.print(`export `); ctx.print(stmt, `export `);
} }
if (stmt.hasModifier(o.StmtModifier.Final)) { if (stmt.hasModifier(o.StmtModifier.Final)) {
ctx.print(`const`); ctx.print(stmt, `const`);
} else { } else {
ctx.print(`var`); ctx.print(stmt, `var`);
} }
ctx.print(` ${stmt.name}:`); ctx.print(stmt, ` ${stmt.name}:`);
this.visitType(stmt.type, ctx); this.visitType(stmt.type, ctx);
ctx.print(` = `); ctx.print(stmt, ` = `);
stmt.value.visitExpression(this, ctx); stmt.value.visitExpression(this, ctx);
ctx.println(`;`); ctx.println(stmt, `;`);
return null; return null;
} }
visitCastExpr(ast: o.CastExpr, ctx: EmitterVisitorContext): any { visitCastExpr(ast: o.CastExpr, ctx: EmitterVisitorContext): any {
ctx.print(`(<`); ctx.print(ast, `(<`);
ast.type.visitType(this, ctx); ast.type.visitType(this, ctx);
ctx.print(`>`); ctx.print(ast, `>`);
ast.value.visitExpression(this, ctx); ast.value.visitExpression(this, ctx);
ctx.print(`)`); ctx.print(ast, `)`);
return null; return null;
} }
visitInstantiateExpr(ast: o.InstantiateExpr, ctx: EmitterVisitorContext): any { visitInstantiateExpr(ast: o.InstantiateExpr, ctx: EmitterVisitorContext): any {
ctx.print(`new `); ctx.print(ast, `new `);
this.typeExpression++; this.typeExpression++;
ast.classExpr.visitExpression(this, ctx); ast.classExpr.visitExpression(this, ctx);
this.typeExpression--; this.typeExpression--;
ctx.print(`(`); ctx.print(ast, `(`);
this.visitAllExpressions(ast.args, ctx, ','); this.visitAllExpressions(ast.args, ctx, ',');
ctx.print(`)`); ctx.print(ast, `)`);
return null; return null;
} }
visitDeclareClassStmt(stmt: o.ClassStmt, ctx: EmitterVisitorContext): any { visitDeclareClassStmt(stmt: o.ClassStmt, ctx: EmitterVisitorContext): any {
ctx.pushClass(stmt); ctx.pushClass(stmt);
if (ctx.isExportedVar(stmt.name)) { 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)) { if (isPresent(stmt.parent)) {
ctx.print(` extends `); ctx.print(stmt, ` extends `);
this.typeExpression++; this.typeExpression++;
stmt.parent.visitExpression(this, ctx); stmt.parent.visitExpression(this, ctx);
this.typeExpression--; this.typeExpression--;
} }
ctx.println(` {`); ctx.println(stmt, ` {`);
ctx.incIndent(); ctx.incIndent();
stmt.fields.forEach((field) => this._visitClassField(field, ctx)); stmt.fields.forEach((field) => this._visitClassField(field, ctx));
if (isPresent(stmt.constructorMethod)) { if (isPresent(stmt.constructorMethod)) {
@ -186,7 +201,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
stmt.getters.forEach((getter) => this._visitClassGetter(getter, ctx)); stmt.getters.forEach((getter) => this._visitClassGetter(getter, ctx));
stmt.methods.forEach((method) => this._visitClassMethod(method, ctx)); stmt.methods.forEach((method) => this._visitClassMethod(method, ctx));
ctx.decIndent(); ctx.decIndent();
ctx.println(`}`); ctx.println(stmt, `}`);
ctx.popClass(); ctx.popClass();
return null; return null;
} }
@ -194,88 +209,88 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
private _visitClassField(field: o.ClassField, ctx: EmitterVisitorContext) { private _visitClassField(field: o.ClassField, ctx: EmitterVisitorContext) {
if (field.hasModifier(o.StmtModifier.Private)) { if (field.hasModifier(o.StmtModifier.Private)) {
// comment out as a workaround for #10967 // comment out as a workaround for #10967
ctx.print(`/*private*/ `); ctx.print(null, `/*private*/ `);
} }
ctx.print(field.name); ctx.print(null, field.name);
ctx.print(':'); ctx.print(null, ':');
this.visitType(field.type, ctx); this.visitType(field.type, ctx);
ctx.println(`;`); ctx.println(null, `;`);
} }
private _visitClassGetter(getter: o.ClassGetter, ctx: EmitterVisitorContext) { private _visitClassGetter(getter: o.ClassGetter, ctx: EmitterVisitorContext) {
if (getter.hasModifier(o.StmtModifier.Private)) { if (getter.hasModifier(o.StmtModifier.Private)) {
ctx.print(`private `); ctx.print(null, `private `);
} }
ctx.print(`get ${getter.name}()`); ctx.print(null, `get ${getter.name}()`);
ctx.print(':'); ctx.print(null, ':');
this.visitType(getter.type, ctx); this.visitType(getter.type, ctx);
ctx.println(` {`); ctx.println(null, ` {`);
ctx.incIndent(); ctx.incIndent();
this.visitAllStatements(getter.body, ctx); this.visitAllStatements(getter.body, ctx);
ctx.decIndent(); ctx.decIndent();
ctx.println(`}`); ctx.println(null, `}`);
} }
private _visitClassConstructor(stmt: o.ClassStmt, ctx: EmitterVisitorContext) { private _visitClassConstructor(stmt: o.ClassStmt, ctx: EmitterVisitorContext) {
ctx.print(`constructor(`); ctx.print(stmt, `constructor(`);
this._visitParams(stmt.constructorMethod.params, ctx); this._visitParams(stmt.constructorMethod.params, ctx);
ctx.println(`) {`); ctx.println(stmt, `) {`);
ctx.incIndent(); ctx.incIndent();
this.visitAllStatements(stmt.constructorMethod.body, ctx); this.visitAllStatements(stmt.constructorMethod.body, ctx);
ctx.decIndent(); ctx.decIndent();
ctx.println(`}`); ctx.println(stmt, `}`);
} }
private _visitClassMethod(method: o.ClassMethod, ctx: EmitterVisitorContext) { private _visitClassMethod(method: o.ClassMethod, ctx: EmitterVisitorContext) {
if (method.hasModifier(o.StmtModifier.Private)) { 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); this._visitParams(method.params, ctx);
ctx.print(`):`); ctx.print(null, `):`);
this.visitType(method.type, ctx, 'void'); this.visitType(method.type, ctx, 'void');
ctx.println(` {`); ctx.println(null, ` {`);
ctx.incIndent(); ctx.incIndent();
this.visitAllStatements(method.body, ctx); this.visitAllStatements(method.body, ctx);
ctx.decIndent(); ctx.decIndent();
ctx.println(`}`); ctx.println(null, `}`);
} }
visitFunctionExpr(ast: o.FunctionExpr, ctx: EmitterVisitorContext): any { visitFunctionExpr(ast: o.FunctionExpr, ctx: EmitterVisitorContext): any {
ctx.print(`(`); ctx.print(ast, `(`);
this._visitParams(ast.params, ctx); this._visitParams(ast.params, ctx);
ctx.print(`):`); ctx.print(ast, `):`);
this.visitType(ast.type, ctx, 'void'); this.visitType(ast.type, ctx, 'void');
ctx.println(` => {`); ctx.println(ast, ` => {`);
ctx.incIndent(); ctx.incIndent();
this.visitAllStatements(ast.statements, ctx); this.visitAllStatements(ast.statements, ctx);
ctx.decIndent(); ctx.decIndent();
ctx.print(`}`); ctx.print(ast, `}`);
return null; return null;
} }
visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, ctx: EmitterVisitorContext): any { visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, ctx: EmitterVisitorContext): any {
if (ctx.isExportedVar(stmt.name)) { 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); this._visitParams(stmt.params, ctx);
ctx.print(`):`); ctx.print(stmt, `):`);
this.visitType(stmt.type, ctx, 'void'); this.visitType(stmt.type, ctx, 'void');
ctx.println(` {`); ctx.println(stmt, ` {`);
ctx.incIndent(); ctx.incIndent();
this.visitAllStatements(stmt.statements, ctx); this.visitAllStatements(stmt.statements, ctx);
ctx.decIndent(); ctx.decIndent();
ctx.println(`}`); ctx.println(stmt, `}`);
return null; return null;
} }
visitTryCatchStmt(stmt: o.TryCatchStmt, ctx: EmitterVisitorContext): any { visitTryCatchStmt(stmt: o.TryCatchStmt, ctx: EmitterVisitorContext): any {
ctx.println(`try {`); ctx.println(stmt, `try {`);
ctx.incIndent(); ctx.incIndent();
this.visitAllStatements(stmt.bodyStmts, ctx); this.visitAllStatements(stmt.bodyStmts, ctx);
ctx.decIndent(); ctx.decIndent();
ctx.println(`} catch (${CATCH_ERROR_VAR.name}) {`); ctx.println(stmt, `} catch (${CATCH_ERROR_VAR.name}) {`);
ctx.incIndent(); ctx.incIndent();
const catchStmts = const catchStmts =
[<o.Statement>CATCH_STACK_VAR.set(CATCH_ERROR_VAR.prop('stack')).toDeclStmt(null, [ [<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); ])].concat(stmt.catchStmts);
this.visitAllStatements(catchStmts, ctx); this.visitAllStatements(catchStmts, ctx);
ctx.decIndent(); ctx.decIndent();
ctx.println(`}`); ctx.println(stmt, `}`);
return null; return null;
} }
@ -311,7 +326,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
default: default:
throw new Error(`Unsupported builtin type ${type.name}`); throw new Error(`Unsupported builtin type ${type.name}`);
} }
ctx.print(typeStr); ctx.print(null, typeStr);
return null; return null;
} }
@ -322,14 +337,14 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
visitArrayType(type: o.ArrayType, ctx: EmitterVisitorContext): any { visitArrayType(type: o.ArrayType, ctx: EmitterVisitorContext): any {
this.visitType(type.of, ctx); this.visitType(type.of, ctx);
ctx.print(`[]`); ctx.print(null, `[]`);
return null; return null;
} }
visitMapType(type: o.MapType, ctx: EmitterVisitorContext): any { visitMapType(type: o.MapType, ctx: EmitterVisitorContext): any {
ctx.print(`{[key: string]:`); ctx.print(null, `{[key: string]:`);
this.visitType(type.valueType, ctx); this.visitType(type.valueType, ctx);
ctx.print(`}`); ctx.print(null, `}`);
return null; return null;
} }
@ -353,8 +368,8 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
private _visitParams(params: o.FnParam[], ctx: EmitterVisitorContext): void { private _visitParams(params: o.FnParam[], ctx: EmitterVisitorContext): void {
this.visitAllObjects(param => { this.visitAllObjects(param => {
ctx.print(param.name); ctx.print(null, param.name);
ctx.print(':'); ctx.print(null, ':');
this.visitType(param.type, ctx); this.visitType(param.type, ctx);
}, params, ctx, ','); }, params, ctx, ',');
} }
@ -383,18 +398,18 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
prefix = `import${this.importsWithPrefixes.size}`; prefix = `import${this.importsWithPrefixes.size}`;
this.importsWithPrefixes.set(filePath, prefix); this.importsWithPrefixes.set(filePath, prefix);
} }
ctx.print(`${prefix}.`); ctx.print(null, `${prefix}.`);
} }
if (members.length) { if (members.length) {
ctx.print(name); ctx.print(null, name);
ctx.print('.'); ctx.print(null, '.');
ctx.print(members.join('.')); ctx.print(null, members.join('.'));
} else { } else {
ctx.print(name); ctx.print(null, name);
} }
if (this.typeExpression > 0) { 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 // the required type parameters. If there were not enough type parameters
// supplied, supply any as the type. Outside a type expression the reference // 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 // 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 suppliedParameters = (typeParams && typeParams.length) || 0;
const additionalParameters = (arity || 0) - suppliedParameters; const additionalParameters = (arity || 0) - suppliedParameters;
if (suppliedParameters > 0 || additionalParameters > 0) { if (suppliedParameters > 0 || additionalParameters > 0) {
ctx.print(`<`); ctx.print(null, `<`);
if (suppliedParameters > 0) { if (suppliedParameters > 0) {
this.visitAllObjects(type => type.visitType(this, ctx), typeParams, ctx, ','); this.visitAllObjects(type => type.visitType(this, ctx), typeParams, ctx, ',');
} }
if (additionalParameters > 0) { if (additionalParameters > 0) {
for (let i = 0; i < additionalParameters; i++) { for (let i = 0; i < additionalParameters; i++) {
if (i > 0 || suppliedParameters > 0) ctx.print(','); if (i > 0 || suppliedParameters > 0) ctx.print(null, ',');
ctx.print('any'); ctx.print(null, 'any');
} }
} }
ctx.print(`>`); ctx.print(null, `>`);
} }
} }
} }

View File

@ -0,0 +1,148 @@
/**
* @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
*/
import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '@angular/compiler';
import {EmitterVisitorContext} from '@angular/compiler/src/output/abstract_emitter';
import {SourceMap} from '@angular/compiler/src/output/source_map';
const SourceMapConsumer = require('source-map').SourceMapConsumer;
const b64 = require('base64-js');
export function main() {
describe('AbstractEmitter', () => {
describe('EmitterVisitorContext', () => {
const fileA = new ParseSourceFile('a0a1a2a3a4a5a6a7a8a9', 'a.js');
const fileB = new ParseSourceFile('b0b1b2b3b4b5b6b7b8b9', 'b.js');
let ctx: EmitterVisitorContext;
beforeEach(() => { ctx = EmitterVisitorContext.createRoot([]); });
it('should add source files to the source map', () => {
ctx.print(createSourceSpan(fileA, 0), 'o0');
ctx.print(createSourceSpan(fileA, 1), 'o1');
ctx.print(createSourceSpan(fileB, 0), 'o2');
ctx.print(createSourceSpan(fileB, 1), 'o3');
const sm = ctx.toSourceMapGenerator('o.js').toJSON();
expect(sm.sources).toEqual([fileA.url, fileB.url]);
expect(sm.sourcesContent).toEqual([fileA.content, fileB.content]);
});
it('should generate a valid mapping', () => {
ctx.print(createSourceSpan(fileA, 0), 'fileA-0');
ctx.println(createSourceSpan(fileB, 1), 'fileB-1');
ctx.print(createSourceSpan(fileA, 2), 'fileA-2');
expectMap(ctx, 0, 0, 'a.js', 0, 0);
expectMap(ctx, 0, 7, 'b.js', 0, 2);
expectMap(ctx, 1, 0, 'a.js', 0, 4);
});
it('should be able to shift the content', () => {
ctx.print(createSourceSpan(fileA, 0), 'fileA-0');
const sm = ctx.toSourceMapGenerator(null, 10).toJSON();
const smc = new SourceMapConsumer(sm);
expect(smc.originalPositionFor({line: 11, column: 0})).toEqual({
line: 1,
column: 0,
source: 'a.js',
name: null,
});
});
it('should not map leading segment without span', () => {
ctx.print(null, '....');
ctx.print(createSourceSpan(fileA, 0), 'fileA-0');
expectMap(ctx, 0, 0);
expectMap(ctx, 0, 4, 'a.js', 0, 0);
expect(nbSegmentsPerLine(ctx)).toEqual([1]);
});
it('should handle indent', () => {
ctx.incIndent();
ctx.println(createSourceSpan(fileA, 0), 'fileA-0');
ctx.incIndent();
ctx.println(createSourceSpan(fileA, 1), 'fileA-1');
ctx.decIndent();
ctx.println(createSourceSpan(fileA, 2), 'fileA-2');
expectMap(ctx, 0, 0);
expectMap(ctx, 0, 2, 'a.js', 0, 0);
expectMap(ctx, 1, 0);
expectMap(ctx, 1, 2);
expectMap(ctx, 1, 4, 'a.js', 0, 2);
expectMap(ctx, 2, 0);
expectMap(ctx, 2, 2, 'a.js', 0, 4);
expect(nbSegmentsPerLine(ctx)).toEqual([1, 1, 1]);
});
it('should coalesce identical span', () => {
const span = createSourceSpan(fileA, 0);
ctx.print(span, 'fileA-0');
ctx.print(null, '...');
ctx.print(span, 'fileA-0');
ctx.print(createSourceSpan(fileB, 0), 'fileB-0');
expectMap(ctx, 0, 0, 'a.js', 0, 0);
expectMap(ctx, 0, 7, 'a.js', 0, 0);
expectMap(ctx, 0, 10, 'a.js', 0, 0);
expectMap(ctx, 0, 17, 'b.js', 0, 0);
expect(nbSegmentsPerLine(ctx)).toEqual([2]);
});
});
});
}
// All lines / columns indexes are 0-based
// Note: source-map line indexes are 1-based, column 0-based
function expectMap(
ctx: EmitterVisitorContext, genLine: number, genCol: number, source: string = null,
srcLine: number = null, srcCol: number = null) {
const sm = ctx.toSourceMapGenerator().toJSON();
const smc = new SourceMapConsumer(sm);
const genPosition = {line: genLine + 1, column: genCol};
const origPosition = smc.originalPositionFor(genPosition);
expect(origPosition.source).toEqual(source);
expect(origPosition.line).toEqual(srcLine === null ? null : srcLine + 1);
expect(origPosition.column).toEqual(srcCol);
}
// returns the number of segments per line
function nbSegmentsPerLine(ctx: EmitterVisitorContext) {
const sm = ctx.toSourceMapGenerator().toJSON();
const lines = sm.mappings.split(';');
return lines.map(l => {
const m = l.match(/,/g);
return m === null ? 1 : m.length + 1;
});
}
function createSourceSpan(file: ParseSourceFile, idx: number) {
const col = 2 * idx;
const start = new ParseLocation(file, col, 0, col);
const end = new ParseLocation(file, col + 2, 0, col + 2);
const sourceSpan = new ParseSourceSpan(start, end);
return {sourceSpan};
}
export function extractSourceMap(source: string): SourceMap {
let idx = source.lastIndexOf('\n//#');
if (idx == -1) return null;
const smComment = source.slice(idx).trim();
const smB64 = smComment.split('sourceMappingURL=data:application/json;base64,')[1];
return smB64 ? JSON.parse(decodeB64String(smB64)) : null;
}
function decodeB64String(s: string): string {
return b64.toByteArray(s).reduce((s: string, c: number) => s + String.fromCharCode(c), '');
}

View File

@ -7,7 +7,6 @@
*/ */
import {escapeIdentifier} from '@angular/compiler/src/output/abstract_emitter'; import {escapeIdentifier} from '@angular/compiler/src/output/abstract_emitter';
import {describe, expect, it} from '@angular/core/testing/testing_internal';
export function main() { export function main() {
describe('AbstractEmitter', () => { describe('AbstractEmitter', () => {
@ -31,6 +30,11 @@ export function main() {
it('does not escape class (but it probably should)', it('does not escape class (but it probably should)',
() => { expect(escapeIdentifier('class', false, false)).toEqual('class'); }); () => { expect(escapeIdentifier('class', false, false)).toEqual('class'); });
}); });
}); });
} }
export function stripSourceMap(source: string): string {
const smi = source.lastIndexOf('\n//#');
if (smi == -1) return source;
return source.slice(0, smi);
}

View File

@ -0,0 +1,66 @@
/**
* @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
*/
import {StaticSymbol} from '@angular/compiler/src/aot/static_symbol';
import {CompileIdentifierMetadata} from '@angular/compiler/src/compile_metadata';
import {JavaScriptEmitter} from '@angular/compiler/src/output/js_emitter';
import * as o from '@angular/compiler/src/output/output_ast';
import {ImportResolver} from '@angular/compiler/src/output/path_util';
import {SourceMap} from '@angular/compiler/src/output/source_map';
import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '@angular/compiler/src/parse_util';
import {extractSourceMap} from './abstract_emitter_node_only_spec';
const SourceMapConsumer = require('source-map').SourceMapConsumer;
const someModuleUrl = 'somePackage/somePath';
class SimpleJsImportGenerator implements ImportResolver {
fileNameToModuleName(importedUrlStr: string, moduleUrlStr: string): string {
return importedUrlStr;
}
getImportAs(symbol: StaticSymbol): StaticSymbol { return null; }
getTypeArity(symbol: StaticSymbol): number /*|null*/ { return null; }
}
export function main() {
describe('JavaScriptEmitter', () => {
let importResolver: ImportResolver;
let emitter: JavaScriptEmitter;
let someVar: o.ReadVarExpr;
beforeEach(() => {
importResolver = new SimpleJsImportGenerator();
emitter = new JavaScriptEmitter(importResolver);
});
function emitSourceMap(
stmt: o.Statement | o.Statement[], exportedVars: string[] = null): SourceMap {
const stmts = Array.isArray(stmt) ? stmt : [stmt];
const source = emitter.emitStatements(someModuleUrl, stmts, exportedVars || []);
return extractSourceMap(source);
}
describe('source maps', () => {
it('should emit an inline source map', () => {
const source = new ParseSourceFile(';;;var', 'in.js');
const startLocation = new ParseLocation(source, 0, 0, 3);
const endLocation = new ParseLocation(source, 7, 0, 6);
const sourceSpan = new ParseSourceSpan(startLocation, endLocation);
const someVar = o.variable('someVar', null, sourceSpan);
const sm = emitSourceMap(someVar.toStmt());
const smc = new SourceMapConsumer(sm);
expect(sm.sources).toEqual(['in.js']);
expect(sm.sourcesContent).toEqual([';;;var']);
expect(smc.originalPositionFor({line: 1, column: 0}))
.toEqual({line: 1, column: 3, source: 'in.js', name: null});
});
});
});
}

View File

@ -12,6 +12,8 @@ import {JavaScriptEmitter} from '@angular/compiler/src/output/js_emitter';
import * as o from '@angular/compiler/src/output/output_ast'; import * as o from '@angular/compiler/src/output/output_ast';
import {ImportResolver} from '@angular/compiler/src/output/path_util'; import {ImportResolver} from '@angular/compiler/src/output/path_util';
import {stripSourceMap} from './abstract_emitter_spec';
const someModuleUrl = 'somePackage/somePath'; const someModuleUrl = 'somePackage/somePath';
const anotherModuleUrl = 'somePackage/someOtherPath'; const anotherModuleUrl = 'somePackage/someOtherPath';
@ -47,10 +49,8 @@ export function main() {
}); });
function emitStmt(stmt: o.Statement, exportedVars: string[] = null): string { function emitStmt(stmt: o.Statement, exportedVars: string[] = null): string {
if (!exportedVars) { const source = emitter.emitStatements(someModuleUrl, [stmt], exportedVars || []);
exportedVars = []; return stripSourceMap(source);
}
return emitter.emitStatements(someModuleUrl, [stmt], exportedVars);
} }
it('should declare variables', () => { it('should declare variables', () => {

View File

@ -0,0 +1,130 @@
/**
* @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
*/
import {SourceMapGenerator, toBase64String} from '@angular/compiler/src/output/source_map';
export function main() {
describe('source map generation', () => {
describe('generation', () => {
it('should generate a valid source map', () => {
const map = new SourceMapGenerator('out.js')
.addSource('a.js', null)
.addLine()
.addMapping(0, 'a.js', 0, 0)
.addMapping(4, 'a.js', 0, 6)
.addMapping(5, 'a.js', 0, 7)
.addMapping(8, 'a.js', 0, 22)
.addMapping(9, 'a.js', 0, 23)
.addMapping(10, 'a.js', 0, 24)
.addLine()
.addMapping(0, 'a.js', 1, 0)
.addMapping(4, 'a.js', 1, 6)
.addMapping(5, 'a.js', 1, 7)
.addMapping(8, 'a.js', 1, 10)
.addMapping(9, 'a.js', 1, 11)
.addMapping(10, 'a.js', 1, 12)
.addLine()
.addMapping(0, 'a.js', 3, 0)
.addMapping(2, 'a.js', 3, 2)
.addMapping(3, 'a.js', 3, 3)
.addMapping(10, 'a.js', 3, 10)
.addMapping(11, 'a.js', 3, 11)
.addMapping(21, 'a.js', 3, 11)
.addMapping(22, 'a.js', 3, 12)
.addLine()
.addMapping(4, 'a.js', 4, 4)
.addMapping(11, 'a.js', 4, 11)
.addMapping(12, 'a.js', 4, 12)
.addMapping(15, 'a.js', 4, 15)
.addMapping(16, 'a.js', 4, 16)
.addMapping(21, 'a.js', 4, 21)
.addMapping(22, 'a.js', 4, 22)
.addMapping(23, 'a.js', 4, 23)
.addLine()
.addMapping(0, 'a.js', 5, 0)
.addMapping(1, 'a.js', 5, 1)
.addMapping(2, 'a.js', 5, 2)
.addMapping(3, 'a.js', 5, 2);
// Generated with https://sokra.github.io/source-map-visualization using a TS source map
expect(map.toJSON().mappings)
.toEqual(
'AAAA,IAAM,CAAC,GAAe,CAAC,CAAC;AACxB,IAAM,CAAC,GAAG,CAAC,CAAC;AAEZ,EAAE,CAAC,OAAO,CAAC,UAAA,CAAC;IACR,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC,CAAC,CAAA');
});
it('should include the files and their contents', () => {
const map = new SourceMapGenerator('out.js')
.addSource('inline.ts', 'inline')
.addSource('inline.ts', 'inline') // make sur the sources are dedup
.addSource('url.ts', null)
.addLine()
.addMapping(0, 'inline.ts', 0, 0)
.toJSON();
expect(map.file).toEqual('out.js');
expect(map.sources).toEqual(['inline.ts', 'url.ts']);
expect(map.sourcesContent).toEqual(['inline', null]);
});
it('should not generate source maps when there is no mapping', () => {
const smg = new SourceMapGenerator('out.js').addSource('inline.ts', 'inline').addLine();
expect(smg.toJSON()).toEqual(null);
expect(smg.toJsComment()).toEqual('');
});
});
describe('encodeB64String', () => {
it('should return the b64 encoded value', () => {
[['', ''], ['a', 'YQ=='], ['Foo', 'Rm9v'], ['Foo1', 'Rm9vMQ=='], ['Foo12', 'Rm9vMTI='],
['Foo123', 'Rm9vMTIz'],
].forEach(([src, b64]) => expect(toBase64String(src)).toEqual(b64));
});
});
describe('errors', () => {
it('should throw when mappings are added out of order', () => {
expect(() => {
new SourceMapGenerator('out.js')
.addSource('in.js')
.addLine()
.addMapping(10, 'in.js', 0, 0)
.addMapping(0, 'in.js', 0, 0);
}).toThrowError('Mapping should be added in output order');
});
it('should throw when adding segments before any line is created', () => {
expect(() => {
new SourceMapGenerator('out.js').addSource('in.js').addMapping(0, 'in.js', 0, 0);
}).toThrowError('A line must be added before mappings can be added');
});
it('should throw when adding segments referencing unknown sources', () => {
expect(() => {
new SourceMapGenerator('out.js').addSource('in.js').addLine().addMapping(
0, 'in_.js', 0, 0);
}).toThrowError('Unknown source file "in_.js"');
});
it('should throw when adding segments without column', () => {
expect(() => {
new SourceMapGenerator('out.js').addSource('in.js').addLine().addMapping(null);
}).toThrowError('The column in the generated code must be provided');
});
it('should throw when adding segments with a source url but no position', () => {
expect(() => {
new SourceMapGenerator('out.js').addSource('in.js').addLine().addMapping(0, 'in.js');
}).toThrowError('The source location must be provided when a source url is provided');
expect(() => {
new SourceMapGenerator('out.js').addSource('in.js').addLine().addMapping(0, 'in.js', 0);
}).toThrowError('The source location must be provided when a source url is provided');
});
});
});
}

View File

@ -6,74 +6,66 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, StaticSymbolResolverHost} from '@angular/compiler'; import {ParseLocation, ParseSourceFile} from '@angular/compiler';
import {StaticSymbol} from '@angular/compiler/src/aot/static_symbol';
import * as o from '@angular/compiler/src/output/output_ast'; import * as o from '@angular/compiler/src/output/output_ast';
import {ImportResolver} from '@angular/compiler/src/output/path_util'; import {ImportResolver} from '@angular/compiler/src/output/path_util';
import {SourceMap} from '@angular/compiler/src/output/source_map';
import {TypeScriptEmitter} from '@angular/compiler/src/output/ts_emitter'; import {TypeScriptEmitter} from '@angular/compiler/src/output/ts_emitter';
import {convertValueToOutputAst} from '@angular/compiler/src/output/value_util'; import {ParseSourceSpan} from '@angular/compiler/src/parse_util';
import {MetadataCollector, isClassMetadata, isMetadataSymbolicCallExpression} from '@angular/tsc-wrapped';
import * as ts from 'typescript';
import {MockSummaryResolver} from '../aot/static_symbol_resolver_spec'; import {extractSourceMap} from './abstract_emitter_node_only_spec';
describe('TypeScriptEmitter (node only)', () => { const SourceMapConsumer = require('source-map').SourceMapConsumer;
it('should quote identifiers quoted in the source', () => {
const sourceText = `
import {Component} from '@angular/core';
@Component({ const someModuleUrl = 'somePackage/somePath';
providers: [{ provide: 'SomeToken', useValue: {a: 1, 'b': 2, c: 3, 'd': 4}}]
})
export class MyComponent {}
`;
const source = ts.createSourceFile('test.ts', sourceText, ts.ScriptTarget.Latest);
const collector = new MetadataCollector({quotedNames: true});
const stubHost = new StubReflectorHost();
const symbolCache = new StaticSymbolCache();
const symbolResolver =
new StaticSymbolResolver(stubHost, symbolCache, new MockSummaryResolver());
const reflector = new StaticReflector(symbolResolver);
// Get the metadata from the above source class SimpleJsImportGenerator implements ImportResolver {
const metadata = collector.getMetadata(source); fileNameToModuleName(importedUrlStr: string, moduleUrlStr: string): string {
const componentMetadata = metadata.metadata['MyComponent']; return importedUrlStr;
// Get the first argument of the decorator call which is passed to @Component
expect(isClassMetadata(componentMetadata)).toBeTruthy();
if (!isClassMetadata(componentMetadata)) return;
const decorators = componentMetadata.decorators;
const firstDecorator = decorators[0];
expect(isMetadataSymbolicCallExpression(firstDecorator)).toBeTruthy();
if (!isMetadataSymbolicCallExpression(firstDecorator)) return;
const firstArgument = firstDecorator.arguments[0];
// Simplify this value using the StaticReflector
const context = reflector.getStaticSymbol('none', 'none');
const argumentValue = reflector.simplify(context, firstArgument);
// Convert the value to an output AST
const outputAst = convertValueToOutputAst(argumentValue);
const statement = outputAst.toStmt();
// Convert the value to text using the typescript emitter
const emitter = new TypeScriptEmitter(new StubImportResolver());
const text = emitter.emitStatements('module', [statement], []);
// Expect the keys for 'b' and 'd' to be quoted but 'a' and 'c' not to be.
expect(text).toContain('\'b\': 2');
expect(text).toContain('\'d\': 4');
expect(text).not.toContain('\'a\'');
expect(text).not.toContain('\'c\'');
});
});
class StubReflectorHost implements StaticSymbolResolverHost {
getMetadataFor(modulePath: string): {[key: string]: any}[] { return []; }
moduleNameToFileName(moduleName: string, containingFile: string): string { return 'somePath'; }
} }
class StubImportResolver extends ImportResolver {
fileNameToModuleName(importedFilePath: string, containingFilePath: string): string { return ''; }
getImportAs(symbol: StaticSymbol): StaticSymbol { return null; } getImportAs(symbol: StaticSymbol): StaticSymbol { return null; }
getTypeArity(symbol: StaticSymbol): number /*|null*/ { return null; } getTypeArity(symbol: StaticSymbol): number /*|null*/ { return null; }
} }
export function main() {
// Not supported features of our OutputAst in TS:
// - real `const` like in Dart
// - final fields
describe('TypeScriptEmitter', () => {
let importResolver: ImportResolver;
let emitter: TypeScriptEmitter;
let someVar: o.ReadVarExpr;
beforeEach(() => {
importResolver = new SimpleJsImportGenerator();
emitter = new TypeScriptEmitter(importResolver);
someVar = o.variable('someVar');
});
function emitSourceMap(
stmt: o.Statement | o.Statement[], exportedVars: string[] = null): SourceMap {
const stmts = Array.isArray(stmt) ? stmt : [stmt];
const source = emitter.emitStatements(someModuleUrl, stmts, exportedVars || []);
return extractSourceMap(source);
}
describe('source maps', () => {
it('should emit an inline source map', () => {
const source = new ParseSourceFile(';;;var', 'in.js');
const startLocation = new ParseLocation(source, 0, 0, 3);
const endLocation = new ParseLocation(source, 7, 0, 6);
const sourceSpan = new ParseSourceSpan(startLocation, endLocation);
const someVar = o.variable('someVar', null, sourceSpan);
const sm = emitSourceMap(someVar.toStmt());
const smc = new SourceMapConsumer(sm);
expect(sm.sources).toEqual(['in.js']);
expect(sm.sourcesContent).toEqual([';;;var']);
expect(smc.originalPositionFor({line: 1, column: 0}))
.toEqual({line: 1, column: 3, source: 'in.js', name: null});
});
});
});
}

View File

@ -12,6 +12,8 @@ import * as o from '@angular/compiler/src/output/output_ast';
import {ImportResolver} from '@angular/compiler/src/output/path_util'; import {ImportResolver} from '@angular/compiler/src/output/path_util';
import {TypeScriptEmitter} from '@angular/compiler/src/output/ts_emitter'; import {TypeScriptEmitter} from '@angular/compiler/src/output/ts_emitter';
import {stripSourceMap} from './abstract_emitter_spec';
const someModuleUrl = 'somePackage/somePath'; const someModuleUrl = 'somePackage/somePath';
const anotherModuleUrl = 'somePackage/someOtherPath'; const anotherModuleUrl = 'somePackage/someOtherPath';
@ -48,11 +50,9 @@ export function main() {
}); });
function emitStmt(stmt: o.Statement | o.Statement[], exportedVars: string[] = null): string { function emitStmt(stmt: o.Statement | o.Statement[], exportedVars: string[] = null): string {
if (!exportedVars) {
exportedVars = [];
}
const stmts = Array.isArray(stmt) ? stmt : [stmt]; const stmts = Array.isArray(stmt) ? stmt : [stmt];
return emitter.emitStatements(someModuleUrl, stmts, exportedVars); const source = emitter.emitStatements(someModuleUrl, stmts, exportedVars || []);
return stripSourceMap(source);
} }
it('should declare variables', () => { it('should declare variables', () => {

View File

@ -6,7 +6,6 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import * as testUtil from 'e2e_util/e2e_util';
import {$, browser} from 'protractor'; import {$, browser} from 'protractor';
import {logging} from 'selenium-webdriver'; import {logging} from 'selenium-webdriver';

View File

@ -5847,7 +5847,7 @@
"dev": true "dev": true
}, },
"source-map": { "source-map": {
"version": "0.3.0", "version": "0.5.6",
"dev": true "dev": true
}, },
"source-map-support": { "source-map-support": {

6
npm-shrinkwrap.json generated
View File

@ -8549,9 +8549,9 @@
"dev": true "dev": true
}, },
"source-map": { "source-map": {
"version": "0.3.0", "version": "0.5.6",
"from": "source-map@>=0.3.0 <0.4.0", "from": "source-map@latest",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.3.0.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz",
"dev": true "dev": true
}, },
"source-map-support": { "source-map-support": {

View File

@ -83,7 +83,7 @@
"rollup-plugin-commonjs": "^5.0.5", "rollup-plugin-commonjs": "^5.0.5",
"selenium-webdriver": "^2.53.3", "selenium-webdriver": "^2.53.3",
"semver": "^5.1.0", "semver": "^5.1.0",
"source-map": "^0.3.0", "source-map": "^0.5.6",
"source-map-support": "^0.4.2", "source-map-support": "^0.4.2",
"systemjs": "0.18.10", "systemjs": "0.18.10",
"ts-api-guardian": "^0.2.0", "ts-api-guardian": "^0.2.0",