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