refactor(compiler): set element attributes via one call
This makes the cost of using directives that have host attributes smaller. Part of #11683
This commit is contained in:
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* @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 {CompileTokenMetadata} from '../compile_metadata';
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {Identifiers, resolveIdentifier} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
|
||||
export function createDiTokenExpression(token: CompileTokenMetadata): o.Expression {
|
||||
if (isPresent(token.value)) {
|
||||
return o.literal(token.value);
|
||||
} else if (token.identifierIsInstance) {
|
||||
return o.importExpr(token.identifier)
|
||||
.instantiate([], o.importType(token.identifier, [], [o.TypeModifier.Const]));
|
||||
} else {
|
||||
return o.importExpr(token.identifier);
|
||||
}
|
||||
}
|
||||
|
||||
export function createFastArray(values: o.Expression[]): o.Expression {
|
||||
if (values.length === 0) {
|
||||
return o.importExpr(resolveIdentifier(Identifiers.EMPTY_FAST_ARRAY));
|
||||
}
|
||||
const index = Math.ceil(values.length / 2) - 1;
|
||||
const identifierSpec = index < Identifiers.fastArrays.length ? Identifiers.fastArrays[index] :
|
||||
Identifiers.FastArrayDynamic;
|
||||
const identifier = resolveIdentifier(identifierSpec);
|
||||
return o.importExpr(identifier).instantiate([
|
||||
<o.Expression>o.literal(values.length)
|
||||
].concat(values));
|
||||
}
|
@ -10,7 +10,6 @@ import {ANALYZE_FOR_ENTRY_COMPONENTS, AnimationTransitionEvent, ChangeDetectionS
|
||||
|
||||
import {CompileIdentifierMetadata, CompileTokenMetadata} from './compile_metadata';
|
||||
import {AnimationGroupPlayer, AnimationKeyframe, AnimationSequencePlayer, AnimationStyles, AnimationTransition, AppElement, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, DebugAppView, DebugContext, NgModuleInjector, NoOpAnimationPlayer, StaticNodeDebugInfo, TemplateRef_, UNINITIALIZED, ValueUnwrapper, ViewType, balanceAnimationKeyframes, clearStyles, collectAndResolveStyles, devModeEqual, prepareFinalAnimationStyles, reflector, registerModuleFactory, renderStyles, view_utils} from './private_import_core';
|
||||
import {assetUrl} from './util';
|
||||
|
||||
var APP_VIEW_MODULE_URL = assetUrl('core', 'linker/view');
|
||||
var VIEW_UTILS_MODULE_URL = assetUrl('core', 'linker/view_utils');
|
||||
@ -190,8 +189,17 @@ export class Identifiers {
|
||||
moduleUrl: VIEW_UTILS_MODULE_URL,
|
||||
runtime: view_utils.EMPTY_MAP
|
||||
};
|
||||
|
||||
static pureProxies = [
|
||||
static createRenderElement: IdentifierSpec = {
|
||||
name: 'createRenderElement',
|
||||
moduleUrl: VIEW_UTILS_MODULE_URL,
|
||||
runtime: view_utils.createRenderElement
|
||||
};
|
||||
static selectOrCreateRenderHostElement: IdentifierSpec = {
|
||||
name: 'selectOrCreateRenderHostElement',
|
||||
moduleUrl: VIEW_UTILS_MODULE_URL,
|
||||
runtime: view_utils.selectOrCreateRenderHostElement
|
||||
};
|
||||
static pureProxies: IdentifierSpec[] = [
|
||||
null,
|
||||
{name: 'pureProxy1', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy1},
|
||||
{name: 'pureProxy2', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy2},
|
||||
@ -284,6 +292,34 @@ export class Identifiers {
|
||||
moduleUrl: assetUrl('core', 'animation/animation_transition'),
|
||||
runtime: AnimationTransition
|
||||
};
|
||||
|
||||
// This is just the interface!
|
||||
static FastArray:
|
||||
IdentifierSpec = {name: 'FastArray', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: null};
|
||||
static fastArrays: IdentifierSpec[] = [
|
||||
{name: 'FastArray2', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.FastArray2},
|
||||
{name: 'FastArray4', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.FastArray4},
|
||||
{name: 'FastArray8', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.FastArray8},
|
||||
{name: 'FastArray16', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.FastArray16},
|
||||
];
|
||||
static EMPTY_FAST_ARRAY: IdentifierSpec = {
|
||||
name: 'EMPTY_FAST_ARRAY',
|
||||
moduleUrl: VIEW_UTILS_MODULE_URL,
|
||||
runtime: view_utils.EMPTY_FAST_ARRAY
|
||||
};
|
||||
static FastArrayDynamic: IdentifierSpec = {
|
||||
name: 'FastArrayDynamic',
|
||||
moduleUrl: VIEW_UTILS_MODULE_URL,
|
||||
runtime: view_utils.FastArrayDynamic
|
||||
};
|
||||
}
|
||||
|
||||
export function assetUrl(pkg: string, path: string = null, type: string = 'src'): string {
|
||||
if (path == null) {
|
||||
return `asset:@angular/lib/${pkg}/index`;
|
||||
} else {
|
||||
return `asset:@angular/lib/${pkg}/src/${path}`;
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveIdentifier(identifier: IdentifierSpec) {
|
||||
|
@ -9,6 +9,7 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
|
||||
import {CompileDiDependencyMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, CompileTokenMetadata} from './compile_metadata';
|
||||
import {createDiTokenExpression} from './compiler_util/identifier_util';
|
||||
import {isPresent} from './facade/lang';
|
||||
import {Identifiers, resolveIdentifier, resolveIdentifierToken} from './identifiers';
|
||||
import * as o from './output/output_ast';
|
||||
@ -17,7 +18,6 @@ import {ParseLocation, ParseSourceFile, ParseSourceSpan} from './parse_util';
|
||||
import {LifecycleHooks} from './private_import_core';
|
||||
import {NgModuleProviderAnalyzer} from './provider_analyzer';
|
||||
import {ProviderAst} from './template_parser/template_ast';
|
||||
import {createDiTokenExpression} from './util';
|
||||
|
||||
export class ComponentFactoryDependency {
|
||||
constructor(
|
||||
|
@ -6,9 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {CompileTokenMetadata} from './compile_metadata';
|
||||
import {isBlank, isPresent, isPrimitive, isStrictStringMap} from './facade/lang';
|
||||
import * as o from './output/output_ast';
|
||||
import {isBlank, isPrimitive, isStrictStringMap} from './facade/lang';
|
||||
|
||||
export const MODULE_SUFFIX = '';
|
||||
|
||||
@ -72,25 +70,6 @@ export class ValueTransformer implements ValueVisitor {
|
||||
visitOther(value: any, context: any): any { return value; }
|
||||
}
|
||||
|
||||
export function assetUrl(pkg: string, path: string = null, type: string = 'src'): string {
|
||||
if (path == null) {
|
||||
return `asset:@angular/lib/${pkg}/index`;
|
||||
} else {
|
||||
return `asset:@angular/lib/${pkg}/src/${path}`;
|
||||
}
|
||||
}
|
||||
|
||||
export function createDiTokenExpression(token: CompileTokenMetadata): o.Expression {
|
||||
if (isPresent(token.value)) {
|
||||
return o.literal(token.value);
|
||||
} else if (token.identifierIsInstance) {
|
||||
return o.importExpr(token.identifier)
|
||||
.instantiate([], o.importType(token.identifier, [], [o.TypeModifier.Const]));
|
||||
} else {
|
||||
return o.importExpr(token.identifier);
|
||||
}
|
||||
}
|
||||
|
||||
export class SyncAsyncResult<T> {
|
||||
constructor(public syncResult: T, public asyncResult: Promise<T> = null) {
|
||||
if (!asyncResult) {
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
|
||||
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileIdentifierMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata} from '../compile_metadata';
|
||||
import {createDiTokenExpression} from '../compiler_util/identifier_util';
|
||||
import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
|
||||
import {MapWrapper} from '../facade/collection';
|
||||
import {isPresent} from '../facade/lang';
|
||||
@ -15,7 +16,6 @@ import {Identifiers, identifierToken, resolveIdentifier, resolveIdentifierToken}
|
||||
import * as o from '../output/output_ast';
|
||||
import {convertValueToOutputAst} from '../output/value_util';
|
||||
import {ProviderAst, ProviderAstType, ReferenceAst, TemplateAst} from '../template_parser/template_ast';
|
||||
import {createDiTokenExpression} from '../util';
|
||||
|
||||
import {CompileMethod} from './compile_method';
|
||||
import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query';
|
||||
|
@ -8,10 +8,10 @@
|
||||
|
||||
|
||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileTokenMetadata} from '../compile_metadata';
|
||||
import {createDiTokenExpression} from '../compiler_util/identifier_util';
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {Identifiers, resolveIdentifier} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
import {createDiTokenExpression} from '../util';
|
||||
|
||||
import {CompileView} from './compile_view';
|
||||
|
||||
|
@ -9,12 +9,12 @@
|
||||
import {ViewEncapsulation} from '@angular/core';
|
||||
|
||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileTokenMetadata} from '../compile_metadata';
|
||||
import {createDiTokenExpression, createFastArray} from '../compiler_util/identifier_util';
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {Identifiers, identifierToken, resolveIdentifier} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
import {ChangeDetectorStatus, ViewType, isDefaultChangeDetectionStrategy} from '../private_import_core';
|
||||
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_parser/template_ast';
|
||||
import {createDiTokenExpression} from '../util';
|
||||
|
||||
import {CompileElement, CompileNode} from './compile_element';
|
||||
import {CompileView} from './compile_view';
|
||||
@ -156,19 +156,29 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
|
||||
|
||||
visitElement(ast: ElementAst, parent: CompileElement): any {
|
||||
var nodeIndex = this.view.nodes.length;
|
||||
var createRenderNodeExpr: o.InvokeMethodExpr;
|
||||
var createRenderNodeExpr: o.Expression;
|
||||
var debugContextExpr = this.view.createMethod.resetDebugInfoExpr(nodeIndex, ast);
|
||||
if (nodeIndex === 0 && this.view.viewType === ViewType.HOST) {
|
||||
createRenderNodeExpr = o.THIS_EXPR.callMethod(
|
||||
'selectOrCreateHostElement', [o.literal(ast.name), rootSelectorVar, debugContextExpr]);
|
||||
var directives = ast.directives.map(directiveAst => directiveAst.directive);
|
||||
var component = directives.find(directive => directive.isComponent);
|
||||
if (ast.name === NG_CONTAINER_TAG) {
|
||||
createRenderNodeExpr = ViewProperties.renderer.callMethod(
|
||||
'createTemplateAnchor', [this._getParentRenderNode(parent), debugContextExpr]);
|
||||
} else {
|
||||
if (ast.name === NG_CONTAINER_TAG) {
|
||||
createRenderNodeExpr = ViewProperties.renderer.callMethod(
|
||||
'createTemplateAnchor', [this._getParentRenderNode(parent), debugContextExpr]);
|
||||
const htmlAttrs = _readHtmlAttrs(ast.attrs);
|
||||
const attrNameAndValues = createFastArray(
|
||||
_mergeHtmlAndDirectiveAttrs(htmlAttrs, directives).map(v => o.literal(v)));
|
||||
if (nodeIndex === 0 && this.view.viewType === ViewType.HOST) {
|
||||
createRenderNodeExpr =
|
||||
o.importExpr(resolveIdentifier(Identifiers.selectOrCreateRenderHostElement)).callFn([
|
||||
ViewProperties.renderer, o.literal(ast.name), attrNameAndValues, rootSelectorVar,
|
||||
debugContextExpr
|
||||
]);
|
||||
} else {
|
||||
createRenderNodeExpr = ViewProperties.renderer.callMethod(
|
||||
'createElement',
|
||||
[this._getParentRenderNode(parent), o.literal(ast.name), debugContextExpr]);
|
||||
createRenderNodeExpr =
|
||||
o.importExpr(resolveIdentifier(Identifiers.createRenderElement)).callFn([
|
||||
ViewProperties.renderer, this._getParentRenderNode(parent), o.literal(ast.name),
|
||||
attrNameAndValues, debugContextExpr
|
||||
]);
|
||||
}
|
||||
}
|
||||
var fieldName = `_el_${nodeIndex}`;
|
||||
@ -178,22 +188,6 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
|
||||
|
||||
var renderNode = o.THIS_EXPR.prop(fieldName);
|
||||
|
||||
var directives = ast.directives.map(directiveAst => directiveAst.directive);
|
||||
var component = directives.find(directive => directive.isComponent);
|
||||
var htmlAttrs = _readHtmlAttrs(ast.attrs);
|
||||
var attrNameAndValues = _mergeHtmlAndDirectiveAttrs(htmlAttrs, directives);
|
||||
for (var i = 0; i < attrNameAndValues.length; i++) {
|
||||
const attrName = attrNameAndValues[i][0];
|
||||
if (ast.name !== NG_CONTAINER_TAG) {
|
||||
// <ng-container> are not rendered in the DOM
|
||||
const attrValue = attrNameAndValues[i][1];
|
||||
this.view.createMethod.addStmt(
|
||||
ViewProperties.renderer
|
||||
.callMethod(
|
||||
'setElementAttribute', [renderNode, o.literal(attrName), o.literal(attrValue)])
|
||||
.toStmt());
|
||||
}
|
||||
}
|
||||
var compileElement = new CompileElement(
|
||||
parent, this.view, nodeIndex, renderNode, ast, component, directives, ast.providers,
|
||||
ast.hasViewContainer, false, ast.references, this.targetDependencies);
|
||||
@ -328,18 +322,22 @@ function _isNgContainer(node: CompileNode, view: CompileView): boolean {
|
||||
|
||||
|
||||
function _mergeHtmlAndDirectiveAttrs(
|
||||
declaredHtmlAttrs: {[key: string]: string},
|
||||
directives: CompileDirectiveMetadata[]): string[][] {
|
||||
var result: {[key: string]: string} = {};
|
||||
Object.keys(declaredHtmlAttrs).forEach(key => { result[key] = declaredHtmlAttrs[key]; });
|
||||
declaredHtmlAttrs: {[key: string]: string}, directives: CompileDirectiveMetadata[]): string[] {
|
||||
const mapResult: {[key: string]: string} = {};
|
||||
Object.keys(declaredHtmlAttrs).forEach(key => { mapResult[key] = declaredHtmlAttrs[key]; });
|
||||
directives.forEach(directiveMeta => {
|
||||
Object.keys(directiveMeta.hostAttributes).forEach(name => {
|
||||
const value = directiveMeta.hostAttributes[name];
|
||||
var prevValue = result[name];
|
||||
result[name] = isPresent(prevValue) ? mergeAttributeValue(name, prevValue, value) : value;
|
||||
const prevValue = mapResult[name];
|
||||
mapResult[name] = isPresent(prevValue) ? mergeAttributeValue(name, prevValue, value) : value;
|
||||
});
|
||||
});
|
||||
return mapToKeyValueArray(result);
|
||||
const arrResult: string[] = [];
|
||||
// Note: We need to sort to get a defined output order
|
||||
// for tests and for caching generated artifacts...
|
||||
Object.keys(mapResult).sort().forEach(
|
||||
(attrName) => { arrResult.push(attrName, mapResult[attrName]); });
|
||||
return arrResult;
|
||||
}
|
||||
|
||||
function _readHtmlAttrs(attrs: AttrAst[]): {[key: string]: string} {
|
||||
@ -356,14 +354,6 @@ function mergeAttributeValue(attrName: string, attrValue1: string, attrValue2: s
|
||||
}
|
||||
}
|
||||
|
||||
function mapToKeyValueArray(data: {[key: string]: string}): string[][] {
|
||||
var entryArray: string[][] = [];
|
||||
Object.keys(data).forEach(name => { entryArray.push([name, data[name]]); });
|
||||
// We need to sort to get a defined output order
|
||||
// for tests and for caching generated artifacts...
|
||||
return entryArray.sort();
|
||||
}
|
||||
|
||||
function createViewTopLevelStmts(view: CompileView, targetStatements: o.Statement[]) {
|
||||
var nodeDebugInfosVar: o.Expression = o.NULL_EXPR;
|
||||
if (view.genConfig.genDebugInfo) {
|
||||
|
@ -11,7 +11,7 @@
|
||||
import {TypeScriptEmitter} from '@angular/compiler/src/output/ts_emitter';
|
||||
|
||||
import {print} from '../../src/facade/lang';
|
||||
import {assetUrl} from '../../src/util';
|
||||
import {assetUrl} from '../../src/identifiers';
|
||||
|
||||
function unimplemented(): any {
|
||||
throw new Error('unimplemented');
|
||||
|
@ -10,7 +10,7 @@
|
||||
import {JavaScriptEmitter} from '@angular/compiler/src/output/js_emitter';
|
||||
|
||||
import {print} from '../../src/facade/lang';
|
||||
import {assetUrl} from '../../src/util';
|
||||
import {assetUrl} from '../../src/identifiers';
|
||||
|
||||
import {SimpleJsImportGenerator, codegenExportsVars, codegenStmts} from './output_emitter_util';
|
||||
|
||||
|
@ -7,9 +7,9 @@
|
||||
*/
|
||||
|
||||
import {CompileIdentifierMetadata} from '@angular/compiler/src/compile_metadata';
|
||||
import {assetUrl} from '@angular/compiler/src/identifiers';
|
||||
import * as o from '@angular/compiler/src/output/output_ast';
|
||||
import {ImportGenerator} from '@angular/compiler/src/output/path_util';
|
||||
import {assetUrl} from '@angular/compiler/src/util';
|
||||
import {EventEmitter} from '@angular/core';
|
||||
import {BaseError} from '@angular/core/src/facade/errors';
|
||||
import {ViewType} from '@angular/core/src/linker/view_type';
|
||||
|
Reference in New Issue
Block a user