jashkenas/underscore

isObject returning unexpected values in Chrome v19 MAC

subimage opened this issue · 27 comments

So I started getting bug reports from my customers this morning that certain things were breaking in my app.

After a little digging, it seems to be caused by the isObject feature of underscore. For some reason the default implementation is returning "true" for simple boolean variables - which it shouldn't be. For instance doing something like the following is returning true...

  var x = true;
  _.isObject(x); // true

Changing this...

  _.isObject = function(obj) {
    return obj === Object(obj);
  };

...to this

  _.isObject = function(obj) {
    return typeof obj == 'object';
  };

...fixes the problem. Unsure why you're doing the first check, as it seems overkill - when using 'typeof == "object"' has been the way to detect objects in JS since day 1.

I'm also running Chrome 19 on OSX, but the code below returns false.

> var x = true;
> _.isObject(x);
  false

Would you mind reproducing your result and posting it as a fiddle?

How do i "post it as a fiddle?" I can reproduce this reliably in the console when my app is loaded.

I just mean post it somewhere on the web so that I can take a look at it. jsfiddle.net, jsbin.com or gist.github.com are great places to post code to share through a url.

Can't reproduce it on Fiddle with about 5 minutes playing with it unfortunately, but here's output of my console after changing the _.isObject function to the following...

  // Is a given variable an object?
  _.isObject = function(obj) {
    var isObj = (obj === Object(obj));
    console.log("\n")
    console.log(obj);
    console.log(typeof obj);
    console.log(isObj);
    return isObj;
  };

http://dl.dropbox.com/u/156966/underscore_mac_console.png

...ideas on the above trace?

Those results look correct to me. I'm going to close this as not reproducible for now, but please post a reproducible test case if you find it again.

A string returning true after returning false looks "correct"? o_O (See the end of the console output...)

Will try to reproduce, but for now I just hacked my underscore copy with my suggested fix and it solves my issue.

I smell a troll...

@michaelficarra yeah i've got nothing better to do than file useless bug reports. STFU idiot.

@subimage can you reproduce it on the console with "true === Object(true)"? Only when your app is loaded, or always? If only with your app, perhaps it or a library it uses messes with Object?

Hey Jakob, I had a similar thought and I'm trying to isolate it.

Unfortunately it only happens with the newest version of chrome. No worries on closing out the bug - at least there will be reference if someone else runs into it.

Seth
Sent with Sparrow (http://www.sparrowmailapp.com/?sig)

On Saturday, May 19, 2012 at 2:04 PM, Jakob Borg wrote:

@subimage can you reproduce it on the console with "true === Object(true)"? Only when your app is loaded, or always? If only with your app, perhaps it or a library it uses messes with Object?


Reply to this email directly or view it on GitHub:
#605 (comment)

I have run into it here. I can't quite figure out why, as it's only occasional. "_.isObject('marker')" returns true under certain conditions. Bug filters down into Backbone.js Model#set(string,value).

Have a coworker who can duplicate it right now in his console:
_.isObject("marker")
true
Same code for me, same Chrome version, returns false.

(We're both on Chrome 19.0.1084.46, Underscore version is 1.3.2)

Unless edge chrome is changing the semantics of immutable JS strings, or the semantics of ==, that's pretty damn crazy. The implementation of isObject is just:

obj === Object(obj)

Do y'all have any changes made to either Object.prototype, or String.prototype?

No changes to either. Third-party scripts running on the page are Backbone, jQuery, Google Maps API. JS is compiled with Coffeescript.

As I said, the problem is only occasional; haven't been able to reproduce it reliably yet. Not sure why. Sessions in the webapp tend to last 20 minutes or more, so possible that something breaks along the way. Only seen it so far in Chrome 19.

@subimage Are you seeing it more predictably?

(When it comes up again, I'll be sure to play around plenty in console, since the problem does persist in the console session. But as soon as you reload the page it's fixed.)

Any reason you're just not using "typeof" here in particular?

Seth
Sent with Sparrow (http://www.sparrowmailapp.com/?sig)

On Thursday, May 24, 2012 at 2:55 PM, Jeremy Ashkenas wrote:

Unless edge chrome is changing the semantics of immutable JS strings, or the semantics of ==, that's pretty damn crazy. The implementation of isObject is just:

obj === Object(obj)


Reply to this email directly or view it on GitHub:
#605 (comment)

@dtetto and yes - I can get it to happen reliably one one of my pages here - but only after my app initialization. I have so much JS on that particular page (prototype, underscore 1.3.2, a modified version of backbone to work with prototype, etc) that I don't want to post it all for a "test case".

Could not get it to happen with just prototype.js & underscore on jsfiddle.

Another oddity - it does not happen in Safari, or Firefox. Only Chrome...and only the newest version. I know they swapped out the v8 javascript engine in the latest version, so I'm sure that has something to do with it.

Just chiming in that we over at SignNow ran into this as well. I even gathered the team to confirm I wasn't on crack. For us, it was only happening in Chrome 20.0.1132.47 and very very sporadically. When it did happen, using the same implementation just by itself; that is, 'asdf' === Object('asdf') returns false, but _.isObject('asdf') returned true. Also confirmed that the implementation of _.isObject wasn't overriden as well.

All other primitives were returning true as well (booleans and numbers)

We thought what everyone else suggested, extending native prototypes, over-shadowing Object constructor, etc and we're still looking into these as they seem the most likely but the strange part is how random it is. When it happens, it is every time you use that helper, but hit refresh or try it again from a fresh load and nada.

No extensions installed. Flash was used on the page as well.

We have no solution yet, but hope this helps other people who stumble on this confirm they aren't crazy. For anyone who changes isObject to native typeof, keep in mind typeof null === 'object'! That'll bite you.

Just for posterity, I believe that this bug, jashkenas/backbone#1543, and nodejs/node-v0.x-archive#3867 are likely one and the same. If not, it seems that they are at least related and perhaps can point you in the right direction, though I'm sure you've come up with a workaround of some sort since posting here.

Crazy V8 bug! Thanks for the update. We have patched our underscore for it over here, but hopefully we'll be able to roll back that patch after a Chrome update with the fix goes out.

It's not a real bug in production Chrome, right? Just Canary?

Running this gist in chrome stable I get the following, which I believe is a repro of the bug.

isObjectA(str): true
isObjectB(str): false
inline check:   false

This was in production Chrome for us. It was brought to our attention when a user couldn't load our web app because it was returning an incorrect value. We could only internally reproduce it in 20.0.1132.47. Haven't tested since my initial report. We moved away from using that function.

Eek.

The source ticket: http://code.google.com/p/v8/issues/detail?id=2291

Looks to be from an optimization that triggers the bug only sporadically in special situations.

Ticket filed and fixed both today. It'll ship to stable/beta/dev/canary Chrome asap, from what I can tell. cc @erikcorry

@erikcorry @paulirish Did this make it into stable? Still listed as assigned.

We've been actively avoiding Object() and isObject() in our app, hoping to close this internal ticket.