Skip to content

Commit

Permalink
refactor(compiler): Support extracting deps functions for defer in …
Browse files Browse the repository at this point in the history
…template pipeline

Some `defer` blocks have external dependencies on other components or directives. These dependencies need to be extracted into deps functions, which either return local deps, or use a dynamic import for non-local deps. Template Pipeline can now generate these functions.
  • Loading branch information
dylhunn committed Nov 1, 2023
1 parent 5ab69d2 commit 0403ef3
Show file tree
Hide file tree
Showing 11 changed files with 96 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,7 @@
],
"failureMessage": "Incorrect template"
}
],
"skipForTemplatePipeline": true
]
},
{
"description": "should generate a deferred block with external dependencies",
Expand All @@ -105,8 +104,7 @@
],
"failureMessage": "Incorrect template"
}
],
"skipForTemplatePipeline": true
]
},
{
"description": "should generate a deferred block with triggers",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {EagerDep} from './deferred_with_external_deps_eager';
import {LoadingDep} from './deferred_with_external_deps_loading';
import * as $r3$ from "@angular/core";

const MyApp_Defer_4_DepsFn = () => [import("./deferred_with_external_deps_lazy").then(m => m.LazyDep)];
const $MyApp_Defer_4_DepsFn$ = () => [import("./deferred_with_external_deps_lazy").then(m => m.LazyDep)];

function MyApp_Defer_2_Template(rf, ctx) {
if (rf & 1) {
Expand All @@ -23,7 +23,7 @@ MyApp.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({
$r3$.ɵɵelementStart(0, "div");
$r3$.ɵɵelement(1, "eager-dep");
$r3$.ɵɵtemplate(2, MyApp_Defer_2_Template, 1, 0)(3, MyApp_DeferLoading_3_Template, 1, 0);
$r3$.ɵɵdefer(4, 2, MyApp_Defer_4_DepsFn, 3);
$r3$.ɵɵdefer(4, 2, $MyApp_Defer_4_DepsFn$, 3);
$r3$.ɵɵdeferOnIdle();
$r3$.ɵɵelementEnd();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const MyApp_Defer_4_DepsFn = () => [LazyDep];
const $MyApp_Defer_4_DepsFn$ = () => [LazyDep];
MyApp.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({
Expand All @@ -7,7 +7,7 @@ MyApp.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({
$r3$.ɵɵelementStart(0, "div");
$r3$.ɵɵelement(1, "eager-dep");
$r3$.ɵɵtemplate(2, MyApp_Defer_2_Template, 1, 0)(3, MyApp_DeferLoading_3_Template, 1, 0);
$r3$.ɵɵdefer(4, 2, MyApp_Defer_4_DepsFn, 3);
$r3$.ɵɵdefer(4, 2, $MyApp_Defer_4_DepsFn$, 3);
$r3$.ɵɵdeferOnIdle();
$r3$.ɵɵelementEnd();
}
Expand Down
2 changes: 1 addition & 1 deletion packages/compiler/src/render3/view/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ export function compileComponentFromMetadata(
// ingested into IR:
const tpl = ingestComponent(
meta.name, meta.template.nodes, constantPool, meta.relativeContextFilePath,
meta.i18nUseExternalIds);
meta.i18nUseExternalIds, meta.deferBlocks);

// Then the IR is transformed to prepare it for cod egeneration.
transform(tpl, CompilationJobKind.Tmpl);
Expand Down
17 changes: 16 additions & 1 deletion packages/compiler/src/template/pipeline/ir/src/ops/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import * as i18n from '../../../../../i18n/i18n_ast';
import * as o from '../../../../../output/output_ast';
import {ParseSourceSpan} from '../../../../../parse_util';
import {R3DeferBlockMetadata} from '../../../../../render3/view/api';
import {BindingKind, DeferTriggerKind, I18nParamValueFlags, Namespace, OpKind} from '../enums';
import {SlotHandle} from '../handle';
import {Op, OpList, XrefId} from '../operations';
Expand Down Expand Up @@ -688,11 +689,23 @@ export interface DeferOp extends Op<CreateOp>, ConsumesSlotOpTrait {
placeholderConfig: o.Expression|null;
loadingConfig: o.Expression|null;

/**
* Metadata about this defer block, provided by the parser.
*/
metadata: R3DeferBlockMetadata;

/**
* After processing, the resolver function for the defer deps will be extracted to the constant
* pool, and a reference to that function will be populated here.
*/
resolverFn: o.Expression|null;

sourceSpan: ParseSourceSpan;
}

export function createDeferOp(
xref: XrefId, main: XrefId, mainSlot: SlotHandle, sourceSpan: ParseSourceSpan): DeferOp {
xref: XrefId, main: XrefId, mainSlot: SlotHandle, metadata: R3DeferBlockMetadata,
sourceSpan: ParseSourceSpan): DeferOp {
return {
kind: OpKind.Defer,
xref,
Expand All @@ -710,6 +723,8 @@ export function createDeferOp(
placeholderMinimumTime: null,
errorView: null,
errorSlot: null,
metadata,
resolverFn: null,
sourceSpan,
...NEW_OP,
...TRAIT_CONSUMES_SLOT,
Expand Down
5 changes: 4 additions & 1 deletion packages/compiler/src/template/pipeline/src/compilation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

import {ConstantPool} from '../../../constant_pool';
import * as o from '../../../output/output_ast';
import * as t from '../../../render3/r3_ast';
import {R3DeferBlockMetadata} from '../../../render3/view/api';
import * as ir from '../ir';

export enum CompilationJobKind {
Expand Down Expand Up @@ -64,7 +66,8 @@ export abstract class CompilationJob {
export class ComponentCompilationJob extends CompilationJob {
constructor(
componentName: string, pool: ConstantPool, compatibility: ir.CompatibilityMode,
readonly relativeContextFilePath: string, readonly i18nUseExternalIds: boolean) {
readonly relativeContextFilePath: string, readonly i18nUseExternalIds: boolean,
readonly deferBlocksMeta: Map<t.DeferredBlock, R3DeferBlockMetadata>) {
super(componentName, pool, compatibility);
this.root = new ViewCompilationUnit(this, this.allocateXrefId(), null);
this.views.set(this.root.xref, this.root);
Expand Down
2 changes: 2 additions & 0 deletions packages/compiler/src/template/pipeline/src/emit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {chain} from './phases/chaining';
import {collapseSingletonInterpolations} from './phases/collapse_singleton_interpolations';
import {generateConditionalExpressions} from './phases/conditionals';
import {collectElementConsts} from './phases/const_collection';
import {createDeferDepsFns} from './phases/create_defer_deps_fns';
import {configureDeferInstructions} from './phases/defer_configs';
import {resolveDeferTargetNames} from './phases/defer_resolve_targets';
import {collapseEmptyInstructions} from './phases/empty_elements';
Expand Down Expand Up @@ -121,6 +122,7 @@ const phases: Phase[] = [
{kind: Kind.Both, fn: expandSafeReads},
{kind: Kind.Both, fn: generateTemporaryVariables},
{kind: Kind.Tmpl, fn: allocateSlots},
{kind: Kind.Tmpl, fn: createDeferDepsFns},
{kind: Kind.Tmpl, fn: extractI18nMessages},
{kind: Kind.Tmpl, fn: resolveI18nElementPlaceholders},
{kind: Kind.Tmpl, fn: resolveI18nExpressionPlaceholders},
Expand Down
16 changes: 12 additions & 4 deletions packages/compiler/src/template/pipeline/src/ingest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {splitNsName} from '../../../ml_parser/tags';
import * as o from '../../../output/output_ast';
import {ParseSourceSpan} from '../../../parse_util';
import * as t from '../../../render3/r3_ast';
import {R3DeferBlockMetadata} from '../../../render3/view/api';
import {BindingParser} from '../../../template_parser/binding_parser';
import * as ir from '../ir';

Expand All @@ -29,9 +30,11 @@ const compatibilityMode = ir.CompatibilityMode.TemplateDefinitionBuilder;
*/
export function ingestComponent(
componentName: string, template: t.Node[], constantPool: ConstantPool,
relativeContextFilePath: string, i18nUseExternalIds: boolean): ComponentCompilationJob {
relativeContextFilePath: string, i18nUseExternalIds: boolean,
deferBlocksMeta: Map<t.DeferredBlock, R3DeferBlockMetadata>): ComponentCompilationJob {
const job = new ComponentCompilationJob(
componentName, constantPool, compatibilityMode, relativeContextFilePath, i18nUseExternalIds);
componentName, constantPool, compatibilityMode, relativeContextFilePath, i18nUseExternalIds,
deferBlocksMeta);
ingestNodes(job.root, template);
return job;
}
Expand Down Expand Up @@ -361,6 +364,11 @@ function ingestDeferView(
}

function ingestDeferBlock(unit: ViewCompilationUnit, deferBlock: t.DeferredBlock): void {
const blockMeta = unit.job.deferBlocksMeta.get(deferBlock);
if (blockMeta === undefined) {
throw new Error(`AssertionError: unable to find metadata for deferred block`)
}

// Generate the defer main view and all secondary views.
const main = ingestDeferView(unit, '', deferBlock.children, deferBlock.sourceSpan)!;
const loading = ingestDeferView(
Expand All @@ -372,7 +380,8 @@ function ingestDeferBlock(unit: ViewCompilationUnit, deferBlock: t.DeferredBlock

// Create the main defer op, and ops for all secondary views.
const deferXref = unit.job.allocateXrefId();
const deferOp = ir.createDeferOp(deferXref, main.xref, main.handle, deferBlock.sourceSpan);
const deferOp =
ir.createDeferOp(deferXref, main.xref, main.handle, blockMeta, deferBlock.sourceSpan);
deferOp.placeholderView = placeholder?.xref ?? null;
deferOp.placeholderSlot = placeholder?.handle ?? null;
deferOp.loadingSlot = loading?.handle ?? null;
Expand All @@ -383,7 +392,6 @@ function ingestDeferBlock(unit: ViewCompilationUnit, deferBlock: t.DeferredBlock
unit.create.push(deferOp);

// Configure all defer `on` conditions.

// TODO: refactor prefetch triggers to use a separate op type, with a shared superclass. This will
// make it easier to refactor prefetch behavior in the future.
let prefetch = false;
Expand Down
10 changes: 5 additions & 5 deletions packages/compiler/src/template/pipeline/src/instruction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,14 +192,14 @@ export function text(
}

export function defer(
selfSlot: number, primarySlot: number, dependencyResolverFn: null, loadingSlot: number|null,
placeholderSlot: number|null, errorSlot: number|null, loadingConfig: o.Expression|null,
placeholderConfig: o.Expression|null, enableTimerScheduling: boolean,
sourceSpan: ParseSourceSpan|null): ir.CreateOp {
selfSlot: number, primarySlot: number, dependencyResolverFn: o.Expression|null,
loadingSlot: number|null, placeholderSlot: number|null, errorSlot: number|null,
loadingConfig: o.Expression|null, placeholderConfig: o.Expression|null,
enableTimerScheduling: boolean, sourceSpan: ParseSourceSpan|null): ir.CreateOp {
const args: Array<o.Expression> = [
o.literal(selfSlot),
o.literal(primarySlot),
o.literal(dependencyResolverFn),
dependencyResolverFn ?? o.literal(null),
o.literal(loadingSlot),
o.literal(placeholderSlot),
o.literal(errorSlot),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* @license
* Copyright Google LLC 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 * as o from '../../../../output/output_ast';
import * as ir from '../../ir';
import {ComponentCompilationJob} from '../compilation';

/**
* Create extracted deps functions for defer ops.
*/
export function createDeferDepsFns(job: ComponentCompilationJob): void {
for (const unit of job.units) {
for (const op of unit.create) {
if (op.kind === ir.OpKind.Defer) {
if (op.metadata.deps.length === 0) {
continue;
}
const dependencies: o.Expression[] = [];
for (const dep of op.metadata.deps) {
if (dep.isDeferrable) {
// Callback function, e.g. `m () => m.MyCmp;`.
const innerFn = o.arrowFn(
[new o.FnParam('m', o.DYNAMIC_TYPE)], o.variable('m').prop(dep.symbolName));

// Dynamic import, e.g. `import('./a').then(...)`.
const importExpr =
(new o.DynamicImportExpr(dep.importPath!)).prop('then').callFn([innerFn]);
dependencies.push(importExpr);
} else {
// Non-deferrable symbol, just use a reference to the type.
dependencies.push(dep.type);
}
}
const depsFnExpr = o.arrowFn([], o.literalArr(dependencies));
if (op.handle.slot === null) {
throw new Error(
'AssertionError: slot must be assigned bfore extracting defer deps functions');
}
op.resolverFn = job.pool.getSharedFunctionReference(
depsFnExpr, `${job.componentName}_Defer_${op.handle.slot}_DepsFn`);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ function reifyCreateOperations(unit: CompilationUnit, ops: ir.OpList<ir.CreateOp
ir.OpList.replace(
op,
ng.defer(
op.handle.slot!, op.mainSlot.slot!, null, op.loadingSlot?.slot ?? null,
op.handle.slot!, op.mainSlot.slot!, op.resolverFn, op.loadingSlot?.slot ?? null,
op.placeholderSlot?.slot! ?? null, op.errorSlot?.slot ?? null, op.loadingConfig,
op.placeholderConfig, timerScheduling, op.sourceSpan));
break;
Expand Down

0 comments on commit 0403ef3

Please sign in to comment.