refactor(core): Remove the need for explicit static query instruction (#40091)

Because the query now has `flags` which specify the mode, the static query
instruction can now be remove. It is simply normal query with `static` flag.

PR Close #40091
This commit is contained in:
Misko Hevery 2021-01-08 16:00:57 -08:00 committed by Andrew Kushnir
parent e32b6256ce
commit d516113803
17 changed files with 62 additions and 99 deletions

View File

@ -570,7 +570,7 @@ A.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: A, selectors: [["", "a", ""]] });`
UndecoratedBase.ɵfac = function UndecoratedBase_Factory(t) { return new (t || UndecoratedBase)(); };
// TRANSPILED
UndecoratedBase.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: UndecoratedBase, viewQuery: function UndecoratedBase_Query(rf, ctx) { if (rf & 1) {
ɵngcc0.ɵɵstaticViewQuery(_c0, true);
ɵngcc0.ɵɵviewQuery(_c0, 3);
} if (rf & 2) {
let _t;
ɵngcc0.ɵɵqueryRefresh(_t = ɵngcc0.ɵɵloadQuery()) && (ctx.test = _t.first);

View File

@ -3,8 +3,9 @@ ContentQueryComponent.ɵcmp = $r3$.ɵɵdefineComponent({
selectors: [["content-query-component"]],
contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) {
if (rf & 1) {
$r3$.ɵɵstaticContentQuery(dirIndex, SomeDirective, 3);
$r3$.ɵɵcontentQuery(dirIndex, $ref0$, 1);
$r3$.ɵɵcontentQuery(
dirIndex, SomeDirective, __QueryFlags.isStatic__|__QueryFlags.descendants__);
$r3$.ɵɵcontentQuery(dirIndex, $ref0$, 1);
}
if (rf & 2) {
let $tmp$;

View File

@ -5,7 +5,7 @@ ViewQueryComponent.ɵcmp = $r3$.ɵɵdefineComponent({
selectors: [["view-query-component"]],
viewQuery: function ViewQueryComponent_Query(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵstaticViewQuery(SomeDirective, 3);
$r3$.ɵɵviewQuery(SomeDirective, __QueryFlags.isStatic__|__QueryFlags.descendants__);
$r3$.ɵɵviewQuery($refs$, 1);
}
if (rf & 2) {

View File

@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AttributeMarker, SelectorFlags} from '@angular/compiler/src/core';
import {QueryFlags} from '@angular/compiler/src/render3/view/compiler';
import {i18nIcuMsg, i18nMsg, i18nMsgWithPostprocess, Placeholder} from './i18n_helpers';
const EXPECTED_FILE_MACROS: [RegExp, (...args: string[]) => string][] = [
@ -35,6 +36,9 @@ const EXPECTED_FILE_MACROS: [RegExp, (...args: string[]) => string][] = [
// E.g. `__SelectorFlags.ELEMENT__`
flagUnion(/__SelectorFlags\.([^_]+)__/, (_match, member) => getSelectorFlag(member)),
// E.g. `__QueryFlags.ELEMENT__`
flagUnion(/__QueryFlags\.([^_]+)__/, (_match, member) => getQueryFlag(member)),
];
/**
@ -107,6 +111,21 @@ function getSelectorFlag(member: string): number {
return marker;
}
const QueryFlagsMap: Record<string, QueryFlags> = {
none: QueryFlags.none,
descendants: QueryFlags.descendants,
isStatic: QueryFlags.isStatic,
emitDistinctChangesOnly: QueryFlags.emitDistinctChangesOnly,
};
function getQueryFlag(member: string): number {
const marker = QueryFlagsMap[member];
if (typeof marker !== 'number') {
throw new Error('Unknown SelectorFlag: ' + member);
}
return marker;
}
function stringParam() {
return /'([^']*?[^\\])'/;
}

View File

@ -1746,7 +1746,7 @@ describe('compiler compliance', () => {
selectors: [["view-query-component"]],
viewQuery: function ViewQueryComponent_Query(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵstaticViewQuery(SomeDirective, 3);
$r3$.ɵɵviewQuery(SomeDirective, 3);
$r3$.ɵɵviewQuery($refs$, 1);
}
if (rf & 2) {
@ -1992,7 +1992,7 @@ describe('compiler compliance', () => {
selectors: [["content-query-component"]],
contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) {
if (rf & 1) {
$r3$.ɵɵstaticContentQuery(dirIndex, SomeDirective, 3);
$r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 3);
$r3$.ɵɵcontentQuery(dirIndex, $ref0$, 1);
}
if (rf & 2) {

View File

@ -40,6 +40,7 @@ export interface Query {
isViewQuery: boolean;
selector: any;
static?: boolean;
emitDistinctChangesOnly: boolean;
}
export const createContentChildren = makeMetadataFactory<Query>(

View File

@ -297,8 +297,6 @@ export class Identifiers {
static queryRefresh: o.ExternalReference = {name: 'ɵɵqueryRefresh', moduleName: CORE};
static viewQuery: o.ExternalReference = {name: 'ɵɵviewQuery', moduleName: CORE};
static staticViewQuery: o.ExternalReference = {name: 'ɵɵstaticViewQuery', moduleName: CORE};
static staticContentQuery: o.ExternalReference = {name: 'ɵɵstaticContentQuery', moduleName: CORE};
static loadQuery: o.ExternalReference = {name: 'ɵɵloadQuery', moduleName: CORE};
static contentQuery: o.ExternalReference = {name: 'ɵɵcontentQuery', moduleName: CORE};

View File

@ -479,10 +479,9 @@ export const enum QueryFlags {
* @param query
*/
function toQueryFlags(query: R3QueryMetadata): number {
// NOTE: Verify that changes here match
return (query.descendants ? 1 /* TQueryFlags.descendants */ : 0) |
(query.static ? 2 /* TQueryFlags.isStatic */ : 0) |
(query.emitDistinctChangesOnly ? 4 /* TQueryFlags.emitDistinctChangesOnly */ : 0);
return (query.descendants ? QueryFlags.descendants : QueryFlags.none) |
(query.static ? QueryFlags.isStatic : QueryFlags.none) |
(query.emitDistinctChangesOnly ? QueryFlags.emitDistinctChangesOnly : QueryFlags.none);
}
function convertAttributesToExpressions(attributes: {[name: string]: o.Expression}):
@ -503,11 +502,9 @@ function createContentQueriesFunction(
const tempAllocator = temporaryAllocator(updateStatements, TEMPORARY_NAME);
for (const query of queries) {
const queryInstruction = query.static ? R3.staticContentQuery : R3.contentQuery;
// creation, e.g. r3.contentQuery(dirIndex, somePredicate, true, null);
createStatements.push(
o.importExpr(queryInstruction)
o.importExpr(R3.contentQuery)
.callFn([o.variable('dirIndex'), ...prepareQueryParams(query, constantPool) as any])
.toStmt());
@ -587,11 +584,9 @@ function createViewQueriesFunction(
const tempAllocator = temporaryAllocator(updateStatements, TEMPORARY_NAME);
viewQueries.forEach((query: R3QueryMetadata) => {
const queryInstruction = query.static ? R3.staticViewQuery : R3.viewQuery;
// creation, e.g. r3.viewQuery(somePredicate, true);
const queryDefinition =
o.importExpr(queryInstruction).callFn(prepareQueryParams(query, constantPool));
o.importExpr(R3.viewQuery).callFn(prepareQueryParams(query, constantPool));
createStatements.push(queryDefinition.toStmt());
// update, e.g. (r3.queryRefresh(tmp = r3.loadQuery()) && (ctx.someDir = tmp));

View File

@ -206,8 +206,6 @@ export {
ɵɵsetComponentScope,
ɵɵsetNgModuleScope,
ɵɵstaticContentQuery,
ɵɵstaticViewQuery,
ɵɵstyleMap,
ɵɵstyleMapInterpolate1,
ɵɵstyleMapInterpolate2,

View File

@ -90,7 +90,6 @@ export class ElementRef<T = any> {
/**
* Unwraps `ElementRef` and return the `nativeElement`.
*
* Conditionally unwrap the `ElementRef`.
* @param value value to unwrap
* @returns `nativeElement` if `ElementRef` otherwise returns value as is.
*/

View File

@ -54,10 +54,6 @@ export class QueryList<T> implements Iterable<T> {
/**
* Returns `Observable` of `QueryList` notifying the subscriber of changes.
*
* NOTE: This currently points to `changesDeprecated` which incorrectly notifies of changes even
* if no changes to `QueryList` have occurred. (It fires more often than it needs to.)
* The implementation will change to point `changesStrict` starting with v12.
*/
get changes(): Observable<any> {
return this._changes || (this._changes = new EventEmitter());
@ -151,10 +147,15 @@ export class QueryList<T> implements Iterable<T> {
* occurs.
*
* @param resultsTree The query results to store
* @param identityAccessor Optional functions for extracting stable object identity from a value
* in the array.
* @param identityAccessor Optional function for extracting stable object identity from a value
* in the array. This function is executed for each element of the query result list while
* comparing current query list with the new one (provided as a first argument of the `reset`
* function) to detect if the lists are different. If the function is not provided, elements
* are compared as is (without any pre-processing).
*/
reset(resultsTree: Array<T|any[]>, identityAccessor?: (value: T) => unknown): void {
// Cast to `QueryListInternal` so that we can mutate fields which are readonly for the usage of
// QueryList (but not for QueryList itself.)
const self = this as QueryListInternal<T>;
(self as {dirty: boolean}).dirty = false;
const newResultFlat = flatten(resultsTree);
@ -170,7 +171,7 @@ export class QueryList<T> implements Iterable<T> {
* Triggers a change event by emitting on the `changes` {@link EventEmitter}.
*/
notifyOnChanges(): void {
if (this._changes && (this._emitDistinctChangesOnly ? this._changesDetected : true))
if (this._changes && (this._changesDetected || !this._emitDistinctChangesOnly))
this._changes.emit(this);
}
@ -196,7 +197,7 @@ export class QueryList<T> implements Iterable<T> {
/**
* Internal set of APIs used by the framework. (not to be made public)
*/
export interface QueryListInternal<T> extends QueryList<T> {
interface QueryListInternal<T> extends QueryList<T> {
reset(a: any[]): void;
notifyOnChanges(): void;
length: number;

View File

@ -162,9 +162,6 @@ export {
ɵɵcontentQuery,
ɵɵloadQuery,
ɵɵqueryRefresh,
ɵɵstaticContentQuery
,
ɵɵstaticViewQuery,
ɵɵviewQuery} from './query';
export {
ɵɵdisableBindings,

View File

@ -98,8 +98,6 @@ export const angularCoreEnv: {[name: string]: Function} =
'ɵɵpipe': r3.ɵɵpipe,
'ɵɵqueryRefresh': r3.ɵɵqueryRefresh,
'ɵɵviewQuery': r3.ɵɵviewQuery,
'ɵɵstaticViewQuery': r3.ɵɵstaticViewQuery,
'ɵɵstaticContentQuery': r3.ɵɵstaticContentQuery,
'ɵɵloadQuery': r3.ɵɵloadQuery,
'ɵɵcontentQuery': r3.ɵɵcontentQuery,
'ɵɵreference': r3.ɵɵreference,

View File

@ -202,8 +202,8 @@ class TQuery_ implements TQuery {
}
private isApplyingToNode(tNode: TNode): boolean {
const isDescend = (this.metadata.flags & QueryFlags.descendants) === QueryFlags.descendants;
if (this._appliesToNextNode && !isDescend) {
if (this._appliesToNextNode &&
(this.metadata.flags & QueryFlags.descendants) !== QueryFlags.descendants) {
const declarationNodeIdx = this._declarationNodeIndex;
let parent = tNode.parent;
// Determine if a given TNode is a "direct" child of a node on which a content query was
@ -428,8 +428,9 @@ export function ɵɵqueryRefresh(queryList: QueryList<any>): boolean {
setCurrentQueryIndex(queryIndex + 1);
const tQuery = getTQuery(tView, queryIndex);
const isStatic = (tQuery.metadata.flags & QueryFlags.isStatic) === QueryFlags.isStatic;
if (queryList.dirty && (isCreationMode(lView) === isStatic)) {
if (queryList.dirty &&
(isCreationMode(lView) ===
((tQuery.metadata.flags & QueryFlags.isStatic) === QueryFlags.isStatic))) {
if (tQuery.matches === null) {
queryList.reset([]);
} else {
@ -445,21 +446,6 @@ export function ɵɵqueryRefresh(queryList: QueryList<any>): boolean {
return false;
}
/**
* Creates new QueryList for a static view query.
*
* @param predicate The type for which the query will search
* @param flags Flags associated with the query
* @param read What to save in the query
*
* @codeGenApi
*/
export function ɵɵstaticViewQuery<T>(
predicate: Type<any>|InjectionToken<unknown>|string[], flags: QueryFlags, read?: any): void {
ngDevMode && assertNumber(flags, 'Expecting flags');
viewQueryInternal(getTView(), getLView(), predicate, flags | QueryFlags.isStatic, read);
}
/**
* Creates new QueryList, stores the reference in LView and returns QueryList.
*
@ -472,19 +458,14 @@ export function ɵɵstaticViewQuery<T>(
export function ɵɵviewQuery<T>(
predicate: Type<any>|InjectionToken<unknown>|string[], flags: QueryFlags, read?: any): void {
ngDevMode && assertNumber(flags, 'Expecting flags');
viewQueryInternal(getTView(), getLView(), predicate, flags, read);
}
function viewQueryInternal<T>(
tView: TView, lView: LView, predicate: Type<any>|InjectionToken<unknown>|string[],
flags: QueryFlags, read: any): void {
const tView = getTView();
if (tView.firstCreatePass) {
createTQuery(tView, new TQueryMetadata_(predicate, flags, read), -1);
if (flags & QueryFlags.isStatic) {
if ((flags & QueryFlags.isStatic) === QueryFlags.isStatic) {
tView.staticViewQueries = true;
}
}
createLQuery<T>(tView, lView, flags);
createLQuery<T>(tView, getLView(), flags);
}
/**
@ -503,42 +484,17 @@ export function ɵɵcontentQuery<T>(
directiveIndex: number, predicate: Type<any>|InjectionToken<unknown>|string[],
flags: QueryFlags, read?: any): void {
ngDevMode && assertNumber(flags, 'Expecting flags');
contentQueryInternal(
getTView(), getLView(), predicate, flags, read, false, getCurrentTNode()!, directiveIndex);
}
/**
* Registers a QueryList, associated with a static content query, for later refresh
* (part of a view refresh).
*
* @param directiveIndex Current directive index
* @param predicate The type for which the query will search
* @param flags Flags associated with the query
* @param read What to save in the query
* @returns QueryList<T>
*
* @codeGenApi
*/
export function ɵɵstaticContentQuery<T>(
directiveIndex: number, predicate: Type<any>|InjectionToken<unknown>|string[],
flags: QueryFlags, read?: any): void {
ngDevMode && assertNumber(flags, 'Expecting flags');
contentQueryInternal(
getTView(), getLView(), predicate, flags, read, true, getCurrentTNode()!, directiveIndex);
}
function contentQueryInternal<T>(
tView: TView, lView: LView, predicate: Type<any>|InjectionToken<unknown>|string[],
flags: QueryFlags, read: any, isStatic: boolean, tNode: TNode, directiveIndex: number): void {
const tView = getTView();
if (tView.firstCreatePass) {
const tNode = getCurrentTNode()!;
createTQuery(tView, new TQueryMetadata_(predicate, flags, read), tNode.index);
saveContentQueryAndDirectiveIndex(tView, directiveIndex);
if (isStatic) {
if ((flags & QueryFlags.isStatic) === QueryFlags.isStatic) {
tView.staticContentQueries = true;
}
}
createLQuery<T>(tView, lView, flags);
createLQuery<T>(tView, getLView(), flags);
}
/**

View File

@ -25,7 +25,7 @@ export function addAllToArray(items: any[], arr: any[]) {
*
* @param a first array
* @param b second array
* @param identityAccessor Optional functions for extracting stable object identity from a value in
* @param identityAccessor Optional function for extracting stable object identity from a value in
* the array.
*/
export function arrayEquals<T>(a: T[], b: T[], identityAccessor?: (value: T) => unknown): boolean {

View File

@ -128,14 +128,14 @@ describe('component declaration jit compilation', () => {
expectComponentDef(def, {
contentQueries: functionContaining([
// "byRef" should use `contentQuery` with `0` (`QueryFlags.none`) for descendants flag
// "byRef" should use `contentQuery` with `0` (`QueryFlags.none`) for query flag
// without a read token, and bind to the full query result.
// NOTE: the `anonymous` match is to support IE11, as functions don't have a name there.
/(?:contentQuery|anonymous)[^(]*\(dirIndex,_c0,4\)/,
'(ctx.byRef = _t)',
// "byToken" should use `staticContentQuery` with `3`
// (`QueryFlags.descendants|QueryFlags.isStatic`) for descendants flag and `ElementRef` as
// (`QueryFlags.descendants|QueryFlags.isStatic`) for query flag and `ElementRef` as
// read token, and bind to the first result in the query result.
// NOTE: the `anonymous` match is to support IE11, as functions don't have a name there.
/(?:contentQuery|anonymous)[^(]*\(dirIndex,[^,]*String[^,]*,3,[^)]*ElementRef[^)]*\)/,
@ -174,7 +174,7 @@ describe('component declaration jit compilation', () => {
'(ctx.byRef = _t)',
// "byToken" should use `viewQuery` with `3`
// (`QueryFlags.descendants|QueryFlags.isStatic`) for descendants flag and `ElementRef` as
// (`QueryFlags.descendants|QueryFlags.isStatic`) for query flag and `ElementRef` as
// read token, and bind to the first result in the query result.
// NOTE: the `anonymous` match is to support IE11, as functions don't have a name there.
/(?:viewQuery|anonymous)[^(]*\([^,]*String[^,]*,3,[^)]*ElementRef[^)]*\)/,

View File

@ -103,13 +103,13 @@ describe('directive declaration jit compilation', () => {
expectDirectiveDef(def, {
contentQueries: functionContaining([
// "byRef" should use `contentQuery` with `0` (`QueryFlags.descendants|QueryFlags.isStatic`)
// for descendants flag without a read token, and bind to the full query result.
// for query flag without a read token, and bind to the full query result.
// NOTE: the `anonymous` match is to support IE11, as functions don't have a name there.
/(?:contentQuery|anonymous)[^(]*\(dirIndex,_c0,4\)/,
'(ctx.byRef = _t)',
// "byToken" should use `viewQuery` with `3` (`QueryFlags.static|QueryFlags.descendants`)
// for descendants flag and `ElementRef` as read token, and bind to the first result in the
// for query flag and `ElementRef` as read token, and bind to the first result in the
// query result.
// NOTE: the `anonymous` match is to support IE11, as functions don't have a name there.
/(?:contentQuery|anonymous)[^(]*\([^,]*dirIndex,[^,]*String[^,]*,3,[^)]*ElementRef[^)]*\)/,
@ -140,14 +140,14 @@ describe('directive declaration jit compilation', () => {
expectDirectiveDef(def, {
viewQuery: functionContaining([
// "byRef" should use `viewQuery` with `false` for descendants flag without a read token,
// and bind to the full query result.
// "byRef" should use `viewQuery` with`0` (`QueryFlags.none`) for query flag without a read
// token, and bind to the full query result.
// NOTE: the `anonymous` match is to support IE11, as functions don't have a name there.
/(?:viewQuery|anonymous)[^(]*\(_c0,4\)/,
'(ctx.byRef = _t)',
// "byToken" should use `viewQuery` with `3` (`QueryFlags.static|QueryFlags.descendants`)
// for descendants flag and `ElementRef` as read token, and bind to the first result in the
// for query flag and `ElementRef` as read token, and bind to the first result in the
// query result.
// NOTE: the `anonymous` match is to support IE11, as functions don't have a name there.
/(?:viewQuery|anonymous)[^(]*\([^,]*String[^,]*,3,[^)]*ElementRef[^)]*\)/,