fix(ivy): update content query compilation to latest runtime (#24957)

PR Close #24957
This commit is contained in:
Pawel Kozlowski 2018-07-18 09:42:42 +02:00 committed by Victor Berchet
parent edef58f466
commit 13f3157823
5 changed files with 78 additions and 40 deletions

View File

@ -987,7 +987,7 @@ describe('compiler compliance', () => {
app: { app: {
...directive, ...directive,
'spec.ts': ` 'spec.ts': `
import {Component, ContentChild, NgModule} from '@angular/core'; import {Component, ContentChild, ContentChildren, NgModule, QueryList} from '@angular/core';
import {SomeDirective} from './some.directive'; import {SomeDirective} from './some.directive';
@Component({ @Component({
@ -998,6 +998,7 @@ describe('compiler compliance', () => {
}) })
export class ContentQueryComponent { export class ContentQueryComponent {
@ContentChild(SomeDirective) someDir: SomeDirective; @ContentChild(SomeDirective) someDir: SomeDirective;
@ContentChildren(SomeDirective) someDirList !: QueryList<SomeDirective>;
} }
@Component({ @Component({
@ -1021,11 +1022,17 @@ describe('compiler compliance', () => {
type: ContentQueryComponent, type: ContentQueryComponent,
selectors: [["content-query-component"]], selectors: [["content-query-component"]],
factory: function ContentQueryComponent_Factory() { 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$; 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) { template: function ContentQueryComponent_Template(rf, ctx) {
if (rf & 1) { if (rf & 1) {

View File

@ -54,16 +54,6 @@ export interface R3FactoryMetadata {
* function could be different, and other options control how it will be invoked. * function could be different, and other options control how it will be invoked.
*/ */
injectFn: o.ExternalReference; 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) : const expr = meta.useNew ? new o.InstantiateExpr(meta.fnOrClass, args) :
new o.InvokeFunctionExpr(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( 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( function compileInjectDependency(

View File

@ -80,6 +80,7 @@ export class Identifiers {
static load: o.ExternalReference = {name: 'ɵld', moduleName: CORE}; static load: o.ExternalReference = {name: 'ɵld', moduleName: CORE};
static loadDirective: o.ExternalReference = {name: 'ɵd', 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}; static pipe: o.ExternalReference = {name: 'ɵPp', moduleName: CORE};
@ -142,6 +143,7 @@ export class Identifiers {
static query: o.ExternalReference = {name: 'ɵQ', moduleName: CORE}; static query: o.ExternalReference = {name: 'ɵQ', moduleName: CORE};
static queryRefresh: o.ExternalReference = {name: 'ɵqR', 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}; static NgOnChangesFeature: o.ExternalReference = {name: 'ɵNgOnChangesFeature', moduleName: CORE};

View File

@ -41,7 +41,6 @@ function baseDirectiveFields(
// e.g. `selectors: [['', 'someDir', '']]` // e.g. `selectors: [['', 'someDir', '']]`
definitionMap.set('selectors', createDirectiveSelector(meta.selector !)); definitionMap.set('selectors', createDirectiveSelector(meta.selector !));
const queryDefinitions = createQueryDefinitions(meta.queries, constantPool);
// e.g. `factory: () => new MyApp(injectElementRef())` // e.g. `factory: () => new MyApp(injectElementRef())`
definitionMap.set('factory', compileFactoryFunction({ definitionMap.set('factory', compileFactoryFunction({
@ -50,9 +49,12 @@ function baseDirectiveFields(
deps: meta.deps, deps: meta.deps,
useNew: true, useNew: true,
injectFn: R3.directiveInject, injectFn: R3.directiveInject,
extraResults: queryDefinitions,
})); }));
definitionMap.set('contentQueries', createContentQueriesFunction(meta, constantPool));
definitionMap.set('contentQueriesRefresh', createContentQueriesRefreshFunction(meta));
// e.g. `hostBindings: (dirIndex, elIndex) => { ... } // e.g. `hostBindings: (dirIndex, elIndex) => { ... }
definitionMap.set('hostBindings', createHostBindingsFunction(meta, bindingParser)); definitionMap.set('hostBindings', createHostBindingsFunction(meta, bindingParser));
@ -366,32 +368,71 @@ function createHostAttributesArray(meta: R3DirectiveMetadata): o.Expression|null
return 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. // Return a host binding function or null if one is not necessary.
function createHostBindingsFunction( function createHostBindingsFunction(
meta: R3DirectiveMetadata, bindingParser: BindingParser): o.Expression|null { meta: R3DirectiveMetadata, bindingParser: BindingParser): o.Expression|null {
const statements: o.Statement[] = []; const statements: o.Statement[] = [];
const temporary = temporaryAllocator(statements, TEMPORARY_NAME);
const hostBindingSourceSpan = meta.typeSourceSpan; 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); const directiveSummary = metadataAsSummary(meta);
// Calculate the host property bindings // Calculate the host property bindings

View File

@ -38,6 +38,7 @@ export const angularCoreEnv: {[name: string]: Function} = {
'ɵcR': r3.cR, 'ɵcR': r3.cR,
'ɵcr': r3.cr, 'ɵcr': r3.cr,
'ɵd': r3.d, 'ɵd': r3.d,
'ɵql': r3.ql,
'ɵNH': r3.NH, 'ɵNH': r3.NH,
'ɵNM': r3.NM, 'ɵNM': r3.NM,
'ɵNS': r3.NS, 'ɵNS': r3.NS,
@ -77,6 +78,7 @@ export const angularCoreEnv: {[name: string]: Function} = {
'ɵPp': r3.Pp, 'ɵPp': r3.Pp,
'ɵQ': r3.Q, 'ɵQ': r3.Q,
'ɵqR': r3.qR, 'ɵqR': r3.qR,
'ɵQr': r3.Qr,
'ɵrS': r3.rS, 'ɵrS': r3.rS,
'ɵs': r3.s, 'ɵs': r3.s,
'ɵsm': r3.sm, 'ɵsm': r3.sm,