
committed by
Miško Hevery

parent
8c51c276c7
commit
86d9612230
@ -19,7 +19,7 @@ import {NgModuleCompiler} from '../ng_module_compiler';
|
||||
import {OutputEmitter} from '../output/abstract_emitter';
|
||||
import * as o from '../output/output_ast';
|
||||
import {ParseError} from '../parse_util';
|
||||
import {compileComponent as compileIvyComponent} from '../render3/r3_view_compiler';
|
||||
import {compileComponent as compileIvyComponent, compileDirective as compileIvyDirective} from '../render3/r3_view_compiler';
|
||||
import {CompiledStylesheet, StyleCompiler} from '../style_compiler';
|
||||
import {SummaryResolver} from '../summary_resolver';
|
||||
import {TemplateAst} from '../template_parser/template_ast';
|
||||
@ -325,7 +325,7 @@ export class AotCompiler {
|
||||
|
||||
const context = this._createOutputContext(fileName);
|
||||
|
||||
// Process all components
|
||||
// Process all components and directives
|
||||
directives.forEach(directiveType => {
|
||||
const directiveMetadata = this._metadataResolver.getDirectiveMetadata(directiveType);
|
||||
if (directiveMetadata.isComponent) {
|
||||
@ -337,6 +337,8 @@ export class AotCompiler {
|
||||
const {template: parsedTemplate} =
|
||||
this._parseTemplate(directiveMetadata, module, module.transitiveModule.directives);
|
||||
compileIvyComponent(context, directiveMetadata, parsedTemplate, this._reflector);
|
||||
} else {
|
||||
compileIvyDirective(context, directiveMetadata, this._reflector);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -9,6 +9,8 @@
|
||||
import * as o from './output/output_ast';
|
||||
import {OutputContext, error} from './util';
|
||||
|
||||
const CONSTANT_PREFIX = '_c';
|
||||
|
||||
export const enum DefinitionKind {Injector, Directive, Component}
|
||||
|
||||
/**
|
||||
@ -48,29 +50,34 @@ export class ConstantPool {
|
||||
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 componentDefinitions = new Map<any, FixupExpression>();
|
||||
|
||||
private nextNameIndex = 0;
|
||||
|
||||
getConstLiteral(literal: o.Expression): o.Expression {
|
||||
getConstLiteral(literal: o.Expression, forceShared?: boolean): o.Expression {
|
||||
const key = this.keyOf(literal);
|
||||
let fixup = this.literals.get(key);
|
||||
let newValue = false;
|
||||
if (!fixup) {
|
||||
fixup = new FixupExpression(literal);
|
||||
this.literals.set(key, fixup);
|
||||
} else if (!fixup.shared) {
|
||||
newValue = true;
|
||||
}
|
||||
|
||||
if ((!newValue && !fixup.shared) || (newValue && forceShared)) {
|
||||
// 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 :
|
||||
this.componentDefinitions :
|
||||
kind == DefinitionKind.Directive ? this.directiveDefinitions : this.injectorDefinitions;
|
||||
let fixup = declarations.get(type);
|
||||
if (!fixup) {
|
||||
@ -97,7 +104,7 @@ export class ConstantPool {
|
||||
*/
|
||||
uniqueName(prefix: string): string { return `${prefix}${this.nextNameIndex++}`; }
|
||||
|
||||
private freshName(): string { return this.uniqueName(`_$`); }
|
||||
private freshName(): string { return this.uniqueName(CONSTANT_PREFIX); }
|
||||
|
||||
private keyOf(expression: o.Expression) {
|
||||
return expression.visitExpression(new KeyVisitor(), null);
|
||||
@ -105,15 +112,22 @@ export class ConstantPool {
|
||||
}
|
||||
|
||||
class KeyVisitor implements o.ExpressionVisitor {
|
||||
visitLiteralExpr(ast: o.LiteralExpr): string { return `${ast.value}`; }
|
||||
visitLiteralExpr(ast: o.LiteralExpr): string {
|
||||
return `${typeof ast.value === 'string' ? '"' + ast.value + '"' : ast.value}`;
|
||||
}
|
||||
visitLiteralArrayExpr(ast: o.LiteralArrayExpr): string {
|
||||
return ast.entries.map(entry => entry.visitExpression(this, null)).join(',');
|
||||
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(',')}`;
|
||||
const mapEntry = (entry: o.LiteralMapEntry) =>
|
||||
`${entry.key}:${entry.value.visitExpression(this, null)}`;
|
||||
return `{${ast.entries.map(mapEntry).join(',')}`;
|
||||
}
|
||||
|
||||
visitExternalExpr(ast: o.ExternalExpr): string {
|
||||
return ast.value.moduleName ? `EX:${ast.value.moduleName}:${ast.value.name}` :
|
||||
`EX:${ast.value.runtime.name}`;
|
||||
}
|
||||
|
||||
visitReadVarExpr = invalid;
|
||||
@ -123,7 +137,6 @@ class KeyVisitor implements o.ExpressionVisitor {
|
||||
visitInvokeMethodExpr = invalid;
|
||||
visitInvokeFunctionExpr = invalid;
|
||||
visitInstantiateExpr = invalid;
|
||||
visitExternalExpr = invalid;
|
||||
visitConditionalExpr = invalid;
|
||||
visitNotExpr = invalid;
|
||||
visitAssertNotNullExpr = invalid;
|
||||
|
@ -40,9 +40,9 @@ export type ReferenceFilter = (reference: o.ExternalReference) => boolean;
|
||||
export class TypeScriptEmitter implements OutputEmitter {
|
||||
emitStatementsAndContext(
|
||||
genFilePath: string, stmts: o.Statement[], preamble: string = '',
|
||||
emitSourceMaps: boolean = true,
|
||||
referenceFilter?: ReferenceFilter): {sourceText: string, context: EmitterVisitorContext} {
|
||||
const converter = new _TsEmitterVisitor(referenceFilter);
|
||||
emitSourceMaps: boolean = true, referenceFilter?: ReferenceFilter,
|
||||
importFilter?: ReferenceFilter): {sourceText: string, context: EmitterVisitorContext} {
|
||||
const converter = new _TsEmitterVisitor(referenceFilter, importFilter);
|
||||
|
||||
const ctx = EmitterVisitorContext.createRoot();
|
||||
|
||||
@ -83,7 +83,9 @@ export class TypeScriptEmitter implements OutputEmitter {
|
||||
class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor {
|
||||
private typeExpression = 0;
|
||||
|
||||
constructor(private referenceFilter?: ReferenceFilter) { super(false); }
|
||||
constructor(private referenceFilter?: ReferenceFilter, private importFilter?: ReferenceFilter) {
|
||||
super(false);
|
||||
}
|
||||
|
||||
importsWithPrefixes = new Map<string, string>();
|
||||
reexports = new Map<string, {name: string, as: string}[]>();
|
||||
@ -390,7 +392,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
||||
ctx.print(null, '(null as any)');
|
||||
return;
|
||||
}
|
||||
if (moduleName) {
|
||||
if (moduleName && (!this.importFilter || !this.importFilter(value))) {
|
||||
let prefix = this.importsWithPrefixes.get(moduleName);
|
||||
if (prefix == null) {
|
||||
prefix = `i${this.importsWithPrefixes.size}`;
|
||||
|
@ -40,6 +40,10 @@ export class Identifiers {
|
||||
|
||||
static containerEnd: o.ExternalReference = {name: 'ɵc', moduleName: CORE};
|
||||
|
||||
static containerRefreshStart: o.ExternalReference = {name: 'ɵcR', moduleName: CORE};
|
||||
|
||||
static containerRefreshEnd: o.ExternalReference = {name: 'ɵcr', moduleName: CORE};
|
||||
|
||||
static directiveCreate: o.ExternalReference = {name: 'ɵD', moduleName: CORE};
|
||||
|
||||
static text: o.ExternalReference = {name: 'ɵT', moduleName: CORE};
|
||||
@ -61,6 +65,8 @@ export class Identifiers {
|
||||
static bind9: o.ExternalReference = {name: 'ɵb9', moduleName: CORE};
|
||||
static bindV: o.ExternalReference = {name: 'ɵbV', moduleName: CORE};
|
||||
|
||||
static memory: o.ExternalReference = {name: 'ɵm', moduleName: CORE};
|
||||
|
||||
static refreshComponent: o.ExternalReference = {name: 'ɵr', moduleName: CORE};
|
||||
|
||||
static directiveLifeCycle: o.ExternalReference = {name: 'ɵl', moduleName: CORE};
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {CompileDirectiveMetadata, CompilePipeSummary, CompileTokenMetadata, CompileTypeMetadata, identifierName, rendererTypeName, tokenReference, viewClassName} from '../compile_metadata';
|
||||
import {CompileDirectiveMetadata, CompilePipeSummary, CompileTokenMetadata, CompileTypeMetadata, flatten, identifierName, rendererTypeName, tokenReference, viewClassName} from '../compile_metadata';
|
||||
import {CompileReflector} from '../compile_reflector';
|
||||
import {BindingForm, BuiltinConverter, EventHandlerVars, LocalResolver, convertActionBinding, convertPropertyBinding, convertPropertyBindingBuiltins} from '../compiler_util/expression_converter';
|
||||
import {ConstantPool, DefinitionKind} from '../constant_pool';
|
||||
@ -21,6 +21,7 @@ import {OutputContext, error} from '../util';
|
||||
import {Identifiers as R3} from './r3_identifiers';
|
||||
|
||||
|
||||
|
||||
/** Name of the context parameter passed into a template function */
|
||||
const CONTEXT_NAME = 'ctx';
|
||||
|
||||
@ -30,15 +31,40 @@ const CREATION_MODE_FLAG = 'cm';
|
||||
/** Name of the temporary to use during data binding */
|
||||
const TEMPORARY_NAME = '_t';
|
||||
|
||||
/** The prefix reference variables */
|
||||
const REFERENCE_PREFIX = '_r';
|
||||
|
||||
export function compileDirective(
|
||||
outputCtx: OutputContext, directive: CompileDirectiveMetadata, reflector: CompileReflector) {
|
||||
const definitionMapValues: {key: string, quoted: boolean, value: o.Expression}[] = [];
|
||||
|
||||
// e.g. `factory: () => new MyApp(injectElementRef())`
|
||||
const templateFactory = createFactory(directive.type, outputCtx, reflector);
|
||||
definitionMapValues.push({key: 'factory', value: templateFactory, quoted: false});
|
||||
|
||||
const className = identifierName(directive.type) !;
|
||||
className || error(`Cannot resolver the name of ${directive.type}`);
|
||||
|
||||
// Create the partial class to be merged with the actual class.
|
||||
outputCtx.statements.push(new o.ClassStmt(
|
||||
/* name */ className,
|
||||
/* parent */ null,
|
||||
/* fields */[new o.ClassField(
|
||||
/* name */ 'ngDirectiveDef',
|
||||
/* type */ o.INFERRED_TYPE,
|
||||
/* modifiers */[o.StmtModifier.Static],
|
||||
/* initializer */ o.importExpr(R3.defineDirective).callFn([o.literalMap(
|
||||
definitionMapValues)]))],
|
||||
/* getters */[],
|
||||
/* constructorMethod */ new o.ClassMethod(null, [], []),
|
||||
/* methods */[]));
|
||||
}
|
||||
|
||||
export function compileComponent(
|
||||
outputCtx: OutputContext, component: CompileDirectiveMetadata, template: TemplateAst[],
|
||||
reflector: CompileReflector) {
|
||||
const definitionMapValues: {key: string, quoted: boolean, value: o.Expression}[] = [];
|
||||
|
||||
// e.g. `type: MyApp`
|
||||
definitionMapValues.push(
|
||||
{key: 'type', value: outputCtx.importExpr(component.type.reference), quoted: false});
|
||||
|
||||
// e.g. `tag: 'my-app'
|
||||
// This is optional and only included if the first selector of a component has element.
|
||||
const selector = component.selector && CssSelector.parse(component.selector);
|
||||
@ -54,24 +80,27 @@ export function compileComponent(
|
||||
if (selectorAttributes.length) {
|
||||
definitionMapValues.push({
|
||||
key: 'attrs',
|
||||
value: outputCtx.constantPool.getConstLiteral(o.literalArr(selectorAttributes.map(
|
||||
value => value != null ? o.literal(value) : o.literal(undefined)))),
|
||||
value: outputCtx.constantPool.getConstLiteral(
|
||||
o.literalArr(selectorAttributes.map(
|
||||
value => value != null ? o.literal(value) : o.literal(undefined))),
|
||||
/* forceShared */ true),
|
||||
quoted: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// e.g. `template: function(_ctx, _cm) {...}`
|
||||
const templateFunctionExpression =
|
||||
new TemplateDefinitionBuilder(outputCtx, outputCtx.constantPool, CONTEXT_NAME)
|
||||
.buildTemplateFunction(template);
|
||||
definitionMapValues.push({key: 'template', value: templateFunctionExpression, quoted: false});
|
||||
|
||||
|
||||
// e.g. `factory: () => new MyApp(injectElementRef())`
|
||||
const templateFactory = createFactory(component.type, outputCtx, reflector);
|
||||
definitionMapValues.push({key: 'factory', value: templateFactory, quoted: false});
|
||||
|
||||
// e.g. `template: function(_ctx, _cm) {...}`
|
||||
const templateFunctionExpression =
|
||||
new TemplateDefinitionBuilder(
|
||||
outputCtx, outputCtx.constantPool, CONTEXT_NAME, ROOT_SCOPE.nestedScope())
|
||||
.buildTemplateFunction(template);
|
||||
definitionMapValues.push({key: 'template', value: templateFunctionExpression, quoted: false});
|
||||
|
||||
|
||||
const className = identifierName(component.type) !;
|
||||
className || error(`Cannot resolver the name of ${component.type}`);
|
||||
|
||||
@ -136,9 +165,49 @@ function interpolate(args: o.Expression[]): o.Expression {
|
||||
return o.importExpr(R3.bindV).callFn(args);
|
||||
}
|
||||
|
||||
class TemplateDefinitionBuilder implements TemplateAstVisitor {
|
||||
class BindingScope {
|
||||
private map = new Map<string, o.Expression>();
|
||||
private referenceNameIndex = 0;
|
||||
|
||||
constructor(private parent: BindingScope|null) {}
|
||||
|
||||
get(name: string): o.Expression|null {
|
||||
let current: BindingScope|null = this;
|
||||
while (current) {
|
||||
const value = current.map.get(name);
|
||||
if (value != null) {
|
||||
// Cache the value locally.
|
||||
this.map.set(name, value);
|
||||
return value;
|
||||
}
|
||||
current = current.parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
set(name: string, variableName: string): BindingScope {
|
||||
!this.map.has(name) ||
|
||||
error(`The name ${name} is already defined in scope to be ${this.map.get(name)}`);
|
||||
this.map.set(name, o.variable(variableName));
|
||||
return this;
|
||||
}
|
||||
|
||||
nestedScope(): BindingScope { return new BindingScope(this); }
|
||||
|
||||
freshReferenceName(): string {
|
||||
let current: BindingScope|null = this;
|
||||
// Find the top scope as it maintains the global reference count
|
||||
while (current.parent) current = current.parent;
|
||||
return `${REFERENCE_PREFIX}${current.referenceNameIndex++}`;
|
||||
}
|
||||
}
|
||||
|
||||
const ROOT_SCOPE = new BindingScope(null).set('$event', '$event');
|
||||
|
||||
class TemplateDefinitionBuilder implements TemplateAstVisitor, LocalResolver {
|
||||
private _dataIndex = 0;
|
||||
private _bindingContext = 0;
|
||||
private _referenceIndex = 0;
|
||||
private _temporaryAllocated = false;
|
||||
private _prefix: o.Statement[] = [];
|
||||
private _creationMode: o.Statement[] = [];
|
||||
@ -151,7 +220,7 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor {
|
||||
|
||||
constructor(
|
||||
private outputCtx: OutputContext, private constantPool: ConstantPool,
|
||||
private contextParameter: string, private level = 0) {}
|
||||
private contextParameter: string, private bindingScope: BindingScope, private level = 0) {}
|
||||
|
||||
buildTemplateFunction(asts: TemplateAst[]): o.FunctionExpr {
|
||||
templateVisitAll(this, asts);
|
||||
@ -173,7 +242,7 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor {
|
||||
// Host mode (i.e. Comp.h(...))
|
||||
...this._hostMode,
|
||||
|
||||
// Refesh mode (i.e. Comp.r(...))
|
||||
// Refresh mode (i.e. Comp.r(...))
|
||||
...this._refreshMode,
|
||||
|
||||
// Nested templates (i.e. function CompTemplate() {})
|
||||
@ -182,31 +251,91 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor {
|
||||
o.INFERRED_TYPE);
|
||||
}
|
||||
|
||||
getLocal(name: string): o.Expression|null { return this.bindingScope.get(name); }
|
||||
|
||||
// TODO(chuckj): Implement ng-content
|
||||
visitNgContent = unknown;
|
||||
|
||||
private _computeDirectivesArray(directives: DirectiveAst[]) {
|
||||
const directiveIndexMap = new Map<any, number>();
|
||||
const directiveExpressions: o.Expression[] =
|
||||
directives.filter(directive => !directive.directive.isComponent).map(directive => {
|
||||
directiveIndexMap.set(directive.directive.type.reference, this.allocateDataSlot());
|
||||
return this.typeReference(directive.directive.type.reference);
|
||||
});
|
||||
return {
|
||||
directivesArray: directiveExpressions.length ?
|
||||
this.constantPool.getConstLiteral(
|
||||
o.literalArr(directiveExpressions), /* forceShared */ true) :
|
||||
o.literal(null),
|
||||
directiveIndexMap
|
||||
};
|
||||
}
|
||||
|
||||
visitElement(ast: ElementAst) {
|
||||
let bindingCount = 0;
|
||||
const elementIndex = this.allocateNode();
|
||||
const elementIndex = this.allocateDataSlot();
|
||||
let componentIndex: number|undefined = undefined;
|
||||
const referenceDataSlots = new Map<string, number>();
|
||||
|
||||
// Element creation mode
|
||||
const component = findComponent(ast.directives);
|
||||
const nullNode = o.literal(null, o.INFERRED_TYPE);
|
||||
const parameters: o.Expression[] = [o.literal(elementIndex)];
|
||||
|
||||
// Add component type or element tag
|
||||
if (component) {
|
||||
parameters.push(this.typeReference(component.directive.type.reference));
|
||||
componentIndex = this.allocateDataSlot();
|
||||
} else {
|
||||
parameters.push(o.literal(ast.name));
|
||||
}
|
||||
|
||||
// Add attributes array
|
||||
const attributes: o.Expression[] = [];
|
||||
for (let attr of ast.attrs) {
|
||||
attributes.push(o.literal(attr.name), o.literal(attr.value));
|
||||
}
|
||||
parameters.push(
|
||||
attributes.length > 0 ?
|
||||
this.constantPool.getConstLiteral(o.literalArr(attributes), /* forceShared */ true) :
|
||||
nullNode);
|
||||
|
||||
if (attributes.length !== 0) {
|
||||
parameters.push(this.constantPool.getConstLiteral(o.literalArr(attributes)));
|
||||
// Add directives array
|
||||
const {directivesArray, directiveIndexMap} = this._computeDirectivesArray(ast.directives);
|
||||
parameters.push(directiveIndexMap.size > 0 ? directivesArray : nullNode);
|
||||
|
||||
if (component && componentIndex != null) {
|
||||
// Record the data slot for the component
|
||||
directiveIndexMap.set(component.directive.type.reference, componentIndex);
|
||||
}
|
||||
|
||||
// Add references array
|
||||
if (ast.references && ast.references.length > 0) {
|
||||
const references =
|
||||
flatten(ast.references.map(reference => {
|
||||
const slot = this.allocateDataSlot();
|
||||
referenceDataSlots.set(reference.name, slot);
|
||||
// Generate the update temporary.
|
||||
const variableName = this.bindingScope.freshReferenceName();
|
||||
this._bindingMode.push(o.variable(variableName, o.INFERRED_TYPE)
|
||||
.set(o.importExpr(R3.memory).callFn([o.literal(slot)]))
|
||||
.toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Final]));
|
||||
this.bindingScope.set(reference.name, variableName);
|
||||
return [reference.name, reference.originalValue];
|
||||
})).map(value => o.literal(value));
|
||||
parameters.push(
|
||||
this.constantPool.getConstLiteral(o.literalArr(references), /* forceShared */ true));
|
||||
} else {
|
||||
parameters.push(nullNode);
|
||||
}
|
||||
|
||||
// Remove trailing null nodes as they are implied.
|
||||
while (parameters[parameters.length - 1] === nullNode) {
|
||||
parameters.pop();
|
||||
}
|
||||
|
||||
// Generate the instruction create element instruction
|
||||
this.instruction(this._creationMode, ast.sourceSpan, R3.createElement, ...parameters);
|
||||
|
||||
const implicit = o.variable(this.contextParameter);
|
||||
@ -216,9 +345,9 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor {
|
||||
if (input.isAnimation) {
|
||||
this.unsupported('animations');
|
||||
}
|
||||
// TODO(chuckj): Builtins transform?
|
||||
// TODO(chuckj): Built-in transform?
|
||||
const convertedBinding = convertPropertyBinding(
|
||||
null, implicit, input.value, this.bindingContext(), BindingForm.TrySimple, interpolate);
|
||||
this, implicit, input.value, this.bindingContext(), BindingForm.TrySimple, interpolate);
|
||||
this._bindingMode.push(...convertedBinding.stmts);
|
||||
const parameters =
|
||||
[o.literal(elementIndex), o.literal(input.name), convertedBinding.currValExpr];
|
||||
@ -234,19 +363,20 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor {
|
||||
}
|
||||
|
||||
// Generate directives input bindings
|
||||
this._visitDirectives(ast.directives, implicit, elementIndex);
|
||||
this._visitDirectives(ast.directives, implicit, elementIndex, directiveIndexMap);
|
||||
|
||||
// Traverse element child nodes
|
||||
templateVisitAll(this, ast.children);
|
||||
|
||||
|
||||
// Finish element construction mode.
|
||||
this.instruction(this._creationMode, ast.endSourceSpan || ast.sourceSpan, R3.elementEnd);
|
||||
}
|
||||
|
||||
private _visitDirectives(directives: DirectiveAst[], implicit: o.Expression, nodeIndex: number) {
|
||||
private _visitDirectives(
|
||||
directives: DirectiveAst[], implicit: o.Expression, nodeIndex: number,
|
||||
directiveIndexMap: Map<any, number>) {
|
||||
for (let directive of directives) {
|
||||
const directiveIndex = this.allocateDirective();
|
||||
const directiveIndex = directiveIndexMap.get(directive.directive.type.reference);
|
||||
|
||||
// Creation mode
|
||||
// e.g. D(0, TodoComponentDef.n(), TodoComponentDef);
|
||||
@ -258,28 +388,16 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor {
|
||||
// node is referenced multiple times to know that it must generate the reference into a
|
||||
// temporary.
|
||||
|
||||
this.instruction(
|
||||
this._creationMode, directive.sourceSpan, R3.directiveCreate, o.literal(directiveIndex),
|
||||
this.definitionOf(directiveType, kind)
|
||||
.callMethod(R3.NEW_METHOD, [], directive.sourceSpan),
|
||||
this.definitionOf(directiveType, kind));
|
||||
|
||||
// Bindings
|
||||
for (const input of directive.inputs) {
|
||||
const convertedBinding = convertPropertyBinding(
|
||||
null, implicit, input.value, this.bindingContext(), BindingForm.TrySimple, interpolate);
|
||||
this, implicit, input.value, this.bindingContext(), BindingForm.TrySimple, interpolate);
|
||||
this._bindingMode.push(...convertedBinding.stmts);
|
||||
this.instruction(
|
||||
this._bindingMode, directive.sourceSpan, R3.elementProperty,
|
||||
o.literal(input.templateName), o.literal(nodeIndex), convertedBinding.currValExpr);
|
||||
}
|
||||
|
||||
// e.g. TodoComponentDef.h(0, 0);
|
||||
this._hostMode.push(
|
||||
this.definitionOf(directiveType, kind)
|
||||
.callMethod(R3.HOST_BINDING_METHOD, [o.literal(directiveIndex), o.literal(nodeIndex)])
|
||||
.toStmt());
|
||||
|
||||
// e.g. TodoComponentDef.r(0, 0);
|
||||
this._refreshMode.push(
|
||||
this.definitionOf(directiveType, kind)
|
||||
@ -289,32 +407,35 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor {
|
||||
}
|
||||
|
||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst) {
|
||||
const templateIndex = this.allocateNode();
|
||||
const templateIndex = this.allocateDataSlot();
|
||||
|
||||
const templateName = `C${templateIndex}Template`;
|
||||
const templateContext = `ctx${this.level}`;
|
||||
|
||||
// TODO(chuckj): attrs?
|
||||
const {directivesArray, directiveIndexMap} = this._computeDirectivesArray(ast.directives);
|
||||
|
||||
// e.g. C(1, C1Template)
|
||||
this.instruction(
|
||||
this._creationMode, ast.sourceSpan, R3.containerCreate, o.literal(templateIndex),
|
||||
o.variable(templateName));
|
||||
directivesArray, o.variable(templateName));
|
||||
|
||||
// Generate directies
|
||||
// e.g. Cr(1)
|
||||
this.instruction(
|
||||
this._refreshMode, ast.sourceSpan, R3.containerRefreshStart, o.literal(templateIndex));
|
||||
|
||||
// Generate directives
|
||||
this._visitDirectives(
|
||||
ast.directives, o.variable(this.contextParameter),
|
||||
// TODO(chuckj): This should be the element index of the element that contained the template
|
||||
templateIndex);
|
||||
ast.directives, o.variable(this.contextParameter), templateIndex, directiveIndexMap);
|
||||
|
||||
// e.g. cr();
|
||||
this.instruction(this._refreshMode, ast.sourceSpan, R3.containerRefreshEnd);
|
||||
|
||||
// Create the template function
|
||||
const templateVisitor = new TemplateDefinitionBuilder(
|
||||
this.outputCtx, this.constantPool, templateContext, this.level + 1);
|
||||
this.outputCtx, this.constantPool, templateContext, this.bindingScope.nestedScope(),
|
||||
this.level + 1);
|
||||
const templateFunctionExpr = templateVisitor.buildTemplateFunction(ast.children);
|
||||
this._postfix.push(templateFunctionExpr.toDeclStmt(templateName, null));
|
||||
|
||||
// Terminate the definition
|
||||
this.instruction(this._creationMode, ast.sourceSpan, R3.containerEnd);
|
||||
}
|
||||
|
||||
// These should be handled in the template or element directly.
|
||||
@ -325,7 +446,7 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor {
|
||||
readonly visitAttr = invalid;
|
||||
|
||||
visitBoundText(ast: BoundTextAst) {
|
||||
const nodeIndex = this.allocateNode();
|
||||
const nodeIndex = this.allocateDataSlot();
|
||||
|
||||
// Creation mode
|
||||
this.instruction(this._creationMode, ast.sourceSpan, R3.text, o.literal(nodeIndex));
|
||||
@ -333,20 +454,21 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor {
|
||||
// Refresh mode
|
||||
this.instruction(
|
||||
this._refreshMode, ast.sourceSpan, R3.textCreateBound, o.literal(nodeIndex),
|
||||
this.bind(o.variable(this.contextParameter), ast.value, ast.sourceSpan));
|
||||
this.bind(o.variable(CONTEXT_NAME), ast.value, ast.sourceSpan));
|
||||
}
|
||||
|
||||
visitText(ast: TextAst) {
|
||||
// Text is defined in creation mode only.
|
||||
this.instruction(this._creationMode, ast.sourceSpan, R3.text, o.literal(ast.value));
|
||||
this.instruction(
|
||||
this._creationMode, ast.sourceSpan, R3.text, o.literal(this.allocateDataSlot()),
|
||||
o.literal(ast.value));
|
||||
}
|
||||
|
||||
// These should be handled in the template or element directly
|
||||
readonly visitDirective = invalid;
|
||||
readonly visitDirectiveProperty = invalid;
|
||||
|
||||
private allocateDirective() { return this._dataIndex++; }
|
||||
private allocateNode() { return this._dataIndex++; }
|
||||
private allocateDataSlot() { return this._dataIndex++; }
|
||||
private bindingContext() { return `${this._bindingContext++}`; }
|
||||
|
||||
private instruction(
|
||||
@ -373,13 +495,13 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor {
|
||||
|
||||
private convertPropertyBinding(implicit: o.Expression, value: AST): o.Expression {
|
||||
const convertedPropertyBinding = convertPropertyBinding(
|
||||
null, implicit, value, this.bindingContext(), BindingForm.TrySimple, interpolate);
|
||||
this, implicit, value, this.bindingContext(), BindingForm.TrySimple, interpolate);
|
||||
this._refreshMode.push(...convertedPropertyBinding.stmts);
|
||||
return convertedPropertyBinding.currValExpr;
|
||||
}
|
||||
|
||||
private bind(implicit: o.Expression, value: AST, sourceSpan: ParseSourceSpan): o.Expression {
|
||||
return o.importExpr(R3.bind).callFn([this.convertPropertyBinding(implicit, value)]);
|
||||
return this.convertPropertyBinding(implicit, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,8 +110,8 @@ export class BoundEventAst implements TemplateAst {
|
||||
*/
|
||||
export class ReferenceAst implements TemplateAst {
|
||||
constructor(
|
||||
public name: string, public value: CompileTokenMetadata, public sourceSpan: ParseSourceSpan) {
|
||||
}
|
||||
public name: string, public value: CompileTokenMetadata, public originalValue: string,
|
||||
public sourceSpan: ParseSourceSpan) {}
|
||||
visit(visitor: TemplateAstVisitor, context: any): any {
|
||||
return visitor.visitReference(this, context);
|
||||
}
|
||||
|
@ -574,7 +574,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
if ((elOrDirRef.value.length === 0 && directive.isComponent) ||
|
||||
(elOrDirRef.isReferenceToDirective(directive))) {
|
||||
targetReferences.push(new ReferenceAst(
|
||||
elOrDirRef.name, createTokenForReference(directive.type.reference),
|
||||
elOrDirRef.name, createTokenForReference(directive.type.reference), elOrDirRef.value,
|
||||
elOrDirRef.sourceSpan));
|
||||
matchedReferences.add(elOrDirRef.name);
|
||||
}
|
||||
@ -598,7 +598,8 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
if (isTemplateElement) {
|
||||
refToken = createTokenForExternalReference(this.reflector, Identifiers.TemplateRef);
|
||||
}
|
||||
targetReferences.push(new ReferenceAst(elOrDirRef.name, refToken, elOrDirRef.sourceSpan));
|
||||
targetReferences.push(
|
||||
new ReferenceAst(elOrDirRef.name, refToken, elOrDirRef.value, elOrDirRef.sourceSpan));
|
||||
}
|
||||
});
|
||||
return directiveAsts;
|
||||
|
Reference in New Issue
Block a user