perf(core): do not recurse into modules that have already been registered (#39514)

When registering an NgModule based on its id, all transitively imported
NgModules are also registered. This commit introduces a visited set to
avoid traversing into NgModules that are reachable from multiple import
paths multiple times.

Fixes #39487

PR Close #39514
This commit is contained in:
JoostK 2020-10-30 20:31:47 +01:00 committed by Joey Perrott
parent 40bf1e0475
commit 8317042483
2 changed files with 24 additions and 16 deletions

View File

@ -8,8 +8,9 @@
import {Type} from '../interface/type';
import {autoRegisterModuleById} from '../render3/definition';
import {autoRegisterModuleById, getNgModuleDef} from '../render3/definition';
import {NgModuleType} from '../render3/ng_module_ref';
import {maybeUnwrapFn} from '../render3/util/misc_utils';
import {stringify} from '../util/stringify';
import {NgModuleFactory} from './ng_module_factory';
@ -39,20 +40,27 @@ function assertSameOrNotExisting(id: string, type: Type<any>|null, incoming: Typ
}
}
export function registerNgModuleType(ngModuleType: NgModuleType) {
if (ngModuleType.ɵmod.id !== null) {
const id = ngModuleType.ɵmod.id;
export function registerNgModuleType(ngModuleType: NgModuleType): void {
const visited = new Set<NgModuleType>();
recurse(ngModuleType);
function recurse(ngModuleType: NgModuleType): void {
// The imports array of an NgModule must refer to other NgModules,
// so an error is thrown if no module definition is available.
const def = getNgModuleDef(ngModuleType, /* throwNotFound */ true);
const id = def.id;
if (id !== null) {
const existing = modules.get(id) as NgModuleType | null;
assertSameOrNotExisting(id, existing, ngModuleType);
modules.set(id, ngModuleType);
}
let imports = ngModuleType.ɵmod.imports;
if (imports instanceof Function) {
imports = imports();
const imports = maybeUnwrapFn(def.imports) as NgModuleType[];
for (const i of imports) {
if (!visited.has(i)) {
visited.add(i);
recurse(i);
}
}
if (imports) {
imports.forEach(i => registerNgModuleType(i as NgModuleType));
}
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Component as _Component, ComponentFactoryResolver, ElementRef, Injectable as _Injectable, InjectFlags, InjectionToken, InjectorType, Provider, RendererFactory2, Type, ViewContainerRef, ɵNgModuleDef as NgModuleDef, ɵɵdefineInjectable, ɵɵdefineInjector, ɵɵinject} from '../../src/core';
import {Component as _Component, ComponentFactoryResolver, ElementRef, Injectable as _Injectable, InjectFlags, InjectionToken, InjectorType, Provider, RendererFactory2, Type, ViewContainerRef, ɵɵdefineInjectable, ɵɵdefineInjector, ɵɵdefineNgModule, ɵɵinject} from '../../src/core';
import {forwardRef} from '../../src/di/forward_ref';
import {createInjector} from '../../src/di/r3_injector';
import {injectComponentFactoryResolver, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵdirectiveInject, ɵɵelement, ɵɵelementEnd, ɵɵelementStart, ɵɵgetInheritedFactory, ɵɵProvidersFeature, ɵɵtext, ɵɵtextInterpolate1} from '../../src/render3/index';
@ -1092,7 +1092,7 @@ describe('providers', () => {
{provide: String, useValue: 'From module injector'}
]
});
static ɵmod: NgModuleDef<any> = {bootstrap: []} as any;
static ɵmod = ɵɵdefineNgModule({type: MyAppModule});
}
const myAppModuleFactory = new NgModuleFactory(MyAppModule);
const ngModuleRef = myAppModuleFactory.create(null);