From 55a14e48662d7267cf755f8526710273febf4e52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=A1ko=20Hevery?= Date: Fri, 17 May 2019 09:52:31 -0700 Subject: [PATCH] feat(ivy): in `ngDevMode` use named object literals and arrays for easier debugging/profiling (#30542) PR Close #30542 --- packages/core/src/render3/i18n.ts | 44 +++--- .../src/render3/instructions/lview_debug.ts | 84 +++++++++- .../core/src/render3/instructions/shared.ts | 146 +++++++++++------- packages/core/src/util/named_array_type.ts | 42 +++++ packages/core/src/util/ng_dev_mode.ts | 6 +- .../core/test/acceptance/styling_next_spec.ts | 2 +- 6 files changed, 244 insertions(+), 80 deletions(-) create mode 100644 packages/core/src/util/named_array_type.ts diff --git a/packages/core/src/render3/i18n.ts b/packages/core/src/render3/i18n.ts index 0b82ab5a1b..ae8008fafe 100644 --- a/packages/core/src/render3/i18n.ts +++ b/packages/core/src/render3/i18n.ts @@ -6,31 +6,31 @@ * found in the LICENSE file at https://angular.io/license */ -import { getPluralCase } from '../i18n/localization'; -import { getTemplateContent, SRCSET_ATTRS, URI_ATTRS, VALID_ATTRS, VALID_ELEMENTS } from '../sanitization/html_sanitizer'; -import { InertBodyHelper } from '../sanitization/inert_body'; -import { sanitizeSrcset, _sanitizeUrl } from '../sanitization/url_sanitizer'; -import { addAllToArray } from '../util/array_utils'; -import { assertDataInRange, assertDefined, assertEqual, assertGreaterThan } from '../util/assert'; import '../util/ng_i18n_closure_mode'; -import { attachPatchData } from './context_discovery'; -import { attachI18nOpCodesDebug } from './debug'; -import { elementAttributeInternal, ɵɵload, ɵɵtextBinding } from './instructions/all'; -import { allocExpando, elementPropertyInternal, getOrCreateTNode, setInputsForProperty } from './instructions/shared'; -import { LContainer, NATIVE } from './interfaces/container'; -import { COMMENT_MARKER, ELEMENT_MARKER, I18nMutateOpCode, I18nMutateOpCodes, I18nUpdateOpCode, I18nUpdateOpCodes, IcuType, TI18n, TIcu } from './interfaces/i18n'; -import { TElementNode, TIcuContainerNode, TNode, TNodeType } from './interfaces/node'; -import { RComment, RElement, RText } from './interfaces/renderer'; -import { SanitizerFn } from './interfaces/sanitization'; -import { StylingContext } from './interfaces/styling'; -import { BINDING_INDEX, HEADER_OFFSET, LView, RENDERER, TVIEW, TView, T_HOST } from './interfaces/view'; -import { appendChild, createTextNode, nativeRemoveNode } from './node_manipulation'; -import { getIsParent, getLView, getPreviousOrParentTNode, setIsNotParent, setPreviousOrParentTNode } from './state'; -import { NO_CHANGE } from './tokens'; -import { renderStringify } from './util/misc_utils'; -import { getNativeByIndex, getNativeByTNode, getTNode, isLContainer } from './util/view_utils'; +import {getPluralCase} from '../i18n/localization'; +import {SRCSET_ATTRS, URI_ATTRS, VALID_ATTRS, VALID_ELEMENTS, getTemplateContent} from '../sanitization/html_sanitizer'; +import {InertBodyHelper} from '../sanitization/inert_body'; +import {_sanitizeUrl, sanitizeSrcset} from '../sanitization/url_sanitizer'; +import {addAllToArray} from '../util/array_utils'; +import {assertDataInRange, assertDefined, assertEqual, assertGreaterThan} from '../util/assert'; +import {attachPatchData} from './context_discovery'; +import {elementAttributeInternal, ɵɵload, ɵɵtextBinding} from './instructions/all'; +import {attachI18nOpCodesDebug} from './instructions/lview_debug'; +import {allocExpando, elementPropertyInternal, getOrCreateTNode, setInputsForProperty} from './instructions/shared'; +import {LContainer, NATIVE} from './interfaces/container'; +import {COMMENT_MARKER, ELEMENT_MARKER, I18nMutateOpCode, I18nMutateOpCodes, I18nUpdateOpCode, I18nUpdateOpCodes, IcuType, TI18n, TIcu} from './interfaces/i18n'; +import {TElementNode, TIcuContainerNode, TNode, TNodeType} from './interfaces/node'; +import {RComment, RElement, RText} from './interfaces/renderer'; +import {SanitizerFn} from './interfaces/sanitization'; +import {StylingContext} from './interfaces/styling'; +import {BINDING_INDEX, HEADER_OFFSET, LView, RENDERER, TVIEW, TView, T_HOST} from './interfaces/view'; +import {appendChild, createTextNode, nativeRemoveNode} from './node_manipulation'; +import {getIsParent, getLView, getPreviousOrParentTNode, setIsNotParent, setPreviousOrParentTNode} from './state'; +import {NO_CHANGE} from './tokens'; +import {renderStringify} from './util/misc_utils'; +import {getNativeByIndex, getNativeByTNode, getTNode, isLContainer} from './util/view_utils'; const MARKER = `�`; diff --git a/packages/core/src/render3/instructions/lview_debug.ts b/packages/core/src/render3/instructions/lview_debug.ts index dcf9badded..f82ca663a7 100644 --- a/packages/core/src/render3/instructions/lview_debug.ts +++ b/packages/core/src/render3/instructions/lview_debug.ts @@ -6,20 +6,24 @@ * found in the LICENSE file at https://angular.io/license */ +import {ComponentTemplate} from '..'; +import {SchemaMetadata} from '../../core'; import {assertDefined} from '../../util/assert'; - +import {createNamedArrayType} from '../../util/named_array_type'; import {ACTIVE_INDEX, CONTAINER_HEADER_OFFSET, LContainer, NATIVE} from '../interfaces/container'; +import {DirectiveDefList, PipeDefList, ViewQueriesFunction} from '../interfaces/definition'; import {COMMENT_MARKER, ELEMENT_MARKER, I18nMutateOpCode, I18nMutateOpCodes, I18nUpdateOpCode, I18nUpdateOpCodes, TIcu} from '../interfaces/i18n'; -import {TNode} from '../interfaces/node'; +import {TElementNode, TNode, TViewNode} from '../interfaces/node'; import {LQueries} from '../interfaces/query'; import {RComment, RElement} from '../interfaces/renderer'; import {StylingContext} from '../interfaces/styling'; -import {BINDING_INDEX, CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTENT_QUERIES, CONTEXT, DECLARATION_VIEW, FLAGS, HEADER_OFFSET, HOST, INJECTOR, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, SANITIZER, TVIEW, T_HOST} from '../interfaces/view'; +import {BINDING_INDEX, CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTENT_QUERIES, CONTEXT, DECLARATION_VIEW, ExpandoInstructions, FLAGS, HEADER_OFFSET, HOST, HookData, INJECTOR, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, SANITIZER, TData, TVIEW, TView as ITView, T_HOST} from '../interfaces/view'; import {runtimeIsNewStylingInUse} from '../styling_next/state'; import {DebugStyling as DebugNewStyling, NodeStylingDebug} from '../styling_next/styling_debug'; import {attachDebugObject} from '../util/debug_utils'; import {getTNode, isStylingContext, unwrapRNode} from '../util/view_utils'; + /* * This file contains conditionally attached classes which provide human readable (debug) level * information for `LView`, `LContainer` and other internal data structures. These data structures @@ -50,6 +54,80 @@ import {getTNode, isStylingContext, unwrapRNode} from '../util/view_utils'; */ +export const LViewArray = ngDevMode && createNamedArrayType('LView'); +let LVIEW_EMPTY: unknown[]; // can't initialize here or it will not be tree shaken, because `LView` + // constructor could have side-effects. +/** + * This function clones a blueprint and creates LView. + * + * Simple slice will keep the same type, and we need it to be LView + */ +export function cloneToLView(list: any[]): LView { + if (LVIEW_EMPTY === undefined) LVIEW_EMPTY = new LViewArray !(); + return LVIEW_EMPTY.concat(list) as any; +} + +/** + * This class is a debug version of Object literal so that we can have constructor name show up in + * debug tools in ngDevMode. + */ +export const TViewConstructor = class TView implements ITView { + constructor( + public id: number, // + public blueprint: LView, // + public template: ComponentTemplate<{}>|null, // + public viewQuery: ViewQueriesFunction<{}>|null, // + public node: TViewNode|TElementNode|null, // + public data: TData, // + public bindingStartIndex: number, // + public viewQueryStartIndex: number, // + public expandoStartIndex: number, // + public expandoInstructions: ExpandoInstructions|null, // + public firstTemplatePass: boolean, // + public staticViewQueries: boolean, // + public staticContentQueries: boolean, // + public preOrderHooks: HookData|null, // + public preOrderCheckHooks: HookData|null, // + public contentHooks: HookData|null, // + public contentCheckHooks: HookData|null, // + public viewHooks: HookData|null, // + public viewCheckHooks: HookData|null, // + public destroyHooks: HookData|null, // + public cleanup: any[]|null, // + public contentQueries: number[]|null, // + public components: number[]|null, // + public directiveRegistry: DirectiveDefList|null, // + public pipeRegistry: PipeDefList|null, // + public firstChild: TNode|null, // + public schemas: SchemaMetadata[]|null, // + ) {} +}; + +const TViewData = ngDevMode && createNamedArrayType('TViewData'); +let TVIEWDATA_EMPTY: + unknown[]; // can't initialize here or it will not be tree shaken, because `LView` + // constructor could have side-effects. +/** + * This function clones a blueprint and creates TData. + * + * Simple slice will keep the same type, and we need it to be TData + */ +export function cloneToTViewData(list: any[]): TData { + if (TVIEWDATA_EMPTY === undefined) TVIEWDATA_EMPTY = new TViewData !(); + return TVIEWDATA_EMPTY.concat(list) as any; +} + +export const LViewBlueprint = ngDevMode && createNamedArrayType('LViewBlueprint'); +export const MatchesArray = ngDevMode && createNamedArrayType('MatchesArray'); +export const TViewComponents = ngDevMode && createNamedArrayType('TViewComponents'); +export const TNodeLocalNames = ngDevMode && createNamedArrayType('TNodeLocalNames'); +export const TNodeInitialInputs = ngDevMode && createNamedArrayType('TNodeInitialInputs'); +export const TNodeInitialData = ngDevMode && createNamedArrayType('TNodeInitialData'); +export const LCleanup = ngDevMode && createNamedArrayType('LCleanup'); +export const TCleanup = ngDevMode && createNamedArrayType('TCleanup'); + + + export function attachLViewDebug(lView: LView) { attachDebugObject(lView, new LViewDebug(lView)); } diff --git a/packages/core/src/render3/instructions/shared.ts b/packages/core/src/render3/instructions/shared.ts index 2a869d7dc9..6607f8d93b 100644 --- a/packages/core/src/render3/instructions/shared.ts +++ b/packages/core/src/render3/instructions/shared.ts @@ -12,6 +12,7 @@ import {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, SchemaMetadata} from '../../me import {validateAgainstEventProperties} from '../../sanitization/sanitization'; import {Sanitizer} from '../../sanitization/security'; import {assertDataInRange, assertDefined, assertDomNode, assertEqual, assertLessThan, assertNotEqual, assertNotSame} from '../../util/assert'; +import {createNamedArrayType} from '../../util/named_array_type'; import {normalizeDebugBindingName, normalizeDebugBindingValue} from '../../util/ng_reflect'; import {assertLView, assertPreviousIsParent} from '../assert'; import {attachPatchData, getComponentViewByInstance} from '../context_discovery'; @@ -37,7 +38,8 @@ import {attrsStylingIndexOf} from '../util/attrs_utils'; import {INTERPOLATION_DELIMITER, stringifyForError} from '../util/misc_utils'; import {getLViewParent, getRootContext} from '../util/view_traversal_utils'; import {getComponentViewByIndex, getNativeByIndex, getNativeByTNode, getTNode, isComponent, isComponentDef, isContentQueryHost, isLContainer, isRootView, readPatchedLView, resetPreOrderHookFlags, unwrapRNode, viewAttachedToChangeDetector} from '../util/view_utils'; -import {attachLContainerDebug, attachLViewDebug} from './lview_debug'; + +import {LCleanup, LViewBlueprint, MatchesArray, TCleanup, TNodeInitialData, TNodeInitialInputs, TNodeLocalNames, TViewComponents, TViewConstructor, attachLContainerDebug, attachLViewDebug, cloneToLView, cloneToTViewData} from './lview_debug'; @@ -199,13 +201,12 @@ export function elementCreate(name: string, overriddenRenderer?: Renderer3): REl } return native; } - export function createLView( parentLView: LView | null, tView: TView, context: T | null, flags: LViewFlags, host: RElement | null, tHostNode: TViewNode | TElementNode | null, rendererFactory?: RendererFactory3 | null, renderer?: Renderer3 | null, sanitizer?: Sanitizer | null, injector?: Injector | null): LView { - const lView = tView.blueprint.slice() as LView; + const lView = ngDevMode ? cloneToLView(tView.blueprint) : tView.blueprint.slice() as LView; lView[HOST] = host; lView[FLAGS] = flags | LViewFlags.CreationMode | LViewFlags.Attached | LViewFlags.FirstLViewPass; resetPreOrderHookFlags(lView); @@ -553,6 +554,7 @@ export function getOrCreateTView(def: ComponentDef): TView { def.viewQuery, def.schemas)); } + /** * Creates a TView instance * @@ -575,38 +577,71 @@ export function createTView( // that has a host binding, we will update the blueprint with that def's hostVars count. const initialViewLength = bindingStartIndex + vars; const blueprint = createViewBlueprint(bindingStartIndex, initialViewLength); - return blueprint[TVIEW as any] = { - id: viewIndex, - blueprint: blueprint, - template: templateFn, - viewQuery: viewQuery, - node: null !, - data: blueprint.slice().fill(null, bindingStartIndex), - bindingStartIndex: bindingStartIndex, - viewQueryStartIndex: initialViewLength, - expandoStartIndex: initialViewLength, - expandoInstructions: null, - firstTemplatePass: true, - staticViewQueries: false, - staticContentQueries: false, - preOrderHooks: null, - preOrderCheckHooks: null, - contentHooks: null, - contentCheckHooks: null, - viewHooks: null, - viewCheckHooks: null, - destroyHooks: null, - cleanup: null, - contentQueries: null, - components: null, - directiveRegistry: typeof directives === 'function' ? directives() : directives, - pipeRegistry: typeof pipes === 'function' ? pipes() : pipes, - firstChild: null, - schemas: schemas, - }; + return blueprint[TVIEW as any] = ngDevMode ? + new TViewConstructor( + viewIndex, // id: number, + blueprint, // blueprint: LView, + templateFn, // template: ComponentTemplate<{}>|null, + viewQuery, // viewQuery: ViewQueriesFunction<{}>|null, + null !, // node: TViewNode|TElementNode|null, + cloneToTViewData(blueprint).fill(null, bindingStartIndex), // data: TData, + bindingStartIndex, // bindingStartIndex: number, + initialViewLength, // viewQueryStartIndex: number, + initialViewLength, // expandoStartIndex: number, + null, // expandoInstructions: ExpandoInstructions|null, + true, // firstTemplatePass: boolean, + false, // staticViewQueries: boolean, + false, // staticContentQueries: boolean, + null, // preOrderHooks: HookData|null, + null, // preOrderCheckHooks: HookData|null, + null, // contentHooks: HookData|null, + null, // contentCheckHooks: HookData|null, + null, // viewHooks: HookData|null, + null, // viewCheckHooks: HookData|null, + null, // destroyHooks: HookData|null, + null, // cleanup: any[]|null, + null, // contentQueries: number[]|null, + null, // components: number[]|null, + typeof directives === 'function' ? + directives() : + directives, // directiveRegistry: DirectiveDefList|null, + typeof pipes === 'function' ? pipes() : pipes, // pipeRegistry: PipeDefList|null, + null, // firstChild: TNode|null, + schemas, // schemas: SchemaMetadata[]|null, + ) : + { + id: viewIndex, + blueprint: blueprint, + template: templateFn, + viewQuery: viewQuery, + node: null !, + data: blueprint.slice().fill(null, bindingStartIndex), + bindingStartIndex: bindingStartIndex, + viewQueryStartIndex: initialViewLength, + expandoStartIndex: initialViewLength, + expandoInstructions: null, + firstTemplatePass: true, + staticViewQueries: false, + staticContentQueries: false, + preOrderHooks: null, + preOrderCheckHooks: null, + contentHooks: null, + contentCheckHooks: null, + viewHooks: null, + viewCheckHooks: null, + destroyHooks: null, + cleanup: null, + contentQueries: null, + components: null, + directiveRegistry: typeof directives === 'function' ? directives() : directives, + pipeRegistry: typeof pipes === 'function' ? pipes() : pipes, + firstChild: null, + schemas: schemas, + }; } + function createViewBlueprint(bindingStartIndex: number, initialViewLength: number): LView { - const blueprint = new Array(initialViewLength) + const blueprint = new (ngDevMode ? LViewBlueprint ! : Array)(initialViewLength) .fill(null, 0, bindingStartIndex) .fill(NO_CHANGE, bindingStartIndex) as LView; blueprint[BINDING_INDEX] = bindingStartIndex; @@ -1132,7 +1167,6 @@ function postProcessBaseDirective( } - /** * Matches the current node against all available selectors. * If a component is matched (at most one), it is returned in first position in the array. @@ -1146,7 +1180,7 @@ function findDirectiveMatches(tView: TView, viewData: LView, tNode: TNode): Dire for (let i = 0; i < registry.length; i++) { const def = registry[i] as ComponentDef| DirectiveDef; if (isNodeMatchingSelectorList(tNode, def.selectors !, /* isProjectionMode */ false)) { - matches || (matches = []); + matches || (matches = ngDevMode ? new MatchesArray !() : []); diPublicInInjector( getOrCreateNodeInjectorForNode( getPreviousOrParentTNode() as TElementNode | TContainerNode | TElementContainerNode, @@ -1173,7 +1207,8 @@ export function queueComponentIndexForCheck(previousOrParentTNode: TNode): void const tView = getLView()[TVIEW]; ngDevMode && assertEqual(tView.firstTemplatePass, true, 'Should only be called in first template pass.'); - (tView.components || (tView.components = [])).push(previousOrParentTNode.index); + (tView.components || (tView.components = ngDevMode ? new TViewComponents !() : [ + ])).push(previousOrParentTNode.index); } @@ -1181,7 +1216,8 @@ export function queueComponentIndexForCheck(previousOrParentTNode: TNode): void function cacheMatchingLocalNames( tNode: TNode, localRefs: string[] | null, exportsMap: {[key: string]: number}): void { if (localRefs) { - const localNames: (string | number)[] = tNode.localNames = []; + const localNames: (string | number)[] = tNode.localNames = + ngDevMode ? new TNodeLocalNames !() : []; // Local names must be stored in tNode in the same order that localRefs are defined // in the template to ensure the data is loaded in the same slots as their refs @@ -1320,7 +1356,8 @@ function setInputsFromAttrs( */ function generateInitialInputs( directiveIndex: number, inputs: {[key: string]: string}, tNode: TNode): InitialInputData { - const initialInputData: InitialInputData = tNode.initialInputs || (tNode.initialInputs = []); + const initialInputData: InitialInputData = + tNode.initialInputs || (tNode.initialInputs = ngDevMode ? new TNodeInitialInputs !() : []); // Ensure that we don't create sparse arrays for (let i = initialInputData.length; i <= directiveIndex; i++) { initialInputData.push(null); @@ -1347,8 +1384,8 @@ function generateInitialInputs( const attrValue = attrs[i + 1]; if (minifiedInputName !== undefined) { - const inputsToStore: InitialInputs = - initialInputData[directiveIndex] || (initialInputData[directiveIndex] = []); + const inputsToStore: InitialInputs = initialInputData[directiveIndex] || + (initialInputData[directiveIndex] = ngDevMode ? new TNodeInitialData !() : []); inputsToStore.push(attrName as string, minifiedInputName, attrValue as string); } @@ -1361,6 +1398,9 @@ function generateInitialInputs( //// ViewContainer & View ////////////////////////// +// Not sure why I need to do `any` here but TS complains later. +const LContainerArray: any = ngDevMode && createNamedArrayType('LContainer'); + /** * Creates a LContainer, either from a container instruction, or for a ViewContainerRef. * @@ -1376,16 +1416,17 @@ export function createLContainer( tNode: TNode, isForViewContainerRef?: boolean): LContainer { ngDevMode && assertDomNode(native); ngDevMode && assertLView(currentView); - const lContainer: LContainer = [ - hostNative, // host native - true, // Boolean `true` in this position signifies that this is an `LContainer` - isForViewContainerRef ? -1 : 0, // active index - currentView, // parent - null, // next - null, // queries - tNode, // t_host - native, // native - ]; + // https://jsperf.com/array-literal-vs-new-array-really + const lContainer: LContainer = new (ngDevMode ? LContainerArray : Array)( + hostNative, // host native + true, // Boolean `true` in this position signifies that this is an `LContainer` + isForViewContainerRef ? -1 : 0, // active index + currentView, // parent + null, // next + null, // queries + tNode, // t_host + native, // native + ); ngDevMode && attachLContainerDebug(lContainer); return lContainer; } @@ -1711,14 +1752,13 @@ export function initializeTNodeInputs(tNode: TNode): PropertyAliases|null { return tNode.inputs; } - export function getCleanup(view: LView): any[] { // top level variables should not be exported for performance reasons (PERF_NOTES.md) - return view[CLEANUP] || (view[CLEANUP] = []); + return view[CLEANUP] || (view[CLEANUP] = ngDevMode ? new LCleanup !() : []); } function getTViewCleanup(view: LView): any[] { - return view[TVIEW].cleanup || (view[TVIEW].cleanup = []); + return view[TVIEW].cleanup || (view[TVIEW].cleanup = ngDevMode ? new TCleanup !() : []); } /** diff --git a/packages/core/src/util/named_array_type.ts b/packages/core/src/util/named_array_type.ts new file mode 100644 index 0000000000..f73a00ce35 --- /dev/null +++ b/packages/core/src/util/named_array_type.ts @@ -0,0 +1,42 @@ + +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * 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 './ng_dev_mode'; +import {global} from './global'; + +/** + * THIS FILE CONTAINS CODE WHICH SHOULD BE TREE SHAKEN AND NEVER CALLED FROM PRODUCTION CODE!!! + */ + + +/** + * Creates an `Array` construction with a given name. This is useful when + * looking for memory consumption to see what time of array it is. + * + * + * @param name Name to give to the constructor + * @returns A subclass of `Array` if possible. This can only be done in + * environments which support `class` construct. + */ +export function createNamedArrayType(name: string): typeof Array { + // This should never be called in prod mode, so let's verify that is the case. + if (ngDevMode) { + try { + // We need to do it this way so that TypeScript does not down-level the below code. + const FunctionConstructor: any = createNamedArrayType.constructor; + return (new FunctionConstructor('Array', `return class ABC extends Array{}`))(Array); + } catch (e) { + // If it does not work just give up and fall back to regular Array. + return Array; + } + } else { + throw new Error( + 'Looks like we are in \'prod mode\', but we are creating a named Array type, which is wrong! Check your code'); + } +} \ No newline at end of file diff --git a/packages/core/src/util/ng_dev_mode.ts b/packages/core/src/util/ng_dev_mode.ts index d70d65c44a..f0424c44b3 100644 --- a/packages/core/src/util/ng_dev_mode.ts +++ b/packages/core/src/util/ng_dev_mode.ts @@ -11,6 +11,7 @@ import {global} from './global'; declare global { const ngDevMode: null|NgDevModePerfCounters; interface NgDevModePerfCounters { + namedConstructors: boolean; firstTemplatePass: number; tNode: number; tView: number; @@ -45,7 +46,9 @@ declare global { } export function ngDevModeResetPerfCounters(): NgDevModePerfCounters { + const locationString = typeof location !== 'undefined' ? location.toString() : ''; const newCounters: NgDevModePerfCounters = { + namedConstructors: locationString.indexOf('ngDevMode=namedConstructors') != -1, firstTemplatePass: 0, tNode: 0, tView: 0, @@ -79,7 +82,8 @@ export function ngDevModeResetPerfCounters(): NgDevModePerfCounters { }; // Make sure to refer to ngDevMode as ['ngDevMode'] for closure. - global['ngDevMode'] = newCounters; + const allowNgDevModeTrue = locationString.indexOf('ngDevMode=false') === -1; + global['ngDevMode'] = allowNgDevModeTrue && newCounters; return newCounters; } diff --git a/packages/core/test/acceptance/styling_next_spec.ts b/packages/core/test/acceptance/styling_next_spec.ts index 1b22f5d273..4c5b50cd1b 100644 --- a/packages/core/test/acceptance/styling_next_spec.ts +++ b/packages/core/test/acceptance/styling_next_spec.ts @@ -9,7 +9,7 @@ import {CompilerStylingMode, compilerSetStylingMode} from '@angular/compiler/src import {Component, Directive, HostBinding, Input, ViewChild} from '@angular/core'; import {SecurityContext} from '@angular/core/src/core'; import {getLContext} from '@angular/core/src/render3/context_discovery'; -import {DebugNode, LViewDebug, toDebug} from '@angular/core/src/render3/debug'; +import {DebugNode, LViewDebug, toDebug} from '@angular/core/src/render3/instructions/lview_debug'; import {SANITIZER} from '@angular/core/src/render3/interfaces/view'; import {RuntimeStylingMode, runtimeSetStylingMode, setCurrentStyleSanitizer} from '@angular/core/src/render3/styling_next/state'; import {loadLContextFromNode} from '@angular/core/src/render3/util/discovery_utils';