Commit 6f214bdd authored by yangguo's avatar yangguo Committed by Commit bot

Revert of Revert of Promise assimilation fix. (patchset #1 id:1 of...

Revert of Revert of Promise assimilation fix. (patchset #1 id:1 of https://codereview.chromium.org/1176163004/)

Reason for revert:
Test failures are bogus. Snapshot blob and natives blob are out of sync due to build being weird.

Original issue's description:
> Revert of Promise assimilation fix. (patchset #8 id:160001 of https://codereview.chromium.org/1098663002/)
>
> Reason for revert:
> Test failures: https://chromegw.corp.google.com/i/client.v8/builders/V8%20Linux64/builds/3829
>
> Original issue's description:
> > Promise assimilation fix.
> >
> > Let x be a fulfilled promise and y be another promise. |x.then(() => y)|
> > should call |y.then|, but the current implementation calls PromiseChain.
> > We can see the difference when we set a custom function to |y.then|.
> >
> > This CL fixes the spec violation, but as a result |then| is no longer
> > a wrapper of |chain| and in some cases it does not work well with
> > |accept| or |chain|. That is not a problem for ES6 promise users because
> > ES6 promise doesn't have them.
> >
> > LOG=N
> > BUG=477921
> >
> > Committed: https://crrev.com/2f57dff3ea0c45e1a61b334fda962460f89d71bc
> > Cr-Commit-Position: refs/heads/master@{#28926}
>
> TBR=arv@chromium.org,caitpotter88@gmail.com,rossberg@chromium.org,yhirano@chromium.org
> NOPRESUBMIT=true
> NOTREECHECKS=true
> NOTRY=true
> BUG=477921
>
> Committed: https://crrev.com/5bb75f514027f79303396dba823c2d78c6add83b
> Cr-Commit-Position: refs/heads/master@{#28927}

TBR=arv@chromium.org,caitpotter88@gmail.com,rossberg@chromium.org,yhirano@chromium.org
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=477921

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

Cr-Commit-Position: refs/heads/master@{#28928}
parent 5bb75f51
......@@ -105,17 +105,25 @@ function PromiseCoerce(constructor, x) {
return x;
}
function PromiseHandle(value, handler, deferred) {
function PromiseHandle(value, handler, deferred, thenable) {
try {
%DebugPushPromise(deferred.promise, PromiseHandle);
DEBUG_PREPARE_STEP_IN_IF_STEPPING(handler);
var result = handler(value);
if (result === deferred.promise)
if (result === deferred.promise) {
throw MakeTypeError(kPromiseCyclic, result);
else if (IsPromise(result))
} else if (IsPromise(result) && thenable) {
var then = result.then;
if (IS_SPEC_FUNCTION(then)) {
PromiseCallThenInSeparateTask(result, deferred, then);
} else {
deferred.resolve(result);
}
} else if (IsPromise(result)) {
%_CallFunction(result, deferred.resolve, deferred.reject, PromiseChain);
else
} else {
deferred.resolve(result);
}
} catch (exception) {
try { deferred.reject(exception); } catch (e) { }
} finally {
......@@ -123,14 +131,38 @@ function PromiseHandle(value, handler, deferred) {
}
}
function PromiseCallThenInSeparateTask(result, deferred, then) {
var id, name, instrumenting = DEBUG_IS_ACTIVE;
%EnqueueMicrotask(function() {
if (instrumenting) {
%DebugAsyncTaskEvent({ type: "willCall", id: id, name: name });
}
try {
%_CallFunction(result, deferred.resolve, deferred.reject, then);
} catch(exception) {
try { deferred.reject(exception); } catch (e) { }
}
if (instrumenting) {
%DebugAsyncTaskEvent({ type: "didCall", id: id, name: name });
}
});
if (instrumenting) {
id = ++lastMicrotaskId;
name = "Promise.prototype.then";
%DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name });
}
}
function PromiseEnqueue(value, tasks, status) {
var id, name, instrumenting = DEBUG_IS_ACTIVE;
%EnqueueMicrotask(function() {
if (instrumenting) {
%DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name });
}
for (var i = 0; i < tasks.length; i += 2) {
PromiseHandle(value, tasks[i], tasks[i + 1])
for (var i = 0; i < tasks.length; i += 3) {
// tasks[i: i + 2] consists of the reaction handler, the associated
// deferred object and whether the thenable handling is required.
PromiseHandle(value, tasks[i], tasks[i + 1], tasks[i + 2])
}
if (instrumenting) {
%DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name });
......@@ -143,6 +175,41 @@ function PromiseEnqueue(value, tasks, status) {
}
}
function PromiseChainInternal(onResolve, onReject, thenable) {
onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve;
onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject;
var deferred = %_CallFunction(this.constructor, PromiseDeferred);
switch (GET_PRIVATE(this, promiseStatus)) {
case UNDEFINED:
throw MakeTypeError(kNotAPromise, this);
case 0: // Pending
GET_PRIVATE(this, promiseOnResolve).push(onResolve, deferred, thenable);
GET_PRIVATE(this, promiseOnReject).push(onReject, deferred, thenable);
break;
case +1: // Resolved
PromiseEnqueue(GET_PRIVATE(this, promiseValue),
[onResolve, deferred, thenable],
+1);
break;
case -1: // Rejected
if (!HAS_DEFINED_PRIVATE(this, promiseHasHandler)) {
// Promise has already been rejected, but had no handler.
// Revoke previously triggered reject event.
%PromiseRevokeReject(this);
}
PromiseEnqueue(GET_PRIVATE(this, promiseValue),
[onReject, deferred, thenable],
-1);
break;
}
// Mark this promise as having handler.
SET_PRIVATE(this, promiseHasHandler, true);
if (DEBUG_IS_ACTIVE) {
%DebugPromiseEvent({ promise: deferred.promise, parentPromise: this });
}
return deferred.promise;
}
function PromiseIdResolveHandler(x) { return x }
function PromiseIdRejectHandler(r) { throw r }
......@@ -224,38 +291,8 @@ function PromiseRejected(r) {
// Simple chaining.
function PromiseChain(onResolve, onReject) { // a.k.a. flatMap
onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve;
onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject;
var deferred = %_CallFunction(this.constructor, PromiseDeferred);
switch (GET_PRIVATE(this, promiseStatus)) {
case UNDEFINED:
throw MakeTypeError(kNotAPromise, this);
case 0: // Pending
GET_PRIVATE(this, promiseOnResolve).push(onResolve, deferred);
GET_PRIVATE(this, promiseOnReject).push(onReject, deferred);
break;
case +1: // Resolved
PromiseEnqueue(GET_PRIVATE(this, promiseValue),
[onResolve, deferred],
+1);
break;
case -1: // Rejected
if (!HAS_DEFINED_PRIVATE(this, promiseHasHandler)) {
// Promise has already been rejected, but had no handler.
// Revoke previously triggered reject event.
%PromiseRevokeReject(this);
}
PromiseEnqueue(GET_PRIVATE(this, promiseValue),
[onReject, deferred],
-1);
break;
}
// Mark this promise as having handler.
SET_PRIVATE(this, promiseHasHandler, true);
if (DEBUG_IS_ACTIVE) {
%DebugPromiseEvent({ promise: deferred.promise, parentPromise: this });
}
return deferred.promise;
return %_CallFunction(this, onResolve, onReject, false,
PromiseChainInternal);
}
function PromiseCatch(onReject) {
......@@ -286,7 +323,8 @@ function PromiseThen(onResolve, onReject) {
}
},
onReject,
PromiseChain
true,
PromiseChainInternal
);
}
......@@ -348,7 +386,7 @@ function PromiseRace(iterable) {
function PromiseHasUserDefinedRejectHandlerRecursive(promise) {
var queue = GET_PRIVATE(promise, promiseOnReject);
if (IS_UNDEFINED(queue)) return false;
for (var i = 0; i < queue.length; i += 2) {
for (var i = 0; i < queue.length; i += 3) {
if (queue[i] != PromiseIdRejectHandler) return true;
if (PromiseHasUserDefinedRejectHandlerRecursive(queue[i + 1].promise)) {
return true;
......
......@@ -390,6 +390,59 @@ function assertAsyncDone(iteration) {
assertAsyncRan()
})();
(function() {
var p1 = Promise.accept(5)
var p2 = Promise.accept(p1)
var called = false
p2.then = function(onResolve, onReject) {
called = true;
return call(Promise.prototype.then, p2, onResolve, onReject)
}
var p3 = Promise.accept(p2)
p3.chain(
function(x) {
assertAsync(x === p2 && !called, "resolved/thenable-promise/chain")
},
assertUnreachable)
assertAsyncRan()
})();
(function() {
var p1 = Promise.accept(5)
var p2 = Promise.accept(p1)
var called = false
p2.then = function(onResolve, onReject) {
called = true
return call(Promise.prototype.then, p2, onResolve, onReject)
}
var p3 = Promise.accept(p2)
p3.then(
function(x) {
assertAsync(x === 5 && called, "resolved/thenable-promise/then")
},
assertUnreachable)
assertAsyncRan()
})();
(function() {
var p1 = Promise.accept(5)
var called = false
var p3 = p1.then(function(x) {
var p2 = Promise.accept(5)
p2.then = function(onResolve, onReject) {
called = true
throw 25
}
return p2
});
p3.then(
assertUnreachable,
function(x) {
assertAsync(called && x === 25, "thenable-promise/then-call-throw")
})
assertAsyncRan()
})();
(function() {
var deferred = Promise.defer()
var p1 = deferred.promise
......@@ -518,6 +571,106 @@ function assertAsyncDone(iteration) {
assertAsyncRan()
})();
(function() {
var deferred = Promise.defer()
var deferred2 = Promise.defer()
var deferred3 = Promise.defer()
var p1 = deferred.promise
var p2 = deferred2.promise
var p3 = deferred3.promise
var called = false
p2.then = function(onResolve, onReject) {
called = true
return call(Promise.prototype.then, p2, onResolve, onReject)
}
p3.chain(
function(x) { assertAsync(x === p2 && !called,
"chain/resolve/thenable-promise") },
assertUnreachable
)
deferred3.resolve(p2)
deferred2.resolve(p1)
deferred.resolve(5)
assertAsyncRan()
})();
(function() {
var deferred = Promise.defer()
var deferred2 = Promise.defer()
var deferred3 = Promise.defer()
var p1 = deferred.promise
var p2 = deferred2.promise
var p3 = deferred3.promise
var called = false
p2.then = function(onResolve, onReject) {
called = true
return call(Promise.prototype.then, p2, onResolve, onReject)
}
p3.then(
function(x) { assertAsync(x === 5 && called,
"then/resolve/thenable-promise") },
assertUnreachable
)
deferred3.resolve(p2)
deferred2.resolve(p1)
deferred.resolve(5)
assertAsyncRan()
})();
(function() {
var deferred = Promise.defer()
var deferred2 = Promise.defer()
var deferred3 = Promise.defer()
var p1 = deferred.promise
var p2 = deferred2.promise
var p3 = deferred3.promise
var called = false
p2.then = function(onResolve, onReject) {
called = true
return call(Promise.prototype.then, p2, onResolve, onReject)
}
p3.chain(
function(x) { assertAsync(x === p2 && !called,
"chain/reject/thenable-promise") },
assertUnreachable
)
deferred3.resolve(p2)
deferred2.resolve(p1)
deferred.reject(5)
assertAsyncRan()
})();
(function() {
var deferred = Promise.defer()
var deferred2 = Promise.defer()
var deferred3 = Promise.defer()
var p1 = deferred.promise
var p2 = deferred2.promise
var p3 = deferred3.promise
var called = false
p2.then = function(onResolve, onReject) {
called = true
return call(Promise.prototype.then, p2, onResolve, onReject)
}
p3.then(
assertUnreachable,
function(x) { assertAsync(x === 5 && called,
"then/reject/thenable-promise") }
)
deferred3.resolve(p2)
deferred2.resolve(p1)
deferred.reject(5)
assertAsyncRan()
})();
(function() {
var p1 = Promise.accept(5)
var p2 = Promise.accept(p1)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment