From 13f3157823f5f88ce00c46dc44f57073a1c3fad9 Mon Sep 17 00:00:00 2001 From: Pawel Kozlowski Date: Wed, 18 Jul 2018 09:42:42 +0200 Subject: [PATCH] fix(ivy): update content query compilation to latest runtime (#24957) PR Close #24957 --- .../compliance/r3_compiler_compliance_spec.ts | 15 +++- packages/compiler/src/render3/r3_factory.ts | 16 +--- .../compiler/src/render3/r3_identifiers.ts | 2 + .../compiler/src/render3/view/compiler.ts | 83 ++++++++++++++----- packages/core/src/render3/jit/environment.ts | 2 + 5 files changed, 78 insertions(+), 40 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 33bf098b7f..6fd0a1c9ed 100644 --- a/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts @@ -987,7 +987,7 @@ describe('compiler compliance', () => { app: { ...directive, 'spec.ts': ` - import {Component, ContentChild, NgModule} from '@angular/core'; + import {Component, ContentChild, ContentChildren, NgModule, QueryList} from '@angular/core'; import {SomeDirective} from './some.directive'; @Component({ @@ -998,6 +998,7 @@ describe('compiler compliance', () => { }) export class ContentQueryComponent { @ContentChild(SomeDirective) someDir: SomeDirective; + @ContentChildren(SomeDirective) someDirList !: QueryList; } @Component({ @@ -1021,11 +1022,17 @@ describe('compiler compliance', () => { type: ContentQueryComponent, selectors: [["content-query-component"]], factory: function ContentQueryComponent_Factory() { - return [new ContentQueryComponent(), $r3$.ɵQ(null, SomeDirective, true)]; + return new ContentQueryComponent(); + }, + contentQueries: function ContentQueryComponent_ContentQueries() { + $r3$.ɵQr($r3$.ɵQ(null, SomeDirective, true)); + $r3$.ɵQr($r3$.ɵQ(null, SomeDirective, false)); }, - hostBindings: function ContentQueryComponent_HostBindings(dirIndex, elIndex) { + contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) { + const instance = $r3$.ɵd(dirIndex); var $tmp$; - ($r3$.ɵqR(($tmp$ = $r3$.ɵd(dirIndex)[1])) && ($r3$.ɵd(dirIndex)[0].someDir = $tmp$.first)); + ($r3$.ɵqR(($tmp$ = $r3$.ɵql(queryStartIndex))) && ($instance$.someDir = $tmp$.first)); + ($r3$.ɵqR(($tmp$ = $r3$.ɵql((queryStartIndex + 1)))) && ($instance$.someDirList = $tmp$)); }, template: function ContentQueryComponent_Template(rf, ctx) { if (rf & 1) { diff --git a/packages/compiler/src/render3/r3_factory.ts b/packages/compiler/src/render3/r3_factory.ts index ba50ae1550..949f6672de 100644 --- a/packages/compiler/src/render3/r3_factory.ts +++ b/packages/compiler/src/render3/r3_factory.ts @@ -54,16 +54,6 @@ export interface R3FactoryMetadata { * function could be different, and other options control how it will be invoked. */ injectFn: o.ExternalReference; - - /** - * If present, the return of the factory function will be an array with the injected value in the - * 0th position and the extra results included in subsequent positions. - * - * Occasionally APIs want to construct additional values when the factory function is called. The - * paradigm there is to have the factory function return an array, with the DI-created value as - * well as other values. Specifying `extraResults` enables this functionality. - */ - extraResults?: o.Expression[]; } /** @@ -160,12 +150,8 @@ export function compileFactoryFunction(meta: R3FactoryMetadata): o.Expression { const expr = meta.useNew ? new o.InstantiateExpr(meta.fnOrClass, args) : new o.InvokeFunctionExpr(meta.fnOrClass, args); - // If `extraResults` is specified, then the result is an array consisting of the instantiated - // value plus any extra results. - const retExpr = - meta.extraResults === undefined ? expr : o.literalArr([expr, ...meta.extraResults]); return o.fn( - [], [new o.ReturnStatement(retExpr)], o.INFERRED_TYPE, undefined, `${meta.name}_Factory`); + [], [new o.ReturnStatement(expr)], o.INFERRED_TYPE, undefined, `${meta.name}_Factory`); } function compileInjectDependency( diff --git a/packages/compiler/src/render3/r3_identifiers.ts b/packages/compiler/src/render3/r3_identifiers.ts index 72f2f30ba0..ae60d396a1 100644 --- a/packages/compiler/src/render3/r3_identifiers.ts +++ b/packages/compiler/src/render3/r3_identifiers.ts @@ -80,6 +80,7 @@ export class Identifiers { static load: o.ExternalReference = {name: 'ɵld', moduleName: CORE}; static loadDirective: o.ExternalReference = {name: 'ɵd', moduleName: CORE}; + static loadQueryList: o.ExternalReference = {name: 'ɵql', moduleName: CORE}; static pipe: o.ExternalReference = {name: 'ɵPp', moduleName: CORE}; @@ -142,6 +143,7 @@ export class Identifiers { static query: o.ExternalReference = {name: 'ɵQ', moduleName: CORE}; static queryRefresh: o.ExternalReference = {name: 'ɵqR', moduleName: CORE}; + static registerContentQuery: o.ExternalReference = {name: 'ɵQr', moduleName: CORE}; static NgOnChangesFeature: o.ExternalReference = {name: 'ɵNgOnChangesFeature', moduleName: CORE}; diff --git a/packages/compiler/src/render3/view/compiler.ts b/packages/compiler/src/render3/view/compiler.ts index 0905821b33..f008fa572e 100644 --- a/packages/compiler/src/render3/view/compiler.ts +++ b/packages/compiler/src/render3/view/compiler.ts @@ -41,7 +41,6 @@ function baseDirectiveFields( // e.g. `selectors: [['', 'someDir', '']]` definitionMap.set('selectors', createDirectiveSelector(meta.selector !)); - const queryDefinitions = createQueryDefinitions(meta.queries, constantPool); // e.g. `factory: () => new MyApp(injectElementRef())` definitionMap.set('factory', compileFactoryFunction({ @@ -50,9 +49,12 @@ function baseDirectiveFields( deps: meta.deps, useNew: true, injectFn: R3.directiveInject, - extraResults: queryDefinitions, })); + definitionMap.set('contentQueries', createContentQueriesFunction(meta, constantPool)); + + definitionMap.set('contentQueriesRefresh', createContentQueriesRefreshFunction(meta)); + // e.g. `hostBindings: (dirIndex, elIndex) => { ... } definitionMap.set('hostBindings', createHostBindingsFunction(meta, bindingParser)); @@ -366,32 +368,71 @@ function createHostAttributesArray(meta: R3DirectiveMetadata): o.Expression|null return 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(); + }); + const typeName = meta.name; + return o.fn( + [], statements, o.INFERRED_TYPE, null, typeName ? `${typeName}_ContentQueries` : null); + } + + return null; +} + +// Return a contentQueriesRefresh function or null if one is not necessary. +function createContentQueriesRefreshFunction(meta: R3DirectiveMetadata): o.Expression|null { + if (meta.queries.length > 0) { + const statements: o.Statement[] = []; + const typeName = meta.name; + const parameters = [ + new o.FnParam('dirIndex', o.NUMBER_TYPE), + new o.FnParam('queryStartIndex', o.NUMBER_TYPE), + ]; + const directiveInstanceVar = o.variable('instance'); + // var $tmp$: any; + const temporary = temporaryAllocator(statements, TEMPORARY_NAME); + + // const $instance$ = $r3$.ɵd(dirIndex); + statements.push( + directiveInstanceVar.set(o.importExpr(R3.loadDirective).callFn([o.variable('dirIndex')])) + .toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Final])); + + meta.queries.forEach((query: R3QueryMetadata, idx: number) => { + const loadQLArg = o.variable('queryStartIndex'); + const getQueryList = o.importExpr(R3.loadQueryList).callFn([ + idx > 0 ? loadQLArg.plus(o.literal(idx)) : loadQLArg + ]); + const assignToTemporary = temporary().set(getQueryList); + const callQueryRefresh = o.importExpr(R3.queryRefresh).callFn([assignToTemporary]); + + const updateDirective = directiveInstanceVar.prop(query.propertyName) + .set(query.first ? temporary().prop('first') : temporary()); + const refreshQueryAndUpdateDirective = callQueryRefresh.and(updateDirective); + + statements.push(refreshQueryAndUpdateDirective.toStmt()); + }); + + return o.fn( + parameters, statements, o.INFERRED_TYPE, null, + typeName ? `${typeName}_ContentQueriesRefresh` : null); + } + + return null; +} + // Return a host binding function or null if one is not necessary. function createHostBindingsFunction( meta: R3DirectiveMetadata, bindingParser: BindingParser): o.Expression|null { const statements: o.Statement[] = []; - const temporary = temporaryAllocator(statements, TEMPORARY_NAME); - const hostBindingSourceSpan = meta.typeSourceSpan; - // Calculate the queries - for (let index = 0; index < meta.queries.length; index++) { - const query = meta.queries[index]; - - // e.g. r3.qR(tmp = r3.d(dirIndex)[1]) && (r3.d(dirIndex)[0].someDir = tmp); - const getDirectiveMemory = o.importExpr(R3.loadDirective).callFn([o.variable('dirIndex')]); - // The query list is at the query index + 1 because the directive itself is in slot 0. - const getQueryList = getDirectiveMemory.key(o.literal(index + 1)); - const assignToTemporary = temporary().set(getQueryList); - const callQueryRefresh = o.importExpr(R3.queryRefresh).callFn([assignToTemporary]); - const updateDirective = getDirectiveMemory.key(o.literal(0, o.INFERRED_TYPE)) - .prop(query.propertyName) - .set(query.first ? temporary().prop('first') : temporary()); - const andExpression = callQueryRefresh.and(updateDirective); - statements.push(andExpression.toStmt()); - } - const directiveSummary = metadataAsSummary(meta); // Calculate the host property bindings diff --git a/packages/core/src/render3/jit/environment.ts b/packages/core/src/render3/jit/environment.ts index b5808dd6ad..65e122c5f5 100644 --- a/packages/core/src/render3/jit/environment.ts +++ b/packages/core/src/render3/jit/environment.ts @@ -38,6 +38,7 @@ export const angularCoreEnv: {[name: string]: Function} = { 'ɵcR': r3.cR, 'ɵcr': r3.cr, 'ɵd': r3.d, + 'ɵql': r3.ql, 'ɵNH': r3.NH, 'ɵNM': r3.NM, 'ɵNS': r3.NS, @@ -77,6 +78,7 @@ export const angularCoreEnv: {[name: string]: Function} = { 'ɵPp': r3.Pp, 'ɵQ': r3.Q, 'ɵqR': r3.qR, + 'ɵQr': r3.Qr, 'ɵrS': r3.rS, 'ɵs': r3.s, 'ɵsm': r3.sm,