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

Null check for calculateToString method #16478

Closed
wants to merge 12 commits into from
Closed

Null check for calculateToString method #16478

wants to merge 12 commits into from

Conversation

lifeart
Copy link
Contributor

@lifeart lifeart commented Apr 10, 2018

Issue found in: https://travis-ci.org/emberjs/ember-inspector/jobs/364445291

emberjs/ember-inspector#781

Object.getPrototypeOf(null)
// Uncaught TypeError: Cannot convert undefined or null to object
//     at Function.getPrototypeOf (<anonymous>)
//     at <anonymous>:1:8
TypeError: Cannot convert undefined or null to object
                at Function.getPrototypeOf (<anonymous>)
                at calculateToString (http://localhost:7357/testing/assets/vendor.js:44972:29)
                at Mixin.classToString [as toString] (http://localhost:7357/testing/assets/vendor.js:44890:12)
                at mixins.forEach.mixin (http://localhost:7357/testing/ember_debug.js:3015:26)
                at Array.forEach (<anonymous>)
                at Class.mixinsForObject (http://localhost:7357/testing/ember_debug.js:3007:14)
                at Class.sendObject (http://localhost:7357/testing/ember_debug.js:2923:26)
                at Class.inspect (http://localhost:7357/testing/ember_debug.js:2347:35)
                at Object.<anonymous> (http://localhost:7357/testing/assets/tests.js:4637:22)
                at runTest (http://localhost:7357/testing/assets/test-support.js:4286:30)

How to reproduce?

function foo() {"use strict"; return this };
classToString(foo);

Copy link
Member

@rwjblue rwjblue left a comment

Choose a reason for hiding this comment

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

Seems good, can you create a regression test here that roughly simulates what the inspector is doing?

@rwjblue
Copy link
Member

rwjblue commented Apr 10, 2018

Thank you for the added test! I'm still having trouble figuring out how this can actually happen though. Generally speaking, the following is being done:

class Foo {}
Foo.prototype.toString = classToString;

How is it, in this circumstance, that you would have the toString function without a this?

@rwjblue
Copy link
Member

rwjblue commented Apr 10, 2018

After a bit of digging, it seems like the ember-inspector is essentially doing this (in the test that was failing from the CI log above):

let obj = Ember.Object.create();
let mixins = Ember.Mixin.mixins(obj);
let mixinDetails = [];
mixins.forEach(mixin => {
  let name = mixin[Ember.NAME_KEY] || mixin.ownerConstructor;
  if (!name && typeof mixin.toString === 'function') {
    name = mixin.toString();
  }
  if (!name) {
    name = 'Unknown mixin';
  }
  mixinDetails.push({
    name: name.toString(), 
    properties: propertiesForMixin(mixin),
  });
});

Basically, what is going on is that the inspector is trying to call .toString() on each mixin and the current guard only checks for Function.prototype to break out of the loop which (since this is an instance at that point it would need to check for Object.prototype to break the loop).

class extends AbstractTestCase {
['@test classToString: null as this inside class must not throw error'](assert) {
function F() {"use strict"; return this };
result = classToString(F.bind(null));
Copy link
Member

Choose a reason for hiding this comment

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

Can you update the regression test to do roughly:

let mixin = Mixin.create();
mixin.toString(); // without the fix, this throws

@@ -184,6 +184,9 @@ function calculateToString(target) {
}
let superclass = target;
do {
if (superclass === null) {
break;
}
superclass = Object.getPrototypeOf(superclass);
if (superclass === Function.prototype) {
Copy link
Member

Choose a reason for hiding this comment

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

Can you update this to be:

if (superclass === Function.prototype || superclass === Object.prototype) {
  break;
}

@@ -184,6 +184,9 @@ function calculateToString(target) {
}
let superclass = target;
do {
if (superclass === null) {
Copy link
Member

Choose a reason for hiding this comment

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

If we add Object.prototype check (mentioned in another comment) we can remove the explicit null check..

@rwjblue
Copy link
Member

rwjblue commented Apr 10, 2018

Sorry for the run around here @lifeart, thank you for working on it!

@lifeart
Copy link
Contributor Author

lifeart commented Apr 10, 2018

@rwjblue How is it, in this circumstance, that you would have the toString function without a this?
https://github.com/emberjs/ember-inspector/pull/781/files#diff-7c2c7ea1dfc0ef384d1ec6e685711d15R480

@rwjblue
Copy link
Member

rwjblue commented Apr 11, 2018

OK, looks great, would you mind squashing down to a single commit and prefixing the commit message with [BUGFIX release]?

cibernox and others added 12 commits April 11, 2018 23:51
In general,Ember's testing system leverages `ajaxSend` and
`ajaxComplete` events to know when `jQuery.ajax` requests are pending
(and it should therefore "pause settledness" of the testing helpers like
`click` / `andThen` / etc).

In the Ember 3.1 cycle Ember changed from using `jQuery.on` to
`document.addEventListener` to subscribe to these events. Unfortunately,
`jQuery.ajax` **does not** trigger listeners added in this way (we would
have had to do `document.onajaxSend = function() { /* stuff here */ }`).
The result of this change is that "normal" acceptance testing helpers
will _no longer_ be able to detect and wait for pending `jQuery.ajax`.

So, for example, you might expect that given the following

```js
await click('#some-selector');

assert.equal(
   $('#something-that-should-exist-after-an-ajax-call').text(),
   'hi'
);
```

the click helper would wait until `jQuery.ajax` calls were settled
before the assertion, but prior to the changes in this commit
the promise returned by `click` would **not** wait until the
`jQuery.ajax` request was completed and therefore the assertion would
fail.

---

This commit reverts the changes to use `document.addEventListener` back
to using `jQuery.on` directly when `jQuery` is available.

Many bothans died to bring us this information. 😭 😢 :crying_cat:

Paired with @rwjblue to resolve this issue.
* Remove unused legacy global flags.
* Move imports "to the top"
* Bring back `Ember.NAME_KEY`
* Ensure all `computed.*` exports are setup in reexports test
* Have `ember-metal/libaries` register the `Ember` library
The refactor to move `index.js` -> `lib/index.js` left this in a broken
state (the paths are incorrect and therefore the funnel threw an error
at build time).
The shrug emoticon went from having a broken arm (improperly escaped) to no arm during the [prettifying](https://github.com/emberjs/ember.js/pull/16440/files#diff-437a149f1b0f7d248f37db51346c8ff2L156) process.
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.

9 participants