Add @@species/better subclassing support to Promises

This patch makes Promise.prototype.then use @@species as specified
in ES2015. The fix is hoped for by certain users, such as legacy
core.js versions which encounter an unhandled Promise reject (complete
with an ugly console message) when Promise subclassing is supported
in a mostly correct way, and we do error checking on Promise
constructors, but @@species is not supported.

BUG=chromium:575314,v8:4633
LOG=Y
R=adamk

Review URL: https://codereview.chromium.org/1577223002

Cr-Commit-Position: refs/heads/master@{#33225}
This commit is contained in:
littledan 2016-01-11 22:32:57 -08:00 committed by Commit bot
parent 150887a13c
commit 569145019d
2 changed files with 45 additions and 1 deletions

View File

@ -23,10 +23,12 @@ var promiseOnResolveSymbol =
var promiseRawSymbol = utils.ImportNow("promise_raw_symbol");
var promiseStatusSymbol = utils.ImportNow("promise_status_symbol");
var promiseValueSymbol = utils.ImportNow("promise_value_symbol");
var SpeciesConstructor;
var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
utils.Import(function(from) {
MakeTypeError = from.MakeTypeError;
SpeciesConstructor = from.SpeciesConstructor;
});
// -------------------------------------------------------------------
@ -276,7 +278,7 @@ function PromiseThen(onResolve, onReject) {
throw MakeTypeError(kNotAPromise, this);
}
var constructor = this.constructor;
var constructor = SpeciesConstructor(this, GlobalPromise);
onResolve = IS_CALLABLE(onResolve) ? onResolve : PromiseIdResolveHandler;
onReject = IS_CALLABLE(onReject) ? onReject : PromiseIdRejectHandler;
var deferred = NewPromiseCapability(constructor);

View File

@ -0,0 +1,42 @@
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-species --allow-natives-syntax
// Test that Promises use @@species appropriately
// Another constructor with no species will not be instantiated
var test = new Promise(function(){});
var bogoCount = 0;
function bogusConstructor() { bogoCount++; }
test.constructor = bogusConstructor;
assertTrue(Promise.resolve(test) instanceof Promise);
assertFalse(Promise.resolve(test) instanceof bogusConstructor);
// Tests that chromium:575314 is fixed thoroughly
Promise.resolve(test).catch(e => %AbortJS("Error " + e)).then(() => {
if (bogoCount != 0) %AbortJS("bogoCount was " + bogoCount + " should be 0");
});
// If there is a species, it will be instantiated
// @@species will be read exactly once, and the constructor is called with a
// function
var count = 0;
var params;
class MyPromise extends Promise {
constructor(...args) {
super(...args);
params = args;
}
static get [Symbol.species]() {
count++
return this;
}
}
var myPromise = MyPromise.resolve().then();
assertEquals(1, count);
assertEquals(1, params.length);
assertEquals('function', typeof(params[0]));
assertTrue(myPromise instanceof MyPromise);
assertTrue(myPromise instanceof Promise);