• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/inspector/v8-inspector-session-impl.h"
6 
7 #include "src/inspector/injected-script.h"
8 #include "src/inspector/inspected-context.h"
9 #include "src/inspector/protocol/Protocol.h"
10 #include "src/inspector/remote-object-id.h"
11 #include "src/inspector/search-util.h"
12 #include "src/inspector/string-util.h"
13 #include "src/inspector/v8-console-agent-impl.h"
14 #include "src/inspector/v8-debugger-agent-impl.h"
15 #include "src/inspector/v8-debugger.h"
16 #include "src/inspector/v8-heap-profiler-agent-impl.h"
17 #include "src/inspector/v8-inspector-impl.h"
18 #include "src/inspector/v8-profiler-agent-impl.h"
19 #include "src/inspector/v8-runtime-agent-impl.h"
20 #include "src/inspector/v8-schema-agent-impl.h"
21 
22 namespace v8_inspector {
23 
24 // static
canDispatchMethod(const StringView & method)25 bool V8InspectorSession::canDispatchMethod(const StringView& method) {
26   return stringViewStartsWith(method,
27                               protocol::Runtime::Metainfo::commandPrefix) ||
28          stringViewStartsWith(method,
29                               protocol::Debugger::Metainfo::commandPrefix) ||
30          stringViewStartsWith(method,
31                               protocol::Profiler::Metainfo::commandPrefix) ||
32          stringViewStartsWith(
33              method, protocol::HeapProfiler::Metainfo::commandPrefix) ||
34          stringViewStartsWith(method,
35                               protocol::Console::Metainfo::commandPrefix) ||
36          stringViewStartsWith(method,
37                               protocol::Schema::Metainfo::commandPrefix);
38 }
39 
40 // static
executionContextId(v8::Local<v8::Context> context)41 int V8ContextInfo::executionContextId(v8::Local<v8::Context> context) {
42   return InspectedContext::contextId(context);
43 }
44 
create(V8InspectorImpl * inspector,int contextGroupId,int sessionId,V8Inspector::Channel * channel,const StringView & state)45 std::unique_ptr<V8InspectorSessionImpl> V8InspectorSessionImpl::create(
46     V8InspectorImpl* inspector, int contextGroupId, int sessionId,
47     V8Inspector::Channel* channel, const StringView& state) {
48   return std::unique_ptr<V8InspectorSessionImpl>(new V8InspectorSessionImpl(
49       inspector, contextGroupId, sessionId, channel, state));
50 }
51 
V8InspectorSessionImpl(V8InspectorImpl * inspector,int contextGroupId,int sessionId,V8Inspector::Channel * channel,const StringView & savedState)52 V8InspectorSessionImpl::V8InspectorSessionImpl(V8InspectorImpl* inspector,
53                                                int contextGroupId,
54                                                int sessionId,
55                                                V8Inspector::Channel* channel,
56                                                const StringView& savedState)
57     : m_contextGroupId(contextGroupId),
58       m_sessionId(sessionId),
59       m_inspector(inspector),
60       m_channel(channel),
61       m_customObjectFormatterEnabled(false),
62       m_dispatcher(this),
63       m_state(nullptr),
64       m_runtimeAgent(nullptr),
65       m_debuggerAgent(nullptr),
66       m_heapProfilerAgent(nullptr),
67       m_profilerAgent(nullptr),
68       m_consoleAgent(nullptr),
69       m_schemaAgent(nullptr) {
70   if (savedState.length()) {
71     std::unique_ptr<protocol::Value> state =
72         protocol::StringUtil::parseJSON(toString16(savedState));
73     if (state) m_state = protocol::DictionaryValue::cast(std::move(state));
74     if (!m_state) m_state = protocol::DictionaryValue::create();
75   } else {
76     m_state = protocol::DictionaryValue::create();
77   }
78 
79   m_runtimeAgent.reset(new V8RuntimeAgentImpl(
80       this, this, agentState(protocol::Runtime::Metainfo::domainName)));
81   protocol::Runtime::Dispatcher::wire(&m_dispatcher, m_runtimeAgent.get());
82 
83   m_debuggerAgent.reset(new V8DebuggerAgentImpl(
84       this, this, agentState(protocol::Debugger::Metainfo::domainName)));
85   protocol::Debugger::Dispatcher::wire(&m_dispatcher, m_debuggerAgent.get());
86 
87   m_profilerAgent.reset(new V8ProfilerAgentImpl(
88       this, this, agentState(protocol::Profiler::Metainfo::domainName)));
89   protocol::Profiler::Dispatcher::wire(&m_dispatcher, m_profilerAgent.get());
90 
91   m_heapProfilerAgent.reset(new V8HeapProfilerAgentImpl(
92       this, this, agentState(protocol::HeapProfiler::Metainfo::domainName)));
93   protocol::HeapProfiler::Dispatcher::wire(&m_dispatcher,
94                                            m_heapProfilerAgent.get());
95 
96   m_consoleAgent.reset(new V8ConsoleAgentImpl(
97       this, this, agentState(protocol::Console::Metainfo::domainName)));
98   protocol::Console::Dispatcher::wire(&m_dispatcher, m_consoleAgent.get());
99 
100   m_schemaAgent.reset(new V8SchemaAgentImpl(
101       this, this, agentState(protocol::Schema::Metainfo::domainName)));
102   protocol::Schema::Dispatcher::wire(&m_dispatcher, m_schemaAgent.get());
103 
104   if (savedState.length()) {
105     m_runtimeAgent->restore();
106     m_debuggerAgent->restore();
107     m_heapProfilerAgent->restore();
108     m_profilerAgent->restore();
109     m_consoleAgent->restore();
110   }
111 }
112 
~V8InspectorSessionImpl()113 V8InspectorSessionImpl::~V8InspectorSessionImpl() {
114   discardInjectedScripts();
115   m_consoleAgent->disable();
116   m_profilerAgent->disable();
117   m_heapProfilerAgent->disable();
118   m_debuggerAgent->disable();
119   m_runtimeAgent->disable();
120   m_inspector->disconnect(this);
121 }
122 
agentState(const String16 & name)123 protocol::DictionaryValue* V8InspectorSessionImpl::agentState(
124     const String16& name) {
125   protocol::DictionaryValue* state = m_state->getObject(name);
126   if (!state) {
127     std::unique_ptr<protocol::DictionaryValue> newState =
128         protocol::DictionaryValue::create();
129     state = newState.get();
130     m_state->setObject(name, std::move(newState));
131   }
132   return state;
133 }
134 
135 namespace {
136 
137 class MessageBuffer : public StringBuffer {
138  public:
create(std::unique_ptr<protocol::Serializable> message)139   static std::unique_ptr<MessageBuffer> create(
140       std::unique_ptr<protocol::Serializable> message) {
141     return std::unique_ptr<MessageBuffer>(
142         new MessageBuffer(std::move(message)));
143   }
144 
string()145   const StringView& string() override {
146     if (!m_serialized) {
147       m_serialized = StringBuffer::create(toStringView(m_message->serialize()));
148       m_message.reset(nullptr);
149     }
150     return m_serialized->string();
151   }
152 
153  private:
MessageBuffer(std::unique_ptr<protocol::Serializable> message)154   explicit MessageBuffer(std::unique_ptr<protocol::Serializable> message)
155       : m_message(std::move(message)) {}
156 
157   std::unique_ptr<protocol::Serializable> m_message;
158   std::unique_ptr<StringBuffer> m_serialized;
159 };
160 
161 }  // namespace
162 
sendProtocolResponse(int callId,std::unique_ptr<protocol::Serializable> message)163 void V8InspectorSessionImpl::sendProtocolResponse(
164     int callId, std::unique_ptr<protocol::Serializable> message) {
165   m_channel->sendResponse(callId, MessageBuffer::create(std::move(message)));
166 }
167 
sendProtocolNotification(std::unique_ptr<protocol::Serializable> message)168 void V8InspectorSessionImpl::sendProtocolNotification(
169     std::unique_ptr<protocol::Serializable> message) {
170   m_channel->sendNotification(MessageBuffer::create(std::move(message)));
171 }
172 
fallThrough(int callId,const String16 & method,const String16 & message)173 void V8InspectorSessionImpl::fallThrough(int callId, const String16& method,
174                                          const String16& message) {
175   // There's no other layer to handle the command.
176   UNREACHABLE();
177 }
178 
flushProtocolNotifications()179 void V8InspectorSessionImpl::flushProtocolNotifications() {
180   m_channel->flushProtocolNotifications();
181 }
182 
reset()183 void V8InspectorSessionImpl::reset() {
184   m_debuggerAgent->reset();
185   m_runtimeAgent->reset();
186   discardInjectedScripts();
187 }
188 
discardInjectedScripts()189 void V8InspectorSessionImpl::discardInjectedScripts() {
190   m_inspectedObjects.clear();
191   int sessionId = m_sessionId;
192   m_inspector->forEachContext(m_contextGroupId,
193                               [&sessionId](InspectedContext* context) {
194                                 context->discardInjectedScript(sessionId);
195                               });
196 }
197 
findInjectedScript(int contextId,InjectedScript * & injectedScript)198 Response V8InspectorSessionImpl::findInjectedScript(
199     int contextId, InjectedScript*& injectedScript) {
200   injectedScript = nullptr;
201   InspectedContext* context =
202       m_inspector->getContext(m_contextGroupId, contextId);
203   if (!context) return Response::Error("Cannot find context with specified id");
204   injectedScript = context->getInjectedScript(m_sessionId);
205   if (!injectedScript) {
206     if (!context->createInjectedScript(m_sessionId)) {
207       if (m_inspector->isolate()->IsExecutionTerminating())
208         return Response::Error("Execution was terminated");
209       return Response::Error("Cannot access specified execution context");
210     }
211     injectedScript = context->getInjectedScript(m_sessionId);
212     if (m_customObjectFormatterEnabled)
213       injectedScript->setCustomObjectFormatterEnabled(true);
214   }
215   return Response::OK();
216 }
217 
findInjectedScript(RemoteObjectIdBase * objectId,InjectedScript * & injectedScript)218 Response V8InspectorSessionImpl::findInjectedScript(
219     RemoteObjectIdBase* objectId, InjectedScript*& injectedScript) {
220   return findInjectedScript(objectId->contextId(), injectedScript);
221 }
222 
releaseObjectGroup(const StringView & objectGroup)223 void V8InspectorSessionImpl::releaseObjectGroup(const StringView& objectGroup) {
224   releaseObjectGroup(toString16(objectGroup));
225 }
226 
releaseObjectGroup(const String16 & objectGroup)227 void V8InspectorSessionImpl::releaseObjectGroup(const String16& objectGroup) {
228   int sessionId = m_sessionId;
229   m_inspector->forEachContext(
230       m_contextGroupId, [&objectGroup, &sessionId](InspectedContext* context) {
231         InjectedScript* injectedScript = context->getInjectedScript(sessionId);
232         if (injectedScript) injectedScript->releaseObjectGroup(objectGroup);
233       });
234 }
235 
unwrapObject(std::unique_ptr<StringBuffer> * error,const StringView & objectId,v8::Local<v8::Value> * object,v8::Local<v8::Context> * context,std::unique_ptr<StringBuffer> * objectGroup)236 bool V8InspectorSessionImpl::unwrapObject(
237     std::unique_ptr<StringBuffer>* error, const StringView& objectId,
238     v8::Local<v8::Value>* object, v8::Local<v8::Context>* context,
239     std::unique_ptr<StringBuffer>* objectGroup) {
240   String16 objectGroupString;
241   Response response = unwrapObject(toString16(objectId), object, context,
242                                    objectGroup ? &objectGroupString : nullptr);
243   if (!response.isSuccess()) {
244     if (error) {
245       String16 errorMessage = response.errorMessage();
246       *error = StringBufferImpl::adopt(errorMessage);
247     }
248     return false;
249   }
250   if (objectGroup) *objectGroup = StringBufferImpl::adopt(objectGroupString);
251   return true;
252 }
253 
unwrapObject(const String16 & objectId,v8::Local<v8::Value> * object,v8::Local<v8::Context> * context,String16 * objectGroup)254 Response V8InspectorSessionImpl::unwrapObject(const String16& objectId,
255                                               v8::Local<v8::Value>* object,
256                                               v8::Local<v8::Context>* context,
257                                               String16* objectGroup) {
258   std::unique_ptr<RemoteObjectId> remoteId;
259   Response response = RemoteObjectId::parse(objectId, &remoteId);
260   if (!response.isSuccess()) return response;
261   InjectedScript* injectedScript = nullptr;
262   response = findInjectedScript(remoteId.get(), injectedScript);
263   if (!response.isSuccess()) return response;
264   response = injectedScript->findObject(*remoteId, object);
265   if (!response.isSuccess()) return response;
266   *context = injectedScript->context()->context();
267   if (objectGroup) *objectGroup = injectedScript->objectGroupName(*remoteId);
268   return Response::OK();
269 }
270 
271 std::unique_ptr<protocol::Runtime::API::RemoteObject>
wrapObject(v8::Local<v8::Context> context,v8::Local<v8::Value> value,const StringView & groupName,bool generatePreview)272 V8InspectorSessionImpl::wrapObject(v8::Local<v8::Context> context,
273                                    v8::Local<v8::Value> value,
274                                    const StringView& groupName,
275                                    bool generatePreview) {
276   return wrapObject(context, value, toString16(groupName), generatePreview);
277 }
278 
279 std::unique_ptr<protocol::Runtime::RemoteObject>
wrapObject(v8::Local<v8::Context> context,v8::Local<v8::Value> value,const String16 & groupName,bool generatePreview)280 V8InspectorSessionImpl::wrapObject(v8::Local<v8::Context> context,
281                                    v8::Local<v8::Value> value,
282                                    const String16& groupName,
283                                    bool generatePreview) {
284   InjectedScript* injectedScript = nullptr;
285   findInjectedScript(InspectedContext::contextId(context), injectedScript);
286   if (!injectedScript) return nullptr;
287   std::unique_ptr<protocol::Runtime::RemoteObject> result;
288   injectedScript->wrapObject(value, groupName, false, generatePreview, &result);
289   return result;
290 }
291 
292 std::unique_ptr<protocol::Runtime::RemoteObject>
wrapTable(v8::Local<v8::Context> context,v8::Local<v8::Value> table,v8::Local<v8::Value> columns)293 V8InspectorSessionImpl::wrapTable(v8::Local<v8::Context> context,
294                                   v8::Local<v8::Value> table,
295                                   v8::Local<v8::Value> columns) {
296   InjectedScript* injectedScript = nullptr;
297   findInjectedScript(InspectedContext::contextId(context), injectedScript);
298   if (!injectedScript) return nullptr;
299   return injectedScript->wrapTable(table, columns);
300 }
301 
setCustomObjectFormatterEnabled(bool enabled)302 void V8InspectorSessionImpl::setCustomObjectFormatterEnabled(bool enabled) {
303   m_customObjectFormatterEnabled = enabled;
304   int sessionId = m_sessionId;
305   m_inspector->forEachContext(
306       m_contextGroupId, [&enabled, &sessionId](InspectedContext* context) {
307         InjectedScript* injectedScript = context->getInjectedScript(sessionId);
308         if (injectedScript)
309           injectedScript->setCustomObjectFormatterEnabled(enabled);
310       });
311 }
312 
reportAllContexts(V8RuntimeAgentImpl * agent)313 void V8InspectorSessionImpl::reportAllContexts(V8RuntimeAgentImpl* agent) {
314   m_inspector->forEachContext(m_contextGroupId,
315                               [&agent](InspectedContext* context) {
316                                 agent->reportExecutionContextCreated(context);
317                               });
318 }
319 
dispatchProtocolMessage(const StringView & message)320 void V8InspectorSessionImpl::dispatchProtocolMessage(
321     const StringView& message) {
322   int callId;
323   String16 method;
324   std::unique_ptr<protocol::Value> parsedMessage =
325       protocol::StringUtil::parseJSON(message);
326   if (m_dispatcher.parseCommand(parsedMessage.get(), &callId, &method)) {
327     // Pass empty string instead of the actual message to save on a conversion.
328     // We're allowed to do so because fall-through is not implemented.
329     m_dispatcher.dispatch(callId, method, std::move(parsedMessage), "");
330   }
331 }
332 
stateJSON()333 std::unique_ptr<StringBuffer> V8InspectorSessionImpl::stateJSON() {
334   String16 json = m_state->serialize();
335   return StringBufferImpl::adopt(json);
336 }
337 
338 std::vector<std::unique_ptr<protocol::Schema::API::Domain>>
supportedDomains()339 V8InspectorSessionImpl::supportedDomains() {
340   std::vector<std::unique_ptr<protocol::Schema::Domain>> domains =
341       supportedDomainsImpl();
342   std::vector<std::unique_ptr<protocol::Schema::API::Domain>> result;
343   for (size_t i = 0; i < domains.size(); ++i)
344     result.push_back(std::move(domains[i]));
345   return result;
346 }
347 
348 std::vector<std::unique_ptr<protocol::Schema::Domain>>
supportedDomainsImpl()349 V8InspectorSessionImpl::supportedDomainsImpl() {
350   std::vector<std::unique_ptr<protocol::Schema::Domain>> result;
351   result.push_back(protocol::Schema::Domain::create()
352                        .setName(protocol::Runtime::Metainfo::domainName)
353                        .setVersion(protocol::Runtime::Metainfo::version)
354                        .build());
355   result.push_back(protocol::Schema::Domain::create()
356                        .setName(protocol::Debugger::Metainfo::domainName)
357                        .setVersion(protocol::Debugger::Metainfo::version)
358                        .build());
359   result.push_back(protocol::Schema::Domain::create()
360                        .setName(protocol::Profiler::Metainfo::domainName)
361                        .setVersion(protocol::Profiler::Metainfo::version)
362                        .build());
363   result.push_back(protocol::Schema::Domain::create()
364                        .setName(protocol::HeapProfiler::Metainfo::domainName)
365                        .setVersion(protocol::HeapProfiler::Metainfo::version)
366                        .build());
367   result.push_back(protocol::Schema::Domain::create()
368                        .setName(protocol::Schema::Metainfo::domainName)
369                        .setVersion(protocol::Schema::Metainfo::version)
370                        .build());
371   return result;
372 }
373 
addInspectedObject(std::unique_ptr<V8InspectorSession::Inspectable> inspectable)374 void V8InspectorSessionImpl::addInspectedObject(
375     std::unique_ptr<V8InspectorSession::Inspectable> inspectable) {
376   m_inspectedObjects.insert(m_inspectedObjects.begin(), std::move(inspectable));
377   if (m_inspectedObjects.size() > kInspectedObjectBufferSize)
378     m_inspectedObjects.resize(kInspectedObjectBufferSize);
379 }
380 
inspectedObject(unsigned num)381 V8InspectorSession::Inspectable* V8InspectorSessionImpl::inspectedObject(
382     unsigned num) {
383   if (num >= m_inspectedObjects.size()) return nullptr;
384   return m_inspectedObjects[num].get();
385 }
386 
schedulePauseOnNextStatement(const StringView & breakReason,const StringView & breakDetails)387 void V8InspectorSessionImpl::schedulePauseOnNextStatement(
388     const StringView& breakReason, const StringView& breakDetails) {
389   m_debuggerAgent->schedulePauseOnNextStatement(
390       toString16(breakReason),
391       protocol::DictionaryValue::cast(
392           protocol::StringUtil::parseJSON(breakDetails)));
393 }
394 
cancelPauseOnNextStatement()395 void V8InspectorSessionImpl::cancelPauseOnNextStatement() {
396   m_debuggerAgent->cancelPauseOnNextStatement();
397 }
398 
breakProgram(const StringView & breakReason,const StringView & breakDetails)399 void V8InspectorSessionImpl::breakProgram(const StringView& breakReason,
400                                           const StringView& breakDetails) {
401   m_debuggerAgent->breakProgram(
402       toString16(breakReason),
403       protocol::DictionaryValue::cast(
404           protocol::StringUtil::parseJSON(breakDetails)));
405 }
406 
setSkipAllPauses(bool skip)407 void V8InspectorSessionImpl::setSkipAllPauses(bool skip) {
408   m_debuggerAgent->setSkipAllPauses(skip);
409 }
410 
resume()411 void V8InspectorSessionImpl::resume() { m_debuggerAgent->resume(); }
412 
stepOver()413 void V8InspectorSessionImpl::stepOver() { m_debuggerAgent->stepOver(); }
414 
415 std::vector<std::unique_ptr<protocol::Debugger::API::SearchMatch>>
searchInTextByLines(const StringView & text,const StringView & query,bool caseSensitive,bool isRegex)416 V8InspectorSessionImpl::searchInTextByLines(const StringView& text,
417                                             const StringView& query,
418                                             bool caseSensitive, bool isRegex) {
419   // TODO(dgozman): search may operate on StringView and avoid copying |text|.
420   std::vector<std::unique_ptr<protocol::Debugger::SearchMatch>> matches =
421       searchInTextByLinesImpl(this, toString16(text), toString16(query),
422                               caseSensitive, isRegex);
423   std::vector<std::unique_ptr<protocol::Debugger::API::SearchMatch>> result;
424   for (size_t i = 0; i < matches.size(); ++i)
425     result.push_back(std::move(matches[i]));
426   return result;
427 }
428 
429 }  // namespace v8_inspector
430