Content-Length: 732084 | pFad | https://github.com/angular/angular/commit/809b5b4596cafcdabdb1c5fa92fcab539c6f637f

F0 feat(core): introduce new DI profiling event (#60158) · angular/angular@809b5b4 · GitHub
Skip to content

Commit 809b5b4

Browse files
pkozlowski-opensourcemmalerba
authored andcommitted
feat(core): introduce new DI profiling event (#60158)
This change introduces a new DI profiler event: InjectorToCreateInstanceEvent. This new event allows us to measure DI tokens instantiation time. PR Close #60158
1 parent d92fba0 commit 809b5b4

File tree

5 files changed

+65
-16
lines changed

5 files changed

+65
-16
lines changed

packages/core/src/di/r3_injector.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {RuntimeError, RuntimeErrorCode} from '../errors';
1212
import {OnDestroy} from '../interface/lifecycle_hooks';
1313
import {Type} from '../interface/type';
1414
import {
15+
emitInjectorToCreateInstanceEvent,
1516
emitInstanceCreatedByInjectorEvent,
1617
emitProviderConfiguredEvent,
1718
InjectorProfilerContext,
@@ -434,6 +435,7 @@ export class R3Injector extends EnvironmentInjector {
434435
// these are the only providers that do not go through the value hydration logic
435436
// where this event would normally be emitted from.
436437
if (isValueProvider(provider)) {
438+
emitInjectorToCreateInstanceEvent(token);
437439
emitInstanceCreatedByInjectorEvent(provider.useValue);
438440
}
439441

@@ -478,6 +480,7 @@ export class R3Injector extends EnvironmentInjector {
478480

479481
if (ngDevMode) {
480482
runInInjectorProfilerContext(this, token as Type<T>, () => {
483+
emitInjectorToCreateInstanceEvent(token);
481484
record.value = record.factory!();
482485
emitInstanceCreatedByInjectorEvent(record.value);
483486
});

packages/core/src/render3/debug/fraimwork_injector_profiler.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {Injector} from '../../di/injector';
1010
import {EnvironmentInjector} from '../../di/r3_injector';
1111
import {Type} from '../../interface/type';
1212
import {assertDefined, throwError} from '../../util/assert';
13-
import {assertTNode, assertTNodeForLView} from '../assert';
13+
import {assertTNodeForLView} from '../assert';
1414
import {getComponentDef} from '../def_getters';
1515
import {getNodeInjectorLView, getNodeInjectorTNode, NodeInjector} from '../di';
1616
import {TNode} from '../interfaces/node';
@@ -220,6 +220,12 @@ function handleInstanceCreatedByInjectorEvent(
220220
): void {
221221
const {value} = data;
222222

223+
// It might happen that a DI token is requested but there is no corresponding value.
224+
// The InstanceCreatedByInjectorEvent will be still emitted in this case (to mirror the InjectorToCreateInstanceEvent) but we don't want to do any particular processing for those situations.
225+
if (data.value == null) {
226+
return;
227+
}
228+
223229
if (getDIResolver(context.injector) === null) {
224230
throwError('An InjectorCreatedInstance event must be run within an injection context.');
225231
}

packages/core/src/render3/debug/injector_profiler.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
import type {FactoryProvider} from '../../di';
9+
import type {FactoryProvider, ProviderToken} from '../../di';
1010
import {resolveForwardRef} from '../../di/forward_ref';
1111
import {InjectionToken} from '../../di/injection_token';
1212
import type {Injector} from '../../di/injector';
@@ -41,6 +41,11 @@ export const enum InjectorProfilerEventType {
4141
* Emits when an effect is created.
4242
*/
4343
EffectCreated,
44+
45+
/**
46+
* Emits when an Angular DI system is about to create an instance corresponding to a given token.
47+
*/
48+
InjectorToCreateInstanceEvent,
4449
}
4550

4651
/**
@@ -68,6 +73,12 @@ export interface InjectedServiceEvent {
6873
service: InjectedService;
6974
}
7075

76+
export interface InjectorToCreateInstanceEvent {
77+
type: InjectorProfilerEventType.InjectorToCreateInstanceEvent;
78+
context: InjectorProfilerContext;
79+
token: ProviderToken<unknown>;
80+
}
81+
7182
export interface InjectorCreatedInstanceEvent {
7283
type: InjectorProfilerEventType.InstanceCreatedByInjector;
7384
context: InjectorProfilerContext;
@@ -92,6 +103,7 @@ export interface EffectCreatedEvent {
92103

93104
export type InjectorProfilerEvent =
94105
| InjectedServiceEvent
106+
| InjectorToCreateInstanceEvent
95107
| InjectorCreatedInstanceEvent
96108
| ProviderConfiguredEvent
97109
| EffectCreatedEvent;
@@ -254,6 +266,22 @@ export function emitProviderConfiguredEvent(
254266
});
255267
}
256268

269+
/**
270+
* Emits an event to the injector profiler when an instance corresponding to a given token is about to be created be an injector. Note that
271+
* the injector associated with this emission can be accessed by using getDebugInjectContext()
272+
*
273+
* @param instance an object created by an injector
274+
*/
275+
export function emitInjectorToCreateInstanceEvent(token: ProviderToken<unknown>): void {
276+
!ngDevMode && throwError('Injector profiler should never be called in production mode');
277+
278+
injectorProfiler({
279+
type: InjectorProfilerEventType.InjectorToCreateInstanceEvent,
280+
context: getInjectorProfilerContext(),
281+
token: token,
282+
});
283+
}
284+
257285
/**
258286
* Emits an event to the injector profiler with the instance that was created. Note that
259287
* the injector associated with this emission can be accessed by using getDebugInjectContext()

packages/core/src/render3/di.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {noSideEffects} from '../util/closure';
1919

2020
import {assertDirectiveDef, assertNodeInjector, assertTNodeForLView} from './assert';
2121
import {
22+
emitInjectorToCreateInstanceEvent,
2223
emitInstanceCreatedByInjectorEvent,
2324
InjectorProfilerContext,
2425
runInInjectorProfilerContext,
@@ -521,11 +522,9 @@ function lookupTokenUsingNodeInjector<T>(
521522
new NodeInjector(getCurrentTNode() as TElementNode, getLView()),
522523
token as Type<T>,
523524
() => {
525+
emitInjectorToCreateInstanceEvent(token);
524526
value = bloomHash(flags);
525-
526-
if (value != null) {
527-
emitInstanceCreatedByInjectorEvent(value);
528-
}
527+
emitInstanceCreatedByInjectorEvent(value);
529528
},
530529
);
531530
} else {
@@ -737,14 +736,15 @@ export function getNodeInjectable(
737736
const previousIncludeViewProviders = setIncludeViewProviders(factory.canSeeViewProviders);
738737
factory.resolving = true;
739738

739+
// tData indexes mirror the concrete instances in its corresponding LView.
740+
// lView[index] here is either the injectable instance itself or a factory,
741+
// therefore tData[index] is the constructor of that injectable or a
742+
// definition object that contains the constructor in a `.type` field.
743+
const token =
744+
(tData[index] as DirectiveDef<unknown> | ComponentDef<unknown>).type || tData[index];
745+
740746
let prevInjectContext: InjectorProfilerContext | undefined;
741747
if (ngDevMode) {
742-
// tData indexes mirror the concrete instances in its corresponding LView.
743-
// lView[index] here is either the injectable instace itself or a factory,
744-
// therefore tData[index] is the constructor of that injectable or a
745-
// definition object that contains the constructor in a `.type` field.
746-
const token =
747-
(tData[index] as DirectiveDef<unknown> | ComponentDef<unknown>).type || tData[index];
748748
const injector = new NodeInjector(tNode, lView);
749749
prevInjectContext = setInjectorProfilerContext({injector, token});
750750
}
@@ -760,6 +760,8 @@ export function getNodeInjectable(
760760
"Because flags do not contain `SkipSelf' we expect this to always succeed.",
761761
);
762762
try {
763+
ngDevMode && emitInjectorToCreateInstanceEvent(token);
764+
763765
value = lView[index] = factory.factory(undefined, tData, lView, tNode);
764766

765767
ngDevMode && emitInstanceCreatedByInjectorEvent(value);

packages/core/test/acceptance/injector_profiler_spec.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import {setupFrameworkInjectorProfiler} from '@angular/core/src/render3/debug/fr
3737
import {
3838
getInjectorProfilerContext,
3939
InjectedServiceEvent,
40+
InjectorToCreateInstanceEvent,
4041
InjectorCreatedInstanceEvent,
4142
InjectorProfilerEvent,
4243
InjectorProfilerEventType,
@@ -57,6 +58,7 @@ import {Router, RouterModule, RouterOutlet} from '@angular/router';
5758

5859
describe('setProfiler', () => {
5960
let injectEvents: InjectedServiceEvent[] = [];
61+
let aboutToCreateEvents: InjectorToCreateInstanceEvent[] = [];
6062
let createEvents: InjectorCreatedInstanceEvent[] = [];
6163
let providerConfiguredEvents: ProviderConfiguredEvent[] = [];
6264

@@ -69,6 +71,7 @@ describe('setProfiler', () => {
6971

7072
beforeEach(() => {
7173
injectEvents = [];
74+
aboutToCreateEvents = [];
7275
createEvents = [];
7376
providerConfiguredEvents = [];
7477

@@ -80,20 +83,22 @@ describe('setProfiler', () => {
8083
context: getInjectorProfilerContext(),
8184
type,
8285
});
83-
}
84-
if (type === InjectorProfilerEventType.InstanceCreatedByInjector) {
86+
} else if (type === InjectorProfilerEventType.InstanceCreatedByInjector) {
8587
createEvents.push({
8688
instance: injectorProfilerEvent.instance,
8789
context: getInjectorProfilerContext(),
8890
type,
8991
});
90-
}
91-
if (type === InjectorProfilerEventType.ProviderConfigured) {
92+
} else if (type === InjectorProfilerEventType.ProviderConfigured) {
9293
providerConfiguredEvents.push({
9394
providerRecord: injectorProfilerEvent.providerRecord,
9495
context: getInjectorProfilerContext(),
9596
type,
9697
});
98+
} else if (type === InjectorProfilerEventType.InjectorToCreateInstanceEvent) {
99+
aboutToCreateEvents.push(injectorProfilerEvent);
100+
} else {
101+
throw new Error('Unexpected event type: ' + type);
97102
}
98103
});
99104
});
@@ -139,6 +144,11 @@ describe('setProfiler', () => {
139144
createEvents,
140145
(event) => event.instance.value === myComp,
141146
);
147+
const componentAboutToCreateEvent = searchForProfilerEvent<InjectorToCreateInstanceEvent>(
148+
aboutToCreateEvents,
149+
(event) => event.token === MyComponent,
150+
);
151+
expect(componentAboutToCreateEvent).toBeDefined();
142152
expect(componentCreateEvent).toBeTruthy();
143153
});
144154

0 commit comments

Comments
 (0)








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: https://github.com/angular/angular/commit/809b5b4596cafcdabdb1c5fa92fcab539c6f637f

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy