From fc8694ed11747c20fbb606bd8cdb35c43a435e25 Mon Sep 17 00:00:00 2001 From: Tobias Bosch Date: Mon, 23 Jan 2017 10:23:44 -0800 Subject: [PATCH] refactor(core): view engine, refactor runtime data Structure in a better way, in preparation for queries. --- modules/@angular/core/src/view/element.ts | 10 ++++---- modules/@angular/core/src/view/provider.ts | 18 +++++++------- .../@angular/core/src/view/pure_expression.ts | 14 ++++------- modules/@angular/core/src/view/services.ts | 12 ++++++---- modules/@angular/core/src/view/text.ts | 12 ++++++---- modules/@angular/core/src/view/types.ts | 15 +++++++++--- modules/@angular/core/src/view/view.ts | 24 +++++++++---------- modules/@angular/core/src/view/view_attach.ts | 21 ++++++++-------- .../core/test/view/component_view_spec.ts | 4 ++-- .../core/test/view/pure_expression_spec.ts | 6 ++--- modules/benchmarks/src/tree/ng2_next/tree.ts | 4 ++-- 11 files changed, 74 insertions(+), 66 deletions(-) diff --git a/modules/@angular/core/src/view/element.ts b/modules/@angular/core/src/view/element.ts index 303758f120..0d59bb4a40 100644 --- a/modules/@angular/core/src/view/element.ts +++ b/modules/@angular/core/src/view/element.ts @@ -97,7 +97,7 @@ export function elementDef( } export function createElement(view: ViewData, renderHost: any, def: NodeDef): NodeData { - const parentNode = def.parent != null ? view.nodes[def.parent].renderNode : renderHost; + const parentNode = def.parent != null ? view.nodes[def.parent].elementOrText.node : renderHost; const elDef = def.element; let el: any; if (view.renderer) { @@ -150,10 +150,10 @@ export function createElement(view: ViewData, renderHost: any, def: NodeDef): No } } return { - renderNode: el, + elementOrText: + {node: el, embeddedViews: (def.flags & NodeFlags.HasEmbeddedViews) ? [] : undefined}, provider: undefined, - embeddedViews: (def.flags & NodeFlags.HasEmbeddedViews) ? [] : undefined, - componentView: undefined + pureExpression: undefined, }; } @@ -213,7 +213,7 @@ function checkAndUpdateElementValue(view: ViewData, def: NodeDef, bindingIdx: nu const binding = def.bindings[bindingIdx]; const name = binding.name; - const renderNode = view.nodes[def.index].renderNode; + const renderNode = view.nodes[def.index].elementOrText.node; switch (binding.type) { case BindingType.ElementAttribute: setElementAttribute(view, binding, renderNode, name, value); diff --git a/modules/@angular/core/src/view/provider.ts b/modules/@angular/core/src/view/provider.ts index acfa222943..b0f1a4c6e0 100644 --- a/modules/@angular/core/src/view/provider.ts +++ b/modules/@angular/core/src/view/provider.ts @@ -102,16 +102,16 @@ export function createProvider(view: ViewData, def: NodeDef, componentView: View } } return { - renderNode: undefined, - provider, - embeddedViews: undefined, componentView, + elementOrText: undefined, + provider: {instance: provider, componentView: componentView}, + pureExpression: undefined, }; } export function checkAndUpdateProviderInline( view: ViewData, def: NodeDef, v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any, v7: any, v8: any, v9: any) { - const provider = view.nodes[def.index].provider; + const provider = view.nodes[def.index].provider.instance; let changes: SimpleChanges; // Note: fallthrough is intended! switch (def.bindings.length) { @@ -148,7 +148,7 @@ export function checkAndUpdateProviderInline( } export function checkAndUpdateProviderDynamic(view: ViewData, def: NodeDef, values: any[]) { - const provider = view.nodes[def.index].provider; + const provider = view.nodes[def.index].provider.instance; let changes: SimpleChanges; for (let i = 0; i < values.length; i++) { changes = checkAndUpdateProp(view, provider, def, i, values[i], changes); @@ -217,7 +217,7 @@ export function resolveDep( return Injector.NULL.get(depDef.token, notFoundValue); } case ElementRefTokenKey: - return new ElementRef(view.nodes[elIndex].renderNode); + return new ElementRef(view.nodes[elIndex].elementOrText.node); case ViewContainerRefTokenKey: return view.services.createViewContainerRef(view.nodes[elIndex]); case TemplateRefTokenKey: @@ -225,7 +225,7 @@ export function resolveDep( default: const providerIndex = elDef.providerIndices[tokenKey]; if (providerIndex != null) { - return view.nodes[providerIndex].provider; + return view.nodes[providerIndex].provider.instance; } } elIndex = view.parentIndex; @@ -255,7 +255,7 @@ function checkAndUpdateProp( if (view.def.flags & ViewFlags.LogBindingUpdate) { setBindingDebugInfo( - view.renderer, view.nodes[def.parent].renderNode, binding.nonMinifiedName, value); + view.renderer, view.nodes[def.parent].elementOrText.node, binding.nonMinifiedName, value); } if (change) { changes = changes || {}; @@ -276,7 +276,7 @@ export function callLifecycleHooksChildrenFirst(view: ViewData, lifecycles: Node const nodeIndex = nodeDef.index; if (nodeDef.flags & lifecycles) { // a leaf - callProviderLifecycles(view.nodes[nodeIndex].provider, nodeDef.flags & lifecycles); + callProviderLifecycles(view.nodes[nodeIndex].provider.instance, nodeDef.flags & lifecycles); } else if ((nodeDef.childFlags & lifecycles) === 0) { // a parent with leafs // no child matches one of the lifecycles, diff --git a/modules/@angular/core/src/view/pure_expression.ts b/modules/@angular/core/src/view/pure_expression.ts index baa40449ac..2e5363bba8 100644 --- a/modules/@angular/core/src/view/pure_expression.ts +++ b/modules/@angular/core/src/view/pure_expression.ts @@ -7,7 +7,7 @@ */ import {resolveDep, tokenKey} from './provider'; -import {BindingDef, BindingType, DepDef, DepFlags, NodeData, NodeDef, NodeType, PureExpressionData, PureExpressionType, ViewData} from './types'; +import {BindingDef, BindingType, DepDef, DepFlags, NodeData, NodeDef, NodeType, ProviderData, PureExpressionType, ViewData} from './types'; import {checkAndUpdateBinding} from './util'; export function purePipeDef(pipeToken: any, argCount: number): NodeDef { @@ -62,13 +62,7 @@ export function createPureExpression(view: ViewData, def: NodeDef): NodeData { const pipe = def.pureExpression.pipeDep ? resolveDep(view, def.parent, def.pureExpression.pipeDep) : undefined; - const data: PureExpressionData = {value: undefined, pipe: pipe}; - return { - renderNode: undefined, - provider: data, - embeddedViews: undefined, - componentView: undefined - }; + return {elementOrText: undefined, provider: undefined, pureExpression: {value: undefined, pipe}}; } export function checkAndUpdatePureExpressionInline( @@ -101,7 +95,7 @@ export function checkAndUpdatePureExpressionInline( } if (changed) { - const data: PureExpressionData = view.nodes[def.index].provider; + const data = view.nodes[def.index].pureExpression; let value: any; switch (def.pureExpression.type) { case PureExpressionType.Array: @@ -206,7 +200,7 @@ export function checkAndUpdatePureExpressionDynamic(view: ViewData, def: NodeDef } } if (changed) { - const data: PureExpressionData = view.nodes[def.index].provider; + const data = view.nodes[def.index].pureExpression; let value: any; switch (def.pureExpression.type) { case PureExpressionType.Array: diff --git a/modules/@angular/core/src/view/services.ts b/modules/@angular/core/src/view/services.ts index 93664d98d1..17cb3d2522 100644 --- a/modules/@angular/core/src/view/services.ts +++ b/modules/@angular/core/src/view/services.ts @@ -48,16 +48,18 @@ class ViewContainerRef_ implements ViewContainerRef { get parentInjector(): Injector { return unimplemented(); } clear(): void { - const len = this._data.embeddedViews.length; + const len = this._data.elementOrText.embeddedViews.length; for (let i = len - 1; i >= 0; i--) { const view = detachEmbeddedView(this._data, i); destroyView(view); } } - get(index: number): ViewRef { return new ViewRef_(this._data.embeddedViews[index]); } + get(index: number): ViewRef { + return new ViewRef_(this._data.elementOrText.embeddedViews[index]); + } - get length(): number { return this._data.embeddedViews.length; }; + get length(): number { return this._data.elementOrText.embeddedViews.length; }; createEmbeddedView(templateRef: TemplateRef, context?: C, index?: number): EmbeddedViewRef { @@ -81,7 +83,7 @@ class ViewContainerRef_ implements ViewContainerRef { move(viewRef: ViewRef, currentIndex: number): ViewRef { return unimplemented(); } indexOf(viewRef: ViewRef): number { - return this._data.embeddedViews.indexOf((viewRef)._view); + return this._data.elementOrText.embeddedViews.indexOf((viewRef)._view); } remove(index?: number): void { @@ -126,6 +128,6 @@ class TemplateRef_ implements TemplateRef { } get elementRef(): ElementRef { - return new ElementRef(this._parentView.nodes[this._def.index].renderNode); + return new ElementRef(this._parentView.nodes[this._def.index].elementOrText.node); } } diff --git a/modules/@angular/core/src/view/text.ts b/modules/@angular/core/src/view/text.ts index 7b093fe2ab..7ac3f25aba 100644 --- a/modules/@angular/core/src/view/text.ts +++ b/modules/@angular/core/src/view/text.ts @@ -44,7 +44,7 @@ export function textDef(constants: string[]): NodeDef { } export function createText(view: ViewData, renderHost: any, def: NodeDef): NodeData { - const parentNode = def.parent != null ? view.nodes[def.parent].renderNode : renderHost; + const parentNode = def.parent != null ? view.nodes[def.parent].elementOrText.node : renderHost; let renderNode: any; if (view.renderer) { renderNode = view.renderer.createText(parentNode, def.text.prefix); @@ -54,7 +54,11 @@ export function createText(view: ViewData, renderHost: any, def: NodeDef): NodeD parentNode.appendChild(renderNode); } } - return {renderNode, provider: undefined, embeddedViews: undefined, componentView: undefined}; + return { + elementOrText: {node: renderNode, embeddedViews: undefined}, + provider: undefined, + pureExpression: undefined + }; } export function checkAndUpdateTextInline( @@ -112,7 +116,7 @@ export function checkAndUpdateTextInline( value = _addInterpolationPart(v0, bindings[0]) + value; } value = def.text.prefix + value; - const renderNode = view.nodes[def.index].renderNode; + const renderNode = view.nodes[def.index].elementOrText.node; if (view.renderer) { view.renderer.setText(renderNode, value); } else { @@ -137,7 +141,7 @@ export function checkAndUpdateTextDynamic(view: ViewData, def: NodeDef, values: value = value + _addInterpolationPart(values[i], bindings[i]); } value = def.text.prefix + value; - const renderNode = view.nodes[def.index].renderNode; + const renderNode = view.nodes[def.index].elementOrText.node; if (view.renderer) { view.renderer.setText(renderNode, value); } else { diff --git a/modules/@angular/core/src/view/types.ts b/modules/@angular/core/src/view/types.ts index 3b1682536b..8f32c27c36 100644 --- a/modules/@angular/core/src/view/types.ts +++ b/modules/@angular/core/src/view/types.ts @@ -206,12 +206,21 @@ export type DisposableFn = () => void; * Attention: Adding fields to this is performance sensitive! */ export interface NodeData { - renderNode: any; - provider: PureExpressionData|any; - componentView: ViewData; + elementOrText: ElementOrTextData; + provider: ProviderData; + pureExpression: PureExpressionData; +} + +export interface ElementOrTextData { + node: any; embeddedViews: ViewData[]; } +export interface ProviderData { + instance: any; + componentView: ViewData; +} + export interface PureExpressionData { value: any; pipe: PipeTransform; diff --git a/modules/@angular/core/src/view/view.ts b/modules/@angular/core/src/view/view.ts index a3c5bfae67..6999eb9d71 100644 --- a/modules/@angular/core/src/view/view.ts +++ b/modules/@angular/core/src/view/view.ts @@ -13,7 +13,7 @@ import {checkAndUpdateElementDynamic, checkAndUpdateElementInline, createElement import {callLifecycleHooksChildrenFirst, checkAndUpdateProviderDynamic, checkAndUpdateProviderInline, createProvider} from './provider'; import {checkAndUpdatePureExpressionDynamic, checkAndUpdatePureExpressionInline, createPureExpression} from './pure_expression'; import {checkAndUpdateTextDynamic, checkAndUpdateTextInline, createText} from './text'; -import {ElementDef, NodeData, NodeDef, NodeFlags, NodeType, NodeUpdater, ProviderDef, PureExpressionData, Services, TextDef, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn} from './types'; +import {ElementDef, NodeData, NodeDef, NodeFlags, NodeType, NodeUpdater, ProviderData, ProviderDef, Services, TextDef, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn} from './types'; import {checkBindingNoChanges} from './util'; const NOOP = (): any => undefined; @@ -274,8 +274,7 @@ const CheckNoChanges: NodeUpdater = { checkBindingNoChanges(view, nodeDef, 0, v0); } if (nodeDef.type === NodeType.PureExpression) { - const data: PureExpressionData = view.nodes[index].provider; - return data.value; + return view.nodes[index].pureExpression.value; } return undefined; }, @@ -285,8 +284,7 @@ const CheckNoChanges: NodeUpdater = { checkBindingNoChanges(view, nodeDef, i, values[i]); } if (nodeDef.type === NodeType.PureExpression) { - const data: PureExpressionData = view.nodes[index].provider; - return data.value; + return view.nodes[index].pureExpression.value; } return undefined; } @@ -321,8 +319,7 @@ const CheckAndUpdate: NodeUpdater = { return undefined; case NodeType.PureExpression: checkAndUpdatePureExpressionInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9); - const data: PureExpressionData = view.nodes[index].provider; - return data.value; + return view.nodes[index].pureExpression.value; } }, checkDynamic: (view: ViewData, index: number, values: any[]): void => { @@ -339,8 +336,7 @@ const CheckAndUpdate: NodeUpdater = { return undefined; case NodeType.PureExpression: checkAndUpdatePureExpressionDynamic(view, nodeDef, values); - const data: PureExpressionData = view.nodes[index].provider; - return data.value; + return view.nodes[index].pureExpression.value; } } }; @@ -374,13 +370,15 @@ function execComponentViewsAction(view: ViewData, action: ViewAction) { // a leaf const nodeData = view.nodes[i]; if (action === ViewAction.InitComponent) { - let renderHost = view.nodes[nodeDef.parent].renderNode; + let renderHost = view.nodes[nodeDef.parent].elementOrText.node; if (view.renderer) { renderHost = view.renderer.createViewRoot(renderHost); } - initView(nodeData.componentView, renderHost, nodeData.provider, nodeData.provider); + initView( + nodeData.provider.componentView, renderHost, nodeData.provider.instance, + nodeData.provider.instance); } else { - callViewAction(nodeData.componentView, action); + callViewAction(nodeData.provider.componentView, action); } } else if ((nodeDef.childFlags & NodeFlags.HasComponent) === 0) { // a parent with leafs @@ -401,7 +399,7 @@ function execEmbeddedViewsAction(view: ViewData, action: ViewAction) { if (nodeDef.flags & NodeFlags.HasEmbeddedViews) { // a leaf const nodeData = view.nodes[i]; - const embeddedViews = nodeData.embeddedViews; + const embeddedViews = nodeData.elementOrText.embeddedViews; if (embeddedViews) { for (let k = 0; k < embeddedViews.length; k++) { callViewAction(embeddedViews[k], action); diff --git a/modules/@angular/core/src/view/view_attach.ts b/modules/@angular/core/src/view/view_attach.ts index 66e1833178..143330d173 100644 --- a/modules/@angular/core/src/view/view_attach.ts +++ b/modules/@angular/core/src/view/view_attach.ts @@ -9,7 +9,7 @@ import {NodeData, NodeFlags, ViewData} from './types'; export function attachEmbeddedView(node: NodeData, viewIndex: number, view: ViewData) { - let embeddedViews = node.embeddedViews; + let embeddedViews = node.elementOrText.embeddedViews; if (viewIndex == null) { viewIndex = embeddedViews.length; } @@ -21,7 +21,7 @@ export function attachEmbeddedView(node: NodeData, viewIndex: number, view: View } const prevView = viewIndex > 0 ? embeddedViews[viewIndex - 1] : null; const prevNode = prevView ? prevView.nodes[prevView.def.lastRootNode] : node; - const prevRenderNode = prevNode.renderNode; + const prevRenderNode = prevNode.elementOrText.node; if (view.renderer) { view.renderer.attachViewAfter(prevRenderNode, rootRenderNodes(view)); } else { @@ -35,7 +35,8 @@ export function attachEmbeddedView(node: NodeData, viewIndex: number, view: View } export function detachEmbeddedView(node: NodeData, viewIndex: number): ViewData { - const embeddedViews = node.embeddedViews; + const renderData = node.elementOrText; + const embeddedViews = renderData.embeddedViews; if (viewIndex == null) { viewIndex = embeddedViews.length; } @@ -49,7 +50,7 @@ export function detachEmbeddedView(node: NodeData, viewIndex: number): ViewData if (view.renderer) { view.renderer.detachView(rootRenderNodes(view)); } else { - const parentNode = node.renderNode.parentNode; + const parentNode = renderData.node.parentNode; if (parentNode) { directDomAttachDetachSiblingRenderNodes( view, 0, DirectDomAction.RemoveChild, parentNode, null); @@ -67,8 +68,8 @@ export function rootRenderNodes(view: ViewData): any[] { function collectSiblingRenderNodes(view: ViewData, startIndex: number, target: any[]) { for (let i = startIndex; i < view.nodes.length; i++) { const nodeDef = view.def.nodes[i]; - const nodeData = view.nodes[i]; - target.push(nodeData.renderNode); + const nodeData = view.nodes[i].elementOrText; + target.push(nodeData.node); if (nodeDef.flags & NodeFlags.HasEmbeddedViews) { const embeddedViews = nodeData.embeddedViews; if (embeddedViews) { @@ -93,16 +94,16 @@ function directDomAttachDetachSiblingRenderNodes( nextSibling: any) { for (let i = startIndex; i < view.nodes.length; i++) { const nodeDef = view.def.nodes[i]; - const nodeData = view.nodes[i]; + const nodeData = view.nodes[i].elementOrText; switch (action) { case DirectDomAction.AppendChild: - parentNode.appendChild(nodeData.renderNode); + parentNode.appendChild(nodeData.node); break; case DirectDomAction.InsertBefore: - parentNode.insertBefore(nodeData.renderNode, nextSibling); + parentNode.insertBefore(nodeData.node, nextSibling); break; case DirectDomAction.RemoveChild: - parentNode.removeChild(nodeData.renderNode); + parentNode.removeChild(nodeData.node); break; } if (nodeDef.flags & NodeFlags.HasEmbeddedViews) { diff --git a/modules/@angular/core/test/view/component_view_spec.ts b/modules/@angular/core/test/view/component_view_spec.ts index 77cb396885..16e76c412c 100644 --- a/modules/@angular/core/test/view/component_view_spec.ts +++ b/modules/@angular/core/test/view/component_view_spec.ts @@ -58,7 +58,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) { ])), ])); - const compView = view.nodes[1].componentView; + const compView = view.nodes[1].provider.componentView; expect(compView.context).toBe(instance); expect(compView.component).toBe(instance); @@ -85,7 +85,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) { ], update )), ], jasmine.createSpy('parentUpdater'))); - const compView = view.nodes[1].componentView; + const compView = view.nodes[1].provider.componentView; checkAndUpdateView(view); diff --git a/modules/@angular/core/test/view/pure_expression_spec.ts b/modules/@angular/core/test/view/pure_expression_spec.ts index 17ef219e72..887cdaef5c 100644 --- a/modules/@angular/core/test/view/pure_expression_spec.ts +++ b/modules/@angular/core/test/view/pure_expression_spec.ts @@ -53,7 +53,7 @@ export function main() { updater, inlineDynamic, view, 2, [callUpdater(updater, inlineDynamic, view, 1, values)]); })); - const service = view.nodes[2].provider; + const service = view.nodes[2].provider.instance; values = [1, 2]; checkAndUpdateView(view); @@ -87,7 +87,7 @@ export function main() { updater, inlineDynamic, view, 2, [callUpdater(updater, inlineDynamic, view, 1, values)]); })); - const service = view.nodes[2].provider; + const service = view.nodes[2].provider.instance; values = [1, 2]; checkAndUpdateView(view); @@ -126,7 +126,7 @@ export function main() { updater, inlineDynamic, view, 3, [callUpdater(updater, inlineDynamic, view, 2, values)]); })); - const service = view.nodes[3].provider; + const service = view.nodes[3].provider.instance; values = [1, 2]; checkAndUpdateView(view); diff --git a/modules/benchmarks/src/tree/ng2_next/tree.ts b/modules/benchmarks/src/tree/ng2_next/tree.ts index 327b92bda9..3c1e5504c7 100644 --- a/modules/benchmarks/src/tree/ng2_next/tree.ts +++ b/modules/benchmarks/src/tree/ng2_next/tree.ts @@ -84,8 +84,8 @@ export class AppModule { } bootstrap() { this.rootView = createRootView(new DefaultServices(null, this.sanitizer), TreeComponent_Host); - this.rootComp = this.rootView.nodes[1].provider; - this.rootEl = this.rootView.nodes[0].renderNode; + this.rootComp = this.rootView.nodes[1].provider.instance; + this.rootEl = this.rootView.nodes[0].elementOrText.node; } tick() { checkAndUpdateView(this.rootView); } }