diff --git a/goldens/circular-deps/packages.json b/goldens/circular-deps/packages.json
index 54ddcdca4f..275a539843 100644
--- a/goldens/circular-deps/packages.json
+++ b/goldens/circular-deps/packages.json
@@ -255,21 +255,6 @@
     "packages/core/src/render3/definition.ts",
     "packages/core/src/metadata/ng_module.ts"
   ],
-  [
-    "packages/core/src/application_ref.ts",
-    "packages/core/src/application_tokens.ts",
-    "packages/core/src/linker/component_factory.ts",
-    "packages/core/src/change_detection/change_detection.ts",
-    "packages/core/src/change_detection/change_detector_ref.ts",
-    "packages/core/src/render3/view_ref.ts",
-    "packages/core/src/linker/view_container_ref.ts",
-    "packages/core/src/render3/di.ts",
-    "packages/core/src/di/injector_compatibility.ts",
-    "packages/core/src/di/injector.ts",
-    "packages/core/src/di/r3_injector.ts",
-    "packages/core/src/render3/definition.ts",
-    "packages/core/src/metadata/ng_module.ts"
-  ],
   [
     "packages/core/src/application_ref.ts",
     "packages/core/src/application_tokens.ts",
@@ -641,16 +626,6 @@
     "packages/core/src/render3/definition.ts",
     "packages/core/src/metadata/ng_module.ts"
   ],
-  [
-    "packages/core/src/application_ref.ts",
-    "packages/core/src/linker/compiler.ts",
-    "packages/core/src/render3/ng_module_ref.ts",
-    "packages/core/src/di/injector_compatibility.ts",
-    "packages/core/src/di/injector.ts",
-    "packages/core/src/di/r3_injector.ts",
-    "packages/core/src/render3/definition.ts",
-    "packages/core/src/metadata/ng_module.ts"
-  ],
   [
     "packages/core/src/application_ref.ts",
     "packages/core/src/linker/compiler.ts",
@@ -786,20 +761,6 @@
     "packages/core/src/render3/definition.ts",
     "packages/core/src/metadata/ng_module.ts"
   ],
-  [
-    "packages/core/src/application_ref.ts",
-    "packages/core/src/metadata/resource_loading.ts",
-    "packages/core/src/metadata/directives.ts",
-    "packages/core/src/render3/jit/directive.ts",
-    "packages/core/src/render3/jit/environment.ts",
-    "packages/core/src/render3/index.ts",
-    "packages/core/src/render3/pipe.ts",
-    "packages/core/src/di/injector_compatibility.ts",
-    "packages/core/src/di/injector.ts",
-    "packages/core/src/di/r3_injector.ts",
-    "packages/core/src/render3/definition.ts",
-    "packages/core/src/metadata/ng_module.ts"
-  ],
   [
     "packages/core/src/application_ref.ts",
     "packages/core/src/metadata/resource_loading.ts",
@@ -900,6 +861,16 @@
     "packages/core/src/view/entrypoint.ts",
     "packages/core/src/view/services.ts"
   ],
+  [
+    "packages/core/src/di.ts",
+    "packages/core/src/di/index.ts",
+    "packages/core/src/di/injectable.ts",
+    "packages/core/src/di/jit/injectable.ts",
+    "packages/core/src/di/jit/environment.ts",
+    "packages/core/src/di/injector_compatibility.ts",
+    "packages/core/src/di/injector.ts",
+    "packages/core/src/di/null_injector.ts"
+  ],
   [
     "packages/core/src/di/injectable.ts",
     "packages/core/src/di/jit/injectable.ts"
@@ -908,6 +879,11 @@
     "packages/core/src/di/injector_compatibility.ts",
     "packages/core/src/di/injector.ts"
   ],
+  [
+    "packages/core/src/di/injector_compatibility.ts",
+    "packages/core/src/di/injector.ts",
+    "packages/core/src/di/null_injector.ts"
+  ],
   [
     "packages/core/src/di/injector_compatibility.ts",
     "packages/core/src/di/injector.ts",
@@ -921,6 +897,10 @@
     "packages/core/src/metadata/ng_module.ts",
     "packages/core/src/di/util.ts"
   ],
+  [
+    "packages/core/src/di/injector_token.ts",
+    "packages/core/src/di/injector.ts"
+  ],
   [
     "packages/core/src/di/injector.ts",
     "packages/core/src/di/r3_injector.ts"
diff --git a/goldens/public-api/core/core.d.ts b/goldens/public-api/core/core.d.ts
index e2a87e69cf..5a861d8adb 100644
--- a/goldens/public-api/core/core.d.ts
+++ b/goldens/public-api/core/core.d.ts
@@ -837,11 +837,11 @@ export declare interface RendererType2 {
 }
 
 export declare class ResolvedReflectiveFactory {
-    dependencies: ɵangular_packages_core_core_e[];
+    dependencies: ɵangular_packages_core_core_d[];
     factory: Function;
     constructor(
     factory: Function,
-    dependencies: ɵangular_packages_core_core_e[]);
+    dependencies: ɵangular_packages_core_core_d[]);
 }
 
 export declare interface ResolvedReflectiveProvider {
diff --git a/goldens/size-tracking/aio-payloads.json b/goldens/size-tracking/aio-payloads.json
index 716bf6c5e4..f2432d0dec 100755
--- a/goldens/size-tracking/aio-payloads.json
+++ b/goldens/size-tracking/aio-payloads.json
@@ -12,7 +12,7 @@
     "master": {
       "uncompressed": {
         "runtime-es2015": 3037,
-        "main-es2015": 449483,
+        "main-es2015": 448796,
         "polyfills-es2015": 52415
       }
     }
diff --git a/goldens/size-tracking/integration-payloads.json b/goldens/size-tracking/integration-payloads.json
index 292c6782b5..f00ac8b847 100644
--- a/goldens/size-tracking/integration-payloads.json
+++ b/goldens/size-tracking/integration-payloads.json
@@ -21,7 +21,7 @@
     "master": {
       "uncompressed": {
         "runtime-es2015": 1485,
-        "main-es2015": 147252,
+        "main-es2015": 146680,
         "polyfills-es2015": 36964
       }
     }
diff --git a/packages/core/src/di/index.ts b/packages/core/src/di/index.ts
index 1f10512092..845757d5d6 100644
--- a/packages/core/src/di/index.ts
+++ b/packages/core/src/di/index.ts
@@ -18,7 +18,8 @@ export {ɵɵdefineInjectable, defineInjectable, ɵɵdefineInjector, InjectableTy
 export {forwardRef, resolveForwardRef, ForwardRefFn} from './forward_ref';
 export {Injectable, InjectableDecorator, InjectableProvider} from './injectable';
 export {Injector} from './injector';
-export {ɵɵinject, inject, INJECTOR, ɵɵinvalidFactoryDep} from './injector_compatibility';
+export {ɵɵinject, inject, ɵɵinvalidFactoryDep} from './injector_compatibility';
+export {INJECTOR} from './injector_token';
 export {ReflectiveInjector} from './reflective_injector';
 export {ClassProvider, ClassSansProvider, ConstructorProvider, ConstructorSansProvider, ExistingProvider, ExistingSansProvider, FactoryProvider, FactorySansProvider, Provider, StaticClassProvider, StaticClassSansProvider, StaticProvider, TypeProvider, ValueProvider, ValueSansProvider} from './interface/provider';
 export {ResolvedReflectiveFactory, ResolvedReflectiveProvider} from './reflective_provider';
diff --git a/packages/core/src/di/inject_switch.ts b/packages/core/src/di/inject_switch.ts
new file mode 100644
index 0000000000..bc5527566e
--- /dev/null
+++ b/packages/core/src/di/inject_switch.ts
@@ -0,0 +1,76 @@
+/**
+ * @license
+ * Copyright Google LLC All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+import {Type} from '../interface/type';
+import {assertNotEqual} from '../util/assert';
+import {stringify} from '../util/stringify';
+import {InjectionToken} from './injection_token';
+import {getInjectableDef, ɵɵInjectableDef} from './interface/defs';
+import {InjectFlags} from './interface/injector';
+
+
+/**
+ * Current implementation of inject.
+ *
+ * By default, it is `injectInjectorOnly`, which makes it `Injector`-only aware. It can be changed
+ * to `directiveInject`, which brings in the `NodeInjector` system of ivy. It is designed this
+ * way for two reasons:
+ *  1. `Injector` should not depend on ivy logic.
+ *  2. To maintain tree shake-ability we don't want to bring in unnecessary code.
+ */
+let _injectImplementation: (<T>(token: Type<T>|InjectionToken<T>, flags?: InjectFlags) => T | null)|
+    undefined;
+export function getInjectImplementation() {
+  return _injectImplementation;
+}
+
+
+/**
+ * Sets the current inject implementation.
+ */
+export function setInjectImplementation(
+    impl: (<T>(token: Type<T>|InjectionToken<T>, flags?: InjectFlags) => T | null)|
+    undefined): (<T>(token: Type<T>|InjectionToken<T>, flags?: InjectFlags) => T | null)|undefined {
+  const previous = _injectImplementation;
+  _injectImplementation = impl;
+  return previous;
+}
+
+
+/**
+ * Injects `root` tokens in limp mode.
+ *
+ * If no injector exists, we can still inject tree-shakable providers which have `providedIn` set to
+ * `"root"`. This is known as the limp mode injection. In such case the value is stored in the
+ * `InjectableDef`.
+ */
+export function injectRootLimpMode<T>(
+    token: Type<T>|InjectionToken<T>, notFoundValue: T|undefined, flags: InjectFlags): T|null {
+  const injectableDef: ɵɵInjectableDef<T>|null = getInjectableDef(token);
+  if (injectableDef && injectableDef.providedIn == 'root') {
+    return injectableDef.value === undefined ? injectableDef.value = injectableDef.factory() :
+                                               injectableDef.value;
+  }
+  if (flags & InjectFlags.Optional) return null;
+  if (notFoundValue !== undefined) return notFoundValue;
+  throw new Error(`Injector: NOT_FOUND [${stringify(token)}]`);
+}
+
+
+/**
+ * Assert that `_injectImplementation` is not `fn`.
+ *
+ * This is useful, to prevent infinite recursion.
+ *
+ * @param fn Function which it should not equal to
+ */
+export function assertInjectImplementationNotEqual(
+    fn: (<T>(token: Type<T>|InjectionToken<T>, flags?: InjectFlags) => T | null)) {
+  ngDevMode &&
+      assertNotEqual(_injectImplementation, fn, 'Calling ɵɵinject would cause infinite recursion');
+}
diff --git a/packages/core/src/di/injection_token.ts b/packages/core/src/di/injection_token.ts
index 9abf11a849..f8b26f7eab 100644
--- a/packages/core/src/di/injection_token.ts
+++ b/packages/core/src/di/injection_token.ts
@@ -7,6 +7,7 @@
  */
 
 import {Type} from '../interface/type';
+import {assertLessThan} from '../util/assert';
 
 import {ɵɵdefineInjectable} from './interface/defs';
 
@@ -61,9 +62,10 @@ export class InjectionToken<T> {
   }) {
     this.ɵprov = undefined;
     if (typeof options == 'number') {
+      (typeof ngDevMode === 'undefined' || ngDevMode) &&
+          assertLessThan(options, 0, 'Only negative numbers are supported here');
       // This is a special hack to assign __NG_ELEMENT_ID__ to this instance.
-      // __NG_ELEMENT_ID__ is Used by Ivy to determine bloom filter id.
-      // We are using it to assign `-1` which is used to identify `Injector`.
+      // See `InjectorMarkers`
       (this as any).__NG_ELEMENT_ID__ = options;
     } else if (options !== undefined) {
       this.ɵprov = ɵɵdefineInjectable({
diff --git a/packages/core/src/di/injector.ts b/packages/core/src/di/injector.ts
index f8c93c42a0..5fda8bfbf1 100644
--- a/packages/core/src/di/injector.ts
+++ b/packages/core/src/di/injector.ts
@@ -8,14 +8,16 @@
 
 import {AbstractType, Type} from '../interface/type';
 import {stringify} from '../util/stringify';
-
 import {resolveForwardRef} from './forward_ref';
 import {InjectionToken} from './injection_token';
-import {catchInjectorError, formatError, INJECTOR, NG_TEMP_TOKEN_PATH, NullInjector, setCurrentInjector, THROW_IF_NOT_FOUND, USE_VALUE, ɵɵinject} from './injector_compatibility';
+import {catchInjectorError, formatError, NG_TEMP_TOKEN_PATH, setCurrentInjector, THROW_IF_NOT_FOUND, USE_VALUE, ɵɵinject} from './injector_compatibility';
+import {InjectorMarkers} from './injector_marker';
+import {INJECTOR} from './injector_token';
 import {getInjectableDef, ɵɵdefineInjectable} from './interface/defs';
 import {InjectFlags} from './interface/injector';
 import {ConstructorProvider, ExistingProvider, FactoryProvider, StaticClassProvider, StaticProvider, ValueProvider} from './interface/provider';
 import {Inject, Optional, Self, SkipSelf} from './metadata';
+import {NullInjector} from './null_injector';
 import {createInjector} from './r3_injector';
 import {INJECTOR_SCOPE} from './scope';
 
@@ -113,7 +115,7 @@ export abstract class Injector {
    * @internal
    * @nocollapse
    */
-  static __NG_ELEMENT_ID__ = -1;
+  static __NG_ELEMENT_ID__ = InjectorMarkers.Injector;
 }
 
 
diff --git a/packages/core/src/di/injector_compatibility.ts b/packages/core/src/di/injector_compatibility.ts
index fde76e1801..249ce8b992 100644
--- a/packages/core/src/di/injector_compatibility.ts
+++ b/packages/core/src/di/injector_compatibility.ts
@@ -14,28 +14,14 @@ import {getClosureSafeProperty} from '../util/property';
 import {stringify} from '../util/stringify';
 
 import {resolveForwardRef} from './forward_ref';
+import {getInjectImplementation, injectRootLimpMode} from './inject_switch';
 import {InjectionToken} from './injection_token';
 import {Injector} from './injector';
-import {getInjectableDef, ɵɵInjectableDef} from './interface/defs';
 import {InjectFlags} from './interface/injector';
 import {ValueProvider} from './interface/provider';
 import {Inject, Optional, Self, SkipSelf} from './metadata';
 
 
-
-/**
- * An InjectionToken that gets the current `Injector` for `createInjector()`-style injectors.
- *
- * Requesting this token instead of `Injector` allows `StaticInjector` to be tree-shaken from a
- * project.
- *
- * @publicApi
- */
-export const INJECTOR = new InjectionToken<Injector>(
-    'INJECTOR',
-    -1 as any  // `-1` is used by Ivy DI system as special value to recognize it as `Injector`.
-);
-
 const _THROW_IF_NOT_FOUND = {};
 export const THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND;
 
@@ -62,41 +48,6 @@ export function setCurrentInjector(injector: Injector|null|undefined): Injector|
   return former;
 }
 
-/**
- * Current implementation of inject.
- *
- * By default, it is `injectInjectorOnly`, which makes it `Injector`-only aware. It can be changed
- * to `directiveInject`, which brings in the `NodeInjector` system of ivy. It is designed this
- * way for two reasons:
- *  1. `Injector` should not depend on ivy logic.
- *  2. To maintain tree shake-ability we don't want to bring in unnecessary code.
- */
-let _injectImplementation: (<T>(token: Type<T>|InjectionToken<T>, flags?: InjectFlags) => T | null)|
-    undefined;
-
-/**
- * Sets the current inject implementation.
- */
-export function setInjectImplementation(
-    impl: (<T>(token: Type<T>|InjectionToken<T>, flags?: InjectFlags) => T | null)|
-    undefined): (<T>(token: Type<T>|InjectionToken<T>, flags?: InjectFlags) => T | null)|undefined {
-  const previous = _injectImplementation;
-  _injectImplementation = impl;
-  return previous;
-}
-
-/**
- * Assert that `_injectImplementation` is not `fn`.
- *
- * This is useful, to prevent infinite recursion.
- *
- * @param fn Function which it should not equal to
- */
-export function assertInjectImplementationNot(
-    fn: (<T>(token: Type<T>|InjectionToken<T>, flags?: InjectFlags) => T | null)) {
-  ngDevMode &&
-      assertNotEqual(_injectImplementation, fn, 'Calling ɵɵinject would cause infinite recursion');
-}
 
 export function injectInjectorOnly<T>(token: Type<T>|InjectionToken<T>): T;
 export function injectInjectorOnly<T>(token: Type<T>|InjectionToken<T>, flags?: InjectFlags): T|
@@ -128,7 +79,7 @@ export function injectInjectorOnly<T>(
 export function ɵɵinject<T>(token: Type<T>|InjectionToken<T>): T;
 export function ɵɵinject<T>(token: Type<T>|InjectionToken<T>, flags?: InjectFlags): T|null;
 export function ɵɵinject<T>(token: Type<T>|InjectionToken<T>, flags = InjectFlags.Default): T|null {
-  return (_injectImplementation || injectInjectorOnly)(resolveForwardRef(token), flags);
+  return (getInjectImplementation() || injectInjectorOnly)(resolveForwardRef(token), flags);
 }
 
 /**
@@ -181,24 +132,6 @@ Please check that 1) the type for the parameter at index ${
  */
 export const inject = ɵɵinject;
 
-/**
- * Injects `root` tokens in limp mode.
- *
- * If no injector exists, we can still inject tree-shakable providers which have `providedIn` set to
- * `"root"`. This is known as the limp mode injection. In such case the value is stored in the
- * `InjectableDef`.
- */
-export function injectRootLimpMode<T>(
-    token: Type<T>|InjectionToken<T>, notFoundValue: T|undefined, flags: InjectFlags): T|null {
-  const injectableDef: ɵɵInjectableDef<T>|null = getInjectableDef(token);
-  if (injectableDef && injectableDef.providedIn == 'root') {
-    return injectableDef.value === undefined ? injectableDef.value = injectableDef.factory() :
-                                               injectableDef.value;
-  }
-  if (flags & InjectFlags.Optional) return null;
-  if (notFoundValue !== undefined) return notFoundValue;
-  throw new Error(`Injector: NOT_FOUND [${stringify(token)}]`);
-}
 
 export function injectArgs(types: (Type<any>|InjectionToken<any>|any[])[]): any[] {
   const args: any[] = [];
@@ -236,22 +169,6 @@ export function injectArgs(types: (Type<any>|InjectionToken<any>|any[])[]): any[
 }
 
 
-export class NullInjector implements Injector {
-  get(token: any, notFoundValue: any = THROW_IF_NOT_FOUND): any {
-    if (notFoundValue === THROW_IF_NOT_FOUND) {
-      // Intentionally left behind: With dev tools open the debugger will stop here. There is no
-      // reason why correctly written application should cause this exception.
-      // TODO(misko): uncomment the next line once `ngDevMode` works with closure.
-      // if (ngDevMode) debugger;
-      const error = new Error(`NullInjectorError: No provider for ${stringify(token)}!`);
-      error.name = 'NullInjectorError';
-      throw error;
-    }
-    return notFoundValue;
-  }
-}
-
-
 export function catchInjectorError(
     e: any, token: any, injectorErrorName: string, source: string|null): never {
   const tokenPath: any[] = e[NG_TEMP_TOKEN_PATH];
diff --git a/packages/core/src/di/injector_marker.ts b/packages/core/src/di/injector_marker.ts
new file mode 100644
index 0000000000..ff13f93b72
--- /dev/null
+++ b/packages/core/src/di/injector_marker.ts
@@ -0,0 +1,24 @@
+/**
+ * @license
+ * Copyright Google LLC All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+/**
+ * Special markers which can be left on `Type.__NG_ELEMENT_ID__` which are used by the Ivy's
+ * `NodeInjector`. Usually these markers contain factory functions. But in case of this special
+ * marker we can't leave behind a function because it would create tree shaking problem.
+ *
+ * Currently only `Injector` is special.
+ *
+ * NOTE: the numbers here must be negative, because positive numbers are used as IDs for bloom
+ * filter.
+ */
+export const enum InjectorMarkers {
+  /**
+   * Marks that the current type is `Injector`
+   */
+  Injector = -1
+}
\ No newline at end of file
diff --git a/packages/core/src/di/injector_token.ts b/packages/core/src/di/injector_token.ts
new file mode 100644
index 0000000000..b84fc4a261
--- /dev/null
+++ b/packages/core/src/di/injector_token.ts
@@ -0,0 +1,28 @@
+/**
+ * @license
+ * Copyright Google LLC All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+import {InjectionToken} from './injection_token';
+import {Injector} from './injector';
+import {InjectorMarkers} from './injector_marker';
+
+
+
+/**
+ * An InjectionToken that gets the current `Injector` for `createInjector()`-style injectors.
+ *
+ * Requesting this token instead of `Injector` allows `StaticInjector` to be tree-shaken from a
+ * project.
+ *
+ * @publicApi
+ */
+export const INJECTOR = new InjectionToken<Injector>(
+    'INJECTOR',
+    // Dissable tslint because this is const enum which gets inlined not top level prop access.
+    // tslint:disable-next-line: no-toplevel-property-access
+    InjectorMarkers.Injector as any,  // Special value used by Ivy to identify `Injector`.
+);
diff --git a/packages/core/src/di/null_injector.ts b/packages/core/src/di/null_injector.ts
new file mode 100644
index 0000000000..fb889c92c9
--- /dev/null
+++ b/packages/core/src/di/null_injector.ts
@@ -0,0 +1,23 @@
+/**
+ * @license
+ * Copyright Google LLC All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+import {stringify} from '../util/stringify';
+import {Injector} from '.';
+import {THROW_IF_NOT_FOUND} from './injector_compatibility';
+
+
+export class NullInjector implements Injector {
+  get(token: any, notFoundValue: any = THROW_IF_NOT_FOUND): any {
+    if (notFoundValue === THROW_IF_NOT_FOUND) {
+      const error = new Error(`NullInjectorError: No provider for ${stringify(token)}!`);
+      error.name = 'NullInjectorError';
+      throw error;
+    }
+    return notFoundValue;
+  }
+}
diff --git a/packages/core/src/di/r3_injector.ts b/packages/core/src/di/r3_injector.ts
index 1c147e622e..53823799d6 100644
--- a/packages/core/src/di/r3_injector.ts
+++ b/packages/core/src/di/r3_injector.ts
@@ -19,10 +19,12 @@ import {stringify} from '../util/stringify';
 import {resolveForwardRef} from './forward_ref';
 import {InjectionToken} from './injection_token';
 import {Injector} from './injector';
-import {catchInjectorError, injectArgs, INJECTOR, NG_TEMP_TOKEN_PATH, NullInjector, setCurrentInjector, THROW_IF_NOT_FOUND, USE_VALUE, ɵɵinject} from './injector_compatibility';
+import {catchInjectorError, injectArgs, NG_TEMP_TOKEN_PATH, setCurrentInjector, THROW_IF_NOT_FOUND, USE_VALUE, ɵɵinject} from './injector_compatibility';
+import {INJECTOR} from './injector_token';
 import {getInheritedInjectableDef, getInjectableDef, getInjectorDef, InjectorType, InjectorTypeWithProviders, ɵɵInjectableDef} from './interface/defs';
 import {InjectFlags} from './interface/injector';
 import {ClassProvider, ConstructorProvider, ExistingProvider, FactoryProvider, StaticClassProvider, StaticProvider, TypeProvider, ValueProvider} from './interface/provider';
+import {NullInjector} from './null_injector';
 import {INJECTOR_SCOPE} from './scope';
 
 
diff --git a/packages/core/src/render3/di.ts b/packages/core/src/render3/di.ts
index 5e0fc857df..5f4fd9054a 100644
--- a/packages/core/src/render3/di.ts
+++ b/packages/core/src/render3/di.ts
@@ -7,9 +7,10 @@
  */
 
 import {isForwardRef, resolveForwardRef} from '../di/forward_ref';
+import {injectRootLimpMode, setInjectImplementation} from '../di/inject_switch';
 import {InjectionToken} from '../di/injection_token';
 import {Injector} from '../di/injector';
-import {injectRootLimpMode, setInjectImplementation} from '../di/injector_compatibility';
+import {InjectorMarkers} from '../di/injector_marker';
 import {getInjectorDef} from '../di/interface/defs';
 import {InjectFlags} from '../di/interface/injector';
 import {Type} from '../interface/type';
@@ -415,8 +416,11 @@ export function getOrCreateInjectable<T>(
     // so just call the factory function to create it.
     if (typeof bloomHash === 'function') {
       if (!enterDI(lView, tNode, flags)) {
-        // Failed to enter DI use module injector instead.
-        return lookupTokenUsingModuleInjector<T>(lView, token, flags, notFoundValue);
+        // Failed to enter DI, try module injector instead. If a token is injected with the @Host
+        // flag, the module injector is not searched for that token in Ivy.
+        return (flags & InjectFlags.Host) ?
+            notFoundValueOrThrow<T>(notFoundValue, token, flags) :
+            lookupTokenUsingModuleInjector<T>(lView, token, flags, notFoundValue);
       }
       try {
         const value = bloomHash();
@@ -429,32 +433,6 @@ export function getOrCreateInjectable<T>(
         leaveDI();
       }
     } else if (typeof bloomHash === 'number') {
-      // This is a value used to identify __NG_ELEMENT_ID__
-      // `-1` is a special value used to identify `Injector` types in NodeInjector
-      // This is a workaround for the fact that if the `Injector.__NG_ELEMENT_ID__`
-      // would have a factory function (such as `ElementRef`) it would cause Ivy
-      // to be pulled into the ViewEngine, because they both share `Injector` type.
-      // This should be refactored to follow `ElementRef` pattern once ViewEngine is
-      // removed
-      if (bloomHash === -1) {
-        if (!enterDI(lView, tNode, flags)) {
-          // Failed to enter DI, try module injector instead. If a token is injected with the @Host
-          // flag, the module injector is not searched for that token in Ivy.
-          return (flags & InjectFlags.Host) ?
-              notFoundValueOrThrow<T>(notFoundValue, token, flags) :
-              lookupTokenUsingModuleInjector<T>(lView, token, flags, notFoundValue);
-        }
-        try {
-          // Retrieving current `TNode` and `LView` from the state (rather than using `tNode` and
-          // `lView`), because entering DI (by calling `enterDI`) may cause these values to change
-          // (in case `@SkipSelf` flag is present).
-          return new NodeInjector(getCurrentTNode()! as TDirectiveHostNode, getLView()) as any;
-        } finally {
-          leaveDI();
-        }
-      }
-      // If the token has a bloom hash, then it is a token which could be in NodeInjector.
-
       // A reference to the previous injector TView that was found while climbing the element
       // injector tree. This is used to know if viewProviders can be accessed on the current
       // injector.
@@ -525,6 +503,10 @@ export function getOrCreateInjectable<T>(
 
 const NOT_FOUND = {};
 
+export function createNodeInjector(): Injector {
+  return new NodeInjector(getCurrentTNode()! as TDirectiveHostNode, getLView()) as any;
+}
+
 function searchTokensOnInjector<T>(
     injectorIndex: number, lView: LView, token: Type<T>|InjectionToken<T>,
     previousTView: TView|null, flags: InjectFlags, hostTElementNode: TNode|null) {
@@ -674,7 +656,17 @@ export function bloomHashBitOrFactory(token: Type<any>|InjectionToken<any>|strin
       // First check with `hasOwnProperty` so we don't get an inherited ID.
       token.hasOwnProperty(NG_ELEMENT_ID) ? (token as any)[NG_ELEMENT_ID] : undefined;
   // Negative token IDs are used for special objects such as `Injector`
-  return (typeof tokenId === 'number' && tokenId > 0) ? tokenId & BLOOM_MASK : tokenId;
+  if (typeof tokenId === 'number') {
+    if (tokenId >= 0) {
+      return tokenId & BLOOM_MASK;
+    } else {
+      ngDevMode &&
+          assertEqual(tokenId, InjectorMarkers.Injector, 'Expecting to get Special Injector Id');
+      return createNodeInjector;
+    }
+  } else {
+    return tokenId;
+  }
 }
 
 export function bloomHasToken(bloomHash: number, injectorIndex: number, injectorView: LView|TData) {
diff --git a/packages/core/src/render3/instructions/di.ts b/packages/core/src/render3/instructions/di.ts
index 1868e1868c..bb08814e5a 100644
--- a/packages/core/src/render3/instructions/di.ts
+++ b/packages/core/src/render3/instructions/di.ts
@@ -6,7 +6,8 @@
  * found in the LICENSE file at https://angular.io/license
  */
 import {InjectFlags, InjectionToken, resolveForwardRef} from '../../di';
-import {assertInjectImplementationNot, ɵɵinject} from '../../di/injector_compatibility';
+import {assertInjectImplementationNotEqual} from '../../di/inject_switch';
+import {ɵɵinject} from '../../di/injector_compatibility';
 import {Type} from '../../interface/type';
 import {getOrCreateInjectable, injectAttributeImpl} from '../di';
 import {TDirectiveHostNode} from '../interfaces/node';
@@ -45,7 +46,7 @@ export function ɵɵdirectiveInject<T>(
   // if inject utilities are used before bootstrapping.
   if (lView === null) {
     // Verify that we will not get into infinite loop.
-    ngDevMode && assertInjectImplementationNot(ɵɵdirectiveInject);
+    ngDevMode && assertInjectImplementationNotEqual(ɵɵdirectiveInject);
     return ɵɵinject(token, flags);
   }
   const tNode = getCurrentTNode();
diff --git a/packages/core/src/render3/ng_module_ref.ts b/packages/core/src/render3/ng_module_ref.ts
index 4cb7227c6f..a388e68aa8 100644
--- a/packages/core/src/render3/ng_module_ref.ts
+++ b/packages/core/src/render3/ng_module_ref.ts
@@ -7,7 +7,7 @@
  */
 
 import {Injector} from '../di/injector';
-import {INJECTOR} from '../di/injector_compatibility';
+import {INJECTOR} from '../di/injector_token';
 import {InjectFlags} from '../di/interface/injector';
 import {createInjectorWithoutInjectorInstances, R3Injector} from '../di/r3_injector';
 import {Type} from '../interface/type';
diff --git a/packages/core/src/render3/pipe.ts b/packages/core/src/render3/pipe.ts
index e9fb78a1ae..148e795184 100644
--- a/packages/core/src/render3/pipe.ts
+++ b/packages/core/src/render3/pipe.ts
@@ -8,8 +8,7 @@
 
 import {WrappedValue} from '../change_detection/change_detection_util';
 import {PipeTransform} from '../change_detection/pipe_transform';
-import {setInjectImplementation} from '../di/injector_compatibility';
-
+import {setInjectImplementation} from '../di/inject_switch';
 import {getFactoryDef} from './definition';
 import {setIncludeViewProviders} from './di';
 import {RuntimeError, RuntimeErrorCode} from './error_code';
diff --git a/packages/core/src/view/ng_module.ts b/packages/core/src/view/ng_module.ts
index ac46b4921e..5a414d4548 100644
--- a/packages/core/src/view/ng_module.ts
+++ b/packages/core/src/view/ng_module.ts
@@ -8,7 +8,8 @@
 
 import {resolveForwardRef} from '../di/forward_ref';
 import {Injector} from '../di/injector';
-import {INJECTOR, setCurrentInjector} from '../di/injector_compatibility';
+import {setCurrentInjector} from '../di/injector_compatibility';
+import {INJECTOR} from '../di/injector_token';
 import {getInjectableDef, ɵɵInjectableDef} from '../di/interface/defs';
 import {INJECTOR_SCOPE} from '../di/scope';
 import {NgModuleRef} from '../linker/ng_module_factory';
diff --git a/packages/core/test/bundling/forms/bundle.golden_symbols.json b/packages/core/test/bundling/forms/bundle.golden_symbols.json
index 6325a87e2c..14d53f82a3 100644
--- a/packages/core/test/bundling/forms/bundle.golden_symbols.json
+++ b/packages/core/test/bundling/forms/bundle.golden_symbols.json
@@ -833,6 +833,9 @@
   {
     "name": "createLView"
   },
+  {
+    "name": "createNodeInjector"
+  },
   {
     "name": "createPlatformFactory"
   },
diff --git a/packages/core/test/bundling/router/bundle.golden_symbols.json b/packages/core/test/bundling/router/bundle.golden_symbols.json
index c00bdbf7b8..0f0af28545 100644
--- a/packages/core/test/bundling/router/bundle.golden_symbols.json
+++ b/packages/core/test/bundling/router/bundle.golden_symbols.json
@@ -1073,6 +1073,9 @@
   {
     "name": "createNewSegmentGroup"
   },
+  {
+    "name": "createNodeInjector"
+  },
   {
     "name": "createPlatformFactory"
   },
diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json
index ada284be31..64b81583e2 100644
--- a/packages/core/test/bundling/todo/bundle.golden_symbols.json
+++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json
@@ -272,6 +272,9 @@
   {
     "name": "createLView"
   },
+  {
+    "name": "createNodeInjector"
+  },
   {
     "name": "createTView"
   },
diff --git a/packages/core/test/view/ng_module_spec.ts b/packages/core/test/view/ng_module_spec.ts
index a139047e8a..937568cfb6 100644
--- a/packages/core/test/view/ng_module_spec.ts
+++ b/packages/core/test/view/ng_module_spec.ts
@@ -9,7 +9,7 @@
 import {NgModuleRef, ɵINJECTOR_SCOPE as INJECTOR_SCOPE} from '@angular/core';
 import {inject, InjectFlags} from '@angular/core/src/di';
 import {Injector} from '@angular/core/src/di/injector';
-import {INJECTOR} from '@angular/core/src/di/injector_compatibility';
+import {INJECTOR} from '@angular/core/src/di/injector_token';
 import {ɵɵdefineInjectable, ɵɵInjectableDef} from '@angular/core/src/di/interface/defs';
 import {NgModuleDefinition, NgModuleProviderDef, NodeFlags} from '@angular/core/src/view';
 import {moduleDef} from '@angular/core/src/view/ng_module';