feat(ivy): JIT renders the TODO app (#24138)
This commit builds out enough of the JIT compiler to render //packages/core/test/bundling/todo, and allows the tests to run in JIT mode. To play with the app, run: bazel run --define=compile=jit //packages/core/test/bundling/todo:prodserver PR Close #24138
This commit is contained in:

committed by
Victor Berchet

parent
24e5c5b425
commit
646b42a113
@ -81,9 +81,9 @@ export {getParseErrors, isSyntaxError, syntaxError, Version} from './util';
|
||||
export {SourceMap} from './output/source_map';
|
||||
export * from './injectable_compiler_2';
|
||||
export * from './render3/view/api';
|
||||
export {jitPatchDefinition} from './render3/r3_jit';
|
||||
export {jitExpression} from './render3/r3_jit';
|
||||
export {R3DependencyMetadata, R3FactoryMetadata, R3ResolvedDependencyType} from './render3/r3_factory';
|
||||
export {compileNgModule, R3NgModuleMetadata} from './render3/r3_module_compiler';
|
||||
export {makeBindingParser, parseTemplate} from './render3/view/template';
|
||||
export {compileComponent, compileDirective} from './render3/view/compiler';
|
||||
export {compileComponentFromMetadata, compileDirectiveFromMetadata} from './render3/view/compiler';
|
||||
// This file only reexports content of the `src` folder. Keep it that way.
|
@ -49,7 +49,7 @@ export function replaceNgsp(value: string): string {
|
||||
* whitespace removal. The default option for whitespace removal will be revisited in Angular 6
|
||||
* and might be changed to "on" by default.
|
||||
*/
|
||||
class WhitespaceVisitor implements html.Visitor {
|
||||
export class WhitespaceVisitor implements html.Visitor {
|
||||
visitElement(element: html.Element, context: any): any {
|
||||
if (SKIP_WS_TRIM_TAGS.has(element.name) || hasPreserveWhitespacesAttr(element.attrs)) {
|
||||
// don't descent into elements where we need to preserve whitespaces
|
||||
|
@ -50,19 +50,17 @@ class R3JitReflector implements CompileReflector {
|
||||
}
|
||||
|
||||
/**
|
||||
* JIT compiles an expression and monkey-patches the result of executing the expression onto a given
|
||||
* type.
|
||||
* JIT compiles an expression and returns the result of executing that expression.
|
||||
*
|
||||
* @param type the type which will receive the monkey-patched result
|
||||
* @param field name of the field on the type to monkey-patch
|
||||
* @param def the definition which will be compiled and executed to get the value to patch
|
||||
* @param context an object map of @angular/core symbol names to symbols which will be available in
|
||||
* the context of the compiled expression
|
||||
* @param sourceUrl a URL to use for the source map of the compiled expression
|
||||
* @param constantPool an optional `ConstantPool` which contains constants used in the expression
|
||||
*/
|
||||
export function jitPatchDefinition(
|
||||
type: any, field: string, def: o.Expression, context: {[key: string]: any},
|
||||
constantPool?: ConstantPool): void {
|
||||
export function jitExpression(
|
||||
def: o.Expression, context: {[key: string]: any}, sourceUrl: string,
|
||||
constantPool?: ConstantPool): any {
|
||||
// The ConstantPool may contain Statements which declare variables used in the final expression.
|
||||
// Therefore, its statements need to precede the actual JIT operation. The final statement is a
|
||||
// declaration of $def which is set to the expression being compiled.
|
||||
@ -71,8 +69,6 @@ export function jitPatchDefinition(
|
||||
new o.DeclareVarStmt('$def', def, undefined, [o.StmtModifier.Exported]),
|
||||
];
|
||||
|
||||
// Monkey patch the field on the given type with the result of compilation.
|
||||
// TODO(alxhub): consider a better source url.
|
||||
type[field] = jitStatements(
|
||||
`ng://${type && type.name}/${field}`, statements, new R3JitReflector(context), false)['$def'];
|
||||
const res = jitStatements(sourceUrl, statements, new R3JitReflector(context), false);
|
||||
return res['$def'];
|
||||
}
|
||||
|
@ -66,6 +66,17 @@ export interface R3DirectiveMetadata {
|
||||
properties: {[key: string]: string};
|
||||
};
|
||||
|
||||
/**
|
||||
* Information about usage of specific lifecycle events which require special treatment in the
|
||||
* code generator.
|
||||
*/
|
||||
lifecycle: {
|
||||
/**
|
||||
* Whether the directive uses NgOnChanges.
|
||||
*/
|
||||
usesOnChanges: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* A mapping of input field names to the property names.
|
||||
*/
|
||||
@ -101,17 +112,6 @@ export interface R3ComponentMetadata extends R3DirectiveMetadata {
|
||||
ngContentSelectors: string[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Information about usage of specific lifecycle events which require special treatment in the
|
||||
* code generator.
|
||||
*/
|
||||
lifecycle: {
|
||||
/**
|
||||
* Whether the component uses NgOnChanges.
|
||||
*/
|
||||
usesOnChanges: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Information about the view queries made by the component.
|
||||
*/
|
||||
|
@ -65,13 +65,22 @@ function baseDirectiveFields(
|
||||
// e.g 'outputs: {a: 'a'}`
|
||||
definitionMap.set('outputs', conditionallyCreateMapObjectLiteral(meta.outputs));
|
||||
|
||||
// e.g. `features: [NgOnChangesFeature(MyComponent)]`
|
||||
const features: o.Expression[] = [];
|
||||
if (meta.lifecycle.usesOnChanges) {
|
||||
features.push(o.importExpr(R3.NgOnChangesFeature, null, null).callFn([meta.type]));
|
||||
}
|
||||
if (features.length) {
|
||||
definitionMap.set('features', o.literalArr(features));
|
||||
}
|
||||
|
||||
return definitionMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a directive for the render3 runtime as defined by the `R3DirectiveMetadata`.
|
||||
*/
|
||||
export function compileDirective(
|
||||
export function compileDirectiveFromMetadata(
|
||||
meta: R3DirectiveMetadata, constantPool: ConstantPool,
|
||||
bindingParser: BindingParser): R3DirectiveDef {
|
||||
const definitionMap = baseDirectiveFields(meta, constantPool, bindingParser);
|
||||
@ -84,7 +93,7 @@ export function compileDirective(
|
||||
/**
|
||||
* Compile a component for the render3 runtime as defined by the `R3ComponentMetadata`.
|
||||
*/
|
||||
export function compileComponent(
|
||||
export function compileComponentFromMetadata(
|
||||
meta: R3ComponentMetadata, constantPool: ConstantPool,
|
||||
bindingParser: BindingParser): R3ComponentDef {
|
||||
const definitionMap = baseDirectiveFields(meta, constantPool, bindingParser);
|
||||
@ -143,15 +152,6 @@ export function compileComponent(
|
||||
definitionMap.set('pipes', o.literalArr(Array.from(pipesUsed)));
|
||||
}
|
||||
|
||||
// e.g. `features: [NgOnChangesFeature(MyComponent)]`
|
||||
const features: o.Expression[] = [];
|
||||
if (meta.lifecycle.usesOnChanges) {
|
||||
features.push(o.importExpr(R3.NgOnChangesFeature, null, null).callFn([meta.type]));
|
||||
}
|
||||
if (features.length) {
|
||||
definitionMap.set('features', o.literalArr(features));
|
||||
}
|
||||
|
||||
const expression = o.importExpr(R3.defineComponent).callFn([definitionMap.toLiteralMap()]);
|
||||
const type =
|
||||
new o.ExpressionType(o.importExpr(R3.ComponentDef, [new o.ExpressionType(meta.type)]));
|
||||
@ -175,7 +175,7 @@ export function compileDirectiveFromRender2(
|
||||
const definitionField = outputCtx.constantPool.propertyNameOf(DefinitionKind.Directive);
|
||||
|
||||
const meta = directiveMetadataFromGlobalMetadata(directive, outputCtx, reflector);
|
||||
const res = compileDirective(meta, outputCtx.constantPool, bindingParser);
|
||||
const res = compileDirectiveFromMetadata(meta, outputCtx.constantPool, bindingParser);
|
||||
|
||||
// Create the partial class to be merged with the actual class.
|
||||
outputCtx.statements.push(new o.ClassStmt(
|
||||
@ -211,15 +211,11 @@ export function compileComponentFromRender2(
|
||||
hasNgContent: render3Ast.hasNgContent,
|
||||
ngContentSelectors: render3Ast.ngContentSelectors,
|
||||
},
|
||||
lifecycle: {
|
||||
usesOnChanges:
|
||||
component.type.lifecycleHooks.some(lifecycle => lifecycle == LifecycleHooks.OnChanges),
|
||||
},
|
||||
directives: typeMapToExpressionMap(directiveTypeBySel, outputCtx),
|
||||
pipes: typeMapToExpressionMap(pipeTypeByName, outputCtx),
|
||||
viewQueries: queriesFromGlobalMetadata(component.viewQueries, outputCtx),
|
||||
};
|
||||
const res = compileComponent(meta, outputCtx.constantPool, bindingParser);
|
||||
const res = compileComponentFromMetadata(meta, outputCtx.constantPool, bindingParser);
|
||||
|
||||
// Create the partial class to be merged with the actual class.
|
||||
outputCtx.statements.push(new o.ClassStmt(
|
||||
@ -251,6 +247,10 @@ function directiveMetadataFromGlobalMetadata(
|
||||
listeners: summary.hostListeners,
|
||||
properties: summary.hostProperties,
|
||||
},
|
||||
lifecycle: {
|
||||
usesOnChanges:
|
||||
directive.type.lifecycleHooks.some(lifecycle => lifecycle == LifecycleHooks.OnChanges),
|
||||
},
|
||||
inputs: directive.inputs,
|
||||
outputs: directive.outputs,
|
||||
};
|
||||
|
@ -16,6 +16,7 @@ import {Lexer} from '../../expression_parser/lexer';
|
||||
import {Parser} from '../../expression_parser/parser';
|
||||
import * as html from '../../ml_parser/ast';
|
||||
import {HtmlParser} from '../../ml_parser/html_parser';
|
||||
import {WhitespaceVisitor} from '../../ml_parser/html_whitespaces';
|
||||
import {DEFAULT_INTERPOLATION_CONFIG} from '../../ml_parser/interpolation_config';
|
||||
import * as o from '../../output/output_ast';
|
||||
import {ParseError, ParseSourceSpan} from '../../parse_util';
|
||||
@ -777,16 +778,24 @@ function interpolate(args: o.Expression[]): o.Expression {
|
||||
* @param template text of the template to parse
|
||||
* @param templateUrl URL to use for source mapping of the parsed template
|
||||
*/
|
||||
export function parseTemplate(template: string, templateUrl: string):
|
||||
export function parseTemplate(
|
||||
template: string, templateUrl: string, options: {preserveWhitespace?: boolean} = {}):
|
||||
{errors?: ParseError[], nodes: t.Node[], hasNgContent: boolean, ngContentSelectors: string[]} {
|
||||
const bindingParser = makeBindingParser();
|
||||
const htmlParser = new HtmlParser();
|
||||
const parseResult = htmlParser.parse(template, templateUrl);
|
||||
|
||||
if (parseResult.errors && parseResult.errors.length > 0) {
|
||||
return {errors: parseResult.errors, nodes: [], hasNgContent: false, ngContentSelectors: []};
|
||||
}
|
||||
|
||||
let rootNodes: html.Node[] = parseResult.rootNodes;
|
||||
if (!options.preserveWhitespace) {
|
||||
rootNodes = html.visitAll(new WhitespaceVisitor(), rootNodes);
|
||||
}
|
||||
|
||||
const {nodes, hasNgContent, ngContentSelectors, errors} =
|
||||
htmlAstToRender3Ast(parseResult.rootNodes, bindingParser);
|
||||
htmlAstToRender3Ast(rootNodes, bindingParser);
|
||||
if (errors && errors.length > 0) {
|
||||
return {errors, nodes: [], hasNgContent: false, ngContentSelectors: []};
|
||||
}
|
||||
|
@ -1078,8 +1078,8 @@ describe('compiler compliance', () => {
|
||||
selectors: [['lifecycle-comp']],
|
||||
factory: function LifecycleComp_Factory() { return new LifecycleComp(); },
|
||||
inputs: {nameMin: 'name'},
|
||||
template: function LifecycleComp_Template(rf: IDENT, ctx: IDENT) {},
|
||||
features: [$r3$.ɵNgOnChangesFeature(LifecycleComp)]
|
||||
features: [$r3$.ɵNgOnChangesFeature(LifecycleComp)],
|
||||
template: function LifecycleComp_Template(rf: IDENT, ctx: IDENT) {}
|
||||
});`;
|
||||
|
||||
const SimpleLayoutDefinition = `
|
||||
|
Reference in New Issue
Block a user