Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unexplained behavior in Safari with negative array indices

Edit: Simpler repro case; the following code:

setInterval(function(){
  var a=[10,20,30,40], i=-1;
  a[-1] = 42;
  while (i<10000) a[i++];
  console.log(a[-1],a[4294967295]);
},100);

…produces the output:

     42 undefined
     undefined 42
     42 undefined
37x  undefined 42
     42 undefined
     undefined 42
     42 undefined
41x  undefined 42
     42 undefined
     undefined 42
     42 undefined

Try it yourself: http://jsfiddle.net/Fjwsg/


(Original question follows)

Given the following code (or code like it (fiddle)):

<!DOCTYPE HTML>
<html><head><title>-1 Array Index</title></head><body>
  <label>p: <input id="p" size="3"></label>
  <script type="text/javascript">
  var p = document.getElementById('p');
  p.onkeyup = function(){
    var a = "10 20 30 40".split(/\s+/);
    foo(a, p.value*1);
  }

  function foo(a,p){
    var count=a.length, i=0, x;
    if (p) a[i=-1]=p;
    while (i<10000) x = a[i++ % count];
    console.dir(a);
  }
  </script>
</body></html>

I see the following in the Developer Console when I focus the "p" input and type 1 backspace 2:
Array has index of -1 on first pass, index of 4294967295 on third pass

Once I see the index of 4294967295 (232 - 1) show up, things sometimes begin to "go bad": sometimes the Developer Tools automatically close, sometimes all Safari tabs freeze and require a relaunch to recover.

Oddly, I can't repro this if I remove the (largely useless in this case) while loop. And I can't repro this on Chrome or Firefox.

Can anyone shed any light on what could possibly be at the root of this problem?

This is occurring with Safari 5.1.7 on OS X 10.7.4

like image 516
Phrogz Avatar asked May 17 '12 02:05

Phrogz


2 Answers

Using negative indices may result in undefined behaviour.

According to the ECMAScript documentation, a particular value p can only be an array index if and only if:

(p >>> 0 === p) && (p >>> 0 !== Math.pow(2, 32) - 1)

In your case:

# -1 >>> 0
4294967295
# Math.pow(2, 32) - 1
4294967295

edit

If p fails above test, it should be treated as a regular object property like a.foo or a['foo']. That said, as it turns out, setting the negative index with a string cast works around the issue.

a['' + (i =-1)] = p

Conclusion: browser bug

like image 173
Ja͢ck Avatar answered Sep 27 '22 18:09

Ja͢ck


This sounds like a bug in JavaScriptCore to me. Perhaps foo is being JITted when it's called the second time and the JITted code introduces the bug.

I'd recommend filing a bug against JavaScriptCore and attaching your test case.

like image 37
Adam Roben Avatar answered Sep 27 '22 18:09

Adam Roben