feat(ivy): first steps towards ngtsc mode (#23455)

This commit adds a new compiler pipeline that isn't dependent on global
analysis, referred to as 'ngtsc'. This new compiler is accessed by
running ngc with "enableIvy" set to "ngtsc". It reuses the same initialization
logic but creates a new implementation of Program which does not perform the
global-level analysis that AngularCompilerProgram does. It will be the
foundation for the production Ivy compiler.

PR Close #23455
This commit is contained in:
Alex Rickabaugh
2018-04-06 09:53:10 -07:00
committed by Igor Minar
parent f567e1898f
commit ab5bc42da0
44 changed files with 2827 additions and 10 deletions

View File

@ -0,0 +1,101 @@
/**
* @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
*/
import {InjectFlags} from './core';
import * as o from './output/output_ast';
import {Identifiers} from './render3/r3_identifiers';
type MapEntry = {
key: string; quoted: boolean; value: o.Expression;
};
function mapToMapExpression(map: {[key: string]: o.Expression}): o.LiteralMapExpr {
const result = Object.keys(map).map(key => ({key, value: map[key], quoted: false}));
return o.literalMap(result);
}
export interface InjectableDef {
expression: o.Expression;
type: o.Type;
}
export interface IvyInjectableDep {
token: o.Expression;
optional: boolean;
self: boolean;
skipSelf: boolean;
}
export interface IvyInjectableMetadata {
name: string;
type: o.Expression;
providedIn: o.Expression;
useType?: IvyInjectableDep[];
useClass?: o.Expression;
useFactory?: {factory: o.Expression; deps: IvyInjectableDep[];};
useExisting?: o.Expression;
useValue?: o.Expression;
}
export function compileIvyInjectable(meta: IvyInjectableMetadata): InjectableDef {
let ret: o.Expression = o.NULL_EXPR;
if (meta.useType !== undefined) {
const args = meta.useType.map(dep => injectDep(dep));
ret = new o.InstantiateExpr(meta.type, args);
} else if (meta.useClass !== undefined) {
const factory =
new o.ReadPropExpr(new o.ReadPropExpr(meta.useClass, 'ngInjectableDef'), 'factory');
ret = new o.InvokeFunctionExpr(factory, []);
} else if (meta.useValue !== undefined) {
ret = meta.useValue;
} else if (meta.useExisting !== undefined) {
ret = o.importExpr(Identifiers.inject).callFn([meta.useExisting]);
} else if (meta.useFactory !== undefined) {
const args = meta.useFactory.deps.map(dep => injectDep(dep));
ret = new o.InvokeFunctionExpr(meta.useFactory.factory, args);
} else {
throw new Error('No instructions for injectable compiler!');
}
const token = meta.type;
const providedIn = meta.providedIn;
const factory =
o.fn([], [new o.ReturnStatement(ret)], undefined, undefined, `${meta.name}_Factory`);
const expression = o.importExpr({
moduleName: '@angular/core',
name: 'defineInjectable',
}).callFn([mapToMapExpression({token, factory, providedIn})]);
const type = new o.ExpressionType(o.importExpr(
{
moduleName: '@angular/core',
name: 'InjectableDef',
},
[new o.ExpressionType(meta.type)]));
return {
expression, type,
};
}
function injectDep(dep: IvyInjectableDep): o.Expression {
const defaultValue = dep.optional ? o.NULL_EXPR : o.literal(undefined);
const flags = o.literal(
InjectFlags.Default | (dep.self && InjectFlags.Self || 0) |
(dep.skipSelf && InjectFlags.SkipSelf || 0));
if (!dep.optional && !dep.skipSelf && !dep.self) {
return o.importExpr(Identifiers.inject).callFn([dep.token]);
} else {
return o.importExpr(Identifiers.inject).callFn([
dep.token,
defaultValue,
flags,
]);
}
}