fix(ivy): add names to function expressions (#21714)

PR Close #21714
This commit is contained in:
Chuck Jazdzewski 2018-01-22 15:35:18 -08:00 committed by Miško Hevery
parent f11b2fb00b
commit 8baff1858b
6 changed files with 63 additions and 43 deletions

View File

@ -30,10 +30,10 @@ export class TypeScriptNodeEmitter {
const commentStmt = this.createCommentStatement(sourceFile, preamble); const commentStmt = this.createCommentStatement(sourceFile, preamble);
preambleStmts.push(commentStmt); preambleStmts.push(commentStmt);
} }
const sourceStatments = const sourceStatements =
[...preambleStmts, ...converter.getReexports(), ...converter.getImports(), ...statements]; [...preambleStmts, ...converter.getReexports(), ...converter.getImports(), ...statements];
converter.updateSourceMap(sourceStatments); converter.updateSourceMap(sourceStatements);
const newSourceFile = ts.updateSourceFileNode(sourceFile, sourceStatments); const newSourceFile = ts.updateSourceFileNode(sourceFile, sourceStatements);
return [newSourceFile, converter.getNodeMap()]; return [newSourceFile, converter.getNodeMap()];
} }
@ -143,7 +143,7 @@ function firstAfter<T>(a: T[], predicate: (value: T) => boolean) {
return index; return index;
} }
// A recorded node is a subtype of the node that is marked as being recoreded. This is used // A recorded node is a subtype of the node that is marked as being recorded. This is used
// to ensure that NodeEmitterVisitor.record has been called on all nodes returned by the // to ensure that NodeEmitterVisitor.record has been called on all nodes returned by the
// NodeEmitterVisitor // NodeEmitterVisitor
type RecordedNode<T extends ts.Node = ts.Node> = (T & { __recorded: any; }) | null; type RecordedNode<T extends ts.Node = ts.Node> = (T & { __recorded: any; }) | null;
@ -498,7 +498,8 @@ class _NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor {
visitFunctionExpr(expr: FunctionExpr) { visitFunctionExpr(expr: FunctionExpr) {
return this.record( return this.record(
expr, ts.createFunctionExpression( expr, ts.createFunctionExpression(
/* modifiers */ undefined, /* astriskToken */ undefined, /* name */ undefined, /* modifiers */ undefined, /* astriskToken */ undefined,
/* name */ expr.name || undefined,
/* typeParameters */ undefined, /* typeParameters */ undefined,
expr.params.map( expr.params.map(
p => ts.createParameter( p => ts.createParameter(

View File

@ -107,7 +107,7 @@ export abstract class AbstractJsEmitterVisitor extends AbstractEmitterVisitor {
return null; return null;
} }
visitFunctionExpr(ast: o.FunctionExpr, ctx: EmitterVisitorContext): any { visitFunctionExpr(ast: o.FunctionExpr, ctx: EmitterVisitorContext): any {
ctx.print(ast, `function(`); ctx.print(ast, `function${ast.name ? ' ' + ast.name : ''}(`);
this._visitParams(ast.params, ctx); this._visitParams(ast.params, ctx);
ctx.println(ast, `) {`); ctx.println(ast, `) {`);
ctx.incIndent(); ctx.incIndent();

View File

@ -41,7 +41,7 @@ export class BuiltinType extends Type {
super(modifiers); super(modifiers);
} }
visitType(visitor: TypeVisitor, context: any): any { visitType(visitor: TypeVisitor, context: any): any {
return visitor.visitBuiltintType(this, context); return visitor.visitBuiltinType(this, context);
} }
} }
@ -79,7 +79,7 @@ export const STRING_TYPE = new BuiltinType(BuiltinTypeName.String);
export const FUNCTION_TYPE = new BuiltinType(BuiltinTypeName.Function); export const FUNCTION_TYPE = new BuiltinType(BuiltinTypeName.Function);
export interface TypeVisitor { export interface TypeVisitor {
visitBuiltintType(type: BuiltinType, context: any): any; visitBuiltinType(type: BuiltinType, context: any): any;
visitExpressionType(type: ExpressionType, context: any): any; visitExpressionType(type: ExpressionType, context: any): any;
visitArrayType(type: ArrayType, context: any): any; visitArrayType(type: ArrayType, context: any): any;
visitMapType(type: MapType, context: any): any; visitMapType(type: MapType, context: any): any;
@ -487,7 +487,7 @@ export class FnParam {
export class FunctionExpr extends Expression { export class FunctionExpr extends Expression {
constructor( constructor(
public params: FnParam[], public statements: Statement[], type?: Type|null, public params: FnParam[], public statements: Statement[], type?: Type|null,
sourceSpan?: ParseSourceSpan|null) { sourceSpan?: ParseSourceSpan|null, public name?: string|null) {
super(type, sourceSpan); super(type, sourceSpan);
} }
isEquivalent(e: Expression): boolean { isEquivalent(e: Expression): boolean {
@ -1088,7 +1088,7 @@ export class RecursiveAstVisitor implements StatementVisitor, ExpressionVisitor
} }
return ast; return ast;
} }
visitBuiltintType(type: BuiltinType, context: any): any { return this.visitType(type, context); } visitBuiltinType(type: BuiltinType, context: any): any { return this.visitType(type, context); }
visitExpressionType(type: ExpressionType, context: any): any { visitExpressionType(type: ExpressionType, context: any): any {
type.value.visitExpression(this, context); type.value.visitExpression(this, context);
return this.visitType(type, context); return this.visitType(type, context);
@ -1369,9 +1369,9 @@ export function assertNotNull(
} }
export function fn( export function fn(
params: FnParam[], body: Statement[], type?: Type | null, params: FnParam[], body: Statement[], type?: Type | null, sourceSpan?: ParseSourceSpan | null,
sourceSpan?: ParseSourceSpan | null): FunctionExpr { name?: string | null): FunctionExpr {
return new FunctionExpr(params, body, type, sourceSpan); return new FunctionExpr(params, body, type, sourceSpan, name);
} }
export function ifStmt(condition: Expression, thenClause: Statement[], elseClause?: Statement[]) { export function ifStmt(condition: Expression, thenClause: Statement[], elseClause?: Statement[]) {

View File

@ -269,15 +269,23 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
} }
visitFunctionExpr(ast: o.FunctionExpr, ctx: EmitterVisitorContext): any { visitFunctionExpr(ast: o.FunctionExpr, ctx: EmitterVisitorContext): any {
if (ast.name) {
ctx.print(ast, 'function ');
ctx.print(ast, ast.name);
}
ctx.print(ast, `(`); ctx.print(ast, `(`);
this._visitParams(ast.params, ctx); this._visitParams(ast.params, ctx);
ctx.print(ast, `)`); ctx.print(ast, `)`);
this._printColonType(ast.type, ctx, 'void'); this._printColonType(ast.type, ctx, 'void');
ctx.println(ast, ` => {`); if (!ast.name) {
ctx.print(ast, ` => `);
}
ctx.println(ast, '{');
ctx.incIndent(); ctx.incIndent();
this.visitAllStatements(ast.statements, ctx); this.visitAllStatements(ast.statements, ctx);
ctx.decIndent(); ctx.decIndent();
ctx.print(ast, `}`); ctx.print(ast, `}`);
return null; return null;
} }
@ -314,7 +322,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
return null; return null;
} }
visitBuiltintType(type: o.BuiltinType, ctx: EmitterVisitorContext): any { visitBuiltinType(type: o.BuiltinType, ctx: EmitterVisitorContext): any {
let typeStr: string; let typeStr: string;
switch (type.name) { switch (type.name) {
case o.BuiltinTypeName.Bool: case o.BuiltinTypeName.Bool:

View File

@ -20,8 +20,6 @@ import {OutputContext, error} from '../util';
import {Identifiers as R3} from './r3_identifiers'; import {Identifiers as R3} from './r3_identifiers';
/** Name of the context parameter passed into a template function */ /** Name of the context parameter passed into a template function */
const CONTEXT_NAME = 'ctx'; const CONTEXT_NAME = 'ctx';
@ -89,14 +87,17 @@ export function compileComponent(
} }
} }
// e.g. `factory: () => new MyApp(injectElementRef())` // e.g. `factory: function MyApp_Factory() { return new MyApp(injectElementRef()); }`
const templateFactory = createFactory(component.type, outputCtx, reflector); const templateFactory = createFactory(component.type, outputCtx, reflector);
definitionMapValues.push({key: 'factory', value: templateFactory, quoted: false}); definitionMapValues.push({key: 'factory', value: templateFactory, quoted: false});
// e.g. `template: function(_ctx, _cm) {...}` // e.g. `template: function MyComponent_Template(_ctx, _cm) {...}`
const templateTypeName = component.type.reference.name;
const templateName = templateTypeName ? `${templateTypeName}_Template` : null;
const templateFunctionExpression = const templateFunctionExpression =
new TemplateDefinitionBuilder( new TemplateDefinitionBuilder(
outputCtx, outputCtx.constantPool, CONTEXT_NAME, ROOT_SCOPE.nestedScope()) outputCtx, outputCtx.constantPool, reflector, CONTEXT_NAME, ROOT_SCOPE.nestedScope(), 0,
templateTypeName, templateName)
.buildTemplateFunction(template); .buildTemplateFunction(template);
definitionMapValues.push({key: 'template', value: templateFunctionExpression, quoted: false}); definitionMapValues.push({key: 'template', value: templateFunctionExpression, quoted: false});
@ -218,7 +219,9 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor, LocalResolver {
constructor( constructor(
private outputCtx: OutputContext, private constantPool: ConstantPool, private outputCtx: OutputContext, private constantPool: ConstantPool,
private contextParameter: string, private bindingScope: BindingScope, private level = 0) {} private reflector: CompileReflector, private contextParameter: string,
private bindingScope: BindingScope, private level = 0, private contextName: string|null,
private templateName: string|null) {}
buildTemplateFunction(asts: TemplateAst[]): o.FunctionExpr { buildTemplateFunction(asts: TemplateAst[]): o.FunctionExpr {
templateVisitAll(this, asts); templateVisitAll(this, asts);
@ -246,7 +249,7 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor, LocalResolver {
// Nested templates (i.e. function CompTemplate() {}) // Nested templates (i.e. function CompTemplate() {})
...this._postfix ...this._postfix
], ],
o.INFERRED_TYPE); o.INFERRED_TYPE, null, this.templateName);
} }
getLocal(name: string): o.Expression|null { return this.bindingScope.get(name); } getLocal(name: string): o.Expression|null { return this.bindingScope.get(name); }
@ -407,7 +410,17 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor, LocalResolver {
visitEmbeddedTemplate(ast: EmbeddedTemplateAst) { visitEmbeddedTemplate(ast: EmbeddedTemplateAst) {
const templateIndex = this.allocateDataSlot(); const templateIndex = this.allocateDataSlot();
const templateName = `C${templateIndex}Template`; const templateRef = this.reflector.resolveExternalReference(Identifiers.TemplateRef);
const templateDirective = ast.directives.find(
directive => directive.directive.type.diDeps.some(
dependency =>
dependency.token != null && (tokenReference(dependency.token) == templateRef)));
const contextName =
this.contextName && templateDirective && templateDirective.directive.type.reference.name ?
`${this.contextName}_${templateDirective.directive.type.reference.name}` :
null;
const templateName =
contextName ? `${contextName}_Template_${templateIndex}` : `Template_${templateIndex}`;
const templateContext = `ctx${this.level}`; const templateContext = `ctx${this.level}`;
const {directivesArray, directiveIndexMap} = this._computeDirectivesArray(ast.directives); const {directivesArray, directiveIndexMap} = this._computeDirectivesArray(ast.directives);
@ -430,8 +443,8 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor, LocalResolver {
// Create the template function // Create the template function
const templateVisitor = new TemplateDefinitionBuilder( const templateVisitor = new TemplateDefinitionBuilder(
this.outputCtx, this.constantPool, templateContext, this.bindingScope.nestedScope(), this.outputCtx, this.constantPool, this.reflector, templateContext,
this.level + 1); this.bindingScope.nestedScope(), this.level + 1, contextName, templateName);
const templateFunctionExpr = templateVisitor.buildTemplateFunction(ast.children); const templateFunctionExpr = templateVisitor.buildTemplateFunction(ast.children);
this._postfix.push(templateFunctionExpr.toDeclStmt(templateName, null)); this._postfix.push(templateFunctionExpr.toDeclStmt(templateName, null));
} }
@ -539,7 +552,7 @@ function createFactory(
return o.fn( return o.fn(
[], [],
[new o.ReturnStatement(new o.InstantiateExpr(outputCtx.importExpr(type.reference), args))], [new o.ReturnStatement(new o.InstantiateExpr(outputCtx.importExpr(type.reference), args))],
o.INFERRED_TYPE); o.INFERRED_TYPE, null, type.reference.name ? `${type.reference.name}_Factory` : null);
} }
function invalid<T>(arg: o.Expression | o.Statement | TemplateAst): never { function invalid<T>(arg: o.Expression | o.Statement | TemplateAst): never {

View File

@ -124,11 +124,11 @@ describe('r3_view_compiler', () => {
}; };
// The factory should look like this: // The factory should look like this:
const factory = 'factory: () => { return new MyComponent(); }'; const factory = 'factory: function MyComponent_Factory() { return new MyComponent(); }';
// The template should look like this (where IDENT is a wild card for an identifier): // The template should look like this (where IDENT is a wild card for an identifier):
const template = ` const template = `
template: (ctx: IDENT, cm: IDENT) => { template: function MyComponent_Template(ctx: IDENT, cm: IDENT) {
if (cm) { if (cm) {
IDENT.ɵE(0, 'div', IDENT); IDENT.ɵE(0, 'div', IDENT);
IDENT.ɵT(1, 'Hello '); IDENT.ɵT(1, 'Hello ');
@ -179,8 +179,8 @@ describe('r3_view_compiler', () => {
const ChildComponentDefinition = ` const ChildComponentDefinition = `
static ngComponentDef = IDENT.ɵdefineComponent({ static ngComponentDef = IDENT.ɵdefineComponent({
tag: 'child', tag: 'child',
factory: () => { return new ChildComponent(); }, factory: function ChildComponent_Factory() { return new ChildComponent(); },
template: (ctx: IDENT, cm: IDENT) => { template: function ChildComponent_Template(ctx: IDENT, cm: IDENT) {
if (cm) { if (cm) {
IDENT.ɵT(0, 'child-view'); IDENT.ɵT(0, 'child-view');
} }
@ -190,7 +190,7 @@ describe('r3_view_compiler', () => {
// SomeDirective definition should be: // SomeDirective definition should be:
const SomeDirectiveDefinition = ` const SomeDirectiveDefinition = `
static ngDirectiveDef = IDENT.ɵdefineDirective({ static ngDirectiveDef = IDENT.ɵdefineDirective({
factory: () => {return new SomeDirective(); } factory: function SomeDirective_Factory() {return new SomeDirective(); }
}); });
`; `;
@ -198,8 +198,8 @@ describe('r3_view_compiler', () => {
const MyComponentDefinition = ` const MyComponentDefinition = `
static ngComponentDef = IDENT.ɵdefineComponent({ static ngComponentDef = IDENT.ɵdefineComponent({
tag: 'my-component', tag: 'my-component',
factory: () => { return new MyComponent(); }, factory: function MyComponent_Factory() { return new MyComponent(); },
template: (ctx: IDENT, cm: IDENT) => { template: function MyComponent_Template(ctx: IDENT, cm: IDENT) {
if (cm) { if (cm) {
IDENT.ɵE(0, ChildComponent, IDENT, IDENT); IDENT.ɵE(0, ChildComponent, IDENT, IDENT);
IDENT.ɵe(); IDENT.ɵe();
@ -253,16 +253,16 @@ describe('r3_view_compiler', () => {
const IfDirectiveDefinition = ` const IfDirectiveDefinition = `
static ngDirectiveDef = IDENT.ɵdefineDirective({ static ngDirectiveDef = IDENT.ɵdefineDirective({
factory: () => { return new IfDirective(IDENT.ɵinjectTemplateRef()); } factory: function IfDirective_Factory() { return new IfDirective(IDENT.ɵinjectTemplateRef()); }
});`; });`;
const MyComponentDefinition = ` const MyComponentDefinition = `
static ngComponentDef = IDENT.ɵdefineComponent({ static ngComponentDef = IDENT.ɵdefineComponent({
tag: 'my-component', tag: 'my-component',
factory: () => { return new MyComponent(); }, factory: function MyComponent_Factory() { return new MyComponent(); },
template: (ctx: IDENT, cm: IDENT) => { template: function MyComponent_Template(ctx: IDENT, cm: IDENT) {
if (cm) { if (cm) {
IDENT.ɵE(0, 'ul', null, null, IDENT); IDENT.ɵE(0, 'ul', null, null, IDENT);
IDENT.ɵC(2, IDENT, C2Template); IDENT.ɵC(2, IDENT, MyComponent_IfDirective_Template_2);
IDENT.ɵe(); IDENT.ɵe();
} }
const IDENT = IDENT.ɵm(1); const IDENT = IDENT.ɵm(1);
@ -270,7 +270,7 @@ describe('r3_view_compiler', () => {
IfDirective.ngDirectiveDef.r(3,2); IfDirective.ngDirectiveDef.r(3,2);
IDENT.ɵcr(); IDENT.ɵcr();
function C2Template(ctx0: IDENT, cm: IDENT) { function MyComponent_IfDirective_Template_2(ctx0: IDENT, cm: IDENT) {
if (cm) { if (cm) {
IDENT.ɵE(0, 'li'); IDENT.ɵE(0, 'li');
IDENT.ɵT(1); IDENT.ɵT(1);
@ -310,8 +310,8 @@ describe('r3_view_compiler', () => {
const MyComponentDefinition = ` const MyComponentDefinition = `
static ngComponentDef = IDENT.ɵdefineComponent({ static ngComponentDef = IDENT.ɵdefineComponent({
tag: 'my-component', tag: 'my-component',
factory: () => { return new MyComponent(); }, factory: function MyComponent_Factory() { return new MyComponent(); },
template: (ctx: IDENT, cm: IDENT) => { template: function MyComponent_Template(ctx: IDENT, cm: IDENT) {
if (cm) { if (cm) {
IDENT.ɵE(0, 'input', null, null, IDENT); IDENT.ɵE(0, 'input', null, null, IDENT);
IDENT.ɵe(); IDENT.ɵe();
@ -323,9 +323,7 @@ describe('r3_view_compiler', () => {
}); });
`; `;
const locals = ` const locals = `const IDENT = ['user', ''];`;
const IDENT = ['user', ''];
`;
const result = compile(files, angularFiles); const result = compile(files, angularFiles);
const source = result.source; const source = result.source;