L'état de la baie sera mis en cache dans iOS 12 Safari. Est-ce un bug ou une fonctionnalité?

432

Mise à jour au 31/10/2018

Ce bug a été corrigé dans iOS 12.1, passez une bonne journée ~

J'ai trouvé un problème avec l'état de la valeur d'Array dans le nouveau iOS 12 Safari, par exemple, du code comme celui-ci:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
    <title>iOS 12 Safari bugs</title>
    <script type="text/javascript">
    window.addEventListener("load", function ()
    {
        let arr = [1, 2, 3, 4, 5];
        alert(arr.join());

        document.querySelector("button").addEventListener("click", function ()
        {
            arr.reverse();
        });
    });
    </script>
</head>
<body>
    <button>Array.reverse()</button>
    <p style="color:red;">test: click button and refresh page, code:</p>
</body>
</html>

Après avoir actualisé la page, la valeur du tableau est toujours inversée. Est-ce un bug ou une fonctionnalité du nouveau Safari?


Voici une page de démonstration. Essayez de l'utiliser avec iOS 12 Safari: https://abelyao.github.io/others/ios12-safari-bug.html

abelyao
la source
41
Bug confirmé également dans macOS 10.14 Mojave - i.imgur.com/ZJtJJC1.png
a_rahmanshah
43
macOS 10.13.6 (High Sierra) avec Safari version 12.0 (13606.2.11) a le même problème. Le tableau est toujours inversé après l'actualisation de la page.
Kevin Gimbel
2
Le bogue a été corrigé dans Safari 12.0.1 (macOS), ainsi que dans iOS 12.1.
MrMister

Réponses:

272

C'est définitivement un BUG! Et c'est un bug très grave.

Le bogue est dû à l'optimisation des initialiseurs de tableau dans lesquels toutes les valeurs sont des littéraux primitifs. Par exemple, étant donné la fonction:

function buildArray() {
    return [1, null, 'x'];
}

Toutes les références de tableau renvoyées depuis les appels à buildArray()seront liées à la même mémoire, et certaines méthodes telles que toString()verront leurs résultats mis en cache. Normalement, pour préserver la cohérence, toute opération modifiable sur de tels tableaux optimisés copiera les données dans un espace mémoire séparé et y sera liée; ce modèle est appelé copie sur écriture , ou CoW pour faire court.

La reverse()méthode mute le tableau, elle doit donc déclencher une copie sur écriture. Mais ce n'est pas le cas, car l'implémenteur d'origine (Keith Miller d'Apple) a raté le reverse()cas, même s'il avait écrit de nombreux tests.

Ce bogue a été signalé à Apple le 21 août. Le correctif a atterri dans le référentiel WebKit le 27 août et a été livré dans Safari 12.0.1 et iOS 12.1 le 30 octobre 2018.

hax
la source
11
Remarque: Safari 12.0 sur Mac OS X a également le même problème.
hax
17
Oui, il a déjà été corrigé dans les sources et déjà livré dans Safari Technology Preview. Essayez cdn.miss.cat/demo/ios12-safari-bug.html dans Safari Technology Preview 65. Vous constaterez qu'il n'a pas le bogue.
sideshowbarker
6
Je ne crois pas que la cause sous-jacente du bogue soit le résultat d'un mélange d'index; au lieu de cela, il semble être causé par la négligence de vérifier si un objet est immuable avant de le modifier. Le problème de tranche peut avoir une explication similaire, mais ce n'est pas la même chose et ne sera pas résolu par le patch pour reverse, pour autant que je sache. Vous devriez envisager d'ouvrir un rapport de bogue WebKit pour le problème de tranche.
Zenexer
5
@Zenexer Vous avez raison. J'ai écrit cette réponse avant de trouver le bugs.webkit.org/show_bug.cgi?id=188794 et voir le code source. Je vais modifier ma réponse.
hax
75

J'ai écrit une lib pour corriger le bug. https://www.npmjs.com/package/array-reverse-polyfill

Voici le code :

(function() {
  function buggy() {
    var a = [1, 2];
    return String(a) === String(a.reverse());
  }
  if(!buggy()) return;
  var r = Array.prototype.reverse;
  Array.prototype.reverse = function reverse() {
    if (Array.isArray(this)) this.length = this.length;
    return r.call(this);
  }
})();

Edire Fan
la source
4
Mettre à jour à tout moment. Bienvenue à contribuer.
Edire Fan
14
@zephi, je suppose que l'écriture sur length ( this.length = this.length) déclenchera la copie lors de l'écriture, donc changera l'adresse mémoire du tableau, et corrigera ainsi le comportement de reverse.
Cœur
14

Il s'agit d'un bug dans le webkit . Bien que cela ait été résolu à la fin, mais pas encore livré avec la version iOS de GM. Une des solutions à ce problème:

(function() {
  function getReverseStr() {
    return [1, 2].reverse();
  }

  var n1 = getReverseStr()[0];
  var n2 = getReverseStr()[0];
  // check if there is an issue
  if(n1 != n2) {
    var origReverseFunction = Array.prototype.reverse;
    Array.prototype.reverse = function() {
      var newArr = this.slice();
      // use original reverse function so that edge cases are taken care of
      origReverseFunction.apply(newArr, arguments);
      var that = this;
      // copy reversed array
      newArr.forEach(function(value, index) {
        that[index] = value;
      });
      return this;
    }
  }
})();
jsist
la source
6

Il ne semble pas être mis en cache si le nombre d'éléments change.
J'ai pu éviter ça comme ça.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
    <title>iOS 12 Safari bugs</title>
    <script type="text/javascript">
    window.addEventListener("load", function ()
    {
        let arr = [1, 2, 3, 4, 5];
        arr.push('');
        arr.pop();
        alert(arr.join());

        document.querySelector("button").addEventListener("click", function ()
        {
            arr.reverse();
        });
    });
    </script>
</head>
<body>
    <button>Array.reverse()</button>
    <p style="color:red;">test: click button and refresh page, code:</p>
</body>
</html>

Atsushi Sasaki
la source