mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
refactor(core): allow multiple DI profilers (#60562)
This commit changes the DI profiler infrastructure to allow multiple profilers running at the same time. PR Close #60562
This commit is contained in:
committed by
Jessica Janiuk
parent
bae7b75ff5
commit
b21c6e5ac5
@@ -194,33 +194,55 @@ export function setInjectorProfilerContext(context: InjectorProfilerContext) {
|
||||
return previous;
|
||||
}
|
||||
|
||||
let injectorProfilerCallback: InjectorProfiler | null = null;
|
||||
const injectorProfilerCallbacks: InjectorProfiler[] = [];
|
||||
|
||||
const NOOP_PROFILER_REMOVAL = () => {};
|
||||
|
||||
function removeProfiler(profiler: InjectorProfiler) {
|
||||
const profilerIdx = injectorProfilerCallbacks.indexOf(profiler);
|
||||
if (profilerIdx !== -1) {
|
||||
injectorProfilerCallbacks.splice(profilerIdx, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the callback function which will be invoked during certain DI events within the
|
||||
* runtime (for example: injecting services, creating injectable instances, configuring providers)
|
||||
* Adds a callback function which will be invoked during certain DI events within the
|
||||
* runtime (for example: injecting services, creating injectable instances, configuring providers).
|
||||
* Multiple profiler callbacks can be set: in this case profiling events are
|
||||
* reported to every registered callback.
|
||||
*
|
||||
* Warning: this function is *INTERNAL* and should not be relied upon in application's code.
|
||||
* The contract of the function might be changed in any release and/or the function can be removed
|
||||
* completely.
|
||||
*
|
||||
* @param profiler function provided by the caller or null value to disable profiling.
|
||||
* @returns a cleanup function that, when invoked, removes a given profiler callback.
|
||||
*/
|
||||
export const setInjectorProfiler = (injectorProfiler: InjectorProfiler | null) => {
|
||||
export function setInjectorProfiler(injectorProfiler: InjectorProfiler | null): () => void {
|
||||
!ngDevMode && throwError('setInjectorProfiler should never be called in production mode');
|
||||
injectorProfilerCallback = injectorProfiler;
|
||||
};
|
||||
|
||||
if (injectorProfiler !== null) {
|
||||
if (!injectorProfilerCallbacks.includes(injectorProfiler)) {
|
||||
injectorProfilerCallbacks.push(injectorProfiler);
|
||||
}
|
||||
return () => removeProfiler(injectorProfiler);
|
||||
} else {
|
||||
injectorProfilerCallbacks.length = 0;
|
||||
return NOOP_PROFILER_REMOVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Injector profiler function which emits on DI events executed by the runtime.
|
||||
*
|
||||
* @param event InjectorProfilerEvent corresponding to the DI event being emitted
|
||||
*/
|
||||
function injectorProfiler(event: InjectorProfilerEvent): void {
|
||||
export function injectorProfiler(event: InjectorProfilerEvent): void {
|
||||
!ngDevMode && throwError('Injector profiler should never be called in production mode');
|
||||
|
||||
if (injectorProfilerCallback != null /* both `null` and `undefined` */) {
|
||||
injectorProfilerCallback!(event);
|
||||
for (let i = 0; i < injectorProfilerCallbacks.length; i++) {
|
||||
const injectorProfilerCallback = injectorProfilerCallbacks[i];
|
||||
injectorProfilerCallback(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
afterRender,
|
||||
ClassProvider,
|
||||
Component,
|
||||
createEnvironmentInjector,
|
||||
Directive,
|
||||
ElementRef,
|
||||
inject,
|
||||
@@ -43,6 +44,8 @@ import {
|
||||
InjectorProfilerEventType,
|
||||
ProviderConfiguredEvent,
|
||||
setInjectorProfiler,
|
||||
injectorProfiler,
|
||||
InjectorProfilerContext,
|
||||
} from '../../src/render3/debug/injector_profiler';
|
||||
import {getNodeInjectorLView, NodeInjector} from '../../src/render3/di';
|
||||
import {
|
||||
@@ -103,7 +106,7 @@ describe('setProfiler', () => {
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => setInjectorProfiler(null));
|
||||
afterEach(() => setInjectorProfiler(null));
|
||||
|
||||
it('should emit DI events when a component contains a provider and injects it', () => {
|
||||
class MyService {}
|
||||
@@ -382,6 +385,70 @@ describe('setProfiler', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('profiler activation and removal', () => {
|
||||
class SomeClass {}
|
||||
|
||||
const fakeContext: InjectorProfilerContext = {
|
||||
injector: Injector.create({providers: []}),
|
||||
token: SomeClass,
|
||||
};
|
||||
|
||||
const fakeEvent: InjectorCreatedInstanceEvent = {
|
||||
type: InjectorProfilerEventType.InstanceCreatedByInjector,
|
||||
context: fakeContext,
|
||||
instance: {value: new SomeClass()},
|
||||
};
|
||||
|
||||
it('should allow adding and removing multiple profilers', () => {
|
||||
const events: string[] = [];
|
||||
const r1 = setInjectorProfiler((e) => events.push('P1: ' + e.type));
|
||||
const r2 = setInjectorProfiler((e) => events.push('P2: ' + e.type));
|
||||
|
||||
injectorProfiler(fakeEvent);
|
||||
expect(events).toEqual(['P1: 1', 'P2: 1']);
|
||||
|
||||
r1();
|
||||
injectorProfiler(fakeEvent);
|
||||
expect(events).toEqual(['P1: 1', 'P2: 1', 'P2: 1']);
|
||||
|
||||
r2();
|
||||
injectorProfiler(fakeEvent);
|
||||
expect(events).toEqual(['P1: 1', 'P2: 1', 'P2: 1']);
|
||||
});
|
||||
|
||||
it('should not add / remove the same profiler twice', () => {
|
||||
const events: string[] = [];
|
||||
const p1 = (e: InjectorProfilerEvent) => events.push('P1: ' + e.type);
|
||||
const r1 = setInjectorProfiler(p1);
|
||||
const r2 = setInjectorProfiler(p1);
|
||||
|
||||
injectorProfiler(fakeEvent);
|
||||
expect(events).toEqual(['P1: 1']);
|
||||
|
||||
r1();
|
||||
injectorProfiler(fakeEvent);
|
||||
expect(events).toEqual(['P1: 1']);
|
||||
|
||||
// subsequent removals should be noop
|
||||
r1();
|
||||
r2();
|
||||
});
|
||||
|
||||
it('should clear all profilers when passing null', () => {
|
||||
const events: string[] = [];
|
||||
setInjectorProfiler((e) => events.push('P1: ' + e.type));
|
||||
setInjectorProfiler((e) => events.push('P2: ' + e.type));
|
||||
|
||||
injectorProfiler(fakeEvent);
|
||||
expect(events).toEqual(['P1: 1', 'P2: 1']);
|
||||
|
||||
// clear all profilers
|
||||
setInjectorProfiler(null);
|
||||
injectorProfiler(fakeEvent);
|
||||
expect(events).toEqual(['P1: 1', 'P2: 1']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getInjectorMetadata', () => {
|
||||
it('should be able to determine injector type and name', fakeAsync(() => {
|
||||
class MyServiceA {}
|
||||
@@ -487,7 +554,7 @@ describe('getInjectorMetadata', () => {
|
||||
|
||||
describe('getInjectorProviders', () => {
|
||||
beforeEach(() => setupFrameworkInjectorProfiler());
|
||||
afterAll(() => setInjectorProfiler(null));
|
||||
afterEach(() => setInjectorProfiler(null));
|
||||
|
||||
it('should be able to get the providers from a components injector', () => {
|
||||
class MyService {}
|
||||
@@ -953,7 +1020,7 @@ describe('getInjectorProviders', () => {
|
||||
|
||||
describe('getDependenciesFromInjectable', () => {
|
||||
beforeEach(() => setupFrameworkInjectorProfiler());
|
||||
afterAll(() => setInjectorProfiler(null));
|
||||
afterEach(() => setInjectorProfiler(null));
|
||||
|
||||
it('should be able to determine which injector dependencies come from', fakeAsync(() => {
|
||||
class MyService {}
|
||||
@@ -1244,7 +1311,7 @@ describe('getDependenciesFromInjectable', () => {
|
||||
|
||||
describe('getInjectorResolutionPath', () => {
|
||||
beforeEach(() => setupFrameworkInjectorProfiler());
|
||||
afterAll(() => setInjectorProfiler(null));
|
||||
afterEach(() => setInjectorProfiler(null));
|
||||
|
||||
it('should be able to inspect injector hierarchy structure', fakeAsync(() => {
|
||||
class MyServiceA {}
|
||||
|
||||
Reference in New Issue
Block a user