Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Introducing Break the Web: Array extra methods case

Introducing Break the Web: Array extra methods case

Introducing Break the Web: Array extra methods case.

Yusuke SUZUKI

October 29, 2014
Tweet

More Decks by Yusuke SUZUKI

Other Decks in Technology

Transcript

  1. Introducing Break the Web: Array
    extra methods case
    Yusuke Suzuki
    @Constellation
    [email protected]
    2014/10/29

    View Slide

  2. Background: ES6 Array extensions
    • Upcoming ES6 adds many useful methods to
    Array.prototype
    – Array.prototype.values
    – Array.prototype.keys
    – Array.prototype.entries
    – Array.prototype.find
    – etc.

    View Slide

  3. Problem: Break the Web
    1. You already added these methods
    – by extending Array.prototype
    – Sometimes, your crafted methods don’t have the
    same semantics to the ES6 standards
    2. Too generic name to add it
    – “values”, “keys” etc.
    • In this talk, we’ll show the actual BtW cases

    View Slide

  4. Problem: Well-known with problem
    • Newly added properties will hide the variable
    in the with block
    • @@unscopables (in ES6) hides some
    properties in with block
    – (“values”, “entries” etc.)
    (function () {
    var values = [];
    // If object inherits Array.prototype, inner "values" variable becomes
    // Array.prototype.
    with (object) {
    values = ...;
    }
    })());

    View Slide

  5. Outline
    • Background
    • Problem
    • Case Study
    • Conclusion

    View Slide

  6. Case Study 1: MooTools
    • MooTools has its own Array#contains
    – FYI: MooTools also has its own String#contains
    • However, it checks the methods enumerability
    when extending the other object
    – So MooTools doesn’t consider about newly added
    Array methods

    View Slide

  7. Case Study 1: MooTools
    • Investigating MooTools and non-enumerable
    object.forEachMethod = function(fn){
    if (!methodsEnumerable) for (var i = 0, l = methods.length; i < l; i++){
    fn.call(prototype, prototype[methods[i]], methods[i]);
    }
    for (var key in prototype) fn.call(prototype, prototype[key], key)
    };
    Array.implement({
    // ...
    contains: function(item, from){
    return this.indexOf(item, from) != -1;
    },
    // ...
    });

    View Slide

  8. Case Study 1: MooTools
    • Investigating MooTools and non-enumerable
    Only listed standard
    methods are
    considered
    object.forEachMethod = function(fn){
    if (!methodsEnumerable) for (var i = 0, l = methods.length; i < l; i++){
    fn.call(prototype, prototype[methods[i]], methods[i]);
    }
    for (var key in prototype) fn.call(prototype, prototype[key], key)
    };
    Array.implement({
    // ...
    contains: function(item, from){
    return this.indexOf(item, from) != -1;
    },
    // ...
    });

    View Slide

  9. Case Study 1: MooTools
    • Investigating MooTools and non-enumerable
    Only listed standard
    methods are
    considered
    object.forEachMethod = function(fn){
    if (!methodsEnumerable) for (var i = 0, l = methods.length; i < l; i++){
    fn.call(prototype, prototype[methods[i]], methods[i]);
    }
    for (var key in prototype) fn.call(prototype, prototype[key], key)
    };
    Array.implement({
    // ...
    contains: function(item, from){
    return this.indexOf(item, from) != -1;
    },
    // ...
    });
    There’s no standard
    method definition,
    but it becomes
    non-enumerable

    View Slide

  10. Case Study 1: MooTools
    • Investigating MooTools and non-enumerable
    Only listed standard
    methods are
    considered
    object.forEachMethod = function(fn){
    if (!methodsEnumerable) for (var i = 0, l = methods.length; i < l; i++){
    fn.call(prototype, prototype[methods[i]], methods[i]);
    }
    for (var key in prototype) fn.call(prototype, prototype[key], key)
    };
    Array.implement({
    // ...
    contains: function(item, from){
    return this.indexOf(item, from) != -1;
    },
    // ...
    });
    There’s no standard
    method definition,
    but it becomes
    non-enumerable
    Array.prototype.contains = function () {
    ...;
    };
    If there’s non-enumerable “contains”,
    it becomes non-enumerable

    View Slide

  11. Case Study 1: MooTools
    • And Break the Web
    ...('Array', Array, [
    'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice',
    'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce',
    'reduceRight'
    ]) ...
    // Now, 'contains' is not enumerated.
    Array.forEachMethod(function(method, name){
    Elements.implement(name, method);
    });
    element.contains(...);

    View Slide

  12. Case Study 1: MooTools
    • And Break the Web
    There’s no “contains”
    ...('Array', Array, [
    'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice',
    'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce',
    'reduceRight'
    ]) ...
    // Now, 'contains' is not enumerated.
    Array.forEachMethod(function(method, name){
    Elements.implement(name, method);
    });
    element.contains(...);

    View Slide

  13. Case Study 1: MooTools
    • And Break the Web
    There’s no “contains”
    There’s no “contains”
    in Element
    ...('Array', Array, [
    'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice',
    'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce',
    'reduceRight'
    ]) ...
    // Now, 'contains' is not enumerated.
    Array.forEachMethod(function(method, name){
    Elements.implement(name, method);
    });
    element.contains(...);

    View Slide

  14. Case Study 1: MooTools
    • And Break the Web
    There’s no “contains”
    There’s no “contains”
    in Element
    ...('Array', Array, [
    'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice',
    'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce',
    'reduceRight'
    ]) ...
    // Now, 'contains' is not enumerated.
    Array.forEachMethod(function(method, name){
    Elements.implement(name, method);
    });
    element.contains(...);
    There’s no
    element.contains!

    View Slide

  15. Case Study 1: MooTools
    • This breaks the jsfiddle.net
    – https://bugzilla.mozilla.org/show_bug.cgi?id=107505
    9
    • MooTools update is introduced
    – https://github.com/mootools/mootools-
    core/commit/521471f5c0617edc12c680cdad8346c3e
    b95776a
    • es-discuss is going
    – https://esdiscuss.org/topic/having-a-non-
    enumerable-array-prototype-contains-may-not-be-
    web-compatible
    – https://esdiscuss.org/topic/array-prototype-contains-
    solutions

    View Slide

  16. Case Study 2: Breaking outlook.com
    • Array.prototype.values breaks the existing
    code in outlook.com
    – http://windowssystemspecialist.blogspot.jp/2014/10/fixing-blank-calendar-
    with-chrome-38.html

    View Slide

  17. Case Study 2: Breaking outlook.com
    • Exposing Array.prototype.values in V8 breaks
    the outlook.com in stable 38
    // Old code.
    "values" in n
    n

    View Slide

  18. Case Study 2: Breaking outlook.com
    • Exposing Array.prototype.values in V8 breaks
    the outlook.com in stable 38
    // Old code.
    "values" in n Array.prototype
    n
    [[Prototype]] …
    Object.prototype
    [[Prototype]]

    View Slide

  19. Case Study 2: Breaking outlook.com
    • Exposing Array.prototype.values in V8 breaks
    the outlook.com in stable 38
    // Old code.
    "values" in n Array.prototype
    n
    [[Prototype]] …
    Object.prototype
    [[Prototype]]
    “values”: …

    View Slide

  20. Case Study 2: Breaking outlook.com
    • Exposing Array.prototype.values in V8 breaks
    the outlook.com in stable 38
    // Old code.
    "values" in n Array.prototype
    n
    [[Prototype]] …
    Object.prototype
    [[Prototype]]
    “values”: …
    Trapped
    Becomes always true

    View Slide

  21. Case Study 2: Breaking outlook.com
    • Exposing Array.prototype.values in V8 breaks
    the outlook.com in stable 38
    // Old code.
    "values" in n Array.prototype
    n
    [[Prototype]] …
    Object.prototype
    [[Prototype]]
    “values”: …
    Trapped
    Becomes always true
    // Appropriate code.
    n.hasOwnProperty("values”)

    View Slide

  22. Case Study 2: Breaking outlook.com
    • Once Chrome stable 38 is shipped, but
    updated later

    View Slide

  23. Case Study 2: Breaking outlook.com
    • Once Chrome stable 38 is shipped, but
    updated later

    View Slide

  24. Outline
    • Background
    • Problem
    • Case Study
    • Conclusion

    View Slide

  25. Conclusion
    • Take the appropriate way
    – e.g. If you’d like to check own property existence,
    you should use obj.hasOwnProperty
    • Don’t trust the current shape of Objects overly
    – ECMAScript standards will extend the prototypes
    • And don’t use with
    – Use “use strict”

    View Slide