-
-
Notifications
You must be signed in to change notification settings - Fork 204
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
base: master
Are you sure you want to change the base?
Conversation
Added new rule and tests to avoid callback memory leaks
const addedListeners = []; | ||
const removedListeners = []; | ||
return { | ||
'ExportDefaultDeclaration:exit': function (node) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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') { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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."; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 | |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 | |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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: [{ |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
schema removed
});`, | ||
}, | ||
], | ||
invalid: [ |
There was a problem hiding this comment.
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:
- One callback which gets leaked
- Two callbacks which get leaked
- 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.
There was a problem hiding this comment.
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."; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
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
@bmish Thanks for reviewing, addressed the comments in the latest changes |
Ran yarn update to update the new rule names in readme and recommended rules
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'), |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this change.
There was a problem hiding this comment.
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({ |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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'
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
@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 |
There was a problem hiding this comment.
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.
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 | |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
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