perf(ivy): avoid repeat global state accesses in i18n instructions (#32916)

Removes repeat global state accesses from the i18n instructions in the cases where we have the information available already.

PR Close #32916
This commit is contained in:
crisbeto 2019-09-30 19:31:52 +02:00 committed by atscott
parent b3549e6680
commit ffc34b3676
1 changed files with 34 additions and 35 deletions

View File

@ -360,13 +360,14 @@ const parentIndexStack: number[] = [];
* @codeGenApi
*/
export function ɵɵi18nStart(index: number, message: string, subTemplateIndex?: number): void {
const tView = getLView()[TVIEW];
const lView = getLView();
const tView = lView[TVIEW];
ngDevMode && assertDefined(tView, `tView should be defined`);
i18nIndexStack[++i18nIndexStackPointer] = index;
// We need to delay projections until `i18nEnd`
setDelayProjection(true);
if (tView.firstTemplatePass && tView.data[index + HEADER_OFFSET] === null) {
i18nStartFirstPass(tView, index, message, subTemplateIndex);
i18nStartFirstPass(lView, tView, index, message, subTemplateIndex);
}
}
@ -379,15 +380,14 @@ let i18nVarsCount: number;
* See `i18nStart` above.
*/
function i18nStartFirstPass(
tView: TView, index: number, message: string, subTemplateIndex?: number) {
const viewData = getLView();
lView: LView, tView: TView, index: number, message: string, subTemplateIndex?: number) {
const startIndex = tView.blueprint.length - HEADER_OFFSET;
i18nVarsCount = 0;
const previousOrParentTNode = getPreviousOrParentTNode();
const parentTNode = getIsParent() ? getPreviousOrParentTNode() :
previousOrParentTNode && previousOrParentTNode.parent;
const parentTNode =
getIsParent() ? previousOrParentTNode : previousOrParentTNode && previousOrParentTNode.parent;
let parentIndex =
parentTNode && parentTNode !== viewData[T_HOST] ? parentTNode.index - HEADER_OFFSET : index;
parentTNode && parentTNode !== lView[T_HOST] ? parentTNode.index - HEADER_OFFSET : index;
let parentIndexPointer = 0;
parentIndexStack[parentIndexPointer] = parentIndex;
const createOpCodes: I18nMutateOpCodes = [];
@ -471,12 +471,12 @@ function i18nStartFirstPass(
}
if (i18nVarsCount > 0) {
allocExpando(viewData, i18nVarsCount);
allocExpando(lView, i18nVarsCount);
}
ngDevMode &&
attachI18nOpCodesDebug(
createOpCodes, updateOpCodes, icuExpressions.length ? icuExpressions : null, viewData);
createOpCodes, updateOpCodes, icuExpressions.length ? icuExpressions : null, lView);
// NOTE: local var needed to properly assert the type of `TI18n`.
const tI18n: TI18n = {
@ -656,9 +656,10 @@ export function ɵɵi18nPostprocess(
* @codeGenApi
*/
export function ɵɵi18nEnd(): void {
const tView = getLView()[TVIEW];
const lView = getLView();
const tView = lView[TVIEW];
ngDevMode && assertDefined(tView, `tView should be defined`);
i18nEndFirstPass(tView);
i18nEndFirstPass(lView, tView);
// Stop delaying projections
setDelayProjection(false);
}
@ -666,10 +667,9 @@ export function ɵɵi18nEnd(): void {
/**
* See `i18nEnd` above.
*/
function i18nEndFirstPass(tView: TView) {
const viewData = getLView();
function i18nEndFirstPass(lView: LView, tView: TView) {
ngDevMode && assertEqual(
viewData[BINDING_INDEX], viewData[TVIEW].bindingStartIndex,
lView[BINDING_INDEX], tView.bindingStartIndex,
'i18nEnd should be called before any binding');
const rootIndex = i18nIndexStack[i18nIndexStackPointer--];
@ -677,15 +677,15 @@ function i18nEndFirstPass(tView: TView) {
ngDevMode && assertDefined(tI18n, `You should call i18nStart before i18nEnd`);
// Find the last node that was added before `i18nEnd`
let lastCreatedNode = getPreviousOrParentTNode();
const lastCreatedNode = getPreviousOrParentTNode();
// Read the instructions to insert/move/remove DOM elements
const visitedNodes = readCreateOpCodes(rootIndex, tI18n.create, tI18n.icus, viewData);
const visitedNodes = readCreateOpCodes(rootIndex, tI18n.create, lView);
// Remove deleted nodes
for (let i = rootIndex + 1; i <= lastCreatedNode.index - HEADER_OFFSET; i++) {
if (visitedNodes.indexOf(i) === -1) {
removeNode(i, viewData, /* markAsDetached */ true);
removeNode(i, lView, /* markAsDetached */ true);
}
}
}
@ -711,9 +711,8 @@ function createDynamicNodeAtIndex(
}
function readCreateOpCodes(
index: number, createOpCodes: I18nMutateOpCodes, icus: TIcu[] | null,
viewData: LView): number[] {
const renderer = getLView()[RENDERER];
index: number, createOpCodes: I18nMutateOpCodes, lView: LView): number[] {
const renderer = lView[RENDERER];
let currentTNode: TNode|null = null;
let previousTNode: TNode|null = null;
const visitedNodes: number[] = [];
@ -725,7 +724,7 @@ function readCreateOpCodes(
ngDevMode && ngDevMode.rendererCreateTextNode++;
previousTNode = currentTNode;
currentTNode =
createDynamicNodeAtIndex(viewData, textNodeIndex, TNodeType.Element, textRNode, null);
createDynamicNodeAtIndex(lView, textNodeIndex, TNodeType.Element, textRNode, null);
visitedNodes.push(textNodeIndex);
setIsNotParent();
} else if (typeof opCode == 'number') {
@ -736,28 +735,28 @@ function readCreateOpCodes(
if (destinationNodeIndex === index) {
// If the destination node is `i18nStart`, we don't have a
// top-level node and we should use the host node instead
destinationTNode = viewData[T_HOST] !;
destinationTNode = lView[T_HOST] !;
} else {
destinationTNode = getTNode(destinationNodeIndex, viewData);
destinationTNode = getTNode(destinationNodeIndex, lView);
}
ngDevMode &&
assertDefined(
currentTNode !,
`You need to create or select a node before you can insert it into the DOM`);
previousTNode = appendI18nNode(currentTNode !, destinationTNode, previousTNode, viewData);
previousTNode = appendI18nNode(currentTNode !, destinationTNode, previousTNode, lView);
break;
case I18nMutateOpCode.Select:
const nodeIndex = opCode >>> I18nMutateOpCode.SHIFT_REF;
visitedNodes.push(nodeIndex);
previousTNode = currentTNode;
currentTNode = getTNode(nodeIndex, viewData);
currentTNode = getTNode(nodeIndex, lView);
if (currentTNode) {
setPreviousOrParentTNode(currentTNode, currentTNode.type === TNodeType.Element);
}
break;
case I18nMutateOpCode.ElementEnd:
const elementIndex = opCode >>> I18nMutateOpCode.SHIFT_REF;
previousTNode = currentTNode = getTNode(elementIndex, viewData);
previousTNode = currentTNode = getTNode(elementIndex, lView);
setPreviousOrParentTNode(currentTNode, false);
break;
case I18nMutateOpCode.Attr:
@ -766,7 +765,7 @@ function readCreateOpCodes(
const attrValue = createOpCodes[++i] as string;
// This code is used for ICU expressions only, since we don't support
// directives/components in ICUs, we don't need to worry about inputs here
elementAttributeInternal(elementNodeIndex, attrName, attrValue, viewData);
elementAttributeInternal(elementNodeIndex, attrName, attrValue, lView);
break;
default:
throw new Error(`Unable to determine the type of mutate operation for "${opCode}"`);
@ -783,9 +782,9 @@ function readCreateOpCodes(
ngDevMode && ngDevMode.rendererCreateComment++;
previousTNode = currentTNode;
currentTNode = createDynamicNodeAtIndex(
viewData, commentNodeIndex, TNodeType.IcuContainer, commentRNode, null);
lView, commentNodeIndex, TNodeType.IcuContainer, commentRNode, null);
visitedNodes.push(commentNodeIndex);
attachPatchData(commentRNode, viewData);
attachPatchData(commentRNode, lView);
(currentTNode as TIcuContainerNode).activeCaseIndex = null;
// We will add the case nodes later, during the update phase
setIsNotParent();
@ -800,7 +799,7 @@ function readCreateOpCodes(
ngDevMode && ngDevMode.rendererCreateElement++;
previousTNode = currentTNode;
currentTNode = createDynamicNodeAtIndex(
viewData, elementNodeIndex, TNodeType.Element, elementRNode, tagNameValue);
lView, elementNodeIndex, TNodeType.Element, elementRNode, tagNameValue);
visitedNodes.push(elementNodeIndex);
break;
default:
@ -886,7 +885,7 @@ function readUpdateOpCodes(
icuTNode.activeCaseIndex = caseIndex !== -1 ? caseIndex : null;
// Add the nodes for the new case
readCreateOpCodes(-1, tIcu.create[caseIndex], icus, viewData);
readCreateOpCodes(-1, tIcu.create[caseIndex], viewData);
caseCreated = true;
break;
case I18nUpdateOpCode.IcuUpdate:
@ -968,15 +967,16 @@ export function ɵɵi18n(index: number, message: string, subTemplateIndex?: numb
* @codeGenApi
*/
export function ɵɵi18nAttributes(index: number, values: string[]): void {
const tView = getLView()[TVIEW];
const lView = getLView();
const tView = lView[TVIEW];
ngDevMode && assertDefined(tView, `tView should be defined`);
i18nAttributesFirstPass(tView, index, values);
i18nAttributesFirstPass(lView, tView, index, values);
}
/**
* See `i18nAttributes` above.
*/
function i18nAttributesFirstPass(tView: TView, index: number, values: string[]) {
function i18nAttributesFirstPass(lView: LView, tView: TView, index: number, values: string[]) {
const previousElement = getPreviousOrParentTNode();
const previousElementIndex = previousElement.index - HEADER_OFFSET;
const updateOpCodes: I18nUpdateOpCodes = [];
@ -1000,7 +1000,6 @@ function i18nAttributesFirstPass(tView: TView, index: number, values: string[])
generateBindingUpdateOpCodes(value, previousElementIndex, attrName), updateOpCodes);
}
} else {
const lView = getLView();
elementAttributeInternal(previousElementIndex, attrName, value, lView);
// Check if that attribute is a directive input
const tNode = getTNode(previousElementIndex, lView);