/*
 *  Bug in test262 test case: ch15/15.12/15.12.2/S15.12.2_A1
 *
 *  Duktape does not parse __proto__ as a concrete property when ES2015
 *  __proto__ property is enabled, but rather sets the object prototype.
 */

/*===
basic case
[object Object]
object
true
false
true
[object Object]
===*/

/*===
reviver case
reviver: __proto__
reviver: 
[object Object]
object
true
false
true
[object Object]
===*/

/*===
complex reviver case
reviver: foo [object Object] bar
before: this.__proto__: [object Object]
after: this.__proto__: undefined
reviver: __proto__ undefined [object Object]
reviver:  [object Object] [object Object]
[object Object]
object
true
false
true
[object Object]
===*/

function test() {
    var x;

    function dump(x) {
        print(x);
        print(typeof x);
        print(x instanceof Object);
        print(x instanceof Array);
        print('__proto__' in x);
        print(Object.getOwnPropertyDescriptor(x, '__proto__'));
    }

    // Basic parse case

    print('basic case');
    dump(JSON.parse('{"__proto__":[]}'));

    // Similar issue triggered when reviving '__proto__'

    print('reviver case');
    dump(JSON.parse('{"__proto__":[]}', function (name, val) {
        // this=holder
        print('reviver:', name);
        return val;
    }));

    // Here the reviver is quite devious, and modifies the holder
    // so that an own property '__proto__' is removed before that
    // property is revived.  Because Duktape writes the replacement
    // value with duk_put_prop() now (in duk_bi_json.c), this runs
    // the risk of doing a normal property put on __proto__ with no
    // such own property in the object -- which then changes the
    // prototype of the result (which is incorrect).

    print('complex reviver case');
    dump(JSON.parse('{"foo":"bar", "__proto__":[1,2,3]}', function (name, val) {
        // this=holder
        print('reviver:', name, Object.getOwnPropertyDescriptor(this, name), val);
        if (name === 'foo') {
            // Delete '__proto__' while reviving 'foo'.
            print('before: this.__proto__:', Object.getOwnPropertyDescriptor(this, '__proto__'));
            delete this.__proto__;
            print('after: this.__proto__:', Object.getOwnPropertyDescriptor(this, '__proto__'));
        }
        if (name === '__proto__') {
            // Since __proto__ is deleted, duk_bi_json.c will read its value as
            // Object.prototype (= val) before calling us.  That's why the test
            // case prints out Object.getOwnPropertyDescriptor(this, name) returning
            // undefined, but 'val' being '[object Object'].  Force an array return
            // so that it gets (incorrectly) written to __proto__ using duk_put_prop().
            // The correct behavior here would be to set it as an own property.
            return [];
        }
        return val;
    }));
}

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