Skip to content

Commit

Permalink
[BUGFIX beta] Prevent infinite cycles from lazy computed computation
Browse files Browse the repository at this point in the history
Lazy computed chaining can sometimes lead to infinite cycles that, if a
computed property setup has a natural cycle that leads back to the
original CP which is finishing its chains. Ensuring the cache and
revisions are set _before_ we finish chaining prevents this. Also moves
the `set` into the set block, so we don't reset the value if we're
returning from cache.

(cherry picked from commit ca22291)
  • Loading branch information
Chris Garrett authored and chancancode committed Sep 18, 2019
1 parent a920922 commit 8c8e916
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 4 deletions.
8 changes: 4 additions & 4 deletions packages/@ember/-internals/metal/lib/computed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -555,8 +555,6 @@ export class ComputedProperty extends ComputedDescriptor {
});
}

finishLazyChains(obj, keyName, ret);

if (this._dependentKeys !== undefined) {
let tag = combine(getChainTagsForKeys(obj, this._dependentKeys));

Expand All @@ -568,6 +566,10 @@ export class ComputedProperty extends ComputedDescriptor {
}

setLastRevisionFor(obj, keyName, tagValue(propertyTag));

cache.set(keyName, ret);

finishLazyChains(obj, keyName, ret);
}

consume(propertyTag!);
Expand All @@ -578,8 +580,6 @@ export class ComputedProperty extends ComputedDescriptor {
consume(tagForProperty(ret, '[]'));
}

cache.set(keyName, ret);

return ret;
} else {
if (cache.has(keyName)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -497,5 +497,26 @@ moduleFor(
set(options.objectAt(0), 'value', 'bar');
assert.deepEqual(n.normalized, ['bar']);
}

['@test lazy computation cannot cause infinite cycles'](assert) {
// This is based off a real world bug found in ember-cp-validations:
// https://github.com/offirgolan/ember-cp-validations/issues/659
let CycleObject = EmberObject.extend({
foo: computed(function() {
return EmberObject.extend({
parent: this,
alias: alias('parent.foo'),
}).create();
}),
bar: computed('foo.alias', () => {}),
});

let obj = CycleObject.create();

obj.bar;
obj.foo;

assert.ok(true);
}
}
);

0 comments on commit 8c8e916

Please sign in to comment.