feat(ivy): update compiler to specification (#21657)

PR Close #21657
This commit is contained in:
Chuck Jazdzewski
2018-01-11 15:37:56 -08:00
committed by Miško Hevery
parent 8c51c276c7
commit 86d9612230
9 changed files with 565 additions and 90 deletions

View File

@ -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);
}
});

View File

@ -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;

View File

@ -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}`;

View File

@ -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};

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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;