// Test yield* with iter.throw and monkeypatching.

function* g1() { return (yield 1); }
function* g2() { try { yield 1; } catch (e) { yield e; } }
function* delegate(iter) { return yield* iter; }
var GeneratorObjectPrototype = Object.getPrototypeOf(g1).prototype;
var GeneratorObjectPrototype_throw = GeneratorObjectPrototype.throw;

// An uncaught delegated throw.
var inner = g1();
var outer = delegate(inner);
assertIteratorNext(outer, 1);
assertThrowsValue(function () { outer.throw(42) }, 42);
assertThrowsValue(function () { outer.throw(42) }, 42);

// A caught delegated throw.
inner = g2();
outer = delegate(inner);
assertIteratorNext(outer, 1);
assertIteratorResult(outer.throw(42), 42, false);
assertThrowsValue(function () { outer.throw(42) }, 42);
assertThrowsValue(function () { outer.throw(42) }, 42);

// What would be an uncaught delegated throw, but with a monkeypatched iterator.
inner = g1();
outer = delegate(inner);
assertIteratorNext(outer, 1);
inner.throw = function(e) { return { value: e*2 }; };
assertEq(84, outer.throw(42).value);
assertIteratorDone(outer, undefined);

// Monkeypatching inner.next.
inner = g1();
outer = delegate(inner);
inner.next = function() { return { value: 13, done: true } };
assertIteratorDone(outer, 13);

// What would be a caught delegated throw, but with a monkeypunched prototype.
inner = g2();
outer = delegate(inner);
assertIteratorNext(outer, 1);
delete GeneratorObjectPrototype.throw;
var outer_throw_42 = GeneratorObjectPrototype_throw.bind(outer, 42);
// yield* protocol violation: no 'throw' method
assertThrowsInstanceOf(outer_throw_42, TypeError);
// Now done, so just throws.
assertThrowsValue(outer_throw_42, 42);

// Monkeypunch a different throw handler.
inner = g2();
outer = delegate(inner);
outer_throw_42 = GeneratorObjectPrototype_throw.bind(outer, 42);
assertIteratorNext(outer, 1);
GeneratorObjectPrototype.throw = function(e) { return { value: e*2 }; }
assertEq(84, outer_throw_42().value);
assertEq(84, outer_throw_42().value);
// This continues indefinitely.
assertEq(84, outer_throw_42().value);
assertIteratorDone(outer, undefined);

// The same, but restoring the original pre-monkey throw.
inner = g2();
outer = delegate(inner);
outer_throw_42 = GeneratorObjectPrototype_throw.bind(outer, 42);
assertIteratorNext(outer, 1);
assertEq(84, outer_throw_42().value);
assertEq(84, outer_throw_42().value);
GeneratorObjectPrototype.throw = GeneratorObjectPrototype_throw;
assertIteratorResult(outer_throw_42(), 42, false);
assertIteratorDone(outer, undefined);

if (typeof reportCompare == "function")
    reportCompare(true, true);
