refactor(compiler): generate less code for bindings to DOM elements
Detailed changes: - remove `UNINITIALIZED`, initialize change detection fields with `undefined`. * we use `view.numberOfChecks === 0` now everywhere as indicator whether we are in the first change detection cycle (previously we used this only in a couple of places). * we keep the initialization itself as change detection get slower without it. - remove passing around `throwOnChange` in various generated calls, and store it on the view as property instead. - change generated code for bindings to DOM elements as follows: Before: ``` var currVal_10 = self.context.bgColor; if (jit_checkBinding15(self.throwOnChange,self._expr_10,currVal_10)) { self.renderer.setElementStyle(self._el_0,'backgroundColor',((self.viewUtils.sanitizer.sanitize(jit_21,currVal_10) == null)? null: self.viewUtils.sanitizer.sanitize(jit_21,currVal_10).toString())); self._expr_10 = currVal_10; } var currVal_11 = jit_inlineInterpolate16(1,' ',self.context.data.value,' '); if (jit_checkBinding15(self.throwOnChange,self._expr_11,currVal_11)) { self.renderer.setText(self._text_1,currVal_11); self._expr_11 = currVal_11; } ```, After: ``` var currVal_10 = self.context.bgColor; jit_checkRenderStyle14(self,self._el_0,'backgroundColor',null,self._expr_10,self._expr_10=currVal_10,false,jit_21); var currVal_11 = jit_inlineInterpolate15(1,' ',self.context.data.value,' '); jit_checkRenderText16(self,self._text_1,self._expr_11,self._expr_11=currVal_11,false); ``` Performance impact: - None seen (checked against internal latency lab) Part of #13651
This commit is contained in:
@ -21,28 +21,14 @@ export function createCheckBindingField(builder: ClassBuilder): CheckBindingFiel
|
||||
const fieldExpr = createBindFieldExpr(bindingId);
|
||||
// private is fine here as no child view will reference the cached value...
|
||||
builder.fields.push(new o.ClassField(fieldExpr.name, null, [o.StmtModifier.Private]));
|
||||
builder.ctorStmts.push(o.THIS_EXPR.prop(fieldExpr.name)
|
||||
.set(o.importExpr(createIdentifier(Identifiers.UNINITIALIZED)))
|
||||
.toStmt());
|
||||
builder.ctorStmts.push(o.THIS_EXPR.prop(fieldExpr.name).set(o.literal(undefined)).toStmt());
|
||||
return new CheckBindingField(fieldExpr, bindingId);
|
||||
}
|
||||
|
||||
export function createCheckBindingStmt(
|
||||
evalResult: ConvertPropertyBindingResult, fieldExpr: o.ReadPropExpr,
|
||||
throwOnChangeVar: o.Expression, actions: o.Statement[]): o.Statement[] {
|
||||
let condition: o.Expression = o.importExpr(createIdentifier(Identifiers.checkBinding)).callFn([
|
||||
throwOnChangeVar, fieldExpr, evalResult.currValExpr
|
||||
]);
|
||||
if (evalResult.forceUpdate) {
|
||||
condition = evalResult.forceUpdate.or(condition);
|
||||
}
|
||||
return [
|
||||
...evalResult.stmts, new o.IfStmt(condition, actions.concat([
|
||||
<o.Statement>o.THIS_EXPR.prop(fieldExpr.name).set(evalResult.currValExpr).toStmt()
|
||||
]))
|
||||
];
|
||||
}
|
||||
|
||||
function createBindFieldExpr(bindingId: string): o.ReadPropExpr {
|
||||
return o.THIS_EXPR.prop(`_expr_${bindingId}`);
|
||||
}
|
||||
|
||||
export function isFirstViewCheck(view: o.Expression): o.Expression {
|
||||
return o.not(view.prop('numberOfChecks'));
|
||||
}
|
@ -13,69 +13,64 @@ import * as o from '../output/output_ast';
|
||||
import {EMPTY_STATE as EMPTY_ANIMATION_STATE} from '../private_import_core';
|
||||
import {BoundElementPropertyAst, BoundEventAst, PropertyBindingType} from '../template_parser/template_ast';
|
||||
|
||||
import {isFirstViewCheck} from './binding_util';
|
||||
import {ConvertPropertyBindingResult} from './expression_converter';
|
||||
import {createEnumExpression} from './identifier_util';
|
||||
|
||||
export function writeToRenderer(
|
||||
view: o.Expression, boundProp: BoundElementPropertyAst, renderElement: o.Expression,
|
||||
renderValue: o.Expression, logBindingUpdate: boolean,
|
||||
export function createCheckRenderBindingStmt(
|
||||
view: o.Expression, renderElement: o.Expression, boundProp: BoundElementPropertyAst,
|
||||
oldValue: o.ReadPropExpr, evalResult: ConvertPropertyBindingResult,
|
||||
securityContextExpression?: o.Expression): o.Statement[] {
|
||||
const updateStmts: o.Statement[] = [];
|
||||
const renderer = view.prop('renderer');
|
||||
renderValue = sanitizedValue(view, boundProp, renderValue, securityContextExpression);
|
||||
const checkStmts: o.Statement[] = [...evalResult.stmts];
|
||||
const securityContext = calcSecurityContext(boundProp, securityContextExpression);
|
||||
switch (boundProp.type) {
|
||||
case PropertyBindingType.Property:
|
||||
if (logBindingUpdate) {
|
||||
updateStmts.push(
|
||||
o.importExpr(createIdentifier(Identifiers.setBindingDebugInfo))
|
||||
.callFn([renderer, renderElement, o.literal(boundProp.name), renderValue])
|
||||
.toStmt());
|
||||
}
|
||||
updateStmts.push(
|
||||
renderer
|
||||
.callMethod(
|
||||
'setElementProperty', [renderElement, o.literal(boundProp.name), renderValue])
|
||||
.toStmt());
|
||||
checkStmts.push(o.importExpr(createIdentifier(Identifiers.checkRenderProperty))
|
||||
.callFn([
|
||||
view, renderElement, o.literal(boundProp.name), oldValue,
|
||||
oldValue.set(evalResult.currValExpr),
|
||||
evalResult.forceUpdate || o.literal(false), securityContext
|
||||
])
|
||||
.toStmt());
|
||||
break;
|
||||
case PropertyBindingType.Attribute:
|
||||
renderValue =
|
||||
renderValue.isBlank().conditional(o.NULL_EXPR, renderValue.callMethod('toString', []));
|
||||
updateStmts.push(
|
||||
renderer
|
||||
.callMethod(
|
||||
'setElementAttribute', [renderElement, o.literal(boundProp.name), renderValue])
|
||||
.toStmt());
|
||||
checkStmts.push(o.importExpr(createIdentifier(Identifiers.checkRenderAttribute))
|
||||
.callFn([
|
||||
view, renderElement, o.literal(boundProp.name), oldValue,
|
||||
oldValue.set(evalResult.currValExpr),
|
||||
evalResult.forceUpdate || o.literal(false), securityContext
|
||||
])
|
||||
.toStmt());
|
||||
break;
|
||||
case PropertyBindingType.Class:
|
||||
updateStmts.push(
|
||||
renderer
|
||||
.callMethod(
|
||||
'setElementClass', [renderElement, o.literal(boundProp.name), renderValue])
|
||||
checkStmts.push(
|
||||
o.importExpr(createIdentifier(Identifiers.checkRenderClass))
|
||||
.callFn([
|
||||
view, renderElement, o.literal(boundProp.name), oldValue,
|
||||
oldValue.set(evalResult.currValExpr), evalResult.forceUpdate || o.literal(false)
|
||||
])
|
||||
.toStmt());
|
||||
break;
|
||||
case PropertyBindingType.Style:
|
||||
let strValue: o.Expression = renderValue.callMethod('toString', []);
|
||||
if (isPresent(boundProp.unit)) {
|
||||
strValue = strValue.plus(o.literal(boundProp.unit));
|
||||
}
|
||||
|
||||
renderValue = renderValue.isBlank().conditional(o.NULL_EXPR, strValue);
|
||||
updateStmts.push(
|
||||
renderer
|
||||
.callMethod(
|
||||
'setElementStyle', [renderElement, o.literal(boundProp.name), renderValue])
|
||||
checkStmts.push(
|
||||
o.importExpr(createIdentifier(Identifiers.checkRenderStyle))
|
||||
.callFn([
|
||||
view, renderElement, o.literal(boundProp.name), o.literal(boundProp.unit), oldValue,
|
||||
oldValue.set(evalResult.currValExpr), evalResult.forceUpdate || o.literal(false),
|
||||
securityContext
|
||||
])
|
||||
.toStmt());
|
||||
break;
|
||||
case PropertyBindingType.Animation:
|
||||
throw new Error('Illegal state: Should not come here!');
|
||||
}
|
||||
return updateStmts;
|
||||
return checkStmts;
|
||||
}
|
||||
|
||||
function sanitizedValue(
|
||||
view: o.Expression, boundProp: BoundElementPropertyAst, renderValue: o.Expression,
|
||||
securityContextExpression?: o.Expression): o.Expression {
|
||||
function calcSecurityContext(
|
||||
boundProp: BoundElementPropertyAst, securityContextExpression?: o.Expression): o.Expression {
|
||||
if (boundProp.securityContext === SecurityContext.NONE) {
|
||||
return renderValue; // No sanitization needed.
|
||||
return o.NULL_EXPR; // No sanitization needed.
|
||||
}
|
||||
if (!boundProp.needsRuntimeSecurityContext) {
|
||||
securityContextExpression =
|
||||
@ -84,15 +79,13 @@ function sanitizedValue(
|
||||
if (!securityContextExpression) {
|
||||
throw new Error(`internal error, no SecurityContext given ${boundProp.name}`);
|
||||
}
|
||||
const ctx = view.prop('viewUtils').prop('sanitizer');
|
||||
const args = [securityContextExpression, renderValue];
|
||||
return ctx.callMethod('sanitize', args);
|
||||
return securityContextExpression;
|
||||
}
|
||||
|
||||
export function triggerAnimation(
|
||||
export function createCheckAnimationBindingStmts(
|
||||
view: o.Expression, componentView: o.Expression, boundProp: BoundElementPropertyAst,
|
||||
boundOutputs: BoundEventAst[], eventListener: o.Expression, renderElement: o.Expression,
|
||||
renderValue: o.Expression, lastRenderValue: o.Expression) {
|
||||
oldValue: o.ReadPropExpr, evalResult: ConvertPropertyBindingResult) {
|
||||
const detachStmts: o.Statement[] = [];
|
||||
const updateStmts: o.Statement[] = [];
|
||||
|
||||
@ -104,22 +97,21 @@ export function triggerAnimation(
|
||||
// it's important to normalize the void value as `void` explicitly
|
||||
// so that the styles data can be obtained from the stringmap
|
||||
const emptyStateValue = o.literal(EMPTY_ANIMATION_STATE);
|
||||
const unitializedValue = o.importExpr(createIdentifier(Identifiers.UNINITIALIZED));
|
||||
const animationTransitionVar = o.variable('animationTransition_' + animationName);
|
||||
|
||||
updateStmts.push(
|
||||
animationTransitionVar
|
||||
.set(animationFnExpr.callFn([
|
||||
view, renderElement,
|
||||
lastRenderValue.equals(unitializedValue).conditional(emptyStateValue, lastRenderValue),
|
||||
renderValue.equals(unitializedValue).conditional(emptyStateValue, renderValue)
|
||||
view, renderElement, isFirstViewCheck(view).conditional(emptyStateValue, oldValue),
|
||||
evalResult.currValExpr
|
||||
]))
|
||||
.toDeclStmt());
|
||||
updateStmts.push(oldValue.set(evalResult.currValExpr).toStmt());
|
||||
|
||||
detachStmts.push(
|
||||
animationTransitionVar
|
||||
.set(animationFnExpr.callFn([view, renderElement, lastRenderValue, emptyStateValue]))
|
||||
.toDeclStmt());
|
||||
detachStmts.push(animationTransitionVar
|
||||
.set(animationFnExpr.callFn(
|
||||
[view, renderElement, evalResult.currValExpr, emptyStateValue]))
|
||||
.toDeclStmt());
|
||||
|
||||
const registerStmts: o.Statement[] = [];
|
||||
const animationStartMethodExists = boundOutputs.find(
|
||||
@ -151,5 +143,14 @@ export function triggerAnimation(
|
||||
updateStmts.push(...registerStmts);
|
||||
detachStmts.push(...registerStmts);
|
||||
|
||||
return {updateStmts, detachStmts};
|
||||
const checkUpdateStmts: o.Statement[] = [
|
||||
...evalResult.stmts,
|
||||
new o.IfStmt(
|
||||
o.importExpr(createIdentifier(Identifiers.checkBinding)).callFn([
|
||||
view, oldValue, evalResult.currValExpr, evalResult.forceUpdate || o.literal(false)
|
||||
]),
|
||||
updateStmts)
|
||||
];
|
||||
const checkDetachStmts: o.Statement[] = [...evalResult.stmts, ...detachStmts];
|
||||
return {checkUpdateStmts, checkDetachStmts};
|
||||
}
|
||||
|
Reference in New Issue
Block a user