fix(ivy): incorrectly generating shared pure function between null and object literal (#35481)

In #33705 we made it so that we generate pure functions for object/array literals in order to avoid having them be shared across elements/views. The problem this introduced is that further down the line the `ContantPool` uses the generated literal in order to figure out whether to share an existing factory or to create a new one. `ConstantPool` determines whether to share a factory by creating a key from the AST node and using it to look it up in the factory cache, however the key generation function didn't handle function invocations and replaced them with `null`. This means that the key for `{foo: pureFunction0(...)}` and `{foo: null}` are the same.

These changes rework the logic so that instead of generating a `null` key
for function invocations, we generate a variable called `<unknown>` which
shouldn't be able to collide with anything.

Fixes #35298.

PR Close #35481
This commit is contained in:
crisbeto
2020-02-16 12:49:23 +01:00
committed by Miško Hevery
parent 9228d7f15d
commit 22786c8e88
3 changed files with 226 additions and 3 deletions

View File

@ -11,6 +11,16 @@ import {OutputContext, error} from './util';
const CONSTANT_PREFIX = '_c';
/**
* `ConstantPool` tries to reuse literal factories when two or more literals are identical.
* We determine whether literals are identical by creating a key out of their AST using the
* `KeyVisitor`. This constant is used to replace dynamic expressions which can't be safely
* converted into a key. E.g. given an expression `{foo: bar()}`, since we don't know what
* the result of `bar` will be, we create a key that looks like `{foo: <unknown>}`. Note
* that we use a variable, rather than something like `null` in order to avoid collisions.
*/
const UNKNOWN_VALUE_KEY = o.variable('<unknown>');
export const enum DefinitionKind {Injector, Directive, Component, Pipe}
/**
@ -127,16 +137,16 @@ export class ConstantPool {
getLiteralFactory(literal: o.LiteralArrayExpr|o.LiteralMapExpr):
{literalFactory: o.Expression, literalFactoryArguments: o.Expression[]} {
// Create a pure function that builds an array of a mix of constant and variable expressions
// Create a pure function that builds an array of a mix of constant and variable expressions
if (literal instanceof o.LiteralArrayExpr) {
const argumentsForKey = literal.entries.map(e => e.isConstant() ? e : o.literal(null));
const argumentsForKey = literal.entries.map(e => e.isConstant() ? e : UNKNOWN_VALUE_KEY);
const key = this.keyOf(o.literalArr(argumentsForKey));
return this._getLiteralFactory(key, literal.entries, entries => o.literalArr(entries));
} else {
const expressionForKey = o.literalMap(
literal.entries.map(e => ({
key: e.key,
value: e.value.isConstant() ? e.value : o.literal(null),
value: e.value.isConstant() ? e.value : UNKNOWN_VALUE_KEY,
quoted: e.quoted
})));
const key = this.keyOf(expressionForKey);