feat(compiler): implement "enableIvy" compiler option (#21427)

The "enableIvy" compiler option is the initial implementation
of the Render3 (or Ivy) code generation. This commit enables
generation generating "Hello, World" (example in the test)
but not much else. It is currenly only useful for internal Ivy
testing as Ivy is in development.

PR Close #21427
This commit is contained in:
Chuck Jazdzewski
2017-11-20 10:21:17 -08:00
committed by Miško Hevery
parent ce8b5877e2
commit 64d16dee02
27 changed files with 1484 additions and 47 deletions

View File

@ -0,0 +1,141 @@
/**
* @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 * as o from './output/output_ast';
import {OutputContext, error} from './util';
export const enum DefinitionKind {Injector, Directive, Component}
/**
* A node that is a place-holder that allows the node to be replaced when the actual
* node is known.
*
* This allows the constant pool to change an expression from a direct reference to
* a constant to a shared constant. It returns a fix-up node that is later allowed to
* change the referenced expression.
*/
class FixupExpression extends o.Expression {
constructor(public resolved: o.Expression) { super(resolved.type); }
shared: boolean;
visitExpression(visitor: o.ExpressionVisitor, context: any): any {
this.resolved.visitExpression(visitor, context);
}
isEquivalent(e: o.Expression): boolean {
return e instanceof FixupExpression && this.resolved.isEquivalent(e.resolved);
}
fixup(expression: o.Expression) {
this.resolved = expression;
this.shared = true;
}
}
/**
* A constant pool allows a code emitter to share constant in an output context.
*
* The constant pool also supports sharing access to ivy definitions references.
*/
export class ConstantPool {
statements: o.Statement[] = [];
private literals = new Map<string, FixupExpression>();
private injectorDefinitions = new Map<any, FixupExpression>();
private directiveDefinitions = new Map<any, FixupExpression>();
private componentDefintions = new Map<any, FixupExpression>();
private nextNameIndex = 0;
getConstLiteral(literal: o.Expression): o.Expression {
const key = this.keyOf(literal);
let fixup = this.literals.get(key);
if (!fixup) {
fixup = new FixupExpression(literal);
this.literals.set(key, fixup);
} else if (!fixup.shared) {
// Replace the expression with a variable
const name = this.freshName();
this.statements.push(
o.variable(name).set(literal).toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Final]));
fixup.fixup(o.variable(name));
}
return fixup;
}
getDefinition(type: any, kind: DefinitionKind, ctx: OutputContext): o.Expression {
const declarations = kind == DefinitionKind.Component ?
this.componentDefintions :
kind == DefinitionKind.Directive ? this.directiveDefinitions : this.injectorDefinitions;
let fixup = declarations.get(type);
if (!fixup) {
const property = kind == DefinitionKind.Component ?
'ngComponentDef' :
kind == DefinitionKind.Directive ? 'ngDirectiveDef' : 'ngInjectorDef';
fixup = new FixupExpression(ctx.importExpr(type).prop(property));
declarations.set(type, fixup);
} else if (!fixup.shared) {
const name = this.freshName();
this.statements.push(
o.variable(name).set(fixup.resolved).toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Final]));
fixup.fixup(o.variable(name));
}
return fixup;
}
/**
* Produce a unique name.
*
* The name might be unique among different prefixes if any of the prefixes end in
* a digit so the prefix should be a constant string (not based on user input) and
* must not end in a digit.
*/
uniqueName(prefix: string): string { return `${prefix}${this.nextNameIndex++}`; }
private freshName(): string { return this.uniqueName(`_$`); }
private keyOf(expression: o.Expression) {
return expression.visitExpression(new KeyVisitor(), null);
}
}
class KeyVisitor implements o.ExpressionVisitor {
visitLiteralExpr(ast: o.LiteralExpr): string { return `${ast.value}`; }
visitLiteralArrayExpr(ast: o.LiteralArrayExpr): string {
return ast.entries.map(entry => entry.visitExpression(this, null)).join(',');
}
visitLiteralMapExpr(ast: o.LiteralMapExpr): string {
const entries =
ast.entries.map(entry => `${entry.key}:${entry.value.visitExpression(this, null)}`);
return `{${entries.join(',')}`;
}
visitReadVarExpr = invalid;
visitWriteVarExpr = invalid;
visitWriteKeyExpr = invalid;
visitWritePropExpr = invalid;
visitInvokeMethodExpr = invalid;
visitInvokeFunctionExpr = invalid;
visitInstantiateExpr = invalid;
visitExternalExpr = invalid;
visitConditionalExpr = invalid;
visitNotExpr = invalid;
visitAssertNotNullExpr = invalid;
visitCastExpr = invalid;
visitFunctionExpr = invalid;
visitBinaryOperatorExpr = invalid;
visitReadPropExpr = invalid;
visitReadKeyExpr = invalid;
visitCommaExpr = invalid;
}
function invalid<T>(arg: o.Expression | o.Statement): never {
throw new Error(
`Invalid state: Visitor ${this.constructor.name} doesn't handle ${o.constructor.name}`);
}