GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — refactor(compiler): Simplify some code in ingest (#53457) · angular/angular@876f90a

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
  • packages/compiler/src/template/pipeline
    • ir/src/ops
      • create.ts
    • src
      • ingest.ts
      • phases
        • attribute_extraction.ts
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎packages/compiler/src/template/pipeline/ir/src/ops/create.ts‎

Copy file name to clipboardExpand all lines: packages/compiler/src/template/pipeline/ir/src/ops/create.ts
+5-2Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -517,15 +517,18 @@ export interface ListenerOp extends Op<CreateOp> {
517517
*/
518518
export function createListenerOp(
519519
target: XrefId, targetSlot: SlotHandle, name: string, tag: string|null,
520-
animationPhase: string|null, hostListener: boolean, sourceSpan: ParseSourceSpan): ListenerOp {
520+
handlerOps: Array<UpdateOp>, animationPhase: string|null, hostListener: boolean,
521+
sourceSpan: ParseSourceSpan): ListenerOp {
522+
const handlerList = new OpList<UpdateOp>();
523+
handlerList.push(handlerOps);
521524
return {
522525
kind: OpKind.Listener,
523526
target,
524527
targetSlot,
525528
tag,
526529
hostListener,
527530
name,
528-
handlerOps: new OpList(),
531+
handlerOps: handlerList,
529532
handlerFnName: null,
530533
consumesDollarEvent: false,
531534
isAnimationListener: animationPhase !== null,
Collapse file

‎packages/compiler/src/template/pipeline/src/ingest.ts‎

Copy file name to clipboardExpand all lines: packages/compiler/src/template/pipeline/src/ingest.ts
+133-128Lines changed: 133 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {icuFromI18nMessage, isSingleI18nIcu} from '../../../render3/view/i18n/ut
1919
import {BindingParser} from '../../../template_parser/binding_parser';
2020
import * as ir from '../ir';
2121

22-
import {ComponentCompilationJob, HostBindingCompilationJob, type CompilationJob, type ViewCompilationUnit} from './compilation';
22+
import {CompilationUnit, ComponentCompilationJob, HostBindingCompilationJob, type CompilationJob, type ViewCompilationUnit} from './compilation';
2323
import {BINARY_OPERATORS, namespaceForKey, prefixWithNamespace} from './conversion';
2424

2525
const compatibilityMode = ir.CompatibilityMode.TemplateDefinitionBuilder;
@@ -108,7 +108,7 @@ export function ingestHostAttribute(
108108

109109
export function ingestHostEvent(job: HostBindingCompilationJob, event: e.ParsedEvent) {
110110
const eventBinding = ir.createListenerOp(
111-
job.root.xref, new ir.SlotHandle(), event.name, null, event.targetOrPhase, true,
111+
job.root.xref, new ir.SlotHandle(), event.name, null, [], event.targetOrPhase, true,
112112
event.sourceSpan);
113113
// TODO: Can this be a chain?
114114
eventBinding.handlerOps.push(ir.createStatementOp(new o.ReturnStatement(
@@ -240,7 +240,7 @@ function ingestTemplate(unit: ViewCompilationUnit, tmpl: t.Template): void {
240240
}
241241

242242
/**
243-
* Ingest a literal text node from the AST into the given `ViewCompilation`.
243+
* Ingest a content node from the AST into the given `ViewCompilation`.
244244
*/
245245
function ingestContent(unit: ViewCompilationUnit, content: t.Content): void {
246246
if (content.i18n !== undefined && !(content.i18n instanceof i18n.TagPlaceholder)) {
@@ -250,9 +250,14 @@ function ingestContent(unit: ViewCompilationUnit, content: t.Content): void {
250250
const op = ir.createProjectionOp(
251251
unit.job.allocateXrefId(), content.selector, content.i18n, attrs, content.sourceSpan);
252252
for (const attr of content.attributes) {
253-
ingestBinding(
253+
let b = createBinding(
254254
unit, op.xref, attr.name, o.literal(attr.value), e.BindingType.Attribute, null,
255-
SecurityContext.NONE, attr.sourceSpan, BindingFlags.TextValue, null, attr.i18n);
255+
SecurityContext.NONE, attr.sourceSpan, BindingFlags.TextValue, null, asMessage(attr.i18n));
256+
if (b?.kind === ir.OpKind.Binding) {
257+
unit.update.push(b);
258+
} else if (b?.kind === ir.OpKind.ExtractedAttribute) {
259+
unit.create.push(b);
260+
}
256261
}
257262
unit.create.push(op);
258263
}
@@ -747,6 +752,55 @@ function convertAst(
747752
}
748753
}
749754

755+
function convertAstWithInterpolation(
756+
job: CompilationJob, value: e.AST|o.Expression, i18nMeta?: i18n.Message): o.Expression|
757+
ir.Interpolation {
758+
let expression: o.Expression|ir.Interpolation;
759+
if (value instanceof e.Interpolation) {
760+
expression = new ir.Interpolation(
761+
value.strings, value.expressions.map(e => convertAst(e, job, null)),
762+
Object.keys(i18nMeta?.placeholders ?? {}));
763+
} else if (value instanceof e.AST) {
764+
expression = convertAst(value, job, null);
765+
} else {
766+
expression = value;
767+
}
768+
return expression;
769+
}
770+
771+
// TODO: Can we populate Template binding kinds in ingest?
772+
const BINDING_KINDS = new Map<e.BindingType, ir.BindingKind>([
773+
[e.BindingType.Property, ir.BindingKind.Property],
774+
[e.BindingType.Attribute, ir.BindingKind.Attribute],
775+
[e.BindingType.Class, ir.BindingKind.ClassName],
776+
[e.BindingType.Style, ir.BindingKind.StyleProperty],
777+
[e.BindingType.Animation, ir.BindingKind.Animation],
778+
]);
779+
780+
enum BindingFlags {
781+
None = 0b000,
782+
783+
/**
784+
* The binding is to a static text literal and not to an expression.
785+
*/
786+
TextValue = 0b0001,
787+
788+
/**
789+
* The binding belongs to the `<ng-template>` side of a `t.Template`.
790+
*/
791+
BindingTargetsTemplate = 0b0010,
792+
793+
/**
794+
* The binding is on a structural directive.
795+
*/
796+
IsStructuralTemplateAttribute = 0b0100,
797+
798+
/**
799+
* The binding is on a `t.Template`.
800+
*/
801+
OnNgTemplateElement = 0b1000,
802+
}
803+
750804
/**
751805
* Checks whether the given template is a plain ng-template (as opposed to another kind of template
752806
* such as a structural directive template or control flow template). This is checked based on the
@@ -768,6 +822,19 @@ function isPlainTemplate(tmpl: t.Template) {
768822
return splitNsName(tmpl.tagName ?? '')[1] === 'ng-template';
769823
}
770824

825+
/**
826+
* Ensures that the i18nMeta, if provided, is an i18n.Message.
827+
*/
828+
function asMessage(i18nMeta?: i18n.I18nMeta): i18n.Message|undefined {
829+
if (i18nMeta === undefined) {
830+
return undefined;
831+
}
832+
if (!(i18nMeta instanceof i18n.Message)) {
833+
throw Error(`Unhandled i18n metadata type for binding: ${i18nMeta.constructor.name}`);
834+
}
835+
return i18nMeta;
836+
}
837+
771838
/**
772839
* Process all of the bindings on an element-like structure in the template AST and convert them
773840
* to their IR representation.
@@ -776,7 +843,8 @@ function ingestBindings(
776843
unit: ViewCompilationUnit, op: ir.ElementOpBase, element: t.Element|t.Template,
777844
templateKind: ir.TemplateKind|null): void {
778845
let flags: BindingFlags = BindingFlags.None;
779-
let hasI18nAttributes = false;
846+
let bindings = new Array<ir.BindingOp|ir.ExtractedAttributeOp|null>();
847+
780848

781849
if (element instanceof t.Template) {
782850
flags |= BindingFlags.OnNgTemplateElement;
@@ -788,16 +856,14 @@ function ingestBindings(
788856
flags | BindingFlags.BindingTargetsTemplate | BindingFlags.IsStructuralTemplateAttribute;
789857
for (const attr of element.templateAttrs) {
790858
if (attr instanceof t.TextAttribute) {
791-
ingestBinding(
859+
bindings.push(createBinding(
792860
unit, op.xref, attr.name, o.literal(attr.value), e.BindingType.Attribute, null,
793861
SecurityContext.NONE, attr.sourceSpan, templateAttrFlags | BindingFlags.TextValue,
794-
templateKind, attr.i18n);
795-
hasI18nAttributes ||= attr.i18n !== undefined;
862+
templateKind, asMessage(attr.i18n)));
796863
} else {
797-
ingestBinding(
798-
unit, op.xref, attr.name, attr.value, attr.type, attr.unit, attr.securityContext,
799-
attr.sourceSpan, templateAttrFlags, templateKind, attr.i18n);
800-
hasI18nAttributes ||= attr.i18n !== undefined;
864+
bindings.push(createBinding(
865+
unit, op.xref, attr.name, astOf(attr.value), attr.type, attr.unit, attr.securityContext,
866+
attr.sourceSpan, templateAttrFlags, templateKind, asMessage(attr.i18n)));
801867
}
802868
}
803869
}
@@ -806,26 +872,25 @@ function ingestBindings(
806872
// This is only attribute TextLiteral bindings, such as `attr.foo="bar"`. This can never be
807873
// `[attr.foo]="bar"` or `attr.foo="{{bar}}"`, both of which will be handled as inputs with
808874
// `BindingType.Attribute`.
809-
ingestBinding(
875+
bindings.push(createBinding(
810876
unit, op.xref, attr.name, o.literal(attr.value), e.BindingType.Attribute, null,
811877
SecurityContext.NONE, attr.sourceSpan, flags | BindingFlags.TextValue, templateKind,
812-
attr.i18n);
813-
hasI18nAttributes ||= attr.i18n !== undefined;
878+
asMessage(attr.i18n)));
814879
}
815880

816881
for (const input of element.inputs) {
817-
ingestBinding(
818-
unit, op.xref, input.name, input.value, input.type, input.unit, input.securityContext,
819-
input.sourceSpan, flags, templateKind, input.i18n);
820-
hasI18nAttributes ||= input.i18n !== undefined;
882+
bindings.push(createBinding(
883+
unit, op.xref, input.name, astOf(input.value), input.type, input.unit,
884+
input.securityContext, input.sourceSpan, flags, templateKind, asMessage(input.i18n)));
821885
}
822886

887+
unit.create.push(bindings.filter(
888+
(b): b is ir.ExtractedAttributeOp => b?.kind === ir.OpKind.ExtractedAttribute));
889+
unit.update.push(bindings.filter((b): b is ir.BindingOp => b?.kind === ir.OpKind.Binding));
890+
823891
for (const output of element.outputs) {
824-
let listenerOp: ir.ListenerOp;
825-
if (output.type === e.ParsedEventType.Animation) {
826-
if (output.phase === null) {
827-
throw Error('Animation listener should have a phase');
828-
}
892+
if (output.type === e.ParsedEventType.Animation && output.phase === null) {
893+
throw Error('Animation listener should have a phase');
829894
}
830895

831896
if (element instanceof t.Template && !isPlainTemplate(element)) {
@@ -834,130 +899,65 @@ function ingestBindings(
834899
continue;
835900
}
836901

837-
listenerOp = ir.createListenerOp(
838-
op.xref, op.handle, output.name, op.tag, output.phase, false, output.sourceSpan);
839-
840-
// if output.handler is a chain, then push each statement from the chain separately, and
841-
// return the last one?
842-
let handlerExprs: e.AST[];
843-
let handler: e.AST = output.handler;
844-
if (handler instanceof e.ASTWithSource) {
845-
handler = handler.ast;
846-
}
847-
848-
if (handler instanceof e.Chain) {
849-
handlerExprs = handler.expressions;
850-
} else {
851-
handlerExprs = [handler];
852-
}
853-
854-
if (handlerExprs.length === 0) {
855-
throw new Error('Expected listener to have non-empty expression list.');
856-
}
857-
858-
const expressions = handlerExprs.map(expr => convertAst(expr, unit.job, output.handlerSpan));
859-
const returnExpr = expressions.pop()!;
860-
861-
for (const expr of expressions) {
862-
const stmtOp =
863-
ir.createStatementOp<ir.UpdateOp>(new o.ExpressionStatement(expr, expr.sourceSpan));
864-
listenerOp.handlerOps.push(stmtOp);
865-
}
866-
listenerOp.handlerOps.push(
867-
ir.createStatementOp(new o.ReturnStatement(returnExpr, returnExpr.sourceSpan)));
868-
unit.create.push(listenerOp);
902+
unit.create.push(ir.createListenerOp(
903+
op.xref, op.handle, output.name, op.tag,
904+
makeListenerHandlerOps(unit, output.handler, output.handlerSpan), output.phase, false,
905+
output.sourceSpan));
869906
}
870907

908+
871909
// TODO: Perhaps we could do this in a phase? (It likely wouldn't change the slot indices.)
872-
if (hasI18nAttributes) {
910+
if (bindings.some(b => b?.i18nMessage) !== null) {
873911
unit.create.push(
874912
ir.createI18nAttributesOp(unit.job.allocateXrefId(), new ir.SlotHandle(), op.xref));
875913
}
876914
}
877915

878-
const BINDING_KINDS = new Map<e.BindingType, ir.BindingKind>([
879-
[e.BindingType.Property, ir.BindingKind.Property],
880-
[e.BindingType.Attribute, ir.BindingKind.Attribute],
881-
[e.BindingType.Class, ir.BindingKind.ClassName],
882-
[e.BindingType.Style, ir.BindingKind.StyleProperty],
883-
[e.BindingType.Animation, ir.BindingKind.Animation],
884-
]);
885-
886-
enum BindingFlags {
887-
None = 0b000,
888-
889-
/**
890-
* The binding is to a static text literal and not to an expression.
891-
*/
892-
TextValue = 0b0001,
893-
894-
/**
895-
* The binding belongs to the `<ng-template>` side of a `t.Template`.
896-
*/
897-
BindingTargetsTemplate = 0b0010,
898-
899-
/**
900-
* The binding is on a structural directive.
901-
*/
902-
IsStructuralTemplateAttribute = 0b0100,
903-
904-
/**
905-
* The binding is on a `t.Template`.
906-
*/
907-
OnNgTemplateElement = 0b1000,
908-
}
909-
910-
function ingestBinding(
916+
function createBinding(
911917
view: ViewCompilationUnit, xref: ir.XrefId, name: string, value: e.AST|o.Expression,
912918
type: e.BindingType, unit: string|null, securityContext: SecurityContext,
913919
sourceSpan: ParseSourceSpan, flags: BindingFlags, templateKind: ir.TemplateKind|null,
914-
i18nMeta: i18n.I18nMeta|undefined): void {
915-
if (value instanceof e.ASTWithSource) {
916-
value = value.ast;
917-
}
918-
919-
if (i18nMeta !== undefined && !(i18nMeta instanceof i18n.Message)) {
920-
throw Error(`Unhandled i18n metadata type for binding: ${i18nMeta.constructor.name}`);
921-
}
922-
920+
i18nMessage: i18n.Message|undefined): ir.BindingOp|ir.ExtractedAttributeOp|null {
923921
if (flags & BindingFlags.OnNgTemplateElement && !(flags & BindingFlags.BindingTargetsTemplate) &&
924922
(type === e.BindingType.Property || type === e.BindingType.Class ||
925923
type === e.BindingType.Style)) {
926924
// This binding only exists for later const extraction, and is not an actual binding to be
927925
// created.
928-
view.create.push(ir.createExtractedAttributeOp(
929-
xref, ir.BindingKind.Property, name, null, null, i18nMeta ?? null));
930-
return;
931-
}
932-
933-
let expression: o.Expression|ir.Interpolation;
934-
// TODO: We could easily generate source maps for subexpressions in these cases, but
935-
// TemplateDefinitionBuilder does not. Should we do so?
936-
if (value instanceof e.Interpolation) {
937-
let i18nPlaceholders: string[] = [];
938-
if (i18nMeta !== undefined) {
939-
i18nPlaceholders = Object.keys(i18nMeta.placeholders);
940-
}
941-
expression = new ir.Interpolation(
942-
value.strings, value.expressions.map(expr => convertAst(expr, view.job, null)),
943-
i18nPlaceholders);
944-
} else if (value instanceof e.AST) {
945-
expression = convertAst(value, view.job, null);
946-
} else {
947-
expression = value;
926+
return ir.createExtractedAttributeOp(
927+
xref, ir.BindingKind.Property, name, null, null, i18nMessage ?? null);
948928
}
949929

950930
if (type === e.BindingType.Attribute && !(flags & BindingFlags.TextValue) &&
951931
templateKind === ir.TemplateKind.Structural) {
952932
// TODO: big comment about why this is stupid.
953-
return;
933+
return null;
954934
}
955935

956-
const kind: ir.BindingKind = BINDING_KINDS.get(type)!;
957-
view.update.push(ir.createBindingOp(
958-
xref, kind, name, expression, unit, securityContext, !!(flags & BindingFlags.TextValue),
959-
!!(flags & BindingFlags.IsStructuralTemplateAttribute), templateKind, i18nMeta ?? null,
960-
sourceSpan));
936+
return ir.createBindingOp(
937+
xref, BINDING_KINDS.get(type)!, name,
938+
convertAstWithInterpolation(view.job, value, i18nMessage), unit, securityContext,
939+
!!(flags & BindingFlags.TextValue), !!(flags & BindingFlags.IsStructuralTemplateAttribute),
940+
templateKind, i18nMessage ?? null, sourceSpan);
941+
}
942+
943+
function makeListenerHandlerOps(
944+
unit: CompilationUnit, handler: e.AST, handlerSpan: ParseSourceSpan): ir.UpdateOp[] {
945+
handler = astOf(handler);
946+
const handlerOps = new Array<ir.UpdateOp>();
947+
let handlerExprs: e.AST[] = handler instanceof e.Chain ? handler.expressions : [handler];
948+
if (handlerExprs.length === 0) {
949+
throw new Error('Expected listener to have non-empty expression list.');
950+
}
951+
const expressions = handlerExprs.map(expr => convertAst(expr, unit.job, handlerSpan));
952+
const returnExpr = expressions.pop()!;
953+
handlerOps.push(...expressions.map(
954+
e => ir.createStatementOp<ir.UpdateOp>(new o.ExpressionStatement(e, e.sourceSpan))));
955+
handlerOps.push(ir.createStatementOp(new o.ReturnStatement(returnExpr, returnExpr.sourceSpan)));
956+
return handlerOps;
957+
}
958+
959+
function astOf(ast: e.AST|e.ASTWithSource): e.AST {
960+
return ast instanceof e.ASTWithSource ? ast.ast : ast;
961961
}
962962

963963
/**
@@ -1055,10 +1055,15 @@ function ingestControlFlowInsertionPoint(
10551055
// and they can be used in directive matching (in the case of `Template.templateAttrs`).
10561056
if (root !== null) {
10571057
for (const attr of root.attributes) {
1058-
ingestBinding(
1058+
let b = createBinding(
10591059
unit, xref, attr.name, o.literal(attr.value), e.BindingType.Attribute, null,
10601060
SecurityContext.NONE, attr.sourceSpan, BindingFlags.TextValue, ir.TemplateKind.Block,
1061-
attr.i18n);
1061+
asMessage(attr.i18n));
1062+
if (b?.kind === ir.OpKind.Binding) {
1063+
unit.update.push(b);
1064+
} else if (b?.kind === ir.OpKind.ExtractedAttribute) {
1065+
unit.create.push(b);
1066+
}
10621067
}
10631068

10641069
const tagName = root instanceof t.Element ? root.name : root.tagName;

0 commit comments

Comments
0 (0)
You are visitor 01402520 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.