Skip to content

Commit

Permalink
refactor(core): migrate JIT to use deps tracker behind a flag
Browse files Browse the repository at this point in the history
  • Loading branch information
pmvald committed Jul 20, 2023
1 parent 05657cf commit 7b9e3ea
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 101 deletions.
2 changes: 1 addition & 1 deletion packages/core/src/render3/deps_tracker/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export interface DepsTrackerApi {
* The main application of this method is for test beds where we want to clear the cache to
* enforce scope update after overriding.
*/
clearScopeCacheFor(type: ComponentType<any>|NgModuleType): void;
clearScopeCacheFor(type: Type<any>): void;

/**
* Returns the scope of NgModule. Mainly to be used by JIT and test bed.
Expand Down
14 changes: 11 additions & 3 deletions packages/core/src/render3/deps_tracker/deps_tracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,20 @@ import {Type} from '../../interface/type';
import {NgModuleType} from '../../metadata/ng_module_def';
import {getComponentDef, getNgModuleDef, isStandalone} from '../definition';
import {ComponentType, NgModuleScopeInfoFromDecorator} from '../interfaces/definition';
import {verifyStandaloneImport} from '../jit/directive';
import {isComponent, isDirective, isModuleWithProviders, isNgModule, isPipe} from '../jit/util';
import {isComponent, isDirective, isNgModule, isPipe, verifyStandaloneImport} from '../jit/util';
import {maybeUnwrapFn} from '../util/misc_utils';

import {ComponentDependencies, DepsTrackerApi, NgModuleScope, StandaloneComponentScope} from './api';

/**
* Indicates whether to use the runtime dependency tracker for scope calculation in JIT compilation.
* The value "false" means the old code path based on patching scope info into the types will be
* used.
*
* @deprecated For migration purposes only, to be removed soon.
*/
export const USE_RDT_FOR_JIT = false;

/**
* An implementation of DepsTrackerApi which will be used for JIT and local compilation.
*/
Expand Down Expand Up @@ -115,7 +123,7 @@ class DepsTracker implements DepsTrackerApi {
}

/** @override */
clearScopeCacheFor(type: ComponentType<any>|NgModuleType): void {
clearScopeCacheFor(type: Type<any>): void {
if (isNgModule(type)) {
this.ngModulesScopeCache.delete(type);
} else if (isComponent(type)) {
Expand Down
181 changes: 87 additions & 94 deletions packages/core/src/render3/jit/directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import {getCompilerFacade, JitCompilerUsage, R3DirectiveMetadataFacade} from '../../compiler/compiler_facade';
import {R3ComponentMetadataFacade, R3QueryMetadataFacade} from '../../compiler/compiler_facade_interface';
import {isForwardRef, resolveForwardRef} from '../../di/forward_ref';
import {resolveForwardRef} from '../../di/forward_ref';
import {getReflect, reflectDependencies} from '../../di/jit/util';
import {Type} from '../../interface/type';
import {Query} from '../../metadata/di';
Expand All @@ -19,14 +19,15 @@ import {flatten} from '../../util/array_utils';
import {EMPTY_ARRAY, EMPTY_OBJ} from '../../util/empty';
import {initNgDevMode} from '../../util/ng_dev_mode';
import {getComponentDef, getDirectiveDef, getNgModuleDef, getPipeDef} from '../definition';
import {depsTracker, USE_RDT_FOR_JIT} from '../deps_tracker/deps_tracker';
import {NG_COMP_DEF, NG_DIR_DEF, NG_FACTORY_DEF} from '../fields';
import {ComponentDef, ComponentType, DirectiveDefList, PipeDefList} from '../interfaces/definition';
import {stringifyForError} from '../util/stringify_utils';

import {angularCoreEnv} from './environment';
import {getJitOptions} from './jit_options';
import {flushModuleScopingQueueAsMuchAsPossible, patchComponentDefWithScope, transitiveScopesFor} from './module';
import {isModuleWithProviders} from './util';
import {isComponent, verifyStandaloneImport} from './util';

/**
* Keep track of the compilation depth to avoid reentrancy issues during JIT compilation. This
Expand Down Expand Up @@ -186,48 +187,6 @@ export function compileComponent(type: Type<any>, metadata: Component): void {
});
}

function getDependencyTypeForError(type: Type<any>) {
if (getComponentDef(type)) return 'component';
if (getDirectiveDef(type)) return 'directive';
if (getPipeDef(type)) return 'pipe';
return 'type';
}

export function verifyStandaloneImport(depType: Type<unknown>, importingType: Type<unknown>) {
if (isForwardRef(depType)) {
depType = resolveForwardRef(depType);
if (!depType) {
throw new Error(`Expected forwardRef function, imported from "${
stringifyForError(importingType)}", to return a standalone entity or NgModule but got "${
stringifyForError(depType) || depType}".`);
}
}

if (getNgModuleDef(depType) == null) {
const def = getComponentDef(depType) || getDirectiveDef(depType) || getPipeDef(depType);
if (def != null) {
// if a component, directive or pipe is imported make sure that it is standalone
if (!def.standalone) {
throw new Error(`The "${stringifyForError(depType)}" ${
getDependencyTypeForError(depType)}, imported from "${
stringifyForError(
importingType)}", is not standalone. Did you forget to add the standalone: true flag?`);
}
} else {
// it can be either a module with provider or an unknown (not annotated) type
if (isModuleWithProviders(depType)) {
throw new Error(`A module with providers was imported from "${
stringifyForError(
importingType)}". Modules with providers are not supported in standalone components imports.`);
} else {
throw new Error(`The "${stringifyForError(depType)}" type, imported from "${
stringifyForError(
importingType)}", must be a standalone component / directive / pipe or an NgModule. Did you forget to add the required @Component / @Directive / @Pipe or @NgModule annotation?`);
}
}
}
}

/**
* Build memoized `directiveDefs` and `pipeDefs` functions for the component definition of a
* standalone component, which process `imports` and filter out directives and pipes. The use of
Expand All @@ -241,71 +200,105 @@ function getStandaloneDefFunctions(type: Type<any>, imports: Type<any>[]): {
let cachedDirectiveDefs: DirectiveDefList|null = null;
let cachedPipeDefs: PipeDefList|null = null;
const directiveDefs = () => {
if (cachedDirectiveDefs === null) {
// Standalone components are always able to self-reference, so include the component's own
// definition in its `directiveDefs`.
cachedDirectiveDefs = [getComponentDef(type)!];
const seen = new Set<Type<unknown>>([type]);

for (const rawDep of imports) {
ngDevMode && verifyStandaloneImport(rawDep, type);

const dep = resolveForwardRef(rawDep);
if (seen.has(dep)) {
continue;
}
seen.add(dep);

if (!!getNgModuleDef(dep)) {
const scope = transitiveScopesFor(dep);
for (const dir of scope.exported.directives) {
const def = getComponentDef(dir) || getDirectiveDef(dir);
if (def && !seen.has(dir)) {
seen.add(dir);
if (!USE_RDT_FOR_JIT) {
if (cachedDirectiveDefs === null) {
// Standalone components are always able to self-reference, so include the component's own
// definition in its `directiveDefs`.
cachedDirectiveDefs = [getComponentDef(type)!];
const seen = new Set<Type<unknown>>([type]);

for (const rawDep of imports) {
ngDevMode && verifyStandaloneImport(rawDep, type);

const dep = resolveForwardRef(rawDep);
if (seen.has(dep)) {
continue;
}
seen.add(dep);

if (!!getNgModuleDef(dep)) {
const scope = transitiveScopesFor(dep);
for (const dir of scope.exported.directives) {
const def = getComponentDef(dir) || getDirectiveDef(dir);
if (def && !seen.has(dir)) {
seen.add(dir);
cachedDirectiveDefs.push(def);
}
}
} else {
const def = getComponentDef(dep) || getDirectiveDef(dep);
if (def) {
cachedDirectiveDefs.push(def);
}
}
} else {
const def = getComponentDef(dep) || getDirectiveDef(dep);
if (def) {
cachedDirectiveDefs.push(def);
}
}
}
return cachedDirectiveDefs;
} else {
if (ngDevMode) {
for (const rawDep of imports) {
verifyStandaloneImport(rawDep, type);
}
}

if (!isComponent(type)) {
return [];
}

const scope = depsTracker.getStandaloneComponentScope(type, imports);

return [...scope.compilation.directives]
.map(p => (getComponentDef(p) || getDirectiveDef(p))!)
.filter(d => d !== null);
}
return cachedDirectiveDefs;
};

const pipeDefs = () => {
if (cachedPipeDefs === null) {
cachedPipeDefs = [];
const seen = new Set<Type<unknown>>();

for (const rawDep of imports) {
const dep = resolveForwardRef(rawDep);
if (seen.has(dep)) {
continue;
}
seen.add(dep);

if (!!getNgModuleDef(dep)) {
const scope = transitiveScopesFor(dep);
for (const pipe of scope.exported.pipes) {
const def = getPipeDef(pipe);
if (def && !seen.has(pipe)) {
seen.add(pipe);
if (!USE_RDT_FOR_JIT) {
if (cachedPipeDefs === null) {
cachedPipeDefs = [];
const seen = new Set<Type<unknown>>();

for (const rawDep of imports) {
const dep = resolveForwardRef(rawDep);
if (seen.has(dep)) {
continue;
}
seen.add(dep);

if (!!getNgModuleDef(dep)) {
const scope = transitiveScopesFor(dep);
for (const pipe of scope.exported.pipes) {
const def = getPipeDef(pipe);
if (def && !seen.has(pipe)) {
seen.add(pipe);
cachedPipeDefs.push(def);
}
}
} else {
const def = getPipeDef(dep);
if (def) {
cachedPipeDefs.push(def);
}
}
} else {
const def = getPipeDef(dep);
if (def) {
cachedPipeDefs.push(def);
}
}
}
return cachedPipeDefs;
} else {
if (ngDevMode) {
for (const rawDep of imports) {
verifyStandaloneImport(rawDep, type);
}
}

if (!isComponent(type)) {
return [];
}

const scope = depsTracker.getStandaloneComponentScope(type, imports);

return [...scope.compilation.pipes].map(p => getPipeDef(p)!).filter(d => d !== null);
}
return cachedPipeDefs;
};

return {
Expand Down
12 changes: 11 additions & 1 deletion packages/core/src/render3/jit/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {deepForEach, flatten} from '../../util/array_utils';
import {assertDefined} from '../../util/assert';
import {EMPTY_ARRAY} from '../../util/empty';
import {GENERATED_COMP_IDS, getComponentDef, getDirectiveDef, getNgModuleDef, getPipeDef, isStandalone} from '../definition';
import {depsTracker, USE_RDT_FOR_JIT} from '../deps_tracker/deps_tracker';
import {NG_COMP_DEF, NG_DIR_DEF, NG_FACTORY_DEF, NG_MOD_DEF, NG_PIPE_DEF} from '../fields';
import {ComponentDef} from '../interfaces/definition';
import {maybeUnwrapFn} from '../util/misc_utils';
Expand Down Expand Up @@ -489,7 +490,16 @@ export function patchComponentDefWithScope<C>(
*/
export function transitiveScopesFor<T>(type: Type<T>): NgModuleTransitiveScopes {
if (isNgModule(type)) {
return transitiveScopesForNgModule(type);
if (USE_RDT_FOR_JIT) {
const scope = depsTracker.getNgModuleScope(type);
const def = getNgModuleDef(type, true);
return {
schemas: def.schemas || null,
...scope,
};
} else {
return transitiveScopesForNgModule(type);
}
} else if (isStandalone(type)) {
const directiveDef = getComponentDef(type) || getDirectiveDef(type);
if (directiveDef !== null) {
Expand Down
44 changes: 44 additions & 0 deletions packages/core/src/render3/jit/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
* found in the LICENSE file at https://angular.io/license
*/

import {isForwardRef, resolveForwardRef} from '../../di/forward_ref';
import {ModuleWithProviders} from '../../di/interface/provider';
import {Type} from '../../interface/type';
import {NgModuleDef} from '../../metadata/ng_module_def';
import {getComponentDef, getDirectiveDef, getNgModuleDef, getPipeDef} from '../definition';
import {ComponentType, DirectiveType, PipeType} from '../interfaces/definition';
import {stringifyForError} from '../util/stringify_utils';

export function isModuleWithProviders(value: any): value is ModuleWithProviders<{}> {
return (value as {ngModule?: any}).ngModule !== undefined;
Expand All @@ -31,3 +33,45 @@ export function isDirective<T>(value: Type<T>): value is DirectiveType<T> {
export function isComponent<T>(value: Type<T>): value is ComponentType<T> {
return !!getComponentDef(value);
}

function getDependencyTypeForError(type: Type<any>) {
if (getComponentDef(type)) return 'component';
if (getDirectiveDef(type)) return 'directive';
if (getPipeDef(type)) return 'pipe';
return 'type';
}

export function verifyStandaloneImport(depType: Type<unknown>, importingType: Type<unknown>) {
if (isForwardRef(depType)) {
depType = resolveForwardRef(depType);
if (!depType) {
throw new Error(`Expected forwardRef function, imported from "${
stringifyForError(importingType)}", to return a standalone entity or NgModule but got "${
stringifyForError(depType) || depType}".`);
}
}

if (getNgModuleDef(depType) == null) {
const def = getComponentDef(depType) || getDirectiveDef(depType) || getPipeDef(depType);
if (def != null) {
// if a component, directive or pipe is imported make sure that it is standalone
if (!def.standalone) {
throw new Error(`The "${stringifyForError(depType)}" ${
getDependencyTypeForError(depType)}, imported from "${
stringifyForError(
importingType)}", is not standalone. Did you forget to add the standalone: true flag?`);
}
} else {
// it can be either a module with provider or an unknown (not annotated) type
if (isModuleWithProviders(depType)) {
throw new Error(`A module with providers was imported from "${
stringifyForError(
importingType)}". Modules with providers are not supported in standalone components imports.`);
} else {
throw new Error(`The "${stringifyForError(depType)}" type, imported from "${
stringifyForError(
importingType)}", must be a standalone component / directive / pipe or an NgModule. Did you forget to add the required @Component / @Directive / @Pipe or @NgModule annotation?`);
}
}
}
}
Loading

0 comments on commit 7b9e3ea

Please sign in to comment.