refactor(ivy): move directive, component and pipe factories to ngFactoryFn (#31953)

Reworks the compiler to output the factories for directives, components and pipes under a new static field called `ngFactoryFn`, instead of the usual `factory` property in their respective defs. This should eventually allow us to inject any kind of decorated class (e.g. a pipe).

**Note:** these changes are the first part of the refactor and they don't include injectables. I decided to leave injectables for a follow-up PR, because there's some more cases we need to handle when it comes to their factories. Furthermore, directives, components and pipes make up most of the compiler output tests that need to be refactored and it'll make follow-up PRs easier to review if the tests are cleaned up now.

This is part of the larger refactor for FW-1468.

PR Close #31953
This commit is contained in:
Kristiyan Kostadinov
2019-08-12 09:26:20 +03:00
committed by atscott
parent 14feb56139
commit c885178d5f
71 changed files with 894 additions and 675 deletions

View File

@ -95,7 +95,7 @@ export {BoundAttribute as TmplAstBoundAttribute, BoundEvent as TmplAstBoundEvent
export * from './render3/view/t2_api';
export * from './render3/view/t2_binder';
export {Identifiers as R3Identifiers} from './render3/r3_identifiers';
export {R3DependencyMetadata, R3FactoryMetadata, R3ResolvedDependencyType} from './render3/r3_factory';
export {R3DependencyMetadata, R3FactoryDefMetadata, R3ResolvedDependencyType, compileFactoryFromMetadata, R3FactoryMetadata} from './render3/r3_factory';
export {compileInjector, compileNgModule, R3InjectorMetadata, R3NgModuleMetadata} from './render3/r3_module_compiler';
export {compilePipeFromMetadata, R3PipeMetadata} from './render3/r3_pipe_compiler';
export {makeBindingParser, parseTemplate, ParseTemplateOptions} from './render3/view/template';
@ -106,4 +106,4 @@ export {publishFacade} from './jit_compiler_facade';
// This function call has a global side effects and publishes the compiler into global namespace for
// the late binding of the Compiler to the @angular/core for jit compilation.
publishFacade(global);
publishFacade(global);

View File

@ -39,6 +39,10 @@ export interface CompilerFacade {
angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3ComponentMetadataFacade): any;
compileBase(angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3BaseMetadataFacade):
any;
compileFactory(
angularCoreEnv: CoreEnvironment, sourceMapUrl: string,
meta: R3PipeMetadataFacade|R3DirectiveMetadataFacade|R3ComponentMetadataFacade,
isPipe?: boolean): any;
createParseSourceSpan(kind: string, typeName: string, sourceUrl: string): ParseSourceSpan;

View File

@ -37,6 +37,7 @@ export function compileInjectable(meta: R3InjectableMetadata): InjectableDef {
const factoryMeta = {
name: meta.name,
type: meta.type,
typeArgumentCount: meta.typeArgumentCount,
deps: meta.ctorDeps,
injectFn: Identifiers.inject,
};

View File

@ -15,7 +15,7 @@ import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from './ml_parser/int
import {DeclareVarStmt, Expression, LiteralExpr, Statement, StmtModifier, WrappedNodeExpr} from './output/output_ast';
import {JitEvaluator} from './output/output_jit';
import {ParseError, ParseSourceSpan, r3JitTypeSourceSpan} from './parse_util';
import {R3DependencyMetadata, R3ResolvedDependencyType} from './render3/r3_factory';
import {R3DependencyMetadata, R3ResolvedDependencyType, compileFactoryFromMetadata} from './render3/r3_factory';
import {R3JitReflector} from './render3/r3_jit';
import {R3InjectorMetadata, R3NgModuleMetadata, compileInjector, compileNgModule} from './render3/r3_module_compiler';
import {compilePipeFromMetadata} from './render3/r3_pipe_compiler';
@ -35,15 +35,16 @@ export class CompilerFacadeImpl implements CompilerFacade {
compilePipe(angularCoreEnv: CoreEnvironment, sourceMapUrl: string, facade: R3PipeMetadataFacade):
any {
const res = compilePipeFromMetadata({
const metadata = {
name: facade.name,
type: new WrappedNodeExpr(facade.type),
typeArgumentCount: facade.typeArgumentCount,
deps: convertR3DependencyMetadataArray(facade.deps),
pipeName: facade.pipeName,
pure: facade.pure,
});
return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, res.statements);
};
const res = compilePipeFromMetadata(metadata);
return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []);
}
compileInjectable(
@ -105,8 +106,8 @@ export class CompilerFacadeImpl implements CompilerFacade {
const meta: R3DirectiveMetadata = convertDirectiveFacadeToMetadata(facade);
const res = compileDirectiveFromMetadata(meta, constantPool, bindingParser);
const preStatements = [...constantPool.statements, ...res.statements];
return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, preStatements);
return this.jitExpression(
res.expression, angularCoreEnv, sourceMapUrl, constantPool.statements);
}
compileComponent(
@ -129,27 +130,41 @@ export class CompilerFacadeImpl implements CompilerFacade {
// Compile the component metadata, including template, into an expression.
// TODO(alxhub): implement inputs, outputs, queries, etc.
const metadata = {
...facade as R3ComponentMetadataFacadeNoPropAndWhitespace,
...convertDirectiveFacadeToMetadata(facade),
selector: facade.selector || this.elementSchemaRegistry.getDefaultComponentElementName(),
template,
wrapDirectivesAndPipesInClosure: false,
styles: facade.styles || [],
encapsulation: facade.encapsulation as any,
interpolation: interpolationConfig,
changeDetection: facade.changeDetection,
animations: facade.animations != null ? new WrappedNodeExpr(facade.animations) : null,
viewProviders: facade.viewProviders != null ? new WrappedNodeExpr(facade.viewProviders) :
null,
relativeContextFilePath: '',
i18nUseExternalIds: true,
};
const res = compileComponentFromMetadata(
{
...facade as R3ComponentMetadataFacadeNoPropAndWhitespace,
...convertDirectiveFacadeToMetadata(facade),
selector: facade.selector || this.elementSchemaRegistry.getDefaultComponentElementName(),
template,
wrapDirectivesAndPipesInClosure: false,
styles: facade.styles || [],
encapsulation: facade.encapsulation as any,
interpolation: interpolationConfig,
changeDetection: facade.changeDetection,
animations: facade.animations != null ? new WrappedNodeExpr(facade.animations) : null,
viewProviders: facade.viewProviders != null ? new WrappedNodeExpr(facade.viewProviders) :
null,
relativeContextFilePath: '',
i18nUseExternalIds: true,
},
constantPool, makeBindingParser(interpolationConfig));
const preStatements = [...constantPool.statements, ...res.statements];
metadata, constantPool, makeBindingParser(interpolationConfig));
const jitExpressionSourceMap = `ng:///${facade.name}.js`;
return this.jitExpression(
res.expression, angularCoreEnv, `ng:///${facade.name}.js`, preStatements);
res.expression, angularCoreEnv, jitExpressionSourceMap, constantPool.statements);
}
compileFactory(
angularCoreEnv: CoreEnvironment, sourceMapUrl: string,
meta: R3PipeMetadataFacade|R3DirectiveMetadataFacade|R3ComponentMetadataFacade,
isPipe = false) {
const factoryRes = compileFactoryFromMetadata({
name: meta.name,
type: new WrappedNodeExpr(meta.type),
typeArgumentCount: meta.typeArgumentCount,
deps: convertR3DependencyMetadataArray(meta.deps), isPipe
});
return this.jitExpression(
factoryRes.factory, angularCoreEnv, sourceMapUrl, factoryRes.statements);
}
compileBase(angularCoreEnv: CoreEnvironment, sourceMapUrl: string, facade: R3BaseMetadataFacade):

View File

@ -15,9 +15,11 @@ import * as o from '../output/output_ast';
import {Identifiers as R3} from '../render3/r3_identifiers';
import {OutputContext} from '../util';
import {typeWithParameters} from './util';
import {unsupported} from './view/util';
/**
* Metadata required by the factory generator to generate a `factory` function for a type.
*/
@ -36,6 +38,9 @@ export interface R3ConstructorFactoryMetadata {
*/
type: o.Expression;
/** Number of arguments for the `type`. */
typeArgumentCount: number;
/**
* Regardless of whether `fnOrClass` is a constructor function or a user-defined factory, it
* may have 0 or more parameters, which will be injected according to the `R3DependencyMetadata`
@ -77,6 +82,14 @@ export interface R3ExpressionFactoryMetadata extends R3ConstructorFactoryMetadat
export type R3FactoryMetadata = R3ConstructorFactoryMetadata | R3DelegatedFactoryMetadata |
R3DelegatedFnOrClassMetadata | R3ExpressionFactoryMetadata;
export interface R3FactoryDefMetadata {
name: string;
type: o.Expression;
typeArgumentCount: number;
deps: R3DependencyMetadata[]|null;
isPipe?: boolean;
}
/**
* Resolved type of a dependency.
*
@ -140,11 +153,16 @@ export interface R3DependencyMetadata {
skipSelf: boolean;
}
export interface R3FactoryFn {
factory: o.Expression;
statements: o.Statement[];
type: o.ExpressionType;
}
/**
* Construct a factory function expression for the given `R3FactoryMetadata`.
*/
export function compileFactoryFunction(
meta: R3FactoryMetadata, isPipe = false): {factory: o.Expression, statements: o.Statement[]} {
export function compileFactoryFunction(meta: R3FactoryMetadata, isPipe = false): R3FactoryFn {
const t = o.variable('t');
const statements: o.Statement[] = [];
@ -234,9 +252,27 @@ export function compileFactoryFunction(
[new o.FnParam('t', o.DYNAMIC_TYPE)], body, o.INFERRED_TYPE, undefined,
`${meta.name}_Factory`),
statements,
type: o.expressionType(
o.importExpr(R3.FactoryDef, [typeWithParameters(meta.type, meta.typeArgumentCount)]))
};
}
/**
* Constructs the `ngFactoryDef` from directive/component/pipe metadata.
*/
export function compileFactoryFromMetadata(meta: R3FactoryDefMetadata): R3FactoryFn {
return compileFactoryFunction(
{
name: meta.name,
type: meta.type,
deps: meta.deps,
typeArgumentCount: meta.typeArgumentCount,
// TODO(crisbeto): this should be refactored once we start using it for injectables.
injectFn: R3.directiveInject,
},
meta.isPipe);
}
function injectDependencies(
deps: R3DependencyMetadata[], injectFn: o.ExternalReference, isPipe: boolean): o.Expression[] {
return deps.map(dep => compileInjectDependency(dep, injectFn, isPipe));

View File

@ -241,6 +241,11 @@ export class Identifiers {
moduleName: CORE,
};
static FactoryDef: o.ExternalReference = {
name: 'ɵɵFactoryDef',
moduleName: CORE,
};
static defineDirective: o.ExternalReference = {
name: 'ɵɵdefineDirective',
moduleName: CORE,

View File

@ -207,6 +207,7 @@ export function compileInjector(meta: R3InjectorMetadata): R3InjectorDef {
const result = compileFactoryFunction({
name: meta.name,
type: meta.type,
typeArgumentCount: 0,
deps: meta.deps,
injectFn: R3.inject,
});

View File

@ -12,7 +12,7 @@ import {DefinitionKind} from '../constant_pool';
import * as o from '../output/output_ast';
import {OutputContext, error} from '../util';
import {R3DependencyMetadata, compileFactoryFunction, dependenciesFromGlobalMetadata} from './r3_factory';
import {R3DependencyMetadata, compileFactoryFromMetadata, compileFactoryFunction, dependenciesFromGlobalMetadata} from './r3_factory';
import {Identifiers as R3} from './r3_identifiers';
import {typeWithParameters} from './util';
@ -57,16 +57,6 @@ export function compilePipeFromMetadata(metadata: R3PipeMetadata) {
// e.g. `type: MyPipe`
definitionMapValues.push({key: 'type', value: metadata.type, quoted: false});
const templateFactory = compileFactoryFunction(
{
name: metadata.name,
type: metadata.type,
deps: metadata.deps,
injectFn: R3.directiveInject,
},
true);
definitionMapValues.push({key: 'factory', value: templateFactory.factory, quoted: false});
// e.g. `pure: true`
definitionMapValues.push({key: 'pure', value: o.literal(metadata.pure), quoted: false});
@ -75,7 +65,8 @@ export function compilePipeFromMetadata(metadata: R3PipeMetadata) {
typeWithParameters(metadata.type, metadata.typeArgumentCount),
new o.ExpressionType(new o.LiteralExpr(metadata.pipeName)),
]));
return {expression, type, statements: templateFactory.statements};
return {expression, type};
}
/**
@ -83,9 +74,8 @@ export function compilePipeFromMetadata(metadata: R3PipeMetadata) {
*/
export function compilePipeFromRender2(
outputCtx: OutputContext, pipe: CompilePipeMetadata, reflector: CompileReflector) {
const definitionMapValues: {key: string, quoted: boolean, value: o.Expression}[] = [];
const name = identifierName(pipe.type);
if (!name) {
return error(`Cannot resolve the name of ${pipe.type}`);
}
@ -98,12 +88,22 @@ export function compilePipeFromRender2(
deps: dependenciesFromGlobalMetadata(pipe.type, outputCtx, reflector),
pure: pipe.pure,
};
const res = compilePipeFromMetadata(metadata);
const factoryRes = compileFactoryFromMetadata({...metadata, isPipe: true});
const definitionField = outputCtx.constantPool.propertyNameOf(DefinitionKind.Pipe);
outputCtx.statements.push(new o.ClassStmt(
const ngFactoryDefStatement = new o.ClassStmt(
/* name */ name,
/* parent */ null,
/* fields */
[new o.ClassField(
/* name */ 'ngFactoryDef',
/* type */ o.INFERRED_TYPE,
/* modifiers */[o.StmtModifier.Static],
/* initializer */ factoryRes.factory)],
/* getters */[],
/* constructorMethod */ new o.ClassMethod(null, [], []),
/* methods */[]);
const pipeDefStatement = new o.ClassStmt(
/* name */ name,
/* parent */ null,
/* fields */[new o.ClassField(
@ -113,5 +113,7 @@ export function compilePipeFromRender2(
/* initializer */ res.expression)],
/* getters */[],
/* constructorMethod */ new o.ClassMethod(null, [], []),
/* methods */[]));
/* methods */[]);
outputCtx.statements.push(ngFactoryDefStatement, pipeDefStatement);
}

View File

@ -237,7 +237,6 @@ export interface R3QueryMetadata {
export interface R3DirectiveDef {
expression: o.Expression;
type: o.Type;
statements: o.Statement[];
}
/**
@ -246,7 +245,6 @@ export interface R3DirectiveDef {
export interface R3ComponentDef {
expression: o.Expression;
type: o.Type;
statements: o.Statement[];
}
/**

View File

@ -22,7 +22,7 @@ import {CONTENT_ATTR, HOST_ATTR} from '../../style_compiler';
import {BindingParser} from '../../template_parser/binding_parser';
import {OutputContext, error} from '../../util';
import {BoundEvent} from '../r3_ast';
import {compileFactoryFunction, dependenciesFromGlobalMetadata} from '../r3_factory';
import {compileFactoryFromMetadata, compileFactoryFunction, dependenciesFromGlobalMetadata} from '../r3_factory';
import {Identifiers as R3} from '../r3_identifiers';
import {Render3ParseResult} from '../r3_template_transform';
import {prepareSyntheticListenerFunctionName, prepareSyntheticPropertyName, typeWithParameters} from '../util';
@ -44,7 +44,7 @@ function getStylingPrefix(name: string): string {
function baseDirectiveFields(
meta: R3DirectiveMetadata, constantPool: ConstantPool,
bindingParser: BindingParser): {definitionMap: DefinitionMap, statements: o.Statement[]} {
bindingParser: BindingParser): DefinitionMap {
const definitionMap = new DefinitionMap();
// e.g. `type: MyDirective`
@ -53,16 +53,6 @@ function baseDirectiveFields(
// e.g. `selectors: [['', 'someDir', '']]`
definitionMap.set('selectors', createDirectiveSelector(meta.selector));
// e.g. `factory: () => new MyApp(directiveInject(ElementRef))`
const result = compileFactoryFunction({
name: meta.name,
type: meta.type,
deps: meta.deps,
injectFn: R3.directiveInject,
});
definitionMap.set('factory', result.factory);
if (meta.queries.length > 0) {
// e.g. `contentQueries: (rf, ctx, dirIndex) => { ... }
definitionMap.set(
@ -90,7 +80,7 @@ function baseDirectiveFields(
definitionMap.set('exportAs', o.literalArr(meta.exportAs.map(e => o.literal(e))));
}
return {definitionMap, statements: result.statements};
return definitionMap;
}
/**
@ -128,12 +118,12 @@ function addFeatures(
export function compileDirectiveFromMetadata(
meta: R3DirectiveMetadata, constantPool: ConstantPool,
bindingParser: BindingParser): R3DirectiveDef {
const {definitionMap, statements} = baseDirectiveFields(meta, constantPool, bindingParser);
const definitionMap = baseDirectiveFields(meta, constantPool, bindingParser);
addFeatures(definitionMap, meta);
const expression = o.importExpr(R3.defineDirective).callFn([definitionMap.toLiteralMap()]);
const type = createTypeForDef(meta, R3.DirectiveDefWithMeta);
return {expression, type, statements};
return {expression, type};
}
export interface R3BaseRefMetaData {
@ -197,7 +187,7 @@ export function compileBaseDefFromMetadata(
export function compileComponentFromMetadata(
meta: R3ComponentMetadata, constantPool: ConstantPool,
bindingParser: BindingParser): R3ComponentDef {
const {definitionMap, statements} = baseDirectiveFields(meta, constantPool, bindingParser);
const definitionMap = baseDirectiveFields(meta, constantPool, bindingParser);
addFeatures(definitionMap, meta);
const selector = meta.selector && CssSelector.parse(meta.selector);
@ -315,7 +305,7 @@ export function compileComponentFromMetadata(
const expression = o.importExpr(R3.defineComponent).callFn([definitionMap.toLiteralMap()]);
const type = createTypeForDef(meta, R3.ComponentDefWithMeta);
return {expression, type, statements};
return {expression, type};
}
/**
@ -335,12 +325,19 @@ export function compileDirectiveFromRender2(
const meta = directiveMetadataFromGlobalMetadata(directive, outputCtx, reflector);
const res = compileDirectiveFromMetadata(meta, outputCtx.constantPool, bindingParser);
// Create the partial class to be merged with the actual class.
outputCtx.statements.push(new o.ClassStmt(
const factoryRes = compileFactoryFromMetadata(meta);
const ngFactoryDefStatement = new o.ClassStmt(
name, null,
[new o.ClassField(
'ngFactoryDef', o.INFERRED_TYPE, [o.StmtModifier.Static], factoryRes.factory)],
[], new o.ClassMethod(null, [], []), []);
const directiveDefStatement = new o.ClassStmt(
name, null,
[new o.ClassField(definitionField, o.INFERRED_TYPE, [o.StmtModifier.Static], res.expression)],
[], new o.ClassMethod(null, [], []), []));
[], new o.ClassMethod(null, [], []), []);
// Create the partial class to be merged with the actual class.
outputCtx.statements.push(ngFactoryDefStatement, directiveDefStatement);
}
/**
@ -381,12 +378,19 @@ export function compileComponentFromRender2(
i18nUseExternalIds: true,
};
const res = compileComponentFromMetadata(meta, outputCtx.constantPool, bindingParser);
// Create the partial class to be merged with the actual class.
outputCtx.statements.push(new o.ClassStmt(
const factoryRes = compileFactoryFromMetadata(meta);
const ngFactoryDefStatement = new o.ClassStmt(
name, null,
[new o.ClassField(
'ngFactoryDef', o.INFERRED_TYPE, [o.StmtModifier.Static], factoryRes.factory)],
[], new o.ClassMethod(null, [], []), []);
const componentDefStatement = new o.ClassStmt(
name, null,
[new o.ClassField(definitionField, o.INFERRED_TYPE, [o.StmtModifier.Static], res.expression)],
[], new o.ClassMethod(null, [], []), []));
[], new o.ClassMethod(null, [], []), []);
// Create the partial class to be merged with the actual class.
outputCtx.statements.push(ngFactoryDefStatement, componentDefStatement);
}
/**