@@ -33,16 +33,16 @@ import {Renderer, RendererFactory} from '../interfaces/renderer';
3333import { RComment , RElement , RNode , RText } from '../interfaces/renderer_dom' ;
3434import { SanitizerFn } from '../interfaces/sanitization' ;
3535import { isComponentDef , isComponentHost , isContentQueryHost , isRootView } from '../interfaces/type_checks' ;
36- import { CHILD_HEAD , CHILD_TAIL , CLEANUP , CONTEXT , DECLARATION_COMPONENT_VIEW , DECLARATION_VIEW , EMBEDDED_VIEW_INJECTOR , FLAGS , HEADER_OFFSET , HOST , HostBindingOpCodes , ID , InitPhaseState , INJECTOR , LView , LViewFlags , NEXT , PARENT , RENDERER , RENDERER_FACTORY , SANITIZER , T_HOST , TData , TRANSPLANTED_VIEWS_TO_REFRESH , TVIEW , TView , TViewType } from '../interfaces/view' ;
36+ import { CHILD_HEAD , CHILD_TAIL , CLEANUP , CONTEXT , DECLARATION_COMPONENT_VIEW , DECLARATION_VIEW , EMBEDDED_VIEW_INJECTOR , FLAGS , HEADER_OFFSET , HOST , HostBindingOpCodes , ID , InitPhaseState , INJECTOR , LView , LViewFlags , NEXT , ON_DESTROY_HOOKS , PARENT , RENDERER , RENDERER_FACTORY , SANITIZER , T_HOST , TData , TRANSPLANTED_VIEWS_TO_REFRESH , TVIEW , TView , TViewType } from '../interfaces/view' ;
3737import { assertPureTNodeType , assertTNodeType } from '../node_assert' ;
3838import { updateTextNode } from '../node_manipulation' ;
3939import { isInlineTemplate , isNodeMatchingSelectorList } from '../node_selector_matcher' ;
4040import { profiler , ProfilerEvent } from '../profiler' ;
41- import { enterView , getBindingsEnabled , getCurrentDirectiveIndex , getCurrentParentTNode , getCurrentTNode , getCurrentTNodePlaceholderOk , getSelectedIndex , isCurrentTNodeParent , isInCheckNoChangesMode , isInI18nBlock , leaveView , setBindingIndex , setBindingRootForHostBindings , setCurrentDirectiveIndex , setCurrentQueryIndex , setCurrentTNode , setIsInCheckNoChangesMode , setSelectedIndex } from '../state' ;
41+ import { enterView , getBindingsEnabled , getCurrentDirectiveIndex , getCurrentParentTNode , getCurrentTNodePlaceholderOk , getSelectedIndex , isCurrentTNodeParent , isInCheckNoChangesMode , isInI18nBlock , leaveView , setBindingIndex , setBindingRootForHostBindings , setCurrentDirectiveIndex , setCurrentQueryIndex , setCurrentTNode , setIsInCheckNoChangesMode , setSelectedIndex } from '../state' ;
4242import { NO_CHANGE } from '../tokens' ;
4343import { mergeHostAttrs } from '../util/attrs_utils' ;
4444import { INTERPOLATION_DELIMITER } from '../util/misc_utils' ;
45- import { renderStringify , stringifyForError } from '../util/stringify_utils' ;
45+ import { renderStringify } from '../util/stringify_utils' ;
4646import { getFirstLContainer , getLViewParent , getNextLContainer } from '../util/view_traversal_utils' ;
4747import { getComponentLViewByIndex , getNativeByIndex , getNativeByTNode , isCreationMode , resetPreOrderHookFlags , unwrapLView , updateTransplantedViewCount , viewAttachedToChangeDetector } from '../util/view_utils' ;
4848
@@ -676,26 +676,28 @@ export function locateHostElement(
676676 * On the first template pass, saves in TView:
677677 * - Cleanup function
678678 * - Index of context we just saved in LView.cleanupInstances
679- *
680- * This function can also be used to store instance specific cleanup fns. In that case the `context`
681- * is `null` and the function is store in `LView` (rather than it `TView`).
682679 */
683680export function storeCleanupWithContext (
684681 tView : TView , lView : LView , context : any , cleanupFn : Function ) : void {
685682 const lCleanup = getOrCreateLViewCleanup ( lView ) ;
686- if ( context === null ) {
687- // If context is null that this is instance specific callback. These callbacks can only be
688- // inserted after template shared instances. For this reason in ngDevMode we freeze the TView.
683+
684+ // Historically the `storeCleanupWithContext` was used to register both framework-level and
685+ // user-defined cleanup callbacks, but over time those two types of cleanups were separated. This
686+ // dev mode checks assures that user-level cleanup callbacks are _not_ stored in data structures
687+ // reserved for framework-specific hooks.
688+ ngDevMode &&
689+ assertDefined (
690+ context , 'Cleanup context is mandatory when registering framework-level destroy hooks' ) ;
691+ lCleanup . push ( context ) ;
692+
693+ if ( tView . firstCreatePass ) {
694+ getOrCreateTViewCleanup ( tView ) . push ( cleanupFn , lCleanup . length - 1 ) ;
695+ } else {
696+ // Make sure that no new framework-level cleanup functions are registered after the first
697+ // template pass is done (and TView data structures are meant to fully constructed).
689698 if ( ngDevMode ) {
690699 Object . freeze ( getOrCreateTViewCleanup ( tView ) ) ;
691700 }
692- lCleanup . push ( cleanupFn ) ;
693- } else {
694- lCleanup . push ( context ) ;
695-
696- if ( tView . firstCreatePass ) {
697- getOrCreateTViewCleanup ( tView ) . push ( cleanupFn , lCleanup . length - 1 ) ;
698- }
699701 }
700702}
701703
0 commit comments