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 {Type} from '../interface/type';
import {autoRegisterModuleById} from '../render3/definition'; import {autoRegisterModuleById, getNgModuleDef} from '../render3/definition';
import {NgModuleType} from '../render3/ng_module_ref'; import {NgModuleType} from '../render3/ng_module_ref';
import {maybeUnwrapFn} from '../render3/util/misc_utils';
import {stringify} from '../util/stringify'; import {stringify} from '../util/stringify';
import {NgModuleFactory} from './ng_module_factory'; 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) { export function registerNgModuleType(ngModuleType: NgModuleType): void {
if (ngModuleType.ɵmod.id !== null) { const visited = new Set<NgModuleType>();
const id = ngModuleType.ɵmod.id; recurse(ngModuleType);
const existing = modules.get(id) as NgModuleType | null; function recurse(ngModuleType: NgModuleType): void {
assertSameOrNotExisting(id, existing, ngModuleType); // The imports array of an NgModule must refer to other NgModules,
modules.set(id, ngModuleType); // 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; const imports = maybeUnwrapFn(def.imports) as NgModuleType[];
if (imports instanceof Function) { for (const i of imports) {
imports = imports(); if (!visited.has(i)) {
} visited.add(i);
if (imports) { recurse(i);
imports.forEach(i => registerNgModuleType(i as NgModuleType)); }
}
} }
} }

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * 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 {forwardRef} from '../../src/di/forward_ref';
import {createInjector} from '../../src/di/r3_injector'; import {createInjector} from '../../src/di/r3_injector';
import {injectComponentFactoryResolver, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵdirectiveInject, ɵɵelement, ɵɵelementEnd, ɵɵelementStart, ɵɵgetInheritedFactory, ɵɵProvidersFeature, ɵɵtext, ɵɵtextInterpolate1} from '../../src/render3/index'; 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'} {provide: String, useValue: 'From module injector'}
] ]
}); });
static ɵmod: NgModuleDef<any> = {bootstrap: []} as any; static ɵmod = ɵɵdefineNgModule({type: MyAppModule});
} }
const myAppModuleFactory = new NgModuleFactory(MyAppModule); const myAppModuleFactory = new NgModuleFactory(MyAppModule);
const ngModuleRef = myAppModuleFactory.create(null); const ngModuleRef = myAppModuleFactory.create(null);