refactor(core): Consistent use of `HEADER_OFFSET` (in `ɵɵ*` instructions only) (#39233)

IMPORTANT: `HEADER_OFFSET` should only be refereed to the in the `ɵɵ*` instructions to translate
instruction index into `LView` index. All other indexes should be in the `LView` index space and
there should be no need to refer to `HEADER_OFFSET` anywhere else.

PR Close #39233
This commit is contained in:
Misko Hevery 2020-10-13 22:00:43 -07:00 committed by Alex Rickabaugh
parent bc5005e35b
commit 54303688fa
34 changed files with 308 additions and 319 deletions

View File

@ -21,7 +21,7 @@
"master": { "master": {
"uncompressed": { "uncompressed": {
"runtime-es2015": 1485, "runtime-es2015": 1485,
"main-es2015": 147242, "main-es2015": 146698,
"polyfills-es2015": 36571 "polyfills-es2015": 36571
} }
} }

View File

@ -12,7 +12,6 @@ import {Type} from '../core';
import {Injector} from '../di/injector'; import {Injector} from '../di/injector';
import {Sanitizer} from '../sanitization/sanitizer'; import {Sanitizer} from '../sanitization/sanitizer';
import {assertDefined, assertIndexInRange} from '../util/assert'; import {assertDefined, assertIndexInRange} from '../util/assert';
import {assertComponentType} from './assert'; import {assertComponentType} from './assert';
import {getComponentDef} from './definition'; import {getComponentDef} from './definition';
import {diPublicInInjector, getOrCreateNodeInjectorForNode} from './di'; import {diPublicInInjector, getOrCreateNodeInjectorForNode} from './di';
@ -173,12 +172,13 @@ export function createRootComponentView(
rNode: RElement|null, def: ComponentDef<any>, rootView: LView, rNode: RElement|null, def: ComponentDef<any>, rootView: LView,
rendererFactory: RendererFactory3, hostRenderer: Renderer3, sanitizer?: Sanitizer|null): LView { rendererFactory: RendererFactory3, hostRenderer: Renderer3, sanitizer?: Sanitizer|null): LView {
const tView = rootView[TVIEW]; const tView = rootView[TVIEW];
ngDevMode && assertIndexInRange(rootView, 0 + HEADER_OFFSET); const index = HEADER_OFFSET;
rootView[0 + HEADER_OFFSET] = rNode; ngDevMode && assertIndexInRange(rootView, index);
rootView[index] = rNode;
// '#host' is added here as we don't know the real host DOM name (we don't want to read it) and at // '#host' is added here as we don't know the real host DOM name (we don't want to read it) and at
// the same time we want to communicate the the debug `TNode` that this is a special `TNode` // the same time we want to communicate the the debug `TNode` that this is a special `TNode`
// representing a host element. // representing a host element.
const tNode = getOrCreateTNode(tView, 0, TNodeType.Element, '#host', null); const tNode: TElementNode = getOrCreateTNode(tView, index, TNodeType.Element, '#host', null);
const mergedAttrs = tNode.mergedAttrs = def.hostAttrs; const mergedAttrs = tNode.mergedAttrs = def.hostAttrs;
if (mergedAttrs !== null) { if (mergedAttrs !== null) {
computeStaticStyling(tNode, mergedAttrs, true); computeStaticStyling(tNode, mergedAttrs, true);
@ -196,7 +196,7 @@ export function createRootComponentView(
const viewRenderer = rendererFactory.createRenderer(rNode, def); const viewRenderer = rendererFactory.createRenderer(rNode, def);
const componentView = createLView( const componentView = createLView(
rootView, getOrCreateTComponentView(def), null, rootView, getOrCreateTComponentView(def), null,
def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, rootView[HEADER_OFFSET], tNode, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, rootView[index], tNode,
rendererFactory, viewRenderer, sanitizer || null, null); rendererFactory, viewRenderer, sanitizer || null, null);
if (tView.firstCreatePass) { if (tView.firstCreatePass) {
@ -208,7 +208,7 @@ export function createRootComponentView(
addToViewTree(rootView, componentView); addToViewTree(rootView, componentView);
// Store component view at node index, with node as the HOST // Store component view at node index, with node as the HOST
return rootView[HEADER_OFFSET] = componentView; return rootView[index] = componentView;
} }
/** /**
@ -237,8 +237,7 @@ export function createRootComponent<T>(
ngDevMode && assertDefined(rootTNode, 'tNode should have been already created'); ngDevMode && assertDefined(rootTNode, 'tNode should have been already created');
if (tView.firstCreatePass && if (tView.firstCreatePass &&
(componentDef.hostBindings !== null || componentDef.hostAttrs !== null)) { (componentDef.hostBindings !== null || componentDef.hostAttrs !== null)) {
const elementIndex = rootTNode.index - HEADER_OFFSET; setSelectedIndex(rootTNode.index);
setSelectedIndex(elementIndex);
const rootTView = rootLView[TVIEW]; const rootTView = rootLView[TVIEW];
addHostBindingsToExpandoInstructions(rootTView, componentDef); addHostBindingsToExpandoInstructions(rootTView, componentDef);

View File

@ -27,7 +27,7 @@ import {createLView, createTView, locateHostElement, renderView} from './instruc
import {ComponentDef} from './interfaces/definition'; import {ComponentDef} from './interfaces/definition';
import {TContainerNode, TElementContainerNode, TElementNode, TNode} from './interfaces/node'; import {TContainerNode, TElementContainerNode, TElementNode, TNode} from './interfaces/node';
import {domRendererFactory3, RendererFactory3, RNode} from './interfaces/renderer'; import {domRendererFactory3, RendererFactory3, RNode} from './interfaces/renderer';
import {LView, LViewFlags, TViewType} from './interfaces/view'; import {HEADER_OFFSET, LView, LViewFlags, TViewType} from './interfaces/view';
import {MATH_ML_NAMESPACE, SVG_NAMESPACE} from './namespaces'; import {MATH_ML_NAMESPACE, SVG_NAMESPACE} from './namespaces';
import {createElementNode, writeDirectClass} from './node_manipulation'; import {createElementNode, writeDirectClass} from './node_manipulation';
import {extractAttrsAndClassesFromSelector, stringifyCSSSelectorList} from './node_selector_matcher'; import {extractAttrsAndClassesFromSelector, stringifyCSSSelectorList} from './node_selector_matcher';
@ -192,7 +192,7 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
} }
} }
tElementNode = getTNode(rootTView, 0) as TElementNode; tElementNode = getTNode(rootTView, HEADER_OFFSET) as TElementNode;
if (projectableNodes !== undefined) { if (projectableNodes !== undefined) {
const projection: (TNode|RNode[]|null)[] = tElementNode.projection = []; const projection: (TNode|RNode[]|null)[] = tElementNode.projection = [];

View File

@ -14,7 +14,7 @@ import {LContext, MONKEY_PATCH_KEY_NAME} from './interfaces/context';
import {TNode, TNodeFlags} from './interfaces/node'; import {TNode, TNodeFlags} from './interfaces/node';
import {RElement, RNode} from './interfaces/renderer'; import {RElement, RNode} from './interfaces/renderer';
import {CONTEXT, HEADER_OFFSET, HOST, LView, TVIEW} from './interfaces/view'; import {CONTEXT, HEADER_OFFSET, HOST, LView, TVIEW} from './interfaces/view';
import {getComponentLViewByIndex, getNativeByTNodeOrNull, readPatchedData, unwrapRNode} from './util/view_utils'; import {getComponentLViewByIndex, readPatchedData, unwrapRNode} from './util/view_utils';

View File

@ -65,7 +65,7 @@ export function setMaskBit(hasChange: boolean) {
export function applyI18n(tView: TView, lView: LView, index: number) { export function applyI18n(tView: TView, lView: LView, index: number) {
if (changeMaskCounter > 0) { if (changeMaskCounter > 0) {
ngDevMode && assertDefined(tView, `tView should be defined`); ngDevMode && assertDefined(tView, `tView should be defined`);
const tI18n = tView.data[index + HEADER_OFFSET] as TI18n | I18nUpdateOpCodes; const tI18n = tView.data[index] as TI18n | I18nUpdateOpCodes;
// When `index` points to an `ɵɵi18nAttributes` then we have an array otherwise `TI18n` // When `index` points to an `ɵɵi18nAttributes` then we have an array otherwise `TI18n`
const updateOpCodes: I18nUpdateOpCodes = const updateOpCodes: I18nUpdateOpCodes =
Array.isArray(tI18n) ? tI18n as I18nUpdateOpCodes : (tI18n as TI18n).update; Array.isArray(tI18n) ? tI18n as I18nUpdateOpCodes : (tI18n as TI18n).update;
@ -195,8 +195,8 @@ export function applyMutableOpCodes(
// 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
setElementAttribute( setElementAttribute(
renderer, getNativeByIndex(elementNodeIndex - HEADER_OFFSET, lView) as RElement, null, renderer, getNativeByIndex(elementNodeIndex, lView) as RElement, null, null, attrName,
null, attrName, attrValue, null); attrValue, null);
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}"`);
@ -281,7 +281,7 @@ export function applyUpdateOpCodes(
const propName = updateOpCodes[++j] as string; const propName = updateOpCodes[++j] as string;
const sanitizeFn = updateOpCodes[++j] as SanitizerFn | null; const sanitizeFn = updateOpCodes[++j] as SanitizerFn | null;
const tNodeOrTagName = tView.data[nodeIndex] as TNode | string; const tNodeOrTagName = tView.data[nodeIndex] as TNode | string;
ngDevMode && assertDefined(tNodeOrTagName, 'Expecting TNode or string'); ngDevMode && assertDefined(tNodeOrTagName, 'Experting TNode or string');
if (typeof tNodeOrTagName === 'string') { if (typeof tNodeOrTagName === 'string') {
// IF we don't have a `TNode`, then we are an element in ICU (as ICU content does // IF we don't have a `TNode`, then we are an element in ICU (as ICU content does
// not have TNode), in which case we know that there are no directives, and hence // not have TNode), in which case we know that there are no directives, and hence

View File

@ -132,7 +132,7 @@ export function i18nStartFirstCreatePass(
} }
} }
tView.data[index + HEADER_OFFSET] = <TI18n>{ tView.data[index] = <TI18n>{
create: createOpCodes, create: createOpCodes,
update: updateOpCodes, update: updateOpCodes,
}; };
@ -247,11 +247,11 @@ export function i18nAttributesFirstPass(
// Even indexes are text (including bindings) // Even indexes are text (including bindings)
const hasBinding = !!value.match(BINDING_REGEXP); const hasBinding = !!value.match(BINDING_REGEXP);
if (hasBinding) { if (hasBinding) {
if (tView.firstCreatePass && tView.data[index + HEADER_OFFSET] === null) { if (tView.firstCreatePass && tView.data[index] === null) {
generateBindingUpdateOpCodes(updateOpCodes, value, previousElementIndex, attrName); generateBindingUpdateOpCodes(updateOpCodes, value, previousElementIndex, attrName);
} }
} else { } else {
const tNode = getTNode(tView, previousElementIndex - HEADER_OFFSET); const tNode = getTNode(tView, previousElementIndex);
// Set attributes for Elements only, for other types (like ElementContainer), // Set attributes for Elements only, for other types (like ElementContainer),
// only set inputs below // only set inputs below
if (tNode.type & TNodeType.AnyRNode) { if (tNode.type & TNodeType.AnyRNode) {
@ -262,9 +262,7 @@ export function i18nAttributesFirstPass(
if (dataValue) { if (dataValue) {
setInputsForProperty(tView, lView, dataValue, attrName, value); setInputsForProperty(tView, lView, dataValue, attrName, value);
if (ngDevMode) { if (ngDevMode) {
const element = const element = getNativeByIndex(previousElementIndex, lView) as RElement | RComment;
getNativeByIndex(previousElementIndex - HEADER_OFFSET, lView) as RElement |
RComment;
setNgReflectProperties(lView, element, tNode.type, dataValue, value); setNgReflectProperties(lView, element, tNode.type, dataValue, value);
} }
} }
@ -273,8 +271,8 @@ export function i18nAttributesFirstPass(
} }
} }
if (tView.firstCreatePass && tView.data[index + HEADER_OFFSET] === null) { if (tView.firstCreatePass && tView.data[index] === null) {
tView.data[index + HEADER_OFFSET] = updateOpCodes; tView.data[index] = updateOpCodes;
} }
} }
@ -329,25 +327,6 @@ export function generateBindingUpdateOpCodes(
return mask; return mask;
} }
function getBindingMask(icuExpression: IcuExpression, mask = 0): number {
mask = mask | toMaskBit(icuExpression.mainBinding);
let match;
for (let i = 0; i < icuExpression.values.length; i++) {
const valueArr = icuExpression.values[i];
for (let j = 0; j < valueArr.length; j++) {
const value = valueArr[j];
if (typeof value === 'string') {
while (match = BINDING_REGEXP.exec(value)) {
mask = mask | toMaskBit(parseInt(match[1], 10));
}
} else {
mask = getBindingMask(value as IcuExpression, mask);
}
}
}
return mask;
}
/** /**
* Convert binding index to mask bit. * Convert binding index to mask bit.

View File

@ -8,7 +8,7 @@
import {assertGreaterThan} from '../../util/assert'; import {assertGreaterThan} from '../../util/assert';
import {assertIndexInDeclRange} from '../assert'; import {assertIndexInDeclRange} from '../assert';
import {executeCheckHooks, executeInitAndCheckHooks} from '../hooks'; import {executeCheckHooks, executeInitAndCheckHooks} from '../hooks';
import {FLAGS, HEADER_OFFSET, InitPhaseState, LView, LViewFlags, TView} from '../interfaces/view'; import {FLAGS, InitPhaseState, LView, LViewFlags, TView} from '../interfaces/view';
import {getLView, getSelectedIndex, getTView, isInCheckNoChangesMode, setSelectedIndex} from '../state'; import {getLView, getSelectedIndex, getTView, isInCheckNoChangesMode, setSelectedIndex} from '../state';
@ -42,7 +42,7 @@ export function ɵɵadvance(delta: number): void {
export function selectIndexInternal( export function selectIndexInternal(
tView: TView, lView: LView, index: number, checkNoChangesMode: boolean) { tView: TView, lView: LView, index: number, checkNoChangesMode: boolean) {
ngDevMode && assertIndexInDeclRange(lView, index + HEADER_OFFSET); ngDevMode && assertIndexInDeclRange(lView, index);
// Flush the initial hooks for elements in the view that have been added up to this point. // Flush the initial hooks for elements in the view that have been added up to this point.
// PERF WARNING: do NOT extract this to a separate function without running benchmarks // PERF WARNING: do NOT extract this to a separate function without running benchmarks

View File

@ -83,7 +83,8 @@ export function ɵɵelementStart(
const renderer = lView[RENDERER]; const renderer = lView[RENDERER];
const native = lView[adjustedIndex] = createElementNode(renderer, name, getNamespace()); const native = lView[adjustedIndex] = createElementNode(renderer, name, getNamespace());
const tNode = tView.firstCreatePass ? const tNode = tView.firstCreatePass ?
elementStartFirstCreatePass(index, tView, lView, native, name, attrsIndex, localRefsIndex) : elementStartFirstCreatePass(
adjustedIndex, tView, lView, native, name, attrsIndex, localRefsIndex) :
tView.data[adjustedIndex] as TElementNode; tView.data[adjustedIndex] as TElementNode;
setCurrentTNode(tNode, true); setCurrentTNode(tNode, true);

View File

@ -72,7 +72,8 @@ export function ɵɵelementContainerStart(
'element containers should be created before any bindings'); 'element containers should be created before any bindings');
const tNode = tView.firstCreatePass ? const tNode = tView.firstCreatePass ?
elementContainerStartFirstCreatePass(index, tView, lView, attrsIndex, localRefsIndex) : elementContainerStartFirstCreatePass(
adjustedIndex, tView, lView, attrsIndex, localRefsIndex) :
tView.data[adjustedIndex] as TElementContainerNode; tView.data[adjustedIndex] as TElementContainerNode;
setCurrentTNode(tNode, true); setCurrentTNode(tNode, true);

View File

@ -49,15 +49,16 @@ export function ɵɵi18nStart(
index: number, messageIndex: number, subTemplateIndex: number = -1): void { index: number, messageIndex: number, subTemplateIndex: number = -1): void {
const tView = getTView(); const tView = getTView();
const lView = getLView(); const lView = getLView();
const adjustedIndex = HEADER_OFFSET + index;
ngDevMode && assertDefined(tView, `tView should be defined`); ngDevMode && assertDefined(tView, `tView should be defined`);
const message = getConstant<string>(tView.consts, messageIndex)!; const message = getConstant<string>(tView.consts, messageIndex)!;
const parentTNode = getCurrentParentTNode() as TElementNode | null; const parentTNode = getCurrentParentTNode() as TElementNode | null;
if (tView.firstCreatePass) { if (tView.firstCreatePass) {
i18nStartFirstCreatePass( i18nStartFirstCreatePass(
tView, parentTNode === null ? 0 : parentTNode.index, lView, index, message, tView, parentTNode === null ? 0 : parentTNode.index, lView, adjustedIndex, message,
subTemplateIndex); subTemplateIndex);
} }
const tI18n = tView.data[HEADER_OFFSET + index] as TI18n; const tI18n = tView.data[adjustedIndex] as TI18n;
const sameViewParentTNode = parentTNode === lView[T_HOST] ? null : parentTNode; const sameViewParentTNode = parentTNode === lView[T_HOST] ? null : parentTNode;
const parentRNode = getClosestRElement(tView, sameViewParentTNode, lView); const parentRNode = getClosestRElement(tView, sameViewParentTNode, lView);
// If `parentTNode` is an `ElementContainer` than it has `<!--ng-container--->`. // If `parentTNode` is an `ElementContainer` than it has `<!--ng-container--->`.
@ -125,7 +126,7 @@ export function ɵɵi18nAttributes(index: number, attrsIndex: number): void {
const tView = getTView(); const tView = getTView();
ngDevMode && assertDefined(tView, `tView should be defined`); ngDevMode && assertDefined(tView, `tView should be defined`);
const attrs = getConstant<string[]>(tView.consts, attrsIndex)!; const attrs = getConstant<string[]>(tView.consts, attrsIndex)!;
i18nAttributesFirstPass(lView, tView, index, attrs); i18nAttributesFirstPass(lView, tView, index + HEADER_OFFSET, attrs);
} }
@ -154,7 +155,7 @@ export function ɵɵi18nExp<T>(value: T): typeof ɵɵi18nExp {
* @codeGenApi * @codeGenApi
*/ */
export function ɵɵi18nApply(index: number) { export function ɵɵi18nApply(index: number) {
applyI18n(getTView(), getLView(), index); applyI18n(getTView(), getLView(), index + HEADER_OFFSET);
} }
/** /**

View File

@ -676,18 +676,3 @@ export class LContainerDebug implements ILContainerDebug {
return toDebug(this._raw_lContainer[NEXT]); return toDebug(this._raw_lContainer[NEXT]);
} }
} }
/**
* Return an `LView` value if found.
*
* @param value `LView` if any
*/
export function readLViewValue(value: any): LView|null {
while (Array.isArray(value)) {
// This check is not quite right, as it does not take into account `StylingContext`
// This is why it is in debug, not in util.ts
if (value.length >= HEADER_OFFSET - 1) return value as LView;
value = value[HOST];
}
return null;
}

View File

@ -8,7 +8,7 @@
import {newArray} from '../../util/array_utils'; import {newArray} from '../../util/array_utils';
import {TAttributes, TElementNode, TNode, TNodeFlags, TNodeType} from '../interfaces/node'; import {TAttributes, TElementNode, TNode, TNodeFlags, TNodeType} from '../interfaces/node';
import {ProjectionSlots} from '../interfaces/projection'; import {ProjectionSlots} from '../interfaces/projection';
import {DECLARATION_COMPONENT_VIEW, T_HOST} from '../interfaces/view'; import {DECLARATION_COMPONENT_VIEW, HEADER_OFFSET, T_HOST} from '../interfaces/view';
import {applyProjection} from '../node_manipulation'; import {applyProjection} from '../node_manipulation';
import {getProjectAsAttrValue, isNodeMatchingSelectorList, isSelectorInSelectorList} from '../node_selector_matcher'; import {getProjectAsAttrValue, isNodeMatchingSelectorList, isSelectorInSelectorList} from '../node_selector_matcher';
import {getLView, getTView, setCurrentTNodeAsNotParent} from '../state'; import {getLView, getTView, setCurrentTNodeAsNotParent} from '../state';
@ -120,7 +120,7 @@ export function ɵɵprojection(
const lView = getLView(); const lView = getLView();
const tView = getTView(); const tView = getTView();
const tProjectionNode = const tProjectionNode =
getOrCreateTNode(tView, nodeIndex, TNodeType.Projection, null, attrs || null); getOrCreateTNode(tView, HEADER_OFFSET + nodeIndex, TNodeType.Projection, null, attrs || null);
// We can't use viewData[HOST_NODE] because projection nodes can be nested in embedded views. // We can't use viewData[HOST_NODE] because projection nodes can be nested in embedded views.
if (tProjectionNode.projection === null) tProjectionNode.projection = selectorIndex; if (tProjectionNode.projection === null) tProjectionNode.projection = selectorIndex;

View File

@ -12,7 +12,7 @@ import {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, SchemaMetadata} from '../../me
import {ViewEncapsulation} from '../../metadata/view'; import {ViewEncapsulation} from '../../metadata/view';
import {validateAgainstEventAttributes, validateAgainstEventProperties} from '../../sanitization/sanitization'; import {validateAgainstEventAttributes, validateAgainstEventProperties} from '../../sanitization/sanitization';
import {Sanitizer} from '../../sanitization/sanitizer'; import {Sanitizer} from '../../sanitization/sanitizer';
import {assertDefined, assertDomNode, assertEqual, assertGreaterThan, assertIndexInRange, assertLessThan, assertNotEqual, assertNotSame, assertSame, assertString} from '../../util/assert'; import {assertDefined, assertDomNode, assertEqual, assertGreaterThan, assertGreaterThanOrEqual, assertIndexInRange, assertLessThan, assertNotEqual, assertNotSame, assertSame, assertString} from '../../util/assert';
import {createNamedArrayType} from '../../util/named_array_type'; import {createNamedArrayType} from '../../util/named_array_type';
import {initNgDevMode} from '../../util/ng_dev_mode'; import {initNgDevMode} from '../../util/ng_dev_mode';
import {normalizeDebugBindingName, normalizeDebugBindingValue} from '../../util/ng_reflect'; import {normalizeDebugBindingName, normalizeDebugBindingValue} from '../../util/ng_reflect';
@ -214,12 +214,14 @@ export function getOrCreateTNode(
export function getOrCreateTNode( export function getOrCreateTNode(
tView: TView, index: number, type: TNodeType, name: string|null, attrs: TAttributes|null): tView: TView, index: number, type: TNodeType, name: string|null, attrs: TAttributes|null):
TElementNode&TContainerNode&TElementContainerNode&TProjectionNode&TIcuContainerNode { TElementNode&TContainerNode&TElementContainerNode&TProjectionNode&TIcuContainerNode {
ngDevMode && index !== 0 && // 0 are bogus nodes and they are OK. See `createContainerRef` in
// `view_engine_compatibility` for additional context.
assertGreaterThanOrEqual(index, HEADER_OFFSET, 'TNodes can\'t be in the LView header.');
// Keep this function short, so that the VM will inline it. // Keep this function short, so that the VM will inline it.
ngDevMode && assertPureTNodeType(type); ngDevMode && assertPureTNodeType(type);
const adjustedIndex = index + HEADER_OFFSET; let tNode = tView.data[index] as TNode;
let tNode = tView.data[adjustedIndex] as TNode;
if (tNode === null) { if (tNode === null) {
tNode = createTNodeAtIndex(tView, adjustedIndex, type, name, attrs); tNode = createTNodeAtIndex(tView, index, type, name, attrs);
if (isInI18nBlock()) { if (isInI18nBlock()) {
// If we are in i18n block then all elements should be pre declared through `Placeholder` // If we are in i18n block then all elements should be pre declared through `Placeholder`
// See `TNodeType.Placeholder` and `LFrame.inI18n` for more context. // See `TNodeType.Placeholder` and `LFrame.inI18n` for more context.
@ -234,7 +236,7 @@ export function getOrCreateTNode(
const parent = getCurrentParentTNode(); const parent = getCurrentParentTNode();
tNode.injectorIndex = parent === null ? -1 : parent.injectorIndex; tNode.injectorIndex = parent === null ? -1 : parent.injectorIndex;
ngDevMode && assertTNodeForTView(tNode, tView); ngDevMode && assertTNodeForTView(tNode, tView);
ngDevMode && assertEqual(index + HEADER_OFFSET, tNode.index, 'Expecting same index'); ngDevMode && assertEqual(index, tNode.index, 'Expecting same index');
} }
setCurrentTNode(tNode, true); setCurrentTNode(tNode, true);
return tNode as TElementNode & TContainerNode & TElementContainerNode & TProjectionNode & return tNode as TElementNode & TContainerNode & TElementContainerNode & TProjectionNode &
@ -242,14 +244,13 @@ export function getOrCreateTNode(
} }
export function createTNodeAtIndex( export function createTNodeAtIndex(
tView: TView, adjustedIndex: number, type: TNodeType, name: string|null, tView: TView, index: number, type: TNodeType, name: string|null, attrs: TAttributes|null) {
attrs: TAttributes|null) {
const currentTNode = getCurrentTNodePlaceholderOk(); const currentTNode = getCurrentTNodePlaceholderOk();
const isParent = isCurrentTNodeParent(); const isParent = isCurrentTNodeParent();
const parent = isParent ? currentTNode : currentTNode && currentTNode.parent; const parent = isParent ? currentTNode : currentTNode && currentTNode.parent;
// Parents cannot cross component boundaries because components will be used in multiple places. // Parents cannot cross component boundaries because components will be used in multiple places.
const tNode = tView.data[adjustedIndex] = const tNode = tView.data[index] =
createTNode(tView, parent as TElementNode | TContainerNode, type, adjustedIndex, name, attrs); createTNode(tView, parent as TElementNode | TContainerNode, type, index, name, attrs);
// Assign a pointer to the first child node of a given view. The first node is not always the one // Assign a pointer to the first child node of a given view. The first node is not always the one
// at index 0, in case of i18n, index 0 can be the instruction `i18nStart` and the first node has // at index 0, in case of i18n, index 0 can be the instruction `i18nStart` and the first node has
// the index 1 or more, so we can't just check node index. // the index 1 or more, so we can't just check node index.
@ -546,7 +547,7 @@ function executeTemplate<T>(
if (rf & RenderFlags.Update && lView.length > HEADER_OFFSET) { if (rf & RenderFlags.Update && lView.length > HEADER_OFFSET) {
// When we're updating, inherently select 0 so we don't // When we're updating, inherently select 0 so we don't
// have to generate that instruction for most update blocks. // have to generate that instruction for most update blocks.
selectIndexInternal(tView, lView, 0, isInCheckNoChangesMode()); selectIndexInternal(tView, lView, HEADER_OFFSET, isInCheckNoChangesMode());
} }
templateFn(rf, context); templateFn(rf, context);
} finally { } finally {
@ -810,7 +811,7 @@ export function storeCleanupWithContext(
* @param tView `TView` to which this `TNode` belongs (used only in `ngDevMode`) * @param tView `TView` to which this `TNode` belongs (used only in `ngDevMode`)
* @param tParent Parent `TNode` * @param tParent Parent `TNode`
* @param type The type of the node * @param type The type of the node
* @param adjustedIndex The index of the TNode in TView.data, adjusted for HEADER_OFFSET * @param index The index of the TNode in TView.data, adjusted for HEADER_OFFSET
* @param tagName The tag name of the node * @param tagName The tag name of the node
* @param attrs The attributes defined on this node * @param attrs The attributes defined on this node
* @param tViews Any TViews attached to this node * @param tViews Any TViews attached to this node
@ -818,25 +819,28 @@ export function storeCleanupWithContext(
*/ */
export function createTNode( export function createTNode(
tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType.Container, tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType.Container,
adjustedIndex: number, tagName: string|null, attrs: TAttributes|null): TContainerNode; index: number, tagName: string|null, attrs: TAttributes|null): TContainerNode;
export function createTNode( export function createTNode(
tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType.Element|TNodeType.Text, tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType.Element|TNodeType.Text,
adjustedIndex: number, tagName: string|null, attrs: TAttributes|null): TElementNode; index: number, tagName: string|null, attrs: TAttributes|null): TElementNode;
export function createTNode( export function createTNode(
tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType.ElementContainer, tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType.ElementContainer,
adjustedIndex: number, tagName: string|null, attrs: TAttributes|null): TElementContainerNode; index: number, tagName: string|null, attrs: TAttributes|null): TElementContainerNode;
export function createTNode( export function createTNode(
tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType.Icu, tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType.Icu, index: number,
adjustedIndex: number, tagName: string|null, attrs: TAttributes|null): TIcuContainerNode; tagName: string|null, attrs: TAttributes|null): TIcuContainerNode;
export function createTNode( export function createTNode(
tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType.Projection, tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType.Projection,
adjustedIndex: number, tagName: string|null, attrs: TAttributes|null): TProjectionNode; index: number, tagName: string|null, attrs: TAttributes|null): TProjectionNode;
export function createTNode( export function createTNode(
tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType, adjustedIndex: number, tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType, index: number,
tagName: string|null, attrs: TAttributes|null): TNode; tagName: string|null, attrs: TAttributes|null): TNode;
export function createTNode( export function createTNode(
tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType, adjustedIndex: number, tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType, index: number,
value: string|null, attrs: TAttributes|null): TNode { value: string|null, attrs: TAttributes|null): TNode {
ngDevMode && index !== 0 && // 0 are bogus nodes and they are OK. See `createContainerRef` in
// `view_engine_compatibility` for additional context.
assertGreaterThanOrEqual(index, HEADER_OFFSET, 'TNodes can\'t be in the LView header.');
ngDevMode && assertNotSame(attrs, undefined, '\'undefined\' is not valid value for \'attrs\''); ngDevMode && assertNotSame(attrs, undefined, '\'undefined\' is not valid value for \'attrs\'');
ngDevMode && ngDevMode.tNode++; ngDevMode && ngDevMode.tNode++;
ngDevMode && tParent && assertTNodeForTView(tParent, tView); ngDevMode && tParent && assertTNodeForTView(tParent, tView);
@ -845,7 +849,7 @@ export function createTNode(
new TNodeDebug( new TNodeDebug(
tView, // tView_: TView tView, // tView_: TView
type, // type: TNodeType type, // type: TNodeType
adjustedIndex, // index: number index, // index: number
null, // insertBeforeIndex: null|-1|number|number[] null, // insertBeforeIndex: null|-1|number|number[]
injectorIndex, // injectorIndex: number injectorIndex, // injectorIndex: number
-1, // directiveStart: number -1, // directiveStart: number
@ -877,10 +881,10 @@ export function createTNode(
0 as any, // styleBindings: TStylingRange; 0 as any, // styleBindings: TStylingRange;
) : ) :
{ {
type: type, type,
index: adjustedIndex, index,
insertBeforeIndex: null, insertBeforeIndex: null,
injectorIndex: injectorIndex, injectorIndex,
directiveStart: -1, directiveStart: -1,
directiveEnd: -1, directiveEnd: -1,
directiveStylingLast: -1, directiveStylingLast: -1,
@ -1213,13 +1217,12 @@ export function resolveDirectives(
// We will push the actual hook function into this array later during dir instantiation. // We will push the actual hook function into this array later during dir instantiation.
// We cannot do it now because we must ensure hooks are registered in the same // We cannot do it now because we must ensure hooks are registered in the same
// order that directives are created (i.e. injection order). // order that directives are created (i.e. injection order).
(tView.preOrderHooks || (tView.preOrderHooks = [])).push(tNode.index - HEADER_OFFSET); (tView.preOrderHooks || (tView.preOrderHooks = [])).push(tNode.index);
preOrderHooksFound = true; preOrderHooksFound = true;
} }
if (!preOrderCheckHooksFound && (lifeCycleHooks.ngOnChanges || lifeCycleHooks.ngDoCheck)) { if (!preOrderCheckHooksFound && (lifeCycleHooks.ngOnChanges || lifeCycleHooks.ngDoCheck)) {
(tView.preOrderCheckHooks || (tView.preOrderCheckHooks = [])) (tView.preOrderCheckHooks || (tView.preOrderCheckHooks = [])).push(tNode.index);
.push(tNode.index - HEADER_OFFSET);
preOrderCheckHooksFound = true; preOrderCheckHooksFound = true;
} }
@ -1325,7 +1328,7 @@ function invokeDirectivesHostBindings(tView: TView, lView: LView, tNode: TNode)
const end = tNode.directiveEnd; const end = tNode.directiveEnd;
const expando = tView.expandoInstructions!; const expando = tView.expandoInstructions!;
const firstCreatePass = tView.firstCreatePass; const firstCreatePass = tView.firstCreatePass;
const elementIndex = tNode.index - HEADER_OFFSET; const elementIndex = tNode.index;
const currentDirectiveIndex = getCurrentDirectiveIndex(); const currentDirectiveIndex = getCurrentDirectiveIndex();
try { try {
setSelectedIndex(elementIndex); setSelectedIndex(elementIndex);
@ -1373,7 +1376,7 @@ export function generateExpandoInstructionBlock(
// Important: In JS `-x` and `0-x` is not the same! If `x===0` then `-x` will produce `-0` which // Important: In JS `-x` and `0-x` is not the same! If `x===0` then `-x` will produce `-0` which
// requires non standard math arithmetic and it can prevent VM optimizations. // requires non standard math arithmetic and it can prevent VM optimizations.
// `0-0` will always produce `0` and will not cause a potential deoptimization in VM. // `0-0` will always produce `0` and will not cause a potential deoptimization in VM.
const elementIndex = HEADER_OFFSET - tNode.index; const elementIndex = 0 - tNode.index;
const providerStartIndex = tNode.providerIndexes & TNodeProviderIndexes.ProvidersStartIndexMask; const providerStartIndex = tNode.providerIndexes & TNodeProviderIndexes.ProvidersStartIndexMask;
const providerCount = tView.data.length - providerStartIndex; const providerCount = tView.data.length - providerStartIndex;
(tView.expandoInstructions || (tView.expandoInstructions = [])) (tView.expandoInstructions || (tView.expandoInstructions = []))
@ -2087,7 +2090,7 @@ export function setInputsForProperty(
export function textBindingInternal(lView: LView, index: number, value: string): void { export function textBindingInternal(lView: LView, index: number, value: string): void {
ngDevMode && assertString(value, 'Value should be a string'); ngDevMode && assertString(value, 'Value should be a string');
ngDevMode && assertNotSame(value, NO_CHANGE as any, 'value should not be NO_CHANGE'); ngDevMode && assertNotSame(value, NO_CHANGE as any, 'value should not be NO_CHANGE');
ngDevMode && assertIndexInRange(lView, index + HEADER_OFFSET); ngDevMode && assertIndexInRange(lView, index);
const element = getNativeByIndex(index, lView) as any as RText; const element = getNativeByIndex(index, lView) as any as RText;
ngDevMode && assertDefined(element, 'native element should exist'); ngDevMode && assertDefined(element, 'native element should exist');
updateTextNode(lView[RENDERER], element, value); updateTextNode(lView[RENDERER], element, value);

View File

@ -14,12 +14,11 @@ import {load} from '../util/view_utils';
export function store<T>(tView: TView, lView: LView, index: number, value: T): void { export function store<T>(tView: TView, lView: LView, index: number, value: T): void {
// We don't store any static data for local variables, so the first time // We don't store any static data for local variables, so the first time
// we see the template, we should store as null to avoid a sparse array // we see the template, we should store as null to avoid a sparse array
const adjustedIndex = index + HEADER_OFFSET; if (index >= tView.data.length) {
if (adjustedIndex >= tView.data.length) { tView.data[index] = null;
tView.data[adjustedIndex] = null; tView.blueprint[index] = null;
tView.blueprint[adjustedIndex] = null;
} }
lView[adjustedIndex] = value; lView[index] = value;
} }
/** /**
@ -34,5 +33,5 @@ export function store<T>(tView: TView, lView: LView, index: number, value: T): v
*/ */
export function ɵɵreference<T>(index: number) { export function ɵɵreference<T>(index: number) {
const contextLView = getContextLView(); const contextLView = getContextLView();
return load<T>(contextLView, index); return load<T>(contextLView, HEADER_OFFSET + index);
} }

View File

@ -17,14 +17,13 @@ import {DirectiveDef} from '../interfaces/definition';
import {AttributeMarker, TAttributes, TNode, TNodeFlags, TNodeType} from '../interfaces/node'; import {AttributeMarker, TAttributes, TNode, TNodeFlags, TNodeType} from '../interfaces/node';
import {RElement, Renderer3} from '../interfaces/renderer'; import {RElement, Renderer3} from '../interfaces/renderer';
import {getTStylingRangeNext, getTStylingRangeNextDuplicate, getTStylingRangePrev, getTStylingRangePrevDuplicate, TStylingKey, TStylingRange} from '../interfaces/styling'; import {getTStylingRangeNext, getTStylingRangeNextDuplicate, getTStylingRangePrev, getTStylingRangePrevDuplicate, TStylingKey, TStylingRange} from '../interfaces/styling';
import {HEADER_OFFSET, LView, RENDERER, TData, TView} from '../interfaces/view'; import {LView, RENDERER, TData, TView} from '../interfaces/view';
import {applyStyling} from '../node_manipulation'; import {applyStyling} from '../node_manipulation';
import {getCurrentDirectiveDef, getLView, getSelectedIndex, getTView, incrementBindingIndex} from '../state'; import {getCurrentDirectiveDef, getLView, getSelectedIndex, getTView, incrementBindingIndex} from '../state';
import {insertTStylingBinding} from '../styling/style_binding_list'; import {insertTStylingBinding} from '../styling/style_binding_list';
import {getLastParsedKey, getLastParsedValue, parseClassName, parseClassNameNext, parseStyle, parseStyleNext} from '../styling/styling_parser'; import {getLastParsedKey, getLastParsedValue, parseClassName, parseClassNameNext, parseStyle, parseStyleNext} from '../styling/styling_parser';
import {NO_CHANGE} from '../tokens'; import {NO_CHANGE} from '../tokens';
import {getNativeByIndex} from '../util/view_utils'; import {getNativeByIndex} from '../util/view_utils';
import {setDirectiveInputsWhichShadowsStyling} from './property'; import {setDirectiveInputsWhichShadowsStyling} from './property';
@ -174,7 +173,7 @@ export function checkStylingProperty(
stylingFirstUpdatePass(tView, prop, bindingIndex, isClassBased); stylingFirstUpdatePass(tView, prop, bindingIndex, isClassBased);
} }
if (value !== NO_CHANGE && bindingUpdated(lView, bindingIndex, value)) { if (value !== NO_CHANGE && bindingUpdated(lView, bindingIndex, value)) {
const tNode = tView.data[getSelectedIndex() + HEADER_OFFSET] as TNode; const tNode = tView.data[getSelectedIndex()] as TNode;
updateStyling( updateStyling(
tView, tNode, lView, lView[RENDERER], prop, tView, tNode, lView, lView[RENDERER], prop,
lView[bindingIndex + 1] = normalizeSuffix(value, suffix), isClassBased, bindingIndex); lView[bindingIndex + 1] = normalizeSuffix(value, suffix), isClassBased, bindingIndex);
@ -204,7 +203,7 @@ export function checkStylingMap(
if (value !== NO_CHANGE && bindingUpdated(lView, bindingIndex, value)) { if (value !== NO_CHANGE && bindingUpdated(lView, bindingIndex, value)) {
// `getSelectedIndex()` should be here (rather than in instruction) so that it is guarded by the // `getSelectedIndex()` should be here (rather than in instruction) so that it is guarded by the
// if so as not to read unnecessarily. // if so as not to read unnecessarily.
const tNode = tView.data[getSelectedIndex() + HEADER_OFFSET] as TNode; const tNode = tView.data[getSelectedIndex()] as TNode;
if (hasStylingInputShadow(tNode, isClassBased) && !isInHostBindings(tView, bindingIndex)) { if (hasStylingInputShadow(tNode, isClassBased) && !isInHostBindings(tView, bindingIndex)) {
if (ngDevMode) { if (ngDevMode) {
// verify that if we are shadowing then `TData` is appropriately marked so that we skip // verify that if we are shadowing then `TData` is appropriately marked so that we skip
@ -271,7 +270,8 @@ function stylingFirstUpdatePass(
// itself to the list. // itself to the list.
// `getSelectedIndex()` should be here (rather than in instruction) so that it is guarded by the // `getSelectedIndex()` should be here (rather than in instruction) so that it is guarded by the
// if so as not to read unnecessarily. // if so as not to read unnecessarily.
const tNode = tData[getSelectedIndex() + HEADER_OFFSET] as TNode; const tNode = tData[getSelectedIndex()] as TNode;
ngDevMode && assertDefined(tNode, 'TNode expected');
const isHostBindings = isInHostBindings(tView, bindingIndex); const isHostBindings = isInHostBindings(tView, bindingIndex);
if (hasStylingInputShadow(tNode, isClassBased) && tStylingKey === null && !isHostBindings) { if (hasStylingInputShadow(tNode, isClassBased) && tStylingKey === null && !isHostBindings) {
// `tStylingKey === null` implies that we are either `[style]` or `[class]` binding. // `tStylingKey === null` implies that we are either `[style]` or `[class]` binding.

View File

@ -73,10 +73,10 @@ export function ɵɵtemplate(
const tView = getTView(); const tView = getTView();
const adjustedIndex = index + HEADER_OFFSET; const adjustedIndex = index + HEADER_OFFSET;
const tNode = tView.firstCreatePass ? const tNode = tView.firstCreatePass ? templateFirstCreatePass(
templateFirstCreatePass( adjustedIndex, tView, lView, templateFn, decls, vars,
index, tView, lView, templateFn, decls, vars, tagName, attrsIndex, localRefsIndex) : tagName, attrsIndex, localRefsIndex) :
tView.data[adjustedIndex] as TContainerNode; tView.data[adjustedIndex] as TContainerNode;
setCurrentTNode(tNode, false); setCurrentTNode(tNode, false);
const comment = lView[RENDERER].createComment(ngDevMode ? 'container' : ''); const comment = lView[RENDERER].createComment(ngDevMode ? 'container' : '');

View File

@ -35,7 +35,7 @@ export function ɵɵtext(index: number, value: string = ''): void {
ngDevMode && assertIndexInRange(lView, adjustedIndex); ngDevMode && assertIndexInRange(lView, adjustedIndex);
const tNode = tView.firstCreatePass ? const tNode = tView.firstCreatePass ?
getOrCreateTNode(tView, index, TNodeType.Text, value, null) : getOrCreateTNode(tView, adjustedIndex, TNodeType.Text, value, null) :
tView.data[adjustedIndex] as TElementNode; tView.data[adjustedIndex] as TElementNode;
const textNative = lView[adjustedIndex] = createTextNode(lView[RENDERER], value); const textNative = lView[adjustedIndex] = createTextNode(lView[RENDERER], value);

View File

@ -47,7 +47,13 @@ export const DECLARATION_COMPONENT_VIEW = 16;
export const DECLARATION_LCONTAINER = 17; export const DECLARATION_LCONTAINER = 17;
export const PREORDER_HOOK_FLAGS = 18; export const PREORDER_HOOK_FLAGS = 18;
export const QUERIES = 19; export const QUERIES = 19;
/** Size of LView's header. Necessary to adjust for it when setting slots. */ /**
* Size of LView's header. Necessary to adjust for it when setting slots.
*
* IMPORTANT: `HEADER_OFFSET` should only be referred to the in the `ɵɵ*` instructions to translate
* instruction index into `LView` index. All other indexes should be in the `LView` index space and
* there should be no need to refer to `HEADER_OFFSET` anywhere else.
*/
export const HEADER_OFFSET = 20; export const HEADER_OFFSET = 20;

View File

@ -54,7 +54,7 @@ export function ɵɵpipe(index: number, pipeName: string): any {
const previousIncludeViewProviders = setIncludeViewProviders(false); const previousIncludeViewProviders = setIncludeViewProviders(false);
const pipeInstance = pipeFactory(); const pipeInstance = pipeFactory();
setIncludeViewProviders(previousIncludeViewProviders); setIncludeViewProviders(previousIncludeViewProviders);
store(tView, getLView(), index, pipeInstance); store(tView, getLView(), adjustedIndex, pipeInstance);
return pipeInstance; return pipeInstance;
} finally { } finally {
// we have to restore the injector implementation in finally, just in case the creation of the // we have to restore the injector implementation in finally, just in case the creation of the
@ -96,11 +96,12 @@ function getPipeDef(name: string, registry: PipeDefList|null): PipeDef<any> {
* @codeGenApi * @codeGenApi
*/ */
export function ɵɵpipeBind1(index: number, slotOffset: number, v1: any): any { export function ɵɵpipeBind1(index: number, slotOffset: number, v1: any): any {
const adjustedIndex = index + HEADER_OFFSET;
const lView = getLView(); const lView = getLView();
const pipeInstance = load<PipeTransform>(lView, index); const pipeInstance = load<PipeTransform>(lView, adjustedIndex);
return unwrapValue( return unwrapValue(
lView, lView,
isPure(lView, index) ? isPure(lView, adjustedIndex) ?
pureFunction1Internal( pureFunction1Internal(
lView, getBindingRoot(), slotOffset, pipeInstance.transform, v1, pipeInstance) : lView, getBindingRoot(), slotOffset, pipeInstance.transform, v1, pipeInstance) :
pipeInstance.transform(v1)); pipeInstance.transform(v1));
@ -120,11 +121,12 @@ export function ɵɵpipeBind1(index: number, slotOffset: number, v1: any): any {
* @codeGenApi * @codeGenApi
*/ */
export function ɵɵpipeBind2(index: number, slotOffset: number, v1: any, v2: any): any { export function ɵɵpipeBind2(index: number, slotOffset: number, v1: any, v2: any): any {
const adjustedIndex = index + HEADER_OFFSET;
const lView = getLView(); const lView = getLView();
const pipeInstance = load<PipeTransform>(lView, index); const pipeInstance = load<PipeTransform>(lView, adjustedIndex);
return unwrapValue( return unwrapValue(
lView, lView,
isPure(lView, index) ? isPure(lView, adjustedIndex) ?
pureFunction2Internal( pureFunction2Internal(
lView, getBindingRoot(), slotOffset, pipeInstance.transform, v1, v2, pipeInstance) : lView, getBindingRoot(), slotOffset, pipeInstance.transform, v1, v2, pipeInstance) :
pipeInstance.transform(v1, v2)); pipeInstance.transform(v1, v2));
@ -145,14 +147,15 @@ export function ɵɵpipeBind2(index: number, slotOffset: number, v1: any, v2: an
* @codeGenApi * @codeGenApi
*/ */
export function ɵɵpipeBind3(index: number, slotOffset: number, v1: any, v2: any, v3: any): any { export function ɵɵpipeBind3(index: number, slotOffset: number, v1: any, v2: any, v3: any): any {
const adjustedIndex = index + HEADER_OFFSET;
const lView = getLView(); const lView = getLView();
const pipeInstance = load<PipeTransform>(lView, index); const pipeInstance = load<PipeTransform>(lView, adjustedIndex);
return unwrapValue( return unwrapValue(
lView, lView,
isPure(lView, index) ? pureFunction3Internal( isPure(lView, adjustedIndex) ? pureFunction3Internal(
lView, getBindingRoot(), slotOffset, pipeInstance.transform, v1, lView, getBindingRoot(), slotOffset,
v2, v3, pipeInstance) : pipeInstance.transform, v1, v2, v3, pipeInstance) :
pipeInstance.transform(v1, v2, v3)); pipeInstance.transform(v1, v2, v3));
} }
/** /**
@ -172,14 +175,15 @@ export function ɵɵpipeBind3(index: number, slotOffset: number, v1: any, v2: an
*/ */
export function ɵɵpipeBind4( export function ɵɵpipeBind4(
index: number, slotOffset: number, v1: any, v2: any, v3: any, v4: any): any { index: number, slotOffset: number, v1: any, v2: any, v3: any, v4: any): any {
const adjustedIndex = index + HEADER_OFFSET;
const lView = getLView(); const lView = getLView();
const pipeInstance = load<PipeTransform>(lView, index); const pipeInstance = load<PipeTransform>(lView, adjustedIndex);
return unwrapValue( return unwrapValue(
lView, lView,
isPure(lView, index) ? pureFunction4Internal( isPure(lView, adjustedIndex) ? pureFunction4Internal(
lView, getBindingRoot(), slotOffset, pipeInstance.transform, v1, lView, getBindingRoot(), slotOffset,
v2, v3, v4, pipeInstance) : pipeInstance.transform, v1, v2, v3, v4, pipeInstance) :
pipeInstance.transform(v1, v2, v3, v4)); pipeInstance.transform(v1, v2, v3, v4));
} }
/** /**
@ -195,18 +199,19 @@ export function ɵɵpipeBind4(
* @codeGenApi * @codeGenApi
*/ */
export function ɵɵpipeBindV(index: number, slotOffset: number, values: [any, ...any[]]): any { export function ɵɵpipeBindV(index: number, slotOffset: number, values: [any, ...any[]]): any {
const adjustedIndex = index + HEADER_OFFSET;
const lView = getLView(); const lView = getLView();
const pipeInstance = load<PipeTransform>(lView, index); const pipeInstance = load<PipeTransform>(lView, adjustedIndex);
return unwrapValue( return unwrapValue(
lView, lView,
isPure(lView, index) ? isPure(lView, adjustedIndex) ?
pureFunctionVInternal( pureFunctionVInternal(
lView, getBindingRoot(), slotOffset, pipeInstance.transform, values, pipeInstance) : lView, getBindingRoot(), slotOffset, pipeInstance.transform, values, pipeInstance) :
pipeInstance.transform.apply(pipeInstance, values)); pipeInstance.transform.apply(pipeInstance, values));
} }
function isPure(lView: LView, index: number): boolean { function isPure(lView: LView, index: number): boolean {
return (<PipeDef<any>>lView[TVIEW].data[index + HEADER_OFFSET]).pure; return (<PipeDef<any>>lView[TVIEW].data[index]).pure;
} }
/** /**

View File

@ -6,11 +6,11 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {assertDefined, assertEqual, assertNotEqual} from '../util/assert'; import {assertDefined, assertEqual, assertGreaterThanOrEqual, assertLessThan, assertNotEqual} from '../util/assert';
import {assertLViewOrUndefined, assertTNodeForTView} from './assert'; import {assertLViewOrUndefined, assertTNodeForTView} from './assert';
import {DirectiveDef} from './interfaces/definition'; import {DirectiveDef} from './interfaces/definition';
import {TNode, TNodeType} from './interfaces/node'; import {TNode, TNodeType} from './interfaces/node';
import {CONTEXT, DECLARATION_VIEW, LView, OpaqueViewState, TData, TVIEW, TView} from './interfaces/view'; import {CONTEXT, DECLARATION_VIEW, HEADER_OFFSET, LView, OpaqueViewState, TData, TVIEW, TView} from './interfaces/view';
import {MATH_ML_NAMESPACE, SVG_NAMESPACE} from './namespaces'; import {MATH_ML_NAMESPACE, SVG_NAMESPACE} from './namespaces';
import {assertTNodeType} from './node_assert'; import {assertTNodeType} from './node_assert';
import {getTNode} from './util/view_utils'; import {getTNode} from './util/view_utils';
@ -458,13 +458,14 @@ export function enterDI(newView: LView, tNode: TNode) {
* @returns the previously active lView; * @returns the previously active lView;
*/ */
export function enterView(newView: LView): void { export function enterView(newView: LView): void {
ngDevMode && assertNotEqual(newView[0], newView[1] as any, '????');
ngDevMode && assertLViewOrUndefined(newView); ngDevMode && assertLViewOrUndefined(newView);
const newLFrame = allocLFrame(); const newLFrame = allocLFrame();
if (ngDevMode) { if (ngDevMode) {
assertEqual(newLFrame.isParent, true, 'Expected clean LFrame'); assertEqual(newLFrame.isParent, true, 'Expected clean LFrame');
assertEqual(newLFrame.lView, null, 'Expected clean LFrame'); assertEqual(newLFrame.lView, null, 'Expected clean LFrame');
assertEqual(newLFrame.tView, null, 'Expected clean LFrame'); assertEqual(newLFrame.tView, null, 'Expected clean LFrame');
assertEqual(newLFrame.selectedIndex, 0, 'Expected clean LFrame'); assertEqual(newLFrame.selectedIndex, -1, 'Expected clean LFrame');
assertEqual(newLFrame.elementDepthCount, 0, 'Expected clean LFrame'); assertEqual(newLFrame.elementDepthCount, 0, 'Expected clean LFrame');
assertEqual(newLFrame.currentDirectiveIndex, -1, 'Expected clean LFrame'); assertEqual(newLFrame.currentDirectiveIndex, -1, 'Expected clean LFrame');
assertEqual(newLFrame.currentNamespace, null, 'Expected clean LFrame'); assertEqual(newLFrame.currentNamespace, null, 'Expected clean LFrame');
@ -498,7 +499,7 @@ function createLFrame(parent: LFrame|null): LFrame {
isParent: true, isParent: true,
lView: null!, lView: null!,
tView: null!, tView: null!,
selectedIndex: 0, selectedIndex: -1,
contextLView: null!, contextLView: null!,
elementDepthCount: 0, elementDepthCount: 0,
currentNamespace: null, currentNamespace: null,
@ -551,7 +552,7 @@ export function leaveView() {
const oldLFrame = leaveViewLight(); const oldLFrame = leaveViewLight();
oldLFrame.isParent = true; oldLFrame.isParent = true;
oldLFrame.tView = null!; oldLFrame.tView = null!;
oldLFrame.selectedIndex = 0; oldLFrame.selectedIndex = -1;
oldLFrame.contextLView = null!; oldLFrame.contextLView = null!;
oldLFrame.elementDepthCount = 0; oldLFrame.elementDepthCount = 0;
oldLFrame.currentDirectiveIndex = -1; oldLFrame.currentDirectiveIndex = -1;
@ -599,6 +600,11 @@ export function getSelectedIndex() {
* run if and when the provided `index` value is different from the current selected index value.) * run if and when the provided `index` value is different from the current selected index value.)
*/ */
export function setSelectedIndex(index: number) { export function setSelectedIndex(index: number) {
ngDevMode && index !== -1 &&
assertGreaterThanOrEqual(index, HEADER_OFFSET, 'Index must be past HEADER_OFFSET (or -1).');
ngDevMode &&
assertLessThan(
index, instructionState.lFrame.lView.length, 'Can\'t set index passed end of LView');
instructionState.lFrame.selectedIndex = index; instructionState.lFrame.selectedIndex = index;
} }

View File

@ -16,7 +16,7 @@ import {LContext} from '../interfaces/context';
import {DirectiveDef} from '../interfaces/definition'; import {DirectiveDef} from '../interfaces/definition';
import {TElementNode, TNode, TNodeProviderIndexes} from '../interfaces/node'; import {TElementNode, TNode, TNodeProviderIndexes} from '../interfaces/node';
import {isLView} from '../interfaces/type_checks'; import {isLView} from '../interfaces/type_checks';
import {CLEANUP, CONTEXT, DebugNode, FLAGS, HEADER_OFFSET, HOST, LView, LViewFlags, T_HOST, TVIEW, TViewType} from '../interfaces/view'; import {CLEANUP, CONTEXT, DebugNode, FLAGS, LView, LViewFlags, T_HOST, TVIEW, TViewType} from '../interfaces/view';
import {stringifyForError} from './misc_utils'; import {stringifyForError} from './misc_utils';
import {getLViewParent, getRootContext} from './view_traversal_utils'; import {getLViewParent, getRootContext} from './view_traversal_utils';
@ -387,8 +387,8 @@ export function getDebugNode(element: Element): DebugNode|null {
const valueInLView = lView[nodeIndex]; const valueInLView = lView[nodeIndex];
// this means that value in the lView is a component with its own // this means that value in the lView is a component with its own
// data. In this situation the TNode is not accessed at the same spot. // data. In this situation the TNode is not accessed at the same spot.
const tNode = isLView(valueInLView) ? (valueInLView[T_HOST] as TNode) : const tNode =
getTNode(lView[TVIEW], nodeIndex - HEADER_OFFSET); isLView(valueInLView) ? (valueInLView[T_HOST] as TNode) : getTNode(lView[TVIEW], nodeIndex);
ngDevMode && ngDevMode &&
assertEqual(tNode.index, nodeIndex, 'Expecting that TNode at index is same as index'); assertEqual(tNode.index, nodeIndex, 'Expecting that TNode at index is same as index');
debugNode = buildDebugNode(tNode, lView); debugNode = buildDebugNode(tNode, lView);

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {assertDefined, assertDomNode, assertGreaterThan, assertIndexInRange, assertLessThan} from '../../util/assert'; import {assertDefined, assertDomNode, assertGreaterThan, assertGreaterThanOrEqual, assertIndexInRange, assertLessThan} from '../../util/assert';
import {assertTNode, assertTNodeForLView} from '../assert'; import {assertTNode, assertTNodeForLView} from '../assert';
import {LContainer, TYPE} from '../interfaces/container'; import {LContainer, TYPE} from '../interfaces/container';
import {LContext, MONKEY_PATCH_KEY_NAME} from '../interfaces/context'; import {LContext, MONKEY_PATCH_KEY_NAME} from '../interfaces/context';
@ -78,7 +78,9 @@ export function unwrapLContainer(value: RNode|LView|LContainer): LContainer|null
* from any containers, component views, or style contexts. * from any containers, component views, or style contexts.
*/ */
export function getNativeByIndex(index: number, lView: LView): RNode { export function getNativeByIndex(index: number, lView: LView): RNode {
return unwrapRNode(lView[index + HEADER_OFFSET]); ngDevMode && assertIndexInRange(lView, index);
ngDevMode && assertGreaterThanOrEqual(index, HEADER_OFFSET, 'Expected to be past HEADER_OFFSET');
return unwrapRNode(lView[index]);
} }
/** /**
@ -120,16 +122,16 @@ export function getNativeByTNodeOrNull(tNode: TNode|null, lView: LView): RNode|n
// fixme(misko): The return Type should be `TNode|null` // fixme(misko): The return Type should be `TNode|null`
export function getTNode(tView: TView, index: number): TNode { export function getTNode(tView: TView, index: number): TNode {
ngDevMode && assertGreaterThan(index, -1, 'wrong index for TNode'); ngDevMode && assertGreaterThan(index, -1, 'wrong index for TNode');
ngDevMode && assertLessThan(index, tView.data.length - HEADER_OFFSET, 'wrong index for TNode'); ngDevMode && assertLessThan(index, tView.data.length, 'wrong index for TNode');
const tNode = tView.data[index + HEADER_OFFSET] as TNode; const tNode = tView.data[index] as TNode;
ngDevMode && tNode !== null && assertTNode(tNode); ngDevMode && tNode !== null && assertTNode(tNode);
return tNode; return tNode;
} }
/** Retrieves a value from any `LView` or `TData`. */ /** Retrieves a value from any `LView` or `TData`. */
export function load<T>(view: LView|TData, index: number): T { export function load<T>(view: LView|TData, index: number): T {
ngDevMode && assertIndexInRange(view, index + HEADER_OFFSET); ngDevMode && assertIndexInRange(view, index);
return view[index + HEADER_OFFSET]; return view[index];
} }
export function getComponentLViewByIndex(nodeIndex: number, hostView: LView): LView { export function getComponentLViewByIndex(nodeIndex: number, hostView: LView): LView {

View File

@ -391,8 +391,8 @@ export function createContainerRef(
} else { } else {
// The TNode created here is bogus, in that it is not added to the TView. It is only created // The TNode created here is bogus, in that it is not added to the TView. It is only created
// to allow us to create a dynamic Comment node. // to allow us to create a dynamic Comment node.
const commentTNode = createTNode( const commentTNode =
hostView[TVIEW], hostTNode.parent, TNodeType.Container, hostTNode.type, null, null); createTNode(hostView[TVIEW], hostTNode.parent, TNodeType.Container, 0, null, null);
appendChild(hostView[TVIEW], hostView, commentNode, commentTNode); appendChild(hostView[TVIEW], hostView, commentNode, commentTNode);
} }
} }

View File

@ -652,12 +652,12 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
const lViewDebug = lView.debug!; const lViewDebug = lView.debug!;
fixture.detectChanges(); fixture.detectChanges();
expect((fixture.nativeElement as Element).textContent).toEqual('just now'); expect((fixture.nativeElement as Element).textContent).toEqual('just now');
expect(lViewDebug.nodes.map(toTypeContent)).toEqual(['IcuContainer(<!--ICU 0:0-->)']); expect(lViewDebug.nodes.map(toTypeContent)).toEqual(['IcuContainer(<!--ICU 20:0-->)']);
// We want to ensure that the ICU container does not have any content! // We want to ensure that the ICU container does not have any content!
// This is because the content is instance dependent and therefore can't be shared // This is because the content is instance dependent and therefore can't be shared
// across `TNode`s. // across `TNode`s.
expect(lViewDebug.nodes[0].children.map(toTypeContent)).toEqual([]); expect(lViewDebug.nodes[0].children.map(toTypeContent)).toEqual([]);
expect(fixture.nativeElement.innerHTML).toEqual('just now<!--ICU 0:0-->'); expect(fixture.nativeElement.innerHTML).toEqual('just now<!--ICU 20:0-->');
}); });
it('should support multiple ICUs', () => { it('should support multiple ICUs', () => {
@ -674,15 +674,15 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
`); `);
const lView = getComponentLView(fixture.componentInstance); const lView = getComponentLView(fixture.componentInstance);
expect(lView.debug!.nodes.map(toTypeContent)).toEqual([ expect(lView.debug!.nodes.map(toTypeContent)).toEqual([
'IcuContainer(<!--ICU 0:0-->)', 'IcuContainer(<!--ICU 20:0-->)',
'IcuContainer(<!--ICU 1:0-->)', 'IcuContainer(<!--ICU 21:0-->)',
]); ]);
// We want to ensure that the ICU container does not have any content! // We want to ensure that the ICU container does not have any content!
// This is because the content is instance dependent and therefore can't be shared // This is because the content is instance dependent and therefore can't be shared
// across `TNode`s. // across `TNode`s.
expect(lView.debug!.nodes[0].children.map(toTypeContent)).toEqual([]); expect(lView.debug!.nodes[0].children.map(toTypeContent)).toEqual([]);
expect(fixture.nativeElement.innerHTML) expect(fixture.nativeElement.innerHTML)
.toEqual('just now<!--ICU 0:0-->Mr. Angular<!--ICU 1:0-->'); .toEqual('just now<!--ICU 20:0-->Mr. Angular<!--ICU 21:0-->');
}); });
}); });
}); });
@ -778,19 +778,19 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
other {({{name}})} other {({{name}})}
}</div>`); }</div>`);
expect(fixture.nativeElement.innerHTML) expect(fixture.nativeElement.innerHTML)
.toEqual(`<div>aucun <b>email</b>!<!--ICU 1:0--> - (Angular)<!--ICU 1:3--></div>`); .toEqual(`<div>aucun <b>email</b>!<!--ICU 21:0--> - (Angular)<!--ICU 21:3--></div>`);
fixture.componentRef.instance.count = 4; fixture.componentRef.instance.count = 4;
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.nativeElement.innerHTML) expect(fixture.nativeElement.innerHTML)
.toEqual( .toEqual(
`<div>4 <span title="Angular">emails</span><!--ICU 1:0--> - (Angular)<!--ICU 1:3--></div>`); `<div>4 <span title="Angular">emails</span><!--ICU 21:0--> - (Angular)<!--ICU 21:3--></div>`);
fixture.componentRef.instance.count = 0; fixture.componentRef.instance.count = 0;
fixture.componentRef.instance.name = 'John'; fixture.componentRef.instance.name = 'John';
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.nativeElement.innerHTML) expect(fixture.nativeElement.innerHTML)
.toEqual(`<div>aucun <b>email</b>!<!--ICU 1:0--> - (John)<!--ICU 1:3--></div>`); .toEqual(`<div>aucun <b>email</b>!<!--ICU 21:0--> - (John)<!--ICU 21:3--></div>`);
}); });
it('with custom interpolation config', () => { it('with custom interpolation config', () => {
@ -829,9 +829,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
expect(fixture.nativeElement.innerHTML) expect(fixture.nativeElement.innerHTML)
.toEqual( .toEqual(
`<div>` + `<div>` +
`<span>aucun <b>email</b>!<!--ICU 1:0--></span>` + `<span>aucun <b>email</b>!<!--ICU 21:0--></span>` +
` - ` + ` - ` +
`<span>(Angular)<!--ICU 1:3--></span>` + `<span>(Angular)<!--ICU 21:3--></span>` +
`</div>`); `</div>`);
fixture.componentRef.instance.count = 4; fixture.componentRef.instance.count = 4;
@ -839,9 +839,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
expect(fixture.nativeElement.innerHTML) expect(fixture.nativeElement.innerHTML)
.toEqual( .toEqual(
`<div>` + `<div>` +
`<span>4 <span title="Angular">emails</span><!--ICU 1:0--></span>` + `<span>4 <span title="Angular">emails</span><!--ICU 21:0--></span>` +
` - ` + ` - ` +
`<span>(Angular)<!--ICU 1:3--></span>` + `<span>(Angular)<!--ICU 21:3--></span>` +
`</div>`); `</div>`);
fixture.componentRef.instance.count = 0; fixture.componentRef.instance.count = 0;
@ -850,9 +850,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
expect(fixture.nativeElement.innerHTML) expect(fixture.nativeElement.innerHTML)
.toEqual( .toEqual(
`<div>` + `<div>` +
`<span>aucun <b>email</b>!<!--ICU 1:0--></span>` + `<span>aucun <b>email</b>!<!--ICU 21:0--></span>` +
` - ` + ` - ` +
`<span>(John)<!--ICU 1:3--></span>` + `<span>(John)<!--ICU 21:3--></span>` +
`</div>`); `</div>`);
}); });
@ -867,7 +867,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
other {({{name}})} other {({{name}})}
}</span></div>`); }</span></div>`);
expect(fixture.nativeElement.innerHTML) expect(fixture.nativeElement.innerHTML)
.toEqual(`<div><span>(Angular)<!--ICU 0:0--></span><!--bindings={ .toEqual(`<div><span>(Angular)<!--ICU 20:0--></span><!--bindings={
"ng-reflect-ng-if": "true" "ng-reflect-ng-if": "true"
}--></div>`); }--></div>`);
@ -886,7 +886,8 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
const fixture = initWithTemplate(AppComp, `<ng-container i18n>{name, select, const fixture = initWithTemplate(AppComp, `<ng-container i18n>{name, select,
other {({{name}})} other {({{name}})}
}</ng-container>`); }</ng-container>`);
expect(fixture.nativeElement.innerHTML).toEqual(`(Angular)<!--ICU 1:0--><!--ng-container-->`); expect(fixture.nativeElement.innerHTML)
.toEqual(`(Angular)<!--ICU 21:0--><!--ng-container-->`);
}); });
it('inside <ng-template>', () => { it('inside <ng-template>', () => {
@ -921,12 +922,12 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
other {animals} other {animals}
}!} }!}
}</div>`); }</div>`);
expect(fixture.nativeElement.innerHTML).toEqual(`<div>zero<!--ICU 1:1--></div>`); expect(fixture.nativeElement.innerHTML).toEqual(`<div>zero<!--ICU 21:1--></div>`);
fixture.componentRef.instance.count = 4; fixture.componentRef.instance.count = 4;
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.nativeElement.innerHTML) expect(fixture.nativeElement.innerHTML)
.toEqual(`<div>4 animaux<!--nested ICU 0-->!<!--ICU 1:1--></div>`); .toEqual(`<div>4 animaux<!--nested ICU 0-->!<!--ICU 21:1--></div>`);
}); });
it('nested with interpolations in "other" blocks', () => { it('nested with interpolations in "other" blocks', () => {
@ -946,16 +947,16 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
}!} }!}
other {other - {{count}}} other {other - {{count}}}
}</div>`); }</div>`);
expect(fixture.nativeElement.innerHTML).toEqual(`<div>zero<!--ICU 1:1--></div>`); expect(fixture.nativeElement.innerHTML).toEqual(`<div>zero<!--ICU 21:1--></div>`);
fixture.componentRef.instance.count = 2; fixture.componentRef.instance.count = 2;
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.nativeElement.innerHTML) expect(fixture.nativeElement.innerHTML)
.toEqual(`<div>2 animaux<!--nested ICU 0-->!<!--ICU 1:1--></div>`); .toEqual(`<div>2 animaux<!--nested ICU 0-->!<!--ICU 21:1--></div>`);
fixture.componentRef.instance.count = 4; fixture.componentRef.instance.count = 4;
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toEqual(`<div>autre - 4<!--ICU 1:1--></div>`); expect(fixture.nativeElement.innerHTML).toEqual(`<div>autre - 4<!--ICU 21:1--></div>`);
}); });
it('should return the correct plural form for ICU expressions when using "ro" locale', () => { it('should return the correct plural form for ICU expressions when using "ro" locale', () => {
@ -988,31 +989,31 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
=other {lots of emails} =other {lots of emails}
}`); }`);
expect(fixture.nativeElement.innerHTML).toEqual('no email<!--ICU 0:0-->'); expect(fixture.nativeElement.innerHTML).toEqual('no email<!--ICU 20:0-->');
// Change detection cycle, no model changes // Change detection cycle, no model changes
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toEqual('no email<!--ICU 0:0-->'); expect(fixture.nativeElement.innerHTML).toEqual('no email<!--ICU 20:0-->');
fixture.componentInstance.count = 3; fixture.componentInstance.count = 3;
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toEqual('a few emails<!--ICU 0:0-->'); expect(fixture.nativeElement.innerHTML).toEqual('a few emails<!--ICU 20:0-->');
fixture.componentInstance.count = 1; fixture.componentInstance.count = 1;
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toEqual('one email<!--ICU 0:0-->'); expect(fixture.nativeElement.innerHTML).toEqual('one email<!--ICU 20:0-->');
fixture.componentInstance.count = 10; fixture.componentInstance.count = 10;
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toEqual('a few emails<!--ICU 0:0-->'); expect(fixture.nativeElement.innerHTML).toEqual('a few emails<!--ICU 20:0-->');
fixture.componentInstance.count = 20; fixture.componentInstance.count = 20;
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toEqual('lots of emails<!--ICU 0:0-->'); expect(fixture.nativeElement.innerHTML).toEqual('lots of emails<!--ICU 20:0-->');
fixture.componentInstance.count = 0; fixture.componentInstance.count = 0;
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toEqual('no email<!--ICU 0:0-->'); expect(fixture.nativeElement.innerHTML).toEqual('no email<!--ICU 20:0-->');
}); });
it(`should return the correct plural form for ICU expressions when using "es" locale`, () => { it(`should return the correct plural form for ICU expressions when using "es" locale`, () => {
@ -1039,31 +1040,31 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
=other {lots of emails} =other {lots of emails}
}`); }`);
expect(fixture.nativeElement.innerHTML).toEqual('no email<!--ICU 0:0-->'); expect(fixture.nativeElement.innerHTML).toEqual('no email<!--ICU 20:0-->');
// Change detection cycle, no model changes // Change detection cycle, no model changes
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toEqual('no email<!--ICU 0:0-->'); expect(fixture.nativeElement.innerHTML).toEqual('no email<!--ICU 20:0-->');
fixture.componentInstance.count = 3; fixture.componentInstance.count = 3;
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toEqual('lots of emails<!--ICU 0:0-->'); expect(fixture.nativeElement.innerHTML).toEqual('lots of emails<!--ICU 20:0-->');
fixture.componentInstance.count = 1; fixture.componentInstance.count = 1;
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toEqual('one email<!--ICU 0:0-->'); expect(fixture.nativeElement.innerHTML).toEqual('one email<!--ICU 20:0-->');
fixture.componentInstance.count = 10; fixture.componentInstance.count = 10;
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toEqual('lots of emails<!--ICU 0:0-->'); expect(fixture.nativeElement.innerHTML).toEqual('lots of emails<!--ICU 20:0-->');
fixture.componentInstance.count = 20; fixture.componentInstance.count = 20;
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toEqual('lots of emails<!--ICU 0:0-->'); expect(fixture.nativeElement.innerHTML).toEqual('lots of emails<!--ICU 20:0-->');
fixture.componentInstance.count = 0; fixture.componentInstance.count = 0;
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toEqual('no email<!--ICU 0:0-->'); expect(fixture.nativeElement.innerHTML).toEqual('no email<!--ICU 20:0-->');
}); });
it('projection', () => { it('projection', () => {
@ -1158,12 +1159,12 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
const fixture = TestBed.createComponent(App); const fixture = TestBed.createComponent(App);
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.debugElement.nativeElement.innerHTML) expect(fixture.debugElement.nativeElement.innerHTML)
.toContain('<my-cmp><div>ONE<!--ICU 1:0--></div><!--container--></my-cmp>'); .toContain('<my-cmp><div>ONE<!--ICU 21:0--></div><!--container--></my-cmp>');
fixture.componentRef.instance.count = 2; fixture.componentRef.instance.count = 2;
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.debugElement.nativeElement.innerHTML) expect(fixture.debugElement.nativeElement.innerHTML)
.toContain('<my-cmp><div>OTHER<!--ICU 1:0--></div><!--container--></my-cmp>'); .toContain('<my-cmp><div>OTHER<!--ICU 21:0--></div><!--container--></my-cmp>');
// destroy component // destroy component
fixture.componentInstance.condition = false; fixture.componentInstance.condition = false;
@ -1175,7 +1176,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
fixture.componentInstance.count = 1; fixture.componentInstance.count = 1;
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.debugElement.nativeElement.innerHTML) expect(fixture.debugElement.nativeElement.innerHTML)
.toContain('<my-cmp><div>ONE<!--ICU 1:0--></div><!--container--></my-cmp>'); .toContain('<my-cmp><div>ONE<!--ICU 21:0--></div><!--container--></my-cmp>');
}); });
it('with nested ICU expression and inside a container when creating a view via vcr.createEmbeddedView', it('with nested ICU expression and inside a container when creating a view via vcr.createEmbeddedView',
@ -1247,12 +1248,12 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.debugElement.nativeElement.innerHTML) expect(fixture.debugElement.nativeElement.innerHTML)
.toBe( .toBe(
'<my-cmp><div>2 animals<!--nested ICU 0-->!<!--ICU 1:1--></div><!--container--></my-cmp>'); '<my-cmp><div>2 animals<!--nested ICU 0-->!<!--ICU 21:1--></div><!--container--></my-cmp>');
fixture.componentRef.instance.count = 1; fixture.componentRef.instance.count = 1;
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.debugElement.nativeElement.innerHTML) expect(fixture.debugElement.nativeElement.innerHTML)
.toBe('<my-cmp><div>ONE<!--ICU 1:1--></div><!--container--></my-cmp>'); .toBe('<my-cmp><div>ONE<!--ICU 21:1--></div><!--container--></my-cmp>');
}); });
it('with nested containers', () => { it('with nested containers', () => {
@ -2369,13 +2370,13 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.nativeElement.innerHTML) expect(fixture.nativeElement.innerHTML)
.toEqual( .toEqual(
`<child><div>Contenu enfant et projection depuis Parent<!--ICU 1:0--></div></child>`); `<child><div>Contenu enfant et projection depuis Parent<!--ICU 21:0--></div></child>`);
fixture.componentRef.instance.name = 'angular'; fixture.componentRef.instance.name = 'angular';
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.nativeElement.innerHTML) expect(fixture.nativeElement.innerHTML)
.toEqual( .toEqual(
`<child><div>Contenu enfant et projection depuis Angular<!--ICU 1:0--></div></child>`); `<child><div>Contenu enfant et projection depuis Angular<!--ICU 21:0--></div></child>`);
}); });
it(`shouldn't project deleted projections in i18n blocks`, () => { it(`shouldn't project deleted projections in i18n blocks`, () => {

View File

@ -54,44 +54,44 @@ describe('addTNodeAndUpdateInsertBeforeIndex', () => {
describe('whose index is smaller than current nodes', () => { describe('whose index is smaller than current nodes', () => {
it('should update the previous insertBeforeIndex', () => { it('should update the previous insertBeforeIndex', () => {
const previousTNodes: TNode[] = [ const previousTNodes: TNode[] = [
tPlaceholderElementNode(20),
tPlaceholderElementNode(21), tPlaceholderElementNode(21),
tPlaceholderElementNode(22),
]; ];
addTNodeAndUpdateInsertBeforeIndex(previousTNodes, tPlaceholderElementNode(19)); addTNodeAndUpdateInsertBeforeIndex(previousTNodes, tPlaceholderElementNode(20));
expect(previousTNodes).toEqual([ expect(previousTNodes).toEqual([
matchTNode({index: 20, insertBeforeIndex: 19}), matchTNode({index: 21, insertBeforeIndex: 20}),
matchTNode({index: 21, insertBeforeIndex: 19}), matchTNode({index: 22, insertBeforeIndex: 20}),
matchTNode({index: 19, insertBeforeIndex: null}), matchTNode({index: 20, insertBeforeIndex: null}),
]); ]);
}); });
it('should not update the previous insertBeforeIndex if it is already set', () => { it('should not update the previous insertBeforeIndex if it is already set', () => {
const previousTNodes: TNode[] = [ const previousTNodes: TNode[] = [
tPlaceholderElementNode(20, 19), tPlaceholderElementNode(22, 21),
tPlaceholderElementNode(21, 19), tPlaceholderElementNode(23, 21),
tPlaceholderElementNode(19), tPlaceholderElementNode(21),
]; ];
addTNodeAndUpdateInsertBeforeIndex(previousTNodes, tPlaceholderElementNode(18)); addTNodeAndUpdateInsertBeforeIndex(previousTNodes, tPlaceholderElementNode(20));
expect(previousTNodes).toEqual([ expect(previousTNodes).toEqual([
matchTNode({index: 20, insertBeforeIndex: 19}), matchTNode({index: 22, insertBeforeIndex: 21}),
matchTNode({index: 21, insertBeforeIndex: 19}), matchTNode({index: 23, insertBeforeIndex: 21}),
matchTNode({index: 19, insertBeforeIndex: 18}), matchTNode({index: 21, insertBeforeIndex: 20}),
matchTNode({index: 18, insertBeforeIndex: null}), matchTNode({index: 20, insertBeforeIndex: null}),
]); ]);
}); });
it('should not update the previous insertBeforeIndex if it is created after', () => { it('should not update the previous insertBeforeIndex if it is created after', () => {
const previousTNodes: TNode[] = [ const previousTNodes: TNode[] = [
tPlaceholderElementNode(20, 15), tPlaceholderElementNode(25, 20),
tPlaceholderElementNode(21, 15), tPlaceholderElementNode(26, 20),
tPlaceholderElementNode(15), tPlaceholderElementNode(20),
]; ];
addTNodeAndUpdateInsertBeforeIndex(previousTNodes, tPlaceholderElementNode(18)); addTNodeAndUpdateInsertBeforeIndex(previousTNodes, tPlaceholderElementNode(23));
expect(previousTNodes).toEqual([ expect(previousTNodes).toEqual([
matchTNode({index: 20, insertBeforeIndex: 15}), matchTNode({index: 25, insertBeforeIndex: 20}),
matchTNode({index: 21, insertBeforeIndex: 15}), matchTNode({index: 26, insertBeforeIndex: 20}),
matchTNode({index: 15, insertBeforeIndex: null}), matchTNode({index: 20, insertBeforeIndex: null}),
matchTNode({index: 18, insertBeforeIndex: null}), matchTNode({index: 23, insertBeforeIndex: null}),
]); ]);
}); });
}); });
@ -116,44 +116,44 @@ describe('addTNodeAndUpdateInsertBeforeIndex', () => {
describe('whose index is smaller than current nodes', () => { describe('whose index is smaller than current nodes', () => {
it('should update the previous insertBeforeIndex', () => { it('should update the previous insertBeforeIndex', () => {
const previousTNodes: TNode[] = [ const previousTNodes: TNode[] = [
tPlaceholderElementNode(20),
tPlaceholderElementNode(21), tPlaceholderElementNode(21),
tPlaceholderElementNode(22),
]; ];
addTNodeAndUpdateInsertBeforeIndex(previousTNodes, tI18NTextNode(19)); addTNodeAndUpdateInsertBeforeIndex(previousTNodes, tI18NTextNode(20));
expect(previousTNodes).toEqual([ expect(previousTNodes).toEqual([
matchTNode({index: 20, insertBeforeIndex: 19}), matchTNode({index: 21, insertBeforeIndex: 20}),
matchTNode({index: 21, insertBeforeIndex: 19}), matchTNode({index: 22, insertBeforeIndex: 20}),
matchTNode({index: 19, insertBeforeIndex: null}), matchTNode({index: 20, insertBeforeIndex: null}),
]); ]);
}); });
it('should not update the previous insertBeforeIndex if it is already set', () => { it('should not update the previous insertBeforeIndex if it is already set', () => {
const previousTNodes: TNode[] = [ const previousTNodes: TNode[] = [
tPlaceholderElementNode(20, 19), tPlaceholderElementNode(22, 21),
tPlaceholderElementNode(21, 19), tPlaceholderElementNode(23, 21),
tPlaceholderElementNode(19), tPlaceholderElementNode(21),
]; ];
addTNodeAndUpdateInsertBeforeIndex(previousTNodes, tI18NTextNode(18)); addTNodeAndUpdateInsertBeforeIndex(previousTNodes, tI18NTextNode(20));
expect(previousTNodes).toEqual([ expect(previousTNodes).toEqual([
matchTNode({index: 20, insertBeforeIndex: 19}), matchTNode({index: 22, insertBeforeIndex: 21}),
matchTNode({index: 21, insertBeforeIndex: 19}), matchTNode({index: 23, insertBeforeIndex: 21}),
matchTNode({index: 19, insertBeforeIndex: 18}), matchTNode({index: 21, insertBeforeIndex: 20}),
matchTNode({index: 18, insertBeforeIndex: null}), matchTNode({index: 20, insertBeforeIndex: null}),
]); ]);
}); });
it('should not update the previous insertBeforeIndex if it is created after', () => { it('should not update the previous insertBeforeIndex if it is created after', () => {
const previousTNodes: TNode[] = [ const previousTNodes: TNode[] = [
tPlaceholderElementNode(20, 15), tPlaceholderElementNode(25, 20),
tPlaceholderElementNode(21, 15), tPlaceholderElementNode(26, 20),
tPlaceholderElementNode(15), tPlaceholderElementNode(20),
]; ];
addTNodeAndUpdateInsertBeforeIndex(previousTNodes, tI18NTextNode(18)); addTNodeAndUpdateInsertBeforeIndex(previousTNodes, tI18NTextNode(23));
expect(previousTNodes).toEqual([ expect(previousTNodes).toEqual([
matchTNode({index: 20, insertBeforeIndex: 15}), matchTNode({index: 25, insertBeforeIndex: 20}),
matchTNode({index: 21, insertBeforeIndex: 15}), matchTNode({index: 26, insertBeforeIndex: 20}),
matchTNode({index: 15, insertBeforeIndex: 18}), matchTNode({index: 20, insertBeforeIndex: 23}),
matchTNode({index: 18, insertBeforeIndex: null}), matchTNode({index: 23, insertBeforeIndex: null}),
]); ]);
}); });
}); });

View File

@ -45,7 +45,7 @@ describe('i18n_parse', () => {
// 21: Binding for ICU | // 21: Binding for ICU |
// ----- EXPANDO ----- // ----- EXPANDO -----
// 22: null | #text(before|) // 22: null | #text(before|)
// 23: TIcu | <!-- ICU 0:0 --> // 23: TIcu | <!-- ICU 20:0 -->
// 24: null | currently selected ICU case // 24: null | currently selected ICU case
// 25: null | #text(caseA) // 25: null | #text(caseA)
// 26: null | #text(otherCase) // 26: null | #text(otherCase)
@ -59,7 +59,7 @@ describe('i18n_parse', () => {
create: matchDebug([ create: matchDebug([
'lView[22] = document.createText("before|");', 'lView[22] = document.createText("before|");',
'parent.appendChild(lView[22]);', 'parent.appendChild(lView[22]);',
'lView[23] = document.createComment("ICU 0:0");', 'lView[23] = document.createComment("ICU 20:0");',
'parent.appendChild(lView[23]);', 'parent.appendChild(lView[23]);',
'lView[27] = document.createText("|after");', 'lView[27] = document.createText("|after");',
'parent.appendChild(lView[27]);', 'parent.appendChild(lView[27]);',
@ -95,22 +95,22 @@ describe('i18n_parse', () => {
fixture.apply(() => { fixture.apply(() => {
applyCreateOpCodes(fixture.lView, tI18n.create, fixture.host, null); applyCreateOpCodes(fixture.lView, tI18n.create, fixture.host, null);
expect(fixture.host.innerHTML).toEqual('before|<!--ICU 0:0-->|after'); expect(fixture.host.innerHTML).toEqual('before|<!--ICU 20:0-->|after');
}); });
fixture.apply(() => { fixture.apply(() => {
ɵɵi18nExp('A'); ɵɵi18nExp('A');
ɵɵi18nApply(0); // index 0 + HEADER_OFFSET = 20; ɵɵi18nApply(0); // index 0 + HEADER_OFFSET = 20;
expect(fixture.host.innerHTML).toEqual('before|caseA<!--ICU 0:0-->|after'); expect(fixture.host.innerHTML).toEqual('before|caseA<!--ICU 20:0-->|after');
}); });
fixture.apply(() => { fixture.apply(() => {
ɵɵi18nExp('x'); ɵɵi18nExp('x');
ɵɵi18nApply(0); // index 0 + HEADER_OFFSET = 20; ɵɵi18nApply(0); // index 0 + HEADER_OFFSET = 20;
expect(fixture.host.innerHTML).toEqual('before|otherCase<!--ICU 0:0-->|after'); expect(fixture.host.innerHTML).toEqual('before|otherCase<!--ICU 20:0-->|after');
}); });
fixture.apply(() => { fixture.apply(() => {
ɵɵi18nExp('A'); ɵɵi18nExp('A');
ɵɵi18nApply(0); // index 0 + HEADER_OFFSET = 20; ɵɵi18nApply(0); // index 0 + HEADER_OFFSET = 20;
expect(fixture.host.innerHTML).toEqual('before|caseA<!--ICU 0:0-->|after'); expect(fixture.host.innerHTML).toEqual('before|caseA<!--ICU 20:0-->|after');
}); });
}); });
@ -122,23 +122,23 @@ describe('i18n_parse', () => {
}`); }`);
fixture.apply(() => { fixture.apply(() => {
applyCreateOpCodes(fixture.lView, tI18n.create, fixture.host, null); applyCreateOpCodes(fixture.lView, tI18n.create, fixture.host, null);
expect(fixture.host.innerHTML).toEqual('<!--ICU 0:0-->'); expect(fixture.host.innerHTML).toEqual('<!--ICU 20:0-->');
}); });
fixture.apply(() => { fixture.apply(() => {
ɵɵi18nExp('A'); ɵɵi18nExp('A');
ɵɵi18nApply(0); // index 0 + HEADER_OFFSET = 20; ɵɵi18nApply(0); // index 0 + HEADER_OFFSET = 20;
expect(fixture.host.innerHTML).toEqual('Hello <b>world<i>!</i></b><!--ICU 0:0-->'); expect(fixture.host.innerHTML).toEqual('Hello <b>world<i>!</i></b><!--ICU 20:0-->');
}); });
fixture.apply(() => { fixture.apply(() => {
ɵɵi18nExp('x'); ɵɵi18nExp('x');
ɵɵi18nApply(0); // index 0 + HEADER_OFFSET = 20; ɵɵi18nApply(0); // index 0 + HEADER_OFFSET = 20;
expect(fixture.host.innerHTML) expect(fixture.host.innerHTML)
.toEqual('<div>nestedOther<!--nested ICU 0--></div><!--ICU 0:0-->'); .toEqual('<div>nestedOther<!--nested ICU 0--></div><!--ICU 20:0-->');
}); });
fixture.apply(() => { fixture.apply(() => {
ɵɵi18nExp('A'); ɵɵi18nExp('A');
ɵɵi18nApply(0); // index 0 + HEADER_OFFSET = 20; ɵɵi18nApply(0); // index 0 + HEADER_OFFSET = 20;
expect(fixture.host.innerHTML).toEqual('Hello <b>world<i>!</i></b><!--ICU 0:0-->'); expect(fixture.host.innerHTML).toEqual('Hello <b>world<i>!</i></b><!--ICU 20:0-->');
}); });
}); });
@ -154,7 +154,7 @@ describe('i18n_parse', () => {
// 22: Binding for child ICU | // 22: Binding for child ICU |
// 23: Binding for child ICU | // 23: Binding for child ICU |
// ----- EXPANDO ----- // ----- EXPANDO -----
// 24: TIcu (parent) | <!-- ICU 0:0 --> // 24: TIcu (parent) | <!-- ICU 20:0 -->
// 25: null | currently selected ICU case // 25: null | currently selected ICU case
// 26: null | #text( parentA ) // 26: null | #text( parentA )
// 27: TIcu (child) | <!-- nested ICU 0 --> // 27: TIcu (child) | <!-- nested ICU 0 -->
@ -170,7 +170,7 @@ describe('i18n_parse', () => {
}`); }`);
expect(tI18n).toEqual(matchTI18n({ expect(tI18n).toEqual(matchTI18n({
create: matchDebug([ create: matchDebug([
'lView[24] = document.createComment("ICU 0:0");', 'lView[24] = document.createComment("ICU 20:0");',
'parent.appendChild(lView[24]);', 'parent.appendChild(lView[24]);',
]), ]),
update: matchDebug([ update: matchDebug([
@ -244,45 +244,46 @@ describe('i18n_parse', () => {
fixture.apply(() => { fixture.apply(() => {
applyCreateOpCodes(fixture.lView, tI18n.create, fixture.host, null); applyCreateOpCodes(fixture.lView, tI18n.create, fixture.host, null);
expect(fixture.host.innerHTML).toEqual('<!--ICU 0:0-->'); expect(fixture.host.innerHTML).toEqual('<!--ICU 20:0-->');
}); });
fixture.apply(() => { fixture.apply(() => {
ɵɵi18nExp('A'); ɵɵi18nExp('A');
ɵɵi18nExp('0'); ɵɵi18nExp('0');
ɵɵi18nExp('value1'); ɵɵi18nExp('value1');
ɵɵi18nApply(0); // index 0 + HEADER_OFFSET = 20; ɵɵi18nApply(0); // index 0 + HEADER_OFFSET = 20;
expect(fixture.host.innerHTML).toEqual('parentA nested0<!--nested ICU 0-->!<!--ICU 0:0-->'); expect(fixture.host.innerHTML)
.toEqual('parentA nested0<!--nested ICU 0-->!<!--ICU 20:0-->');
}); });
fixture.apply(() => { fixture.apply(() => {
ɵɵi18nExp('A'); ɵɵi18nExp('A');
ɵɵi18nExp('x'); ɵɵi18nExp('x');
ɵɵi18nExp('value1'); ɵɵi18nExp('value1');
ɵɵi18nApply(0); // index 0 + HEADER_OFFSET = 20; ɵɵi18nApply(0); // index 0 + HEADER_OFFSET = 20;
expect(fixture.host.innerHTML).toEqual('parentA value1<!--nested ICU 0-->!<!--ICU 0:0-->'); expect(fixture.host.innerHTML).toEqual('parentA value1<!--nested ICU 0-->!<!--ICU 20:0-->');
}); });
fixture.apply(() => { fixture.apply(() => {
ɵɵi18nExp('x'); ɵɵi18nExp('x');
ɵɵi18nExp('x'); ɵɵi18nExp('x');
ɵɵi18nExp('value2'); ɵɵi18nExp('value2');
ɵɵi18nApply(0); // index 0 + HEADER_OFFSET = 20; ɵɵi18nApply(0); // index 0 + HEADER_OFFSET = 20;
expect(fixture.host.innerHTML).toEqual('parentOther<!--ICU 0:0-->'); expect(fixture.host.innerHTML).toEqual('parentOther<!--ICU 20:0-->');
}); });
fixture.apply(() => { fixture.apply(() => {
ɵɵi18nExp('A'); ɵɵi18nExp('A');
ɵɵi18nExp('A'); ɵɵi18nExp('A');
ɵɵi18nExp('value2'); ɵɵi18nExp('value2');
ɵɵi18nApply(0); // index 0 + HEADER_OFFSET = 20; ɵɵi18nApply(0); // index 0 + HEADER_OFFSET = 20;
expect(fixture.host.innerHTML).toEqual('parentA value2<!--nested ICU 0-->!<!--ICU 0:0-->'); expect(fixture.host.innerHTML).toEqual('parentA value2<!--nested ICU 0-->!<!--ICU 20:0-->');
}); });
}); });
}); });
function toT18n(text: string) { function toT18n(text: string) {
const tNodeIndex = 0; const tNodeIndex = HEADER_OFFSET;
fixture.enterView(); fixture.enterView();
i18nStartFirstCreatePass(fixture.tView, 0, fixture.lView, tNodeIndex, text, -1); i18nStartFirstCreatePass(fixture.tView, 0, fixture.lView, tNodeIndex, text, -1);
fixture.leaveView(); fixture.leaveView();
const tI18n = fixture.tView.data[tNodeIndex + HEADER_OFFSET] as TI18n; const tI18n = fixture.tView.data[tNodeIndex] as TI18n;
expect(tI18n).toEqual(matchTI18n({})); expect(tI18n).toEqual(matchTI18n({}));
return tI18n; return tI18n;
} }

View File

@ -67,7 +67,7 @@ describe('Runtime i18n', () => {
const fixture = new TemplateFixture( const fixture = new TemplateFixture(
{create: createTemplate, update: updateTemplate, decls: nbDecls, consts: [messageOrAtrs]}); {create: createTemplate, update: updateTemplate, decls: nbDecls, consts: [messageOrAtrs]});
tView = fixture.hostView[TVIEW]; tView = fixture.hostView[TVIEW];
return tView.data[index + HEADER_OFFSET] as TI18n; return tView.data[index] as TI18n | I18nUpdateOpCodes;
} }
describe('i18nStart', () => { describe('i18nStart', () => {
@ -79,7 +79,7 @@ describe('Runtime i18n', () => {
ɵɵelementStart(0, 'div'); ɵɵelementStart(0, 'div');
ɵɵi18nStart(index, 0); ɵɵi18nStart(index, 0);
ɵɵelementEnd(); ɵɵelementEnd();
}, undefined, nbConsts, index) as TI18n; }, undefined, nbConsts, HEADER_OFFSET + index) as TI18n;
expect(opCodes).toEqual({ expect(opCodes).toEqual({
create: matchDebug([ create: matchDebug([
@ -100,7 +100,7 @@ describe('Runtime i18n', () => {
ɵɵelementStart(0, 'div'); ɵɵelementStart(0, 'div');
ɵɵi18nStart(index, 0); ɵɵi18nStart(index, 0);
ɵɵelementEnd(); ɵɵelementEnd();
}, undefined, nbConsts, index); }, undefined, nbConsts, HEADER_OFFSET + index);
expect(opCodes).toEqual({ expect(opCodes).toEqual({
create: matchDebug([ create: matchDebug([
@ -125,7 +125,7 @@ describe('Runtime i18n', () => {
ɵɵelementStart(0, 'div'); ɵɵelementStart(0, 'div');
ɵɵi18nStart(index, 0); ɵɵi18nStart(index, 0);
ɵɵelementEnd(); ɵɵelementEnd();
}, undefined, nbConsts, index); }, undefined, nbConsts, HEADER_OFFSET + index);
expect((opCodes as any).update.debug).toEqual([ expect((opCodes as any).update.debug).toEqual([
'if (mask & 0b1) { (lView[22] as Text).textContent = `Hello ${lView[i-1]}!`; }' 'if (mask & 0b1) { (lView[22] as Text).textContent = `Hello ${lView[i-1]}!`; }'
@ -150,7 +150,7 @@ describe('Runtime i18n', () => {
ɵɵelementStart(0, 'div'); ɵɵelementStart(0, 'div');
ɵɵi18nStart(index, 0); ɵɵi18nStart(index, 0);
ɵɵelementEnd(); ɵɵelementEnd();
}, undefined, nbConsts, index); }, undefined, nbConsts, HEADER_OFFSET + index);
expect(opCodes).toEqual({ expect(opCodes).toEqual({
create: matchDebug([ create: matchDebug([
@ -183,7 +183,7 @@ describe('Runtime i18n', () => {
ɵɵelementStart(0, 'div'); ɵɵelementStart(0, 'div');
ɵɵi18nStart(index, 0); ɵɵi18nStart(index, 0);
ɵɵelementEnd(); ɵɵelementEnd();
}, undefined, nbConsts, index); }, undefined, nbConsts, HEADER_OFFSET + index);
expect(opCodes).toEqual({ expect(opCodes).toEqual({
create: matchDebug([ create: matchDebug([
@ -205,7 +205,7 @@ describe('Runtime i18n', () => {
opCodes = getOpCodes(message, () => { opCodes = getOpCodes(message, () => {
ɵɵelementStart(0, 'div'); ɵɵelementStart(0, 'div');
ɵɵi18nStart(index, 0, 1); ɵɵi18nStart(index, 0, 1);
}, undefined, nbConsts, index); }, undefined, nbConsts, index + HEADER_OFFSET);
expect(opCodes).toEqual({ expect(opCodes).toEqual({
create: matchDebug([ create: matchDebug([
@ -223,7 +223,7 @@ describe('Runtime i18n', () => {
opCodes = getOpCodes(message, () => { opCodes = getOpCodes(message, () => {
ɵɵelementStart(0, 'div'); ɵɵelementStart(0, 'div');
ɵɵi18nStart(index, 0, 2); ɵɵi18nStart(index, 0, 2);
}, undefined, nbConsts, index); }, undefined, nbConsts, index + HEADER_OFFSET);
expect(opCodes).toEqual({ expect(opCodes).toEqual({
create: matchDebug([ create: matchDebug([
@ -245,11 +245,11 @@ describe('Runtime i18n', () => {
ɵɵelementStart(0, 'div'); ɵɵelementStart(0, 'div');
ɵɵi18nStart(index, 0); ɵɵi18nStart(index, 0);
ɵɵelementEnd(); ɵɵelementEnd();
}, undefined, nbConsts, index) as TI18n; }, undefined, nbConsts, HEADER_OFFSET + index) as TI18n;
expect(opCodes).toEqual({ expect(opCodes).toEqual({
create: matchDebug([ create: matchDebug([
`lView[${HEADER_OFFSET + 2}] = document.createComment("ICU 1:0");`, `lView[${HEADER_OFFSET + 2}] = document.createComment("ICU 21:0");`,
`parent.appendChild(lView[${HEADER_OFFSET + 2}]);`, `parent.appendChild(lView[${HEADER_OFFSET + 2}]);`,
]), ]),
update: matchDebug([ update: matchDebug([
@ -332,11 +332,11 @@ describe('Runtime i18n', () => {
ɵɵelementStart(0, 'div'); ɵɵelementStart(0, 'div');
ɵɵi18n(index, 0); ɵɵi18n(index, 0);
ɵɵelementEnd(); ɵɵelementEnd();
}, undefined, nbConsts, index); }, undefined, nbConsts, HEADER_OFFSET + index);
expect(opCodes).toEqual({ expect(opCodes).toEqual({
create: matchDebug([ create: matchDebug([
`lView[${HEADER_OFFSET + 2}] = document.createComment("ICU 1:0");`, `lView[${HEADER_OFFSET + 2}] = document.createComment("ICU 21:0");`,
`parent.appendChild(lView[${HEADER_OFFSET + 2}]);`, `parent.appendChild(lView[${HEADER_OFFSET + 2}]);`,
]), ]),
update: matchDebug([ update: matchDebug([
@ -432,11 +432,11 @@ describe('Runtime i18n', () => {
consts: [attrs], consts: [attrs],
}); });
const tView = fixture.hostView[TVIEW]; const tView = fixture.hostView[TVIEW];
const opCodes = tView.data[index + HEADER_OFFSET] as I18nUpdateOpCodes; const opCodes = tView.data[HEADER_OFFSET + index] as I18nUpdateOpCodes;
expect(opCodes).toEqual([]); expect(opCodes).toEqual([]);
expect( expect((getNativeByIndex(HEADER_OFFSET, fixture.hostView as LView) as any as Element)
(getNativeByIndex(0, fixture.hostView as LView) as any as Element).getAttribute('title')) .getAttribute('title'))
.toEqual(message); .toEqual(message);
}); });
@ -449,7 +449,7 @@ describe('Runtime i18n', () => {
ɵɵelementStart(0, 'div'); ɵɵelementStart(0, 'div');
ɵɵi18nAttributes(index, 0); ɵɵi18nAttributes(index, 0);
ɵɵelementEnd(); ɵɵelementEnd();
}, undefined, nbConsts, index); }, undefined, nbConsts, HEADER_OFFSET + index);
expect(opCodes).toEqual(matchDebug([ expect(opCodes).toEqual(matchDebug([
'if (mask & 0b1) { (lView[20] as Element).setAttribute(\'title\', `Hello ${lView[i-1]}!`); }', 'if (mask & 0b1) { (lView[20] as Element).setAttribute(\'title\', `Hello ${lView[i-1]}!`); }',
@ -465,7 +465,7 @@ describe('Runtime i18n', () => {
ɵɵelementStart(0, 'div'); ɵɵelementStart(0, 'div');
ɵɵi18nAttributes(index, 0); ɵɵi18nAttributes(index, 0);
ɵɵelementEnd(); ɵɵelementEnd();
}, undefined, nbConsts, index); }, undefined, nbConsts, HEADER_OFFSET + index);
expect(opCodes).toEqual(matchDebug([ expect(opCodes).toEqual(matchDebug([
'if (mask & 0b11) { (lView[20] as Element).setAttribute(\'title\', `Hello ${lView[i-1]} and ${lView[i-2]}, again ${lView[i-1]}!`); }', 'if (mask & 0b11) { (lView[20] as Element).setAttribute(\'title\', `Hello ${lView[i-1]} and ${lView[i-2]}, again ${lView[i-1]}!`); }',
@ -481,7 +481,7 @@ describe('Runtime i18n', () => {
ɵɵelementStart(0, 'div'); ɵɵelementStart(0, 'div');
ɵɵi18nAttributes(index, 0); ɵɵi18nAttributes(index, 0);
ɵɵelementEnd(); ɵɵelementEnd();
}, undefined, nbConsts, index); }, undefined, nbConsts, HEADER_OFFSET + index);
expect(opCodes).toEqual(matchDebug([ expect(opCodes).toEqual(matchDebug([
'if (mask & 0b1) { (lView[20] as Element).setAttribute(\'title\', `Hello ${lView[i-1]}!`); }', 'if (mask & 0b1) { (lView[20] as Element).setAttribute(\'title\', `Hello ${lView[i-1]}!`); }',
@ -631,14 +631,12 @@ describe('Runtime i18n', () => {
describe('i18nStartFirstCreatePass', () => { describe('i18nStartFirstCreatePass', () => {
let fixture: ViewFixture; let fixture: ViewFixture;
let divTNode: TElementNode;
const DECLS = 20; const DECLS = 20;
const VARS = 10; const VARS = 10;
beforeEach(() => { beforeEach(() => {
fixture = new ViewFixture({decls: DECLS, vars: VARS}); fixture = new ViewFixture({decls: DECLS, vars: VARS});
fixture.enterView(); fixture.enterView();
ɵɵelementStart(0, 'div'); ɵɵelementStart(0, 'div');
divTNode = getCurrentTNode() as TElementNode;
}); });
afterEach(ViewFixture.cleanUp); afterEach(ViewFixture.cleanUp);
@ -661,7 +659,8 @@ describe('Runtime i18n', () => {
} }
it('should process text node with no siblings and no children', () => { it('should process text node with no siblings and no children', () => {
i18nStartFirstCreatePass(fixture.tView, 0, fixture.lView, 1, 'Hello World!', -1); i18nStartFirstCreatePass(
fixture.tView, 0, fixture.lView, HEADER_OFFSET + 1, 'Hello World!', -1);
const ti18n = fixture.tView.data[HEADER_OFFSET + 1] as TI18n; const ti18n = fixture.tView.data[HEADER_OFFSET + 1] as TI18n;
// Expect that we only create the `Hello World!` text node and nothing else. // Expect that we only create the `Hello World!` text node and nothing else.
expect(ti18n.create).toEqual([ expect(ti18n.create).toEqual([
@ -672,7 +671,8 @@ describe('Runtime i18n', () => {
}); });
it('should process text with a child node', () => { it('should process text with a child node', () => {
i18nStartFirstCreatePass(fixture.tView, 0, fixture.lView, 1, 'Hello <20>#2<><32>/#2<>!', -1); i18nStartFirstCreatePass(
fixture.tView, 0, fixture.lView, HEADER_OFFSET + 1, 'Hello <20>#2<><32>/#2<>!', -1);
const ti18n = fixture.tView.data[HEADER_OFFSET + 1] as TI18n; const ti18n = fixture.tView.data[HEADER_OFFSET + 1] as TI18n;
expect(ti18n.create).toEqual([ expect(ti18n.create).toEqual([
i18nRangeOffsetOpcode(0), 'Hello ', // i18nRangeOffsetOpcode(0), 'Hello ', //
@ -689,7 +689,8 @@ describe('Runtime i18n', () => {
}); });
it('should process text with a child node that has text', () => { it('should process text with a child node that has text', () => {
i18nStartFirstCreatePass(fixture.tView, 0, fixture.lView, 1, 'Hello <20>#2<>World<6C>/#2<>!', -1); i18nStartFirstCreatePass(
fixture.tView, 0, fixture.lView, HEADER_OFFSET + 1, 'Hello <20>#2<>World<6C>/#2<>!', -1);
const ti18n = fixture.tView.data[HEADER_OFFSET + 1] as TI18n; const ti18n = fixture.tView.data[HEADER_OFFSET + 1] as TI18n;
expect(ti18n.create).toEqual([ expect(ti18n.create).toEqual([
i18nRangeOffsetOpcode(0), 'Hello ', // i18nRangeOffsetOpcode(0), 'Hello ', //
@ -708,7 +709,7 @@ describe('Runtime i18n', () => {
it('should process text with a child node that has text and with bindings', () => { it('should process text with a child node that has text and with bindings', () => {
i18nStartFirstCreatePass( i18nStartFirstCreatePass(
fixture.tView, 0, fixture.lView, 1, fixture.tView, 0, fixture.lView, HEADER_OFFSET + 1,
'<27>0<EFBFBD> <20>#2<><32>1<EFBFBD><31>/#2<>!' /* {{salutation}} <b>{{name}}</b>! */, -1); '<27>0<EFBFBD> <20>#2<><32>1<EFBFBD><31>/#2<>!' /* {{salutation}} <b>{{name}}</b>! */, -1);
const ti18n = fixture.tView.data[HEADER_OFFSET + 1] as TI18n; const ti18n = fixture.tView.data[HEADER_OFFSET + 1] as TI18n;
expect(ti18n.create).toEqual([ expect(ti18n.create).toEqual([
@ -733,7 +734,8 @@ describe('Runtime i18n', () => {
}); });
it('should process text with a child template', () => { it('should process text with a child template', () => {
i18nStartFirstCreatePass(fixture.tView, 0, fixture.lView, 1, 'Hello <20>*2:1<>World<6C>/*2:1<>!', -1); i18nStartFirstCreatePass(
fixture.tView, 0, fixture.lView, HEADER_OFFSET + 1, 'Hello <20>*2:1<>World<6C>/*2:1<>!', -1);
const ti18n = fixture.tView.data[HEADER_OFFSET + 1] as TI18n; const ti18n = fixture.tView.data[HEADER_OFFSET + 1] as TI18n;
expect(ti18n.create.debug).toEqual([ expect(ti18n.create.debug).toEqual([
'lView[50] = document.createText("Hello ");', 'lView[50] = document.createText("Hello ");',

View File

@ -10,7 +10,7 @@ import {createLView, createTNode, createTView} from '@angular/core/src/render3/i
import {TNodeType} from '@angular/core/src/render3/interfaces/node'; import {TNodeType} from '@angular/core/src/render3/interfaces/node';
import {domRendererFactory3} from '@angular/core/src/render3/interfaces/renderer'; import {domRendererFactory3} from '@angular/core/src/render3/interfaces/renderer';
import {HEADER_OFFSET, LViewFlags, TVIEW, TViewType} from '@angular/core/src/render3/interfaces/view'; import {HEADER_OFFSET, LViewFlags, TVIEW, TViewType} from '@angular/core/src/render3/interfaces/view';
import {enterView, getBindingRoot, getLView, setBindingIndex} from '@angular/core/src/render3/state'; import {enterView, getBindingRoot, getLView, setBindingIndex, setSelectedIndex} from '@angular/core/src/render3/state';
@ -43,9 +43,10 @@ export function enterViewWithOneDiv() {
const lView = createLView( const lView = createLView(
null, tView, null, LViewFlags.CheckAlways, null, null, domRendererFactory3, renderer, null, null, tView, null, LViewFlags.CheckAlways, null, null, domRendererFactory3, renderer, null,
null); null);
lView[0 + HEADER_OFFSET] = div; lView[HEADER_OFFSET] = div;
tView.data[0 + HEADER_OFFSET] = tNode; tView.data[HEADER_OFFSET] = tNode;
enterView(lView); enterView(lView);
setSelectedIndex(HEADER_OFFSET);
} }
export function clearFirstUpdatePass() { export function clearFirstUpdatePass() {

View File

@ -14,8 +14,6 @@ import {getTStylingRangeNext, getTStylingRangeNextDuplicate, getTStylingRangePre
import {HEADER_OFFSET, TVIEW} from '@angular/core/src/render3/interfaces/view'; import {HEADER_OFFSET, TVIEW} from '@angular/core/src/render3/interfaces/view';
import {getLView, leaveView, setBindingRootForHostBindings} from '@angular/core/src/render3/state'; import {getLView, leaveView, setBindingRootForHostBindings} from '@angular/core/src/render3/state';
import {getNativeByIndex} from '@angular/core/src/render3/util/view_utils'; import {getNativeByIndex} from '@angular/core/src/render3/util/view_utils';
import {bypassSanitizationTrustStyle} from '@angular/core/src/sanitization/bypass';
import {ɵɵsanitizeStyle} from '@angular/core/src/sanitization/sanitization';
import {keyValueArraySet} from '@angular/core/src/util/array_utils'; import {keyValueArraySet} from '@angular/core/src/util/array_utils';
import {ngDevModeResetPerfCounters} from '@angular/core/src/util/ng_dev_mode'; import {ngDevModeResetPerfCounters} from '@angular/core/src/util/ng_dev_mode';
import {getElementClasses, getElementStyles} from '@angular/core/testing/src/styling'; import {getElementClasses, getElementStyles} from '@angular/core/testing/src/styling';
@ -28,7 +26,7 @@ describe('styling', () => {
afterEach(leaveView); afterEach(leaveView);
let div!: HTMLElement; let div!: HTMLElement;
beforeEach(() => div = getNativeByIndex(0, getLView()) as HTMLElement); beforeEach(() => div = getNativeByIndex(HEADER_OFFSET, getLView()) as HTMLElement);
it('should do set basic style', () => { it('should do set basic style', () => {
ɵɵstyleProp('color', 'red'); ɵɵstyleProp('color', 'red');

View File

@ -6,14 +6,13 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {HEADER_OFFSET} from '@angular/core/src/render3/interfaces/view';
import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'; import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util';
import {ɵɵdefineComponent, ɵɵdefineDirective, ɵɵreference, ɵɵresolveBody, ɵɵresolveDocument} from '../../src/render3/index'; import {ɵɵdefineComponent, ɵɵdefineDirective, ɵɵreference, ɵɵresolveBody, ɵɵresolveDocument} from '../../src/render3/index';
import {ɵɵelement, ɵɵelementEnd, ɵɵelementStart, ɵɵgetCurrentView, ɵɵlistener, ɵɵtext} from '../../src/render3/instructions/all'; import {ɵɵelement, ɵɵelementEnd, ɵɵelementStart, ɵɵgetCurrentView, ɵɵlistener, ɵɵtext} from '../../src/render3/instructions/all';
import {RenderFlags} from '../../src/render3/interfaces/definition'; import {RenderFlags} from '../../src/render3/interfaces/definition';
import {GlobalTargetResolver} from '../../src/render3/interfaces/renderer'; import {GlobalTargetResolver} from '../../src/render3/interfaces/renderer';
import {ɵɵrestoreView} from '../../src/render3/state'; import {ɵɵrestoreView} from '../../src/render3/state';
import {getRendererFactory2} from './imported_renderer2'; import {getRendererFactory2} from './imported_renderer2';
import {ComponentFixture, containerEl, createComponent, getDirectiveOnNode, renderToHtml, TemplateFixture} from './render_util'; import {ComponentFixture, containerEl, createComponent, getDirectiveOnNode, renderToHtml, TemplateFixture} from './render_util';
@ -497,7 +496,7 @@ describe('event listeners', () => {
} }
// testing only // testing only
compInstance = getDirectiveOnNode(0); compInstance = getDirectiveOnNode(HEADER_OFFSET);
}, },
directives: [Comp] directives: [Comp]
}); });

View File

@ -56,7 +56,7 @@ describe('render3 matchers', () => {
}); });
describe('matchTNode', () => { describe('matchTNode', () => {
const tView = createTView(TViewType.Root, null, null, 2, 3, null, null, null, null, null); const tView = createTView(TViewType.Root, null, null, 2, 3, null, null, null, null, null);
const tNode = createTNode(tView, null, TNodeType.Element, 1, 'tagName', []); const tNode = createTNode(tView, null, TNodeType.Element, 0, 'tagName', []);
it('should match', () => { it('should match', () => {
expect(tNode).toEqual(matchTNode()); expect(tNode).toEqual(matchTNode());

View File

@ -7,6 +7,7 @@
*/ */
import {ElementRef, QueryList, TemplateRef, ViewContainerRef} from '@angular/core'; import {ElementRef, QueryList, TemplateRef, ViewContainerRef} from '@angular/core';
import {HEADER_OFFSET} from '@angular/core/src/render3/interfaces/view';
import {AttributeMarker, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵProvidersFeature} from '../../src/render3/index'; import {AttributeMarker, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵProvidersFeature} from '../../src/render3/index';
import {ɵɵdirectiveInject, ɵɵelement, ɵɵelementContainerEnd, ɵɵelementContainerStart, ɵɵelementEnd, ɵɵelementStart, ɵɵtemplate, ɵɵtext} from '../../src/render3/instructions/all'; import {ɵɵdirectiveInject, ɵɵelement, ɵɵelementContainerEnd, ɵɵelementContainerStart, ɵɵelementEnd, ɵɵelementStart, ɵɵtemplate, ɵɵtext} from '../../src/render3/instructions/all';
@ -72,8 +73,8 @@ describe('query', () => {
ɵɵelementEnd(); ɵɵelementEnd();
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
child1 = getDirectiveOnNode(0); child1 = getDirectiveOnNode(HEADER_OFFSET);
child2 = getDirectiveOnNode(1); child2 = getDirectiveOnNode(HEADER_OFFSET + 1);
} }
}, },
2, 0, [Child], [], 2, 0, [Child], [],
@ -112,7 +113,7 @@ describe('query', () => {
function(rf: RenderFlags, ctx: any) { function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
ɵɵelement(0, 'div', 0); ɵɵelement(0, 'div', 0);
elToQuery = getNativeByIndex(0, getLView()); elToQuery = getNativeByIndex(HEADER_OFFSET, getLView());
} }
}, },
1, 0, [Child], [], 1, 0, [Child], [],
@ -150,7 +151,7 @@ describe('query', () => {
function(rf: RenderFlags, ctx: any) { function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
ɵɵelementStart(0, 'div', 0); ɵɵelementStart(0, 'div', 0);
{ otherChildInstance = getDirectiveOnNode(0, 1); } { otherChildInstance = getDirectiveOnNode(HEADER_OFFSET, 1); }
ɵɵelementEnd(); ɵɵelementEnd();
} }
}, },
@ -345,7 +346,7 @@ describe('query', () => {
function(rf: RenderFlags, ctx: any) { function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
ɵɵelement(0, 'div', null, 0); ɵɵelement(0, 'div', null, 0);
elToQuery = getNativeByIndex(0, getLView()); elToQuery = getNativeByIndex(HEADER_OFFSET, getLView());
ɵɵelement(2, 'div'); ɵɵelement(2, 'div');
} }
}, },
@ -384,7 +385,7 @@ describe('query', () => {
function(rf: RenderFlags, ctx: any) { function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
ɵɵelement(0, 'div', null, 0); ɵɵelement(0, 'div', null, 0);
elToQuery = getNativeByIndex(0, getLView()); elToQuery = getNativeByIndex(HEADER_OFFSET, getLView());
ɵɵelement(3, 'div'); ɵɵelement(3, 'div');
} }
}, },
@ -431,10 +432,10 @@ describe('query', () => {
function(rf: RenderFlags, ctx: any) { function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
ɵɵelement(0, 'div', null, 0); ɵɵelement(0, 'div', null, 0);
el1ToQuery = getNativeByIndex(0, getLView()); el1ToQuery = getNativeByIndex(HEADER_OFFSET, getLView());
ɵɵelement(2, 'div'); ɵɵelement(2, 'div');
ɵɵelement(3, 'div', null, 1); ɵɵelement(3, 'div', null, 1);
el2ToQuery = getNativeByIndex(3, getLView()); el2ToQuery = getNativeByIndex(HEADER_OFFSET + 3, getLView());
} }
}, },
5, 0, [], [], 5, 0, [], [],
@ -471,7 +472,7 @@ describe('query', () => {
function(rf: RenderFlags, ctx: any) { function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
ɵɵelement(0, 'div', null, 0); ɵɵelement(0, 'div', null, 0);
elToQuery = getNativeByIndex(0, getLView()); elToQuery = getNativeByIndex(HEADER_OFFSET, getLView());
ɵɵelement(2, 'div'); ɵɵelement(2, 'div');
} }
}, },
@ -509,7 +510,7 @@ describe('query', () => {
function(rf: RenderFlags, ctx: any) { function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
ɵɵelementContainerStart(0, null, 0); ɵɵelementContainerStart(0, null, 0);
elToQuery = getNativeByIndex(0, getLView()); elToQuery = getNativeByIndex(HEADER_OFFSET, getLView());
ɵɵelementContainerEnd(); ɵɵelementContainerEnd();
} }
}, },
@ -546,7 +547,7 @@ describe('query', () => {
function(rf: RenderFlags, ctx: any) { function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
ɵɵelementContainerStart(0, null, 0); ɵɵelementContainerStart(0, null, 0);
elToQuery = getNativeByIndex(0, getLView()); elToQuery = getNativeByIndex(HEADER_OFFSET, getLView());
ɵɵelementContainerEnd(); ɵɵelementContainerEnd();
} }
}, },
@ -756,7 +757,7 @@ describe('query', () => {
ɵɵelement(0, 'child', null, 0); ɵɵelement(0, 'child', null, 0);
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
childInstance = getDirectiveOnNode(0); childInstance = getDirectiveOnNode(HEADER_OFFSET);
} }
}, },
2, 0, [Child], [], 2, 0, [Child], [],
@ -843,7 +844,7 @@ describe('query', () => {
ɵɵelement(0, 'div', 0, 1); ɵɵelement(0, 'div', 0, 1);
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
childInstance = getDirectiveOnNode(0); childInstance = getDirectiveOnNode(HEADER_OFFSET);
} }
}, },
2, 0, [Child], [], 2, 0, [Child], [],
@ -883,8 +884,8 @@ describe('query', () => {
ɵɵelement(0, 'div', 0, 1); ɵɵelement(0, 'div', 0, 1);
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
child1Instance = getDirectiveOnNode(0, 0); child1Instance = getDirectiveOnNode(HEADER_OFFSET, 0);
child2Instance = getDirectiveOnNode(0, 1); child2Instance = getDirectiveOnNode(HEADER_OFFSET, 1);
} }
}, },
3, 0, [Child1, Child2], [], 3, 0, [Child1, Child2], [],
@ -925,7 +926,7 @@ describe('query', () => {
ɵɵelement(0, 'div', 0, 1); ɵɵelement(0, 'div', 0, 1);
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
childInstance = getDirectiveOnNode(0); childInstance = getDirectiveOnNode(HEADER_OFFSET);
} }
}, },
3, 0, [Child], [], 3, 0, [Child], [],
@ -970,7 +971,7 @@ describe('query', () => {
function(rf: RenderFlags, ctx: any) { function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
ɵɵelement(0, 'div', 0, 1); ɵɵelement(0, 'div', 0, 1);
div = getNativeByIndex(0, getLView()); div = getNativeByIndex(HEADER_OFFSET, getLView());
} }
}, },
2, 0, [Child], [], 2, 0, [Child], [],
@ -1007,10 +1008,10 @@ describe('query', () => {
function(rf: RenderFlags, ctx: any) { function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
ɵɵelement(0, 'div', 0, 1); ɵɵelement(0, 'div', 0, 1);
div = getNativeByIndex(0, getLView()); div = getNativeByIndex(HEADER_OFFSET, getLView());
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
childInstance = getDirectiveOnNode(0); childInstance = getDirectiveOnNode(HEADER_OFFSET);
} }
}, },
3, 0, [Child], [], 3, 0, [Child], [],
@ -1570,8 +1571,8 @@ describe('query', () => {
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
const lView = getLView(); const lView = getLView();
outInstance = load<QueryDirective>(lView, 1); outInstance = load<QueryDirective>(lView, HEADER_OFFSET + 1);
inInstance = load<QueryDirective>(lView, 5); inInstance = load<QueryDirective>(lView, HEADER_OFFSET + 5);
} }
}, },
10, 0, [QueryDirective], [], null, [], [], undefined, [ 10, 0, [QueryDirective], [], null, [], [], undefined, [
@ -1632,8 +1633,8 @@ describe('query', () => {
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
const lView = getLView(); const lView = getLView();
outInstance = load<QueryDirective>(lView, 1); outInstance = load<QueryDirective>(lView, HEADER_OFFSET + 1);
inInstance = load<QueryDirective>(lView, 3); inInstance = load<QueryDirective>(lView, HEADER_OFFSET + 3);
} }
}, },
7, 0, [QueryDirective], [], null, [], [], undefined, [ 7, 0, [QueryDirective], [], null, [], [], undefined, [
@ -1692,8 +1693,8 @@ describe('query', () => {
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
const lView = getLView(); const lView = getLView();
outInstance = load<QueryDirective>(lView, 1); outInstance = load<QueryDirective>(lView, HEADER_OFFSET + 1);
inInstance = load<QueryDirective>(lView, 3); inInstance = load<QueryDirective>(lView, HEADER_OFFSET + 3);
} }
}, },
7, 0, [QueryDirective], [], null, [], [], undefined, [ 7, 0, [QueryDirective], [], null, [], [], undefined, [
@ -1783,8 +1784,8 @@ describe('query', () => {
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
const lView = getLView(); const lView = getLView();
shallowInstance = load<ShallowQueryDirective>(lView, 1); shallowInstance = load<ShallowQueryDirective>(lView, HEADER_OFFSET + 1);
deepInstance = load<DeepQueryDirective>(lView, 2); deepInstance = load<DeepQueryDirective>(lView, HEADER_OFFSET + 2);
} }
}, },
8, 0, [ShallowQueryDirective, DeepQueryDirective], [], null, [], [], undefined, [ 8, 0, [ShallowQueryDirective, DeepQueryDirective], [], null, [], [], undefined, [

View File

@ -17,7 +17,6 @@ import {TConstants, TNodeType} from '@angular/core/src/render3/interfaces/node';
import {enterView, getLView} from '@angular/core/src/render3/state'; import {enterView, getLView} from '@angular/core/src/render3/state';
import {EMPTY_ARRAY} from '@angular/core/src/util/empty'; import {EMPTY_ARRAY} from '@angular/core/src/util/empty';
import {stringifyElement} from '@angular/platform-browser/testing/src/browser_util'; import {stringifyElement} from '@angular/platform-browser/testing/src/browser_util';
import {SWITCH_CHANGE_DETECTOR_REF_FACTORY__POST_R3__ as R3_CHANGE_DETECTOR_REF_FACTORY} from '../../src/change_detection/change_detector_ref'; import {SWITCH_CHANGE_DETECTOR_REF_FACTORY__POST_R3__ as R3_CHANGE_DETECTOR_REF_FACTORY} from '../../src/change_detection/change_detector_ref';
import {Injector} from '../../src/di/injector'; import {Injector} from '../../src/di/injector';
import {Type} from '../../src/interface/type'; import {Type} from '../../src/interface/type';
@ -33,11 +32,10 @@ import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveT
import {DirectiveDefList, DirectiveDefListOrFactory, DirectiveTypesOrFactory, HostBindingsFunction, PipeDef, PipeDefList, PipeDefListOrFactory, PipeTypesOrFactory} from '../../src/render3/interfaces/definition'; import {DirectiveDefList, DirectiveDefListOrFactory, DirectiveTypesOrFactory, HostBindingsFunction, PipeDef, PipeDefList, PipeDefListOrFactory, PipeTypesOrFactory} from '../../src/render3/interfaces/definition';
import {PlayerHandler} from '../../src/render3/interfaces/player'; import {PlayerHandler} from '../../src/render3/interfaces/player';
import {domRendererFactory3, ProceduralRenderer3, RComment, RElement, Renderer3, RendererFactory3, RendererStyleFlags3, RNode, RText} from '../../src/render3/interfaces/renderer'; import {domRendererFactory3, ProceduralRenderer3, RComment, RElement, Renderer3, RendererFactory3, RendererStyleFlags3, RNode, RText} from '../../src/render3/interfaces/renderer';
import {HEADER_OFFSET, LView, LViewFlags, T_HOST, TVIEW, TViewType} from '../../src/render3/interfaces/view'; import {HEADER_OFFSET, LView, LViewFlags, TVIEW, TViewType} from '../../src/render3/interfaces/view';
import {destroyLView} from '../../src/render3/node_manipulation'; import {destroyLView} from '../../src/render3/node_manipulation';
import {getRootView} from '../../src/render3/util/view_traversal_utils'; import {getRootView} from '../../src/render3/util/view_traversal_utils';
import {Sanitizer} from '../../src/sanitization/sanitizer'; import {Sanitizer} from '../../src/sanitization/sanitizer';
import {getRendererFactory2} from './imported_renderer2'; import {getRendererFactory2} from './imported_renderer2';
@ -434,7 +432,7 @@ export function createDirective(
/** Gets the directive on the given node at the given index */ /** Gets the directive on the given node at the given index */
export function getDirectiveOnNode(nodeIndex: number, dirIndex: number = 0) { export function getDirectiveOnNode(nodeIndex: number, dirIndex: number = 0) {
const directives = getDirectivesAtNodeIndex(nodeIndex + HEADER_OFFSET, getLView(), true); const directives = getDirectivesAtNodeIndex(nodeIndex, getLView(), true);
if (directives == null) { if (directives == null) {
throw new Error(`No directives exist on node in slot ${nodeIndex}`); throw new Error(`No directives exist on node in slot ${nodeIndex}`);
} }

View File

@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {HEADER_OFFSET} from '@angular/core/src/render3/interfaces/view';
import {ChangeDetectorRef, Component as _Component, ComponentFactoryResolver, ElementRef, QueryList, TemplateRef, ViewContainerRef, ViewRef} from '../../src/core'; import {ChangeDetectorRef, Component as _Component, ComponentFactoryResolver, ElementRef, QueryList, TemplateRef, ViewContainerRef, ViewRef} from '../../src/core';
import {ViewEncapsulation} from '../../src/metadata'; import {ViewEncapsulation} from '../../src/metadata';
import {injectComponentFactoryResolver, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵlistener, ɵɵloadQuery, ɵɵqueryRefresh, ɵɵviewQuery} from '../../src/render3/index'; import {injectComponentFactoryResolver, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵlistener, ɵɵloadQuery, ɵɵqueryRefresh, ɵɵviewQuery} from '../../src/render3/index';
@ -14,9 +15,9 @@ import {RenderFlags} from '../../src/render3/interfaces/definition';
import {RElement} from '../../src/render3/interfaces/renderer'; import {RElement} from '../../src/render3/interfaces/renderer';
import {getLView} from '../../src/render3/state'; import {getLView} from '../../src/render3/state';
import {getNativeByIndex} from '../../src/render3/util/view_utils'; import {getNativeByIndex} from '../../src/render3/util/view_utils';
import {ComponentFixture, createComponent, TemplateFixture} from './render_util'; import {ComponentFixture, createComponent, TemplateFixture} from './render_util';
const Component: typeof _Component = function(...args: any[]): any { const Component: typeof _Component = function(...args: any[]): any {
// In test we use @Component for documentation only so it's safe to mock out the implementation. // In test we use @Component for documentation only so it's safe to mock out the implementation.
return () => undefined; return () => undefined;
@ -362,7 +363,7 @@ describe('ViewContainerRef', () => {
ɵɵelement(0, 'div', 1, 0); ɵɵelement(0, 'div', 1, 0);
} }
// testing only // testing only
fooEl = getNativeByIndex(0, getLView()) as RElement; fooEl = getNativeByIndex(HEADER_OFFSET, getLView()) as RElement;
}, },
viewQuery: viewQuery:
function(rf: RenderFlags, ctx: any) { function(rf: RenderFlags, ctx: any) {