refactor(ivy): move type from LNode to TNode (#24113)

PR Close #24113
This commit is contained in:
Kara Erickson 2018-05-17 12:54:57 -07:00 committed by Matias Niemelä
parent 8216657681
commit 68bf8c36c6
7 changed files with 95 additions and 91 deletions

View File

@ -22,7 +22,7 @@ import {assertGreaterThan, assertLessThan, assertNotNull} from './assert';
import {addToViewTree, assertPreviousIsParent, createLContainer, createLNodeObject, createTNode, createTView, getDirectiveInstance, getPreviousOrParentNode, getRenderer, isComponent, renderEmbeddedTemplate, resolveDirective} from './instructions'; import {addToViewTree, assertPreviousIsParent, createLContainer, createLNodeObject, createTNode, createTView, getDirectiveInstance, getPreviousOrParentNode, getRenderer, isComponent, renderEmbeddedTemplate, resolveDirective} from './instructions';
import {ComponentTemplate, DirectiveDef, DirectiveDefList, PipeDefList} from './interfaces/definition'; import {ComponentTemplate, DirectiveDef, DirectiveDefList, PipeDefList} from './interfaces/definition';
import {LInjector} from './interfaces/injector'; import {LInjector} from './interfaces/injector';
import {LContainerNode, LElementNode, LNode, LNodeType, LViewNode, TNodeFlags} from './interfaces/node'; import {LContainerNode, LElementNode, LNode, LViewNode, TNodeFlags, TNodeType} from './interfaces/node';
import {QueryReadType} from './interfaces/query'; import {QueryReadType} from './interfaces/query';
import {Renderer3} from './interfaces/renderer'; import {Renderer3} from './interfaces/renderer';
import {LView, TView} from './interfaces/view'; import {LView, TView} from './interfaces/view';
@ -253,7 +253,7 @@ export function injectChangeDetectorRef(): viewEngine_ChangeDetectorRef {
export function injectAttribute(attrName: string): string|undefined { export function injectAttribute(attrName: string): string|undefined {
ngDevMode && assertPreviousIsParent(); ngDevMode && assertPreviousIsParent();
const lElement = getPreviousOrParentNode() as LElementNode; const lElement = getPreviousOrParentNode() as LElementNode;
ngDevMode && assertNodeType(lElement, LNodeType.Element); ngDevMode && assertNodeType(lElement, TNodeType.Element);
const tElement = lElement.tNode; const tElement = lElement.tNode;
ngDevMode && assertNotNull(tElement, 'expecting tNode'); ngDevMode && assertNotNull(tElement, 'expecting tNode');
const attrs = tElement.attrs; const attrs = tElement.attrs;
@ -280,7 +280,7 @@ export function getOrCreateChangeDetectorRef(
const currentNode = di.node; const currentNode = di.node;
if (isComponent(currentNode.tNode)) { if (isComponent(currentNode.tNode)) {
return di.changeDetectorRef = createViewRef(currentNode.data as LView, context); return di.changeDetectorRef = createViewRef(currentNode.data as LView, context);
} else if (currentNode.type === LNodeType.Element) { } else if (currentNode.tNode.type === TNodeType.Element) {
return di.changeDetectorRef = getOrCreateHostChangeDetector(currentNode.view.node); return di.changeDetectorRef = getOrCreateHostChangeDetector(currentNode.view.node);
} }
return null !; return null !;
@ -307,7 +307,7 @@ function getOrCreateHostChangeDetector(currentNode: LViewNode | LElementNode):
* returns itself. * returns itself.
*/ */
function getClosestComponentAncestor(node: LViewNode | LElementNode): LElementNode { function getClosestComponentAncestor(node: LViewNode | LElementNode): LElementNode {
while (node.type === LNodeType.View) { while (node.tNode.type === TNodeType.View) {
node = node.view.node; node = node.view.node;
} }
return node as LElementNode; return node as LElementNode;
@ -523,7 +523,7 @@ export class ReadFromInjectorFn<T> {
*/ */
export function getOrCreateElementRef(di: LInjector): viewEngine_ElementRef { export function getOrCreateElementRef(di: LInjector): viewEngine_ElementRef {
return di.elementRef || (di.elementRef = new ElementRef( return di.elementRef || (di.elementRef = new ElementRef(
di.node.type === LNodeType.Container ? null : di.node.native)); di.node.tNode.type === TNodeType.Container ? null : di.node.native));
} }
export const QUERY_READ_TEMPLATE_REF = <QueryReadType<viewEngine_TemplateRef<any>>>( export const QUERY_READ_TEMPLATE_REF = <QueryReadType<viewEngine_TemplateRef<any>>>(
@ -540,12 +540,12 @@ export const QUERY_READ_ELEMENT_REF =
export const QUERY_READ_FROM_NODE = export const QUERY_READ_FROM_NODE =
(new ReadFromInjectorFn<any>((injector: LInjector, node: LNode, directiveIdx: number) => { (new ReadFromInjectorFn<any>((injector: LInjector, node: LNode, directiveIdx: number) => {
ngDevMode && assertNodeOfPossibleTypes(node, LNodeType.Container, LNodeType.Element); ngDevMode && assertNodeOfPossibleTypes(node, TNodeType.Container, TNodeType.Element);
if (directiveIdx > -1) { if (directiveIdx > -1) {
return node.view.directives ![directiveIdx]; return node.view.directives ![directiveIdx];
} else if (node.type === LNodeType.Element) { } else if (node.tNode.type === TNodeType.Element) {
return getOrCreateElementRef(injector); return getOrCreateElementRef(injector);
} else if (node.type === LNodeType.Container) { } else if (node.tNode.type === TNodeType.Container) {
return getOrCreateTemplateRef(injector); return getOrCreateTemplateRef(injector);
} }
throw new Error('fail'); throw new Error('fail');
@ -567,14 +567,15 @@ export function getOrCreateContainerRef(di: LInjector): viewEngine_ViewContainer
if (!di.viewContainerRef) { if (!di.viewContainerRef) {
const vcRefHost = di.node; const vcRefHost = di.node;
ngDevMode && assertNodeOfPossibleTypes(vcRefHost, LNodeType.Container, LNodeType.Element); ngDevMode && assertNodeOfPossibleTypes(vcRefHost, TNodeType.Container, TNodeType.Element);
const lContainer = createLContainer(vcRefHost.parent !, vcRefHost.view); const lContainer = createLContainer(vcRefHost.parent !, vcRefHost.view);
const lContainerNode: LContainerNode = createLNodeObject( const lContainerNode: LContainerNode = createLNodeObject(
LNodeType.Container, vcRefHost.view, vcRefHost.parent !, undefined, lContainer, null); TNodeType.Container, vcRefHost.view, vcRefHost.parent !, undefined, lContainer, null);
const hostTNode = vcRefHost.tNode; const hostTNode = vcRefHost.tNode;
if (!hostTNode.dynamicContainerNode) { if (!hostTNode.dynamicContainerNode) {
hostTNode.dynamicContainerNode = createTNode(hostTNode.index, null, null, null); hostTNode.dynamicContainerNode =
createTNode(TNodeType.Container, hostTNode.index, null, null, null);
} }
lContainerNode.tNode = hostTNode.dynamicContainerNode; lContainerNode.tNode = hostTNode.dynamicContainerNode;
@ -650,7 +651,7 @@ class ViewContainerRef implements viewEngine_ViewContainerRef {
// Look for the parent node and increment its dynamic view count. // Look for the parent node and increment its dynamic view count.
if (this._lContainerNode.parent !== null && this._lContainerNode.parent.data !== null) { if (this._lContainerNode.parent !== null && this._lContainerNode.parent.data !== null) {
ngDevMode && assertNodeOfPossibleTypes( ngDevMode && assertNodeOfPossibleTypes(
this._lContainerNode.parent, LNodeType.View, LNodeType.Element); this._lContainerNode.parent, TNodeType.View, TNodeType.Element);
this._lContainerNode.parent.data.dynamicViewCount++; this._lContainerNode.parent.data.dynamicViewCount++;
} }
} }
@ -701,7 +702,7 @@ class ViewContainerRef implements viewEngine_ViewContainerRef {
*/ */
export function getOrCreateTemplateRef<T>(di: LInjector): viewEngine_TemplateRef<T> { export function getOrCreateTemplateRef<T>(di: LInjector): viewEngine_TemplateRef<T> {
if (!di.templateRef) { if (!di.templateRef) {
ngDevMode && assertNodeType(di.node, LNodeType.Container); ngDevMode && assertNodeType(di.node, TNodeType.Container);
const hostNode = di.node as LContainerNode; const hostNode = di.node as LContainerNode;
const hostTNode = hostNode.tNode; const hostTNode = hostNode.tNode;
const hostTView = hostNode.view.tView; const hostTView = hostNode.view.tView;

View File

@ -15,7 +15,7 @@ import {CssSelectorList, LProjection, NG_PROJECT_AS_ATTR_NAME} from './interface
import {LQueries} from './interfaces/query'; import {LQueries} from './interfaces/query';
import {CurrentMatchesList, LView, LViewFlags, LifecycleStage, RootContext, TData, TView} from './interfaces/view'; import {CurrentMatchesList, LView, LViewFlags, LifecycleStage, RootContext, TData, TView} from './interfaces/view';
import {LContainerNode, LElementNode, LNode, LNodeType, TNodeFlags, LProjectionNode, LTextNode, LViewNode, TNode, TContainerNode, InitialInputData, InitialInputs, PropertyAliases, PropertyAliasValue,} from './interfaces/node'; import {LContainerNode, LElementNode, LNode, TNodeType, TNodeFlags, LProjectionNode, LTextNode, LViewNode, TNode, TContainerNode, InitialInputData, InitialInputs, PropertyAliases, PropertyAliasValue,} from './interfaces/node';
import {assertNodeType} from './node_assert'; import {assertNodeType} from './node_assert';
import {appendChild, insertView, appendProjectedNode, removeView, canInsertNativeNode, createTextNode, getNextLNode} from './node_manipulation'; import {appendChild, insertView, appendProjectedNode, removeView, canInsertNativeNode, createTextNode, getNextLNode} from './node_manipulation';
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher'; import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
@ -337,11 +337,10 @@ export function createLView<T>(
* (same properties assigned in the same order). * (same properties assigned in the same order).
*/ */
export function createLNodeObject( export function createLNodeObject(
type: LNodeType, currentView: LView, parent: LNode, native: RText | RElement | null | undefined, type: TNodeType, currentView: LView, parent: LNode, native: RText | RElement | null | undefined,
state: any, state: any,
queries: LQueries | null): LElementNode&LTextNode&LViewNode&LContainerNode&LProjectionNode { queries: LQueries | null): LElementNode&LTextNode&LViewNode&LContainerNode&LProjectionNode {
return { return {
type: type,
native: native as any, native: native as any,
view: currentView, view: currentView,
parent: parent as any, parent: parent as any,
@ -368,19 +367,19 @@ export function createLNodeObject(
* @param data Any data that should be saved on the LNode * @param data Any data that should be saved on the LNode
*/ */
export function createLNode( export function createLNode(
index: number | null, type: LNodeType.Element, native: RElement | RText | null, index: number | null, type: TNodeType.Element, native: RElement | RText | null,
name: string | null, attrs: string[] | null, lView?: LView | null): LElementNode; name: string | null, attrs: string[] | null, lView?: LView | null): LElementNode;
export function createLNode( export function createLNode(
index: number | null, type: LNodeType.View, native: null, name: null, attrs: null, index: number | null, type: TNodeType.View, native: null, name: null, attrs: null,
lView: LView): LViewNode; lView: LView): LViewNode;
export function createLNode( export function createLNode(
index: number, type: LNodeType.Container, native: undefined, name: string | null, index: number, type: TNodeType.Container, native: undefined, name: string | null,
attrs: string[] | null, lContainer: LContainer): LContainerNode; attrs: string[] | null, lContainer: LContainer): LContainerNode;
export function createLNode( export function createLNode(
index: number, type: LNodeType.Projection, native: null, name: null, attrs: string[] | null, index: number, type: TNodeType.Projection, native: null, name: null, attrs: string[] | null,
lProjection: LProjection): LProjectionNode; lProjection: LProjection): LProjectionNode;
export function createLNode( export function createLNode(
index: number | null, type: LNodeType, native: RText | RElement | null | undefined, index: number | null, type: TNodeType, native: RText | RElement | null | undefined,
name: string | null, attrs: string[] | null, state?: null | LView | LContainer | name: string | null, attrs: string[] | null, state?: null | LView | LContainer |
LProjection): LElementNode&LTextNode&LViewNode&LContainerNode&LProjectionNode { LProjection): LElementNode&LTextNode&LViewNode&LContainerNode&LProjectionNode {
const parent = isParent ? previousOrParentNode : const parent = isParent ? previousOrParentNode :
@ -392,10 +391,10 @@ export function createLNode(
const node = const node =
createLNodeObject(type, currentView, parent, native, isState ? state as any : null, queries); createLNodeObject(type, currentView, parent, native, isState ? state as any : null, queries);
if (index === null || type === LNodeType.View) { if (index === null || type === TNodeType.View) {
// View nodes are not stored in data because they can be added / removed at runtime (which // View nodes are not stored in data because they can be added / removed at runtime (which
// would cause indices to change). Their TNodes are instead stored in TView.node. // would cause indices to change). Their TNodes are instead stored in TView.node.
node.tNode = (state as LView).tView.node || createTNode(index, null, null, null); node.tNode = (state as LView).tView.node || createTNode(type, index, null, null, null);
} else { } else {
// This is an element or container or projection node // This is an element or container or projection node
ngDevMode && assertDataNext(index); ngDevMode && assertDataNext(index);
@ -403,7 +402,7 @@ export function createLNode(
// Every node adds a value to the static data array to avoid a sparse array // Every node adds a value to the static data array to avoid a sparse array
if (index >= tData.length) { if (index >= tData.length) {
const tNode = tData[index] = createTNode(index, name, attrs, null); const tNode = tData[index] = createTNode(type, index, name, attrs, null);
if (!isParent && previousOrParentNode) { if (!isParent && previousOrParentNode) {
const previousTNode = previousOrParentNode.tNode; const previousTNode = previousOrParentNode.tNode;
previousTNode.next = tNode; previousTNode.next = tNode;
@ -416,7 +415,7 @@ export function createLNode(
if (isParent) { if (isParent) {
currentQueries = null; currentQueries = null;
if (previousOrParentNode.view === currentView || if (previousOrParentNode.view === currentView ||
previousOrParentNode.type === LNodeType.View) { previousOrParentNode.tNode.type === TNodeType.View) {
// We are in the same view, which means we are adding content node to the parent View. // We are in the same view, which means we are adding content node to the parent View.
ngDevMode && assertNull( ngDevMode && assertNull(
previousOrParentNode.child, previousOrParentNode.child,
@ -429,7 +428,7 @@ export function createLNode(
} }
// View nodes and host elements need to set their host node (components set host nodes later) // View nodes and host elements need to set their host node (components set host nodes later)
if ((type & LNodeType.ViewOrElement) === LNodeType.ViewOrElement && isState) { if ((type & TNodeType.ViewOrElement) === TNodeType.ViewOrElement && isState) {
// Bit of a hack to bust through the readonly because there is a circular dep between // Bit of a hack to bust through the readonly because there is a circular dep between
// LView and LNode. // LView and LNode.
ngDevMode && assertNull((state as LView).node, 'LView.node should not have been initialized'); ngDevMode && assertNull((state as LView).node, 'LView.node should not have been initialized');
@ -475,7 +474,7 @@ export function renderTemplate<T>(
rendererFactory = providedRendererFactory; rendererFactory = providedRendererFactory;
const tView = getOrCreateTView(template, directives || null, pipes || null); const tView = getOrCreateTView(template, directives || null, pipes || null);
host = createLNode( host = createLNode(
null, LNodeType.Element, hostNode, null, null, null, TNodeType.Element, hostNode, null, null,
createLView( createLView(
-1, providedRendererFactory.createRenderer(null, null), tView, null, {}, -1, providedRendererFactory.createRenderer(null, null), tView, null, {},
LViewFlags.CheckAlways, sanitizer)); LViewFlags.CheckAlways, sanitizer));
@ -512,7 +511,7 @@ export function renderEmbeddedTemplate<T>(
const lView = createLView( const lView = createLView(
-1, renderer, tView, template, context, LViewFlags.CheckAlways, getCurrentSanitizer()); -1, renderer, tView, template, context, LViewFlags.CheckAlways, getCurrentSanitizer());
viewNode = createLNode(null, LNodeType.View, null, null, null, lView); viewNode = createLNode(null, TNodeType.View, null, null, null, lView);
rf = RenderFlags.Create; rf = RenderFlags.Create;
} }
oldView = enterView(viewNode.data, viewNode); oldView = enterView(viewNode.data, viewNode);
@ -600,7 +599,7 @@ export function elementStart(
ngDevMode && assertDataInRange(index - 1); ngDevMode && assertDataInRange(index - 1);
const node: LElementNode = const node: LElementNode =
createLNode(index, LNodeType.Element, native !, name, attrs || null, null); createLNode(index, TNodeType.Element, native !, name, attrs || null, null);
if (attrs) setUpAttributes(native, attrs); if (attrs) setUpAttributes(native, attrs);
appendChild(node.parent !, native, currentView); appendChild(node.parent !, native, currentView);
@ -884,7 +883,7 @@ export function hostElement(
sanitizer?: Sanitizer | null): LElementNode { sanitizer?: Sanitizer | null): LElementNode {
resetApplicationState(); resetApplicationState();
const node = createLNode( const node = createLNode(
0, LNodeType.Element, rNode, null, null, 0, TNodeType.Element, rNode, null, null,
createLView( createLView(
-1, renderer, getOrCreateTView(def.template, def.directiveDefs, def.pipeDefs), null, null, -1, renderer, getOrCreateTView(def.template, def.directiveDefs, def.pipeDefs), null, null,
def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, sanitizer)); def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, sanitizer));
@ -963,7 +962,7 @@ export function elementEnd() {
ngDevMode && assertHasParent(); ngDevMode && assertHasParent();
previousOrParentNode = previousOrParentNode.parent !; previousOrParentNode = previousOrParentNode.parent !;
} }
ngDevMode && assertNodeType(previousOrParentNode, LNodeType.Element); ngDevMode && assertNodeType(previousOrParentNode, TNodeType.Element);
const queries = previousOrParentNode.queries; const queries = previousOrParentNode.queries;
queries && queries.addNode(previousOrParentNode); queries && queries.addNode(previousOrParentNode);
queueLifecycleHooks(previousOrParentNode.tNode.flags, currentView); queueLifecycleHooks(previousOrParentNode.tNode.flags, currentView);
@ -1041,6 +1040,7 @@ export function elementProperty<T>(
/** /**
* Constructs a TNode object from the arguments. * Constructs a TNode object from the arguments.
* *
* @param type The type of the node
* @param index The index of the TNode in TView.data * @param index The index of the TNode in TView.data
* @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
@ -1048,10 +1048,11 @@ export function elementProperty<T>(
* @returns the TNode object * @returns the TNode object
*/ */
export function createTNode( export function createTNode(
index: number | null, tagName: string | null, attrs: string[] | null, type: TNodeType, index: number | null, tagName: string | null, attrs: string[] | null,
tViews: TView[] | null): TNode { tViews: TView[] | null): TNode {
ngDevMode && ngDevMode.tNode++; ngDevMode && ngDevMode.tNode++;
return { return {
type: type,
index: index, index: index,
flags: 0, flags: 0,
tagName: tagName, tagName: tagName,
@ -1257,7 +1258,7 @@ export function text(index: number, value?: any): void {
currentView.bindingStartIndex, -1, 'text nodes should be created before bindings'); currentView.bindingStartIndex, -1, 'text nodes should be created before bindings');
ngDevMode && ngDevMode.rendererCreateTextNode++; ngDevMode && ngDevMode.rendererCreateTextNode++;
const textNode = createTextNode(value, renderer); const textNode = createTextNode(value, renderer);
const node = createLNode(index, LNodeType.Element, textNode, null, null); const node = createLNode(index, TNodeType.Element, textNode, null, null);
// Text nodes are self closing. // Text nodes are self closing.
isParent = false; isParent = false;
@ -1386,7 +1387,7 @@ export function baseDirectiveCreate<T>(
if (diPublic) diPublic(directiveDef !); if (diPublic) diPublic(directiveDef !);
} }
if (directiveDef !.attributes != null && previousOrParentNode.type == LNodeType.Element) { if (directiveDef !.attributes != null && previousOrParentNode.tNode.type == TNodeType.Element) {
setUpAttributes( setUpAttributes(
(previousOrParentNode as LElementNode).native, directiveDef !.attributes as string[]); (previousOrParentNode as LElementNode).native, directiveDef !.attributes as string[]);
} }
@ -1495,7 +1496,7 @@ export function container(
const lContainer = createLContainer(currentParent, currentView, template); const lContainer = createLContainer(currentParent, currentView, template);
const node = createLNode( const node = createLNode(
index, LNodeType.Container, undefined, tagName || null, attrs || null, lContainer); index, TNodeType.Container, undefined, tagName || null, attrs || null, lContainer);
if (firstTemplatePass && template == null) node.tNode.tViews = []; if (firstTemplatePass && template == null) node.tNode.tViews = [];
@ -1505,7 +1506,7 @@ export function container(
createDirectivesAndLocals(index, tagName || null, attrs, localRefs, template == null); createDirectivesAndLocals(index, tagName || null, attrs, localRefs, template == null);
isParent = false; isParent = false;
ngDevMode && assertNodeType(previousOrParentNode, LNodeType.Container); ngDevMode && assertNodeType(previousOrParentNode, TNodeType.Container);
const queries = node.queries; const queries = node.queries;
if (queries) { if (queries) {
// check if a given container node matches // check if a given container node matches
@ -1523,7 +1524,7 @@ export function container(
export function containerRefreshStart(index: number): void { export function containerRefreshStart(index: number): void {
ngDevMode && assertDataInRange(index); ngDevMode && assertDataInRange(index);
previousOrParentNode = data[index] as LNode; previousOrParentNode = data[index] as LNode;
ngDevMode && assertNodeType(previousOrParentNode, LNodeType.Container); ngDevMode && assertNodeType(previousOrParentNode, TNodeType.Container);
isParent = true; isParent = true;
(previousOrParentNode as LContainerNode).data.nextIndex = 0; (previousOrParentNode as LContainerNode).data.nextIndex = 0;
ngDevMode && assertSame( ngDevMode && assertSame(
@ -1546,14 +1547,14 @@ export function containerRefreshEnd(): void {
if (isParent) { if (isParent) {
isParent = false; isParent = false;
} else { } else {
ngDevMode && assertNodeType(previousOrParentNode, LNodeType.View); ngDevMode && assertNodeType(previousOrParentNode, TNodeType.View);
ngDevMode && assertHasParent(); ngDevMode && assertHasParent();
previousOrParentNode = previousOrParentNode.parent !; previousOrParentNode = previousOrParentNode.parent !;
} }
ngDevMode && assertNodeType(previousOrParentNode, LNodeType.Container); ngDevMode && assertNodeType(previousOrParentNode, TNodeType.Container);
const container = previousOrParentNode as LContainerNode; const container = previousOrParentNode as LContainerNode;
container.native = undefined; container.native = undefined;
ngDevMode && assertNodeType(container, LNodeType.Container); ngDevMode && assertNodeType(container, TNodeType.Container);
const nextIndex = container.data.nextIndex; const nextIndex = container.data.nextIndex;
// remove extra views at the end of the container // remove extra views at the end of the container
@ -1616,13 +1617,13 @@ function scanForView(
export function embeddedViewStart(viewBlockId: number): RenderFlags { export function embeddedViewStart(viewBlockId: number): RenderFlags {
const container = const container =
(isParent ? previousOrParentNode : previousOrParentNode.parent !) as LContainerNode; (isParent ? previousOrParentNode : previousOrParentNode.parent !) as LContainerNode;
ngDevMode && assertNodeType(container, LNodeType.Container); ngDevMode && assertNodeType(container, TNodeType.Container);
const lContainer = container.data; const lContainer = container.data;
let viewNode: LViewNode|null = scanForView(container, lContainer.nextIndex, viewBlockId); let viewNode: LViewNode|null = scanForView(container, lContainer.nextIndex, viewBlockId);
if (viewNode) { if (viewNode) {
previousOrParentNode = viewNode; previousOrParentNode = viewNode;
ngDevMode && assertNodeType(previousOrParentNode, LNodeType.View); ngDevMode && assertNodeType(previousOrParentNode, TNodeType.View);
isParent = true; isParent = true;
enterView(viewNode.data, viewNode); enterView(viewNode.data, viewNode);
} else { } else {
@ -1635,7 +1636,7 @@ export function embeddedViewStart(viewBlockId: number): RenderFlags {
} }
enterView( enterView(
newView, viewNode = createLNode(viewBlockId, LNodeType.View, null, null, null, newView)); newView, viewNode = createLNode(viewBlockId, TNodeType.View, null, null, null, newView));
} }
return getRenderFlags(viewNode.data); return getRenderFlags(viewNode.data);
} }
@ -1652,7 +1653,7 @@ export function embeddedViewStart(viewBlockId: number): RenderFlags {
* @returns TView * @returns TView
*/ */
function getOrCreateEmbeddedTView(viewIndex: number, parent: LContainerNode): TView { function getOrCreateEmbeddedTView(viewIndex: number, parent: LContainerNode): TView {
ngDevMode && assertNodeType(parent, LNodeType.Container); ngDevMode && assertNodeType(parent, TNodeType.Container);
const containerTViews = (parent !.tNode as TContainerNode).tViews as TView[]; const containerTViews = (parent !.tNode as TContainerNode).tViews as TView[];
ngDevMode && assertNotNull(containerTViews, 'TView expected'); ngDevMode && assertNotNull(containerTViews, 'TView expected');
ngDevMode && assertEqual(Array.isArray(containerTViews), true, 'TViews should be in an array'); ngDevMode && assertEqual(Array.isArray(containerTViews), true, 'TViews should be in an array');
@ -1670,8 +1671,8 @@ export function embeddedViewEnd(): void {
const viewNode = previousOrParentNode = currentView.node as LViewNode; const viewNode = previousOrParentNode = currentView.node as LViewNode;
const containerNode = previousOrParentNode.parent as LContainerNode; const containerNode = previousOrParentNode.parent as LContainerNode;
if (containerNode) { if (containerNode) {
ngDevMode && assertNodeType(viewNode, LNodeType.View); ngDevMode && assertNodeType(viewNode, TNodeType.View);
ngDevMode && assertNodeType(containerNode, LNodeType.Container); ngDevMode && assertNodeType(containerNode, TNodeType.Container);
const lContainer = containerNode.data; const lContainer = containerNode.data;
if (creationMode) { if (creationMode) {
@ -1686,7 +1687,7 @@ export function embeddedViewEnd(): void {
} }
leaveView(currentView !.parent !); leaveView(currentView !.parent !);
ngDevMode && assertEqual(isParent, false, 'isParent'); ngDevMode && assertEqual(isParent, false, 'isParent');
ngDevMode && assertNodeType(previousOrParentNode, LNodeType.View); ngDevMode && assertNodeType(previousOrParentNode, TNodeType.View);
} }
/** /**
@ -1700,7 +1701,7 @@ function setRenderParentInProjectedNodes(
if (renderParent != null) { if (renderParent != null) {
let node: LNode|null = viewNode.child; let node: LNode|null = viewNode.child;
while (node) { while (node) {
if (node.type === LNodeType.Projection) { if (node.tNode.type === TNodeType.Projection) {
let nodeToProject: LNode|null = (node as LProjectionNode).data.head; let nodeToProject: LNode|null = (node as LProjectionNode).data.head;
const lastNodeToProject = (node as LProjectionNode).data.tail; const lastNodeToProject = (node as LProjectionNode).data.tail;
while (nodeToProject) { while (nodeToProject) {
@ -1726,7 +1727,7 @@ function setRenderParentInProjectedNodes(
export function componentRefresh<T>(directiveIndex: number, elementIndex: number): void { export function componentRefresh<T>(directiveIndex: number, elementIndex: number): void {
ngDevMode && assertDataInRange(elementIndex); ngDevMode && assertDataInRange(elementIndex);
const element = data ![elementIndex] as LElementNode; const element = data ![elementIndex] as LElementNode;
ngDevMode && assertNodeType(element, LNodeType.Element); ngDevMode && assertNodeType(element, TNodeType.Element);
ngDevMode && assertNotNull(element.data, `Component's host node should have an LView attached.`); ngDevMode && assertNotNull(element.data, `Component's host node should have an LView attached.`);
const hostView = element.data !; const hostView = element.data !;
@ -1836,7 +1837,7 @@ function appendToProjectionNode(
export function projection( export function projection(
nodeIndex: number, localIndex: number, selectorIndex: number = 0, attrs?: string[]): void { nodeIndex: number, localIndex: number, selectorIndex: number = 0, attrs?: string[]): void {
const node = createLNode( const node = createLNode(
nodeIndex, LNodeType.Projection, null, null, attrs || null, {head: null, tail: null}); nodeIndex, TNodeType.Projection, null, null, attrs || null, {head: null, tail: null});
// `<ng-content>` has no content // `<ng-content>` has no content
isParent = false; isParent = false;
@ -1850,7 +1851,7 @@ export function projection(
// build the linked list of projected nodes: // build the linked list of projected nodes:
for (let i = 0; i < nodesForSelector.length; i++) { for (let i = 0; i < nodesForSelector.length; i++) {
const nodeToProject = nodesForSelector[i]; const nodeToProject = nodesForSelector[i];
if (nodeToProject.type === LNodeType.Projection) { if (nodeToProject.tNode.type === TNodeType.Projection) {
// Reprojecting a projection -> append the list of previously projected nodes // Reprojecting a projection -> append the list of previously projected nodes
const previouslyProjected = (nodeToProject as LProjectionNode).data; const previouslyProjected = (nodeToProject as LProjectionNode).data;
appendToProjectionNode(node, previouslyProjected.head, previouslyProjected.tail); appendToProjectionNode(node, previouslyProjected.head, previouslyProjected.tail);
@ -1863,7 +1864,7 @@ export function projection(
} }
if (canInsertNativeNode(currentParent, currentView)) { if (canInsertNativeNode(currentParent, currentView)) {
ngDevMode && assertNodeType(currentParent, LNodeType.Element); ngDevMode && assertNodeType(currentParent, TNodeType.Element);
// process each node in the list of projected nodes: // process each node in the list of projected nodes:
let nodeToProject: LNode|null = node.data.head; let nodeToProject: LNode|null = node.data.head;
const lastNodeToProject = node.data.tail; const lastNodeToProject = node.data.tail;
@ -1884,13 +1885,13 @@ export function projection(
*/ */
function findComponentHost(lView: LView): LElementNode { function findComponentHost(lView: LView): LElementNode {
let viewRootLNode = lView.node; let viewRootLNode = lView.node;
while (viewRootLNode.type === LNodeType.View) { while (viewRootLNode.tNode.type === TNodeType.View) {
ngDevMode && assertNotNull(lView.parent, 'lView.parent'); ngDevMode && assertNotNull(lView.parent, 'lView.parent');
lView = lView.parent !; lView = lView.parent !;
viewRootLNode = lView.node; viewRootLNode = lView.node;
} }
ngDevMode && assertNodeType(viewRootLNode, LNodeType.Element); ngDevMode && assertNodeType(viewRootLNode, TNodeType.Element);
ngDevMode && assertNotNull(viewRootLNode.data, 'node.data'); ngDevMode && assertNotNull(viewRootLNode.data, 'node.data');
return viewRootLNode as LElementNode; return viewRootLNode as LElementNode;

View File

@ -16,10 +16,10 @@ import {LView, TData, TView} from './view';
/** /**
* LNodeType corresponds to the LNode.type property. It contains information * TNodeType corresponds to the TNode.type property. It contains information
* on how to map a particular set of bits in LNode.flags to the node type. * on how to map a particular set of bits in LNode.flags to the node type.
*/ */
export const enum LNodeType { export const enum TNodeType {
Container = 0b00, Container = 0b00,
Projection = 0b01, Projection = 0b01,
View = 0b10, View = 0b10,
@ -58,9 +58,6 @@ export const enum TNodeFlags {
* instructions. * instructions.
*/ */
export interface LNode { export interface LNode {
/** The type of the node (see LNodeFlags) */
type: LNodeType;
/** /**
* The associated DOM node. Storing this allows us to: * The associated DOM node. Storing this allows us to:
* - append children to their element parents in the DOM (e.g. `parent.native.appendChild(...)`) * - append children to their element parents in the DOM (e.g. `parent.native.appendChild(...)`)
@ -121,7 +118,7 @@ export interface LNode {
tNode: TNode; tNode: TNode;
/** /**
* A pointer to a LContainerNode created by directives requesting ViewContainerRef * A pointer to an LContainerNode created by directives requesting ViewContainerRef
*/ */
// TODO(kara): Remove when removing LNodes // TODO(kara): Remove when removing LNodes
dynamicLContainerNode: LContainerNode|null; dynamicLContainerNode: LContainerNode|null;
@ -206,6 +203,9 @@ export interface LProjectionNode extends LNode {
* see: https://en.wikipedia.org/wiki/Flyweight_pattern for more on the Flyweight pattern * see: https://en.wikipedia.org/wiki/Flyweight_pattern for more on the Flyweight pattern
*/ */
export interface TNode { export interface TNode {
/** The type of the TNode. See TNodeType. */
type: TNodeType;
/** /**
* Index of the TNode in TView.data and corresponding LNode in LView.data. * Index of the TNode in TView.data and corresponding LNode in LView.data.
* *
@ -311,7 +311,7 @@ export interface TNode {
next: TNode|null; next: TNode|null;
/** /**
* A pointer to a LContainerNode created by directives requesting ViewContainerRef * A pointer to a TContainerNode created by directives requesting ViewContainerRef
*/ */
dynamicContainerNode: TNode|null; dynamicContainerNode: TNode|null;
} }

View File

@ -7,23 +7,23 @@
*/ */
import {assertEqual, assertNotNull} from './assert'; import {assertEqual, assertNotNull} from './assert';
import {LNode, LNodeType} from './interfaces/node'; import {LNode, TNodeType} from './interfaces/node';
export function assertNodeType(node: LNode, type: LNodeType) { export function assertNodeType(node: LNode, type: TNodeType) {
assertNotNull(node, 'should be called with a node'); assertNotNull(node, 'should be called with a node');
assertEqual(node.type, type, `should be a ${typeName(type)}`); assertEqual(node.tNode.type, type, `should be a ${typeName(type)}`);
} }
export function assertNodeOfPossibleTypes(node: LNode, ...types: LNodeType[]) { export function assertNodeOfPossibleTypes(node: LNode, ...types: TNodeType[]) {
assertNotNull(node, 'should be called with a node'); assertNotNull(node, 'should be called with a node');
const found = types.some(type => node.type === type); const found = types.some(type => node.tNode.type === type);
assertEqual(found, true, `Should be one of ${types.map(typeName).join(', ')}`); assertEqual(found, true, `Should be one of ${types.map(typeName).join(', ')}`);
} }
function typeName(type: LNodeType): string { function typeName(type: TNodeType): string {
if (type == LNodeType.Projection) return 'Projection'; if (type == TNodeType.Projection) return 'Projection';
if (type == LNodeType.Container) return 'Container'; if (type == TNodeType.Container) return 'Container';
if (type == LNodeType.View) return 'View'; if (type == TNodeType.View) return 'View';
if (type == LNodeType.Element) return 'Element'; if (type == TNodeType.Element) return 'Element';
return '<unknown>'; return '<unknown>';
} }

View File

@ -9,7 +9,7 @@
import {assertNotNull} from './assert'; import {assertNotNull} from './assert';
import {callHooks} from './hooks'; import {callHooks} from './hooks';
import {LContainer, unusedValueExportToPlacateAjd as unused1} from './interfaces/container'; import {LContainer, unusedValueExportToPlacateAjd as unused1} from './interfaces/container';
import {LContainerNode, LElementNode, LNode, LNodeType, LProjectionNode, LTextNode, LViewNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node'; import {LContainerNode, LElementNode, LNode, LProjectionNode, LTextNode, LViewNode, TNodeType, unusedValueExportToPlacateAjd as unused2} from './interfaces/node';
import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection'; import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection';
import {ProceduralRenderer3, RElement, RNode, RText, Renderer3, isProceduralRenderer, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer'; import {ProceduralRenderer3, RElement, RNode, RText, Renderer3, isProceduralRenderer, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer';
import {HookData, LView, LViewOrLContainer, TView, unusedValueExportToPlacateAjd as unused5} from './interfaces/view'; import {HookData, LView, LViewOrLContainer, TView, unusedValueExportToPlacateAjd as unused5} from './interfaces/view';
@ -35,7 +35,7 @@ function findNextRNodeSibling(node: LNode | null, stopNode: LNode | null): RElem
while (currentNode && currentNode !== stopNode) { while (currentNode && currentNode !== stopNode) {
let pNextOrParent = currentNode.pNextOrParent; let pNextOrParent = currentNode.pNextOrParent;
if (pNextOrParent) { if (pNextOrParent) {
while (pNextOrParent.type !== LNodeType.Projection) { while (pNextOrParent.tNode.type !== TNodeType.Projection) {
const nativeNode = findFirstRNode(pNextOrParent); const nativeNode = findFirstRNode(pNextOrParent);
if (nativeNode) { if (nativeNode) {
return nativeNode; return nativeNode;
@ -55,8 +55,8 @@ function findNextRNodeSibling(node: LNode | null, stopNode: LNode | null): RElem
const parentNode = currentNode.parent; const parentNode = currentNode.parent;
currentNode = null; currentNode = null;
if (parentNode) { if (parentNode) {
const parentType = parentNode.type; const parentType = parentNode.tNode.type;
if (parentType === LNodeType.Container || parentType === LNodeType.View) { if (parentType === TNodeType.Container || parentType === TNodeType.View) {
currentNode = parentNode; currentNode = parentNode;
} }
} }
@ -68,7 +68,7 @@ function findNextRNodeSibling(node: LNode | null, stopNode: LNode | null): RElem
/** Retrieves the sibling node for the given node. */ /** Retrieves the sibling node for the given node. */
export function getNextLNode(node: LNode): LNode|null { export function getNextLNode(node: LNode): LNode|null {
// View nodes don't have TNodes, so their next must be retrieved through their LView. // View nodes don't have TNodes, so their next must be retrieved through their LView.
if (node.type === LNodeType.View) { if (node.tNode.type === TNodeType.View) {
const lView = node.data as LView; const lView = node.data as LView;
return lView.next ? (lView.next as LView).node : null; return lView.next ? (lView.next as LView).node : null;
} }
@ -87,7 +87,7 @@ function getNextLNodeWithProjection(node: LNode): LNode|null {
if (pNextOrParent) { if (pNextOrParent) {
// The node is projected // The node is projected
const isLastProjectedNode = pNextOrParent.type === LNodeType.Projection; const isLastProjectedNode = pNextOrParent.tNode.type === TNodeType.Projection;
// returns pNextOrParent if we are not at the end of the list, null otherwise // returns pNextOrParent if we are not at the end of the list, null otherwise
return isLastProjectedNode ? null : pNextOrParent; return isLastProjectedNode ? null : pNextOrParent;
} }
@ -132,16 +132,16 @@ function findFirstRNode(rootNode: LNode): RElement|RText|null {
let node: LNode|null = rootNode; let node: LNode|null = rootNode;
while (node) { while (node) {
let nextNode: LNode|null = null; let nextNode: LNode|null = null;
if (node.type === LNodeType.Element) { if (node.tNode.type === TNodeType.Element) {
// A LElementNode has a matching RNode in LElementNode.native // A LElementNode has a matching RNode in LElementNode.native
return (node as LElementNode).native; return (node as LElementNode).native;
} else if (node.type === LNodeType.Container) { } else if (node.tNode.type === TNodeType.Container) {
const lContainerNode: LContainerNode = (node as LContainerNode); const lContainerNode: LContainerNode = (node as LContainerNode);
const childContainerData: LContainer = lContainerNode.dynamicLContainerNode ? const childContainerData: LContainer = lContainerNode.dynamicLContainerNode ?
lContainerNode.dynamicLContainerNode.data : lContainerNode.dynamicLContainerNode.data :
lContainerNode.data; lContainerNode.data;
nextNode = childContainerData.views.length ? childContainerData.views[0].child : null; nextNode = childContainerData.views.length ? childContainerData.views[0].child : null;
} else if (node.type === LNodeType.Projection) { } else if (node.tNode.type === TNodeType.Projection) {
// For Projection look at the first projected node // For Projection look at the first projected node
nextNode = (node as LProjectionNode).data.head; nextNode = (node as LProjectionNode).data.head;
} else { } else {
@ -179,8 +179,8 @@ export function addRemoveViewFromContainer(
export function addRemoveViewFromContainer( export function addRemoveViewFromContainer(
container: LContainerNode, rootNode: LViewNode, insertMode: boolean, container: LContainerNode, rootNode: LViewNode, insertMode: boolean,
beforeNode?: RNode | null): void { beforeNode?: RNode | null): void {
ngDevMode && assertNodeType(container, LNodeType.Container); ngDevMode && assertNodeType(container, TNodeType.Container);
ngDevMode && assertNodeType(rootNode, LNodeType.View); ngDevMode && assertNodeType(rootNode, TNodeType.View);
const parentNode = container.data.renderParent; const parentNode = container.data.renderParent;
const parent = parentNode ? parentNode.native : null; const parent = parentNode ? parentNode.native : null;
let node: LNode|null = rootNode.child; let node: LNode|null = rootNode.child;
@ -188,7 +188,7 @@ export function addRemoveViewFromContainer(
while (node) { while (node) {
let nextNode: LNode|null = null; let nextNode: LNode|null = null;
const renderer = container.view.renderer; const renderer = container.view.renderer;
if (node.type === LNodeType.Element) { if (node.tNode.type === TNodeType.Element) {
if (insertMode) { if (insertMode) {
isProceduralRenderer(renderer) ? isProceduralRenderer(renderer) ?
renderer.insertBefore(parent, node.native !, beforeNode as RNode | null) : renderer.insertBefore(parent, node.native !, beforeNode as RNode | null) :
@ -198,13 +198,13 @@ export function addRemoveViewFromContainer(
parent.removeChild(node.native !); parent.removeChild(node.native !);
} }
nextNode = getNextLNode(node); nextNode = getNextLNode(node);
} else if (node.type === LNodeType.Container) { } else if (node.tNode.type === TNodeType.Container) {
// if we get to a container, it must be a root node of a view because we are only // if we get to a container, it must be a root node of a view because we are only
// propagating down into child views / containers and not child elements // propagating down into child views / containers and not child elements
const childContainerData: LContainer = (node as LContainerNode).data; const childContainerData: LContainer = (node as LContainerNode).data;
childContainerData.renderParent = parentNode; childContainerData.renderParent = parentNode;
nextNode = childContainerData.views.length ? childContainerData.views[0].child : null; nextNode = childContainerData.views.length ? childContainerData.views[0].child : null;
} else if (node.type === LNodeType.Projection) { } else if (node.tNode.type === TNodeType.Projection) {
nextNode = (node as LProjectionNode).data.head; nextNode = (node as LProjectionNode).data.head;
} else { } else {
nextNode = (node as LViewNode).child; nextNode = (node as LViewNode).child;
@ -357,7 +357,7 @@ export function removeView(container: LContainerNode, removeIndex: number): LVie
*/ */
export function getParentState(state: LViewOrLContainer, rootView: LView): LViewOrLContainer|null { export function getParentState(state: LViewOrLContainer, rootView: LView): LViewOrLContainer|null {
let node; let node;
if ((node = (state as LView) !.node) && node.type === LNodeType.View) { if ((node = (state as LView) !.node) && node.tNode.type === TNodeType.View) {
// if it's an embedded view, the state needs to go up to the container, in case the // if it's an embedded view, the state needs to go up to the container, in case the
// container has a next // container has a next
return node.parent !.data as any; return node.parent !.data as any;
@ -429,7 +429,7 @@ function executePipeOnDestroys(view: LView): void {
* @return boolean Whether the child element should be inserted. * @return boolean Whether the child element should be inserted.
*/ */
export function canInsertNativeNode(parent: LNode, currentView: LView): boolean { export function canInsertNativeNode(parent: LNode, currentView: LView): boolean {
const parentIsElement = parent.type === LNodeType.Element; const parentIsElement = parent.tNode.type === TNodeType.Element;
return parentIsElement && return parentIsElement &&
(parent.view !== currentView || parent.data === null /* Regular Element. */); (parent.view !== currentView || parent.data === null /* Regular Element. */);
@ -486,7 +486,7 @@ export function insertChild(node: LNode, currentView: LView): void {
export function appendProjectedNode( export function appendProjectedNode(
node: LElementNode | LTextNode | LContainerNode, currentParent: LElementNode, node: LElementNode | LTextNode | LContainerNode, currentParent: LElementNode,
currentView: LView): void { currentView: LView): void {
if (node.type !== LNodeType.Container) { if (node.tNode.type !== TNodeType.Container) {
appendChild(currentParent, (node as LElementNode | LTextNode).native, currentView); appendChild(currentParent, (node as LElementNode | LTextNode).native, currentView);
} else { } else {
// The node we are adding is a Container and we are adding it to Element which // The node we are adding is a Container and we are adding it to Element which

View File

@ -14,7 +14,7 @@ import {bloomAdd, bloomFindPossibleInjector, getOrCreateNodeInjector, injectAttr
import {NgOnChangesFeature, PublicFeature, defineDirective, directiveInject, injectChangeDetectorRef, injectElementRef, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index'; import {NgOnChangesFeature, PublicFeature, defineDirective, directiveInject, injectChangeDetectorRef, injectElementRef, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index';
import {bind, container, containerRefreshEnd, containerRefreshStart, createLNode, createLView, createTView, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, enterView, interpolation2, leaveView, load, projection, projectionDef, text, textBinding} from '../../src/render3/instructions'; import {bind, container, containerRefreshEnd, containerRefreshStart, createLNode, createLView, createTView, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, enterView, interpolation2, leaveView, load, projection, projectionDef, text, textBinding} from '../../src/render3/instructions';
import {LInjector} from '../../src/render3/interfaces/injector'; import {LInjector} from '../../src/render3/interfaces/injector';
import {LNodeType} from '../../src/render3/interfaces/node'; import {TNodeType} from '../../src/render3/interfaces/node';
import {LViewFlags} from '../../src/render3/interfaces/view'; import {LViewFlags} from '../../src/render3/interfaces/view';
import {ViewRef} from '../../src/render3/view_ref'; import {ViewRef} from '../../src/render3/view_ref';
@ -1367,7 +1367,7 @@ describe('di', () => {
createLView(-1, null !, createTView(null, null), null, null, LViewFlags.CheckAlways); createLView(-1, null !, createTView(null, null), null, null, LViewFlags.CheckAlways);
const oldView = enterView(contentView, null !); const oldView = enterView(contentView, null !);
try { try {
const parent = createLNode(0, LNodeType.Element, null, null, null, null); const parent = createLNode(0, TNodeType.Element, null, null, null, null);
// Simulate the situation where the previous parent is not initialized. // Simulate the situation where the previous parent is not initialized.
// This happens on first bootstrap because we don't init existing values // This happens on first bootstrap because we don't init existing values

View File

@ -6,12 +6,14 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {TNode} from '../../src/render3/interfaces/node'; import {TNode, TNodeType} from '../../src/render3/interfaces/node';
import {CssSelector, CssSelectorList, NG_PROJECT_AS_ATTR_NAME, SelectorFlags,} from '../../src/render3/interfaces/projection'; import {CssSelector, CssSelectorList, NG_PROJECT_AS_ATTR_NAME, SelectorFlags,} from '../../src/render3/interfaces/projection';
import {getProjectAsAttrValue, isNodeMatchingSelectorList, isNodeMatchingSelector} from '../../src/render3/node_selector_matcher'; import {getProjectAsAttrValue, isNodeMatchingSelectorList, isNodeMatchingSelector} from '../../src/render3/node_selector_matcher';
function testLStaticData(tagName: string, attrs: string[] | null): TNode { function testLStaticData(tagName: string, attrs: string[] | null): TNode {
return { return {
type: TNodeType.Element,
index: 0, index: 0,
flags: 0, tagName, attrs, flags: 0, tagName, attrs,
localNames: null, localNames: null,