From 1e28495c898d531db15a8ac26c6c6a31cebcafb9 Mon Sep 17 00:00:00 2001 From: Pawel Kozlowski Date: Tue, 24 Jul 2018 11:56:35 +0200 Subject: [PATCH] fix(ivy): update compiler with latest runtime for view queries (#25061) PR Close #25061 --- .../compliance/r3_compiler_compliance_spec.ts | 10 ++- .../compiler/src/render3/view/compiler.ts | 87 ++++++++++++------- .../compiler/src/render3/view/template.ts | 44 +++------- 3 files changed, 75 insertions(+), 66 deletions(-) diff --git a/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts b/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts index 3d549e7665..049c9ba8d9 100644 --- a/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts @@ -963,16 +963,20 @@ describe('compiler compliance', () => { type: ViewQueryComponent, selectors: [["view-query-component"]], factory: function ViewQueryComponent_Factory() { return new ViewQueryComponent(); }, - template: function ViewQueryComponent_Template(rf, ctx) { - var $tmp$; + viewQuery: function ViewQueryComponent_Query(rf, ctx) { if (rf & 1) { $r3$.ɵQ(0, SomeDirective, true); - $r3$.ɵEe(1, "div", $e0_attrs$); } if (rf & 2) { + var $tmp$; ($r3$.ɵqR(($tmp$ = $r3$.ɵld(0))) && (ctx.someDir = $tmp$.first)); } }, + template: function ViewQueryComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵEe(1, "div", $e0_attrs$); + } + }, directives:[SomeDirective] });`; diff --git a/packages/compiler/src/render3/view/compiler.ts b/packages/compiler/src/render3/view/compiler.ts index f008fa572e..4e6bd46be8 100644 --- a/packages/compiler/src/render3/view/compiler.ts +++ b/packages/compiler/src/render3/view/compiler.ts @@ -27,8 +27,8 @@ import {Render3ParseResult} from '../r3_template_transform'; import {typeWithParameters} from '../util'; import {R3ComponentDef, R3ComponentMetadata, R3DirectiveDef, R3DirectiveMetadata, R3QueryMetadata} from './api'; -import {BindingScope, TemplateDefinitionBuilder} from './template'; -import {CONTEXT_NAME, DefinitionMap, ID_SEPARATOR, MEANING_SEPARATOR, TEMPORARY_NAME, asLiteral, conditionallyCreateMapObjectLiteral, getQueryPredicate, temporaryAllocator, unsupported} from './util'; +import {BindingScope, TemplateDefinitionBuilder, renderFlagCheckIfStmt} from './template'; +import {CONTEXT_NAME, DefinitionMap, ID_SEPARATOR, MEANING_SEPARATOR, RENDER_FLAGS, TEMPORARY_NAME, asLiteral, conditionallyCreateMapObjectLiteral, getQueryPredicate, temporaryAllocator, unsupported} from './util'; function baseDirectiveFields( meta: R3DirectiveMetadata, constantPool: ConstantPool, @@ -138,6 +138,10 @@ export function compileComponentFromMetadata( directiveMatcher = matcher; } + if (meta.viewQueries.length) { + definitionMap.set('viewQuery', createViewQueriesFunction(meta, constantPool)); + } + // e.g. `template: function MyComponent_Template(_ctx, _cm) {...}` const templateTypeName = meta.name; const templateName = templateTypeName ? `${templateTypeName}_Template` : null; @@ -322,32 +326,22 @@ function selectorsFromGlobalMetadata( return o.NULL_EXPR; } -/** - * - * @param meta - * @param constantPool - */ -function createQueryDefinitions( - queries: R3QueryMetadata[], constantPool: ConstantPool): o.Expression[]|undefined { - const queryDefinitions: o.Expression[] = []; - for (let i = 0; i < queries.length; i++) { - const query = queries[i]; - const predicate = getQueryPredicate(query, constantPool); +function createQueryDefinition( + query: R3QueryMetadata, constantPool: ConstantPool, idx: number | null): o.Expression { + const predicate = getQueryPredicate(query, constantPool); - // e.g. r3.Q(null, somePredicate, false) or r3.Q(null, ['div'], false) - const parameters = [ - o.literal(null, o.INFERRED_TYPE), - predicate, - o.literal(query.descendants), - ]; + // e.g. r3.Q(null, somePredicate, false) or r3.Q(0, ['div'], false) + const parameters = [ + o.literal(idx, o.INFERRED_TYPE), + predicate, + o.literal(query.descendants), + ]; - if (query.read) { - parameters.push(query.read); - } - - queryDefinitions.push(o.importExpr(R3.query).callFn(parameters)); + if (query.read) { + parameters.push(query.read); } - return queryDefinitions.length > 0 ? queryDefinitions : undefined; + + return o.importExpr(R3.query).callFn(parameters); } // Turn a directive selector into an R3-compatible selector for directive def @@ -371,11 +365,10 @@ function createHostAttributesArray(meta: R3DirectiveMetadata): o.Expression|null // Return a contentQueries function or null if one is not necessary. function createContentQueriesFunction( meta: R3DirectiveMetadata, constantPool: ConstantPool): o.Expression|null { - const queryDefinitions = createQueryDefinitions(meta.queries, constantPool); - - if (queryDefinitions) { - const statements: o.Statement[] = queryDefinitions.map((qd: o.Expression) => { - return o.importExpr(R3.registerContentQuery).callFn([qd]).toStmt(); + if (meta.queries.length) { + const statements: o.Statement[] = meta.queries.map((query: R3QueryMetadata) => { + const queryDefinition = createQueryDefinition(query, constantPool, null); + return o.importExpr(R3.registerContentQuery).callFn([queryDefinition]).toStmt(); }); const typeName = meta.name; return o.fn( @@ -426,6 +419,40 @@ function createContentQueriesRefreshFunction(meta: R3DirectiveMetadata): o.Expre return null; } +// Define and update any view queries +function createViewQueriesFunction( + meta: R3ComponentMetadata, constantPool: ConstantPool): o.Expression { + const createStatements: o.Statement[] = []; + const updateStatements: o.Statement[] = []; + const tempAllocator = temporaryAllocator(updateStatements, TEMPORARY_NAME); + + for (let i = 0; i < meta.viewQueries.length; i++) { + const query = meta.viewQueries[i]; + + // creation, e.g. r3.Q(0, somePredicate, true); + const queryDefinition = createQueryDefinition(query, constantPool, i); + createStatements.push(queryDefinition.toStmt()); + + // update, e.g. (r3.qR(tmp = r3.ɵld(0)) && (ctx.someDir = tmp)); + const temporary = tempAllocator(); + const getQueryList = o.importExpr(R3.load).callFn([o.literal(i)]); + const refresh = o.importExpr(R3.queryRefresh).callFn([temporary.set(getQueryList)]); + const updateDirective = o.variable(CONTEXT_NAME) + .prop(query.propertyName) + .set(query.first ? temporary.prop('first') : temporary); + updateStatements.push(refresh.and(updateDirective).toStmt()); + } + + const viewQueryFnName = meta.name ? `${meta.name}_Query` : null; + return o.fn( + [new o.FnParam(RENDER_FLAGS, o.NUMBER_TYPE), new o.FnParam(CONTEXT_NAME, null)], + [ + renderFlagCheckIfStmt(core.RenderFlags.Create, createStatements), + renderFlagCheckIfStmt(core.RenderFlags.Update, updateStatements) + ], + o.INFERRED_TYPE, null, viewQueryFnName); +} + // Return a host binding function or null if one is not necessary. function createHostBindingsFunction( meta: R3DirectiveMetadata, bindingParser: BindingParser): o.Expression|null { diff --git a/packages/compiler/src/render3/view/template.ts b/packages/compiler/src/render3/view/template.ts index a2a937d7ea..cdb8f76452 100644 --- a/packages/compiler/src/render3/view/template.ts +++ b/packages/compiler/src/render3/view/template.ts @@ -46,6 +46,12 @@ function mapBindingToInstruction(type: BindingType): o.ExternalReference|undefin } } +// if (rf & flags) { .. } +export function renderFlagCheckIfStmt( + flags: core.RenderFlags, statements: o.Statement[]): o.IfStmt { + return o.ifStmt(o.variable(RENDER_FLAGS).bitwiseAnd(o.literal(flags), null, false), statements); +} + export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver { private _dataIndex = 0; private _bindingContext = 0; @@ -54,7 +60,6 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver private _variableCode: o.Statement[] = []; private _bindingCode: o.Statement[] = []; private _postfixCode: o.Statement[] = []; - private _temporary = temporaryAllocator(this._prefixCode, TEMPORARY_NAME); private _valueConverter: ValueConverter; private _unsupported = unsupported; private _bindingScope: BindingScope; @@ -75,6 +80,9 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver private directiveMatcher: SelectorMatcher|null, private directives: Set, private pipeTypeByName: Map, private pipes: Set, private _namespace: o.ExternalReference) { + // view queries can take up space in data and allocation happens earlier (in the "viewQuery" + // function) + this._dataIndex = viewQueries.length; this._bindingScope = parentBindingScope.nestedScope((lhsVar: o.ReadVarExpr, expression: o.Expression) => { this._bindingCode.push( @@ -127,32 +135,6 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver this.instruction(this._creationCode, null, R3.projectionDef, ...parameters); } - // Define and update any view queries - for (let query of this.viewQueries) { - // e.g. r3.Q(0, somePredicate, true); - const querySlot = this.allocateDataSlot(); - const predicate = getQueryPredicate(query, this.constantPool); - const args: o.Expression[] = [ - o.literal(querySlot, o.INFERRED_TYPE), - predicate, - o.literal(query.descendants, o.INFERRED_TYPE), - ]; - - if (query.read) { - args.push(query.read); - } - this.instruction(this._creationCode, null, R3.query, ...args); - - // (r3.qR(tmp = r3.ɵld(0)) && (ctx.someDir = tmp)); - const temporary = this._temporary(); - const getQueryList = o.importExpr(R3.load).callFn([o.literal(querySlot)]); - const refresh = o.importExpr(R3.queryRefresh).callFn([temporary.set(getQueryList)]); - const updateDirective = o.variable(CONTEXT_NAME) - .prop(query.propertyName) - .set(query.first ? temporary.prop('first') : temporary); - this._bindingCode.push(refresh.and(updateDirective).toStmt()); - } - t.visitAll(this, nodes); if (this._pureFunctionSlots > 0) { @@ -161,15 +143,11 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver } const creationCode = this._creationCode.length > 0 ? - [o.ifStmt( - o.variable(RENDER_FLAGS).bitwiseAnd(o.literal(core.RenderFlags.Create), null, false), - this._creationCode)] : + [renderFlagCheckIfStmt(core.RenderFlags.Create, this._creationCode)] : []; const updateCode = this._bindingCode.length > 0 ? - [o.ifStmt( - o.variable(RENDER_FLAGS).bitwiseAnd(o.literal(core.RenderFlags.Update), null, false), - this._bindingCode)] : + [renderFlagCheckIfStmt(core.RenderFlags.Update, this._bindingCode)] : []; // Generate maps of placeholder name to node indexes