Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NEW RULE] avoid-leaking-callbacks-in-ember-objects #422

Open
wants to merge 10 commits into
base: master
Choose a base branch
from

Conversation

rajasegar
Copy link
Contributor

@rajasegar rajasegar commented May 25, 2019

Fixes #423
Added new rule and tests to avoid callback memory leaks
Callback leaks are memory leaks that occur due to state being caught in a callback function that is never released from memory.

Read more on callback leaks here

lib/rules/no-callback-leaks-in-ember-objects.js Outdated Show resolved Hide resolved
const addedListeners = [];
const removedListeners = [];
return {
'ExportDefaultDeclaration:exit': function (node) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is ExportDefaultDeclaration:exit the best time to run the leak checks? If there are multiple Ember objects in a file, including some after the default export, then running the leak checks at this point wouldn't be sufficient, right?

I believe Program:exit would be better since this runs when we're done visiting the entire file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to Program:exit


CallExpression(node) {
// console.log(node.callee.property);
if (utils.getPropertyValue(node, 'callee.property.name') === 'addEventListener') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be preferred to use native JavaScript (node.callee.property.name) to access this data instead of using the helper function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reasons, it is not working, through the utils method it was working fine thought, that's why i had that console.log previously, now removed it


create(context) {
const report = function (node) {
const message = "Event listeners and interval timers must be unregistered or ensure that the context they're registered with is destroyed, when no longer needed.";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The following error message would be clearer:

All `addEventListener` calls should have a corresponding `removeEventListener` to avoid leaking callbacks.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Message changed

README.md Outdated
@@ -129,6 +129,7 @@ The `--fix` option on the command line automatically fixes problems reported by
|:---|:--------|:------------|
| :white_check_mark: | [avoid-leaking-state-in-ember-objects](./docs/rules/avoid-leaking-state-in-ember-objects.md) | Avoids state leakage |
| | [computed-property-getters](./docs/rules/computed-property-getters.md) | Enforce the consistent use of getters in computed properties |
| :white_check_mark: | [no-callback-leaks-in-ember-objects](./docs/rules/no-callback-leaks-in-ember-objects.md) | Avoids callback memory leaks |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we name this rule avoid-leaking-callbacks-in-ember-objects for consistency with the existing avoid-leaking-state-in-ember-objects rule name?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, makes sense for me too.

README.md Outdated
@@ -129,6 +129,7 @@ The `--fix` option on the command line automatically fixes problems reported by
|:---|:--------|:------------|
| :white_check_mark: | [avoid-leaking-state-in-ember-objects](./docs/rules/avoid-leaking-state-in-ember-objects.md) | Avoids state leakage |
| | [computed-property-getters](./docs/rules/computed-property-getters.md) | Enforce the consistent use of getters in computed properties |
| :white_check_mark: | [no-callback-leaks-in-ember-objects](./docs/rules/no-callback-leaks-in-ember-objects.md) | Avoids callback memory leaks |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remember to run yarn update and add the rule to index.js too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

url: 'https://github.com/ember-best-practices/memory-leak-examples/blob/master/exercises/exercise-2.md'
},
fixable: null, // or "code" or "whitespace"
schema: [{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

schema isn't being used so you should remove this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

schema removed

});`,
},
],
invalid: [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't really tell the difference between most of these invalid test cases.

It's not worth having separate test cases just both Ember.Component.extend and Component.extend.

I would get rid of the duplicate test cases and keep only these test cases:

  1. One callback which gets leaked
  2. Two callbacks which get leaked
  3. One callback which gets leaked and one callback which doesn't get leaked

If there are more legitimate test cases, definitely include them, but it would be helpful to add a comment explaining what each test case is testing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are now having 3 invalid cases as per you mentioned, will add more if possible later

// ------------------------------------------------------------------------------


const message = "Event listeners and interval timers must be unregistered or ensure that the context they're registered with is destroyed, when no longer needed.";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This error message should be imported directly from the rule file instead of copied here. See the no-test-and-then rule for an example.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


if (idx < 0) {
// No removeEventListener
report(node);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

node is not the right element to report the violation on. You should report the violation on the specific addEventListener statement node.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now, i am adding the node of the addEventListener object itself, so that we will get the right location for the error

rajasegar added 3 commits June 3, 2019 11:43
1. Moving error messages to exports
2. Removing commented code
3. Changing the erro message to new string
4. Ran yarn update
1. Renaming the rule to `avoid-leaking-callbacks-in-ember-objects`
1. Updating rule names in Readme
2. Adding rule to index.js
3. Adding node information for proper error reporting by
using addEventListener nodes
4. Fixing test cases and removing duplicates
@rajasegar
Copy link
Contributor Author

@bmish Thanks for reviewing, addressed the comments in the latest changes

rajasegar added 2 commits June 3, 2019 14:27
Ran yarn update to update the new rule names in readme
and recommended rules
@bmish bmish changed the title [NEW RULE] no-callback-leaks [NEW RULE] avoid-leaking-callbacks-in-ember-objects Jun 3, 2019
lib/index.js Outdated
@@ -47,6 +47,7 @@ module.exports = {
'routes-segments-snake-case': require('./rules/routes-segments-snake-case'),
'use-brace-expansion': require('./rules/use-brace-expansion'),
'use-ember-get-and-set': require('./rules/use-ember-get-and-set'),
'avoid-leaking-callbacks-in-ember-objects': require('./rules/avoid-leaking-callbacks-in-ember-objects'),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be alphabetical in the list.

Copy link
Contributor Author

@rajasegar rajasegar Jun 4, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to the top in alphabetical order

README.md Outdated
@@ -104,6 +104,15 @@ The `--fix` option on the command line automatically fixes problems reported by
| :wrench: | [use-ember-get-and-set](./docs/rules/use-ember-get-and-set.md) | Enforces usage of Ember.get and Ember.set |


### Ember Object
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this section was supposed to move. yarn update should generate this file for you.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think after running yarn update only moved this block, i don't remember manually doing it. Anyway restored the position for now

@@ -22,5 +22,6 @@ Array [
"require-super-in-init",
"routes-segments-snake-case",
"use-brace-expansion",
"avoid-leaking-callbacks-in-ember-objects",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

valid: [
{
code: `
export default Ember.Component.extend({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test case seems to duplicate the other valid test case. Let's keep the Component.extend but not the older syntax Ember.Component.extend one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate case removed.

docs: {
description: 'Avoids callback memory leaks',
category: 'Ember Object',
recommended: true,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rules should never be added as recommended since that would be a breaking change. We can consider making it recommended in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the recommended flag


const ERROR_MESSAGE = 'All `addEventListener` calls should have a corresponding `removeEventListener` to avoid leaking callbacks.';
//------------------------------------------------------------------------------
// Ember object rule - Avoid callback memory leaks
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this rule be limited to Ember objects only?

If it should, it would need a if (!(ember.isEmberObject(node) || ember.isReopenObject(node))) check somewhere, similar to the existing avoid-leaking-state-in-ember-objects rule.

If it's not necessary to limit it to Ember objects, and we can just run it on a per-file basis, then we would want to rename the rule to avoid mentioning in-ember-objects, and remove all the extra Ember object code from the test cases.

Copy link
Contributor Author

@rajasegar rajasegar Jun 4, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am afraid we can't directly use the if (!(ember.isEmberObject(node) || ember.isReopenObject(node))) checks here since we are actually targeting the addEventListener and removeEventListener call expressions we can't check them using the above utils.

And if we are not limiting this to Ember objects, is it OK to rename the rule to `avoid-leaking-callbacks'

Copy link
Member

@bmish bmish Jun 4, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason why this rule is specific to Ember?

If this rule is not specific to Ember, then it could just be a generic JavaScript eslint rule, and likely would not belong in eslint-plugin-ember. (I found a similar rule here)

If this rule is specific to Ember, then we need to update the code to actually limit it to something like Ember objects/components. In this case, you could write a helper function like isInsideEmberObject(node) which loops/recurses upwards in the tree checking each ancestor.

Interested to get the opinion of @Turbo87 @rwjblue on this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bmish I tend to agree. I don't see a reason to scope this to just Ember objects.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rajasegar I wonder if you could try contributing this rule to eslint itself as a general no-leaking-callbacks rule? They are pretty strict about accepting new rules but perhaps you could at least ask what they think about it since it seems like a general JavaScript rule.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure @bmish I will try

1. Restoing Ember Object section in Readme
2. Rearranging the new rule in lib/index based on alphabetical order
3. Removing commented code in the rule
4. Removing the recommended flag from the rule
5. Removing the rule from snapshot recommended
6. Removing the duplicate valid test case with Ember.Component.extend
@rajasegar
Copy link
Contributor Author

@bmish Addressed the review comments, please check and let me know


const ERROR_MESSAGE = 'All `addEventListener` calls should have a corresponding `removeEventListener` to avoid leaking callbacks.';
//------------------------------------------------------------------------------
// Ember object rule - Avoid callback memory leaks
Copy link
Member

@bmish bmish Jun 4, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason why this rule is specific to Ember?

If this rule is not specific to Ember, then it could just be a generic JavaScript eslint rule, and likely would not belong in eslint-plugin-ember. (I found a similar rule here)

If this rule is specific to Ember, then we need to update the code to actually limit it to something like Ember objects/components. In this case, you could write a helper function like isInsideEmberObject(node) which loops/recurses upwards in the tree checking each ancestor.

Interested to get the opinion of @Turbo87 @rwjblue on this.

README.md Outdated
@@ -127,6 +127,7 @@ The `--fix` option on the command line automatically fixes problems reported by

| | Rule ID | Description |
|:---|:--------|:------------|
| :white_check_mark: | [avoid-leaking-callbacks-in-ember-objects](./docs/rules/avoid-leaking-callbacks-in-ember-objects.md) | Avoids callback memory leaks |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:white_check_mark: is only supposed to be present for recommended rules. If you run yarn update, this doc would be automatically generated without this since it's not longer a recommended rule.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

1. Removing white checkmark for no callback leaks rules in README
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

New Rule: no-callback-leaks-in-ember-objects
3 participants