168 lines
5.4 KiB
TypeScript
168 lines
5.4 KiB
TypeScript
/**
|
|
* @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 {isPresent} from '../facade/lang';
|
|
|
|
import {AbstractEmitterVisitor, CATCH_ERROR_VAR, CATCH_STACK_VAR, EmitterVisitorContext} from './abstract_emitter';
|
|
import * as o from './output_ast';
|
|
|
|
export abstract class AbstractJsEmitterVisitor extends AbstractEmitterVisitor {
|
|
constructor() { super(false); }
|
|
visitDeclareClassStmt(stmt: o.ClassStmt, ctx: EmitterVisitorContext): any {
|
|
ctx.pushClass(stmt);
|
|
this._visitClassConstructor(stmt, ctx);
|
|
|
|
if (isPresent(stmt.parent)) {
|
|
ctx.print(`${stmt.name}.prototype = Object.create(`);
|
|
stmt.parent.visitExpression(this, ctx);
|
|
ctx.println(`.prototype);`);
|
|
}
|
|
stmt.getters.forEach((getter) => this._visitClassGetter(stmt, getter, ctx));
|
|
stmt.methods.forEach((method) => this._visitClassMethod(stmt, method, ctx));
|
|
ctx.popClass();
|
|
return null;
|
|
}
|
|
|
|
private _visitClassConstructor(stmt: o.ClassStmt, ctx: EmitterVisitorContext) {
|
|
ctx.print(`function ${stmt.name}(`);
|
|
if (isPresent(stmt.constructorMethod)) {
|
|
this._visitParams(stmt.constructorMethod.params, ctx);
|
|
}
|
|
ctx.println(`) {`);
|
|
ctx.incIndent();
|
|
if (isPresent(stmt.constructorMethod)) {
|
|
if (stmt.constructorMethod.body.length > 0) {
|
|
ctx.println(`var self = this;`);
|
|
this.visitAllStatements(stmt.constructorMethod.body, ctx);
|
|
}
|
|
}
|
|
ctx.decIndent();
|
|
ctx.println(`}`);
|
|
}
|
|
|
|
private _visitClassGetter(stmt: o.ClassStmt, getter: o.ClassGetter, ctx: EmitterVisitorContext) {
|
|
ctx.println(
|
|
`Object.defineProperty(${stmt.name}.prototype, '${getter.name}', { get: function() {`);
|
|
ctx.incIndent();
|
|
if (getter.body.length > 0) {
|
|
ctx.println(`var self = this;`);
|
|
this.visitAllStatements(getter.body, ctx);
|
|
}
|
|
ctx.decIndent();
|
|
ctx.println(`}});`);
|
|
}
|
|
|
|
private _visitClassMethod(stmt: o.ClassStmt, method: o.ClassMethod, ctx: EmitterVisitorContext) {
|
|
ctx.print(`${stmt.name}.prototype.${method.name} = function(`);
|
|
this._visitParams(method.params, ctx);
|
|
ctx.println(`) {`);
|
|
ctx.incIndent();
|
|
if (method.body.length > 0) {
|
|
ctx.println(`var self = this;`);
|
|
this.visitAllStatements(method.body, ctx);
|
|
}
|
|
ctx.decIndent();
|
|
ctx.println(`};`);
|
|
}
|
|
|
|
visitReadVarExpr(ast: o.ReadVarExpr, ctx: EmitterVisitorContext): string {
|
|
if (ast.builtin === o.BuiltinVar.This) {
|
|
ctx.print('self');
|
|
} else if (ast.builtin === o.BuiltinVar.Super) {
|
|
throw new Error(
|
|
`'super' needs to be handled at a parent ast node, not at the variable level!`);
|
|
} else {
|
|
super.visitReadVarExpr(ast, ctx);
|
|
}
|
|
return null;
|
|
}
|
|
visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any {
|
|
ctx.print(`var ${stmt.name} = `);
|
|
stmt.value.visitExpression(this, ctx);
|
|
ctx.println(`;`);
|
|
return null;
|
|
}
|
|
visitCastExpr(ast: o.CastExpr, ctx: EmitterVisitorContext): any {
|
|
ast.value.visitExpression(this, ctx);
|
|
return null;
|
|
}
|
|
visitInvokeFunctionExpr(expr: o.InvokeFunctionExpr, ctx: EmitterVisitorContext): string {
|
|
var fnExpr = expr.fn;
|
|
if (fnExpr instanceof o.ReadVarExpr && fnExpr.builtin === o.BuiltinVar.Super) {
|
|
ctx.currentClass.parent.visitExpression(this, ctx);
|
|
ctx.print(`.call(this`);
|
|
if (expr.args.length > 0) {
|
|
ctx.print(`, `);
|
|
this.visitAllExpressions(expr.args, ctx, ',');
|
|
}
|
|
ctx.print(`)`);
|
|
} else {
|
|
super.visitInvokeFunctionExpr(expr, ctx);
|
|
}
|
|
return null;
|
|
}
|
|
visitFunctionExpr(ast: o.FunctionExpr, ctx: EmitterVisitorContext): any {
|
|
ctx.print(`function(`);
|
|
this._visitParams(ast.params, ctx);
|
|
ctx.println(`) {`);
|
|
ctx.incIndent();
|
|
this.visitAllStatements(ast.statements, ctx);
|
|
ctx.decIndent();
|
|
ctx.print(`}`);
|
|
return null;
|
|
}
|
|
visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, ctx: EmitterVisitorContext): any {
|
|
ctx.print(`function ${stmt.name}(`);
|
|
this._visitParams(stmt.params, ctx);
|
|
ctx.println(`) {`);
|
|
ctx.incIndent();
|
|
this.visitAllStatements(stmt.statements, ctx);
|
|
ctx.decIndent();
|
|
ctx.println(`}`);
|
|
return null;
|
|
}
|
|
visitTryCatchStmt(stmt: o.TryCatchStmt, ctx: EmitterVisitorContext): any {
|
|
ctx.println(`try {`);
|
|
ctx.incIndent();
|
|
this.visitAllStatements(stmt.bodyStmts, ctx);
|
|
ctx.decIndent();
|
|
ctx.println(`} catch (${CATCH_ERROR_VAR.name}) {`);
|
|
ctx.incIndent();
|
|
var catchStmts =
|
|
[<o.Statement>CATCH_STACK_VAR.set(CATCH_ERROR_VAR.prop('stack')).toDeclStmt(null, [
|
|
o.StmtModifier.Final
|
|
])].concat(stmt.catchStmts);
|
|
this.visitAllStatements(catchStmts, ctx);
|
|
ctx.decIndent();
|
|
ctx.println(`}`);
|
|
return null;
|
|
}
|
|
|
|
private _visitParams(params: o.FnParam[], ctx: EmitterVisitorContext): void {
|
|
this.visitAllObjects(param => ctx.print(param.name), params, ctx, ',');
|
|
}
|
|
|
|
getBuiltinMethodName(method: o.BuiltinMethod): string {
|
|
var name: string;
|
|
switch (method) {
|
|
case o.BuiltinMethod.ConcatArray:
|
|
name = 'concat';
|
|
break;
|
|
case o.BuiltinMethod.SubscribeObservable:
|
|
name = 'subscribe';
|
|
break;
|
|
case o.BuiltinMethod.Bind:
|
|
name = 'bind';
|
|
break;
|
|
default:
|
|
throw new Error(`Unknown builtin method: ${method}`);
|
|
}
|
|
return name;
|
|
}
|
|
}
|