feat(compiler): integrate compiler with view engine - main integration tests work (#14284)

Part of #14013

PR Close #14284
This commit is contained in:
Tobias Bosch
2017-02-02 15:01:35 -08:00
committed by Miško Hevery
parent dfe29934b6
commit baa654a234
35 changed files with 1232 additions and 277 deletions

View File

@ -71,7 +71,7 @@ export function convertPropertyBinding(
}
export class ConvertActionBindingResult {
constructor(public stmts: o.Statement[], public preventDefault: o.ReadVarExpr) {}
constructor(public stmts: o.Statement[], public allowDefault: o.ReadVarExpr) {}
}
/**

View File

@ -6,11 +6,20 @@
* found in the LICENSE file at https://angular.io/license
*/
import {MissingTranslationStrategy, ViewEncapsulation, isDevMode} from '@angular/core';
import {InjectionToken, MissingTranslationStrategy, ViewEncapsulation, isDevMode} from '@angular/core';
import {CompileIdentifierMetadata} from './compile_metadata';
import {Identifiers, createIdentifier} from './identifiers';
/**
* Temporal switch for the compiler to use the new view engine,
* until it is fully integrated.
*
* Only works in Jit for now.
*/
export const USE_VIEW_ENGINE = new InjectionToken<boolean>('UseViewEngine');
export class CompilerConfig {
public renderTypes: RenderTypes;
public defaultEncapsulation: ViewEncapsulation;

View File

@ -288,8 +288,8 @@ function addHandleEventMethod(hostListeners: BoundEventAst[], builder: Directive
builder, null, o.THIS_EXPR.prop(CONTEXT_FIELD_NAME), hostListener.handler,
`sub_${eventIdx}`);
const trueStmts = evalResult.stmts;
if (evalResult.preventDefault) {
trueStmts.push(resultVar.set(evalResult.preventDefault.and(resultVar)).toStmt());
if (evalResult.allowDefault) {
trueStmts.push(resultVar.set(evalResult.allowDefault.and(resultVar)).toStmt());
}
// TODO(tbosch): convert this into a `switch` once our OutputAst supports it.
actionStmts.push(

View File

@ -9,10 +9,11 @@
import {ANALYZE_FOR_ENTRY_COMPONENTS, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ComponentRef, ElementRef, Injector, LOCALE_ID, NgModuleFactory, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TRANSLATIONS_FORMAT, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
import {CompileIdentifierMetadata, CompileTokenMetadata} from './compile_metadata';
import {AnimationGroupPlayer, AnimationKeyframe, AnimationSequencePlayer, AnimationStyles, AnimationTransition, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, ComponentRef_, DebugAppView, DebugContext, NgModuleInjector, NoOpAnimationPlayer, StaticNodeDebugInfo, TemplateRef_, ValueUnwrapper, ViewContainer, ViewType, balanceAnimationKeyframes, clearStyles, collectAndResolveStyles, devModeEqual, prepareFinalAnimationStyles, reflector, registerModuleFactory, renderStyles, view_utils} from './private_import_core';
import {AnimationGroupPlayer, AnimationKeyframe, AnimationSequencePlayer, AnimationStyles, AnimationTransition, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, ComponentRef_, DebugAppView, DebugContext, NgModuleInjector, NoOpAnimationPlayer, StaticNodeDebugInfo, TemplateRef_, ValueUnwrapper, ViewContainer, ViewType, balanceAnimationKeyframes, clearStyles, collectAndResolveStyles, devModeEqual, prepareFinalAnimationStyles, reflector, registerModuleFactory, renderStyles, viewEngine, view_utils} from './private_import_core';
const APP_VIEW_MODULE_URL = assetUrl('core', 'linker/view');
const VIEW_UTILS_MODULE_URL = assetUrl('core', 'linker/view_utils');
const VIEW_ENGINE_MODULE_URL = assetUrl('core', 'view/index');
const CD_MODULE_URL = assetUrl('core', 'change_detection/change_detection');
const ANIMATION_STYLE_UTIL_ASSET_URL = assetUrl('core', 'animation/animation_style_util');
@ -363,6 +364,47 @@ export class Identifiers {
};
static noop:
IdentifierSpec = {name: 'noop', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.noop};
static viewDef: IdentifierSpec = {
name: 'viewDef',
moduleUrl: VIEW_ENGINE_MODULE_URL,
runtime: viewEngine.viewDef
};
static elementDef: IdentifierSpec = {
name: 'elementDef',
moduleUrl: VIEW_ENGINE_MODULE_URL,
runtime: viewEngine.elementDef
};
static anchorDef: IdentifierSpec = {
name: 'anchorDef',
moduleUrl: VIEW_ENGINE_MODULE_URL,
runtime: viewEngine.anchorDef
};
static textDef: IdentifierSpec = {
name: 'textDef',
moduleUrl: VIEW_ENGINE_MODULE_URL,
runtime: viewEngine.textDef
};
static directiveDef: IdentifierSpec = {
name: 'directiveDef',
moduleUrl: VIEW_ENGINE_MODULE_URL,
runtime: viewEngine.directiveDef
};
static providerDef: IdentifierSpec = {
name: 'providerDef',
moduleUrl: VIEW_ENGINE_MODULE_URL,
runtime: viewEngine.providerDef
};
static queryDef: IdentifierSpec = {
name: 'queryDef',
moduleUrl: VIEW_ENGINE_MODULE_URL,
runtime: viewEngine.queryDef
};
static nodeValue: IdentifierSpec = {
name: 'nodeValue',
moduleUrl: VIEW_ENGINE_MODULE_URL,
runtime: viewEngine.nodeValue
};
}
export function assetUrl(pkg: string, path: string = null, type: string = 'src'): string {

View File

@ -6,12 +6,12 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Compiler, ComponentFactory, Injector, ModuleWithComponentFactories, NgModuleFactory, Type} from '@angular/core';
import {Compiler, ComponentFactory, Inject, Injector, ModuleWithComponentFactories, NgModuleFactory, Type} from '@angular/core';
import {AnimationCompiler} from '../animation/animation_compiler';
import {AnimationParser} from '../animation/animation_parser';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, ProviderMeta, ProxyClass, createHostComponentMeta, identifierName} from '../compile_metadata';
import {CompilerConfig} from '../config';
import {CompilerConfig, USE_VIEW_ENGINE} from '../config';
import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
import {stringify} from '../facade/lang';
import {CompilerInjectable} from '../injectable';
@ -50,7 +50,8 @@ export class JitCompiler implements Compiler {
private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler,
private _viewCompiler: ViewCompiler, private _ngModuleCompiler: NgModuleCompiler,
private _directiveWrapperCompiler: DirectiveWrapperCompiler,
private _compilerConfig: CompilerConfig, private _animationParser: AnimationParser) {}
private _compilerConfig: CompilerConfig, private _animationParser: AnimationParser,
@Inject(USE_VIEW_ENGINE) private _useViewEngine: boolean) {}
get injector(): Injector { return this._injector; }
@ -244,6 +245,9 @@ export class JitCompiler implements Compiler {
private _compileDirectiveWrapper(
dirMeta: CompileDirectiveMetadata, moduleMeta: CompileNgModuleMetadata): void {
if (this._useViewEngine) {
return;
}
const compileResult = this._directiveWrapperCompiler.compile(dirMeta);
const statements = compileResult.statements;
let directiveWrapperClass: any;

View File

@ -9,7 +9,7 @@
import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, InjectionToken, MissingTranslationStrategy, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore} from '@angular/core';
import {AnimationParser} from '../animation/animation_parser';
import {CompilerConfig} from '../config';
import {CompilerConfig, USE_VIEW_ENGINE} from '../config';
import {DirectiveNormalizer} from '../directive_normalizer';
import {DirectiveResolver} from '../directive_resolver';
import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
@ -31,6 +31,7 @@ import {SummaryResolver} from '../summary_resolver';
import {TemplateParser} from '../template_parser/template_parser';
import {DEFAULT_PACKAGE_URL_PROVIDER, UrlResolver} from '../url_resolver';
import {ViewCompiler} from '../view_compiler/view_compiler';
import {ViewCompilerNext} from '../view_compiler_next/view_compiler';
import {JitCompiler} from './compiler';
@ -42,6 +43,11 @@ const _NO_RESOURCE_LOADER: ResourceLoader = {
const baseHtmlParser = new InjectionToken('HtmlParser');
function viewCompilerFactory(
useViewEngine: boolean, cc: CompilerConfig, sr: ElementSchemaRegistry) {
return useViewEngine ? new ViewCompilerNext(cc, sr) : new ViewCompiler(cc, sr);
}
/**
* A set of providers that provide `JitCompiler` and its dependencies to use for
* template compilation.
@ -81,7 +87,12 @@ export const COMPILER_PROVIDERS: Array<any|Type<any>|{[k: string]: any}|any[]> =
CompileMetadataResolver,
DEFAULT_PACKAGE_URL_PROVIDER,
StyleCompiler,
ViewCompiler,
{provide: USE_VIEW_ENGINE, useValue: false},
{
provide: ViewCompiler,
useFactory: viewCompilerFactory,
deps: [USE_VIEW_ENGINE, CompilerConfig, ElementSchemaRegistry]
},
NgModuleCompiler,
DirectiveWrapperCompiler,
{provide: CompilerConfig, useValue: new CompilerConfig()},

View File

@ -12,6 +12,7 @@ import {StaticSymbol, StaticSymbolCache} from './aot/static_symbol';
import {ngfactoryFilePath} from './aot/util';
import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions';
import * as cpl from './compile_metadata';
import {USE_VIEW_ENGINE} from './config';
import {DirectiveNormalizer} from './directive_normalizer';
import {DirectiveResolver} from './directive_resolver';
import {stringify} from './facade/lang';
@ -20,7 +21,7 @@ import {CompilerInjectable} from './injectable';
import {hasLifecycleHook} from './lifecycle_reflector';
import {NgModuleResolver} from './ng_module_resolver';
import {PipeResolver} from './pipe_resolver';
import {ERROR_COMPONENT_TYPE, LIFECYCLE_HOOKS_VALUES, ReflectorReader, reflector} from './private_import_core';
import {ERROR_COMPONENT_TYPE, LIFECYCLE_HOOKS_VALUES, ReflectorReader, reflector, viewEngine} from './private_import_core';
import {ElementSchemaRegistry} from './schema/element_schema_registry';
import {SummaryResolver} from './summary_resolver';
import {getUrlScheme} from './url_resolver';
@ -53,7 +54,8 @@ export class CompileMetadataResolver {
private _directiveNormalizer: DirectiveNormalizer,
@Optional() private _staticSymbolCache: StaticSymbolCache,
private _reflector: ReflectorReader = reflector,
@Optional() @Inject(ERROR_COLLECTOR_TOKEN) private _errorCollector?: ErrorCollector) {}
@Optional() @Inject(ERROR_COLLECTOR_TOKEN) private _errorCollector?: ErrorCollector,
@Optional() @Inject(USE_VIEW_ENGINE) private _useViewEngine?: boolean) {}
clearCacheFor(type: Type<any>) {
const dirMeta = this._directiveCache.get(type);
@ -135,7 +137,11 @@ export class CompileMetadataResolver {
ngfactoryFilePath(dirType.filePath), cpl.componentFactoryName(dirType));
} else {
const hostView = this.getHostComponentViewClass(dirType);
return new ComponentFactory(selector, <any>hostView, dirType);
if (this._useViewEngine) {
return viewEngine.createComponentFactory(selector, dirType, <any>hostView);
} else {
return new ComponentFactory(selector, <any>hostView, dirType);
}
}
}

View File

@ -29,6 +29,7 @@ export const registerModuleFactory: typeof r.registerModuleFactory = r.registerM
export type ViewType = typeof r._ViewType;
export const ViewType: typeof r.ViewType = r.ViewType;
export const view_utils: typeof r.view_utils = r.view_utils;
export const viewEngine: typeof r.viewEngine = r.viewEngine;
export const DebugContext: typeof r.DebugContext = r.DebugContext;
export const StaticNodeDebugInfo: typeof r.StaticNodeDebugInfo = r.StaticNodeDebugInfo;
export const devModeEqual: typeof r.devModeEqual = r.devModeEqual;

View File

@ -9,19 +9,24 @@
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileDirectiveSummary, CompileNgModuleMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata, CompileTypeMetadata, tokenName, tokenReference} from './compile_metadata';
import {isBlank, isPresent} from './facade/lang';
import {Identifiers, resolveIdentifier} from './identifiers';
import {Identifiers, createIdentifierToken, resolveIdentifier} from './identifiers';
import {ParseError, ParseSourceSpan} from './parse_util';
import {AttrAst, DirectiveAst, ProviderAst, ProviderAstType, ReferenceAst} from './template_parser/template_ast';
import {AttrAst, DirectiveAst, ProviderAst, ProviderAstType, QueryId, QueryMatch, ReferenceAst} from './template_parser/template_ast';
export class ProviderError extends ParseError {
constructor(message: string, span: ParseSourceSpan) { super(span, message); }
}
export interface QueryWithId {
meta: CompileQueryMetadata;
id: QueryId;
}
export class ProviderViewContext {
/**
* @internal
*/
viewQueries: Map<any, CompileQueryMetadata[]>;
viewQueries: Map<any, QueryWithId[]>;
/**
* @internal
*/
@ -40,13 +45,14 @@ export class ProviderViewContext {
}
export class ProviderElementContext {
private _contentQueries: Map<any, CompileQueryMetadata[]>;
private _contentQueries: Map<any, QueryWithId[]>;
private _transformedProviders = new Map<any, ProviderAst>();
private _seenProviders = new Map<any, boolean>();
private _allProviders: Map<any, ProviderAst>;
private _attrs: {[key: string]: string};
private _hasViewContainer: boolean = false;
private _queriedTokens = new Map<any, QueryMatch[]>();
constructor(
public viewContext: ProviderViewContext, private _parent: ProviderElementContext,
@ -57,19 +63,21 @@ export class ProviderElementContext {
const directivesMeta = _directiveAsts.map(directiveAst => directiveAst.directive);
this._allProviders =
_resolveProvidersFromDirectives(directivesMeta, _sourceSpan, viewContext.errors);
this._contentQueries = _getContentQueries(directivesMeta);
const queriedTokens = new Map<any, boolean>();
this._contentQueries = _getContentQueries(this.depth, directivesMeta);
Array.from(this._allProviders.values()).forEach((provider) => {
this._addQueryReadsTo(provider.token, queriedTokens);
this._addQueryReadsTo(provider.token, provider.token, this._queriedTokens);
});
refs.forEach((refAst) => { this._addQueryReadsTo({value: refAst.name}, queriedTokens); });
if (isPresent(queriedTokens.get(resolveIdentifier(Identifiers.ViewContainerRef)))) {
refs.forEach((refAst) => {
let defaultQueryValue = refAst.value || createIdentifierToken(Identifiers.ElementRef);
this._addQueryReadsTo({value: refAst.name}, defaultQueryValue, this._queriedTokens);
});
if (this._queriedTokens.get(resolveIdentifier(Identifiers.ViewContainerRef))) {
this._hasViewContainer = true;
}
// create the providers that we know are eager first
Array.from(this._allProviders.values()).forEach((provider) => {
const eager = provider.eager || isPresent(queriedTokens.get(tokenReference(provider.token)));
const eager = provider.eager || this._queriedTokens.get(tokenReference(provider.token));
if (eager) {
this._getOrCreateLocalProvider(provider.providerType, provider.token, true);
}
@ -83,6 +91,16 @@ export class ProviderElementContext {
});
}
get depth(): number {
let d = 0;
let current: ProviderElementContext = this;
while (current._parent) {
d++;
current = current._parent;
}
return d;
}
get transformProviders(): ProviderAst[] {
return Array.from(this._transformedProviders.values());
}
@ -98,24 +116,36 @@ export class ProviderElementContext {
get transformedHasViewContainer(): boolean { return this._hasViewContainer; }
private _addQueryReadsTo(token: CompileTokenMetadata, queryReadTokens: Map<any, boolean>) {
get queryMatches(): QueryMatch[] {
const allMatches: QueryMatch[] = [];
this._queriedTokens.forEach((matches: QueryMatch[]) => { allMatches.push(...matches); });
return allMatches;
}
private _addQueryReadsTo(
token: CompileTokenMetadata, defaultValue: CompileTokenMetadata,
queryReadTokens: Map<any, QueryMatch[]>) {
this._getQueriesFor(token).forEach((query) => {
const queryReadToken = query.read || token;
if (isBlank(queryReadTokens.get(tokenReference(queryReadToken)))) {
queryReadTokens.set(tokenReference(queryReadToken), true);
const queryValue = query.meta.read || defaultValue;
const tokenRef = tokenReference(queryValue);
let queryMatches = queryReadTokens.get(tokenRef);
if (!queryMatches) {
queryMatches = [];
queryReadTokens.set(tokenRef, queryMatches);
}
queryMatches.push({query: query.id, value: queryValue});
});
}
private _getQueriesFor(token: CompileTokenMetadata): CompileQueryMetadata[] {
const result: CompileQueryMetadata[] = [];
private _getQueriesFor(token: CompileTokenMetadata): QueryWithId[] {
const result: QueryWithId[] = [];
let currentEl: ProviderElementContext = this;
let distance = 0;
let queries: CompileQueryMetadata[];
let queries: QueryWithId[];
while (currentEl !== null) {
queries = currentEl._contentQueries.get(tokenReference(token));
if (queries) {
result.push(...queries.filter((query) => query.descendants || distance <= 1));
result.push(...queries.filter((query) => query.meta.descendants || distance <= 1));
}
if (currentEl._directiveAsts.length > 0) {
distance++;
@ -452,27 +482,32 @@ function _resolveProviders(
}
function _getViewQueries(component: CompileDirectiveMetadata): Map<any, CompileQueryMetadata[]> {
const viewQueries = new Map<any, CompileQueryMetadata[]>();
function _getViewQueries(component: CompileDirectiveMetadata): Map<any, QueryWithId[]> {
const viewQueries = new Map<any, QueryWithId[]>();
if (component.viewQueries) {
component.viewQueries.forEach((query) => _addQueryToTokenMap(viewQueries, query));
component.viewQueries.forEach(
(query, queryIndex) => _addQueryToTokenMap(
viewQueries,
{meta: query, id: {elementDepth: null, directiveIndex: null, queryIndex: queryIndex}}));
}
return viewQueries;
}
function _getContentQueries(directives: CompileDirectiveSummary[]):
Map<any, CompileQueryMetadata[]> {
const contentQueries = new Map<any, CompileQueryMetadata[]>();
directives.forEach(directive => {
function _getContentQueries(
elementDepth: number, directives: CompileDirectiveSummary[]): Map<any, QueryWithId[]> {
const contentQueries = new Map<any, QueryWithId[]>();
directives.forEach((directive, directiveIndex) => {
if (directive.queries) {
directive.queries.forEach((query) => _addQueryToTokenMap(contentQueries, query));
directive.queries.forEach(
(query, queryIndex) => _addQueryToTokenMap(
contentQueries, {meta: query, id: {elementDepth, directiveIndex, queryIndex}}));
}
});
return contentQueries;
}
function _addQueryToTokenMap(map: Map<any, CompileQueryMetadata[]>, query: CompileQueryMetadata) {
query.selectors.forEach((token: CompileTokenMetadata) => {
function _addQueryToTokenMap(map: Map<any, QueryWithId[]>, query: QueryWithId) {
query.meta.selectors.forEach((token: CompileTokenMetadata) => {
let entry = map.get(tokenReference(token));
if (!entry) {
entry = [];

View File

@ -126,9 +126,9 @@ export class ElementAst implements TemplateAst {
public name: string, public attrs: AttrAst[], public inputs: BoundElementPropertyAst[],
public outputs: BoundEventAst[], public references: ReferenceAst[],
public directives: DirectiveAst[], public providers: ProviderAst[],
public hasViewContainer: boolean, public children: TemplateAst[],
public ngContentIndex: number, public sourceSpan: ParseSourceSpan,
public endSourceSpan: ParseSourceSpan) {}
public hasViewContainer: boolean, public queryMatches: QueryMatch[],
public children: TemplateAst[], public ngContentIndex: number,
public sourceSpan: ParseSourceSpan, public endSourceSpan: ParseSourceSpan) {}
visit(visitor: TemplateAstVisitor, context: any): any {
return visitor.visitElement(this, context);
@ -143,8 +143,8 @@ export class EmbeddedTemplateAst implements TemplateAst {
public attrs: AttrAst[], public outputs: BoundEventAst[], public references: ReferenceAst[],
public variables: VariableAst[], public directives: DirectiveAst[],
public providers: ProviderAst[], public hasViewContainer: boolean,
public children: TemplateAst[], public ngContentIndex: number,
public sourceSpan: ParseSourceSpan) {}
public queryMatches: QueryMatch[], public children: TemplateAst[],
public ngContentIndex: number, public sourceSpan: ParseSourceSpan) {}
visit(visitor: TemplateAstVisitor, context: any): any {
return visitor.visitEmbeddedTemplate(this, context);
@ -241,6 +241,20 @@ export enum PropertyBindingType {
Animation
}
/**
* This id differentiates a query on an element from any query on any child.
*/
export interface QueryId {
elementDepth: number;
directiveIndex: number;
queryIndex: number;
}
export interface QueryMatch {
query: QueryId;
value: CompileTokenMetadata;
}
/**
* A visitor for {@link TemplateAst} trees that will process each node.
*/

View File

@ -341,8 +341,9 @@ class TemplateParseVisitor implements html.Visitor {
parsedElement = new EmbeddedTemplateAst(
attrs, events, references, elementVars, providerContext.transformedDirectiveAsts,
providerContext.transformProviders, providerContext.transformedHasViewContainer, children,
hasInlineTemplates ? null : ngContentIndex, element.sourceSpan);
providerContext.transformProviders, providerContext.transformedHasViewContainer,
providerContext.queryMatches, children, hasInlineTemplates ? null : ngContentIndex,
element.sourceSpan);
} else {
this._assertElementExists(matchElement, element);
this._assertOnlyOneComponent(directiveAsts, element.sourceSpan);
@ -352,7 +353,7 @@ class TemplateParseVisitor implements html.Visitor {
parsedElement = new ElementAst(
nodeName, attrs, elementProps, events, references,
providerContext.transformedDirectiveAsts, providerContext.transformProviders,
providerContext.transformedHasViewContainer, children,
providerContext.transformedHasViewContainer, providerContext.queryMatches, children,
hasInlineTemplates ? null : ngContentIndex, element.sourceSpan, element.endSourceSpan);
this._findComponentDirectives(directiveAsts)
@ -386,8 +387,8 @@ class TemplateParseVisitor implements html.Visitor {
parsedElement = new EmbeddedTemplateAst(
[], [], [], templateElementVars, templateProviderContext.transformedDirectiveAsts,
templateProviderContext.transformProviders,
templateProviderContext.transformedHasViewContainer, [parsedElement], ngContentIndex,
element.sourceSpan);
templateProviderContext.transformedHasViewContainer, templateProviderContext.queryMatches,
[parsedElement], ngContentIndex, element.sourceSpan);
}
return parsedElement;
@ -755,7 +756,7 @@ class NonBindableVisitor implements html.Visitor {
const ngContentIndex = parent.findNgContentIndex(selector);
const children = html.visitAll(this, ast.children, EMPTY_ELEMENT_CONTEXT);
return new ElementAst(
ast.name, html.visitAll(this, ast.attrs), [], [], [], [], [], false, children,
ast.name, html.visitAll(this, ast.attrs), [], [], [], [], [], false, [], children,
ngContentIndex, ast.sourceSpan, ast.endSourceSpan);
}
visitComment(comment: html.Comment, context: any): any { return null; }

View File

@ -105,8 +105,8 @@ function generateHandleEventMethod(
compileElement.view, compileElement.view, compileElement.view.componentContext,
renderEvent.handler, `sub_${renderEventIdx}`);
const trueStmts = evalResult.stmts;
if (evalResult.preventDefault) {
trueStmts.push(resultVar.set(evalResult.preventDefault.and(resultVar)).toStmt());
if (evalResult.allowDefault) {
trueStmts.push(resultVar.set(evalResult.allowDefault.and(resultVar)).toStmt());
}
// TODO(tbosch): convert this into a `switch` once our OutputAst supports it.
handleEventStmts.push(

View File

@ -0,0 +1,5 @@
# Compiler Integration for new View Engine
This folder contains the code to integrate the compiler with the new view engine.
Note: This is work in progress, and once complete will replace the regular view_compiler folder.

View File

@ -0,0 +1,738 @@
/**
* @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 {ChangeDetectionStrategy} from '@angular/core';
import {AnimationEntryCompileResult} from '../animation/animation_compiler';
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeSummary, CompileProviderMetadata, CompileTokenMetadata, identifierModuleUrl, identifierName, tokenReference} from '../compile_metadata';
import {EventHandlerVars, NameResolver, convertActionBinding, convertPropertyBinding} from '../compiler_util/expression_converter';
import {CompilerConfig} from '../config';
import {AST, ASTWithSource, Interpolation} from '../expression_parser/ast';
import {Identifiers, createIdentifier, resolveIdentifier} from '../identifiers';
import {CompilerInjectable} from '../injectable';
import * as o from '../output/output_ast';
import {convertValueToOutputAst} from '../output/value_util';
import {LifecycleHooks, viewEngine} from '../private_import_core';
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, PropertyBindingType, ProviderAst, ProviderAstType, QueryId, QueryMatch, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_parser/template_ast';
import {ViewEncapsulationEnum} from '../view_compiler/constants';
import {ComponentFactoryDependency, ComponentViewDependency, DirectiveWrapperDependency, ViewCompileResult, ViewCompiler} from '../view_compiler/view_compiler';
const CLASS_ATTR = 'class';
const STYLE_ATTR = 'style';
@CompilerInjectable()
export class ViewCompilerNext extends ViewCompiler {
constructor(
private _genConfigNext: CompilerConfig, private _schemaRegistryNext: ElementSchemaRegistry) {
super(_genConfigNext, _schemaRegistryNext);
}
compileComponent(
component: CompileDirectiveMetadata, template: TemplateAst[], styles: o.Expression,
pipes: CompilePipeSummary[],
compiledAnimations: AnimationEntryCompileResult[]): ViewCompileResult {
const compName = identifierName(component.type) + (component.isHost ? `_Host` : '');
let embeddedViewCount = 0;
const viewBuilderFactory = (parent: ViewBuilder): ViewBuilder => {
const embeddedViewIndex = embeddedViewCount++;
const viewName = `view_${compName}_${embeddedViewIndex}`;
return new ViewBuilder(parent, viewName, viewBuilderFactory);
};
const visitor = viewBuilderFactory(null);
visitor.visitAll([], template, 0);
const statements: o.Statement[] = [];
statements.push(...visitor.build(component));
return new ViewCompileResult(statements, visitor.viewName, []);
}
}
interface ViewBuilderFactory {
(parent: ViewBuilder): ViewBuilder;
}
interface UpdateExpression {
nodeIndex: number;
expressions: {context: o.Expression, value: AST}[];
}
interface HandleEventExpression {
nodeIndex: number;
context: o.Expression;
eventName: string;
expression: AST;
}
const VIEW_VAR = o.variable('view');
const CHECK_VAR = o.variable('check');
const COMP_VAR = o.variable('comp');
const NODE_INDEX_VAR = o.variable('nodeIndex');
const EVENT_NAME_VAR = o.variable('eventName');
const ALLOW_DEFAULT_VAR = o.variable(`allowDefault`);
class ViewBuilder implements TemplateAstVisitor, NameResolver {
private nodeDefs: o.Expression[] = [];
private refNodeIndices: {[refName: string]: number} = {};
private variables: VariableAst[] = [];
private children: ViewBuilder[] = [];
private updateExpressions: UpdateExpression[] = [];
private handleEventExpressions: HandleEventExpression[] = [];
constructor(
private parent: ViewBuilder, public viewName: string,
private viewBuilderFactory: ViewBuilderFactory) {}
visitAll(variables: VariableAst[], astNodes: TemplateAst[], elementDepth: number) {
this.variables = variables;
templateVisitAll(this, astNodes, {elementDepth});
if (astNodes.length === 0 || (this.parent && hasViewContainer(astNodes[astNodes.length - 1]))) {
// if the view is empty, or an embedded view has a view container as last root nde,
// create an additional root node.
this.nodeDefs.push(o.importExpr(createIdentifier(Identifiers.anchorDef)).callFn([
o.literal(viewEngine.NodeFlags.None), o.NULL_EXPR, o.NULL_EXPR, o.literal(0)
]));
}
}
build(component: CompileDirectiveMetadata, targetStatements: o.Statement[] = []): o.Statement[] {
const compType = o.importType(component.type);
this.children.forEach((child) => { child.build(component, targetStatements); });
const updateStmts: o.Statement[] = [];
let updateBindingCount = 0;
this.updateExpressions
.forEach(
({expressions, nodeIndex}) => {
const exprs = expressions.map(({context, value}) => {
const bindingId = `${updateBindingCount++}`;
const {stmts, currValExpr} =
convertPropertyBinding(null, this, context, value, bindingId);
updateStmts.push(...stmts);
return currValExpr;
});
if (exprs.length > 10) {
updateStmts.push(
CHECK_VAR
.callFn([
VIEW_VAR, o.literal(nodeIndex),
o.literal(viewEngine.ArgumentType.Dynamic), o.literalArr(exprs)
])
.toStmt());
} else {
updateStmts.push(
CHECK_VAR.callFn((<o.Expression[]>[VIEW_VAR, o.literal(nodeIndex), o.literal(viewEngine.ArgumentType.Inline)]).concat(exprs)).toStmt());
}
});
let updateFn: o.Expression;
if (updateStmts.length > 0) {
updateFn = o.fn(
[new o.FnParam(CHECK_VAR.name), new o.FnParam(VIEW_VAR.name)],
[COMP_VAR.set(VIEW_VAR.prop('component')).toDeclStmt(compType), ...updateStmts]);
} else {
updateFn = o.NULL_EXPR;
}
const handleEventStmts: o.Statement[] = [];
let handleEventBindingCount = 0;
this.handleEventExpressions.forEach(({expression, context, nodeIndex, eventName}) => {
const bindingId = `${handleEventBindingCount++}`;
const {stmts, allowDefault} =
convertActionBinding(null, this, context, expression, bindingId);
const trueStmts = stmts;
if (allowDefault) {
trueStmts.push(ALLOW_DEFAULT_VAR.set(allowDefault.and(ALLOW_DEFAULT_VAR)).toStmt());
}
handleEventStmts.push(new o.IfStmt(
o.literal(nodeIndex)
.identical(NODE_INDEX_VAR)
.and(o.literal(eventName).identical(EVENT_NAME_VAR)),
trueStmts));
});
let handleEventFn: o.Expression;
if (handleEventStmts.length > 0) {
handleEventFn = o.fn(
[
new o.FnParam(VIEW_VAR.name), new o.FnParam(NODE_INDEX_VAR.name),
new o.FnParam(EVENT_NAME_VAR.name), new o.FnParam(EventHandlerVars.event.name)
],
[
ALLOW_DEFAULT_VAR.set(o.literal(true)).toDeclStmt(o.BOOL_TYPE),
COMP_VAR.set(VIEW_VAR.prop('component')).toDeclStmt(compType), ...handleEventStmts,
new o.ReturnStatement(ALLOW_DEFAULT_VAR)
]);
} else {
handleEventFn = o.NULL_EXPR;
}
let viewFlags = viewEngine.ViewFlags.None;
if (!this.parent && component.changeDetection === ChangeDetectionStrategy.OnPush) {
viewFlags |= viewEngine.ViewFlags.OnPush;
}
const viewFactory = new o.DeclareFunctionStmt(
this.viewName, [],
[new o.ReturnStatement(o.importExpr(createIdentifier(Identifiers.viewDef)).callFn([
o.literal(viewFlags), o.literalArr(this.nodeDefs), updateFn, handleEventFn
]))]);
targetStatements.push(viewFactory);
return targetStatements;
}
visitNgContent(ast: NgContentAst, context: any): any {}
visitText(ast: TextAst, context: any): any {
// textDef(ngContentIndex: number, constants: string[]): NodeDef;
this.nodeDefs.push(o.importExpr(createIdentifier(Identifiers.textDef)).callFn([
o.NULL_EXPR, o.literalArr([o.literal(ast.value)])
]));
}
visitBoundText(ast: BoundTextAst, context: any): any {
const nodeIndex = this.nodeDefs.length;
const astWithSource = <ASTWithSource>ast.value;
const inter = <Interpolation>astWithSource.ast;
this.updateExpressions.push({
nodeIndex,
expressions: inter.expressions.map((expr) => { return {context: COMP_VAR, value: expr}; })
});
// textDef(ngContentIndex: number, constants: string[]): NodeDef;
this.nodeDefs.push(o.importExpr(createIdentifier(Identifiers.textDef)).callFn([
o.NULL_EXPR, o.literalArr(inter.strings.map(s => o.literal(s)))
]));
}
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: {elementDepth: number}): any {
const nodeIndex = this.nodeDefs.length;
// reserve the space in the nodeDefs array
this.nodeDefs.push(null);
const {flags, queryMatchesExpr} = this._visitElementOrTemplate(nodeIndex, ast, context);
const childCount = this.nodeDefs.length - nodeIndex - 1;
const childVisitor = this.viewBuilderFactory(this);
this.children.push(childVisitor);
childVisitor.visitAll(ast.variables, ast.children, context.elementDepth + 1);
// anchorDef(
// flags: NodeFlags, matchedQueries: [string, QueryValueType][], ngContentIndex: number,
// childCount: number, templateFactory?: ViewDefinitionFactory): NodeDef;
this.nodeDefs[nodeIndex] = o.importExpr(createIdentifier(Identifiers.anchorDef)).callFn([
o.literal(flags), queryMatchesExpr, o.NULL_EXPR, o.literal(childCount),
o.variable(childVisitor.viewName)
]);
}
visitElement(ast: ElementAst, context: {elementDepth: number}): any {
const nodeIndex = this.nodeDefs.length;
// reserve the space in the nodeDefs array so we can add children
this.nodeDefs.push(null);
const {flags, usedEvents, queryMatchesExpr, hostBindings} =
this._visitElementOrTemplate(nodeIndex, ast, context);
templateVisitAll(this, ast.children, {elementDepth: context.elementDepth + 1});
const childCount = this.nodeDefs.length - nodeIndex - 1;
ast.inputs.forEach((inputAst) => {
hostBindings.push({context: COMP_VAR, value: (<ASTWithSource>inputAst.value).ast});
});
this.updateExpressions.push({nodeIndex, expressions: hostBindings});
const inputDefs = elementBindingDefs(ast.inputs);
ast.directives.forEach(
(dirAst, dirIndex) => { inputDefs.push(...elementBindingDefs(dirAst.hostProperties)); });
const outputDefs = usedEvents.map(([target, eventName]) => {
return target ? o.literalArr([o.literal(target), o.literal(eventName)]) :
o.literal(eventName);
});
// elementDef(
// flags: NodeFlags, matchedQueries: [string, QueryValueType][], ngContentIndex: number,
// childCount: number, name: string, fixedAttrs: {[name: string]: string} = {},
// bindings?:
// ([BindingType.ElementClass, string] | [BindingType.ElementStyle, string, string] |
// [BindingType.ElementAttribute | BindingType.ElementProperty, string,
// SecurityContext])[],
// outputs?: (string | [string, string])[]): NodeDef;
this.nodeDefs[nodeIndex] = o.importExpr(createIdentifier(Identifiers.elementDef)).callFn([
o.literal(flags), queryMatchesExpr, o.NULL_EXPR, o.literal(childCount), o.literal(ast.name),
fixedAttrsDef(ast), inputDefs.length ? o.literalArr(inputDefs) : o.NULL_EXPR,
outputDefs.length ? o.literalArr(outputDefs) : o.NULL_EXPR
]);
}
private _visitElementOrTemplate(
nodeIndex: number, ast: {
hasViewContainer: boolean,
outputs: BoundEventAst[],
directives: DirectiveAst[],
providers: ProviderAst[],
references: ReferenceAst[],
queryMatches: QueryMatch[]
},
context: {elementDepth: number}): {
flags: number,
usedEvents: [string, string][],
queryMatchesExpr: o.Expression,
hostBindings: {value: AST, context: o.Expression}[],
} {
let flags = viewEngine.NodeFlags.None;
if (ast.hasViewContainer) {
flags |= viewEngine.NodeFlags.HasEmbeddedViews;
}
const usedEvents = new Map<string, [string, string]>();
ast.outputs.forEach((event) => {
usedEvents.set(
viewEngine.elementEventFullName(event.target, event.name), [event.target, event.name]);
});
ast.directives.forEach((dirAst) => {
dirAst.hostEvents.forEach((event) => {
usedEvents.set(
viewEngine.elementEventFullName(event.target, event.name), [event.target, event.name]);
});
});
const hostBindings: {value: AST, context: o.Expression}[] = [];
const hostEvents: {context: o.Expression, eventAst: BoundEventAst}[] = [];
ast.providers.forEach((providerAst, providerIndex) => {
let dirAst: DirectiveAst;
let dirIndex: number;
ast.directives.forEach((localDirAst, i) => {
if (localDirAst.directive.type.reference === providerAst.token.identifier.reference) {
dirAst = localDirAst;
dirIndex = i;
}
});
if (dirAst) {
const {hostBindings: dirHostBindings, hostEvents: dirHostEvents} = this._visitDirective(
providerAst, dirAst, dirIndex, nodeIndex, context.elementDepth, ast.references,
ast.queryMatches, usedEvents);
hostBindings.push(...dirHostBindings);
hostEvents.push(...dirHostEvents);
} else {
this._visitProvider(providerAst, ast.queryMatches);
}
});
let queryMatchExprs: o.Expression[] = [];
ast.queryMatches.forEach((match) => {
let valueType: number;
if (tokenReference(match.value) === resolveIdentifier(Identifiers.ElementRef)) {
valueType = viewEngine.QueryValueType.ElementRef;
} else if (tokenReference(match.value) === resolveIdentifier(Identifiers.ViewContainerRef)) {
valueType = viewEngine.QueryValueType.ViewContainerRef;
} else if (tokenReference(match.value) === resolveIdentifier(Identifiers.TemplateRef)) {
valueType = viewEngine.QueryValueType.TemplateRef;
}
if (valueType != null) {
queryMatchExprs.push(
o.literalArr([o.literal(calcQueryId(match.query)), o.literal(valueType)]));
}
});
ast.references.forEach((ref) => {
let valueType: number;
if (!ref.value) {
valueType = viewEngine.QueryValueType.RenderElement;
} else if (tokenReference(ref.value) === resolveIdentifier(Identifiers.TemplateRef)) {
valueType = viewEngine.QueryValueType.TemplateRef;
}
if (valueType != null) {
this.refNodeIndices[ref.name] = nodeIndex;
queryMatchExprs.push(o.literalArr([o.literal(`#${ref.name}`), o.literal(valueType)]));
}
});
ast.outputs.forEach(
(outputAst) => { hostEvents.push({context: COMP_VAR, eventAst: outputAst}); });
hostEvents.forEach((hostEvent) => {
this.handleEventExpressions.push({
nodeIndex,
context: hostEvent.context,
eventName:
viewEngine.elementEventFullName(hostEvent.eventAst.target, hostEvent.eventAst.name),
expression: (<ASTWithSource>hostEvent.eventAst.handler).ast
});
});
return {
flags,
usedEvents: Array.from(usedEvents.values()),
queryMatchesExpr: queryMatchExprs.length ? o.literalArr(queryMatchExprs) : o.NULL_EXPR,
hostBindings,
};
}
private _visitDirective(
providerAst: ProviderAst, directiveAst: DirectiveAst, directiveIndex: number,
elementNodeIndex: number, elementDepth: number, refs: ReferenceAst[],
queryMatches: QueryMatch[], usedEvents: Map<string, any>): {
hostBindings: {value: AST, context: o.Expression}[],
hostEvents: {context: o.Expression, eventAst: BoundEventAst}[]
} {
const nodeIndex = this.nodeDefs.length;
// reserve the space in the nodeDefs array so we can add children
this.nodeDefs.push(null);
const {flags, queryMatchExprs, providerExpr, providerType, depsExpr} =
this._visitProviderOrDirective(providerAst, queryMatches);
refs.forEach((ref) => {
if (ref.value && tokenReference(ref.value) === tokenReference(providerAst.token)) {
this.refNodeIndices[ref.name] = nodeIndex;
queryMatchExprs.push(o.literalArr(
[o.literal(`#${ref.name}`), o.literal(viewEngine.QueryValueType.Provider)]));
}
});
let compView = o.NULL_EXPR;
if (directiveAst.directive.isComponent) {
compView = o.importExpr({reference: directiveAst.directive.componentViewType});
}
const inputDefs = directiveAst.inputs.map((inputAst, inputIndex) => {
const mapValue = o.literalArr([o.literal(inputIndex), o.literal(inputAst.directiveName)]);
// Note: it's important to not quote the key so that we can capture renames by minifiers!
return new o.LiteralMapEntry(inputAst.directiveName, mapValue, false);
});
const outputDefs: o.LiteralMapEntry[] = [];
const dirMeta = directiveAst.directive;
Object.keys(dirMeta.outputs).forEach((propName) => {
const eventName = dirMeta.outputs[propName];
if (usedEvents.has(eventName)) {
// Note: it's important to not quote the key so that we can capture renames by minifiers!
outputDefs.push(new o.LiteralMapEntry(propName, o.literal(eventName), false));
}
});
if (directiveAst.inputs.length) {
this.updateExpressions.push({
nodeIndex,
expressions: directiveAst.inputs.map(
input => { return {context: COMP_VAR, value: (<ASTWithSource>input.value).ast}; })
});
}
const dirContextExpr = o.importExpr(createIdentifier(Identifiers.nodeValue)).callFn([
VIEW_VAR, o.literal(nodeIndex)
]);
const hostBindings = directiveAst.hostProperties.map((hostBindingAst) => {
return {
value: (<ASTWithSource>hostBindingAst.value).ast,
context: dirContextExpr,
};
});
const hostEvents = directiveAst.hostEvents.map(
(hostEventAst) => { return {context: dirContextExpr, eventAst: hostEventAst}; });
const childCount = directiveAst.directive.queries.length;
directiveAst.directive.queries.forEach((query, queryIndex) => {
const queryId: QueryId = {elementDepth, directiveIndex, queryIndex};
const bindingType =
query.first ? viewEngine.QueryBindingType.First : viewEngine.QueryBindingType.All;
// queryDef(
// flags: NodeFlags, id: string, bindings: {[propName: string]: QueryBindingType}): NodeDef
// {
this.nodeDefs.push(o.importExpr(createIdentifier(Identifiers.queryDef)).callFn([
o.literal(viewEngine.NodeFlags.HasContentQuery), o.literal(calcQueryId(queryId)),
new o.LiteralMapExpr([new o.LiteralMapEntry(query.propertyName, o.literal(bindingType))])
]));
});
// directiveDef(
// flags: NodeFlags, matchedQueries: [string, QueryValueType][], childCount: number, ctor:
// any,
// deps: ([DepFlags, any] | any)[], props?: {[name: string]: [number, string]},
// outputs?: {[name: string]: string}, component?: () => ViewDefinition): NodeDef;
const nodeDef = o.importExpr(createIdentifier(Identifiers.directiveDef)).callFn([
o.literal(flags), queryMatchExprs.length ? o.literalArr(queryMatchExprs) : o.NULL_EXPR,
o.literal(childCount), providerExpr, depsExpr,
inputDefs.length ? new o.LiteralMapExpr(inputDefs) : o.NULL_EXPR,
outputDefs.length ? new o.LiteralMapExpr(outputDefs) : o.NULL_EXPR, compView
]);
this.nodeDefs[nodeIndex] = nodeDef;
return {hostBindings, hostEvents};
}
private _visitProvider(providerAst: ProviderAst, queryMatches: QueryMatch[]): void {
const nodeIndex = this.nodeDefs.length;
// reserve the space in the nodeDefs array so we can add children
this.nodeDefs.push(null);
const {flags, queryMatchExprs, providerExpr, providerType, depsExpr} =
this._visitProviderOrDirective(providerAst, queryMatches);
// providerDef(
// flags: NodeFlags, matchedQueries: [string, QueryValueType][], type: ProviderType, token:
// any,
// value: any, deps: ([DepFlags, any] | any)[]): NodeDef;
const nodeDef = o.importExpr(createIdentifier(Identifiers.providerDef)).callFn([
o.literal(flags), queryMatchExprs.length ? o.literalArr(queryMatchExprs) : o.NULL_EXPR,
o.literal(providerType), tokenExpr(providerAst.token), providerExpr, depsExpr
]);
this.nodeDefs[nodeIndex] = nodeDef;
}
private _visitProviderOrDirective(providerAst: ProviderAst, queryMatches: QueryMatch[]): {
flags: number,
queryMatchExprs: o.Expression[],
providerExpr: o.Expression,
providerType: number,
depsExpr: o.Expression
} {
let flags = viewEngine.NodeFlags.None;
if (!providerAst.eager) {
flags |= viewEngine.NodeFlags.LazyProvider;
}
providerAst.lifecycleHooks.forEach((lifecycleHook) => {
// for regular providers, we only support ngOnDestroy
if (lifecycleHook === LifecycleHooks.OnDestroy ||
providerAst.providerType === ProviderAstType.Directive ||
providerAst.providerType === ProviderAstType.Component) {
flags |= lifecycleHookToNodeFlag(lifecycleHook);
}
});
let queryMatchExprs: o.Expression[] = [];
queryMatches.forEach((match) => {
if (tokenReference(match.value) === tokenReference(providerAst.token)) {
queryMatchExprs.push(o.literalArr(
[o.literal(calcQueryId(match.query)), o.literal(viewEngine.QueryValueType.Provider)]));
}
});
const {providerExpr, providerType, depsExpr} = providerDef(providerAst);
return {flags, queryMatchExprs, providerExpr, providerType, depsExpr};
}
callPipe(name: string, input: o.Expression, args: o.Expression[]): o.Expression {
throw new Error('Pipes are not yet supported!');
}
getLocal(name: string): o.Expression {
if (name == EventHandlerVars.event.name) {
return EventHandlerVars.event;
}
let currViewExpr: o.Expression = VIEW_VAR;
for (let currBuilder: ViewBuilder = this; currBuilder;
currBuilder = currBuilder.parent, currViewExpr = currViewExpr.prop('parent')) {
// check references
const refNodeIndex = currBuilder.refNodeIndices[name];
if (refNodeIndex != null) {
return o.importExpr(createIdentifier(Identifiers.nodeValue)).callFn([
currViewExpr, o.literal(refNodeIndex)
]);
}
// check variables
const varAst = currBuilder.variables.find((varAst) => varAst.name === name);
if (varAst) {
return currViewExpr.prop('context').prop(varAst.value);
}
}
return null;
}
visitDirective(ast: DirectiveAst, context: {usedEvents: Set<string>}): any {}
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any {}
visitReference(ast: ReferenceAst, context: any): any {}
visitVariable(ast: VariableAst, context: any): any {}
visitEvent(ast: BoundEventAst, context: any): any {}
visitElementProperty(ast: BoundElementPropertyAst, context: any): any {}
visitAttr(ast: AttrAst, context: any): any {}
}
function providerDef(providerAst: ProviderAst):
{providerExpr: o.Expression, providerType: number, depsExpr: o.Expression} {
return providerAst.multiProvider ? multiProviderDef(providerAst.providers) :
singleProviderDef(providerAst.providers[0]);
}
function multiProviderDef(providers: CompileProviderMetadata[]):
{providerExpr: o.Expression, providerType: number, depsExpr: o.Expression} {
const allDepDefs: o.Expression[] = [];
const allParams: o.FnParam[] = [];
const exprs = providers.map((provider, providerIndex) => {
const depExprs = provider.deps.map((dep, depIndex) => {
const paramName = `p${providerIndex}_${depIndex}`;
allParams.push(new o.FnParam(paramName, o.DYNAMIC_TYPE));
allDepDefs.push(depDef(dep));
return o.variable(paramName);
});
let expr: o.Expression;
if (provider.useClass) {
expr = o.importExpr(provider.useClass).instantiate(depExprs);
} else if (provider.useFactory) {
expr = o.importExpr(provider.useFactory).callFn(depExprs);
} else if (provider.useExisting) {
expr = depExprs[0];
} else {
expr = convertValueToOutputAst(provider.useValue);
}
return expr;
});
const providerExpr = o.fn(allParams, [new o.ReturnStatement(o.literalArr(exprs))]);
return {
providerExpr,
providerType: viewEngine.ProviderType.Factory,
depsExpr: o.literalArr(allDepDefs)
};
}
function singleProviderDef(providerMeta: CompileProviderMetadata):
{providerExpr: o.Expression, providerType: number, depsExpr: o.Expression} {
let providerExpr: o.Expression;
let providerType: number;
let deps: CompileDiDependencyMetadata[];
if (providerMeta.useClass) {
providerExpr = o.importExpr(providerMeta.useClass);
providerType = viewEngine.ProviderType.Class;
deps = providerMeta.deps || providerMeta.useClass.diDeps;
} else if (providerMeta.useFactory) {
providerExpr = o.importExpr(providerMeta.useFactory);
providerType = viewEngine.ProviderType.Factory;
deps = providerMeta.deps || providerMeta.useFactory.diDeps;
} else if (providerMeta.useExisting) {
providerExpr = o.NULL_EXPR;
providerType = viewEngine.ProviderType.UseExisting;
deps = [{token: providerMeta.useExisting}];
} else {
providerExpr = convertValueToOutputAst(providerMeta.useValue);
providerType = viewEngine.ProviderType.Value;
deps = [];
}
const depsExpr = o.literalArr(deps.map(dep => depDef(dep)));
return {providerExpr, providerType, depsExpr};
}
function tokenExpr(tokenMeta: CompileTokenMetadata): o.Expression {
return tokenMeta.identifier ? o.importExpr(tokenMeta.identifier) : o.literal(tokenMeta.value);
}
function depDef(dep: CompileDiDependencyMetadata): o.Expression {
// Note: the following fields have already been normalized out by provider_analyzer:
// - isAttribute, isSelf, isHost
const expr = dep.isValue ? convertValueToOutputAst(dep.value) : tokenExpr(dep.token);
let flags = viewEngine.DepFlags.None;
if (dep.isSkipSelf) {
flags |= viewEngine.DepFlags.SkipSelf;
}
if (dep.isOptional) {
flags |= viewEngine.DepFlags.Optional;
}
if (dep.isValue) {
flags |= viewEngine.DepFlags.Value;
}
return flags === viewEngine.DepFlags.None ? expr : o.literalArr([o.literal(flags), expr]);
}
function hasViewContainer(ast: TemplateAst): boolean {
if (ast instanceof EmbeddedTemplateAst) {
return ast.hasViewContainer;
} else if (ast instanceof ElementAst) {
return ast.hasViewContainer;
}
return false;
}
function calcQueryId(queryId: QueryId): string {
if (queryId.directiveIndex == null) {
// view query
return `v${queryId.queryIndex}`;
} else {
return `c${queryId.elementDepth}_${queryId.directiveIndex}_${queryId.queryIndex}`;
}
}
function lifecycleHookToNodeFlag(lifecycleHook: LifecycleHooks): number {
let nodeFlag = viewEngine.NodeFlags.None;
switch (lifecycleHook) {
case LifecycleHooks.AfterContentChecked:
nodeFlag = viewEngine.NodeFlags.AfterContentChecked;
break;
case LifecycleHooks.AfterContentInit:
nodeFlag = viewEngine.NodeFlags.AfterContentInit;
break;
case LifecycleHooks.AfterViewChecked:
nodeFlag = viewEngine.NodeFlags.AfterViewChecked;
break;
case LifecycleHooks.AfterViewInit:
nodeFlag = viewEngine.NodeFlags.AfterViewInit;
break;
case LifecycleHooks.DoCheck:
nodeFlag = viewEngine.NodeFlags.DoCheck;
break;
case LifecycleHooks.OnChanges:
nodeFlag = viewEngine.NodeFlags.OnChanges;
break;
case LifecycleHooks.OnDestroy:
nodeFlag = viewEngine.NodeFlags.OnDestroy;
break;
case LifecycleHooks.OnInit:
nodeFlag = viewEngine.NodeFlags.OnInit;
break;
}
return nodeFlag;
}
function elementBindingDefs(inputAsts: BoundElementPropertyAst[]): o.Expression[] {
return inputAsts.map((inputAst) => {
switch (inputAst.type) {
case PropertyBindingType.Attribute:
return o.literalArr([
o.literal(viewEngine.BindingType.ElementAttribute), o.literal(inputAst.name),
o.literal(inputAst.securityContext)
]);
case PropertyBindingType.Property:
return o.literalArr([
o.literal(viewEngine.BindingType.ElementProperty), o.literal(inputAst.name),
o.literal(inputAst.securityContext)
]);
case PropertyBindingType.Class:
return o.literalArr(
[o.literal(viewEngine.BindingType.ElementClass), o.literal(inputAst.name)]);
case PropertyBindingType.Style:
return o.literalArr([
o.literal(viewEngine.BindingType.ElementStyle), o.literal(inputAst.name),
o.literal(inputAst.unit)
]);
}
});
}
function fixedAttrsDef(elementAst: ElementAst): o.LiteralMapExpr {
const mapResult: {[key: string]: string} = {};
elementAst.attrs.forEach(attrAst => { mapResult[attrAst.name] = attrAst.value; });
elementAst.directives.forEach(dirAst => {
Object.keys(dirAst.directive.hostAttributes).forEach(name => {
const value = dirAst.directive.hostAttributes[name];
const prevValue = mapResult[name];
mapResult[name] = prevValue != null ? mergeAttributeValue(name, prevValue, value) : value;
});
});
const mapEntries: o.LiteralMapEntry[] = [];
// Note: We need to sort to get a defined output order
// for tests and for caching generated artifacts...
Object.keys(mapResult).sort().forEach((attrName) => {
mapEntries.push(new o.LiteralMapEntry(attrName, o.literal(mapResult[attrName]), true));
});
return new o.LiteralMapExpr(mapEntries);
}
function mergeAttributeValue(attrName: string, attrValue1: string, attrValue2: string): string {
if (attrName == CLASS_ATTR || attrName == STYLE_ATTR) {
return `${attrValue1} ${attrValue2}`;
} else {
return attrValue2;
}
}