perf(compiler): chain element start/end instructions (#44994)

In templates with several levels of nested nodes, it's common for several `elementStart`/`elementEnd` instructions to show up in a row which can be optimized away.

These changes add chaining support for `elementStart`, `elementEnd`, `elementContainerStart` and `elementContainerEnd` to shave off some bytes when possible.

PR Close #44994
This commit is contained in:
Kristiyan Kostadinov
2022-02-08 09:30:47 +01:00
committed by Dylan Hunn
parent 8f1f35b973
commit 1b91e1049e
25 changed files with 67 additions and 69 deletions
@@ -10,8 +10,7 @@ MyApp.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({
consts: [[__AttributeMarker.Bindings__, "dir"]],
template: function MyApp_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵelement(0, "div", 0);
$r3$.ɵɵelement(1, "div", 0);
$r3$.ɵɵelement(0, "div", 0)(1, "div", 0);
}
if (rf & 2) {
$r3$.ɵɵproperty("dir", $r3$.ɵɵpureFunction0(2, $c0$));
@@ -6,8 +6,7 @@ SimpleLayout.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({
consts: [[__AttributeMarker.Bindings__, "name"]],
template: function SimpleLayout_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵelement(0, "lifecycle-comp", 0);
$r3$.ɵɵelement(1, "lifecycle-comp", 0);
$r3$.ɵɵelement(0, "lifecycle-comp", 0)(1, "lifecycle-comp", 0);
}
if (rf & 2) {
$r3$.ɵɵproperty("name", ctx.name1);
@@ -10,8 +10,7 @@ MyApp.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({
consts: [[__AttributeMarker.Bindings__, "dir"]],
template: function MyApp_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵelement(0, "div", 0);
$r3$.ɵɵelement(1, "div", 0);
$r3$.ɵɵelement(0, "div", 0)(1, "div", 0);
}
if (rf & 2) {
$r3$.ɵɵproperty("dir", $r3$.ɵɵpureFunction0(2, $c0$));
@@ -9,8 +9,7 @@ MyApp.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({
consts: [[__AttributeMarker.Bindings__, "dir"]],
template: function MyApp_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵelement(0, "div", 0);
$r3$.ɵɵelement(1, "div", 0);
$r3$.ɵɵelement(0, "div", 0)(1, "div", 0);
}
if (rf & 2) {
$r3$.ɵɵproperty("dir", $r3$.ɵɵpureFunction0(2, $c0$));
@@ -14,14 +14,12 @@ function MyComponent_li_1_li_4_Template(rf, ctx) {
function MyComponent_li_1_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵelementStart(0, "li");
$r3$.ɵɵelementStart(1, "div");
$r3$.ɵɵelementStart(0, "li")(1, "div");
$r3$.ɵɵtext(2);
$r3$.ɵɵelementEnd();
$r3$.ɵɵelementStart(3, "ul");
$r3$.ɵɵtemplate(4, MyComponent_li_1_li_4_Template, 2, 2, "li", 0);
$r3$.ɵɵelementEnd();
$r3$.ɵɵelementEnd();
$r3$.ɵɵelementEnd()();
}
if (rf & 2) {
const $item$ = ctx.$implicit;
@@ -1,8 +1,7 @@
consts: [["title", "hi"]],
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵelement(0, "div", 0);
$r3$.ɵɵelement(1, "span", 0);
$r3$.ɵɵelement(0, "div", 0)(1, "span", 0);
}
…
}
@@ -10,7 +10,6 @@ template: function MyComponent_Template(rf, ctx) {
$r3$.ɵɵnamespaceHTML();
$r3$.ɵɵelementStart(3, "p");
$r3$.ɵɵtext(4, "test");
$r3$.ɵɵelementEnd();
$r3$.ɵɵelementEnd();
$r3$.ɵɵelementEnd()();
}
}
@@ -6,11 +6,7 @@ consts: [
],
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵelement(0, "embed", 0);
$r3$.ɵɵelement(1, "iframe", 1);
$r3$.ɵɵelement(2, "object", 2);
$r3$.ɵɵelement(3, "embed", 0);
$r3$.ɵɵelement(4, "img", 3);
$r3$.ɵɵelement(0, "embed", 0)(1, "iframe", 1)(2, "object", 2)(3, "embed", 0)(4, "img", 3);
}
…
}
@@ -9,7 +9,6 @@ consts: [["title", "Hello", 1, "my-app"], ["cx", "20", "cy", "30", "r", "50"]],
$r3$.ɵɵnamespaceHTML();
$r3$.ɵɵelementStart(3, "p");
$r3$.ɵɵtext(4, "test");
$r3$.ɵɵelementEnd();
$r3$.ɵɵelementEnd();
$r3$.ɵɵelementEnd()();
}
}
@@ -1,7 +1,6 @@
template: function MyApp_Template(rf, ctx) {
if (rf & 1) {
i0.ɵɵelement(0, "div", 0);
i0.ɵɵelement(1, "span", 0);
i0.ɵɵelement(0, "div", 0)(1, "span", 0);
}
if (rf & 2) {
let $tmp_0_0$;
@@ -1,11 +1,9 @@
function MyComponent_div_0_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵelementStart(0, "div");
$r3$.ɵɵelementStart(1, "div", 1);
$r3$.ɵɵelementStart(0, "div")(1, "div", 1);
$r3$.ɵɵpipe(2, "uppercase");
$r3$.ɵɵi18nAttributes(3, 2);
$r3$.ɵɵelementEnd();
$r3$.ɵɵelementEnd();
$r3$.ɵɵelementEnd()();
}
if (rf & 2) {
const $outer_r1$ = ctx.$implicit;
@@ -25,15 +25,13 @@ consts: function() {
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵnamespaceSVG();
$r3$.ɵɵelementStart(0, "svg", 0);
$r3$.ɵɵelementStart(1, "foreignObject");
$r3$.ɵɵelementStart(0, "svg", 0)(1, "foreignObject");
$r3$.ɵɵi18nStart(2, 1);
$r3$.ɵɵnamespaceHTML();
$r3$.ɵɵelementStart(3, "div", 2);
$r3$.ɵɵelement(4, "span");
$r3$.ɵɵelementEnd();
$r3$.ɵɵi18nEnd();
$r3$.ɵɵelementEnd();
$r3$.ɵɵelementEnd();
$r3$.ɵɵelementEnd()();
}
}
@@ -22,15 +22,12 @@ consts: function() {
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵnamespaceSVG();
$r3$.ɵɵelementStart(0, "svg", 0);
$r3$.ɵɵelementStart(1, "foreignObject");
$r3$.ɵɵelementStart(0, "svg", 0)(1, "foreignObject");
$r3$.ɵɵnamespaceHTML();
$r3$.ɵɵelementStart(2, "div", 1);
$r3$.ɵɵi18nStart(3, 2);
$r3$.ɵɵelement(4, "span");
$r3$.ɵɵi18nEnd();
$r3$.ɵɵelementEnd();
$r3$.ɵɵelementEnd();
$r3$.ɵɵelementEnd();
$r3$.ɵɵelementEnd()()();
}
}
@@ -1,7 +1,5 @@
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵelement(0, "div");
$r3$.ɵɵelement(1, "div");
$r3$.ɵɵelement(2, "div");
$r3$.ɵɵelement(0, "div")(1, "div")(2, "div");
}
}
@@ -18,11 +18,9 @@ template: function MyComponent_Template(rf, ctx) {
$r3$.ɵɵelementStart(3, "div");
$r3$.ɵɵi18nStart(4, 1);
$r3$.ɵɵpipe(5, "uppercase");
$r3$.ɵɵelementStart(6, "div");
$r3$.ɵɵelementStart(7, "div");
$r3$.ɵɵelementStart(6, "div")(7, "div");
$r3$.ɵɵelement(8, "span");
$r3$.ɵɵelementEnd();
$r3$.ɵɵelementEnd();
$r3$.ɵɵelementEnd()();
$r3$.ɵɵi18nEnd();
$r3$.ɵɵelementEnd();
}
@@ -1,13 +1,11 @@
function MyComponent_div_2_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵelementStart(0, "div");
$r3$.ɵɵelementStart(1, "div");
$r3$.ɵɵelementStart(0, "div")(1, "div");
$r3$.ɵɵi18nStart(2, 1);
$r3$.ɵɵelement(3, "div");
$r3$.ɵɵpipe(4, "uppercase");
$r3$.ɵɵi18nEnd();
$r3$.ɵɵelementEnd();
$r3$.ɵɵelementEnd();
$r3$.ɵɵelementEnd()();
}
if (rf & 2) {
const $ctx_r0$ = $r3$.ɵɵnextContext();
@@ -16,12 +16,10 @@ function MyComponent_div_2_div_4_Template(rf, ctx) {
function MyComponent_div_2_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵi18nStart(0, 0, 1);
$r3$.ɵɵelementStart(1, "div");
$r3$.ɵɵelementStart(2, "div");
$r3$.ɵɵelementStart(1, "div")(2, "div");
$r3$.ɵɵpipe(3, "uppercase");
$r3$.ɵɵtemplate(4, MyComponent_div_2_div_4_Template, 3, 2, "div", 1);
$r3$.ɵɵelementEnd();
$r3$.ɵɵelementEnd();
$r3$.ɵɵelementEnd()();
$r3$.ɵɵi18nEnd();
}
if (rf & 2) {
@@ -1,8 +1,7 @@
function MyComponent_div_0_Template(rf, ctx) {
if (rf & 1) {
const $s$ = $r3$.ɵɵgetCurrentView();
$r3$.ɵɵelementStart(0, "div");
$r3$.ɵɵelementStart(1, "div", 1);
$r3$.ɵɵelementStart(0, "div")(1, "div", 1);
$r3$.ɵɵlistener("click", function MyComponent_div_0_Template_div_click_1_listener() {
$r3$.ɵɵrestoreView($s$);
const $comp$ = $r3$.ɵɵnextContext();
@@ -15,8 +14,7 @@ function MyComponent_div_0_Template(rf, ctx) {
const $comp2$ = $r3$.ɵɵnextContext();
return $comp2$.onClick2($comp2$.bar);
});
$r3$.ɵɵelementEnd();
$r3$.ɵɵelementEnd();
$r3$.ɵɵelementEnd()();
}
}
// ...
@@ -5,9 +5,7 @@ MyComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({
vars: 3,
template: function MyComponent_Template(rf, $ctx$) {
if (rf & 1) {
$r3$.ɵɵelement(0, "div");
$r3$.ɵɵelement(1, "div");
$r3$.ɵɵelement(2, "div");
$r3$.ɵɵelement(0, "div")(1, "div")(2, "div");
}
if (rf & 2) {
$r3$.ɵɵproperty("@foo", ctx.exp);
@@ -52,6 +52,12 @@ export const RESTORED_VIEW_CONTEXT_NAME = 'restoredCtx';
/** Instructions that support chaining. */
const CHAINABLE_INSTRUCTIONS = new Set([
R3.element,
R3.elementStart,
R3.elementEnd,
R3.elementContainer,
R3.elementContainerStart,
R3.elementContainerEnd,
R3.i18nExp,
R3.listener,
R3.classProp,
@@ -63,6 +63,7 @@ function elementStartFirstCreatePass(
* @param name Name of the DOM Node
* @param attrsIndex Index of the element's attributes in the `consts` array.
* @param localRefsIndex Index of the element's local references in the `consts` array.
* @returns This function returns itself so that it may be chained.
*
* Attributes and localRefs are passed as an array of strings where elements with an even index
* hold an attribute name and elements with an odd index hold an attribute value, ex.:
@@ -71,7 +72,8 @@ function elementStartFirstCreatePass(
* @codeGenApi
*/
export function ɵɵelementStart(
index: number, name: string, attrsIndex?: number|null, localRefsIndex?: number): void {
index: number, name: string, attrsIndex?: number|null,
localRefsIndex?: number): typeof ɵɵelementStart {
const lView = getLView();
const tView = getTView();
const adjustedIndex = HEADER_OFFSET + index;
@@ -125,14 +127,16 @@ export function ɵɵelementStart(
if (localRefsIndex !== null) {
saveResolvedLocalsInData(lView, tNode);
}
return ɵɵelementStart;
}
/**
* Mark the end of the element.
* @returns This function returns itself so that it may be chained.
*
* @codeGenApi
*/
export function ɵɵelementEnd(): void {
export function ɵɵelementEnd(): typeof ɵɵelementEnd {
let currentTNode = getCurrentTNode()!;
ngDevMode && assertDefined(currentTNode, 'No parent node to close.');
if (isCurrentTNodeParent()) {
@@ -164,6 +168,7 @@ export function ɵɵelementEnd(): void {
if (tNode.stylesWithoutHost != null && hasStyleInput(tNode)) {
setDirectiveInputsWhichShadowsStyling(tView, tNode, getLView(), tNode.stylesWithoutHost, false);
}
return ɵɵelementEnd;
}
/**
@@ -173,13 +178,16 @@ export function ɵɵelementEnd(): void {
* @param name Name of the DOM Node
* @param attrsIndex Index of the element's attributes in the `consts` array.
* @param localRefsIndex Index of the element's local references in the `consts` array.
* @returns This function returns itself so that it may be chained.
*
* @codeGenApi
*/
export function ɵɵelement(
index: number, name: string, attrsIndex?: number|null, localRefsIndex?: number): void {
index: number, name: string, attrsIndex?: number|null,
localRefsIndex?: number): typeof ɵɵelement {
ɵɵelementStart(index, name, attrsIndex, localRefsIndex);
ɵɵelementEnd();
return ɵɵelement;
}
function logUnknownElementError(
@@ -11,7 +11,7 @@ import {attachPatchData} from '../context_discovery';
import {registerPostOrderHooks} from '../hooks';
import {TAttributes, TElementContainerNode, TNodeType} from '../interfaces/node';
import {isContentQueryHost, isDirectiveHost} from '../interfaces/type_checks';
import {HEADER_OFFSET, LView, RENDERER, T_HOST, TView} from '../interfaces/view';
import {HEADER_OFFSET, LView, RENDERER, TView} from '../interfaces/view';
import {assertTNodeType} from '../node_assert';
import {appendChild} from '../node_manipulation';
import {getBindingIndex, getCurrentTNode, getLView, getTView, isCurrentTNodeParent, setCurrentTNode, setCurrentTNodeAsNotParent} from '../state';
@@ -52,6 +52,7 @@ function elementContainerStartFirstCreatePass(
* @param index Index of the element in the LView array
* @param attrsIndex Index of the container attributes in the `consts` array.
* @param localRefsIndex Index of the container's local references in the `consts` array.
* @returns This function returns itself so that it may be chained.
*
* Even if this instruction accepts a set of attributes no actual attribute values are propagated to
* the DOM (as a comment node can't have attributes). Attributes are here only for directive
@@ -60,7 +61,8 @@ function elementContainerStartFirstCreatePass(
* @codeGenApi
*/
export function ɵɵelementContainerStart(
index: number, attrsIndex?: number|null, localRefsIndex?: number): void {
index: number, attrsIndex?: number|null,
localRefsIndex?: number): typeof ɵɵelementContainerStart {
const lView = getLView();
const tView = getTView();
const adjustedIndex = index + HEADER_OFFSET;
@@ -91,14 +93,17 @@ export function ɵɵelementContainerStart(
if (localRefsIndex != null) {
saveResolvedLocalsInData(lView, tNode);
}
return ɵɵelementContainerStart;
}
/**
* Mark the end of the <ng-container>.
* @returns This function returns itself so that it may be chained.
*
* @codeGenApi
*/
export function ɵɵelementContainerEnd(): void {
export function ɵɵelementContainerEnd(): typeof ɵɵelementContainerEnd {
let currentTNode = getCurrentTNode()!;
const tView = getTView();
if (isCurrentTNodeParent()) {
@@ -117,6 +122,7 @@ export function ɵɵelementContainerEnd(): void {
tView.queries!.elementEnd(currentTNode);
}
}
return ɵɵelementContainerEnd;
}
/**
@@ -126,11 +132,13 @@ export function ɵɵelementContainerEnd(): void {
* @param index Index of the element in the LView array
* @param attrsIndex Index of the container attributes in the `consts` array.
* @param localRefsIndex Index of the container's local references in the `consts` array.
* @returns This function returns itself so that it may be chained.
*
* @codeGenApi
*/
export function ɵɵelementContainer(
index: number, attrsIndex?: number|null, localRefsIndex?: number): void {
index: number, attrsIndex?: number|null, localRefsIndex?: number): typeof ɵɵelementContainer {
ɵɵelementContainerStart(index, attrsIndex, localRefsIndex);
ɵɵelementContainerEnd();
return ɵɵelementContainer;
}
@@ -1475,6 +1475,9 @@
{
"name": "ɵɵelement"
},
{
"name": "ɵɵelementEnd"
},
{
"name": "ɵɵelementStart"
},
@@ -362,6 +362,12 @@
{
"name": "ɵɵdefineComponent"
},
{
"name": "ɵɵelement"
},
{
"name": "ɵɵelementEnd"
},
{
"name": "ɵɵelementStart"
}
@@ -121,7 +121,7 @@ describe('jit source mapping', () => {
}));
it('should report di errors with multiple elements and directives', fakeAsync(() => {
const template = `<div someDir></div><div someDir="throw"></div>`;
const template = `<div someDir></div>|<div someDir="throw"></div>`;
@Component({...templateDecorator(template)})
class MyComp {
@@ -146,7 +146,7 @@ describe('jit source mapping', () => {
// The error should be logged from the 2nd-element
expect(jitEvaluator.getSourcePositionForStack(error.stack, generatedUrl)).toEqual({
line: 1,
column: 19,
column: 20,
source: ngUrl,
});
}));