refactor(compiler): minor cleanups

This commit is contained in:
Tobias Bosch 2016-10-24 11:11:31 -07:00 committed by vsavkin
parent 4cac650675
commit fe299f4dfc
11 changed files with 184 additions and 118 deletions

View File

@ -22,13 +22,14 @@ export function createDiTokenExpression(token: CompileTokenMetadata): o.Expressi
} }
} }
export function createFastArray(values: o.Expression[]): o.Expression { export function createInlineArray(values: o.Expression[]): o.Expression {
if (values.length === 0) { if (values.length === 0) {
return o.importExpr(resolveIdentifier(Identifiers.EMPTY_FAST_ARRAY)); return o.importExpr(resolveIdentifier(Identifiers.EMPTY_INLINE_ARRAY));
} }
const index = Math.ceil(values.length / 2) - 1; const log2 = Math.log(values.length) / Math.log(2);
const identifierSpec = index < Identifiers.fastArrays.length ? Identifiers.fastArrays[index] : const index = Math.ceil(log2);
Identifiers.FastArrayDynamic; const identifierSpec = index < Identifiers.inlineArrays.length ? Identifiers.inlineArrays[index] :
Identifiers.InlineArrayDynamic;
const identifier = resolveIdentifier(identifierSpec); const identifier = resolveIdentifier(identifierSpec);
return o.importExpr(identifier).instantiate([ return o.importExpr(identifier).instantiate([
<o.Expression>o.literal(values.length) <o.Expression>o.literal(values.length)

View File

@ -48,9 +48,9 @@ export class DirectiveWrapperCompiler {
compile(dirMeta: CompileDirectiveMetadata): DirectiveWrapperCompileResult { compile(dirMeta: CompileDirectiveMetadata): DirectiveWrapperCompileResult {
const builder = new DirectiveWrapperBuilder(this.compilerConfig, dirMeta); const builder = new DirectiveWrapperBuilder(this.compilerConfig, dirMeta);
Object.keys(dirMeta.inputs).forEach((inputFieldName) => { Object.keys(dirMeta.inputs).forEach((inputFieldName) => {
createCheckInputMethod(inputFieldName, builder); addCheckInputMethod(inputFieldName, builder);
}); });
createDetectChangesInternalMethod(builder); addDetectChangesInternalMethod(builder);
const classStmt = builder.build(); const classStmt = builder.build();
return new DirectiveWrapperCompileResult([classStmt], classStmt.name); return new DirectiveWrapperCompileResult([classStmt], classStmt.name);
} }
@ -102,12 +102,12 @@ class DirectiveWrapperBuilder implements ClassBuilder {
return createClassStmt({ return createClassStmt({
name: DirectiveWrapperCompiler.dirWrapperClassName(this.dirMeta.type), name: DirectiveWrapperCompiler.dirWrapperClassName(this.dirMeta.type),
ctorParams: dirDepParamNames.map((paramName) => new o.FnParam(paramName, o.DYNAMIC_TYPE)), ctorParams: dirDepParamNames.map((paramName) => new o.FnParam(paramName, o.DYNAMIC_TYPE)),
builders: [{fields: fields, ctorStmts: ctorStmts}, this] builders: [{fields, ctorStmts}, this]
}) });
} }
} }
function createDetectChangesInternalMethod(builder: DirectiveWrapperBuilder) { function addDetectChangesInternalMethod(builder: DirectiveWrapperBuilder) {
const changedVar = o.variable('changed'); const changedVar = o.variable('changed');
const stmts: o.Statement[] = [ const stmts: o.Statement[] = [
changedVar.set(o.THIS_EXPR.prop(CHANGED_FIELD_NAME)).toDeclStmt(), changedVar.set(o.THIS_EXPR.prop(CHANGED_FIELD_NAME)).toDeclStmt(),
@ -157,7 +157,7 @@ function createDetectChangesInternalMethod(builder: DirectiveWrapperBuilder) {
stmts, o.BOOL_TYPE)); stmts, o.BOOL_TYPE));
} }
function createCheckInputMethod(input: string, builder: DirectiveWrapperBuilder) { function addCheckInputMethod(input: string, builder: DirectiveWrapperBuilder) {
const fieldName = `_${input}`; const fieldName = `_${input}`;
const fieldExpr = o.THIS_EXPR.prop(fieldName); const fieldExpr = o.THIS_EXPR.prop(fieldName);
// private is fine here as no child view will reference the cached value... // private is fine here as no child view will reference the cached value...

View File

@ -294,23 +294,24 @@ export class Identifiers {
}; };
// This is just the interface! // This is just the interface!
static FastArray: static InlineArray:
IdentifierSpec = {name: 'FastArray', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: null}; IdentifierSpec = {name: 'InlineArray', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: null};
static fastArrays: IdentifierSpec[] = [ static inlineArrays: IdentifierSpec[] = [
{name: 'FastArray2', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.FastArray2}, {name: 'InlineArray2', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.InlineArray2},
{name: 'FastArray4', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.FastArray4}, {name: 'InlineArray2', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.InlineArray2},
{name: 'FastArray8', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.FastArray8}, {name: 'InlineArray4', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.InlineArray4},
{name: 'FastArray16', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.FastArray16}, {name: 'InlineArray8', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.InlineArray8},
{name: 'InlineArray16', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.InlineArray16},
]; ];
static EMPTY_FAST_ARRAY: IdentifierSpec = { static EMPTY_INLINE_ARRAY: IdentifierSpec = {
name: 'EMPTY_FAST_ARRAY', name: 'EMPTY_INLINE_ARRAY',
moduleUrl: VIEW_UTILS_MODULE_URL, moduleUrl: VIEW_UTILS_MODULE_URL,
runtime: view_utils.EMPTY_FAST_ARRAY runtime: view_utils.EMPTY_INLINE_ARRAY
}; };
static FastArrayDynamic: IdentifierSpec = { static InlineArrayDynamic: IdentifierSpec = {
name: 'FastArrayDynamic', name: 'InlineArrayDynamic',
moduleUrl: VIEW_UTILS_MODULE_URL, moduleUrl: VIEW_UTILS_MODULE_URL,
runtime: view_utils.FastArrayDynamic runtime: view_utils.InlineArrayDynamic
}; };
} }

View File

@ -84,14 +84,14 @@ export class NgModuleCompiler {
} }
class _InjectorBuilder implements ClassBuilder { class _InjectorBuilder implements ClassBuilder {
private _tokens: CompileTokenMetadata[] = [];
private _instances = new Map<any, o.Expression>();
fields: o.ClassField[] = []; fields: o.ClassField[] = [];
private _createStmts: o.Statement[] = [];
private _destroyStmts: o.Statement[] = [];
getters: o.ClassGetter[] = []; getters: o.ClassGetter[] = [];
methods: o.ClassMethod[] = []; methods: o.ClassMethod[] = [];
ctorStmts: o.Statement[] = []; ctorStmts: o.Statement[] = [];
private _tokens: CompileTokenMetadata[] = [];
private _instances = new Map<any, o.Expression>();
private _createStmts: o.Statement[] = [];
private _destroyStmts: o.Statement[] = [];
constructor( constructor(
private _ngModuleMeta: CompileNgModuleMetadata, private _ngModuleMeta: CompileNgModuleMetadata,
@ -154,7 +154,7 @@ class _InjectorBuilder implements ClassBuilder {
parent: o.importExpr( parent: o.importExpr(
resolveIdentifier(Identifiers.NgModuleInjector), [o.importType(this._ngModuleMeta.type)]), resolveIdentifier(Identifiers.NgModuleInjector), [o.importType(this._ngModuleMeta.type)]),
parentArgs: parentArgs, parentArgs: parentArgs,
builders: [{methods: methods}, this] builders: [{methods}, this]
}); });
} }

View File

@ -28,9 +28,6 @@ const STYLE_PREFIX = 'style';
const ANIMATE_PROP_PREFIX = 'animate-'; const ANIMATE_PROP_PREFIX = 'animate-';
/**
* Type of a parsed property
*/
export enum BoundPropertyType { export enum BoundPropertyType {
DEFAULT, DEFAULT,
LITERAL_ATTR, LITERAL_ATTR,
@ -55,18 +52,17 @@ export class BoundProperty {
*/ */
export class BindingParser { export class BindingParser {
pipesByName: Map<string, CompilePipeMetadata> = new Map(); pipesByName: Map<string, CompilePipeMetadata> = new Map();
errors: ParseError[] = [];
constructor( constructor(
private _exprParser: Parser, private _interpolationConfig: InterpolationConfig, private _exprParser: Parser, private _interpolationConfig: InterpolationConfig,
protected _schemaRegistry: ElementSchemaRegistry, protected _schemas: SchemaMetadata[], private _schemaRegistry: ElementSchemaRegistry, private _schemas: SchemaMetadata[],
pipes: CompilePipeMetadata[]) { pipes: CompilePipeMetadata[], private _targetErrors: ParseError[]) {
pipes.forEach(pipe => this.pipesByName.set(pipe.name, pipe)); pipes.forEach(pipe => this.pipesByName.set(pipe.name, pipe));
} }
createDirectiveHostPropertyAsts( createDirectiveHostPropertyAsts(
elementName: string, hostProps: {[key: string]: string}, sourceSpan: ParseSourceSpan, elementName: string, hostProps: {[key: string]: string},
targetPropertyAsts: BoundElementPropertyAst[]) { sourceSpan: ParseSourceSpan): BoundElementPropertyAst[] {
if (hostProps) { if (hostProps) {
const boundProps: BoundProperty[] = []; const boundProps: BoundProperty[] = [];
Object.keys(hostProps).forEach(propName => { Object.keys(hostProps).forEach(propName => {
@ -74,30 +70,30 @@ export class BindingParser {
if (typeof expression === 'string') { if (typeof expression === 'string') {
this.parsePropertyBinding(propName, expression, true, sourceSpan, [], boundProps); this.parsePropertyBinding(propName, expression, true, sourceSpan, [], boundProps);
} else { } else {
this.reportError( this._reportError(
`Value of the host property binding "${propName}" needs to be a string representing an expression but got "${expression}" (${typeof expression})`, `Value of the host property binding "${propName}" needs to be a string representing an expression but got "${expression}" (${typeof expression})`,
sourceSpan); sourceSpan);
} }
}); });
boundProps.forEach( return boundProps.map((prop) => this.createElementPropertyAst(elementName, prop));
(prop) => { targetPropertyAsts.push(this.createElementPropertyAst(elementName, prop)); });
} }
} }
createDirectiveHostEventAsts( createDirectiveHostEventAsts(hostListeners: {[key: string]: string}, sourceSpan: ParseSourceSpan):
hostListeners: {[key: string]: string}, sourceSpan: ParseSourceSpan, BoundEventAst[] {
targetEventAsts: BoundEventAst[]) {
if (hostListeners) { if (hostListeners) {
const targetEventAsts: BoundEventAst[] = [];
Object.keys(hostListeners).forEach(propName => { Object.keys(hostListeners).forEach(propName => {
const expression = hostListeners[propName]; const expression = hostListeners[propName];
if (typeof expression === 'string') { if (typeof expression === 'string') {
this.parseEvent(propName, expression, sourceSpan, [], targetEventAsts); this.parseEvent(propName, expression, sourceSpan, [], targetEventAsts);
} else { } else {
this.reportError( this._reportError(
`Value of the host listener "${propName}" needs to be a string representing an expression but got "${expression}" (${typeof expression})`, `Value of the host listener "${propName}" needs to be a string representing an expression but got "${expression}" (${typeof expression})`,
sourceSpan); sourceSpan);
} }
}); });
return targetEventAsts;
} }
} }
@ -115,7 +111,7 @@ export class BindingParser {
} }
return ast; return ast;
} catch (e) { } catch (e) {
this.reportError(`${e}`, sourceSpan); this._reportError(`${e}`, sourceSpan);
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo); return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
} }
} }
@ -150,10 +146,10 @@ export class BindingParser {
} }
}); });
bindingsResult.warnings.forEach( bindingsResult.warnings.forEach(
(warning) => { this.reportError(warning, sourceSpan, ParseErrorLevel.WARNING); }); (warning) => { this._reportError(warning, sourceSpan, ParseErrorLevel.WARNING); });
return bindingsResult.templateBindings; return bindingsResult.templateBindings;
} catch (e) { } catch (e) {
this.reportError(`${e}`, sourceSpan); this._reportError(`${e}`, sourceSpan);
return []; return [];
} }
} }
@ -163,8 +159,8 @@ export class BindingParser {
targetProps: BoundProperty[]) { targetProps: BoundProperty[]) {
if (_isAnimationLabel(name)) { if (_isAnimationLabel(name)) {
name = name.substring(1); name = name.substring(1);
if (isPresent(value) && value.length > 0) { if (value) {
this.reportError( this._reportError(
`Assigning animation triggers via @prop="exp" attributes with an expression is invalid.` + `Assigning animation triggers via @prop="exp" attributes with an expression is invalid.` +
` Use property bindings (e.g. [@prop]="exp") or use an attribute without a value (e.g. @prop) instead.`, ` Use property bindings (e.g. [@prop]="exp") or use an attribute without a value (e.g. @prop) instead.`,
sourceSpan, ParseErrorLevel.FATAL); sourceSpan, ParseErrorLevel.FATAL);
@ -239,7 +235,7 @@ export class BindingParser {
this._checkPipes(ast, sourceSpan); this._checkPipes(ast, sourceSpan);
return ast; return ast;
} catch (e) { } catch (e) {
this.reportError(`${e}`, sourceSpan); this._reportError(`${e}`, sourceSpan);
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo); return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
} }
} }
@ -271,7 +267,7 @@ export class BindingParser {
`\n1. If '${elementName}' is an Angular component and it has '${boundPropertyName}' input, then verify that it is part of this module.` + `\n1. If '${elementName}' is an Angular component and it has '${boundPropertyName}' input, then verify that it is part of this module.` +
`\n2. If '${elementName}' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message.\n`; `\n2. If '${elementName}' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message.\n`;
} }
this.reportError(errorMsg, boundProp.sourceSpan); this._reportError(errorMsg, boundProp.sourceSpan);
} }
} else { } else {
if (parts[0] == ATTRIBUTE_PREFIX) { if (parts[0] == ATTRIBUTE_PREFIX) {
@ -299,7 +295,7 @@ export class BindingParser {
bindingType = PropertyBindingType.Style; bindingType = PropertyBindingType.Style;
securityContext = SecurityContext.STYLE; securityContext = SecurityContext.STYLE;
} else { } else {
this.reportError(`Invalid property name '${boundProp.name}'`, boundProp.sourceSpan); this._reportError(`Invalid property name '${boundProp.name}'`, boundProp.sourceSpan);
bindingType = null; bindingType = null;
securityContext = null; securityContext = null;
} }
@ -336,13 +332,13 @@ export class BindingParser {
break; break;
default: default:
this.reportError( this._reportError(
`The provided animation output phase value "${phase}" for "@${eventName}" is not supported (use start or done)`, `The provided animation output phase value "${phase}" for "@${eventName}" is not supported (use start or done)`,
sourceSpan); sourceSpan);
break; break;
} }
} else { } else {
this.reportError( this._reportError(
`The animation trigger output event (@${eventName}) is missing its phase value name (start or done are currently supported)`, `The animation trigger output event (@${eventName}) is missing its phase value name (start or done are currently supported)`,
sourceSpan); sourceSpan);
} }
@ -369,26 +365,26 @@ export class BindingParser {
this._reportExpressionParserErrors(ast.errors, sourceSpan); this._reportExpressionParserErrors(ast.errors, sourceSpan);
} }
if (!ast || ast.ast instanceof EmptyExpr) { if (!ast || ast.ast instanceof EmptyExpr) {
this.reportError(`Empty expressions are not allowed`, sourceSpan); this._reportError(`Empty expressions are not allowed`, sourceSpan);
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo); return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
} }
this._checkPipes(ast, sourceSpan); this._checkPipes(ast, sourceSpan);
return ast; return ast;
} catch (e) { } catch (e) {
this.reportError(`${e}`, sourceSpan); this._reportError(`${e}`, sourceSpan);
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo); return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
} }
} }
reportError( private _reportError(
message: string, sourceSpan: ParseSourceSpan, message: string, sourceSpan: ParseSourceSpan,
level: ParseErrorLevel = ParseErrorLevel.FATAL) { level: ParseErrorLevel = ParseErrorLevel.FATAL) {
this.errors.push(new ParseError(sourceSpan, message, level)); this._targetErrors.push(new ParseError(sourceSpan, message, level));
} }
private _reportExpressionParserErrors(errors: ParserError[], sourceSpan: ParseSourceSpan) { private _reportExpressionParserErrors(errors: ParserError[], sourceSpan: ParseSourceSpan) {
for (const error of errors) { for (const error of errors) {
this.reportError(error.message, sourceSpan); this._reportError(error.message, sourceSpan);
} }
} }
@ -398,7 +394,7 @@ export class BindingParser {
ast.visit(collector); ast.visit(collector);
collector.pipes.forEach((pipeName) => { collector.pipes.forEach((pipeName) => {
if (!this.pipesByName.has(pipeName)) { if (!this.pipesByName.has(pipeName)) {
this.reportError(`The pipe '${pipeName}' could not be found`, sourceSpan); this._reportError(`The pipe '${pipeName}' could not be found`, sourceSpan);
} }
}); });
} }
@ -415,13 +411,13 @@ export class BindingParser {
const report = isAttr ? this._schemaRegistry.validateAttribute(propName) : const report = isAttr ? this._schemaRegistry.validateAttribute(propName) :
this._schemaRegistry.validateProperty(propName); this._schemaRegistry.validateProperty(propName);
if (report.error) { if (report.error) {
this.reportError(report.msg, sourceSpan, ParseErrorLevel.FATAL); this._reportError(report.msg, sourceSpan, ParseErrorLevel.FATAL);
} }
} }
} }
export class PipeCollector extends RecursiveAstVisitor { export class PipeCollector extends RecursiveAstVisitor {
pipes: Set<string> = new Set<string>(); pipes = new Set<string>();
visitPipe(ast: BindingPipe, context: any): any { visitPipe(ast: BindingPipe, context: any): any {
this.pipes.add(ast.name); this.pipes.add(ast.name);
ast.exp.visit(this); ast.exp.visit(this);

View File

@ -136,11 +136,13 @@ export class TemplateParser {
end: component.template.interpolation[1] end: component.template.interpolation[1]
}; };
} }
const bindingParser = new BindingParser(
this._exprParser, interpolationConfig, this._schemaRegistry, schemas, uniqPipes, errors);
const parseVisitor = new TemplateParseVisitor( const parseVisitor = new TemplateParseVisitor(
providerViewContext, uniqDirectives, uniqPipes, this._exprParser, interpolationConfig, providerViewContext, uniqDirectives, bindingParser, this._schemaRegistry, schemas,
this._schemaRegistry, schemas); errors);
result = html.visitAll(parseVisitor, htmlAstWithErrors.rootNodes, EMPTY_ELEMENT_CONTEXT); result = html.visitAll(parseVisitor, htmlAstWithErrors.rootNodes, EMPTY_ELEMENT_CONTEXT);
errors.push(...parseVisitor.errors, ...providerViewContext.errors); errors.push(...providerViewContext.errors);
} else { } else {
result = []; result = [];
} }
@ -196,18 +198,15 @@ export class TemplateParser {
} }
} }
class TemplateParseVisitor extends BindingParser implements html.Visitor { class TemplateParseVisitor implements html.Visitor {
selectorMatcher = new SelectorMatcher(); selectorMatcher = new SelectorMatcher();
errors: TemplateParseError[] = [];
directivesIndex = new Map<CompileDirectiveMetadata, number>(); directivesIndex = new Map<CompileDirectiveMetadata, number>();
ngContentCount: number = 0; ngContentCount: number = 0;
constructor( constructor(
public providerViewContext: ProviderViewContext, directives: CompileDirectiveMetadata[], public providerViewContext: ProviderViewContext, directives: CompileDirectiveMetadata[],
pipes: CompilePipeMetadata[], _exprParser: Parser, interpolationConfig: InterpolationConfig, private _bindingParser: BindingParser, private _schemaRegistry: ElementSchemaRegistry,
_schemaRegistry: ElementSchemaRegistry, schemas: SchemaMetadata[]) { private _schemas: SchemaMetadata[], private _targetErrors: TemplateParseError[]) {
super(_exprParser, interpolationConfig, _schemaRegistry, schemas, pipes);
directives.forEach((directive: CompileDirectiveMetadata, index: number) => { directives.forEach((directive: CompileDirectiveMetadata, index: number) => {
const selector = CssSelector.parse(directive.selector); const selector = CssSelector.parse(directive.selector);
this.selectorMatcher.addSelectables(selector, directive); this.selectorMatcher.addSelectables(selector, directive);
@ -221,7 +220,7 @@ class TemplateParseVisitor extends BindingParser implements html.Visitor {
visitText(text: html.Text, parent: ElementContext): any { visitText(text: html.Text, parent: ElementContext): any {
const ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR); const ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR);
const expr = this.parseInterpolation(text.value, text.sourceSpan); const expr = this._bindingParser.parseInterpolation(text.value, text.sourceSpan);
if (isPresent(expr)) { if (isPresent(expr)) {
return new BoundTextAst(expr, ngContentIndex, text.sourceSpan); return new BoundTextAst(expr, ngContentIndex, text.sourceSpan);
} else { } else {
@ -282,12 +281,12 @@ class TemplateParseVisitor extends BindingParser implements html.Visitor {
const hasTemplateBinding = isPresent(templateBindingsSource); const hasTemplateBinding = isPresent(templateBindingsSource);
if (hasTemplateBinding) { if (hasTemplateBinding) {
if (hasInlineTemplates) { if (hasInlineTemplates) {
this.reportError( this._reportError(
`Can't have multiple template bindings on one element. Use only one attribute named 'template' or prefixed with *`, `Can't have multiple template bindings on one element. Use only one attribute named 'template' or prefixed with *`,
attr.sourceSpan); attr.sourceSpan);
} }
hasInlineTemplates = true; hasInlineTemplates = true;
this.parseInlineTemplateBinding( this._bindingParser.parseInlineTemplateBinding(
attr.name, templateBindingsSource, attr.sourceSpan, templateMatchableAttrs, attr.name, templateBindingsSource, attr.sourceSpan, templateMatchableAttrs,
templateElementOrDirectiveProps, templateElementVars); templateElementOrDirectiveProps, templateElementVars);
} }
@ -327,7 +326,7 @@ class TemplateParseVisitor extends BindingParser implements html.Visitor {
if (preparsedElement.type === PreparsedElementType.NG_CONTENT) { if (preparsedElement.type === PreparsedElementType.NG_CONTENT) {
if (element.children && !element.children.every(_isEmptyTextNode)) { if (element.children && !element.children.every(_isEmptyTextNode)) {
this.reportError(`<ng-content> element cannot have content.`, element.sourceSpan); this._reportError(`<ng-content> element cannot have content.`, element.sourceSpan);
} }
parsedElement = new NgContentAst( parsedElement = new NgContentAst(
@ -400,7 +399,7 @@ class TemplateParseVisitor extends BindingParser implements html.Visitor {
animationInputs.forEach(input => { animationInputs.forEach(input => {
const name = input.name; const name = input.name;
if (!triggerLookup.has(name)) { if (!triggerLookup.has(name)) {
this.reportError(`Couldn't find an animation entry for "${name}"`, input.sourceSpan); this._reportError(`Couldn't find an animation entry for "${name}"`, input.sourceSpan);
} }
}); });
@ -408,7 +407,7 @@ class TemplateParseVisitor extends BindingParser implements html.Visitor {
if (output.isAnimation) { if (output.isAnimation) {
const found = animationInputs.find(input => input.name == output.name); const found = animationInputs.find(input => input.name == output.name);
if (!found) { if (!found) {
this.reportError( this._reportError(
`Unable to listen on (@${output.name}.${output.phase}) because the animation trigger [@${output.name}] isn't being used on the same element`, `Unable to listen on (@${output.name}.${output.phase}) because the animation trigger [@${output.name}] isn't being used on the same element`,
output.sourceSpan); output.sourceSpan);
} }
@ -430,7 +429,7 @@ class TemplateParseVisitor extends BindingParser implements html.Visitor {
if (bindParts !== null) { if (bindParts !== null) {
hasBinding = true; hasBinding = true;
if (isPresent(bindParts[KW_BIND_IDX])) { if (isPresent(bindParts[KW_BIND_IDX])) {
this.parsePropertyBinding( this._bindingParser.parsePropertyBinding(
bindParts[IDENT_KW_IDX], value, false, srcSpan, targetMatchableAttrs, targetProps); bindParts[IDENT_KW_IDX], value, false, srcSpan, targetMatchableAttrs, targetProps);
} else if (bindParts[KW_LET_IDX]) { } else if (bindParts[KW_LET_IDX]) {
@ -438,7 +437,7 @@ class TemplateParseVisitor extends BindingParser implements html.Visitor {
const identifier = bindParts[IDENT_KW_IDX]; const identifier = bindParts[IDENT_KW_IDX];
this._parseVariable(identifier, value, srcSpan, targetVars); this._parseVariable(identifier, value, srcSpan, targetVars);
} else { } else {
this.reportError(`"let-" is only supported on template elements.`, srcSpan); this._reportError(`"let-" is only supported on template elements.`, srcSpan);
} }
} else if (bindParts[KW_REF_IDX]) { } else if (bindParts[KW_REF_IDX]) {
@ -446,41 +445,42 @@ class TemplateParseVisitor extends BindingParser implements html.Visitor {
this._parseReference(identifier, value, srcSpan, targetRefs); this._parseReference(identifier, value, srcSpan, targetRefs);
} else if (bindParts[KW_ON_IDX]) { } else if (bindParts[KW_ON_IDX]) {
this.parseEvent( this._bindingParser.parseEvent(
bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetEvents); bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
} else if (bindParts[KW_BINDON_IDX]) { } else if (bindParts[KW_BINDON_IDX]) {
this.parsePropertyBinding( this._bindingParser.parsePropertyBinding(
bindParts[IDENT_KW_IDX], value, false, srcSpan, targetMatchableAttrs, targetProps); bindParts[IDENT_KW_IDX], value, false, srcSpan, targetMatchableAttrs, targetProps);
this._parseAssignmentEvent( this._parseAssignmentEvent(
bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetEvents); bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
} else if (bindParts[KW_AT_IDX]) { } else if (bindParts[KW_AT_IDX]) {
this.parseLiteralAttr(name, value, srcSpan, targetMatchableAttrs, targetProps); this._bindingParser.parseLiteralAttr(
name, value, srcSpan, targetMatchableAttrs, targetProps);
} else if (bindParts[IDENT_BANANA_BOX_IDX]) { } else if (bindParts[IDENT_BANANA_BOX_IDX]) {
this.parsePropertyBinding( this._bindingParser.parsePropertyBinding(
bindParts[IDENT_BANANA_BOX_IDX], value, false, srcSpan, targetMatchableAttrs, bindParts[IDENT_BANANA_BOX_IDX], value, false, srcSpan, targetMatchableAttrs,
targetProps); targetProps);
this._parseAssignmentEvent( this._parseAssignmentEvent(
bindParts[IDENT_BANANA_BOX_IDX], value, srcSpan, targetMatchableAttrs, targetEvents); bindParts[IDENT_BANANA_BOX_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
} else if (bindParts[IDENT_PROPERTY_IDX]) { } else if (bindParts[IDENT_PROPERTY_IDX]) {
this.parsePropertyBinding( this._bindingParser.parsePropertyBinding(
bindParts[IDENT_PROPERTY_IDX], value, false, srcSpan, targetMatchableAttrs, bindParts[IDENT_PROPERTY_IDX], value, false, srcSpan, targetMatchableAttrs,
targetProps); targetProps);
} else if (bindParts[IDENT_EVENT_IDX]) { } else if (bindParts[IDENT_EVENT_IDX]) {
this.parseEvent( this._bindingParser.parseEvent(
bindParts[IDENT_EVENT_IDX], value, srcSpan, targetMatchableAttrs, targetEvents); bindParts[IDENT_EVENT_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
} }
} else { } else {
hasBinding = hasBinding = this._bindingParser.parsePropertyInterpolation(
this.parsePropertyInterpolation(name, value, srcSpan, targetMatchableAttrs, targetProps); name, value, srcSpan, targetMatchableAttrs, targetProps);
} }
if (!hasBinding) { if (!hasBinding) {
this.parseLiteralAttr(name, value, srcSpan, targetMatchableAttrs, targetProps); this._bindingParser.parseLiteralAttr(name, value, srcSpan, targetMatchableAttrs, targetProps);
} }
return hasBinding; return hasBinding;
@ -493,7 +493,7 @@ class TemplateParseVisitor extends BindingParser implements html.Visitor {
private _parseVariable( private _parseVariable(
identifier: string, value: string, sourceSpan: ParseSourceSpan, targetVars: VariableAst[]) { identifier: string, value: string, sourceSpan: ParseSourceSpan, targetVars: VariableAst[]) {
if (identifier.indexOf('-') > -1) { if (identifier.indexOf('-') > -1) {
this.reportError(`"-" is not allowed in variable names`, sourceSpan); this._reportError(`"-" is not allowed in variable names`, sourceSpan);
} }
targetVars.push(new VariableAst(identifier, value, sourceSpan)); targetVars.push(new VariableAst(identifier, value, sourceSpan));
@ -503,7 +503,7 @@ class TemplateParseVisitor extends BindingParser implements html.Visitor {
identifier: string, value: string, sourceSpan: ParseSourceSpan, identifier: string, value: string, sourceSpan: ParseSourceSpan,
targetRefs: ElementOrDirectiveRef[]) { targetRefs: ElementOrDirectiveRef[]) {
if (identifier.indexOf('-') > -1) { if (identifier.indexOf('-') > -1) {
this.reportError(`"-" is not allowed in reference names`, sourceSpan); this._reportError(`"-" is not allowed in reference names`, sourceSpan);
} }
targetRefs.push(new ElementOrDirectiveRef(identifier, value, sourceSpan)); targetRefs.push(new ElementOrDirectiveRef(identifier, value, sourceSpan));
@ -512,7 +512,7 @@ class TemplateParseVisitor extends BindingParser implements html.Visitor {
private _parseAssignmentEvent( private _parseAssignmentEvent(
name: string, expression: string, sourceSpan: ParseSourceSpan, name: string, expression: string, sourceSpan: ParseSourceSpan,
targetMatchableAttrs: string[][], targetEvents: BoundEventAst[]) { targetMatchableAttrs: string[][], targetEvents: BoundEventAst[]) {
this.parseEvent( this._bindingParser.parseEvent(
`${name}Change`, `${expression}=$event`, sourceSpan, targetMatchableAttrs, targetEvents); `${name}Change`, `${expression}=$event`, sourceSpan, targetMatchableAttrs, targetEvents);
} }
@ -548,12 +548,11 @@ class TemplateParseVisitor extends BindingParser implements html.Visitor {
if (directive.isComponent) { if (directive.isComponent) {
component = directive; component = directive;
} }
const hostProperties: BoundElementPropertyAst[] = [];
const hostEvents: BoundEventAst[] = [];
const directiveProperties: BoundDirectivePropertyAst[] = []; const directiveProperties: BoundDirectivePropertyAst[] = [];
this.createDirectiveHostPropertyAsts( const hostProperties = this._bindingParser.createDirectiveHostPropertyAsts(
elementName, directive.hostProperties, sourceSpan, hostProperties); elementName, directive.hostProperties, sourceSpan);
this.createDirectiveHostEventAsts(directive.hostListeners, sourceSpan, hostEvents); const hostEvents =
this._bindingParser.createDirectiveHostEventAsts(directive.hostListeners, sourceSpan);
this._createDirectivePropertyAsts(directive.inputs, props, directiveProperties); this._createDirectivePropertyAsts(directive.inputs, props, directiveProperties);
elementOrDirectiveRefs.forEach((elOrDirRef) => { elementOrDirectiveRefs.forEach((elOrDirRef) => {
if ((elOrDirRef.value.length === 0 && directive.isComponent) || if ((elOrDirRef.value.length === 0 && directive.isComponent) ||
@ -569,7 +568,7 @@ class TemplateParseVisitor extends BindingParser implements html.Visitor {
elementOrDirectiveRefs.forEach((elOrDirRef) => { elementOrDirectiveRefs.forEach((elOrDirRef) => {
if (elOrDirRef.value.length > 0) { if (elOrDirRef.value.length > 0) {
if (!matchedReferences.has(elOrDirRef.name)) { if (!matchedReferences.has(elOrDirRef.name)) {
this.reportError( this._reportError(
`There is no directive with "exportAs" set to "${elOrDirRef.value}"`, `There is no directive with "exportAs" set to "${elOrDirRef.value}"`,
elOrDirRef.sourceSpan); elOrDirRef.sourceSpan);
} }
@ -624,7 +623,7 @@ class TemplateParseVisitor extends BindingParser implements html.Visitor {
props.forEach((prop: BoundProperty) => { props.forEach((prop: BoundProperty) => {
if (!prop.isLiteral && !boundDirectivePropsIndex.get(prop.name)) { if (!prop.isLiteral && !boundDirectivePropsIndex.get(prop.name)) {
boundElementProps.push(this.createElementPropertyAst(elementName, prop)); boundElementProps.push(this._bindingParser.createElementPropertyAst(elementName, prop));
} }
}); });
return boundElementProps; return boundElementProps;
@ -642,7 +641,7 @@ class TemplateParseVisitor extends BindingParser implements html.Visitor {
private _assertOnlyOneComponent(directives: DirectiveAst[], sourceSpan: ParseSourceSpan) { private _assertOnlyOneComponent(directives: DirectiveAst[], sourceSpan: ParseSourceSpan) {
const componentTypeNames = this._findComponentDirectiveNames(directives); const componentTypeNames = this._findComponentDirectiveNames(directives);
if (componentTypeNames.length > 1) { if (componentTypeNames.length > 1) {
this.reportError(`More than one component: ${componentTypeNames.join(',')}`, sourceSpan); this._reportError(`More than one component: ${componentTypeNames.join(',')}`, sourceSpan);
} }
} }
@ -662,7 +661,7 @@ class TemplateParseVisitor extends BindingParser implements html.Visitor {
const errorMsg = `'${elName}' is not a known element:\n` + const errorMsg = `'${elName}' is not a known element:\n` +
`1. If '${elName}' is an Angular component, then verify that it is part of this module.\n` + `1. If '${elName}' is an Angular component, then verify that it is part of this module.\n` +
`2. If '${elName}' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message.`; `2. If '${elName}' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message.`;
this.reportError(errorMsg, element.sourceSpan); this._reportError(errorMsg, element.sourceSpan);
} }
} }
@ -671,11 +670,11 @@ class TemplateParseVisitor extends BindingParser implements html.Visitor {
sourceSpan: ParseSourceSpan) { sourceSpan: ParseSourceSpan) {
const componentTypeNames: string[] = this._findComponentDirectiveNames(directives); const componentTypeNames: string[] = this._findComponentDirectiveNames(directives);
if (componentTypeNames.length > 0) { if (componentTypeNames.length > 0) {
this.reportError( this._reportError(
`Components on an embedded template: ${componentTypeNames.join(',')}`, sourceSpan); `Components on an embedded template: ${componentTypeNames.join(',')}`, sourceSpan);
} }
elementProps.forEach(prop => { elementProps.forEach(prop => {
this.reportError( this._reportError(
`Property binding ${prop.name} not used by any directive on an embedded template. Make sure that the property name is spelled correctly and all directives are listed in the "directives" section.`, `Property binding ${prop.name} not used by any directive on an embedded template. Make sure that the property name is spelled correctly and all directives are listed in the "directives" section.`,
sourceSpan); sourceSpan);
}); });
@ -694,12 +693,18 @@ class TemplateParseVisitor extends BindingParser implements html.Visitor {
events.forEach(event => { events.forEach(event => {
if (isPresent(event.target) || !allDirectiveEvents.has(event.name)) { if (isPresent(event.target) || !allDirectiveEvents.has(event.name)) {
this.reportError( this._reportError(
`Event binding ${event.fullName} not emitted by any directive on an embedded template. Make sure that the event name is spelled correctly and all directives are listed in the "directives" section.`, `Event binding ${event.fullName} not emitted by any directive on an embedded template. Make sure that the event name is spelled correctly and all directives are listed in the "directives" section.`,
event.sourceSpan); event.sourceSpan);
} }
}); });
} }
private _reportError(
message: string, sourceSpan: ParseSourceSpan,
level: ParseErrorLevel = ParseErrorLevel.FATAL) {
this._targetErrors.push(new ParseError(sourceSpan, message, level));
}
} }
class NonBindableVisitor implements html.Visitor { class NonBindableVisitor implements html.Visitor {

View File

@ -10,7 +10,7 @@ import {ViewEncapsulation} from '@angular/core';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileTokenMetadata} from '../compile_metadata'; import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileTokenMetadata} from '../compile_metadata';
import {createSharedBindingVariablesIfNeeded} from '../compiler_util/expression_converter'; import {createSharedBindingVariablesIfNeeded} from '../compiler_util/expression_converter';
import {createDiTokenExpression, createFastArray} from '../compiler_util/identifier_util'; import {createDiTokenExpression, createInlineArray} from '../compiler_util/identifier_util';
import {isPresent} from '../facade/lang'; import {isPresent} from '../facade/lang';
import {Identifiers, identifierToken, resolveIdentifier} from '../identifiers'; import {Identifiers, identifierToken, resolveIdentifier} from '../identifiers';
import {createClassStmt} from '../output/class_builder'; import {createClassStmt} from '../output/class_builder';
@ -167,7 +167,7 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
'createTemplateAnchor', [this._getParentRenderNode(parent), debugContextExpr]); 'createTemplateAnchor', [this._getParentRenderNode(parent), debugContextExpr]);
} else { } else {
const htmlAttrs = _readHtmlAttrs(ast.attrs); const htmlAttrs = _readHtmlAttrs(ast.attrs);
const attrNameAndValues = createFastArray( const attrNameAndValues = createInlineArray(
_mergeHtmlAndDirectiveAttrs(htmlAttrs, directives).map(v => o.literal(v))); _mergeHtmlAndDirectiveAttrs(htmlAttrs, directives).map(v => o.literal(v)));
if (nodeIndex === 0 && this.view.viewType === ViewType.HOST) { if (nodeIndex === 0 && this.view.viewType === ViewType.HOST) {
createRenderNodeExpr = createRenderNodeExpr =

View File

@ -0,0 +1,63 @@
/**
* @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 {createInlineArray} from '../../src/compiler_util/identifier_util';
import {Identifiers, resolveIdentifier} from '../../src/identifiers';
import * as o from '../../src/output/output_ast';
export function main() {
describe('createInlineArray', () => {
function check(argCount: number, expectedIdentifier: any) {
const args = createArgs(argCount);
expect(createInlineArray(args))
.toEqual(o.importExpr(resolveIdentifier(expectedIdentifier)).instantiate([
<o.Expression>o.literal(argCount)
].concat(args)));
}
function createArgs(count: number): o.Expression[] {
const result: o.Expression[] = [];
for (var i = 0; i < count; i++) {
result.push(o.NULL_EXPR);
}
return result;
}
it('should work for arrays of length 0', () => {
expect(createInlineArray([
])).toEqual(o.importExpr(resolveIdentifier(Identifiers.EMPTY_INLINE_ARRAY)));
});
it('should work for arrays of length 1 - 2', () => {
check(1, Identifiers.inlineArrays[0]);
check(2, Identifiers.inlineArrays[1]);
});
it('should work for arrays of length 3 - 4', () => {
for (var i = 3; i <= 4; i++) {
check(i, Identifiers.inlineArrays[2]);
}
});
it('should work for arrays of length 5 - 8', () => {
for (var i = 5; i <= 8; i++) {
check(i, Identifiers.inlineArrays[3]);
}
});
it('should work for arrays of length 9 - 16', () => {
for (var i = 9; i <= 16; i++) {
check(i, Identifiers.inlineArrays[4]);
}
});
it('should work for arrays of length > 16',
() => { check(17, Identifiers.InlineArrayDynamic); });
});
}

View File

@ -379,7 +379,7 @@ function camelCaseToDashCase(input: string): string {
} }
export function createRenderElement( export function createRenderElement(
renderer: Renderer, parentElement: any, name: string, attrs: FastArray<string>, renderer: Renderer, parentElement: any, name: string, attrs: InlineArray<string>,
debugInfo?: RenderDebugInfo): any { debugInfo?: RenderDebugInfo): any {
const el = renderer.createElement(parentElement, name, debugInfo); const el = renderer.createElement(parentElement, name, debugInfo);
for (var i = 0; i < attrs.length; i += 2) { for (var i = 0; i < attrs.length; i += 2) {
@ -389,8 +389,8 @@ export function createRenderElement(
} }
export function selectOrCreateRenderHostElement( export function selectOrCreateRenderHostElement(
renderer: Renderer, elementName: string, attrs: FastArray<string>, renderer: Renderer, elementName: string, attrs: InlineArray<string>,
rootSelectorOrNode: string | any, debugInfo: RenderDebugInfo): any { rootSelectorOrNode: string | any, debugInfo?: RenderDebugInfo): any {
var hostElement: any; var hostElement: any;
if (isPresent(rootSelectorOrNode)) { if (isPresent(rootSelectorOrNode)) {
hostElement = renderer.selectRootElement(rootSelectorOrNode, debugInfo); hostElement = renderer.selectRootElement(rootSelectorOrNode, debugInfo);
@ -400,17 +400,17 @@ export function selectOrCreateRenderHostElement(
return hostElement; return hostElement;
} }
export interface FastArray<T> { export interface InlineArray<T> {
length: number; length: number;
get(index: number): T; get(index: number): T;
} }
class FastArray0 implements FastArray<any> { class InlineArray0 implements InlineArray<any> {
length = 0; length = 0;
get(index: number): any { return undefined; } get(index: number): any { return undefined; }
} }
export class FastArray2<T> implements FastArray<T> { export class InlineArray2<T> implements InlineArray<T> {
constructor(public length: number, private _v0: T, private _v1: T) {} constructor(public length: number, private _v0: T, private _v1: T) {}
get(index: number) { get(index: number) {
switch (index) { switch (index) {
@ -424,7 +424,7 @@ export class FastArray2<T> implements FastArray<T> {
} }
} }
export class FastArray4<T> implements FastArray<T> { export class InlineArray4<T> implements InlineArray<T> {
constructor( constructor(
public length: number, private _v0: T, private _v1: T, private _v2: T, private _v3: T) {} public length: number, private _v0: T, private _v1: T, private _v2: T, private _v3: T) {}
get(index: number) { get(index: number) {
@ -443,7 +443,7 @@ export class FastArray4<T> implements FastArray<T> {
} }
} }
export class FastArray8<T> implements FastArray<T> { export class InlineArray8<T> implements InlineArray<T> {
constructor( constructor(
public length: number, private _v0: T, private _v1: T, private _v2: T, private _v3: T, public length: number, private _v0: T, private _v1: T, private _v2: T, private _v3: T,
private _v4: T, private _v5: T, private _v6: T, private _v7: T) {} private _v4: T, private _v5: T, private _v6: T, private _v7: T) {}
@ -471,7 +471,7 @@ export class FastArray8<T> implements FastArray<T> {
} }
} }
export class FastArray16<T> implements FastArray<T> { export class InlineArray16<T> implements InlineArray<T> {
constructor( constructor(
public length: number, private _v0: T, private _v1: T, private _v2: T, private _v3: T, public length: number, private _v0: T, private _v1: T, private _v2: T, private _v3: T,
private _v4: T, private _v5: T, private _v6: T, private _v7: T, private _v8: T, private _v4: T, private _v5: T, private _v6: T, private _v7: T, private _v8: T,
@ -517,7 +517,7 @@ export class FastArray16<T> implements FastArray<T> {
} }
} }
export class FastArrayDynamic<T> implements FastArray<T> { export class InlineArrayDynamic<T> implements InlineArray<T> {
private _values: any[]; private _values: any[];
// Note: We still take the length argument so this class can be created // Note: We still take the length argument so this class can be created
// in the same ways as the other classes! // in the same ways as the other classes!
@ -526,4 +526,4 @@ export class FastArrayDynamic<T> implements FastArray<T> {
get(index: number) { return this._values[index]; } get(index: number) { return this._values[index]; }
} }
export const EMPTY_FAST_ARRAY: FastArray<any> = new FastArray0(); export const EMPTY_INLINE_ARRAY: InlineArray<any> = new InlineArray0();

View File

@ -34,7 +34,7 @@ class _View_TreeComponent_Host0 extends import1.AppView<any> {
} }
createInternal(rootSelector: string): import2.AppElement { createInternal(rootSelector: string): import2.AppElement {
this._el_0 = import4.selectOrCreateRenderHostElement( this._el_0 = import4.selectOrCreateRenderHostElement(
this.renderer, 'tree', import4.EMPTY_FAST_ARRAY, rootSelector, (null as any)); this.renderer, 'tree', import4.EMPTY_INLINE_ARRAY, rootSelector, (null as any));
this._vc_0 = new import2.AppElement(0, (null as any), this, this._el_0); this._vc_0 = new import2.AppElement(0, (null as any), this, this._el_0);
this._TreeComponent_0_4 = new _View_TreeComponent0(this._el_0); this._TreeComponent_0_4 = new _View_TreeComponent0(this._el_0);
this._vc_0.initComponent(this._TreeComponent_0_4.context, [], <any>this._TreeComponent_0_4); this._vc_0.initComponent(this._TreeComponent_0_4.context, [], <any>this._TreeComponent_0_4);

View File

@ -36,7 +36,7 @@ class _View_TreeRootComponent_Host0 extends import1.AppView<any> {
} }
createInternal(rootSelector: string): import2.AppElement { createInternal(rootSelector: string): import2.AppElement {
this._el_0 = import4.selectOrCreateRenderHostElement( this._el_0 = import4.selectOrCreateRenderHostElement(
this.renderer, 'tree', import4.EMPTY_FAST_ARRAY, rootSelector, (null as any)); this.renderer, 'tree', import4.EMPTY_INLINE_ARRAY, rootSelector, (null as any));
this._appEl_0 = new import2.AppElement(0, (null as any), this, this._el_0); this._appEl_0 = new import2.AppElement(0, (null as any), this, this._el_0);
var compView_0: any = var compView_0: any =
viewFactory_TreeRootComponent0(this.viewUtils, this.injector(0), this._appEl_0); viewFactory_TreeRootComponent0(this.viewUtils, this.injector(0), this._appEl_0);