/*---
{
    "skip": true
}
---*/

function test(this_value) {
    var t;

    try {
        var t = Array.prototype.toLocaleString.call(this_value);
        print(typeof t, t);
    } catch (e) {
        print(e.name);
    }
}

/*===
basic
string 
string 1,2,3
string ,,true,false,123,foo,foo,bar,quux,FOO,BAR,[object Object]
string 1,2,,,,,,,,,,,,,,,,,,
string 1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3
string ,,,
string foo,,bar,
string foo,,bar,
string ,foo,,,,,,,,,,,bar,,,,,,,
===*/

print('basic');

function basicTest() {
    var t;

    // very basic tests

    test([]);
    test([1,2,3]);
    test([ undefined, null, true, false, 123, 'foo', [ 'foo', 'bar', 'quux', [ 'FOO', 'BAR' ] ], { foo: 1, bar: 2 } ]);

    t = [1,2];
    t.length = 20;
    test(t);

    // sparse array

    t = [1];
    t[100] = 3;
    t[50] = 2;
    test(t);

    // must also work on non-arrays

    test({ length: 4 });
    test({ '0': 'foo', '2': 'bar', length: 4 });
    test({ '0': 'foo', '2': 'bar', '4': 'quux', length: 4 });
    test({ '1': 'foo', '12': 'bar', length: 20 });
}

try {
    basicTest();
} catch (e) {
    print(e);
}

/*===
coercion
string foo,bar,quux
string foo,bar,quux
string foo,bar,quux,baz
length getter
0 getter
0 toLocaleString
0 retvalue toString
1 getter
1 toLocaleString
2 getter
2 toLocaleString
2 retvalue toString
string elem 0,123,elem 2
0 toLocaleString
0 retvalue toString
0 retvalue valueOf
1 toLocaleString
1 retvalue toString
1 retvalue valueOf
2 toLocaleString
2 retvalue toString
2 retvalue valueOf
string 123,undefined,true
0 toLocaleString
0 retvalue toString
0 retvalue valueOf
TypeError
===*/

print('coercion');

function coercionTest() {

    // ToUint32() coercion of length

    test({
        length: 256*256*256*256 + 3,  // ToUint32() of this -> 3
        '0': 'foo', '1': 'bar', '2': 'quux', '3': 'baz'
    });
    test({
        length: 3.9,
        '0': 'foo', '1': 'bar', '2': 'quux', '3': 'baz'
    });
    test({
        length: -256*256*256*256 + 3.9,   // round towards zero -> this becomes a 4
        '0': 'foo', '1': 'bar', '2': 'quux', '3': 'baz'
    });

    // coercion side effects test
    // (V8 triggers '0 getter' twice for some reason)

    test({
        get length() { print('length getter'); return 3; },
        set length() { print('length setter'); },

        get 0() {
            print('0 getter'); return {
                toLocaleString: function() {
                    print('0 toLocaleString');
                    // the ToString() coercion of the return value is not explicit in the spec
                    return {
                        toString: function() { print('0 retvalue toString'); return 'elem 0'; }
                    };
                }
            }
        },
        set 0() { print('0 setter'); },

        get 1() {
            print('1 getter'); return {
                toLocaleString: function() {
                    print('1 toLocaleString');
                    return '123';
                }
            }
        },
        set 1() { print('1 setter'); },

        get 2() {
            print('2 getter'); return {
                toLocaleString: function() {
                    print('2 toLocaleString');
                    return {
                        toString: function() { print('2 retvalue toString'); return 'elem 2'; }
                    };
                }
            }
        },
        set 2() { print('2 setter'); },
    });

    // Not really Array specific, but if toString() returns a non-primitive value,
    // valueOf() gets called.  If valueOf() also returns a non-primitive value,
    // a TypeError should occur.  If either returns a primitive value, ToString()
    // is applied to it to end up with a string value.

    test({
        length: 3,

        '0': {
            toLocaleString: function() {
                print('0 toLocaleString');
                return {
                    toString: function() { print('0 retvalue toString'); return []; },  // non-primitive -> fall back to valueOf()
                    valueOf: function() { print('0 retvalue valueOf'); return 123; },   // primitive -> 123 coerced to '123'
                };
            }
        },

        '1': {
            toLocaleString: function() {
                print('1 toLocaleString');
                return {
                    toString: function() { print('1 retvalue toString'); return []; },  // non-primitive -> fall back to valueOf()
                    valueOf: function() { print('1 retvalue valueOf'); return undefined; },   // primitive -> 'undefined'
                };
            }
        },

        '2': {
            toLocaleString: function() {
                print('2 toLocaleString');
                return {
                    toString: function() { print('2 retvalue toString'); return []; },  // non-primitive -> fall back to valueOf()
                    valueOf: function() { print('2 retvalue valueOf'); return true; },   // primitive -> 'true'
                };
            }
        }
    });

    test({
        length: 3,

        '0': {
            toLocaleString: function() {
                print('0 toLocaleString');
                return {
                    toString: function() { print('0 retvalue toString'); return []; },  // non-primitive -> fall back to valueOf()
                    valueOf: function() { print('0 retvalue valueOf'); return {}; }     // non-primitive -> TypeError
                };
            }
        }
    });
}

try {
    coercionTest();
} catch(e) {
    print(e);
}

