[inspector] Don't use v8::Promise::Resolver for REPL mode

REPL mode always returns a promise since we basically turn the
evaluated script in an async function. More-over, we stash the result
as a property on a plain JS object. This prevents promise chains to
resolve too far if the result of the evaluation is a promise itself.

Long story short, we don't need to wrap REPL mode results in
`Promise.resolve`, but can add the then/catch handlers directly.

This fixes the DevTools console when working with broken promise
polyfills or broken thenables.

R=bmeurer@chromium.org

Fixed: chromium:1371072
Change-Id: I96aa8eaf5939fdf6231712b047b50fee734efc0b
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3929037
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Michael Achenbach <machenbach@chromium.org>
Cr-Commit-Position: refs/heads/main@{#83578}
This commit is contained in:
Simon Zünd 2022-10-07 07:06:08 +02:00 committed by V8 LUCI CQ
parent ed8953b695
commit ad884d036f
3 changed files with 48 additions and 13 deletions

View File

@ -102,18 +102,6 @@ class InjectedScript::ProtocolPromiseHandler {
Response response = scope.initialize();
if (!response.IsSuccess()) return;
v8::Local<v8::Promise::Resolver> resolver;
if (!v8::Promise::Resolver::New(context).ToLocal(&resolver)) {
EvaluateCallback::sendFailure(callback, scope.injectedScript(),
Response::InternalError());
return;
}
if (!resolver->Resolve(context, value).FromMaybe(false)) {
EvaluateCallback::sendFailure(callback, scope.injectedScript(),
Response::InternalError());
return;
}
v8::MaybeLocal<v8::Promise> originalPromise =
value->IsPromise() ? value.As<v8::Promise>()
: v8::MaybeLocal<v8::Promise>();
@ -130,7 +118,28 @@ class InjectedScript::ProtocolPromiseHandler {
v8::Function::New(context, catchCallback, wrapper, 0,
v8::ConstructorBehavior::kThrow)
.ToLocalChecked();
v8::Local<v8::Promise> promise = resolver->GetPromise();
v8::Local<v8::Promise> promise;
v8::Local<v8::Promise::Resolver> resolver;
if (value->IsPromise()) {
// If value is a promise, we can chain the handlers directly onto `value`.
promise = value.As<v8::Promise>();
} else {
// Otherwise we do `Promise.resolve(value)`.
CHECK(!replMode);
if (!v8::Promise::Resolver::New(context).ToLocal(&resolver)) {
EvaluateCallback::sendFailure(callback, scope.injectedScript(),
Response::InternalError());
return;
}
if (!resolver->Resolve(context, value).FromMaybe(false)) {
EvaluateCallback::sendFailure(callback, scope.injectedScript(),
Response::InternalError());
return;
}
promise = resolver->GetPromise();
}
if (promise->Then(context, thenCallbackFunction, catchCallbackFunction)
.IsEmpty()) {
// Re-initialize after returning from JS.

View File

@ -0,0 +1,6 @@
Tests that REPL mode still works even with a broken Promise.prototype.then
{
description : 42
type : number
value : 42
}

View File

@ -0,0 +1,20 @@
// Copyright 2022 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.
const {contextGroup, Protocol} = InspectorTest.start(
"Tests that REPL mode still works even with a broken Promise.prototype.then");
(async function() {
contextGroup.addScript(`
Promise.prototype.then = () => {throw Error('you shall not evaluate')};
`);
const { result: { result }} = await Protocol.Runtime.evaluate({
expression: '42',
replMode: true,
});
InspectorTest.logMessage(result);
InspectorTest.completeTest();
})();