feat(ivy): produce Renderer2 back-patching and factories. (#22506)
Produces back-patch as described in the #22235 and referenced in #22480. This just contains the compiler implementations and the corresponding unit tests. Connecting the dots as described in #22480 will be in a follow on change. PR Close #22506
This commit is contained in:

committed by
Kara Erickson

parent
5412e10bcd
commit
b0b9ca3386
@ -21,6 +21,7 @@ import {OutputEmitter} from '../output/abstract_emitter';
|
||||
import * as o from '../output/output_ast';
|
||||
import {ParseError} from '../parse_util';
|
||||
import {compilePipe as compileIvyPipe} from '../render3/r3_pipe_compiler';
|
||||
import {OutputMode} from '../render3/r3_types';
|
||||
import {compileComponent as compileIvyComponent, compileDirective as compileIvyDirective} from '../render3/r3_view_compiler';
|
||||
import {CompiledStylesheet, StyleCompiler} from '../style_compiler';
|
||||
import {SummaryResolver} from '../summary_resolver';
|
||||
@ -170,7 +171,7 @@ export class AotCompiler {
|
||||
_createEmptyStub(outputCtx);
|
||||
}
|
||||
// Note: for the stubs, we don't need a property srcFileUrl,
|
||||
// as lateron in emitAllImpls we will create the proper GeneratedFiles with the
|
||||
// as later on in emitAllImpls we will create the proper GeneratedFiles with the
|
||||
// correct srcFileUrl.
|
||||
// This is good as e.g. for .ngstyle.ts files we can't derive
|
||||
// the url of components based on the genFileUrl.
|
||||
@ -223,7 +224,7 @@ export class AotCompiler {
|
||||
let componentId = 0;
|
||||
file.ngModules.forEach((ngModuleMeta, ngModuleIndex) => {
|
||||
// Note: the code below needs to executed for StubEmitFlags.Basic and StubEmitFlags.TypeCheck,
|
||||
// so we don't change the .ngfactory file too much when adding the typecheck block.
|
||||
// so we don't change the .ngfactory file too much when adding the type-check block.
|
||||
|
||||
// create exports that user code can reference
|
||||
this._ngModuleCompiler.createStub(outputCtx, ngModuleMeta.type.reference);
|
||||
@ -256,7 +257,7 @@ export class AotCompiler {
|
||||
});
|
||||
|
||||
if (emitFlags & StubEmitFlags.TypeCheck) {
|
||||
// add the typecheck block for all components of the NgModule
|
||||
// add the type-check block for all components of the NgModule
|
||||
ngModuleMeta.declaredDirectives.forEach((dirId) => {
|
||||
const compMeta = this._metadataResolver.getDirectiveMetadata(dirId.reference);
|
||||
if (!compMeta.isComponent) {
|
||||
@ -366,16 +367,18 @@ export class AotCompiler {
|
||||
this._parseTemplate(directiveMetadata, module, module.transitiveModule.directives);
|
||||
compileIvyComponent(
|
||||
context, directiveMetadata, parsedPipes, parsedTemplate, this._reflector,
|
||||
hostBindingParser);
|
||||
hostBindingParser, OutputMode.PartialClass);
|
||||
} else {
|
||||
compileIvyDirective(context, directiveMetadata, this._reflector, hostBindingParser);
|
||||
compileIvyDirective(
|
||||
context, directiveMetadata, this._reflector, hostBindingParser,
|
||||
OutputMode.PartialClass);
|
||||
}
|
||||
});
|
||||
|
||||
pipes.forEach(pipeType => {
|
||||
const pipeMetadata = this._metadataResolver.getPipeMetadata(pipeType);
|
||||
if (pipeMetadata) {
|
||||
compileIvyPipe(context, pipeMetadata, this._reflector);
|
||||
compileIvyPipe(context, pipeMetadata, this._reflector, OutputMode.PartialClass);
|
||||
}
|
||||
});
|
||||
|
||||
|
106
packages/compiler/src/render3/r3_back_patch_compiler.ts
Normal file
106
packages/compiler/src/render3/r3_back_patch_compiler.ts
Normal file
@ -0,0 +1,106 @@
|
||||
/**
|
||||
* @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 {StaticReflector} from '../aot/static_reflector';
|
||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeSummary, CompileTypeMetadata} from '../compile_metadata';
|
||||
import {DEFAULT_INTERPOLATION_CONFIG, DomElementSchemaRegistry, Lexer, ParseError, Parser} from '../compiler';
|
||||
import {CompileMetadataResolver} from '../metadata_resolver';
|
||||
import * as o from '../output/output_ast';
|
||||
import {BindingParser} from '../template_parser/binding_parser';
|
||||
import {TemplateAst} from '../template_parser/template_ast';
|
||||
import {OutputContext} from '../util';
|
||||
|
||||
import {compilePipe} from './r3_pipe_compiler';
|
||||
import {BUILD_OPTIMIZER_REMOVE, OutputMode} from './r3_types';
|
||||
import {compileComponent, compileDirective} from './r3_view_compiler';
|
||||
|
||||
export const enum ModuleKind {
|
||||
Renderer2,
|
||||
Renderer3,
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce the back-patching function for the given module to the output context.
|
||||
*/
|
||||
export function compileModuleBackPatch(
|
||||
outputCtx: OutputContext, name: string, module: CompileNgModuleMetadata, kind: ModuleKind,
|
||||
backPatchReferenceOf: (module: CompileTypeMetadata) => o.Expression,
|
||||
parseTemplate: (
|
||||
compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata,
|
||||
directiveIdentifiers: CompileIdentifierMetadata[]) => {
|
||||
template: TemplateAst[],
|
||||
pipes: CompilePipeSummary[]
|
||||
},
|
||||
reflector: StaticReflector, resolver: CompileMetadataResolver) {
|
||||
const imports: o.Statement[] = [];
|
||||
let statements: o.Statement[] = [];
|
||||
|
||||
// Call dependent back patching
|
||||
for (const importedModule of module.importedModules) {
|
||||
const importBackPatchFunction = backPatchReferenceOf(importedModule.type);
|
||||
|
||||
// e.g. // @BUILD_OPTIMIZER_REMOVE
|
||||
imports.push(new o.CommentStmt(BUILD_OPTIMIZER_REMOVE));
|
||||
|
||||
// e.g. ngBackPatch_some_other_module_Module();
|
||||
imports.push(importBackPatchFunction.callFn([]).toStmt());
|
||||
}
|
||||
|
||||
// The local output context allows collecting the back-patch statements that
|
||||
// are generated by the various compilers which allows putting placing them
|
||||
// into the body of a function instead of at global scope.
|
||||
const localCtx: OutputContext = {
|
||||
statements,
|
||||
constantPool: outputCtx.constantPool,
|
||||
genFilePath: outputCtx.genFilePath,
|
||||
importExpr: outputCtx.importExpr
|
||||
};
|
||||
|
||||
// e.g. export function ngBackPatch_some_module_Lib1Module()
|
||||
if (kind === ModuleKind.Renderer2) {
|
||||
// For all Renderer2 modules generate back-patching code for all the components, directives,
|
||||
// pipes, and injectables as well as the injector def for the module itself.
|
||||
|
||||
const expressionParser = new Parser(new Lexer());
|
||||
const elementSchemaRegistry = new DomElementSchemaRegistry();
|
||||
const errors: ParseError[] = [];
|
||||
const hostBindingParser = new BindingParser(
|
||||
expressionParser, DEFAULT_INTERPOLATION_CONFIG, elementSchemaRegistry, [], errors);
|
||||
|
||||
// Back-patch all declared directive and components
|
||||
for (const declaredDirective of module.declaredDirectives) {
|
||||
const declaredDirectiveMetadata = resolver.getDirectiveMetadata(declaredDirective.reference);
|
||||
if (declaredDirectiveMetadata.isComponent) {
|
||||
const {template: parsedTemplate, pipes: parsedPipes} =
|
||||
parseTemplate(declaredDirectiveMetadata, module, module.transitiveModule.directives);
|
||||
compileComponent(
|
||||
localCtx, declaredDirectiveMetadata, parsedPipes, parsedTemplate, reflector,
|
||||
hostBindingParser, OutputMode.BackPatch);
|
||||
} else {
|
||||
compileDirective(
|
||||
localCtx, declaredDirectiveMetadata, reflector, hostBindingParser,
|
||||
OutputMode.BackPatch);
|
||||
}
|
||||
}
|
||||
|
||||
// Back-patch all pipes declared in the module.
|
||||
for (const pipeType of module.declaredPipes) {
|
||||
const pipeMetadata = resolver.getPipeMetadata(pipeType.reference);
|
||||
if (pipeMetadata) {
|
||||
compilePipe(localCtx, pipeMetadata, reflector, OutputMode.BackPatch);
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.length) {
|
||||
throw new Error(errors.map(e => e.toString()).join('\n'));
|
||||
}
|
||||
}
|
||||
|
||||
outputCtx.statements.push(new o.DeclareFunctionStmt(
|
||||
name, [], [...imports, ...statements], o.INFERRED_TYPE, [o.StmtModifier.Exported]));
|
||||
}
|
@ -10,18 +10,12 @@ import * as o from '../output/output_ast';
|
||||
|
||||
const CORE = '@angular/core';
|
||||
|
||||
// Copied from core and must be in sync with the value in the runtime.
|
||||
export const enum LifeCycleGuard {ON_INIT = 1, ON_DESTROY = 2, ON_CHANGES = 4}
|
||||
|
||||
// TODO: Include assignments that use the enum literals
|
||||
// e.g. { let a: core.LifeCycleGuard.ON_INIT = LifeCycleGuard.ON_INIT; ...}
|
||||
// Ensure these get removed in bundling.
|
||||
|
||||
export class Identifiers {
|
||||
/* Methods */
|
||||
static NEW_METHOD = 'n';
|
||||
static HOST_BINDING_METHOD = 'h';
|
||||
static TRANSFORM_METHOD = 'transform';
|
||||
static PATCH_DEPS = 'patchedDeps';
|
||||
|
||||
/* Instructions */
|
||||
static createElement: o.ExternalReference = {name: 'ɵE', moduleName: CORE};
|
||||
|
46
packages/compiler/src/render3/r3_module_factory_compiler.ts
Normal file
46
packages/compiler/src/render3/r3_module_factory_compiler.ts
Normal file
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* @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 {StaticReflector} from '../aot/static_reflector';
|
||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeSummary, CompileTypeMetadata, identifierName} from '../compile_metadata';
|
||||
import {CompileMetadataResolver} from '../metadata_resolver';
|
||||
import * as o from '../output/output_ast';
|
||||
import {OutputContext} from '../util';
|
||||
|
||||
import {Identifiers as R3} from './r3_identifiers';
|
||||
|
||||
/**
|
||||
* Write a Renderer2 compatibility module factory to the output context.
|
||||
*/
|
||||
export function compileModuleFactory(
|
||||
outputCtx: OutputContext, module: CompileNgModuleMetadata,
|
||||
backPatchReferenceOf: (module: CompileTypeMetadata) => o.Expression,
|
||||
resolver: CompileMetadataResolver) {
|
||||
const ngModuleFactoryVar = `${identifierName(module.type)}NgFactory`;
|
||||
|
||||
const parentInjector = 'parentInjector';
|
||||
const createFunction = o.fn(
|
||||
[new o.FnParam(parentInjector, o.DYNAMIC_TYPE)],
|
||||
[new o.IfStmt(
|
||||
o.THIS_EXPR.prop(R3.PATCH_DEPS).notIdentical(o.literal(true, o.INFERRED_TYPE)),
|
||||
[
|
||||
o.THIS_EXPR.prop(R3.PATCH_DEPS).set(o.literal(true, o.INFERRED_TYPE)).toStmt(),
|
||||
backPatchReferenceOf(module.type).callFn([]).toStmt()
|
||||
])],
|
||||
o.INFERRED_TYPE, null, `${ngModuleFactoryVar}_Create`);
|
||||
|
||||
const moduleFactoryLiteral = o.literalMap([
|
||||
{key: 'moduleType', value: outputCtx.importExpr(module.type.reference), quoted: false},
|
||||
{key: 'create', value: createFunction, quoted: false}
|
||||
]);
|
||||
|
||||
outputCtx.statements.push(
|
||||
o.variable(ngModuleFactoryVar).set(moduleFactoryLiteral).toDeclStmt(o.DYNAMIC_TYPE, [
|
||||
o.StmtModifier.Exported, o.StmtModifier.Final
|
||||
]));
|
||||
}
|
@ -13,10 +13,15 @@ import * as o from '../output/output_ast';
|
||||
import {OutputContext, error} from '../util';
|
||||
|
||||
import {Identifiers as R3} from './r3_identifiers';
|
||||
import {BUILD_OPTIMIZER_COLOCATE, OutputMode} from './r3_types';
|
||||
import {createFactory} from './r3_view_compiler';
|
||||
|
||||
/**
|
||||
* Write a pipe definition to the output context.
|
||||
*/
|
||||
export function compilePipe(
|
||||
outputCtx: OutputContext, pipe: CompilePipeMetadata, reflector: CompileReflector) {
|
||||
outputCtx: OutputContext, pipe: CompilePipeMetadata, reflector: CompileReflector,
|
||||
mode: OutputMode) {
|
||||
const definitionMapValues: {key: string, quoted: boolean, value: o.Expression}[] = [];
|
||||
|
||||
// e.g. 'type: MyPipe`
|
||||
@ -35,16 +40,29 @@ export function compilePipe(
|
||||
const className = identifierName(pipe.type) !;
|
||||
className || error(`Cannot resolve the name of ${pipe.type}`);
|
||||
|
||||
outputCtx.statements.push(new o.ClassStmt(
|
||||
/* name */ className,
|
||||
/* parent */ null,
|
||||
/* fields */[new o.ClassField(
|
||||
/* name */ outputCtx.constantPool.propertyNameOf(DefinitionKind.Pipe),
|
||||
/* type */ o.INFERRED_TYPE,
|
||||
/* modifiers */[o.StmtModifier.Static],
|
||||
/* initializer */ o.importExpr(R3.definePipe).callFn([o.literalMap(
|
||||
definitionMapValues)]))],
|
||||
/* getters */[],
|
||||
/* constructorMethod */ new o.ClassMethod(null, [], []),
|
||||
/* methods */[]));
|
||||
const definitionField = outputCtx.constantPool.propertyNameOf(DefinitionKind.Pipe);
|
||||
const definitionFunction =
|
||||
o.importExpr(R3.definePipe).callFn([o.literalMap(definitionMapValues)]);
|
||||
|
||||
if (mode === OutputMode.PartialClass) {
|
||||
outputCtx.statements.push(new o.ClassStmt(
|
||||
/* name */ className,
|
||||
/* parent */ null,
|
||||
/* fields */[new o.ClassField(
|
||||
/* name */ definitionField,
|
||||
/* type */ o.INFERRED_TYPE,
|
||||
/* modifiers */[o.StmtModifier.Static],
|
||||
/* initializer */ definitionFunction)],
|
||||
/* getters */[],
|
||||
/* constructorMethod */ new o.ClassMethod(null, [], []),
|
||||
/* methods */[]));
|
||||
} else {
|
||||
// Create back-patch definition.
|
||||
const classReference = outputCtx.importExpr(pipe.type.reference);
|
||||
|
||||
// Create the back-patch statement
|
||||
outputCtx.statements.push(
|
||||
new o.CommentStmt(BUILD_OPTIMIZER_COLOCATE),
|
||||
classReference.prop(definitionField).set(definitionFunction).toStmt());
|
||||
}
|
||||
}
|
25
packages/compiler/src/render3/r3_types.ts
Normal file
25
packages/compiler/src/render3/r3_types.ts
Normal file
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
/**
|
||||
* The statement mode for the render, either as a class back-patch or as a partial class
|
||||
*/
|
||||
export const enum OutputMode {
|
||||
PartialClass,
|
||||
BackPatch,
|
||||
}
|
||||
|
||||
/**
|
||||
* Comment to insert above back-patch
|
||||
*/
|
||||
export const BUILD_OPTIMIZER_COLOCATE = '@__BUILD_OPTIMIZER_COLOCATE__';
|
||||
|
||||
/**
|
||||
* Comment to mark removable expressions
|
||||
*/
|
||||
export const BUILD_OPTIMIZER_REMOVE = '@__BUILD_OPTIMIZER_REMOVE__';
|
@ -21,6 +21,7 @@ import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventA
|
||||
import {OutputContext, error} from '../util';
|
||||
|
||||
import {Identifiers as R3} from './r3_identifiers';
|
||||
import {BUILD_OPTIMIZER_COLOCATE, OutputMode} from './r3_types';
|
||||
|
||||
|
||||
|
||||
@ -41,7 +42,7 @@ const IMPLICIT_REFERENCE = '$implicit';
|
||||
|
||||
export function compileDirective(
|
||||
outputCtx: OutputContext, directive: CompileDirectiveMetadata, reflector: CompileReflector,
|
||||
bindingParser: BindingParser) {
|
||||
bindingParser: BindingParser, mode: OutputMode) {
|
||||
const definitionMapValues: {key: string, quoted: boolean, value: o.Expression}[] = [];
|
||||
|
||||
const field = (key: string, value: o.Expression | null) => {
|
||||
@ -68,24 +69,38 @@ export function compileDirective(
|
||||
const className = identifierName(directive.type) !;
|
||||
className || error(`Cannot resolver the name of ${directive.type}`);
|
||||
|
||||
// Create the partial class to be merged with the actual class.
|
||||
outputCtx.statements.push(new o.ClassStmt(
|
||||
/* name */ className,
|
||||
/* parent */ null,
|
||||
/* fields */[new o.ClassField(
|
||||
/* name */ outputCtx.constantPool.propertyNameOf(DefinitionKind.Directive),
|
||||
/* type */ o.INFERRED_TYPE,
|
||||
/* modifiers */[o.StmtModifier.Static],
|
||||
/* initializer */ o.importExpr(R3.defineDirective).callFn([o.literalMap(
|
||||
definitionMapValues)]))],
|
||||
/* getters */[],
|
||||
/* constructorMethod */ new o.ClassMethod(null, [], []),
|
||||
/* methods */[]));
|
||||
const definitionField = outputCtx.constantPool.propertyNameOf(DefinitionKind.Directive);
|
||||
const definitionFunction =
|
||||
o.importExpr(R3.defineDirective).callFn([o.literalMap(definitionMapValues)]);
|
||||
|
||||
if (mode === OutputMode.PartialClass) {
|
||||
// Create the partial class to be merged with the actual class.
|
||||
outputCtx.statements.push(new o.ClassStmt(
|
||||
/* name */ className,
|
||||
/* parent */ null,
|
||||
/* fields */[new o.ClassField(
|
||||
/* name */ definitionField,
|
||||
/* type */ o.INFERRED_TYPE,
|
||||
/* modifiers */[o.StmtModifier.Static],
|
||||
/* initializer */ definitionFunction)],
|
||||
/* getters */[],
|
||||
/* constructorMethod */ new o.ClassMethod(null, [], []),
|
||||
/* methods */[]));
|
||||
} else {
|
||||
// Create back-patch definition.
|
||||
const classReference = outputCtx.importExpr(directive.type.reference);
|
||||
|
||||
// Create the back-patch statement
|
||||
outputCtx.statements.push(new o.CommentStmt(BUILD_OPTIMIZER_COLOCATE));
|
||||
outputCtx.statements.push(
|
||||
classReference.prop(definitionField).set(definitionFunction).toStmt());
|
||||
}
|
||||
}
|
||||
|
||||
export function compileComponent(
|
||||
outputCtx: OutputContext, component: CompileDirectiveMetadata, pipes: CompilePipeSummary[],
|
||||
template: TemplateAst[], reflector: CompileReflector, bindingParser: BindingParser) {
|
||||
template: TemplateAst[], reflector: CompileReflector, bindingParser: BindingParser,
|
||||
mode: OutputMode) {
|
||||
const definitionMapValues: {key: string, quoted: boolean, value: o.Expression}[] = [];
|
||||
|
||||
const field = (key: string, value: o.Expression | null) => {
|
||||
@ -150,22 +165,33 @@ export function compileComponent(
|
||||
field('features', o.literalArr(features));
|
||||
}
|
||||
|
||||
const className = identifierName(component.type) !;
|
||||
className || error(`Cannot resolver the name of ${component.type}`);
|
||||
const definitionField = outputCtx.constantPool.propertyNameOf(DefinitionKind.Component);
|
||||
const definitionFunction =
|
||||
o.importExpr(R3.defineComponent).callFn([o.literalMap(definitionMapValues)]);
|
||||
if (mode === OutputMode.PartialClass) {
|
||||
const className = identifierName(component.type) !;
|
||||
className || error(`Cannot resolver the name of ${component.type}`);
|
||||
|
||||
// Create the partial class to be merged with the actual class.
|
||||
outputCtx.statements.push(new o.ClassStmt(
|
||||
/* name */ className,
|
||||
/* parent */ null,
|
||||
/* fields */[new o.ClassField(
|
||||
/* name */ outputCtx.constantPool.propertyNameOf(DefinitionKind.Component),
|
||||
/* type */ o.INFERRED_TYPE,
|
||||
/* modifiers */[o.StmtModifier.Static],
|
||||
/* initializer */ o.importExpr(R3.defineComponent).callFn([o.literalMap(
|
||||
definitionMapValues)]))],
|
||||
/* getters */[],
|
||||
/* constructorMethod */ new o.ClassMethod(null, [], []),
|
||||
/* methods */[]));
|
||||
// Create the partial class to be merged with the actual class.
|
||||
outputCtx.statements.push(new o.ClassStmt(
|
||||
/* name */ className,
|
||||
/* parent */ null,
|
||||
/* fields */[new o.ClassField(
|
||||
/* name */ definitionField,
|
||||
/* type */ o.INFERRED_TYPE,
|
||||
/* modifiers */[o.StmtModifier.Static],
|
||||
/* initializer */ definitionFunction)],
|
||||
/* getters */[],
|
||||
/* constructorMethod */ new o.ClassMethod(null, [], []),
|
||||
/* methods */[]));
|
||||
} else {
|
||||
const classReference = outputCtx.importExpr(component.type.reference);
|
||||
|
||||
// Create the back-patch statement
|
||||
outputCtx.statements.push(
|
||||
new o.CommentStmt(BUILD_OPTIMIZER_COLOCATE),
|
||||
classReference.prop(definitionField).set(definitionFunction).toStmt());
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Remove these when the things are fully supported
|
||||
|
Reference in New Issue
Block a user