1 /*
2 * Copyright (c) 2010-2011 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "src/inspector/v8-inspector-impl.h"
32
33 #include <vector>
34
35 #include "include/v8-context.h"
36 #include "include/v8-local-handle.h"
37 #include "include/v8-microtask-queue.h"
38 #include "include/v8-platform.h"
39 #include "src/base/platform/mutex.h"
40 #include "src/debug/debug-interface.h"
41 #include "src/inspector/inspected-context.h"
42 #include "src/inspector/string-util.h"
43 #include "src/inspector/v8-console-agent-impl.h"
44 #include "src/inspector/v8-console-message.h"
45 #include "src/inspector/v8-console.h"
46 #include "src/inspector/v8-debugger-agent-impl.h"
47 #include "src/inspector/v8-debugger-id.h"
48 #include "src/inspector/v8-debugger.h"
49 #include "src/inspector/v8-inspector-session-impl.h"
50 #include "src/inspector/v8-profiler-agent-impl.h"
51 #include "src/inspector/v8-runtime-agent-impl.h"
52 #include "src/inspector/v8-stack-trace-impl.h"
53 #include "src/inspector/value-mirror.h"
54
55 namespace v8_inspector {
56
create(v8::Isolate * isolate,V8InspectorClient * client)57 std::unique_ptr<V8Inspector> V8Inspector::create(v8::Isolate* isolate,
58 V8InspectorClient* client) {
59 return std::unique_ptr<V8Inspector>(new V8InspectorImpl(isolate, client));
60 }
61
V8InspectorImpl(v8::Isolate * isolate,V8InspectorClient * client)62 V8InspectorImpl::V8InspectorImpl(v8::Isolate* isolate,
63 V8InspectorClient* client)
64 : m_isolate(isolate),
65 m_client(client),
66 m_debugger(new V8Debugger(isolate, this)),
67 m_lastExceptionId(0),
68 m_lastContextId(0),
69 m_isolateId(generateUniqueId()) {
70 v8::debug::SetInspector(m_isolate, this);
71 v8::debug::SetConsoleDelegate(m_isolate, console());
72 }
73
~V8InspectorImpl()74 V8InspectorImpl::~V8InspectorImpl() {
75 v8::debug::SetInspector(m_isolate, nullptr);
76 v8::debug::SetConsoleDelegate(m_isolate, nullptr);
77 }
78
contextGroupId(v8::Local<v8::Context> context) const79 int V8InspectorImpl::contextGroupId(v8::Local<v8::Context> context) const {
80 return contextGroupId(InspectedContext::contextId(context));
81 }
82
contextGroupId(int contextId) const83 int V8InspectorImpl::contextGroupId(int contextId) const {
84 auto it = m_contextIdToGroupIdMap.find(contextId);
85 return it != m_contextIdToGroupIdMap.end() ? it->second : 0;
86 }
87
resolveUniqueContextId(internal::V8DebuggerId uniqueId) const88 int V8InspectorImpl::resolveUniqueContextId(
89 internal::V8DebuggerId uniqueId) const {
90 auto it = m_uniqueIdToContextId.find(uniqueId.pair());
91 return it == m_uniqueIdToContextId.end() ? 0 : it->second;
92 }
93
compileAndRunInternalScript(v8::Local<v8::Context> context,v8::Local<v8::String> source)94 v8::MaybeLocal<v8::Value> V8InspectorImpl::compileAndRunInternalScript(
95 v8::Local<v8::Context> context, v8::Local<v8::String> source) {
96 v8::Local<v8::UnboundScript> unboundScript;
97 if (!v8::debug::CompileInspectorScript(m_isolate, source)
98 .ToLocal(&unboundScript))
99 return v8::MaybeLocal<v8::Value>();
100 v8::MicrotasksScope microtasksScope(m_isolate,
101 v8::MicrotasksScope::kDoNotRunMicrotasks);
102 v8::Context::Scope contextScope(context);
103 v8::Isolate::SafeForTerminationScope allowTermination(m_isolate);
104 return unboundScript->BindToCurrentContext()->Run(context);
105 }
106
compileScript(v8::Local<v8::Context> context,const String16 & code,const String16 & fileName)107 v8::MaybeLocal<v8::Script> V8InspectorImpl::compileScript(
108 v8::Local<v8::Context> context, const String16& code,
109 const String16& fileName) {
110 v8::ScriptOrigin origin(m_isolate, toV8String(m_isolate, fileName), 0, 0,
111 false);
112 v8::ScriptCompiler::Source source(toV8String(m_isolate, code), origin);
113 return v8::ScriptCompiler::Compile(context, &source,
114 v8::ScriptCompiler::kNoCompileOptions);
115 }
116
muteExceptions(int contextGroupId)117 void V8InspectorImpl::muteExceptions(int contextGroupId) {
118 m_muteExceptionsMap[contextGroupId]++;
119 }
120
unmuteExceptions(int contextGroupId)121 void V8InspectorImpl::unmuteExceptions(int contextGroupId) {
122 m_muteExceptionsMap[contextGroupId]--;
123 }
124
ensureConsoleMessageStorage(int contextGroupId)125 V8ConsoleMessageStorage* V8InspectorImpl::ensureConsoleMessageStorage(
126 int contextGroupId) {
127 auto storageIt = m_consoleStorageMap.find(contextGroupId);
128 if (storageIt == m_consoleStorageMap.end())
129 storageIt = m_consoleStorageMap
130 .insert(std::make_pair(
131 contextGroupId,
132 std::unique_ptr<V8ConsoleMessageStorage>(
133 new V8ConsoleMessageStorage(this, contextGroupId))))
134 .first;
135 return storageIt->second.get();
136 }
137
hasConsoleMessageStorage(int contextGroupId)138 bool V8InspectorImpl::hasConsoleMessageStorage(int contextGroupId) {
139 auto storageIt = m_consoleStorageMap.find(contextGroupId);
140 return storageIt != m_consoleStorageMap.end();
141 }
142
createStackTrace(v8::Local<v8::StackTrace> stackTrace)143 std::unique_ptr<V8StackTrace> V8InspectorImpl::createStackTrace(
144 v8::Local<v8::StackTrace> stackTrace) {
145 return m_debugger->createStackTrace(stackTrace);
146 }
147
connect(int contextGroupId,V8Inspector::Channel * channel,StringView state)148 std::unique_ptr<V8InspectorSession> V8InspectorImpl::connect(
149 int contextGroupId, V8Inspector::Channel* channel, StringView state) {
150 int sessionId = ++m_lastSessionId;
151 std::unique_ptr<V8InspectorSessionImpl> session =
152 V8InspectorSessionImpl::create(this, contextGroupId, sessionId, channel,
153 state);
154 m_sessions[contextGroupId][sessionId] = session.get();
155 return std::move(session);
156 }
157
disconnect(V8InspectorSessionImpl * session)158 void V8InspectorImpl::disconnect(V8InspectorSessionImpl* session) {
159 auto& map = m_sessions[session->contextGroupId()];
160 map.erase(session->sessionId());
161 if (map.empty()) m_sessions.erase(session->contextGroupId());
162 }
163
getContext(int groupId,int contextId) const164 InspectedContext* V8InspectorImpl::getContext(int groupId,
165 int contextId) const {
166 if (!groupId || !contextId) return nullptr;
167
168 auto contextGroupIt = m_contexts.find(groupId);
169 if (contextGroupIt == m_contexts.end()) return nullptr;
170
171 auto contextIt = contextGroupIt->second->find(contextId);
172 if (contextIt == contextGroupIt->second->end()) return nullptr;
173
174 return contextIt->second.get();
175 }
176
getContext(int contextId) const177 InspectedContext* V8InspectorImpl::getContext(int contextId) const {
178 return getContext(contextGroupId(contextId), contextId);
179 }
180
contextById(int contextId)181 v8::MaybeLocal<v8::Context> V8InspectorImpl::contextById(int contextId) {
182 InspectedContext* context = getContext(contextId);
183 return context ? context->context() : v8::MaybeLocal<v8::Context>();
184 }
185
uniqueDebuggerId(int contextId)186 V8DebuggerId V8InspectorImpl::uniqueDebuggerId(int contextId) {
187 InspectedContext* context = getContext(contextId);
188 internal::V8DebuggerId unique_id;
189 if (context) unique_id = context->uniqueId();
190 return unique_id.toV8DebuggerId();
191 }
192
contextCreated(const V8ContextInfo & info)193 void V8InspectorImpl::contextCreated(const V8ContextInfo& info) {
194 int contextId = ++m_lastContextId;
195 auto* context = new InspectedContext(this, info, contextId);
196 m_contextIdToGroupIdMap[contextId] = info.contextGroupId;
197
198 DCHECK(m_uniqueIdToContextId.find(context->uniqueId().pair()) ==
199 m_uniqueIdToContextId.end());
200 m_uniqueIdToContextId.insert(
201 std::make_pair(context->uniqueId().pair(), contextId));
202
203 auto contextIt = m_contexts.find(info.contextGroupId);
204 if (contextIt == m_contexts.end())
205 contextIt = m_contexts
206 .insert(std::make_pair(
207 info.contextGroupId,
208 std::unique_ptr<ContextByIdMap>(new ContextByIdMap())))
209 .first;
210 const auto& contextById = contextIt->second;
211
212 DCHECK(contextById->find(contextId) == contextById->cend());
213 (*contextById)[contextId].reset(context);
214 forEachSession(
215 info.contextGroupId, [&context](V8InspectorSessionImpl* session) {
216 session->runtimeAgent()->addBindings(context);
217 session->runtimeAgent()->reportExecutionContextCreated(context);
218 });
219 }
220
contextDestroyed(v8::Local<v8::Context> context)221 void V8InspectorImpl::contextDestroyed(v8::Local<v8::Context> context) {
222 int contextId = InspectedContext::contextId(context);
223 int groupId = contextGroupId(context);
224 contextCollected(groupId, contextId);
225 }
226
contextCollected(int groupId,int contextId)227 void V8InspectorImpl::contextCollected(int groupId, int contextId) {
228 m_contextIdToGroupIdMap.erase(contextId);
229
230 auto storageIt = m_consoleStorageMap.find(groupId);
231 if (storageIt != m_consoleStorageMap.end())
232 storageIt->second->contextDestroyed(contextId);
233
234 InspectedContext* inspectedContext = getContext(groupId, contextId);
235 if (!inspectedContext) return;
236
237 forEachSession(groupId, [&inspectedContext](V8InspectorSessionImpl* session) {
238 session->runtimeAgent()->reportExecutionContextDestroyed(inspectedContext);
239 });
240 discardInspectedContext(groupId, contextId);
241 }
242
resetContextGroup(int contextGroupId)243 void V8InspectorImpl::resetContextGroup(int contextGroupId) {
244 m_consoleStorageMap.erase(contextGroupId);
245 m_muteExceptionsMap.erase(contextGroupId);
246 auto contextsIt = m_contexts.find(contextGroupId);
247 // Context might have been removed already by discardContextScript()
248 if (contextsIt != m_contexts.end()) {
249 for (const auto& map_entry : *contextsIt->second)
250 m_uniqueIdToContextId.erase(map_entry.second->uniqueId().pair());
251 m_contexts.erase(contextsIt);
252 }
253 forEachSession(contextGroupId,
254 [](V8InspectorSessionImpl* session) { session->reset(); });
255 }
256
idleStarted()257 void V8InspectorImpl::idleStarted() { m_isolate->SetIdle(true); }
258
idleFinished()259 void V8InspectorImpl::idleFinished() { m_isolate->SetIdle(false); }
260
exceptionThrown(v8::Local<v8::Context> context,StringView message,v8::Local<v8::Value> exception,StringView detailedMessage,StringView url,unsigned lineNumber,unsigned columnNumber,std::unique_ptr<V8StackTrace> stackTrace,int scriptId)261 unsigned V8InspectorImpl::exceptionThrown(
262 v8::Local<v8::Context> context, StringView message,
263 v8::Local<v8::Value> exception, StringView detailedMessage, StringView url,
264 unsigned lineNumber, unsigned columnNumber,
265 std::unique_ptr<V8StackTrace> stackTrace, int scriptId) {
266 int groupId = contextGroupId(context);
267 if (!groupId || m_muteExceptionsMap[groupId]) return 0;
268 std::unique_ptr<V8StackTraceImpl> stackTraceImpl(
269 static_cast<V8StackTraceImpl*>(stackTrace.release()));
270 unsigned exceptionId = nextExceptionId();
271 std::unique_ptr<V8ConsoleMessage> consoleMessage =
272 V8ConsoleMessage::createForException(
273 m_client->currentTimeMS(), toString16(detailedMessage),
274 toString16(url), lineNumber, columnNumber, std::move(stackTraceImpl),
275 scriptId, m_isolate, toString16(message),
276 InspectedContext::contextId(context), exception, exceptionId);
277 ensureConsoleMessageStorage(groupId)->addMessage(std::move(consoleMessage));
278 return exceptionId;
279 }
280
exceptionRevoked(v8::Local<v8::Context> context,unsigned exceptionId,StringView message)281 void V8InspectorImpl::exceptionRevoked(v8::Local<v8::Context> context,
282 unsigned exceptionId,
283 StringView message) {
284 int groupId = contextGroupId(context);
285 if (!groupId) return;
286
287 std::unique_ptr<V8ConsoleMessage> consoleMessage =
288 V8ConsoleMessage::createForRevokedException(
289 m_client->currentTimeMS(), toString16(message), exceptionId);
290 ensureConsoleMessageStorage(groupId)->addMessage(std::move(consoleMessage));
291 }
292
captureStackTrace(bool fullStack)293 std::unique_ptr<V8StackTrace> V8InspectorImpl::captureStackTrace(
294 bool fullStack) {
295 return m_debugger->captureStackTrace(fullStack);
296 }
297
storeCurrentStackTrace(StringView description)298 V8StackTraceId V8InspectorImpl::storeCurrentStackTrace(StringView description) {
299 return m_debugger->storeCurrentStackTrace(description);
300 }
301
externalAsyncTaskStarted(const V8StackTraceId & parent)302 void V8InspectorImpl::externalAsyncTaskStarted(const V8StackTraceId& parent) {
303 m_debugger->externalAsyncTaskStarted(parent);
304 }
305
externalAsyncTaskFinished(const V8StackTraceId & parent)306 void V8InspectorImpl::externalAsyncTaskFinished(const V8StackTraceId& parent) {
307 m_debugger->externalAsyncTaskFinished(parent);
308 }
309
asyncTaskScheduled(StringView taskName,void * task,bool recurring)310 void V8InspectorImpl::asyncTaskScheduled(StringView taskName, void* task,
311 bool recurring) {
312 if (!task) return;
313 m_debugger->asyncTaskScheduled(taskName, task, recurring);
314 }
315
asyncTaskCanceled(void * task)316 void V8InspectorImpl::asyncTaskCanceled(void* task) {
317 if (!task) return;
318 m_debugger->asyncTaskCanceled(task);
319 }
320
asyncTaskStarted(void * task)321 void V8InspectorImpl::asyncTaskStarted(void* task) {
322 if (!task) return;
323 m_debugger->asyncTaskStarted(task);
324 }
325
asyncTaskFinished(void * task)326 void V8InspectorImpl::asyncTaskFinished(void* task) {
327 if (!task) return;
328 m_debugger->asyncTaskFinished(task);
329 }
330
allAsyncTasksCanceled()331 void V8InspectorImpl::allAsyncTasksCanceled() {
332 m_debugger->allAsyncTasksCanceled();
333 }
334
regexContext()335 v8::MaybeLocal<v8::Context> V8InspectorImpl::regexContext() {
336 if (m_regexContext.IsEmpty()) {
337 m_regexContext.Reset(m_isolate, v8::Context::New(m_isolate));
338 if (m_regexContext.IsEmpty()) {
339 DCHECK(m_isolate->IsExecutionTerminating());
340 return {};
341 }
342 }
343 return m_regexContext.Get(m_isolate);
344 }
345
exceptionMetaDataContext()346 v8::MaybeLocal<v8::Context> V8InspectorImpl::exceptionMetaDataContext() {
347 if (m_exceptionMetaDataContext.IsEmpty()) {
348 m_exceptionMetaDataContext.Reset(m_isolate, v8::Context::New(m_isolate));
349 if (m_exceptionMetaDataContext.IsEmpty()) {
350 DCHECK(m_isolate->IsExecutionTerminating());
351 return {};
352 }
353 }
354 return m_exceptionMetaDataContext.Get(m_isolate);
355 }
356
discardInspectedContext(int contextGroupId,int contextId)357 void V8InspectorImpl::discardInspectedContext(int contextGroupId,
358 int contextId) {
359 auto* context = getContext(contextGroupId, contextId);
360 if (!context) return;
361 m_uniqueIdToContextId.erase(context->uniqueId().pair());
362 m_contexts[contextGroupId]->erase(contextId);
363 if (m_contexts[contextGroupId]->empty()) m_contexts.erase(contextGroupId);
364 }
365
sessionById(int contextGroupId,int sessionId)366 V8InspectorSessionImpl* V8InspectorImpl::sessionById(int contextGroupId,
367 int sessionId) {
368 auto it = m_sessions.find(contextGroupId);
369 if (it == m_sessions.end()) return nullptr;
370 auto it2 = it->second.find(sessionId);
371 return it2 == it->second.end() ? nullptr : it2->second;
372 }
373
console()374 V8Console* V8InspectorImpl::console() {
375 if (!m_console) m_console.reset(new V8Console(this));
376 return m_console.get();
377 }
378
forEachContext(int contextGroupId,const std::function<void (InspectedContext *)> & callback)379 void V8InspectorImpl::forEachContext(
380 int contextGroupId,
381 const std::function<void(InspectedContext*)>& callback) {
382 auto it = m_contexts.find(contextGroupId);
383 if (it == m_contexts.end()) return;
384 std::vector<int> ids;
385 ids.reserve(it->second->size());
386 for (auto& contextIt : *(it->second)) ids.push_back(contextIt.first);
387
388 // Retrieve by ids each time since |callback| may destroy some contexts.
389 for (auto& contextId : ids) {
390 it = m_contexts.find(contextGroupId);
391 if (it == m_contexts.end()) continue;
392 auto contextIt = it->second->find(contextId);
393 if (contextIt != it->second->end()) callback(contextIt->second.get());
394 }
395 }
396
forEachSession(int contextGroupId,const std::function<void (V8InspectorSessionImpl *)> & callback)397 void V8InspectorImpl::forEachSession(
398 int contextGroupId,
399 const std::function<void(V8InspectorSessionImpl*)>& callback) {
400 auto it = m_sessions.find(contextGroupId);
401 if (it == m_sessions.end()) return;
402 std::vector<int> ids;
403 ids.reserve(it->second.size());
404 for (auto& sessionIt : it->second) ids.push_back(sessionIt.first);
405
406 // Retrieve by ids each time since |callback| may destroy some contexts.
407 for (auto& sessionId : ids) {
408 it = m_sessions.find(contextGroupId);
409 if (it == m_sessions.end()) continue;
410 auto sessionIt = it->second.find(sessionId);
411 if (sessionIt != it->second.end()) callback(sessionIt->second);
412 }
413 }
414
generateUniqueId()415 int64_t V8InspectorImpl::generateUniqueId() {
416 int64_t id = m_client->generateUniqueId();
417 if (!id) id = v8::debug::GetNextRandomInt64(m_isolate);
418 if (!id) id = 1;
419 return id;
420 }
421
EvaluateScope(const InjectedScript::Scope & scope)422 V8InspectorImpl::EvaluateScope::EvaluateScope(
423 const InjectedScript::Scope& scope)
424 : m_scope(scope),
425 m_isolate(scope.inspector()->isolate()),
426 m_safeForTerminationScope(m_isolate) {}
427
428 struct V8InspectorImpl::EvaluateScope::CancelToken {
429 v8::base::Mutex m_mutex;
430 bool m_canceled = false;
431 };
432
~EvaluateScope()433 V8InspectorImpl::EvaluateScope::~EvaluateScope() {
434 if (m_scope.tryCatch().HasTerminated()) {
435 m_scope.inspector()->debugger()->reportTermination();
436 }
437 if (m_cancelToken) {
438 v8::base::MutexGuard lock(&m_cancelToken->m_mutex);
439 m_cancelToken->m_canceled = true;
440 m_isolate->CancelTerminateExecution();
441 }
442 }
443
444 class V8InspectorImpl::EvaluateScope::TerminateTask : public v8::Task {
445 public:
TerminateTask(v8::Isolate * isolate,std::shared_ptr<CancelToken> token)446 TerminateTask(v8::Isolate* isolate, std::shared_ptr<CancelToken> token)
447 : m_isolate(isolate), m_token(std::move(token)) {}
448
Run()449 void Run() override {
450 // CancelToken contains m_canceled bool which may be changed from main
451 // thread, so lock mutex first.
452 v8::base::MutexGuard lock(&m_token->m_mutex);
453 if (m_token->m_canceled) return;
454 m_isolate->TerminateExecution();
455 }
456
457 private:
458 v8::Isolate* m_isolate;
459 std::shared_ptr<CancelToken> m_token;
460 };
461
setTimeout(double timeout)462 protocol::Response V8InspectorImpl::EvaluateScope::setTimeout(double timeout) {
463 if (m_isolate->IsExecutionTerminating()) {
464 return protocol::Response::ServerError("Execution was terminated");
465 }
466 m_cancelToken.reset(new CancelToken());
467 v8::debug::GetCurrentPlatform()->CallDelayedOnWorkerThread(
468 std::make_unique<TerminateTask>(m_isolate, m_cancelToken), timeout);
469 return protocol::Response::Success();
470 }
471
associateExceptionData(v8::Local<v8::Context>,v8::Local<v8::Value> exception,v8::Local<v8::Name> key,v8::Local<v8::Value> value)472 bool V8InspectorImpl::associateExceptionData(v8::Local<v8::Context>,
473 v8::Local<v8::Value> exception,
474 v8::Local<v8::Name> key,
475 v8::Local<v8::Value> value) {
476 if (!exception->IsObject()) {
477 return false;
478 }
479 v8::Local<v8::Context> context;
480 if (!exceptionMetaDataContext().ToLocal(&context)) return false;
481 v8::TryCatch tryCatch(m_isolate);
482 v8::Context::Scope contextScope(context);
483 v8::HandleScope handles(m_isolate);
484 if (m_exceptionMetaData.IsEmpty())
485 m_exceptionMetaData.Reset(m_isolate,
486 v8::debug::EphemeronTable::New(m_isolate));
487
488 v8::Local<v8::debug::EphemeronTable> map = m_exceptionMetaData.Get(m_isolate);
489 v8::MaybeLocal<v8::Value> entry = map->Get(m_isolate, exception);
490 v8::Local<v8::Object> object;
491 if (entry.IsEmpty() || !entry.ToLocalChecked()->IsObject()) {
492 object =
493 v8::Object::New(m_isolate, v8::Null(m_isolate), nullptr, nullptr, 0);
494 m_exceptionMetaData.Reset(m_isolate,
495 map->Set(m_isolate, exception, object));
496 } else {
497 object = entry.ToLocalChecked().As<v8::Object>();
498 }
499 CHECK(object->IsObject());
500 v8::Maybe<bool> result = object->CreateDataProperty(context, key, value);
501 return result.FromMaybe(false);
502 }
503
getAssociatedExceptionData(v8::Local<v8::Value> exception)504 v8::MaybeLocal<v8::Object> V8InspectorImpl::getAssociatedExceptionData(
505 v8::Local<v8::Value> exception) {
506 if (!exception->IsObject()) {
507 return v8::MaybeLocal<v8::Object>();
508 }
509 v8::EscapableHandleScope scope(m_isolate);
510 v8::Local<v8::Context> context;
511 if (m_exceptionMetaData.IsEmpty() ||
512 !exceptionMetaDataContext().ToLocal(&context)) {
513 return v8::MaybeLocal<v8::Object>();
514 }
515 v8::Local<v8::debug::EphemeronTable> map = m_exceptionMetaData.Get(m_isolate);
516 auto entry = map->Get(m_isolate, exception);
517 v8::Local<v8::Value> object;
518 if (!entry.ToLocal(&object) || !object->IsObject())
519 return v8::MaybeLocal<v8::Object>();
520 return scope.Escape(object.As<v8::Object>());
521 }
522
523 std::unique_ptr<protocol::DictionaryValue>
getAssociatedExceptionDataForProtocol(v8::Local<v8::Value> exception)524 V8InspectorImpl::getAssociatedExceptionDataForProtocol(
525 v8::Local<v8::Value> exception) {
526 v8::MaybeLocal<v8::Object> maybeData = getAssociatedExceptionData(exception);
527 v8::Local<v8::Object> data;
528 if (!maybeData.ToLocal(&data)) return nullptr;
529
530 v8::Local<v8::Context> context;
531 if (!exceptionMetaDataContext().ToLocal(&context)) return nullptr;
532
533 v8::TryCatch tryCatch(m_isolate);
534 v8::MicrotasksScope microtasksScope(m_isolate,
535 v8::MicrotasksScope::kDoNotRunMicrotasks);
536 v8::Context::Scope contextScope(context);
537 std::unique_ptr<protocol::DictionaryValue> jsonObject;
538 objectToProtocolValue(context, data, 2, &jsonObject);
539 return jsonObject;
540 }
541
542 } // namespace v8_inspector
543