Skip to content

Commit fca185e

Browse files
mheveryAndrewKushnir
authored andcommitted
refactor(ivy): create Injector interface; remove dependency on Ivy (angular#28066)
This change is a prerequasity for a later change which will turn the 'di' into its own bazel package. In order to do that we have to: - have `Injector` type be importable by Ivy. This means that we need to create `Injector` as a pure type in `interface` folder which is already a bazel package which Ivy can depend on. - Remove the dependency of `class Injector` on Ivy so that it can be compiled in isolation. We do that by using `-1` as special value for `__NG_ELEMENT_ID__` which tells the Ivy `NodeInjector` than `Injector` is being requested. PR Close angular#28066
1 parent e082fc2 commit fca185e

26 files changed

+151
-97
lines changed

packages/core/src/core_render3_private_export.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -253,8 +253,5 @@ export {
253253
publishGlobalUtil as ɵpublishGlobalUtil,
254254
publishDefaultGlobalUtils as ɵpublishDefaultGlobalUtils
255255
} from './render3/global_utils';
256-
export {
257-
SWITCH_INJECTOR_FACTORY__POST_R3__ as ɵSWITCH_INJECTOR_FACTORY__POST_R3__,
258-
} from './di/injector';
259256

260257
// clang-format on

packages/core/src/di.ts

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,20 @@
77
*/
88

99
/**
10-
* @module
11-
* @description
12-
* The `di` module provides dependency injection container services.
10+
* This file should not be necessary because node resolution should just default to `./di/index`!
11+
*
12+
* However it does not seem to work and it breaks:
13+
* - //packages/animations/browser/test:test_web_chromium-local
14+
* - //packages/compiler-cli/test:extract_i18n
15+
* - //packages/compiler-cli/test:ngc
16+
* - //packages/compiler-cli/test:perform_watch
17+
* - //packages/compiler-cli/test/diagnostics:check_types
18+
* - //packages/compiler-cli/test/transformers:test
19+
* - //packages/compiler/test:test
20+
* - //tools/public_api_guard:core_api
21+
*
22+
* Remove this file once the above is solved or wait until `ngc` is deleted and then it should be
23+
* safe to delete this file.
1324
*/
1425

15-
export * from './di/metadata';
16-
export {InjectableType, InjectorType, defineInjectable, defineInjector} from './di/interface/defs';
17-
export {forwardRef, resolveForwardRef, ForwardRefFn} from './di/forward_ref';
18-
export {Injectable, InjectableDecorator, InjectableProvider} from './di/injectable';
19-
export {INJECTOR, Injector} from './di/injector';
20-
export {inject, InjectFlags} from './di/injector_compatibility';
21-
export {ReflectiveInjector} from './di/reflective_injector';
22-
export {StaticProvider, ValueProvider, ConstructorSansProvider, ExistingProvider, FactoryProvider, Provider, TypeProvider, ClassProvider} from './di/interface/provider';
23-
export {createInjector} from './di/r3_injector';
24-
export {ResolvedReflectiveFactory, ResolvedReflectiveProvider} from './di/reflective_provider';
25-
export {ReflectiveKey} from './di/reflective_key';
26-
export {InjectionToken} from './di/injection_token';
26+
export * from './di/index';

packages/core/src/di/index.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
/**
10+
* @module
11+
* @description
12+
* The `di` module provides dependency injection container services.
13+
*/
14+
15+
export * from './metadata';
16+
export {InjectFlags} from './interface/injector';
17+
export {defineInjectable, defineInjector, InjectableType, InjectorType} from './interface/defs';
18+
export {forwardRef, resolveForwardRef, ForwardRefFn} from './forward_ref';
19+
export {Injectable, InjectableDecorator, InjectableProvider} from './injectable';
20+
export {INJECTOR, Injector} from './injector';
21+
export {inject} from './injector_compatibility';
22+
export {ReflectiveInjector} from './reflective_injector';
23+
export {StaticProvider, ValueProvider, ConstructorSansProvider, ExistingProvider, FactoryProvider, Provider, TypeProvider, ClassProvider} from './interface/provider';
24+
export {createInjector} from './r3_injector';
25+
export {ResolvedReflectiveFactory, ResolvedReflectiveProvider} from './reflective_provider';
26+
export {ReflectiveKey} from './reflective_key';
27+
export {InjectionToken} from './injection_token';

packages/core/src/di/injectable.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ import {Type} from '../interface/type';
1010
import {compileInjectable as render3CompileInjectable} from '../render3/jit/injectable';
1111
import {TypeDecorator, makeDecorator} from '../util/decorators';
1212

13-
import {InjectableDef, InjectableType, defineInjectable, getInjectableDef} from './interface/defs';
14-
import {ClassSansProvider, ConstructorSansProvider, ExistingSansProvider, FactorySansProvider, StaticClassSansProvider, ValueSansProvider} from './interface/provider';
13+
import {ClassSansProvider, ConstructorSansProvider, ExistingSansProvider, FactorySansProvider, InjectableDef, InjectableType, StaticClassSansProvider, ValueSansProvider, defineInjectable, getInjectableDef} from './interface';
1514
import {convertInjectableProviderToFactory} from './util';
1615

1716

packages/core/src/di/injection_token.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,17 @@ export class InjectionToken<T> {
6060
providedIn?: Type<any>| 'root' | null,
6161
factory: () => T
6262
}) {
63-
if (options !== undefined) {
63+
this.ngInjectableDef = undefined;
64+
if (typeof options == 'number') {
65+
// This is a special hack to assign __NG_ELEMENT_ID__ to this instance.
66+
// __NG_ELEMENT_ID__ is Used by Ivy to determine bloom filter id.
67+
// We are using it to assign `-1` which is used to identify `Injector`.
68+
(this as any).__NG_ELEMENT_ID__ = options;
69+
} else if (options !== undefined) {
6470
this.ngInjectableDef = defineInjectable({
6571
providedIn: options.providedIn || 'root',
6672
factory: options.factory,
6773
});
68-
} else {
69-
this.ngInjectableDef = undefined;
7074
}
7175
}
7276

packages/core/src/di/injector.ts

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,13 @@
77
*/
88

99
import {Type} from '../interface/type';
10-
import {injectInjector} from '../render3/di';
11-
import {noop} from '../util/noop';
1210
import {getClosureSafeProperty} from '../util/property';
1311
import {stringify} from '../util/stringify';
1412

1513
import {resolveForwardRef} from './forward_ref';
1614
import {InjectionToken} from './injection_token';
17-
import {InjectFlags, inject} from './injector_compatibility';
18-
import {defineInjectable} from './interface/defs';
19-
import {ConstructorProvider, ExistingProvider, FactoryProvider, StaticClassProvider, StaticProvider, ValueProvider} from './interface/provider';
15+
import {inject} from './injector_compatibility';
16+
import {ConstructorProvider, ExistingProvider, FactoryProvider, InjectFlags, StaticClassProvider, StaticProvider, ValueProvider, defineInjectable} from './interface';
2017
import {Inject, Optional, Self, SkipSelf} from './metadata';
2118

2219
export const SOURCE = '__source';
@@ -31,7 +28,10 @@ export const THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND;
3128
*
3229
* @publicApi
3330
*/
34-
export const INJECTOR = new InjectionToken<Injector>('INJECTOR');
31+
export const INJECTOR = new InjectionToken<Injector>(
32+
'INJECTOR',
33+
-1 as any // `-1` is used by Ivy DI system as special value to recognize it as `Injector`.
34+
);
3535

3636
export class NullInjector implements Injector {
3737
get(token: any, notFoundValue: any = _THROW_IF_NOT_FOUND): any {
@@ -109,16 +109,13 @@ export abstract class Injector {
109109
factory: () => inject(INJECTOR),
110110
});
111111

112-
/** @internal */
113-
/** @nocollapse */
114-
static __NG_ELEMENT_ID__: () => Injector = () => SWITCH_INJECTOR_FACTORY();
112+
/**
113+
* @internal
114+
* @nocollapse
115+
*/
116+
static __NG_ELEMENT_ID__ = -1;
115117
}
116118

117-
export const SWITCH_INJECTOR_FACTORY__POST_R3__ = function() {
118-
return injectInjector();
119-
};
120-
const SWITCH_INJECTOR_FACTORY__PRE_R3__ = noop;
121-
const SWITCH_INJECTOR_FACTORY: typeof injectInjector = SWITCH_INJECTOR_FACTORY__PRE_R3__;
122119

123120

124121
const IDENT = function<T>(value: T): T {

packages/core/src/di/injector_compatibility.ts

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,34 +11,10 @@ import {stringify} from '../util/stringify';
1111

1212
import {InjectionToken} from './injection_token';
1313
import {Injector} from './injector';
14-
import {InjectableDef, getInjectableDef} from './interface/defs';
14+
import {InjectFlags, InjectableDef, getInjectableDef} from './interface';
1515
import {Inject, Optional, Self, SkipSelf} from './metadata';
1616

1717

18-
/**
19-
* Injection flags for DI.
20-
*
21-
* @publicApi
22-
*/
23-
export enum InjectFlags {
24-
// TODO(alxhub): make this 'const' when ngc no longer writes exports of it into ngfactory files.
25-
26-
Default = 0b0000,
27-
28-
/**
29-
* Specifies that an injector should retrieve a dependency from any injector until reaching the
30-
* host element of the current component. (Only used with Element Injector)
31-
*/
32-
Host = 0b0001,
33-
/** Don't descend into ancestors of the node requesting injection. */
34-
Self = 0b0010,
35-
/** Skip the node that is requesting injection. */
36-
SkipSelf = 0b0100,
37-
/** Inject `defaultValue` instead if token not found. */
38-
Optional = 0b1000,
39-
}
40-
41-
4218

4319
/**
4420
* Current injector value used by `inject`.

packages/core/src/di/interface.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
/**
10+
* This file should not be necessary because node resolution should just default to `./di/index`!
11+
*
12+
* However it does not seem to work and it breaks web tests.
13+
*/
14+
15+
export * from './interface/index';
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
export * from './injector';
10+
export * from './defs';
11+
export * from './provider';
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
10+
/**
11+
* Injection flags for DI.
12+
*
13+
* @publicApi
14+
*/
15+
export enum InjectFlags {
16+
// TODO(alxhub): make this 'const' when ngc no longer writes exports of it into ngfactory files.
17+
18+
Default = 0b0000,
19+
20+
/**
21+
* Specifies that an injector should retrieve a dependency from any injector until reaching the
22+
* host element of the current component. (Only used with Element Injector)
23+
*/
24+
Host = 0b0001,
25+
/** Don't ascend to ancestors of the node requesting injection. */
26+
Self = 0b0010,
27+
/** Skip the node that is requesting injection. */
28+
SkipSelf = 0b0100,
29+
/** Inject `defaultValue` instead if token not found. */
30+
Optional = 0b1000,
31+
}

packages/core/src/di/metadata.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {Type} from '../interface/type';
10-
import {ReflectionCapabilities} from '../reflection/reflection_capabilities';
11-
import {makeDecorator, makeParamDecorator} from '../util/decorators';
12-
import {EMPTY_ARRAY} from '../view/util';
13-
14-
import {ClassSansProvider, ConstructorProvider, ConstructorSansProvider, ExistingProvider, ExistingSansProvider, FactoryProvider, FactorySansProvider, StaticClassProvider, StaticClassSansProvider, ValueProvider, ValueSansProvider} from './interface/provider';
9+
import {makeParamDecorator} from '../util/decorators';
1510

1611

1712

packages/core/src/di/r3_injector.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,8 @@ import {stringify} from '../util/stringify';
1313
import {resolveForwardRef} from './forward_ref';
1414
import {InjectionToken} from './injection_token';
1515
import {INJECTOR, Injector, NullInjector, THROW_IF_NOT_FOUND, USE_VALUE} from './injector';
16-
import {InjectFlags, inject, injectArgs, setCurrentInjector} from './injector_compatibility';
17-
import {InjectableDef, InjectableType, InjectorType, InjectorTypeWithProviders, getInjectableDef, getInjectorDef} from './interface/defs';
18-
import {ClassProvider, ConstructorProvider, ExistingProvider, FactoryProvider, Provider, StaticClassProvider, StaticProvider, TypeProvider, ValueProvider} from './interface/provider';
16+
import {inject, injectArgs, setCurrentInjector} from './injector_compatibility';
17+
import {ClassProvider, ConstructorProvider, ExistingProvider, FactoryProvider, InjectFlags, InjectableDef, InjectableType, InjectorType, InjectorTypeWithProviders, Provider, StaticClassProvider, StaticProvider, TypeProvider, ValueProvider, getInjectableDef, getInjectorDef} from './interface';
1918
import {APP_ROOT} from './scope';
2019

2120

packages/core/src/di/reflective_injector.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
import {Injector, THROW_IF_NOT_FOUND} from './injector';
10-
import {Provider} from './interface/provider';
10+
import {Provider} from './interface';
1111
import {Self, SkipSelf} from './metadata';
1212
import {cyclicDependencyError, instantiationError, noProviderError, outOfBoundsError} from './reflective_errors';
1313
import {ReflectiveKey} from './reflective_key';

packages/core/src/di/reflective_provider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {reflector} from '../reflection/reflection';
1111

1212
import {resolveForwardRef} from './forward_ref';
1313
import {InjectionToken} from './injection_token';
14-
import {ClassProvider, ExistingProvider, FactoryProvider, Provider, TypeProvider, ValueProvider} from './interface/provider';
14+
import {ClassProvider, ExistingProvider, FactoryProvider, Provider, TypeProvider, ValueProvider} from './interface';
1515
import {Inject, Optional, Self, SkipSelf} from './metadata';
1616
import {invalidProviderError, mixingMultiProvidersWithRegularProvidersError, noAnnotationError} from './reflective_errors';
1717
import {ReflectiveKey} from './reflective_key';

packages/core/src/di/scope.ts

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

9-
import {Type} from '../interface/type';
109
import {InjectionToken} from './injection_token';
1110

1211

packages/core/src/di/util.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {ReflectionCapabilities} from '../reflection/reflection_capabilities';
1111
import {getClosureSafeProperty} from '../util/property';
1212

1313
import {inject, injectArgs} from './injector_compatibility';
14-
import {ClassSansProvider, ConstructorSansProvider, ExistingSansProvider, FactorySansProvider, StaticClassSansProvider, ValueProvider, ValueSansProvider} from './interface/provider';
14+
import {ClassSansProvider, ConstructorSansProvider, ExistingSansProvider, FactorySansProvider, StaticClassSansProvider, ValueProvider, ValueSansProvider} from './interface';
1515

1616
const USE_VALUE =
1717
getClosureSafeProperty<ValueProvider>({provide: String, useValue: getClosureSafeProperty});

packages/core/src/render/api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {injectRenderer2 as render3InjectRenderer2} from '../render3/view_engine_
1313
import {noop} from '../util/noop';
1414

1515

16+
1617
/**
1718
* @deprecated Use `RendererType2` (and `Renderer2`) instead.
1819
* @publicApi

packages/core/src/render3/di.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {InjectionToken} from '../di/injection_token';
9+
import {InjectFlags, InjectionToken} from '../di';
1010
import {Injector} from '../di/injector';
11-
import {InjectFlags, injectRootLimpMode, setInjectImplementation} from '../di/injector_compatibility';
11+
import {injectRootLimpMode, setInjectImplementation} from '../di/injector_compatibility';
1212
import {getInjectableDef, getInjectorDef} from '../di/interface/defs';
1313
import {Type} from '../interface/type';
14-
1514
import {assertDefined, assertEqual} from '../util/assert';
15+
1616
import {getComponentDef, getDirectiveDef, getPipeDef} from './definition';
1717
import {NG_ELEMENT_ID} from './fields';
1818
import {DirectiveDef} from './interfaces/definition';
@@ -286,6 +286,10 @@ export function injectAttributeImpl(tNode: TNode, attrNameToInject: string): str
286286
* Look for the injector providing the token by walking up the node injector tree and then
287287
* the module injector tree.
288288
*
289+
* This function patches `token` with `__NG_ELEMENT_ID__` which contains the id for the bloom
290+
* filter. Negative values are reserved for special objects.
291+
* - `-1` is reserved for injecting `Injector` (implemented by `NodeInjector`)
292+
*
289293
* @param tNode The Node where the search for the injector should start
290294
* @param lView The `LView` that contains the `tNode`
291295
* @param token The token to look for
@@ -316,6 +320,10 @@ export function getOrCreateInjectable<T>(
316320
setTNodeAndViewData(savePreviousOrParentTNode, saveLView);
317321
}
318322
} else if (typeof bloomHash == 'number') {
323+
if (bloomHash === -1) {
324+
// `-1` is a special value used to identify `Injector` types.
325+
return new NodeInjector(tNode, lView) as any;
326+
}
319327
// If the token has a bloom hash, then it is a token which could be in NodeInjector.
320328

321329
// A reference to the previous injector TView that was found while climbing the element
@@ -531,6 +539,7 @@ export function getNodeInjectable(
531539
*
532540
* @param token the injection token
533541
* @returns the matching bit to check in the bloom filter or `null` if the token is not known.
542+
* When the returned value is negative then it represents special values such as `Injector`.
534543
*/
535544
export function bloomHashBitOrFactory(token: Type<any>| InjectionToken<any>| string): number|
536545
Function|undefined {
@@ -539,7 +548,8 @@ export function bloomHashBitOrFactory(token: Type<any>| InjectionToken<any>| str
539548
return token.charCodeAt(0) || 0;
540549
}
541550
const tokenId: number|undefined = (token as any)[NG_ELEMENT_ID];
542-
return typeof tokenId === 'number' ? tokenId & BLOOM_MASK : tokenId;
551+
// Negative token IDs are used for special objects such as `Injector`
552+
return (typeof tokenId === 'number' && tokenId > 0) ? tokenId & BLOOM_MASK : tokenId;
543553
}
544554

545555
export function bloomHasToken(
@@ -575,11 +585,6 @@ function shouldSearchParent(flags: InjectFlags, isFirstHostTNode: boolean): bool
575585
return !(flags & InjectFlags.Self) && !(flags & InjectFlags.Host && isFirstHostTNode);
576586
}
577587

578-
export function injectInjector() {
579-
const tNode = getPreviousOrParentTNode() as TElementNode | TContainerNode | TElementContainerNode;
580-
return new NodeInjector(tNode, getLView());
581-
}
582-
583588
export class NodeInjector implements Injector {
584589
constructor(
585590
private _tNode: TElementNode|TContainerNode|TElementContainerNode|null,

packages/core/src/render3/fields.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ export const NG_BASE_DEF = getClosureSafeProperty({ngBaseDef: getClosureSafeProp
1919
* the key and the directive's unique ID as the value. This allows us to map directives to their
2020
* bloom filter bit for DI.
2121
*/
22+
// TODO(misko): This is wrong. The NG_ELEMENT_ID should never be minified.
2223
export const NG_ELEMENT_ID = getClosureSafeProperty({__NG_ELEMENT_ID__: getClosureSafeProperty});

0 commit comments

Comments
 (0)