Skip to content

Commit

Permalink
feat(ivy): add ngcc ivy switch (#25238)
Browse files Browse the repository at this point in the history
Provides a runtime and compile time switch for ivy including
`ApplicationRef.bootstrapModule`.

This is done by naming the symbols such that `ngcc` (angular
Compatibility compiler) can rename symbols in such a way that running
`ngcc` command will switch the `@angular/core` module from `legacy` to
`ivy` mode.

This is done as follows:

```
const someToken__PRE_NGCC__ = ‘legacy mode’;
const someToken__POST_NGCC__ = ‘ivy mode’;

export someSymbol = someToken__PRE_NGCC__;
```

The `ngcc` will search for any token which ends with `__PRE_NGCC__`
and replace it with `__POST_NGCC__`. This allows the `@angular/core`
package to be rewritten to ivy mode post `ngcc` execution.

PR Close #25238
  • Loading branch information
mhevery authored and jasonaden committed Aug 16, 2018
1 parent cc55d60 commit 503905c
Show file tree
Hide file tree
Showing 23 changed files with 297 additions and 200 deletions.
4 changes: 3 additions & 1 deletion packages/compiler/test/schema/schema_extractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ const MISSING_FROM_CHROME: {[el: string]: string[]} = {
':svg:cursor^:svg:': [],
};

const _G: any = global;
const _G: any = typeof window != 'undefined' && window || typeof global != 'undefined' && global ||
typeof self != 'undefined' && self;

const document: any = typeof _G['document'] == 'object' ? _G['document'] : null;

export function extractSchema(): Map<string, string[]>|null {
Expand Down
64 changes: 26 additions & 38 deletions packages/core/src/application_ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,60 +9,51 @@
import {Observable, Observer, Subscription, merge} from 'rxjs';
import {share} from 'rxjs/operators';

import {ErrorHandler} from '../src/error_handler';
import {scheduleMicroTask, stringify} from '../src/util';
import {isPromise} from '../src/util/lang';

import {ApplicationInitStatus} from './application_init';
import {APP_BOOTSTRAP_LISTENER, PLATFORM_INITIALIZER} from './application_tokens';
import {Console} from './console';
import {Injectable, InjectionToken, Injector, StaticProvider} from './di';
import {ErrorHandler} from './error_handler';
import {isDevMode} from './is_dev_mode';
import {CompilerFactory, CompilerOptions} from './linker/compiler';
import {ComponentFactory, ComponentRef} from './linker/component_factory';
import {ComponentFactoryBoundToModule, ComponentFactoryResolver} from './linker/component_factory_resolver';
import {InternalNgModuleRef, NgModuleFactory, NgModuleRef} from './linker/ng_module_factory';
import {InternalViewRef, ViewRef} from './linker/view_ref';
import {WtfScopeFn, wtfCreateScope, wtfLeave} from './profile/profile';
import {assertNgModuleType} from './render3/assert';
import {NgModuleFactory as R3NgModuleFactory} from './render3/ng_module_ref';
import {Testability, TestabilityRegistry} from './testability/testability';
import {Type} from './type';
import {scheduleMicroTask, stringify} from './util';
import {isPromise} from './util/lang';
import {NgZone, NoopNgZone} from './zone/ng_zone';

let _devMode: boolean = true;
let _runModeLocked: boolean = false;
let _platform: PlatformRef;

export const ALLOW_MULTIPLE_PLATFORMS = new InjectionToken<boolean>('AllowMultipleToken');
let compileNgModuleFactory:
<M>(injector: Injector, options: CompilerOptions, moduleType: Type<M>) =>
Promise<NgModuleFactory<M>> = compileNgModuleFactory__PRE_NGCC__;

/**
* Disable Angular's development mode, which turns off assertions and other
* checks within the framework.
*
* One important assertion this disables verifies that a change detection pass
* does not result in additional changes to any bindings (also known as
* unidirectional data flow).
*
*
*/
export function enableProdMode(): void {
if (_runModeLocked) {
throw new Error('Cannot enable prod mode after platform setup.');
}
_devMode = false;
function compileNgModuleFactory__PRE_NGCC__<M>(
injector: Injector, options: CompilerOptions,
moduleType: Type<M>): Promise<NgModuleFactory<M>> {
const compilerFactory: CompilerFactory = injector.get(CompilerFactory);
const compiler = compilerFactory.createCompiler([options]);
return compiler.compileModuleAsync(moduleType);
}

/**
* Returns whether Angular is in development mode. After called once,
* the value is locked and won't change any more.
*
* By default, this is true, unless a user calls `enableProdMode` before calling this.
*
* @experimental APIs related to application bootstrap are currently under review.
*/
export function isDevMode(): boolean {
_runModeLocked = true;
return _devMode;
function compileNgModuleFactory__POST_NGCC__<M>(
injector: Injector, options: CompilerOptions,
moduleType: Type<M>): Promise<NgModuleFactory<M>> {
ngDevMode && assertNgModuleType(moduleType);
return Promise.resolve(new R3NgModuleFactory(moduleType));
}

export const ALLOW_MULTIPLE_PLATFORMS = new InjectionToken<boolean>('AllowMultipleToken');



/**
* A token for third-party components that can register themselves with NgProbe.
*
Expand Down Expand Up @@ -267,12 +258,9 @@ export class PlatformRef {
bootstrapModule<M>(
moduleType: Type<M>, compilerOptions: (CompilerOptions&BootstrapOptions)|
Array<CompilerOptions&BootstrapOptions> = []): Promise<NgModuleRef<M>> {
const compilerFactory: CompilerFactory = this.injector.get(CompilerFactory);
const options = optionsReducer({}, compilerOptions);
const compiler = compilerFactory.createCompiler([options]);

return compiler.compileModuleAsync(moduleType)
.then((moduleFactory) => this.bootstrapModuleFactory(moduleFactory, options));
return compileNgModuleFactory(this.injector, options, moduleType)
.then(moduleFactory => this.bootstrapModuleFactory(moduleFactory, options));
}

private _moduleDoBootstrap(moduleRef: InternalNgModuleRef<any>): void {
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ export * from './metadata';
export * from './version';
export {TypeDecorator} from './util/decorators';
export * from './di';
export {createPlatform, assertPlatform, destroyPlatform, getPlatform, PlatformRef, ApplicationRef, enableProdMode, isDevMode, createPlatformFactory, NgProbeToken} from './application_ref';
export {createPlatform, assertPlatform, destroyPlatform, getPlatform, PlatformRef, ApplicationRef, createPlatformFactory, NgProbeToken} from './application_ref';
export {enableProdMode, isDevMode} from './is_dev_mode';
export {APP_ID, PACKAGE_ROOT_URL, PLATFORM_INITIALIZER, PLATFORM_ID, APP_BOOTSTRAP_LISTENER} from './application_tokens';
export {APP_INITIALIZER, ApplicationInitStatus} from './application_init';
export * from './zone';
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/core_render3_private_export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,10 @@ export {
I18nInstruction as ɵI18nInstruction,
I18nExpInstruction as ɵI18nExpInstruction,
WRAP_RENDERER_FACTORY2 as ɵWRAP_RENDERER_FACTORY2,
Render3DebugRendererFactory2 as ɵRender3DebugRendererFactory2,
} from './render3/index';

export { Render3DebugRendererFactory2 as ɵRender3DebugRendererFactory2 } from './render3/debug';


export {
compileNgModuleDefs as ɵcompileNgModuleDefs,
Expand Down
72 changes: 7 additions & 65 deletions packages/core/src/di/injectable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,11 @@
*/

import {R3_COMPILE_INJECTABLE} from '../ivy_switch';
import {ReflectionCapabilities} from '../reflection/reflection_capabilities';
import {Type} from '../type';
import {makeDecorator, makeParamDecorator} from '../util/decorators';
import {getClosureSafeProperty} from '../util/property';
import {makeDecorator} from '../util/decorators';

import {InjectableDef, InjectableType, defineInjectable} from './defs';
import {inject, injectArgs} from './injector';
import {ClassSansProvider, ConstructorProvider, ConstructorSansProvider, ExistingProvider, ExistingSansProvider, FactoryProvider, FactorySansProvider, StaticClassProvider, StaticClassSansProvider, ValueProvider, ValueSansProvider} from './provider';

const GET_PROPERTY_NAME = {} as any;
const USE_VALUE = getClosureSafeProperty<ValueProvider>(
{provide: String, useValue: GET_PROPERTY_NAME}, GET_PROPERTY_NAME);
import {InjectableDef, InjectableType} from './defs';
import {ClassSansProvider, ConstructorSansProvider, ExistingSansProvider, FactorySansProvider, StaticClassSansProvider, ValueProvider, ValueSansProvider} from './provider';

/**
* Injectable providers used in `@Injectable` decorator.
Expand Down Expand Up @@ -61,67 +54,16 @@ export interface InjectableDecorator {
*/
export interface Injectable { providedIn?: Type<any>|'root'|null; }

const EMPTY_ARRAY: any[] = [];

export function convertInjectableProviderToFactory(
type: Type<any>, provider?: InjectableProvider): () => any {
if (!provider) {
const reflectionCapabilities = new ReflectionCapabilities();
const deps = reflectionCapabilities.parameters(type);
// TODO - convert to flags.
return () => new type(...injectArgs(deps as any[]));
}

if (USE_VALUE in provider) {
const valueProvider = (provider as ValueSansProvider);
return () => valueProvider.useValue;
} else if ((provider as ExistingSansProvider).useExisting) {
const existingProvider = (provider as ExistingSansProvider);
return () => inject(existingProvider.useExisting);
} else if ((provider as FactorySansProvider).useFactory) {
const factoryProvider = (provider as FactorySansProvider);
return () => factoryProvider.useFactory(...injectArgs(factoryProvider.deps || EMPTY_ARRAY));
} else if ((provider as StaticClassSansProvider | ClassSansProvider).useClass) {
const classProvider = (provider as StaticClassSansProvider | ClassSansProvider);
let deps = (provider as StaticClassSansProvider).deps;
if (!deps) {
const reflectionCapabilities = new ReflectionCapabilities();
deps = reflectionCapabilities.parameters(type);
}
return () => new classProvider.useClass(...injectArgs(deps));
} else {
let deps = (provider as ConstructorSansProvider).deps;
if (!deps) {
const reflectionCapabilities = new ReflectionCapabilities();
deps = reflectionCapabilities.parameters(type);
}
return () => new type(...injectArgs(deps !));
}
}

/**
* Supports @Injectable() in JIT mode for Render2.
*/
function preR3InjectableCompile(
injectableType: InjectableType<any>,
options: {providedIn?: Type<any>| 'root' | null} & InjectableProvider): void {
if (options && options.providedIn !== undefined && injectableType.ngInjectableDef === undefined) {
injectableType.ngInjectableDef = defineInjectable({
providedIn: options.providedIn,
factory: convertInjectableProviderToFactory(injectableType, options),
});
}
}

/**
* Injectable decorator and metadata.
*
* @Annotation
*/
export const Injectable: InjectableDecorator = makeDecorator(
'Injectable', undefined, undefined, undefined,
(type: Type<any>, meta: Injectable) =>
(R3_COMPILE_INJECTABLE || preR3InjectableCompile)(type, meta));
'Injectable', undefined, undefined, undefined, (type: Type<any>, meta: Injectable) => {
debugger;
return R3_COMPILE_INJECTABLE(type, meta);
});

/**
* Type representing injectable service.
Expand Down
46 changes: 46 additions & 0 deletions packages/core/src/is_dev_mode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* @license
* Copyright Google Inc. 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
*/

/**
* This file is used to control if the default rendering pipeline should be `ViewEngine` or `Ivy`.
*
* For more information on how to run and debug tests with either Ivy or View Engine (legacy),
* please see [BAZEL.md](./docs/BAZEL.md).
*/

let _devMode: boolean = true;
let _runModeLocked: boolean = false;


/**
* Returns whether Angular is in development mode. After called once,
* the value is locked and won't change any more.
*
* By default, this is true, unless a user calls `enableProdMode` before calling this.
*
* @experimental APIs related to application bootstrap are currently under review.
*/
export function isDevMode(): boolean {
_runModeLocked = true;
return _devMode;
}

/**
* Disable Angular's development mode, which turns off assertions and other
* checks within the framework.
*
* One important assertion this disables verifies that a change detection pass
* does not result in additional changes to any bindings (also known as
* unidirectional data flow).
*/
export function enableProdMode(): void {
if (_runModeLocked) {
throw new Error('Cannot enable prod mode after platform setup.');
}
_devMode = false;
}
119 changes: 113 additions & 6 deletions packages/core/src/ivy_switch_legacy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,116 @@
* found in the LICENSE file at https://angular.io/license
*/

export const ivyEnabled = false;
export const R3_COMPILE_COMPONENT: ((type: any, meta: any) => void)|null = null;
export const R3_COMPILE_DIRECTIVE: ((type: any, meta: any) => void)|null = null;
export const R3_COMPILE_INJECTABLE: ((type: any, meta: any) => void)|null = null;
export const R3_COMPILE_NGMODULE: ((type: any, meta: any) => void)|null = null;
export const R3_COMPILE_PIPE: ((type: any, meta: any) => void)|null = null;
import {InjectableType, InjectorType, defineInjectable, defineInjector} from './di/defs';
import {InjectableProvider} from './di/injectable';
import {inject, injectArgs} from './di/injector';
import {ClassSansProvider, ConstructorSansProvider, ExistingSansProvider, FactorySansProvider, StaticClassSansProvider, ValueProvider, ValueSansProvider} from './di/provider';
import * as ivyOn from './ivy_switch_on';
import {NgModule} from './metadata';
import {ReflectionCapabilities} from './reflection/reflection_capabilities';
import {Type} from './type';
import {getClosureSafeProperty} from './util/property';

function noop() {}

export interface DirectiveCompiler { (type: any, meta: any): void; }

const R3_COMPILE_COMPONENT__POST_NGCC__ = ivyOn.R3_COMPILE_COMPONENT;
const R3_COMPILE_DIRECTIVE__POST_NGCC__ = ivyOn.R3_COMPILE_DIRECTIVE;
const R3_COMPILE_INJECTABLE__POST_NGCC__ = ivyOn.R3_COMPILE_INJECTABLE;
const R3_COMPILE_NGMODULE__POST_NGCC__ = ivyOn.R3_COMPILE_NGMODULE;
const R3_COMPILE_PIPE__POST_NGCC__ = ivyOn.R3_COMPILE_PIPE;
const ivyEnable__POST_NGCC__ = ivyOn.ivyEnabled;

const compileComponentQueue: any[] = [];
const compileDirectiveQueue: any[] = [];
const compileInjectableQueue: any[] = [];
const compileNgModuleQueue: any[] = [];
const compilePipeQueue: any[] = [];

const R3_COMPILE_COMPONENT__PRE_NGCC__: DirectiveCompiler = noop;
const R3_COMPILE_DIRECTIVE__PRE_NGCC__: DirectiveCompiler = noop;
const R3_COMPILE_INJECTABLE__PRE_NGCC__: DirectiveCompiler = preR3InjectableCompile;
const R3_COMPILE_NGMODULE__PRE_NGCC__: DirectiveCompiler = preR3NgModuleCompile;
const R3_COMPILE_PIPE__PRE_NGCC__: DirectiveCompiler = noop;
const ivyEnable__PRE_NGCC__ = false;

export const ivyEnabled = ivyEnable__PRE_NGCC__;
export let R3_COMPILE_COMPONENT: DirectiveCompiler = R3_COMPILE_COMPONENT__PRE_NGCC__;
export let R3_COMPILE_DIRECTIVE: DirectiveCompiler = R3_COMPILE_DIRECTIVE__PRE_NGCC__;
export let R3_COMPILE_INJECTABLE: DirectiveCompiler = R3_COMPILE_INJECTABLE__PRE_NGCC__;
export let R3_COMPILE_NGMODULE: DirectiveCompiler = R3_COMPILE_NGMODULE__PRE_NGCC__;
export let R3_COMPILE_PIPE: DirectiveCompiler = R3_COMPILE_PIPE__PRE_NGCC__;


////////////////////////////////////////////////////////////
// Glue code which should be removed after Ivy is default //
////////////////////////////////////////////////////////////

function preR3NgModuleCompile(moduleType: InjectorType<any>, metadata: NgModule): void {
let imports = (metadata && metadata.imports) || [];
if (metadata && metadata.exports) {
imports = [...imports, metadata.exports];
}

moduleType.ngInjectorDef = defineInjector({
factory: convertInjectableProviderToFactory(moduleType, {useClass: moduleType}),
providers: metadata && metadata.providers,
imports: imports,
});
}

const GET_PROPERTY_NAME = {} as any;
const USE_VALUE = getClosureSafeProperty<ValueProvider>(
{provide: String, useValue: GET_PROPERTY_NAME}, GET_PROPERTY_NAME);
const EMPTY_ARRAY: any[] = [];

function convertInjectableProviderToFactory(type: Type<any>, provider?: InjectableProvider): () =>
any {
if (!provider) {
const reflectionCapabilities = new ReflectionCapabilities();
const deps = reflectionCapabilities.parameters(type);
// TODO - convert to flags.
return () => new type(...injectArgs(deps as any[]));
}

if (USE_VALUE in provider) {
const valueProvider = (provider as ValueSansProvider);
return () => valueProvider.useValue;
} else if ((provider as ExistingSansProvider).useExisting) {
const existingProvider = (provider as ExistingSansProvider);
return () => inject(existingProvider.useExisting);
} else if ((provider as FactorySansProvider).useFactory) {
const factoryProvider = (provider as FactorySansProvider);
return () => factoryProvider.useFactory(...injectArgs(factoryProvider.deps || EMPTY_ARRAY));
} else if ((provider as StaticClassSansProvider | ClassSansProvider).useClass) {
const classProvider = (provider as StaticClassSansProvider | ClassSansProvider);
let deps = (provider as StaticClassSansProvider).deps;
if (!deps) {
const reflectionCapabilities = new ReflectionCapabilities();
deps = reflectionCapabilities.parameters(type);
}
return () => new classProvider.useClass(...injectArgs(deps));
} else {
let deps = (provider as ConstructorSansProvider).deps;
if (!deps) {
const reflectionCapabilities = new ReflectionCapabilities();
deps = reflectionCapabilities.parameters(type);
}
return () => new type(...injectArgs(deps !));
}
}

/**
* Supports @Injectable() in JIT mode for Render2.
*/
function preR3InjectableCompile(
injectableType: InjectableType<any>,
options: {providedIn?: Type<any>| 'root' | null} & InjectableProvider): void {
if (options && options.providedIn !== undefined && injectableType.ngInjectableDef === undefined) {
injectableType.ngInjectableDef = defineInjectable({
providedIn: options.providedIn,
factory: convertInjectableProviderToFactory(injectableType, options),
});
}
}
Loading

0 comments on commit 503905c

Please sign in to comment.