text: // Copyright 2013 the V8 project authors. All rights reserved.
  // Redistribution and use in source and binary forms, with or without
  // modification, are permitted provided that the following conditions are
  // met:
  //
  //     * Redistributions of source code must retain the above copyright
  //       notice, this list of conditions and the following disclaimer.
  //     * Redistributions in binary form must reproduce the above
  //       copyright notice, this list of conditions and the following
  //       disclaimer in the documentation and/or other materials provided
  //       with the distribution.
  //     * Neither the name of Google Inc. nor the names of its
  //       contributors may be used to endorse or promote products derived
  //       from this software without specific prior written permission.
  //
  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  
  // Flags: --allow-natives-syntax --harmony-tostring
  
  // Make sure we don't rely on functions patchable by monkeys.
  var call = Function.prototype.call.call.bind(Function.prototype.call)
  var observe = Object.observe;
  var getOwnPropertyNames = Object.getOwnPropertyNames;
  var defineProperty = Object.defineProperty;
  var numberPrototype = Number.prototype;
  var symbolIterator = Symbol.iterator;
  
  
  (function() {
    // Test before clearing global (fails otherwise)
    assertEquals("[object Promise]",
        Object.prototype.toString.call(new Promise(function() {})));
  })();
  
  
  function clear(o) {
    if (o === null || (typeof o !== 'object' && typeof o !== 'function')) return
    clear(o.__proto__)
    var properties = getOwnPropertyNames(o)
    for (var i in properties) {
      clearProp(o, properties[i])
    }
  }
  
  function clearProp(o, name) {
    var poisoned = {caller: 0, callee: 0, arguments: 0}
    try {
      var x = o[name]
      o[name] = undefined
      clear(x)
    } catch(e) {} // assertTrue(name in poisoned) }
  }
  
  // Find intrinsics and null them out.
  var globals = Object.getOwnPropertyNames(this)
  var whitelist = {Promise: true, TypeError: true}
  for (var i in globals) {
    var name = globals[i]
    if (name in whitelist || name[0] === name[0].toLowerCase()) delete globals[i]
  }
  for (var i in globals) {
    if (globals[i]) clearProp(this, globals[i])
  }
  
  
  var asyncAssertsExpected = 0;
  
  function assertAsyncRan() { ++asyncAssertsExpected }
  
  function assertAsync(b, s) {
    if (b) {
      print(s, "succeeded")
    } else {
      %AbortJS(s + " FAILED!")  // Simply throwing here will have no effect.
    }
    --asyncAssertsExpected
  }
  
  function assertAsyncDone(iteration) {
    var iteration = iteration || 0
    var dummy = {}
    observe(dummy,
      function() {
        if (asyncAssertsExpected === 0)
          assertAsync(true, "all")
        else if (iteration > 10)  // Shouldn't take more.
          assertAsync(false, "all")
        else
          assertAsyncDone(iteration + 1)
      }
    )
    dummy.dummy = dummy
  }
  
  
  (function() {
    assertThrows(function() { Promise(function() {}) }, TypeError)
  })();
  
  (function() {
    assertTrue(new Promise(function() {}) instanceof Promise)
  })();
  
  (function() {
    assertThrows(function() { new Promise(5) }, TypeError)
  })();
  
  (function() {
    assertDoesNotThrow(function() { new Promise(function() { throw 5 }) })
  })();
  
  (function() {
    (new Promise(function() { throw 5 })).chain(
      assertUnreachable,
      function(r) { assertAsync(r === 5, "new-throw") }
    )
    assertAsyncRan()
  })();
  
  (function() {
    Promise.accept(5);
    Promise.accept(5).chain(undefined, assertUnreachable).chain(
      function(x) { assertAsync(x === 5, "resolved/chain-nohandler") },
      assertUnreachable
    )
    assertAsyncRan()
  })();
  
  (function() {
    Promise.reject(5).chain(assertUnreachable, undefined).chain(
      assertUnreachable,
      function(r) { assertAsync(r === 5, "rejected/chain-nohandler") }
    )
    assertAsyncRan()
  })();
  
  (function() {
    Promise.accept(5).then(undefined, assertUnreachable).chain(
      function(x) { assertAsync(x === 5, "resolved/then-nohandler-undefined") },
      assertUnreachable
    )
    assertAsyncRan()
    Promise.accept(6).then(null, assertUnreachable).chain(
      function(x) { assertAsync(x === 6, "resolved/then-nohandler-null") },
      assertUnreachable
    )
    assertAsyncRan()
  })();
  
  (function() {
    Promise.reject(5).then(assertUnreachable, undefined).chain(
      assertUnreachable,
      function(r) { assertAsync(r === 5, "rejected/then-nohandler-undefined") }
    )
    assertAsyncRan()
    Promise.reject(6).then(assertUnreachable, null).chain(
      assertUnreachable,
      function(r) { assertAsync(r === 6, "rejected/then-nohandler-null") }
    )
    assertAsyncRan()
  })();
  
  (function() {
    var p1 = Promise.accept(5)
    var p2 = Promise.accept(p1)
    var p3 = Promise.accept(p2)
    p3.chain(
      function(x) { assertAsync(x === p2, "resolved/chain") },
      assertUnreachable
    )
    assertAsyncRan()
  })();
  
  (function() {
    var p1 = Promise.accept(5)
    var p2 = Promise.accept(p1)
    var p3 = Promise.accept(p2)
    p3.then(
      function(x) { assertAsync(x === 5, "resolved/then") },
      assertUnreachable
    )
    assertAsyncRan()
  })();
  
  (function() {
    var p1 = Promise.reject(5)
    var p2 = Promise.accept(p1)
    var p3 = Promise.accept(p2)
    p3.chain(
      function(x) { assertAsync(x === p2, "rejected/chain") },
      assertUnreachable
    )
    assertAsyncRan()
  })();
  
  (function() {
    var p1 = Promise.reject(5)
    var p2 = Promise.accept(p1)
    var p3 = Promise.accept(p2)
    p3.then(
      assertUnreachable,
      function(x) { assertAsync(x === 5, "rejected/then") }
    )
    assertAsyncRan()
  })();
  
  (function() {
    var p1 = Promise.accept(5)
    var p2 = Promise.accept(p1)
    var p3 = Promise.accept(p2)
    p3.chain(function(x) { return x }, assertUnreachable).chain(
      function(x) { assertAsync(x === p1, "resolved/chain/chain") },
      assertUnreachable
    )
    assertAsyncRan()
  })();
  
  (function() {
    var p1 = Promise.accept(5)
    var p2 = Promise.accept(p1)
    var p3 = Promise.accept(p2)
    p3.chain(function(x) { return x }, assertUnreachable).then(
      function(x) { assertAsync(x === 5, "resolved/chain/then") },
      assertUnreachable
    )
    assertAsyncRan()
  })();
  
  (function() {
    var p1 = Promise.accept(5)
    var p2 = Promise.accept(p1)
    var p3 = Promise.accept(p2)
    p3.chain(function(x) { return 6 }, assertUnreachable).chain(
      function(x) { assertAsync(x === 6, "resolved/chain/chain2") },
      assertUnreachable
    )
    assertAsyncRan()
  })();
  
  (function() {
    var p1 = Promise.accept(5)
    var p2 = Promise.accept(p1)
    var p3 = Promise.accept(p2)
    p3.chain(function(x) { return 6 }, assertUnreachable).then(
      function(x) { assertAsync(x === 6, "resolved/chain/then2") },
      assertUnreachable
    )
    assertAsyncRan()
  })();
  
  (function() {
    var p1 = Promise.accept(5)
    var p2 = Promise.accept(p1)
    var p3 = Promise.accept(p2)
    p3.then(function(x) { return x + 1 }, assertUnreachable).chain(
      function(x) { assertAsync(x === 6, "resolved/then/chain") },
      assertUnreachable
    )
    assertAsyncRan()
  })();
  
  (function() {
    var p1 = Promise.accept(5)
    var p2 = Promise.accept(p1)
    var p3 = Promise.accept(p2)
    p3.then(function(x) { return x + 1 }, assertUnreachable).then(
      function(x) { assertAsync(x === 6, "resolved/then/then") },
      assertUnreachable
    )
    assertAsyncRan()
  })();
  
  (function() {
    var p1 = Promise.accept(5)
    var p2 = Promise.accept(p1)
    var p3 = Promise.accept(p2)
    p3.then(function(x){ return Promise.accept(x+1) }, assertUnreachable).chain(
      function(x) { assertAsync(x === 6, "resolved/then/chain2") },
      assertUnreachable
    )
    assertAsyncRan()
  })();
  
  (function() {
    var p1 = Promise.accept(5)
    var p2 = Promise.accept(p1)
    var p3 = Promise.accept(p2)
    p3.then(function(x) { return Promise.accept(x+1) }, assertUnreachable).then(
      function(x) { assertAsync(x === 6, "resolved/then/then2") },
      assertUnreachable
    )
    assertAsyncRan()
  })();
  
  (function() {
    var p1 = Promise.accept(5)
    var p2 = Promise.accept(p1)
    var p3 = Promise.accept(p2)
    p3.chain(function(x) { throw 6 }, assertUnreachable).chain(
      assertUnreachable,
      function(x) { assertAsync(x === 6, "resolved/chain-throw/chain") }
    )
    assertAsyncRan()
  })();
  
  (function() {
    var p1 = Promise.accept(5)
    var p2 = Promise.accept(p1)
    var p3 = Promise.accept(p2)
    p3.chain(function(x) { throw 6 }, assertUnreachable).then(
      assertUnreachable,
      function(x) { assertAsync(x === 6, "resolved/chain-throw/then") }
    )
    assertAsyncRan()
  })();
  
  (function() {
    var p1 = Promise.accept(5)
    var p2 = Promise.accept(p1)
    var p3 = Promise.accept(p2)
    p3.then(function(x) { throw 6 }, assertUnreachable).chain(
      assertUnreachable,
      function(x) { assertAsync(x === 6, "resolved/then-throw/chain") }
    )
    assertAsyncRan()
  })();
  
  (function() {
    var p1 = Promise.accept(5)
    var p2 = Promise.accept(p1)
    var p3 = Promise.accept(p2)
    p3.then(function(x) { throw 6 }, assertUnreachable).then(
      assertUnreachable,
      function(x) { assertAsync(x === 6, "resolved/then-throw/then") }
    )
    assertAsyncRan()
  })();
  
  (function() {
    var p1 = Promise.accept(5)
    var p2 = {then: function(onResolve, onReject) { onResolve(p1) }}
    var p3 = Promise.accept(p2)
    p3.chain(
      function(x) { assertAsync(x === p2, "resolved/thenable/chain") },
      assertUnreachable
    )
    assertAsyncRan()
  })();
  
  (function() {
    var p1 = Promise.accept(5)
    var p2 = {then: function(onResolve, onReject) { onResolve(p1) }}
    var p3 = Promise.accept(p2)
    p3.then(
      function(x) { assertAsync(x === 5, "resolved/thenable/then") },
      assertUnreachable
    )
    assertAsyncRan()
  })();
  
  (function() {
    var p1 = Promise.reject(5)
    var p2 = {then: function(onResolve, onReject) { onResolve(p1) }}
    var p3 = Promise.accept(p2)
    p3.chain(
      function(x) { assertAsync(x === p2, "rejected/thenable/chain") },
      assertUnreachable
    )
    assertAsyncRan()
  })();
  
  (function() {
    var p1 = Promise.reject(5)
    var p2 = {then: function(onResolve, onReject) { onResolve(p1) }}
    var p3 = Promise.accept(p2)
    p3.then(
      assertUnreachable,
      function(x) { assertAsync(x === 5, "rejected/thenable/then") }
    )
    assertAsyncRan()
  })();
  
  (function() {
    var deferred = Promise.defer()
    var p1 = deferred.promise
    var p2 = Promise.accept(p1)
    var p3 = Promise.accept(p2)
    p3.chain(
      function(x) { assertAsync(x === p2, "chain/resolve") },
      assertUnreachable
    )
    deferred.resolve(5)
    assertAsyncRan()
  })();
  
  (function() {
    var deferred = Promise.defer()
    var p1 = deferred.promise
    var p2 = Promise.accept(p1)
    var p3 = Promise.accept(p2)
    p3.then(
      function(x) { assertAsync(x === 5, "then/resolve") },
      assertUnreachable
    )
    deferred.resolve(5)
    assertAsyncRan()
  })();
  
  (function() {
    var deferred = Promise.defer()
    var p1 = deferred.promise
    var p2 = Promise.accept(p1)
    var p3 = Promise.accept(p2)
    p3.chain(
      function(x) { assertAsync(x === p2, "chain/reject") },
      assertUnreachable
    )
    deferred.reject(5)
    assertAsyncRan()
  })();
  
  (function() {
    var deferred = Promise.defer()
    var p1 = deferred.promise
    var p2 = Promise.accept(p1)
    var p3 = Promise.accept(p2)
    p3.then(
      assertUnreachable,
      function(x) { assertAsync(x === 5, "then/reject") }
    )
    deferred.reject(5)
    assertAsyncRan()
  })();
  
  (function() {
    var deferred = Promise.defer()
    var p1 = deferred.promise
    var p2 = p1.then(1, 2)
    p2.then(
      function(x) { assertAsync(x === 5, "then/resolve-non-function") },
      assertUnreachable
    )
    deferred.resolve(5)
    assertAsyncRan()
  })();
  
  (function() {
    var deferred = Promise.defer()
    var p1 = deferred.promise
    var p2 = p1.then(1, 2)
    p2.then(
      assertUnreachable,
      function(x) { assertAsync(x === 5, "then/reject-non-function") }
    )
    deferred.reject(5)
    assertAsyncRan()
  })();
  
  (function() {
    var deferred = Promise.defer()
    var p1 = deferred.promise
    var p2 = {then: function(onResolve, onReject) { onResolve(p1) }}
    var p3 = Promise.accept(p2)
    p3.chain(
      function(x) { assertAsync(x === p2, "chain/resolve/thenable") },
      assertUnreachable
    )
    deferred.resolve(5)
    assertAsyncRan()
  })();
  
  (function() {
    var deferred = Promise.defer()
    var p1 = deferred.promise
    var p2 = {then: function(onResolve, onReject) { onResolve(p1) }}
    var p3 = Promise.accept(p2)
    p3.then(
      function(x) { assertAsync(x === 5, "then/resolve/thenable") },
      assertUnreachable
    )
    deferred.resolve(5)
    assertAsyncRan()
  })();
  
  (function() {
    var deferred = Promise.defer()
    var p1 = deferred.promise
    var p2 = {then: function(onResolve, onReject) { onResolve(p1) }}
    var p3 = Promise.accept(p2)
    p3.chain(
      function(x) { assertAsync(x === p2, "chain/reject/thenable") },
      assertUnreachable
    )
    deferred.reject(5)
    assertAsyncRan()
  })();
  
  (function() {
    var deferred = Promise.defer()
    var p1 = deferred.promise
    var p2 = {then: function(onResolve, onReject) { onResolve(p1) }}
    var p3 = Promise.accept(p2)
    p3.then(
      assertUnreachable,
      function(x) { assertAsync(x === 5, "then/reject/thenable") }
    )
    deferred.reject(5)
    assertAsyncRan()
  })();
  
  (function() {
    var p1 = Promise.accept(5)
    var p2 = Promise.accept(p1)
    var deferred = Promise.defer()
    var p3 = deferred.promise
    p3.chain(
      function(x) { assertAsync(x === p2, "chain/resolve2") },
      assertUnreachable
    )
    deferred.resolve(p2)
    assertAsyncRan()
  })();
  
  (function() {
    var p1 = Promise.accept(5)
    var p2 = Promise.accept(p1)
    var deferred = Promise.defer()
    var p3 = deferred.promise
    p3.then(
      function(x) { assertAsync(x === 5, "then/resolve2") },
      assertUnreachable
    )
    deferred.resolve(p2)
    assertAsyncRan()
  })();
  
  (function() {
    var p1 = Promise.accept(5)
    var p2 = Promise.accept(p1)
    var deferred = Promise.defer()
    var p3 = deferred.promise
    p3.chain(
      assertUnreachable,
      function(x) { assertAsync(x === 5, "chain/reject2") }
    )
    deferred.reject(5)
    assertAsyncRan()
  })();
  
  (function() {
    var p1 = Promise.accept(5)
    var p2 = Promise.accept(p1)
    var deferred = Promise.defer()
    var p3 = deferred.promise
    p3.then(
      assertUnreachable,
      function(x) { assertAsync(x === 5, "then/reject2") }
    )
    deferred.reject(5)
    assertAsyncRan()
  })();
  
  (function() {
    var p1 = Promise.accept(5)
    var p2 = {then: function(onResolve, onReject) { onResolve(p1) }}
    var deferred = Promise.defer()
    var p3 = deferred.promise
    p3.chain(
      function(x) { assertAsync(x === p2, "chain/resolve/thenable2") },
      assertUnreachable
    )
    deferred.resolve(p2)
    assertAsyncRan()
  })();
  
  (function() {
    var p1 = Promise.accept(5)
    var p2 = {then: function(onResolve, onReject) { onResolve(p1) }}
    var deferred = Promise.defer()
    var p3 = deferred.promise
    p3.then(
      function(x) { assertAsync(x === 5, "then/resolve/thenable2") },
      assertUnreachable
    )
    deferred.resolve(p2)
    assertAsyncRan()
  })();
  
  (function() {
    var p1 = Promise.accept(0)
    var p2 = p1.chain(function(x) { return p2 }, assertUnreachable)
    p2.chain(
      assertUnreachable,
      function(r) { assertAsync(r instanceof TypeError, "cyclic/chain") }
    )
    assertAsyncRan()
  })();
  
  (function() {
    var p1 = Promise.accept(0)
    var p2 = p1.then(function(x) { return p2 }, assertUnreachable)
    p2.chain(
      assertUnreachable,
      function(r) { assertAsync(r instanceof TypeError, "cyclic/then") }
    )
    assertAsyncRan()
  })();
  
  (function() {
    var deferred = Promise.defer()
    var p = deferred.promise
    deferred.resolve(p)
    p.chain(
      function(x) { assertAsync(x === p, "cyclic/deferred/chain") },
      assertUnreachable
    )
    assertAsyncRan()
  })();
  
  (function() {
    var deferred = Promise.defer()
    var p = deferred.promise
    deferred.resolve(p)
    p.then(
      assertUnreachable,
      function(r) { assertAsync(r instanceof TypeError, "cyclic/deferred/then") }
    )
    assertAsyncRan()
  })();
  
  (function() {
    Promise.all([]).chain(
      function(x) { assertAsync(x.length === 0, "all/resolve/empty") },
      assertUnreachable
    )
    assertAsyncRan()
  })();
  
  (function() {
    function testPromiseAllNonIterable(value) {
      Promise.all(value).chain(
          assertUnreachable,
          function(r) {
            assertAsync(r instanceof TypeError, 'all/non iterable');
          });
      assertAsyncRan();
    }
    testPromiseAllNonIterable(null);
    testPromiseAllNonIterable(undefined);
    testPromiseAllNonIterable({});
    testPromiseAllNonIterable(42);
  })();
  
  (function() {
    var deferred = Promise.defer();
    var p = deferred.promise;
    function* f() {
      yield 1;
      yield p;
      yield 3;
    }
    Promise.all(f()).chain(
        function(x) {
          assertAsync(x.length === 3, "all/resolve/iterable");
          assertAsync(x[0] === 1, "all/resolve/iterable/0");
          assertAsync(x[1] === 2, "all/resolve/iterable/1");
          assertAsync(x[2] === 3, "all/resolve/iterable/2");
        },
        assertUnreachable);
    deferred.resolve(2);
    assertAsyncRan();
    assertAsyncRan();
    assertAsyncRan();
    assertAsyncRan();
  })();
  
  
  (function() {
    var deferred1 = Promise.defer()
    var p1 = deferred1.promise
    var deferred2 = Promise.defer()
    var p2 = deferred2.promise
    var deferred3 = Promise.defer()
    var p3 = deferred3.promise
    Promise.all([p1, p2, p3]).chain(
      function(x) {
        assertAsync(x.length === 3, "all/resolve")
        assertAsync(x[0] === 1, "all/resolve/0")
        assertAsync(x[1] === 2, "all/resolve/1")
        assertAsync(x[2] === 3, "all/resolve/2")
      },
      assertUnreachable
    )
    deferred1.resolve(1)
    deferred3.resolve(3)
    deferred2.resolve(2)
    assertAsyncRan()
    assertAsyncRan()
    assertAsyncRan()
    assertAsyncRan()
  })();
  
  (function() {
    var deferred = Promise.defer()
    var p1 = deferred.promise
    var p2 = Promise.accept(2)
    var p3 = Promise.defer().promise
    Promise.all([p1, p2, p3]).chain(
      assertUnreachable,
      assertUnreachable
    )
    deferred.resolve(1)
  })();
  
  (function() {
    var deferred1 = Promise.defer()
    var p1 = deferred1.promise
    var deferred2 = Promise.defer()
    var p2 = deferred2.promise
    var deferred3 = Promise.defer()
    var p3 = deferred3.promise
    Promise.all([p1, p2, p3]).chain(
      assertUnreachable,
      function(x) { assertAsync(x === 2, "all/reject") }
    )
    deferred1.resolve(1)
    deferred3.resolve(3)
    deferred2.reject(2)
    assertAsyncRan()
  })();
  
  
  (function() {
    'use strict';
    var getCalls = 0;
    var funcCalls = 0;
    var nextCalls = 0;
    defineProperty(numberPrototype, symbolIterator, {
      get: function() {
        assertEquals('number', typeof this);
        getCalls++;
        return function() {
          assertEquals('number', typeof this);
          funcCalls++;
          var n = this;
          var i = 0
          return {
            next() {
              nextCalls++;
              return {value: i++, done: i > n};
            }
          };
        };
      },
      configurable: true
    });
  
    Promise.all(3).chain(
        function(x) {
          assertAsync(x.length === 3, "all/iterable/number/length");
          assertAsync(x[0] === 0, "all/iterable/number/0");
          assertAsync(x[1] === 1, "all/iterable/number/1");
          assertAsync(x[2] === 2, "all/iterable/number/2");
        },
        assertUnreachable);
    delete numberPrototype[symbolIterator];
  
    assertEquals(getCalls, 1);
    assertEquals(funcCalls, 1);
    assertEquals(nextCalls, 3 + 1);  // + 1 for {done: true}
    assertAsyncRan();
    assertAsyncRan();
    assertAsyncRan();
    assertAsyncRan();
  })();
  
  
  (function() {
    Promise.race([]).chain(
      assertUnreachable,
      assertUnreachable
    )
  })();
  
  (function() {
    var p1 = Promise.accept(1)
    var p2 = Promise.accept(2)
    var p3 = Promise.accept(3)
    Promise.race([p1, p2, p3]).chain(
      function(x) { assertAsync(x === 1, "resolved/one") },
      assertUnreachable
    )
    assertAsyncRan()
  })();
  
  (function() {
    var p1 = Promise.accept(1)
    var p2 = Promise.accept(2)
    var p3 = Promise.accept(3)
    Promise.race([0, p1, p2, p3]).chain(
      function(x) { assertAsync(x === 0, "resolved-const/one") },
      assertUnreachable
    )
    assertAsyncRan()
  })();
  
  (function() {
    var deferred1 = Promise.defer()
    var p1 = deferred1.promise
    var deferred2 = Promise.defer()
    var p2 = deferred2.promise
    var deferred3 = Promise.defer()
    var p3 = deferred3.promise
    Promise.race([p1, p2, p3]).chain(
      function(x) { assertAsync(x === 3, "one/resolve") },
      assertUnreachable
    )
    deferred3.resolve(3)
    deferred1.resolve(1)
    assertAsyncRan()
  })();
  
  (function() {
    var deferred = Promise.defer()
    var p1 = deferred.promise
    var p2 = Promise.accept(2)
    var p3 = Promise.defer().promise
    Promise.race([p1, p2, p3]).chain(
      function(x) { assertAsync(x === 2, "resolved/one") },
      assertUnreachable
    )
    deferred.resolve(1)
    assertAsyncRan()
  })();
  
  (function() {
    var deferred1 = Promise.defer()
    var p1 = deferred1.promise
    var deferred2 = Promise.defer()
    var p2 = deferred2.promise
    var deferred3 = Promise.defer()
    var p3 = deferred3.promise
    Promise.race([p1, p2, p3]).chain(
      function(x) { assertAsync(x === 3, "one/resolve/reject") },
      assertUnreachable
    )
    deferred3.resolve(3)
    deferred1.reject(1)
    assertAsyncRan()
  })();
  
  (function() {
    var deferred1 = Promise.defer()
    var p1 = deferred1.promise
    var deferred2 = Promise.defer()
    var p2 = deferred2.promise
    var deferred3 = Promise.defer()
    var p3 = deferred3.promise
    Promise.race([p1, p2, p3]).chain(
      assertUnreachable,
      function(x) { assertAsync(x === 3, "one/reject/resolve") }
    )
    deferred3.reject(3)
    deferred1.resolve(1)
    assertAsyncRan()
  })();
  
  
  (function() {
    function testPromiseRaceNonIterable(value) {
      Promise.race(value).chain(
          assertUnreachable,
          function(r) {
            assertAsync(r instanceof TypeError, 'race/non iterable');
          });
      assertAsyncRan();
    }
    testPromiseRaceNonIterable(null);
    testPromiseRaceNonIterable(undefined);
    testPromiseRaceNonIterable({});
    testPromiseRaceNonIterable(42);
  })();
  
  
  (function() {
    var deferred1 = Promise.defer()
    var p1 = deferred1.promise
    var deferred2 = Promise.defer()
    var p2 = deferred2.promise
    var deferred3 = Promise.defer()
    var p3 = deferred3.promise
    function* f() {
      yield p1;
      yield p2;
      yield p3;
    }
    Promise.race(f()).chain(
      function(x) { assertAsync(x === 3, "race/iterable/resolve/reject") },
      assertUnreachable
    )
    deferred3.resolve(3)
    deferred1.reject(1)
    assertAsyncRan()
  })();
  
  (function() {
    var deferred1 = Promise.defer()
    var p1 = deferred1.promise
    var deferred2 = Promise.defer()
    var p2 = deferred2.promise
    var deferred3 = Promise.defer()
    var p3 = deferred3.promise
    function* f() {
      yield p1;
      yield p2;
      yield p3;
    }
    Promise.race(f()).chain(
      assertUnreachable,
      function(x) { assertAsync(x === 3, "race/iterable/reject/resolve") }
    )
    deferred3.reject(3)
    deferred1.resolve(1)
    assertAsyncRan()
  })();
  
  (function() {
    'use strict';
    var getCalls = 0;
    var funcCalls = 0;
    var nextCalls = 0;
    defineProperty(numberPrototype, symbolIterator, {
      get: function() {
        assertEquals('number', typeof this);
        getCalls++;
        return function() {
          assertEquals('number', typeof this);
          funcCalls++;
          var n = this;
          var i = 0
          return {
            next() {
              nextCalls++;
              return {value: i++, done: i > n};
            }
          };
        };
      },
      configurable: true
    });
  
    Promise.race(3).chain(
        function(x) {
          assertAsync(x === 0, "race/iterable/number");
        },
        assertUnreachable);
    delete numberPrototype[symbolIterator];
  
    assertEquals(getCalls, 1);
    assertEquals(funcCalls, 1);
    assertEquals(nextCalls, 3 + 1);  // + 1 for {done: true}
    assertAsyncRan();
  })();
  
  (function() {
    var log
    function MyPromise(resolver) {
      log += "n"
      var promise = new Promise(function(resolve, reject) {
        resolver(
          function(x) { log += "x" + x; resolve(x) },
          function(r) { log += "r" + r; reject(r) }
        )
      })
      promise.__proto__ = MyPromise.prototype
      return promise
    }
  
    MyPromise.__proto__ = Promise
    MyPromise.defer = function() {
      log += "d"
      return call(this.__proto__.defer, this)
    }
  
    MyPromise.prototype.__proto__ = Promise.prototype
    MyPromise.prototype.chain = function(resolve, reject) {
      log += "c"
      return call(this.__proto__.__proto__.chain, this, resolve, reject)
    }
  
    log = ""
    var p1 = new MyPromise(function(resolve, reject) { resolve(1) })
    var p2 = new MyPromise(function(resolve, reject) { reject(2) })
    var d3 = MyPromise.defer()
    assertTrue(d3.promise instanceof Promise, "subclass/instance")
    assertTrue(d3.promise instanceof MyPromise, "subclass/instance-my3")
    assertTrue(log === "nx1nr2dn", "subclass/create")
  
    log = ""
    var p4 = MyPromise.resolve(4)
    var p5 = MyPromise.reject(5)
    assertTrue(p4 instanceof Promise, "subclass/instance4")
    assertTrue(p4 instanceof MyPromise, "subclass/instance-my4")
    assertTrue(p5 instanceof Promise, "subclass/instance5")
    assertTrue(p5 instanceof MyPromise, "subclass/instance-my5")
    d3.resolve(3)
    assertTrue(log === "nx4nr5x3", "subclass/resolve")
  
    log = ""
    var d6 = MyPromise.defer()
    d6.promise.chain(function(x) {
      return new Promise(function(resolve) { resolve(x) })
    }).chain(function() {})
    d6.resolve(6)
    assertTrue(log === "dncncnx6", "subclass/chain")
  
    log = ""
    Promise.all([11, Promise.accept(12), 13, MyPromise.accept(14), 15, 16])
    assertTrue(log === "nx14n", "subclass/all/arg")
  
    log = ""
    MyPromise.all([21, Promise.accept(22), 23, MyPromise.accept(24), 25, 26])
    assertTrue(log === "nx24nnx21nnx23nnnx25nnx26n", "subclass/all/self")
  })();
  
  
  assertAsyncDone()
  