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