/*===
basic
0 foo true
1 bar true
2 prop1 true
3 prop2 true
4 prop3 false
5 prop4 false
6 prop5 true
7 prop6 true
8 prop7 false
9 prop8 false
10 acc1 true
11 acc2 false
12 acc3 true
13 acc4 false
14 proto_foo false
15 proto_bar false
16 nonexistent false
===*/

print('basic');

function basicTest() {
    var proto = { proto_foo: 1, proto_bar: 2 };
    var obj;
    var names = [ 'foo', 'bar', 'prop1', 'prop2', 'prop3', 'prop4', 'prop5', 'prop6',
                  'prop7', 'prop8', 'acc1', 'acc2', 'acc3', 'acc4',
                  'proto_foo', 'proto_bar',  // prototype properties NOT given out
                  'nonexistent' ];

    obj = Object.create(proto);
    obj.foo = 3;
    obj.bar = 4;

    Object.defineProperties(obj, {
        prop1: { value: 1, writable: true, enumerable: true, configurable: true },
        prop2: { value: 1, writable: false, enumerable: true, configurable: true },
        prop3: { value: 1, writable: true, enumerable: false, configurable: true },
        prop4: { value: 1, writable: false, enumerable: false, configurable: true },
        prop5: { value: 1, writable: true, enumerable: true, configurable: false },
        prop6: { value: 1, writable: false, enumerable: true, configurable: false },
        prop7: { value: 1, writable: true, enumerable: false, configurable: false },
        prop8: { value: 1, writable: false, enumerable: false, configurable: false },
        acc1: { get: function(){}, set: function(){}, enumerable: true, configurable: true },
        acc2: { get: function(){}, set: function(){}, enumerable: false, configurable: true },
        acc3: { get: function(){}, set: function(){}, enumerable: true, configurable: false },
        acc4: { get: function(){}, set: function(){}, enumerable: false, configurable: false }
    });

    for (i = 0; i < names.length; i++) {
        print(i, names[i], obj.propertyIsEnumerable(names[i]));
    }
}

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

/*===
coercion
toString() name
TypeError
toString() name
TypeError
toString() name
boolean boolean false
toString() name
boolean boolean false
toString() name
number boolean false
toString() name
object boolean false
toString() name
object boolean false
toString() name
object boolean false
===*/

print('coercion');

function coercionTest() {
    var objThis, objProp;

    function test(this_value, prop_name) {
        try {
            t = Object.prototype.propertyIsEnumerable.call(this_value, prop_name);
            print(typeof this_value, typeof t, t);
        } catch (e) {
            print(e.name);
        }
    }

    objThis = {
        toString: function () { print('toString() this'); return 'tostring-this'; },
        valueOf: function () { print('valueOf() this'); return 'valueof-this'; }
    };

    objProp = {
        toString: function () { print('toString() name'); return 'tostring-name'; },
        valueOf: function () { print('valueOf() name'); return 'valueof-name'; }
    };

    // ToObject(this) will throw a TypeError for undefined and null, but this
    // only happens after ToString() is called for property name

    test(undefined, objProp);
    test(null, objProp);
    test(true, objProp);
    test(false, objProp);
    test(123, objProp);
    test([1,2], objProp);
    test({ foo: 1, bar: 2 }, objProp);

    // First argument is already an object so it won't get coerced; second
    // argument gets coerced to a string

    test(objThis, objProp);
}

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

