Use vanilla context for exception meta data
Bug: chromium:1213393, chromium:1218340 Change-Id: Icde33c97d39a3504ca2ab8290ec2f0b0d923060d Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2953194 Commit-Queue: Sigurd Schneider <sigurds@chromium.org> Reviewed-by: Toon Verwaest <verwaest@chromium.org> Reviewed-by: Yang Guo <yangguo@chromium.org> Cr-Commit-Position: refs/heads/master@{#75201}
This commit is contained in:
parent
80f204a6ee
commit
60dfa4de6b
@ -1254,7 +1254,7 @@ domain Runtime
|
|||||||
optional RemoteObject exception
|
optional RemoteObject exception
|
||||||
# Identifier of the context where exception happened.
|
# Identifier of the context where exception happened.
|
||||||
optional ExecutionContextId executionContextId
|
optional ExecutionContextId executionContextId
|
||||||
# Dictionary with entries of meta deta that the client associated
|
# Dictionary with entries of meta data that the client associated
|
||||||
# with this exception, such as information about associated network
|
# with this exception, such as information about associated network
|
||||||
# requests, etc.
|
# requests, etc.
|
||||||
experimental optional object exceptionMetaData
|
experimental optional object exceptionMetaData
|
||||||
|
@ -332,7 +332,8 @@ void V8ConsoleMessage::reportToFrontend(protocol::Runtime::Frontend* frontend,
|
|||||||
}
|
}
|
||||||
if (m_contextId) exceptionDetails->setExecutionContextId(m_contextId);
|
if (m_contextId) exceptionDetails->setExecutionContextId(m_contextId);
|
||||||
if (exception) exceptionDetails->setException(std::move(exception));
|
if (exception) exceptionDetails->setException(std::move(exception));
|
||||||
auto data = getAssociatedExceptionData(inspector, session);
|
std::unique_ptr<protocol::DictionaryValue> data =
|
||||||
|
getAssociatedExceptionData(inspector, session);
|
||||||
if (data) exceptionDetails->setExceptionMetaData(std::move(data));
|
if (data) exceptionDetails->setExceptionMetaData(std::move(data));
|
||||||
frontend->exceptionThrown(m_timestamp, std::move(exceptionDetails));
|
frontend->exceptionThrown(m_timestamp, std::move(exceptionDetails));
|
||||||
return;
|
return;
|
||||||
@ -388,23 +389,24 @@ V8ConsoleMessage::getAssociatedExceptionData(
|
|||||||
V8InspectorImpl* inspector, V8InspectorSessionImpl* session) const {
|
V8InspectorImpl* inspector, V8InspectorSessionImpl* session) const {
|
||||||
if (!m_arguments.size() || !m_contextId) return nullptr;
|
if (!m_arguments.size() || !m_contextId) return nullptr;
|
||||||
DCHECK_EQ(1u, m_arguments.size());
|
DCHECK_EQ(1u, m_arguments.size());
|
||||||
InspectedContext* inspectedContext =
|
|
||||||
session->inspector()->getContext(session->contextGroupId(), m_contextId);
|
|
||||||
if (!inspectedContext) return nullptr;
|
|
||||||
|
|
||||||
v8::Isolate* isolate = inspectedContext->isolate();
|
v8::Isolate* isolate = inspector->isolate();
|
||||||
v8::HandleScope handles(isolate);
|
v8::HandleScope handles(isolate);
|
||||||
|
v8::Local<v8::Context> context;
|
||||||
|
if (!inspector->exceptionMetaDataContext().ToLocal(&context)) return nullptr;
|
||||||
v8::MaybeLocal<v8::Value> maybe_exception = m_arguments[0]->Get(isolate);
|
v8::MaybeLocal<v8::Value> maybe_exception = m_arguments[0]->Get(isolate);
|
||||||
v8::Local<v8::Value> exception;
|
v8::Local<v8::Value> exception;
|
||||||
if (!maybe_exception.ToLocal(&exception)) return nullptr;
|
if (!maybe_exception.ToLocal(&exception)) return nullptr;
|
||||||
|
|
||||||
v8::MaybeLocal<v8::Object> maybe_data = inspector->getAssociatedExceptionData(
|
v8::MaybeLocal<v8::Object> maybe_data =
|
||||||
inspectedContext->context(), exception);
|
inspector->getAssociatedExceptionData(exception);
|
||||||
v8::Local<v8::Object> data;
|
v8::Local<v8::Object> data;
|
||||||
if (!maybe_data.ToLocal(&data)) return nullptr;
|
if (!maybe_data.ToLocal(&data)) return nullptr;
|
||||||
|
v8::MicrotasksScope microtasksScope(isolate,
|
||||||
|
v8::MicrotasksScope::kDoNotRunMicrotasks);
|
||||||
|
v8::Context::Scope contextScope(context);
|
||||||
std::unique_ptr<protocol::DictionaryValue> jsonObject;
|
std::unique_ptr<protocol::DictionaryValue> jsonObject;
|
||||||
objectToProtocolValue(inspectedContext->context(), data, 2, &jsonObject);
|
objectToProtocolValue(context, data, 2, &jsonObject);
|
||||||
return jsonObject;
|
return jsonObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,6 +377,17 @@ v8::MaybeLocal<v8::Context> V8InspectorImpl::regexContext() {
|
|||||||
return m_regexContext.Get(m_isolate);
|
return m_regexContext.Get(m_isolate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v8::MaybeLocal<v8::Context> V8InspectorImpl::exceptionMetaDataContext() {
|
||||||
|
if (m_exceptionMetaDataContext.IsEmpty()) {
|
||||||
|
m_exceptionMetaDataContext.Reset(m_isolate, v8::Context::New(m_isolate));
|
||||||
|
if (m_exceptionMetaDataContext.IsEmpty()) {
|
||||||
|
DCHECK(m_isolate->IsExecutionTerminating());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m_exceptionMetaDataContext.Get(m_isolate);
|
||||||
|
}
|
||||||
|
|
||||||
void V8InspectorImpl::discardInspectedContext(int contextGroupId,
|
void V8InspectorImpl::discardInspectedContext(int contextGroupId,
|
||||||
int contextId) {
|
int contextId) {
|
||||||
auto* context = getContext(contextGroupId, contextId);
|
auto* context = getContext(contextGroupId, contextId);
|
||||||
@ -492,23 +503,26 @@ protocol::Response V8InspectorImpl::EvaluateScope::setTimeout(double timeout) {
|
|||||||
return protocol::Response::Success();
|
return protocol::Response::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool V8InspectorImpl::associateExceptionData(v8::Local<v8::Context> context,
|
bool V8InspectorImpl::associateExceptionData(v8::Local<v8::Context>,
|
||||||
v8::Local<v8::Value> exception,
|
v8::Local<v8::Value> exception,
|
||||||
v8::Local<v8::Name> key,
|
v8::Local<v8::Name> key,
|
||||||
v8::Local<v8::Value> value) {
|
v8::Local<v8::Value> value) {
|
||||||
|
v8::Local<v8::Context> context;
|
||||||
|
if (!exceptionMetaDataContext().ToLocal(&context)) return false;
|
||||||
v8::HandleScope handles(m_isolate);
|
v8::HandleScope handles(m_isolate);
|
||||||
if (m_excepetionMetaData.IsEmpty())
|
if (m_exceptionMetaData.IsEmpty())
|
||||||
m_excepetionMetaData.Reset(m_isolate, v8::debug::WeakMap::New(m_isolate));
|
m_exceptionMetaData.Reset(m_isolate, v8::debug::WeakMap::New(m_isolate));
|
||||||
|
|
||||||
v8::Local<v8::debug::WeakMap> map = m_excepetionMetaData.Get(m_isolate);
|
v8::Local<v8::debug::WeakMap> map = m_exceptionMetaData.Get(m_isolate);
|
||||||
v8::MaybeLocal<v8::Value> entry = map->Get(context, exception);
|
v8::MaybeLocal<v8::Value> entry = map->Get(context, exception);
|
||||||
v8::Local<v8::Object> object;
|
v8::Local<v8::Object> object;
|
||||||
if (entry.IsEmpty() || !entry.ToLocalChecked()->IsObject()) {
|
if (entry.IsEmpty() || !entry.ToLocalChecked()->IsObject()) {
|
||||||
object = v8::Object::New(m_isolate);
|
object =
|
||||||
|
v8::Object::New(m_isolate, v8::Null(m_isolate), nullptr, nullptr, 0);
|
||||||
v8::MaybeLocal<v8::debug::WeakMap> new_map =
|
v8::MaybeLocal<v8::debug::WeakMap> new_map =
|
||||||
map->Set(context, exception, object);
|
map->Set(context, exception, object);
|
||||||
if (!new_map.IsEmpty()) {
|
if (!new_map.IsEmpty()) {
|
||||||
m_excepetionMetaData.Reset(m_isolate, new_map.ToLocalChecked());
|
m_exceptionMetaData.Reset(m_isolate, new_map.ToLocalChecked());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
object = entry.ToLocalChecked().As<v8::Object>();
|
object = entry.ToLocalChecked().As<v8::Object>();
|
||||||
@ -519,12 +533,17 @@ bool V8InspectorImpl::associateExceptionData(v8::Local<v8::Context> context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
v8::MaybeLocal<v8::Object> V8InspectorImpl::getAssociatedExceptionData(
|
v8::MaybeLocal<v8::Object> V8InspectorImpl::getAssociatedExceptionData(
|
||||||
v8::Local<v8::Context> context, v8::Local<v8::Value> exception) {
|
v8::Local<v8::Value> exception) {
|
||||||
if (m_excepetionMetaData.IsEmpty()) return v8::MaybeLocal<v8::Object>();
|
v8::EscapableHandleScope scope(m_isolate);
|
||||||
|
v8::Local<v8::Context> context;
|
||||||
v8::Local<v8::debug::WeakMap> map = m_excepetionMetaData.Get(m_isolate);
|
if (m_exceptionMetaData.IsEmpty() ||
|
||||||
|
!exceptionMetaDataContext().ToLocal(&context)) {
|
||||||
|
return v8::MaybeLocal<v8::Object>();
|
||||||
|
}
|
||||||
|
v8::Local<v8::debug::WeakMap> map = m_exceptionMetaData.Get(m_isolate);
|
||||||
auto entry = map->Get(context, exception);
|
auto entry = map->Get(context, exception);
|
||||||
if (entry.IsEmpty()) return v8::MaybeLocal<v8::Object>();
|
v8::Local<v8::Value> object;
|
||||||
return entry.ToLocalChecked().As<v8::Object>();
|
if (!entry.ToLocal(&object)) return v8::MaybeLocal<v8::Object>();
|
||||||
|
return scope.Escape(object.As<v8::Object>());
|
||||||
}
|
}
|
||||||
} // namespace v8_inspector
|
} // namespace v8_inspector
|
||||||
|
@ -36,13 +36,12 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "include/v8-inspector.h"
|
||||||
#include "src/base/macros.h"
|
#include "src/base/macros.h"
|
||||||
#include "src/base/platform/mutex.h"
|
#include "src/base/platform/mutex.h"
|
||||||
#include "src/inspector/injected-script.h"
|
#include "src/inspector/injected-script.h"
|
||||||
#include "src/inspector/protocol/Protocol.h"
|
#include "src/inspector/protocol/Protocol.h"
|
||||||
|
|
||||||
#include "include/v8-inspector.h"
|
|
||||||
|
|
||||||
namespace v8_inspector {
|
namespace v8_inspector {
|
||||||
|
|
||||||
class InspectedContext;
|
class InspectedContext;
|
||||||
@ -76,6 +75,7 @@ class V8InspectorImpl : public V8Inspector {
|
|||||||
const String16& code,
|
const String16& code,
|
||||||
const String16& fileName);
|
const String16& fileName);
|
||||||
v8::MaybeLocal<v8::Context> regexContext();
|
v8::MaybeLocal<v8::Context> regexContext();
|
||||||
|
v8::MaybeLocal<v8::Context> exceptionMetaDataContext();
|
||||||
|
|
||||||
// V8Inspector implementation.
|
// V8Inspector implementation.
|
||||||
std::unique_ptr<V8InspectorSession> connect(int contextGroupId,
|
std::unique_ptr<V8InspectorSession> connect(int contextGroupId,
|
||||||
@ -137,7 +137,7 @@ class V8InspectorImpl : public V8Inspector {
|
|||||||
const std::function<void(V8InspectorSessionImpl*)>& callback);
|
const std::function<void(V8InspectorSessionImpl*)>& callback);
|
||||||
int64_t generateUniqueId();
|
int64_t generateUniqueId();
|
||||||
v8::MaybeLocal<v8::Object> getAssociatedExceptionData(
|
v8::MaybeLocal<v8::Object> getAssociatedExceptionData(
|
||||||
v8::Local<v8::Context> context, v8::Local<v8::Value> exception);
|
v8::Local<v8::Value> exception);
|
||||||
|
|
||||||
class EvaluateScope {
|
class EvaluateScope {
|
||||||
public:
|
public:
|
||||||
@ -163,7 +163,8 @@ class V8InspectorImpl : public V8Inspector {
|
|||||||
V8InspectorClient* m_client;
|
V8InspectorClient* m_client;
|
||||||
std::unique_ptr<V8Debugger> m_debugger;
|
std::unique_ptr<V8Debugger> m_debugger;
|
||||||
v8::Global<v8::Context> m_regexContext;
|
v8::Global<v8::Context> m_regexContext;
|
||||||
v8::Global<v8::debug::WeakMap> m_excepetionMetaData;
|
v8::Global<v8::Context> m_exceptionMetaDataContext;
|
||||||
|
v8::Global<v8::debug::WeakMap> m_exceptionMetaData;
|
||||||
int m_capturingStackTracesCount;
|
int m_capturingStackTracesCount;
|
||||||
unsigned m_lastExceptionId;
|
unsigned m_lastExceptionId;
|
||||||
int m_lastContextId;
|
int m_lastContextId;
|
||||||
|
@ -475,6 +475,9 @@ class InspectorExtension : public IsolateData::SetupGlobalTask {
|
|||||||
inspector->Set(isolate, "setResourceNamePrefix",
|
inspector->Set(isolate, "setResourceNamePrefix",
|
||||||
v8::FunctionTemplate::New(
|
v8::FunctionTemplate::New(
|
||||||
isolate, &InspectorExtension::SetResourceNamePrefix));
|
isolate, &InspectorExtension::SetResourceNamePrefix));
|
||||||
|
inspector->Set(isolate, "newExceptionWithMetaData",
|
||||||
|
v8::FunctionTemplate::New(
|
||||||
|
isolate, &InspectorExtension::newExceptionWithMetaData));
|
||||||
global->Set(isolate, "inspector", inspector);
|
global->Set(isolate, "inspector", inspector);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -723,7 +726,6 @@ class InspectorExtension : public IsolateData::SetupGlobalTask {
|
|||||||
args.GetIsolate()->GetCurrentContext()->AllowCodeGenerationFromStrings(
|
args.GetIsolate()->GetCurrentContext()->AllowCodeGenerationFromStrings(
|
||||||
args[0].As<v8::Boolean>()->Value());
|
args[0].As<v8::Boolean>()->Value());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SetResourceNamePrefix(
|
static void SetResourceNamePrefix(
|
||||||
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||||
if (args.Length() != 1 || !args[0]->IsString()) {
|
if (args.Length() != 1 || !args[0]->IsString()) {
|
||||||
@ -734,6 +736,24 @@ class InspectorExtension : public IsolateData::SetupGlobalTask {
|
|||||||
IsolateData* data = IsolateData::FromContext(context);
|
IsolateData* data = IsolateData::FromContext(context);
|
||||||
data->SetResourceNamePrefix(v8::Local<v8::String>::Cast(args[0]));
|
data->SetResourceNamePrefix(v8::Local<v8::String>::Cast(args[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void newExceptionWithMetaData(
|
||||||
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||||
|
if (args.Length() != 3 || !args[0]->IsString() || !args[1]->IsString() ||
|
||||||
|
!args[2]->IsString()) {
|
||||||
|
FATAL(
|
||||||
|
"Internal error: newExceptionWithMetaData('message', 'key', "
|
||||||
|
"'value').");
|
||||||
|
}
|
||||||
|
v8::Isolate* isolate = args.GetIsolate();
|
||||||
|
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
||||||
|
IsolateData* data = IsolateData::FromContext(context);
|
||||||
|
|
||||||
|
auto error = v8::Exception::Error(args[0].As<v8::String>());
|
||||||
|
CHECK(data->AssociateExceptionData(error, args[1].As<v8::String>(),
|
||||||
|
args[2].As<v8::String>()));
|
||||||
|
args.GetReturnValue().Set(error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
int InspectorTestMain(int argc, char* argv[]) {
|
int InspectorTestMain(int argc, char* argv[]) {
|
||||||
|
@ -473,6 +473,13 @@ void IsolateData::SetResourceNamePrefix(v8::Local<v8::String> prefix) {
|
|||||||
resource_name_prefix_.Reset(isolate(), prefix);
|
resource_name_prefix_.Reset(isolate(), prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsolateData::AssociateExceptionData(v8::Local<v8::Value> exception,
|
||||||
|
v8::Local<v8::Name> key,
|
||||||
|
v8::Local<v8::Value> value) {
|
||||||
|
return inspector_->associateExceptionData(
|
||||||
|
this->isolate()->GetCurrentContext(), exception, key, value);
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class StringBufferImpl : public v8_inspector::StringBuffer {
|
class StringBufferImpl : public v8_inspector::StringBuffer {
|
||||||
public:
|
public:
|
||||||
|
@ -97,6 +97,9 @@ class IsolateData : public v8_inspector::V8InspectorClient {
|
|||||||
void FireContextDestroyed(v8::Local<v8::Context> context);
|
void FireContextDestroyed(v8::Local<v8::Context> context);
|
||||||
void FreeContext(v8::Local<v8::Context> context);
|
void FreeContext(v8::Local<v8::Context> context);
|
||||||
void SetResourceNamePrefix(v8::Local<v8::String> prefix);
|
void SetResourceNamePrefix(v8::Local<v8::String> prefix);
|
||||||
|
bool AssociateExceptionData(v8::Local<v8::Value> exception,
|
||||||
|
v8::Local<v8::Name> key,
|
||||||
|
v8::Local<v8::Value> value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static v8::MaybeLocal<v8::Module> ModuleResolveCallback(
|
static v8::MaybeLocal<v8::Module> ModuleResolveCallback(
|
||||||
|
105
test/inspector/runtime/exception-thrown-metadata-expected.txt
Normal file
105
test/inspector/runtime/exception-thrown-metadata-expected.txt
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
Check that exceptionThrown is supported by test runner.
|
||||||
|
{
|
||||||
|
method : Runtime.exceptionThrown
|
||||||
|
params : {
|
||||||
|
exceptionDetails : {
|
||||||
|
columnNumber : 0
|
||||||
|
exception : {
|
||||||
|
className : Error
|
||||||
|
description : Error: myerror at <anonymous>:1:17
|
||||||
|
objectId : <objectId>
|
||||||
|
preview : {
|
||||||
|
description : Error: myerror at <anonymous>:1:17
|
||||||
|
overflow : false
|
||||||
|
properties : [
|
||||||
|
[0] : {
|
||||||
|
name : stack
|
||||||
|
type : string
|
||||||
|
value : Error: myerror at <anonymous>:1:17
|
||||||
|
}
|
||||||
|
[1] : {
|
||||||
|
name : message
|
||||||
|
type : string
|
||||||
|
value : myerror
|
||||||
|
}
|
||||||
|
]
|
||||||
|
subtype : error
|
||||||
|
type : object
|
||||||
|
}
|
||||||
|
subtype : error
|
||||||
|
type : object
|
||||||
|
}
|
||||||
|
exceptionId : <exceptionId>
|
||||||
|
exceptionMetaData : {
|
||||||
|
foo : bar
|
||||||
|
}
|
||||||
|
executionContextId : <executionContextId>
|
||||||
|
lineNumber : 0
|
||||||
|
stackTrace : {
|
||||||
|
callFrames : [
|
||||||
|
[0] : {
|
||||||
|
columnNumber : 16
|
||||||
|
functionName :
|
||||||
|
lineNumber : 0
|
||||||
|
scriptId : <scriptId>
|
||||||
|
url :
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
text : Uncaught Error: myerror
|
||||||
|
}
|
||||||
|
timestamp : <timestamp>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
method : Runtime.exceptionThrown
|
||||||
|
params : {
|
||||||
|
exceptionDetails : {
|
||||||
|
columnNumber : 2
|
||||||
|
exception : {
|
||||||
|
className : Error
|
||||||
|
description : Error: myerror2 at <anonymous>:2:19
|
||||||
|
objectId : <objectId>
|
||||||
|
preview : {
|
||||||
|
description : Error: myerror2 at <anonymous>:2:19
|
||||||
|
overflow : false
|
||||||
|
properties : [
|
||||||
|
[0] : {
|
||||||
|
name : stack
|
||||||
|
type : string
|
||||||
|
value : Error: myerror2 at <anonymous>:2:19
|
||||||
|
}
|
||||||
|
[1] : {
|
||||||
|
name : message
|
||||||
|
type : string
|
||||||
|
value : myerror2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
subtype : error
|
||||||
|
type : object
|
||||||
|
}
|
||||||
|
subtype : error
|
||||||
|
type : object
|
||||||
|
}
|
||||||
|
exceptionId : <exceptionId>
|
||||||
|
exceptionMetaData : {
|
||||||
|
foo2 : bar2
|
||||||
|
}
|
||||||
|
executionContextId : <executionContextId>
|
||||||
|
lineNumber : 1
|
||||||
|
stackTrace : {
|
||||||
|
callFrames : [
|
||||||
|
[0] : {
|
||||||
|
columnNumber : 18
|
||||||
|
functionName :
|
||||||
|
lineNumber : 1
|
||||||
|
scriptId : <scriptId>
|
||||||
|
url :
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
text : Uncaught Error: myerror2
|
||||||
|
}
|
||||||
|
timestamp : <timestamp>
|
||||||
|
}
|
||||||
|
}
|
11
test/inspector/runtime/exception-thrown-metadata.js
Normal file
11
test/inspector/runtime/exception-thrown-metadata.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// Copyright 2021 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.
|
||||||
|
|
||||||
|
let {session, contextGroup, Protocol} = InspectorTest.start("Check that exceptionThrown is supported by test runner.")
|
||||||
|
|
||||||
|
Protocol.Runtime.enable();
|
||||||
|
Protocol.Runtime.onExceptionThrown(message => InspectorTest.logMessage(message));
|
||||||
|
contextGroup.addScript("throw inspector.newExceptionWithMetaData('myerror', 'foo', 'bar');");
|
||||||
|
Protocol.Runtime.evaluate({ expression: "setTimeout(() => { \n throw inspector.newExceptionWithMetaData('myerror2', 'foo2', 'bar2'); }, 0)" });
|
||||||
|
InspectorTest.waitForPendingTasks().then(InspectorTest.completeTest);
|
Loading…
Reference in New Issue
Block a user