// FIXME: these should be shared utils for Array testing

function dumpValue(v) {
    var i, n, n_clipped;
    var res = '';

    if (v === undefined) {
        return 'undefined';
    } else if (v === null) {
        return 'null';
    }

    n = v.length;
    res = (typeof v) + ' ' + (typeof n) + ' ' + n;
    if (typeof n === 'undefined') {
        return res;
    }

    // clip dump length (n may be > 2**32)
    n = Math.floor(n);
    n_clipped = Math.min(n, 200);
    tmp = [];
    for (i = 0; i < n_clipped; i++) {
        if (!v.hasOwnProperty(i)) {
            tmp.push('nonexistent');
        } else {
            x = v[i];
            tmp.push(typeof x + ':' + x);
        }
    }
    res = res + (tmp.length > 0 ? ' ' + tmp.join(',') : '') + (n > n_clipped ? ',...' : '');
    return res;
}

function test(this_value, loop_count) {
    var t;
    var pre, post;

    if (loop_count === undefined) {
        loop_count = 1;
    }

    while (loop_count-- > 0) {
        pre = dumpValue(this_value);
        try {
            t = Array.prototype.pop.call(this_value);
        } catch (e) {
            t = e.name;
        }
        post = dumpValue(this_value);
        print(pre, '-->', typeof t, t, '-->', post);
    }
}

/*===
basic
object number 3 number:1,number:2,number:3 --> number 3 --> object number 2 number:1,number:2
object number 2 number:1,number:2 --> number 2 --> object number 1 number:1
object number 1 number:1 --> number 1 --> object number 0
object number 0 --> undefined undefined --> object number 0
object number 0 --> undefined undefined --> object number 0
object number 4 number:1,nonexistent,nonexistent,number:2 --> number 2 --> object number 3 number:1,nonexistent,nonexistent
object number 3 number:1,nonexistent,nonexistent --> undefined undefined --> object number 2 number:1,nonexistent
object number 2 number:1,nonexistent --> undefined undefined --> object number 1 number:1
object number 1 number:1 --> number 1 --> object number 0
object number 0 --> undefined undefined --> object number 0
object number 0 --> undefined undefined --> object number 0
object number 11 number:1,nonexistent,nonexistent,nonexistent,nonexistent,number:2,nonexistent,nonexistent,nonexistent,nonexistent,number:3 --> number 3 --> object number 10 number:1,nonexistent,nonexistent,nonexistent,nonexistent,number:2,nonexistent,nonexistent,nonexistent,nonexistent
object number 10 number:1,nonexistent,nonexistent,nonexistent,nonexistent,number:2,nonexistent,nonexistent,nonexistent,nonexistent --> undefined undefined --> object number 9 number:1,nonexistent,nonexistent,nonexistent,nonexistent,number:2,nonexistent,nonexistent,nonexistent
object number 9 number:1,nonexistent,nonexistent,nonexistent,nonexistent,number:2,nonexistent,nonexistent,nonexistent --> undefined undefined --> object number 8 number:1,nonexistent,nonexistent,nonexistent,nonexistent,number:2,nonexistent,nonexistent
object number 8 number:1,nonexistent,nonexistent,nonexistent,nonexistent,number:2,nonexistent,nonexistent --> undefined undefined --> object number 7 number:1,nonexistent,nonexistent,nonexistent,nonexistent,number:2,nonexistent
object number 7 number:1,nonexistent,nonexistent,nonexistent,nonexistent,number:2,nonexistent --> undefined undefined --> object number 6 number:1,nonexistent,nonexistent,nonexistent,nonexistent,number:2
object number 6 number:1,nonexistent,nonexistent,nonexistent,nonexistent,number:2 --> number 2 --> object number 5 number:1,nonexistent,nonexistent,nonexistent,nonexistent
object number 5 number:1,nonexistent,nonexistent,nonexistent,nonexistent --> undefined undefined --> object number 4 number:1,nonexistent,nonexistent,nonexistent
object number 4 number:1,nonexistent,nonexistent,nonexistent --> undefined undefined --> object number 3 number:1,nonexistent,nonexistent
object number 3 number:1,nonexistent,nonexistent --> undefined undefined --> object number 2 number:1,nonexistent
object number 2 number:1,nonexistent --> undefined undefined --> object number 1 number:1
object number 1 number:1 --> number 1 --> object number 0
object number 0 --> undefined undefined --> object number 0
object number 0 --> undefined undefined --> object number 0
object number 3 number:1,string:2-undel,number:3 --> number 3 --> object number 2 number:1,string:2-undel
object number 2 number:1,string:2-undel --> string 2-undel --> object number 2 number:1,string:2-undel
object number 2 number:1,string:2-undel --> string 2-undel --> object number 2 number:1,string:2-undel
object number 2 number:1,string:2-undel --> string 2-undel --> object number 2 number:1,string:2-undel
object string 10 string:first,string:second,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent --> undefined undefined --> object number 9 string:first,string:second,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent
object number 9 string:first,string:second,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent --> undefined undefined --> object number 8 string:first,string:second,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent
object number 8 string:first,string:second,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent --> undefined undefined --> object number 7 string:first,string:second,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent
object number 7 string:first,string:second,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent --> undefined undefined --> object number 6 string:first,string:second,nonexistent,nonexistent,nonexistent,nonexistent
object number 6 string:first,string:second,nonexistent,nonexistent,nonexistent,nonexistent --> undefined undefined --> object number 5 string:first,string:second,nonexistent,nonexistent,nonexistent
object number 5 string:first,string:second,nonexistent,nonexistent,nonexistent --> undefined undefined --> object number 4 string:first,string:second,nonexistent,nonexistent
object number 4 string:first,string:second,nonexistent,nonexistent --> undefined undefined --> object number 3 string:first,string:second,nonexistent
object number 3 string:first,string:second,nonexistent --> undefined undefined --> object number 2 string:first,string:second
object number 2 string:first,string:second --> string second --> object number 1 string:first
object number 1 string:first --> string first --> object number 0
object number 0 --> undefined undefined --> object number 0
object number 0 --> undefined undefined --> object number 0
===*/

print('basic');

function basicTest() {
    var t;

    // dense array

    t = [1,2,3];
    test(t, 5);

    t = [1,,,2];
    test(t, 6);

    // sparse array

    t = [1];
    t[100] = 'x';   // use this to get into 'sparse mode' but use smaller length to minimize output
    t.length = 1;
    t[10] = 3;
    t[5] = 2;
    test(t, 13);

    // non-deletable element

    t = [ 1, 2, 3 ];
    Object.defineProperties(t, {
        '1': { value: '2-undel', writable: false, enumerable: true, configurable: false }
    });
    test(t, 4);

    // trailing non-existent elements

    t = { '0': 'first', '1': 'second', length: '10' };
    test(t, 12);
}

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

/*===
coercion
undefined --> string TypeError --> undefined
null --> string TypeError --> null
boolean undefined undefined --> undefined undefined --> boolean undefined undefined
boolean undefined undefined --> undefined undefined --> boolean undefined undefined
number undefined undefined --> undefined undefined --> number undefined undefined
string number 3 string:f,string:o,string:o --> string o --> string number 3 string:f,string:o,string:o
string number 3 string:f,string:o,string:o --> string o --> string number 3 string:f,string:o,string:o
string number 3 string:f,string:o,string:o --> string o --> string number 3 string:f,string:o,string:o
string number 3 string:f,string:o,string:o --> string o --> string number 3 string:f,string:o,string:o
object number 2 number:1,number:2 --> number 2 --> object number 1 number:1
object undefined undefined --> undefined undefined --> object number 0
object number 2 string:foo,string:bar --> string bar --> object number 1 string:foo
object string 0.9 --> undefined undefined --> object number 0
object number 5 string:first,string:second,nonexistent,nonexistent,nonexistent --> undefined undefined --> object number 4 string:first,string:second,nonexistent,nonexistent
object number 4 string:first,string:second,nonexistent,nonexistent --> undefined undefined --> object number 3 string:first,string:second,nonexistent
object number 3 string:first,string:second,nonexistent --> undefined undefined --> object number 2 string:first,string:second
object number 2 string:first,string:second --> string second --> object number 1 string:first
object number 1 string:first --> string first --> object number 0
object number 0 --> undefined undefined --> object number 0
object number 0 --> undefined undefined --> object number 0
object number 4294967301 string:first,string:second,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,... --> undefined undefined --> object number 4 string:first,string:second,nonexistent,nonexistent
object number 4 string:first,string:second,nonexistent,nonexistent --> undefined undefined --> object number 3 string:first,string:second,nonexistent
object number 3 string:first,string:second,nonexistent --> undefined undefined --> object number 2 string:first,string:second
object number 2 string:first,string:second --> string second --> object number 1 string:first
object number 1 string:first --> string first --> object number 0
object number 0 --> undefined undefined --> object number 0
object number 0 --> undefined undefined --> object number 0
length getter 3
1 getter
length getter 3
length setter 2
length getter 2
1 getter
object number 3 string:foo,string:bar,nonexistent --> undefined undefined --> object number 2 string:foo,string:bar
length getter 2
1 getter
length getter 2
1 getter
length setter 1
length getter 1
object number 2 string:foo,string:bar --> string bar --> object number 1 string:foo
length getter 1
length getter 1
length setter 0
length getter 0
object number 1 string:foo --> string foo --> object number 0
length getter 0
length getter 0
length setter 0
length getter 0
object number 0 --> undefined undefined --> object number 0
length getter 0
length getter 0
length setter 0
length getter 0
object number 0 --> undefined undefined --> object number 0
===*/

print('coercion');

function coercionTest() {
    // this coercion

    test(undefined);
    test(null);
    test(true);
    test(false);
    test(123);
    test('foo', 4);  // character are non-configurable
    test([1,2]);
    test({ foo: 1, bar: 2 });
    test({ '0': 'foo', '1': 'bar', length: 2 });

    // ToUint32('0.9') -> coerces to zero, return undefined, length will be written to number 0

    t = { length: '0.9' };
    test(t);

    // length goes through ToUint32(), so these should have no difference
    // (V8 3.7.12.22 seems to iterate through 4G elements here)

    t = { '0': 'first', '1': 'second', length: 5 };
    test(t, 7);
    t = { '0': 'first', '1': 'second', length: 256*256*256*256 + 5 };
    test(t, 7);

    // complex test with lots of side effect checks
    // note that pre/post printing confuses the output a bit

    t = Object.create(Object.prototype, {
        '0': {
            value: 'foo',
            writable: true,
            enumerable: true,
            configurable: true
        },
        '1': {
            get: function() {
                print('1 getter');
                return 'bar';
            },
            set: function() {
                print('1 setter');
            },
            enumerable: true,
            configurable: true
        },
        'length': {
            get: function() {
                print('length getter', this.mylen);
                return this.mylen;
            },
            set: function(v) {
                print('length setter', v);
                this.mylen = v;
            },
            enumerable: true,
            configurable: true
        }
    });
    t.mylen = 3;

    test(t, 5);
}

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

/*===
delete/put prevented
object string 0 --> undefined undefined --> object string 0
object string 2 nonexistent,nonexistent --> undefined undefined --> object string 2 nonexistent,nonexistent
string number 3 string:f,string:o,string:o --> string o --> string number 3 string:f,string:o,string:o
object number 2 string:foo,string:bar --> string bar --> object number 1 string:foo
object number 1 string:foo --> string foo --> object number 0
object number 0 --> undefined undefined --> object number 0
object number 2 string:foo,string:bar --> string bar --> object number 2 string:foo,string:bar
object number 2 string:foo,string:bar --> string bar --> object number 2 string:foo,string:bar
object number 2 string:foo,string:bar --> string bar --> object number 2 string:foo,string:bar
===*/

/* Attempt to [[Delete]] entries or [[Put]] to "length" have the "Throw" flag
 * set in both non-strict and strict mode.  If attributes prevent deletion or
 * writing, a TypeError must result even in non-strict mode.
 */

print('delete/put prevented');

function deleteOrPutPreventedTest() {
    var t;

    // Length is zero and non-writable

    t = Object.create(Object.prototype, {
        'length': { value: '0', writable: false, enumerable: true, configurable: true }
    });
    test(t, 1);

    // Length is non-zero and non-writable

    t = Object.create(Object.prototype, {
        'length': { value: '2', writable: false, enumerable: true, configurable: true }
    });
    test(t, 1);

    // Characters are non-configurable, which causes [[Delete]] to fail

    test('foo', 1);

    // Same for this object; because the [[Put]] should fail with TypeError,
    // the (writable) length should not be updated

    t = Object.create(Object.prototype, {
        '0': { value: 'foo', writable: false, enumerable: true, configurable: false },
        '1': { value: 'bar', writable: false, enumerable: true, configurable: false },
        'length': { value: 2, writable: true, enumerable: true, configurable: true }
    });
    test(t, 3);

    // Length is not writable either but should have no effect, TypeError on attempt to [[Delete]] "1"

    t = Object.create(Object.prototype, {
        '0': { value: 'foo', writable: false, enumerable: true, configurable: false },
        '1': { value: 'bar', writable: false, enumerable: true, configurable: false },
        'length': { value: 2, writable: false, enumerable: true, configurable: false }
    });
    test(t, 3);
}

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

