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:

committed by
Igor Minar

parent
f567e1898f
commit
ab5bc42da0
@ -18,5 +18,5 @@ export interface AotCompilerOptions {
|
||||
fullTemplateTypeCheck?: boolean;
|
||||
allowEmptyCodegenFiles?: boolean;
|
||||
strictInjectionParameters?: boolean;
|
||||
enableIvy?: boolean;
|
||||
enableIvy?: boolean|'ngtsc';
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ export * from './ml_parser/html_tags';
|
||||
export * from './ml_parser/interpolation_config';
|
||||
export * from './ml_parser/tags';
|
||||
export {NgModuleCompiler} from './ng_module_compiler';
|
||||
export {AssertNotNull, BinaryOperator, BinaryOperatorExpr, BuiltinMethod, BuiltinVar, CastExpr, ClassField, ClassMethod, ClassStmt, CommaExpr, CommentStmt, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, ExpressionStatement, ExpressionVisitor, ExternalExpr, ExternalReference, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, JSDocCommentStmt, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, NotExpr, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, StatementVisitor, ThrowStmt, TryCatchStmt, WriteKeyExpr, WritePropExpr, WriteVarExpr, StmtModifier, Statement, collectExternalReferences} from './output/output_ast';
|
||||
export {ArrayType, AssertNotNull, BinaryOperator, BinaryOperatorExpr, BuiltinMethod, BuiltinType, BuiltinTypeName, BuiltinVar, CastExpr, ClassField, ClassMethod, ClassStmt, CommaExpr, CommentStmt, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, Expression, ExpressionStatement, ExpressionType, ExpressionVisitor, ExternalExpr, ExternalReference, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, JSDocCommentStmt, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, MapType, NotExpr, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, StatementVisitor, ThrowStmt, TryCatchStmt, Type, TypeVisitor, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr, StmtModifier, Statement, collectExternalReferences} from './output/output_ast';
|
||||
export {EmitterVisitorContext} from './output/abstract_emitter';
|
||||
export * from './output/ts_emitter';
|
||||
export * from './parse_util';
|
||||
@ -78,4 +78,5 @@ export * from './template_parser/template_parser';
|
||||
export {ViewCompiler} from './view_compiler/view_compiler';
|
||||
export {getParseErrors, isSyntaxError, syntaxError, Version} from './util';
|
||||
export {SourceMap} from './output/source_map';
|
||||
// This file only reexports content of the `src` folder. Keep it that way.
|
||||
export * from './injectable_compiler_2';
|
||||
// This file only reexports content of the `src` folder. Keep it that way.
|
@ -295,6 +295,7 @@ class KeyVisitor implements o.ExpressionVisitor {
|
||||
`EX:${ast.value.runtime.name}`;
|
||||
}
|
||||
|
||||
visitWrappedNodeExpr = invalid;
|
||||
visitReadVarExpr = invalid;
|
||||
visitWriteVarExpr = invalid;
|
||||
visitWriteKeyExpr = invalid;
|
||||
|
101
packages/compiler/src/injectable_compiler_2.ts
Normal file
101
packages/compiler/src/injectable_compiler_2.ts
Normal 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,
|
||||
]);
|
||||
}
|
||||
}
|
@ -312,6 +312,9 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
|
||||
ctx.print(expr, `)`);
|
||||
return null;
|
||||
}
|
||||
visitWrappedNodeExpr(ast: o.WrappedNodeExpr<any>, ctx: EmitterVisitorContext): never {
|
||||
throw new Error('Abstract emitter cannot visit WrappedNodeExpr.');
|
||||
}
|
||||
visitReadVarExpr(ast: o.ReadVarExpr, ctx: EmitterVisitorContext): any {
|
||||
let varName = ast.name !;
|
||||
if (ast.builtin != null) {
|
||||
|
@ -70,6 +70,9 @@ export abstract class AbstractJsEmitterVisitor extends AbstractEmitterVisitor {
|
||||
ctx.println(stmt, `};`);
|
||||
}
|
||||
|
||||
visitWrappedNodeExpr(ast: o.WrappedNodeExpr<any>, ctx: EmitterVisitorContext): never {
|
||||
throw new Error('Cannot emit a WrappedNodeExpr in Javascript.');
|
||||
}
|
||||
visitReadVarExpr(ast: o.ReadVarExpr, ctx: EmitterVisitorContext): string|null {
|
||||
if (ast.builtin === o.BuiltinVar.This) {
|
||||
ctx.print(ast, 'self');
|
||||
|
@ -279,6 +279,21 @@ export class ReadVarExpr extends Expression {
|
||||
}
|
||||
}
|
||||
|
||||
export class WrappedNodeExpr<T> extends Expression {
|
||||
constructor(public node: T, type?: Type|null, sourceSpan?: ParseSourceSpan|null) {
|
||||
super(type, sourceSpan);
|
||||
}
|
||||
|
||||
isEquivalent(e: Expression): boolean {
|
||||
return e instanceof WrappedNodeExpr && this.node === e.node;
|
||||
}
|
||||
|
||||
isConstant() { return false; }
|
||||
|
||||
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
||||
return visitor.visitWrappedNodeExpr(this, context);
|
||||
}
|
||||
}
|
||||
|
||||
export class WriteVarExpr extends Expression {
|
||||
public value: Expression;
|
||||
@ -722,6 +737,7 @@ export interface ExpressionVisitor {
|
||||
visitLiteralArrayExpr(ast: LiteralArrayExpr, context: any): any;
|
||||
visitLiteralMapExpr(ast: LiteralMapExpr, context: any): any;
|
||||
visitCommaExpr(ast: CommaExpr, context: any): any;
|
||||
visitWrappedNodeExpr(ast: WrappedNodeExpr<any>, context: any): any;
|
||||
}
|
||||
|
||||
export const THIS_EXPR = new ReadVarExpr(BuiltinVar.This, null, null);
|
||||
@ -973,6 +989,10 @@ export class AstTransformer implements StatementVisitor, ExpressionVisitor {
|
||||
|
||||
visitReadVarExpr(ast: ReadVarExpr, context: any): any { return this.transformExpr(ast, context); }
|
||||
|
||||
visitWrappedNodeExpr(ast: WrappedNodeExpr<any>, context: any): any {
|
||||
return this.transformExpr(ast, context);
|
||||
}
|
||||
|
||||
visitWriteVarExpr(expr: WriteVarExpr, context: any): any {
|
||||
return this.transformExpr(
|
||||
new WriteVarExpr(
|
||||
@ -1199,6 +1219,7 @@ export class RecursiveAstVisitor implements StatementVisitor, ExpressionVisitor
|
||||
}
|
||||
visitArrayType(type: ArrayType, context: any): any { return this.visitType(type, context); }
|
||||
visitMapType(type: MapType, context: any): any { return this.visitType(type, context); }
|
||||
visitWrappedNodeExpr(ast: WrappedNodeExpr<any>, context: any): any { return ast; }
|
||||
visitReadVarExpr(ast: ReadVarExpr, context: any): any {
|
||||
return this.visitExpression(ast, context);
|
||||
}
|
||||
|
@ -114,6 +114,9 @@ class StatementInterpreter implements o.StatementVisitor, o.ExpressionVisitor {
|
||||
}
|
||||
throw new Error(`Not declared variable ${expr.name}`);
|
||||
}
|
||||
visitWrappedNodeExpr(ast: o.WrappedNodeExpr<any>, ctx: _ExecutionContext): never {
|
||||
throw new Error('Cannot interpret a WrappedNodeExpr.');
|
||||
}
|
||||
visitReadVarExpr(ast: o.ReadVarExpr, ctx: _ExecutionContext): any {
|
||||
let varName = ast.name !;
|
||||
if (ast.builtin != null) {
|
||||
|
@ -169,6 +169,10 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
||||
return null;
|
||||
}
|
||||
|
||||
visitWrappedNodeExpr(ast: o.WrappedNodeExpr<any>, ctx: EmitterVisitorContext): never {
|
||||
throw new Error('Cannot visit a WrappedNodeExpr when outputting Typescript.');
|
||||
}
|
||||
|
||||
visitCastExpr(ast: o.CastExpr, ctx: EmitterVisitorContext): any {
|
||||
ctx.print(ast, `(<`);
|
||||
ast.type !.visitType(this, ctx);
|
||||
|
@ -71,6 +71,12 @@ export class Identifiers {
|
||||
static projection: o.ExternalReference = {name: 'ɵP', moduleName: CORE};
|
||||
static projectionDef: o.ExternalReference = {name: 'ɵpD', moduleName: CORE};
|
||||
|
||||
static refreshComponent: o.ExternalReference = {name: 'ɵr', moduleName: CORE};
|
||||
|
||||
static directiveLifeCycle: o.ExternalReference = {name: 'ɵl', moduleName: CORE};
|
||||
|
||||
static inject: o.ExternalReference = {name: 'inject', moduleName: CORE};
|
||||
|
||||
static injectAttribute: o.ExternalReference = {name: 'ɵinjectAttribute', moduleName: CORE};
|
||||
|
||||
static injectElementRef: o.ExternalReference = {name: 'ɵinjectElementRef', moduleName: CORE};
|
||||
|
Reference in New Issue
Block a user