Home | History | Annotate | Download | only in inspector
      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 "src/inspector/inspected-context.h"
     34 #include "src/inspector/string-util.h"
     35 #include "src/inspector/v8-console-agent-impl.h"
     36 #include "src/inspector/v8-console-message.h"
     37 #include "src/inspector/v8-debugger-agent-impl.h"
     38 #include "src/inspector/v8-debugger.h"
     39 #include "src/inspector/v8-inspector-session-impl.h"
     40 #include "src/inspector/v8-profiler-agent-impl.h"
     41 #include "src/inspector/v8-runtime-agent-impl.h"
     42 #include "src/inspector/v8-stack-trace-impl.h"
     43 
     44 namespace v8_inspector {
     45 
     46 std::unique_ptr<V8Inspector> V8Inspector::create(v8::Isolate* isolate,
     47                                                  V8InspectorClient* client) {
     48   return std::unique_ptr<V8Inspector>(new V8InspectorImpl(isolate, client));
     49 }
     50 
     51 V8InspectorImpl::V8InspectorImpl(v8::Isolate* isolate,
     52                                  V8InspectorClient* client)
     53     : m_isolate(isolate),
     54       m_client(client),
     55       m_debugger(new V8Debugger(isolate, this)),
     56       m_capturingStackTracesCount(0),
     57       m_lastExceptionId(0),
     58       m_lastContextId(0) {}
     59 
     60 V8InspectorImpl::~V8InspectorImpl() {}
     61 
     62 int V8InspectorImpl::contextGroupId(v8::Local<v8::Context> context) {
     63   return contextGroupId(InspectedContext::contextId(context));
     64 }
     65 
     66 int V8InspectorImpl::contextGroupId(int contextId) {
     67   protocol::HashMap<int, int>::iterator it =
     68       m_contextIdToGroupIdMap.find(contextId);
     69   return it != m_contextIdToGroupIdMap.end() ? it->second : 0;
     70 }
     71 
     72 V8DebuggerAgentImpl* V8InspectorImpl::enabledDebuggerAgentForGroup(
     73     int contextGroupId) {
     74   V8InspectorSessionImpl* session = sessionForContextGroup(contextGroupId);
     75   V8DebuggerAgentImpl* agent = session ? session->debuggerAgent() : nullptr;
     76   return agent && agent->enabled() ? agent : nullptr;
     77 }
     78 
     79 V8RuntimeAgentImpl* V8InspectorImpl::enabledRuntimeAgentForGroup(
     80     int contextGroupId) {
     81   V8InspectorSessionImpl* session = sessionForContextGroup(contextGroupId);
     82   V8RuntimeAgentImpl* agent = session ? session->runtimeAgent() : nullptr;
     83   return agent && agent->enabled() ? agent : nullptr;
     84 }
     85 
     86 V8ProfilerAgentImpl* V8InspectorImpl::enabledProfilerAgentForGroup(
     87     int contextGroupId) {
     88   V8InspectorSessionImpl* session = sessionForContextGroup(contextGroupId);
     89   V8ProfilerAgentImpl* agent = session ? session->profilerAgent() : nullptr;
     90   return agent && agent->enabled() ? agent : nullptr;
     91 }
     92 
     93 v8::MaybeLocal<v8::Value> V8InspectorImpl::runCompiledScript(
     94     v8::Local<v8::Context> context, v8::Local<v8::Script> script) {
     95   v8::MicrotasksScope microtasksScope(m_isolate,
     96                                       v8::MicrotasksScope::kRunMicrotasks);
     97   int groupId = contextGroupId(context);
     98   if (V8DebuggerAgentImpl* agent = enabledDebuggerAgentForGroup(groupId))
     99     agent->willExecuteScript(script->GetUnboundScript()->GetId());
    100   v8::MaybeLocal<v8::Value> result = script->Run(context);
    101   // Get agent from the map again, since it could have detached during script
    102   // execution.
    103   if (V8DebuggerAgentImpl* agent = enabledDebuggerAgentForGroup(groupId))
    104     agent->didExecuteScript();
    105   return result;
    106 }
    107 
    108 v8::MaybeLocal<v8::Value> V8InspectorImpl::callFunction(
    109     v8::Local<v8::Function> function, v8::Local<v8::Context> context,
    110     v8::Local<v8::Value> receiver, int argc, v8::Local<v8::Value> info[]) {
    111   return callFunction(function, context, receiver, argc, info,
    112                       v8::MicrotasksScope::kRunMicrotasks);
    113 }
    114 
    115 v8::MaybeLocal<v8::Value> V8InspectorImpl::callInternalFunction(
    116     v8::Local<v8::Function> function, v8::Local<v8::Context> context,
    117     v8::Local<v8::Value> receiver, int argc, v8::Local<v8::Value> info[]) {
    118   return callFunction(function, context, receiver, argc, info,
    119                       v8::MicrotasksScope::kDoNotRunMicrotasks);
    120 }
    121 
    122 v8::MaybeLocal<v8::Value> V8InspectorImpl::callFunction(
    123     v8::Local<v8::Function> function, v8::Local<v8::Context> context,
    124     v8::Local<v8::Value> receiver, int argc, v8::Local<v8::Value> info[],
    125     v8::MicrotasksScope::Type runMicrotasks) {
    126   v8::MicrotasksScope microtasksScope(m_isolate, runMicrotasks);
    127   int groupId = contextGroupId(context);
    128   if (V8DebuggerAgentImpl* agent = enabledDebuggerAgentForGroup(groupId))
    129     agent->willExecuteScript(function->ScriptId());
    130   v8::MaybeLocal<v8::Value> result =
    131       function->Call(context, receiver, argc, info);
    132   // Get agent from the map again, since it could have detached during script
    133   // execution.
    134   if (V8DebuggerAgentImpl* agent = enabledDebuggerAgentForGroup(groupId))
    135     agent->didExecuteScript();
    136   return result;
    137 }
    138 
    139 v8::MaybeLocal<v8::Value> V8InspectorImpl::compileAndRunInternalScript(
    140     v8::Local<v8::Context> context, v8::Local<v8::String> source) {
    141   v8::Local<v8::UnboundScript> unboundScript;
    142   if (!v8::debug::CompileInspectorScript(m_isolate, source)
    143            .ToLocal(&unboundScript))
    144     return v8::MaybeLocal<v8::Value>();
    145   v8::MicrotasksScope microtasksScope(m_isolate,
    146                                       v8::MicrotasksScope::kDoNotRunMicrotasks);
    147   v8::Context::Scope contextScope(context);
    148   return unboundScript->BindToCurrentContext()->Run(context);
    149 }
    150 
    151 v8::MaybeLocal<v8::Script> V8InspectorImpl::compileScript(
    152     v8::Local<v8::Context> context, const String16& code,
    153     const String16& fileName) {
    154   v8::ScriptOrigin origin(
    155       toV8String(m_isolate, fileName), v8::Integer::New(m_isolate, 0),
    156       v8::Integer::New(m_isolate, 0),
    157       v8::False(m_isolate),                                         // sharable
    158       v8::Local<v8::Integer>(), toV8String(m_isolate, String16()),  // sourceMap
    159       v8::True(m_isolate));  // opaqueresource
    160   v8::ScriptCompiler::Source source(toV8String(m_isolate, code), origin);
    161   return v8::ScriptCompiler::Compile(context, &source,
    162                                      v8::ScriptCompiler::kNoCompileOptions);
    163 }
    164 
    165 void V8InspectorImpl::enableStackCapturingIfNeeded() {
    166   if (!m_capturingStackTracesCount)
    167     V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions(m_isolate,
    168                                                                 true);
    169   ++m_capturingStackTracesCount;
    170 }
    171 
    172 void V8InspectorImpl::disableStackCapturingIfNeeded() {
    173   if (!(--m_capturingStackTracesCount))
    174     V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions(m_isolate,
    175                                                                 false);
    176 }
    177 
    178 void V8InspectorImpl::muteExceptions(int contextGroupId) {
    179   m_muteExceptionsMap[contextGroupId]++;
    180 }
    181 
    182 void V8InspectorImpl::unmuteExceptions(int contextGroupId) {
    183   m_muteExceptionsMap[contextGroupId]--;
    184 }
    185 
    186 V8ConsoleMessageStorage* V8InspectorImpl::ensureConsoleMessageStorage(
    187     int contextGroupId) {
    188   ConsoleStorageMap::iterator storageIt =
    189       m_consoleStorageMap.find(contextGroupId);
    190   if (storageIt == m_consoleStorageMap.end())
    191     storageIt = m_consoleStorageMap
    192                     .insert(std::make_pair(
    193                         contextGroupId,
    194                         std::unique_ptr<V8ConsoleMessageStorage>(
    195                             new V8ConsoleMessageStorage(this, contextGroupId))))
    196                     .first;
    197   return storageIt->second.get();
    198 }
    199 
    200 bool V8InspectorImpl::hasConsoleMessageStorage(int contextGroupId) {
    201   ConsoleStorageMap::iterator storageIt =
    202       m_consoleStorageMap.find(contextGroupId);
    203   return storageIt != m_consoleStorageMap.end();
    204 }
    205 
    206 std::unique_ptr<V8StackTrace> V8InspectorImpl::createStackTrace(
    207     v8::Local<v8::StackTrace> stackTrace) {
    208   return m_debugger->createStackTrace(stackTrace);
    209 }
    210 
    211 std::unique_ptr<V8InspectorSession> V8InspectorImpl::connect(
    212     int contextGroupId, V8Inspector::Channel* channel,
    213     const StringView& state) {
    214   DCHECK(m_sessions.find(contextGroupId) == m_sessions.cend());
    215   std::unique_ptr<V8InspectorSessionImpl> session =
    216       V8InspectorSessionImpl::create(this, contextGroupId, channel, state);
    217   m_sessions[contextGroupId] = session.get();
    218   return std::move(session);
    219 }
    220 
    221 void V8InspectorImpl::disconnect(V8InspectorSessionImpl* session) {
    222   DCHECK(m_sessions.find(session->contextGroupId()) != m_sessions.end());
    223   m_sessions.erase(session->contextGroupId());
    224 }
    225 
    226 InspectedContext* V8InspectorImpl::getContext(int groupId,
    227                                               int contextId) const {
    228   if (!groupId || !contextId) return nullptr;
    229 
    230   ContextsByGroupMap::const_iterator contextGroupIt = m_contexts.find(groupId);
    231   if (contextGroupIt == m_contexts.end()) return nullptr;
    232 
    233   ContextByIdMap::iterator contextIt = contextGroupIt->second->find(contextId);
    234   if (contextIt == contextGroupIt->second->end()) return nullptr;
    235 
    236   return contextIt->second.get();
    237 }
    238 
    239 void V8InspectorImpl::contextCreated(const V8ContextInfo& info) {
    240   int contextId = ++m_lastContextId;
    241   InspectedContext* context = new InspectedContext(this, info, contextId);
    242   m_contextIdToGroupIdMap[contextId] = info.contextGroupId;
    243 
    244   ContextsByGroupMap::iterator contextIt = m_contexts.find(info.contextGroupId);
    245   if (contextIt == m_contexts.end())
    246     contextIt = m_contexts
    247                     .insert(std::make_pair(
    248                         info.contextGroupId,
    249                         std::unique_ptr<ContextByIdMap>(new ContextByIdMap())))
    250                     .first;
    251   const auto& contextById = contextIt->second;
    252 
    253   DCHECK(contextById->find(contextId) == contextById->cend());
    254   (*contextById)[contextId].reset(context);
    255   SessionMap::iterator sessionIt = m_sessions.find(info.contextGroupId);
    256   if (sessionIt != m_sessions.end())
    257     sessionIt->second->runtimeAgent()->reportExecutionContextCreated(context);
    258 }
    259 
    260 void V8InspectorImpl::contextDestroyed(v8::Local<v8::Context> context) {
    261   int contextId = InspectedContext::contextId(context);
    262   int groupId = contextGroupId(context);
    263   m_contextIdToGroupIdMap.erase(contextId);
    264 
    265   ConsoleStorageMap::iterator storageIt = m_consoleStorageMap.find(groupId);
    266   if (storageIt != m_consoleStorageMap.end())
    267     storageIt->second->contextDestroyed(contextId);
    268 
    269   InspectedContext* inspectedContext = getContext(groupId, contextId);
    270   if (!inspectedContext) return;
    271 
    272   SessionMap::iterator iter = m_sessions.find(groupId);
    273   if (iter != m_sessions.end())
    274     iter->second->runtimeAgent()->reportExecutionContextDestroyed(
    275         inspectedContext);
    276   discardInspectedContext(groupId, contextId);
    277 }
    278 
    279 void V8InspectorImpl::resetContextGroup(int contextGroupId) {
    280   m_consoleStorageMap.erase(contextGroupId);
    281   m_muteExceptionsMap.erase(contextGroupId);
    282   SessionMap::iterator session = m_sessions.find(contextGroupId);
    283   if (session != m_sessions.end()) session->second->reset();
    284   m_contexts.erase(contextGroupId);
    285   m_debugger->wasmTranslation()->Clear();
    286 }
    287 
    288 void V8InspectorImpl::willExecuteScript(v8::Local<v8::Context> context,
    289                                         int scriptId) {
    290   if (V8DebuggerAgentImpl* agent =
    291           enabledDebuggerAgentForGroup(contextGroupId(context))) {
    292     agent->willExecuteScript(scriptId);
    293   }
    294 }
    295 
    296 void V8InspectorImpl::didExecuteScript(v8::Local<v8::Context> context) {
    297   if (V8DebuggerAgentImpl* agent =
    298           enabledDebuggerAgentForGroup(contextGroupId(context))) {
    299     agent->didExecuteScript();
    300   }
    301 }
    302 
    303 void V8InspectorImpl::idleStarted() {
    304   for (auto it = m_sessions.begin(); it != m_sessions.end(); ++it) {
    305     if (it->second->profilerAgent()->idleStarted()) return;
    306   }
    307 }
    308 
    309 void V8InspectorImpl::idleFinished() {
    310   for (auto it = m_sessions.begin(); it != m_sessions.end(); ++it) {
    311     if (it->second->profilerAgent()->idleFinished()) return;
    312   }
    313 }
    314 
    315 unsigned V8InspectorImpl::exceptionThrown(
    316     v8::Local<v8::Context> context, const StringView& message,
    317     v8::Local<v8::Value> exception, const StringView& detailedMessage,
    318     const StringView& url, unsigned lineNumber, unsigned columnNumber,
    319     std::unique_ptr<V8StackTrace> stackTrace, int scriptId) {
    320   int groupId = contextGroupId(context);
    321   if (!groupId || m_muteExceptionsMap[groupId]) return 0;
    322   std::unique_ptr<V8StackTraceImpl> stackTraceImpl(
    323       static_cast<V8StackTraceImpl*>(stackTrace.release()));
    324   unsigned exceptionId = nextExceptionId();
    325   std::unique_ptr<V8ConsoleMessage> consoleMessage =
    326       V8ConsoleMessage::createForException(
    327           m_client->currentTimeMS(), toString16(detailedMessage),
    328           toString16(url), lineNumber, columnNumber, std::move(stackTraceImpl),
    329           scriptId, m_isolate, toString16(message),
    330           InspectedContext::contextId(context), exception, exceptionId);
    331   ensureConsoleMessageStorage(groupId)->addMessage(std::move(consoleMessage));
    332   return exceptionId;
    333 }
    334 
    335 void V8InspectorImpl::exceptionRevoked(v8::Local<v8::Context> context,
    336                                        unsigned exceptionId,
    337                                        const StringView& message) {
    338   int groupId = contextGroupId(context);
    339   if (!groupId) return;
    340 
    341   std::unique_ptr<V8ConsoleMessage> consoleMessage =
    342       V8ConsoleMessage::createForRevokedException(
    343           m_client->currentTimeMS(), toString16(message), exceptionId);
    344   ensureConsoleMessageStorage(groupId)->addMessage(std::move(consoleMessage));
    345 }
    346 
    347 std::unique_ptr<V8StackTrace> V8InspectorImpl::captureStackTrace(
    348     bool fullStack) {
    349   return m_debugger->captureStackTrace(fullStack);
    350 }
    351 
    352 void V8InspectorImpl::asyncTaskScheduled(const StringView& taskName, void* task,
    353                                          bool recurring) {
    354   m_debugger->asyncTaskScheduled(taskName, task, recurring);
    355 }
    356 
    357 void V8InspectorImpl::asyncTaskCanceled(void* task) {
    358   m_debugger->asyncTaskCanceled(task);
    359 }
    360 
    361 void V8InspectorImpl::asyncTaskStarted(void* task) {
    362   m_debugger->asyncTaskStarted(task);
    363 }
    364 
    365 void V8InspectorImpl::asyncTaskFinished(void* task) {
    366   m_debugger->asyncTaskFinished(task);
    367 }
    368 
    369 void V8InspectorImpl::allAsyncTasksCanceled() {
    370   m_debugger->allAsyncTasksCanceled();
    371 }
    372 
    373 v8::Local<v8::Context> V8InspectorImpl::regexContext() {
    374   if (m_regexContext.IsEmpty())
    375     m_regexContext.Reset(m_isolate, v8::Context::New(m_isolate));
    376   return m_regexContext.Get(m_isolate);
    377 }
    378 
    379 void V8InspectorImpl::discardInspectedContext(int contextGroupId,
    380                                               int contextId) {
    381   if (!getContext(contextGroupId, contextId)) return;
    382   m_contexts[contextGroupId]->erase(contextId);
    383   if (m_contexts[contextGroupId]->empty()) m_contexts.erase(contextGroupId);
    384 }
    385 
    386 const V8InspectorImpl::ContextByIdMap* V8InspectorImpl::contextGroup(
    387     int contextGroupId) {
    388   ContextsByGroupMap::iterator iter = m_contexts.find(contextGroupId);
    389   return iter == m_contexts.end() ? nullptr : iter->second.get();
    390 }
    391 
    392 V8InspectorSessionImpl* V8InspectorImpl::sessionForContextGroup(
    393     int contextGroupId) {
    394   if (!contextGroupId) return nullptr;
    395   SessionMap::iterator iter = m_sessions.find(contextGroupId);
    396   return iter == m_sessions.end() ? nullptr : iter->second;
    397 }
    398 
    399 }  // namespace v8_inspector
    400