feat(ivy): generate ɵɵproperty instructions (#29946)

PR Close #29946
This commit is contained in:
Ben Lesh
2019-04-16 17:17:12 -07:00
parent a181e8e7d8
commit 10217bb3bc
22 changed files with 323 additions and 187 deletions

View File

@ -89,6 +89,7 @@ export {
ɵɵloadContentQuery,
ɵɵelementEnd,
ɵɵelementProperty,
ɵɵproperty,
ɵɵcomponentHostSyntheticProperty,
ɵɵcomponentHostSyntheticListener,
ɵɵprojectionDef,

View File

@ -5,6 +5,7 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {assertNotEqual} from '../../util/assert';
import {bindingUpdated} from '../bindings';
import {SanitizerFn} from '../interfaces/sanitization';
import {BINDING_INDEX} from '../interfaces/view';
@ -38,6 +39,7 @@ export function ɵɵproperty<T>(
propName: string, value: T, sanitizer?: SanitizerFn | null,
nativeOnly?: boolean): TsickleIssue1009 {
const index = getSelectedIndex();
ngDevMode && assertNotEqual(index, -1, 'selected index cannot be -1');
const bindReconciledValue = ɵɵbind(value);
elementPropertyInternal(index, propName, bindReconciledValue, sanitizer, nativeOnly);
return ɵɵproperty;

View File

@ -36,7 +36,14 @@ export function ɵɵselect(index: number): void {
ngDevMode &&
assertLessThan(
index, getLView().length - HEADER_OFFSET, 'Should be within range for the view data');
setSelectedIndex(index);
const lView = getLView();
// Flush the initial hooks for elements in the view that have been added up to this point.
executePreOrderHooks(lView, lView[TVIEW], getCheckNoChangesMode(), index);
// We must set the selected index *after* running the hooks, because hooks may have side-effects
// that cause other template functions to run, thus updating the selected index, which is global
// state. If we run `setSelectedIndex` *before* we run the hooks, in some cases the selected index
// will be altered by the time we leave the `ɵɵselect` instruction.
setSelectedIndex(index);
}

View File

@ -30,7 +30,7 @@ import {StylingContext} from '../interfaces/styling';
import {BINDING_INDEX, CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_VIEW, ExpandoInstructions, FLAGS, HEADER_OFFSET, HOST, INJECTOR, InitPhaseState, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, TData, TVIEW, TView, T_HOST} from '../interfaces/view';
import {assertNodeOfPossibleTypes, assertNodeType} from '../node_assert';
import {isNodeMatchingSelectorList} from '../node_selector_matcher';
import {enterView, getBindingsEnabled, getCheckNoChangesMode, getIsParent, getLView, getNamespace, getPreviousOrParentTNode, incrementActiveDirectiveId, isCreationMode, leaveView, resetComponentState, setActiveHostElement, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setCurrentQueryIndex, setIsParent, setPreviousOrParentTNode, setSelectedIndex, ɵɵnamespaceHTML} from '../state';
import {enterView, getBindingsEnabled, getCheckNoChangesMode, getIsParent, getLView, getNamespace, getPreviousOrParentTNode, getSelectedIndex, incrementActiveDirectiveId, isCreationMode, leaveView, resetComponentState, setActiveHostElement, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setCurrentQueryIndex, setIsParent, setPreviousOrParentTNode, setSelectedIndex, ɵɵnamespaceHTML} from '../state';
import {initializeStaticContext as initializeStaticStylingContext} from '../styling/class_and_style_bindings';
import {ANIMATION_PROP_PREFIX, isAnimationProp} from '../styling/util';
import {NO_CHANGE} from '../tokens';
@ -101,51 +101,55 @@ export function refreshDescendantViews(lView: LView) {
/** Sets the host bindings for the current view. */
export function setHostBindings(tView: TView, viewData: LView): void {
if (tView.expandoInstructions) {
let bindingRootIndex = viewData[BINDING_INDEX] = tView.expandoStartIndex;
setBindingRoot(bindingRootIndex);
let currentDirectiveIndex = -1;
let currentElementIndex = -1;
for (let i = 0; i < tView.expandoInstructions.length; i++) {
const instruction = tView.expandoInstructions[i];
if (typeof instruction === 'number') {
if (instruction <= 0) {
// Negative numbers mean that we are starting new EXPANDO block and need to update
// the current element and directive index.
currentElementIndex = -instruction;
setActiveHostElement(currentElementIndex);
const selectedIndex = getSelectedIndex();
try {
if (tView.expandoInstructions) {
let bindingRootIndex = viewData[BINDING_INDEX] = tView.expandoStartIndex;
setBindingRoot(bindingRootIndex);
let currentDirectiveIndex = -1;
let currentElementIndex = -1;
for (let i = 0; i < tView.expandoInstructions.length; i++) {
const instruction = tView.expandoInstructions[i];
if (typeof instruction === 'number') {
if (instruction <= 0) {
// Negative numbers mean that we are starting new EXPANDO block and need to update
// the current element and directive index.
currentElementIndex = -instruction;
setActiveHostElement(currentElementIndex);
// Injector block and providers are taken into account.
const providerCount = (tView.expandoInstructions[++i] as number);
bindingRootIndex += INJECTOR_BLOOM_PARENT_SIZE + providerCount;
// Injector block and providers are taken into account.
const providerCount = (tView.expandoInstructions[++i] as number);
bindingRootIndex += INJECTOR_BLOOM_PARENT_SIZE + providerCount;
currentDirectiveIndex = bindingRootIndex;
currentDirectiveIndex = bindingRootIndex;
} else {
// This is either the injector size (so the binding root can skip over directives
// and get to the first set of host bindings on this node) or the host var count
// (to get to the next set of host bindings on this node).
bindingRootIndex += instruction;
}
setBindingRoot(bindingRootIndex);
} else {
// This is either the injector size (so the binding root can skip over directives
// and get to the first set of host bindings on this node) or the host var count
// (to get to the next set of host bindings on this node).
bindingRootIndex += instruction;
}
setBindingRoot(bindingRootIndex);
} else {
// If it's not a number, it's a host binding function that needs to be executed.
if (instruction !== null) {
viewData[BINDING_INDEX] = bindingRootIndex;
const hostCtx = unwrapRNode(viewData[currentDirectiveIndex]);
instruction(RenderFlags.Update, hostCtx, currentElementIndex);
// If it's not a number, it's a host binding function that needs to be executed.
if (instruction !== null) {
viewData[BINDING_INDEX] = bindingRootIndex;
const hostCtx = unwrapRNode(viewData[currentDirectiveIndex]);
instruction(RenderFlags.Update, hostCtx, currentElementIndex);
// Each directive gets a uniqueId value that is the same for both
// create and update calls when the hostBindings function is called. The
// directive uniqueId is not set anywhere--it is just incremented between
// each hostBindings call and is useful for helping instruction code
// uniquely determine which directive is currently active when executed.
incrementActiveDirectiveId();
// Each directive gets a uniqueId value that is the same for both
// create and update calls when the hostBindings function is called. The
// directive uniqueId is not set anywhere--it is just incremented between
// each hostBindings call and is useful for helping instruction code
// uniquely determine which directive is currently active when executed.
incrementActiveDirectiveId();
}
currentDirectiveIndex++;
}
currentDirectiveIndex++;
}
}
} finally {
setActiveHostElement(selectedIndex);
}
setActiveHostElement(null);
}
/** Refreshes content queries for all directives in the given view. */
@ -431,12 +435,8 @@ export function renderEmbeddedTemplate<T>(viewToRender: LView, tView: TView, con
oldView = enterView(viewToRender, viewToRender[T_HOST]);
resetPreOrderHookFlags(viewToRender);
ɵɵnamespaceHTML();
executeTemplate(tView.template !, getRenderFlags(viewToRender), context);
// Reset the selected index so we can assert that `select` was called later
setSelectedIndex(-1);
tView.template !(getRenderFlags(viewToRender), context);
// This must be set to false immediately after the first creation run because in an
// ngFor loop, all the views will be created together before update mode runs and turns
// off firstTemplatePass. If we don't set it here, instances will perform directive
@ -465,14 +465,7 @@ function renderComponentOrTemplate<T>(
if (creationModeIsActive) {
// creation mode pass
if (templateFn) {
ɵɵnamespaceHTML();
// Reset the selected index so we can assert that `select` was called later
setSelectedIndex(-1);
templateFn(RenderFlags.Create, context);
}
templateFn && executeTemplate(templateFn, RenderFlags.Create, context);
refreshDescendantViews(hostView);
hostView[FLAGS] &= ~LViewFlags.CreationMode;
@ -480,7 +473,7 @@ function renderComponentOrTemplate<T>(
// update mode pass
resetPreOrderHookFlags(hostView);
templateFn && templateFn(RenderFlags.Update, context);
templateFn && executeTemplate(templateFn, RenderFlags.Update, context);
refreshDescendantViews(hostView);
} finally {
if (normalExecutionPath && !creationModeIsActive && rendererFactory.end) {
@ -490,6 +483,17 @@ function renderComponentOrTemplate<T>(
}
}
function executeTemplate<T>(templateFn: ComponentTemplate<T>, rf: RenderFlags, context: T) {
ɵɵnamespaceHTML();
const prevSelectedIndex = getSelectedIndex();
try {
setActiveHostElement(null);
templateFn(rf, context);
} finally {
setSelectedIndex(prevSelectedIndex);
}
}
/**
* This function returns the default configuration of rendering flags depending on when the
* template is in creation mode or update mode. Update block and create block are
@ -1073,26 +1077,29 @@ function invokeDirectivesHostBindings(tView: TView, viewData: LView, tNode: TNod
const expando = tView.expandoInstructions !;
const firstTemplatePass = tView.firstTemplatePass;
const elementIndex = tNode.index - HEADER_OFFSET;
setActiveHostElement(elementIndex);
const selectedIndex = getSelectedIndex();
try {
setActiveHostElement(elementIndex);
for (let i = start; i < end; i++) {
const def = tView.data[i] as DirectiveDef<any>;
const directive = viewData[i];
if (def.hostBindings) {
invokeHostBindingsInCreationMode(def, expando, directive, tNode, firstTemplatePass);
for (let i = start; i < end; i++) {
const def = tView.data[i] as DirectiveDef<any>;
const directive = viewData[i];
if (def.hostBindings) {
invokeHostBindingsInCreationMode(def, expando, directive, tNode, firstTemplatePass);
// Each directive gets a uniqueId value that is the same for both
// create and update calls when the hostBindings function is called. The
// directive uniqueId is not set anywhere--it is just incremented between
// each hostBindings call and is useful for helping instruction code
// uniquely determine which directive is currently active when executed.
incrementActiveDirectiveId();
} else if (firstTemplatePass) {
expando.push(null);
// Each directive gets a uniqueId value that is the same for both
// create and update calls when the hostBindings function is called. The
// directive uniqueId is not set anywhere--it is just incremented between
// each hostBindings call and is useful for helping instruction code
// uniquely determine which directive is currently active when executed.
incrementActiveDirectiveId();
} else if (firstTemplatePass) {
expando.push(null);
}
}
} finally {
setActiveHostElement(selectedIndex);
}
setActiveHostElement(null);
}
export function invokeHostBindingsInCreationMode(
@ -1685,14 +1692,8 @@ export function checkView<T>(hostView: LView, component: T) {
try {
resetPreOrderHookFlags(hostView);
ɵɵnamespaceHTML();
creationMode && executeViewQueryFn(RenderFlags.Create, hostTView, component);
// Reset the selected index so we can assert that `select` was called later
setSelectedIndex(-1);
templateFn(getRenderFlags(hostView), component);
executeTemplate(templateFn, getRenderFlags(hostView), component);
refreshDescendantViews(hostView);
// Only check view queries again in creation mode if there are static view queries
if (!creationMode || hostTView.staticViewQueries) {

View File

@ -85,6 +85,7 @@ export const angularCoreEnv: {[name: string]: Function} = {
'ɵɵpipeBind4': r3.ɵɵpipeBind4,
'ɵɵpipeBindV': r3.ɵɵpipeBindV,
'ɵɵprojectionDef': r3.ɵɵprojectionDef,
'ɵɵproperty': r3.ɵɵproperty,
'ɵɵpipe': r3.ɵɵpipe,
'ɵɵqueryRefresh': r3.ɵɵqueryRefresh,
'ɵɵviewQuery': r3.ɵɵviewQuery,

View File

@ -470,9 +470,6 @@ let _selectedIndex = -1;
* current `LView` to act on.
*/
export function getSelectedIndex() {
ngDevMode &&
assertGreaterThan(
_selectedIndex, -1, 'select() should be called prior to retrieving the selected index');
return _selectedIndex;
}