Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ivy): produce Renderer2 back-patching and factories. #22506

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 9 additions & 6 deletions packages/compiler/src/aot/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
}
});

Expand Down
106 changes: 106 additions & 0 deletions packages/compiler/src/render3/r3_back_patch_compiler.ts
Original file line number Diff line number Diff line change
@@ -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]));
}
8 changes: 1 addition & 7 deletions packages/compiler/src/render3/r3_identifiers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
46 changes: 46 additions & 0 deletions packages/compiler/src/render3/r3_module_factory_compiler.ts
Original file line number Diff line number Diff line change
@@ -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
]));
}
44 changes: 31 additions & 13 deletions packages/compiler/src/render3/r3_pipe_compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand All @@ -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 changes: 25 additions & 0 deletions packages/compiler/src/render3/r3_types.ts
Original file line number Diff line number Diff line change
@@ -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__';