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