• 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-console.h"
6 
7 #include "include/v8-container.h"
8 #include "include/v8-context.h"
9 #include "include/v8-function.h"
10 #include "include/v8-inspector.h"
11 #include "include/v8-microtask-queue.h"
12 #include "src/base/macros.h"
13 #include "src/inspector/injected-script.h"
14 #include "src/inspector/inspected-context.h"
15 #include "src/inspector/string-util.h"
16 #include "src/inspector/v8-console-message.h"
17 #include "src/inspector/v8-debugger-agent-impl.h"
18 #include "src/inspector/v8-inspector-impl.h"
19 #include "src/inspector/v8-inspector-session-impl.h"
20 #include "src/inspector/v8-profiler-agent-impl.h"
21 #include "src/inspector/v8-runtime-agent-impl.h"
22 #include "src/inspector/v8-stack-trace-impl.h"
23 #include "src/inspector/v8-value-utils.h"
24 #include "src/tracing/trace-event.h"
25 
26 namespace v8_inspector {
27 
28 namespace {
29 
consoleContextToString(v8::Isolate * isolate,const v8::debug::ConsoleContext & consoleContext)30 String16 consoleContextToString(
31     v8::Isolate* isolate, const v8::debug::ConsoleContext& consoleContext) {
32   if (consoleContext.id() == 0) return String16();
33   return toProtocolString(isolate, consoleContext.name()) + "#" +
34          String16::fromInteger(consoleContext.id());
35 }
36 
37 class ConsoleHelper {
38  public:
ConsoleHelper(const v8::debug::ConsoleCallArguments & info,const v8::debug::ConsoleContext & consoleContext,V8InspectorImpl * inspector)39   ConsoleHelper(const v8::debug::ConsoleCallArguments& info,
40                 const v8::debug::ConsoleContext& consoleContext,
41                 V8InspectorImpl* inspector)
42       : m_info(info),
43         m_consoleContext(consoleContext),
44         m_isolate(inspector->isolate()),
45         m_context(m_isolate->GetCurrentContext()),
46         m_inspector(inspector),
47         m_contextId(InspectedContext::contextId(m_context)),
48         m_groupId(m_inspector->contextGroupId(m_contextId)) {}
49 
50   ConsoleHelper(const ConsoleHelper&) = delete;
51   ConsoleHelper& operator=(const ConsoleHelper&) = delete;
52 
contextId() const53   int contextId() const { return m_contextId; }
groupId() const54   int groupId() const { return m_groupId; }
55 
injectedScript(int sessionId)56   InjectedScript* injectedScript(int sessionId) {
57     InspectedContext* context = m_inspector->getContext(m_groupId, m_contextId);
58     if (!context) return nullptr;
59     return context->getInjectedScript(sessionId);
60   }
61 
session(int sessionId)62   V8InspectorSessionImpl* session(int sessionId) {
63     return m_inspector->sessionById(m_groupId, sessionId);
64   }
65 
consoleMessageStorage()66   V8ConsoleMessageStorage* consoleMessageStorage() {
67     return m_inspector->ensureConsoleMessageStorage(m_groupId);
68   }
69 
reportCall(ConsoleAPIType type)70   void reportCall(ConsoleAPIType type) {
71     if (!m_info.Length()) return;
72     std::vector<v8::Local<v8::Value>> arguments;
73     arguments.reserve(m_info.Length());
74     for (int i = 0; i < m_info.Length(); ++i) arguments.push_back(m_info[i]);
75     reportCall(type, arguments);
76   }
77 
reportCallWithDefaultArgument(ConsoleAPIType type,const String16 & message)78   void reportCallWithDefaultArgument(ConsoleAPIType type,
79                                      const String16& message) {
80     std::vector<v8::Local<v8::Value>> arguments;
81     arguments.reserve(m_info.Length());
82     for (int i = 0; i < m_info.Length(); ++i) arguments.push_back(m_info[i]);
83     if (!m_info.Length()) arguments.push_back(toV8String(m_isolate, message));
84     reportCall(type, arguments);
85   }
86 
reportCallAndReplaceFirstArgument(ConsoleAPIType type,const String16 & message)87   void reportCallAndReplaceFirstArgument(ConsoleAPIType type,
88                                          const String16& message) {
89     std::vector<v8::Local<v8::Value>> arguments;
90     arguments.push_back(toV8String(m_isolate, message));
91     for (int i = 1; i < m_info.Length(); ++i) arguments.push_back(m_info[i]);
92     reportCall(type, arguments);
93   }
94 
reportCallWithArgument(ConsoleAPIType type,const String16 & message)95   void reportCallWithArgument(ConsoleAPIType type, const String16& message) {
96     std::vector<v8::Local<v8::Value>> arguments(1,
97                                                 toV8String(m_isolate, message));
98     reportCall(type, arguments);
99   }
100 
reportCall(ConsoleAPIType type,const std::vector<v8::Local<v8::Value>> & arguments)101   void reportCall(ConsoleAPIType type,
102                   const std::vector<v8::Local<v8::Value>>& arguments) {
103     if (!m_groupId) return;
104     std::unique_ptr<V8ConsoleMessage> message =
105         V8ConsoleMessage::createForConsoleAPI(
106             m_context, m_contextId, m_groupId, m_inspector,
107             m_inspector->client()->currentTimeMS(), type, arguments,
108             consoleContextToString(m_isolate, m_consoleContext),
109             m_inspector->debugger()->captureStackTrace(false));
110     consoleMessageStorage()->addMessage(std::move(message));
111   }
112 
reportDeprecatedCall(const char * id,const String16 & message)113   void reportDeprecatedCall(const char* id, const String16& message) {
114     if (!consoleMessageStorage()->shouldReportDeprecationMessage(m_contextId,
115                                                                  id)) {
116       return;
117     }
118     std::vector<v8::Local<v8::Value>> arguments(1,
119                                                 toV8String(m_isolate, message));
120     reportCall(ConsoleAPIType::kWarning, arguments);
121   }
122 
firstArgToBoolean(bool defaultValue)123   bool firstArgToBoolean(bool defaultValue) {
124     if (m_info.Length() < 1) return defaultValue;
125     if (m_info[0]->IsBoolean()) return m_info[0].As<v8::Boolean>()->Value();
126     return m_info[0]->BooleanValue(m_context->GetIsolate());
127   }
128 
firstArgToString(const String16 & defaultValue,bool allowUndefined=true)129   String16 firstArgToString(const String16& defaultValue,
130                             bool allowUndefined = true) {
131     if (m_info.Length() < 1 || (!allowUndefined && m_info[0]->IsUndefined())) {
132       return defaultValue;
133     }
134     v8::Local<v8::String> titleValue;
135     if (!m_info[0]->ToString(m_context).ToLocal(&titleValue))
136       return defaultValue;
137     return toProtocolString(m_context->GetIsolate(), titleValue);
138   }
139 
firstArgAsObject()140   v8::MaybeLocal<v8::Object> firstArgAsObject() {
141     if (m_info.Length() < 1 || !m_info[0]->IsObject())
142       return v8::MaybeLocal<v8::Object>();
143     return m_info[0].As<v8::Object>();
144   }
145 
firstArgAsFunction()146   v8::MaybeLocal<v8::Function> firstArgAsFunction() {
147     if (m_info.Length() < 1 || !m_info[0]->IsFunction())
148       return v8::MaybeLocal<v8::Function>();
149     v8::Local<v8::Function> func = m_info[0].As<v8::Function>();
150     while (func->GetBoundFunction()->IsFunction())
151       func = func->GetBoundFunction().As<v8::Function>();
152     return func;
153   }
154 
forEachSession(std::function<void (V8InspectorSessionImpl *)> callback)155   void forEachSession(std::function<void(V8InspectorSessionImpl*)> callback) {
156     m_inspector->forEachSession(m_groupId, std::move(callback));
157   }
158 
159  private:
160   const v8::debug::ConsoleCallArguments& m_info;
161   const v8::debug::ConsoleContext& m_consoleContext;
162   v8::Isolate* m_isolate;
163   v8::Local<v8::Context> m_context;
164   V8InspectorImpl* m_inspector = nullptr;
165   int m_contextId;
166   int m_groupId;
167 };
168 
createBoundFunctionProperty(v8::Local<v8::Context> context,v8::Local<v8::Object> console,v8::Local<v8::Value> data,const char * name,v8::FunctionCallback callback,v8::SideEffectType side_effect_type=v8::SideEffectType::kHasSideEffect)169 void createBoundFunctionProperty(
170     v8::Local<v8::Context> context, v8::Local<v8::Object> console,
171     v8::Local<v8::Value> data, const char* name, v8::FunctionCallback callback,
172     v8::SideEffectType side_effect_type = v8::SideEffectType::kHasSideEffect) {
173   v8::Local<v8::String> funcName =
174       toV8StringInternalized(context->GetIsolate(), name);
175   v8::Local<v8::Function> func;
176   if (!v8::Function::New(context, callback, data, 0,
177                          v8::ConstructorBehavior::kThrow, side_effect_type)
178            .ToLocal(&func))
179     return;
180   func->SetName(funcName);
181   createDataProperty(context, console, funcName, func);
182 }
183 
184 enum InspectRequest { kRegular, kCopyToClipboard, kQueryObjects };
185 
186 }  // namespace
187 
V8Console(V8InspectorImpl * inspector)188 V8Console::V8Console(V8InspectorImpl* inspector) : m_inspector(inspector) {}
189 
Debug(const v8::debug::ConsoleCallArguments & info,const v8::debug::ConsoleContext & consoleContext)190 void V8Console::Debug(const v8::debug::ConsoleCallArguments& info,
191                       const v8::debug::ConsoleContext& consoleContext) {
192   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.inspector"), "V8Console::Debug");
193   ConsoleHelper(info, consoleContext, m_inspector)
194       .reportCall(ConsoleAPIType::kDebug);
195 }
196 
Error(const v8::debug::ConsoleCallArguments & info,const v8::debug::ConsoleContext & consoleContext)197 void V8Console::Error(const v8::debug::ConsoleCallArguments& info,
198                       const v8::debug::ConsoleContext& consoleContext) {
199   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.inspector"), "V8Console::Error");
200   ConsoleHelper(info, consoleContext, m_inspector)
201       .reportCall(ConsoleAPIType::kError);
202 }
203 
Info(const v8::debug::ConsoleCallArguments & info,const v8::debug::ConsoleContext & consoleContext)204 void V8Console::Info(const v8::debug::ConsoleCallArguments& info,
205                      const v8::debug::ConsoleContext& consoleContext) {
206   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.inspector"), "V8Console::Info");
207   ConsoleHelper(info, consoleContext, m_inspector)
208       .reportCall(ConsoleAPIType::kInfo);
209 }
210 
Log(const v8::debug::ConsoleCallArguments & info,const v8::debug::ConsoleContext & consoleContext)211 void V8Console::Log(const v8::debug::ConsoleCallArguments& info,
212                     const v8::debug::ConsoleContext& consoleContext) {
213   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.inspector"), "V8Console::Log");
214   ConsoleHelper(info, consoleContext, m_inspector)
215       .reportCall(ConsoleAPIType::kLog);
216 }
217 
Warn(const v8::debug::ConsoleCallArguments & info,const v8::debug::ConsoleContext & consoleContext)218 void V8Console::Warn(const v8::debug::ConsoleCallArguments& info,
219                      const v8::debug::ConsoleContext& consoleContext) {
220   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.inspector"), "V8Console::Warn");
221   ConsoleHelper(info, consoleContext, m_inspector)
222       .reportCall(ConsoleAPIType::kWarning);
223 }
224 
Dir(const v8::debug::ConsoleCallArguments & info,const v8::debug::ConsoleContext & consoleContext)225 void V8Console::Dir(const v8::debug::ConsoleCallArguments& info,
226                     const v8::debug::ConsoleContext& consoleContext) {
227   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.inspector"), "V8Console::Dir");
228   ConsoleHelper(info, consoleContext, m_inspector)
229       .reportCall(ConsoleAPIType::kDir);
230 }
231 
DirXml(const v8::debug::ConsoleCallArguments & info,const v8::debug::ConsoleContext & consoleContext)232 void V8Console::DirXml(const v8::debug::ConsoleCallArguments& info,
233                        const v8::debug::ConsoleContext& consoleContext) {
234   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.inspector"), "V8Console::DirXml");
235   ConsoleHelper(info, consoleContext, m_inspector)
236       .reportCall(ConsoleAPIType::kDirXML);
237 }
238 
Table(const v8::debug::ConsoleCallArguments & info,const v8::debug::ConsoleContext & consoleContext)239 void V8Console::Table(const v8::debug::ConsoleCallArguments& info,
240                       const v8::debug::ConsoleContext& consoleContext) {
241   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.inspector"), "V8Console::Table");
242   ConsoleHelper(info, consoleContext, m_inspector)
243       .reportCall(ConsoleAPIType::kTable);
244 }
245 
Trace(const v8::debug::ConsoleCallArguments & info,const v8::debug::ConsoleContext & consoleContext)246 void V8Console::Trace(const v8::debug::ConsoleCallArguments& info,
247                       const v8::debug::ConsoleContext& consoleContext) {
248   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.inspector"), "V8Console::Trace");
249   ConsoleHelper(info, consoleContext, m_inspector)
250       .reportCallWithDefaultArgument(ConsoleAPIType::kTrace,
251                                      String16("console.trace"));
252 }
253 
Group(const v8::debug::ConsoleCallArguments & info,const v8::debug::ConsoleContext & consoleContext)254 void V8Console::Group(const v8::debug::ConsoleCallArguments& info,
255                       const v8::debug::ConsoleContext& consoleContext) {
256   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.inspector"), "V8Console::Group");
257   ConsoleHelper(info, consoleContext, m_inspector)
258       .reportCallWithDefaultArgument(ConsoleAPIType::kStartGroup,
259                                      String16("console.group"));
260 }
261 
GroupCollapsed(const v8::debug::ConsoleCallArguments & info,const v8::debug::ConsoleContext & consoleContext)262 void V8Console::GroupCollapsed(
263     const v8::debug::ConsoleCallArguments& info,
264     const v8::debug::ConsoleContext& consoleContext) {
265   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.inspector"),
266                "V8Console::GroupCollapsed");
267   ConsoleHelper(info, consoleContext, m_inspector)
268       .reportCallWithDefaultArgument(ConsoleAPIType::kStartGroupCollapsed,
269                                      String16("console.groupCollapsed"));
270 }
271 
GroupEnd(const v8::debug::ConsoleCallArguments & info,const v8::debug::ConsoleContext & consoleContext)272 void V8Console::GroupEnd(const v8::debug::ConsoleCallArguments& info,
273                          const v8::debug::ConsoleContext& consoleContext) {
274   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.inspector"),
275                "V8Console::GroupEnd");
276   ConsoleHelper(info, consoleContext, m_inspector)
277       .reportCallWithDefaultArgument(ConsoleAPIType::kEndGroup,
278                                      String16("console.groupEnd"));
279 }
280 
Clear(const v8::debug::ConsoleCallArguments & info,const v8::debug::ConsoleContext & consoleContext)281 void V8Console::Clear(const v8::debug::ConsoleCallArguments& info,
282                       const v8::debug::ConsoleContext& consoleContext) {
283   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.inspector"), "V8Console::Clear");
284   ConsoleHelper helper(info, consoleContext, m_inspector);
285   if (!helper.groupId()) return;
286   m_inspector->client()->consoleClear(helper.groupId());
287   helper.reportCallWithDefaultArgument(ConsoleAPIType::kClear,
288                                        String16("console.clear"));
289 }
290 
identifierFromTitleOrStackTrace(const String16 & title,const ConsoleHelper & helper,const v8::debug::ConsoleContext & consoleContext,V8InspectorImpl * inspector)291 static String16 identifierFromTitleOrStackTrace(
292     const String16& title, const ConsoleHelper& helper,
293     const v8::debug::ConsoleContext& consoleContext,
294     V8InspectorImpl* inspector) {
295   String16 identifier;
296   if (title.isEmpty()) {
297     std::unique_ptr<V8StackTraceImpl> stackTrace =
298         V8StackTraceImpl::capture(inspector->debugger(), 1);
299     if (stackTrace && !stackTrace->isEmpty()) {
300       identifier = toString16(stackTrace->topSourceURL()) + ":" +
301                    String16::fromInteger(stackTrace->topLineNumber());
302     }
303   } else {
304     identifier = title + "@";
305   }
306   identifier = consoleContextToString(inspector->isolate(), consoleContext) +
307                "@" + identifier;
308 
309   return identifier;
310 }
311 
Count(const v8::debug::ConsoleCallArguments & info,const v8::debug::ConsoleContext & consoleContext)312 void V8Console::Count(const v8::debug::ConsoleCallArguments& info,
313                       const v8::debug::ConsoleContext& consoleContext) {
314   TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("v8.inspector"),
315                      "V8Console::Count");
316   ConsoleHelper helper(info, consoleContext, m_inspector);
317   String16 title = helper.firstArgToString(String16("default"), false);
318   String16 identifier = identifierFromTitleOrStackTrace(
319       title, helper, consoleContext, m_inspector);
320 
321   int count =
322       helper.consoleMessageStorage()->count(helper.contextId(), identifier);
323   String16 countString = String16::fromInteger(count);
324   helper.reportCallWithArgument(
325       ConsoleAPIType::kCount,
326       title.isEmpty() ? countString : (title + ": " + countString));
327   TRACE_EVENT_END2(TRACE_DISABLED_BY_DEFAULT("v8.inspector"),
328                    "V8Console::Count", "title",
329                    TRACE_STR_COPY(title.utf8().c_str()), "count", count);
330 }
331 
CountReset(const v8::debug::ConsoleCallArguments & info,const v8::debug::ConsoleContext & consoleContext)332 void V8Console::CountReset(const v8::debug::ConsoleCallArguments& info,
333                            const v8::debug::ConsoleContext& consoleContext) {
334   TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("v8.inspector"),
335                      "V8Console::CountReset");
336   ConsoleHelper helper(info, consoleContext, m_inspector);
337   String16 title = helper.firstArgToString(String16("default"), false);
338   String16 identifier = identifierFromTitleOrStackTrace(
339       title, helper, consoleContext, m_inspector);
340 
341   if (!helper.consoleMessageStorage()->countReset(helper.contextId(),
342                                                   identifier)) {
343     helper.reportCallWithArgument(ConsoleAPIType::kWarning,
344                                   "Count for '" + title + "' does not exist");
345   }
346   TRACE_EVENT_END1(TRACE_DISABLED_BY_DEFAULT("v8.inspector"),
347                    "V8Console::CountReset", "title",
348                    TRACE_STR_COPY(title.utf8().c_str()));
349 }
350 
Assert(const v8::debug::ConsoleCallArguments & info,const v8::debug::ConsoleContext & consoleContext)351 void V8Console::Assert(const v8::debug::ConsoleCallArguments& info,
352                        const v8::debug::ConsoleContext& consoleContext) {
353   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.inspector"), "V8Console::Assert");
354   ConsoleHelper helper(info, consoleContext, m_inspector);
355   DCHECK(!helper.firstArgToBoolean(false));
356 
357   std::vector<v8::Local<v8::Value>> arguments;
358   for (int i = 1; i < info.Length(); ++i) arguments.push_back(info[i]);
359   if (info.Length() < 2)
360     arguments.push_back(
361         toV8String(m_inspector->isolate(), String16("console.assert")));
362   helper.reportCall(ConsoleAPIType::kAssert, arguments);
363   m_inspector->debugger()->breakProgramOnAssert(helper.groupId());
364 }
365 
Profile(const v8::debug::ConsoleCallArguments & info,const v8::debug::ConsoleContext & consoleContext)366 void V8Console::Profile(const v8::debug::ConsoleCallArguments& info,
367                         const v8::debug::ConsoleContext& consoleContext) {
368   TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("v8.inspector"),
369                      "V8Console::Profile");
370   ConsoleHelper helper(info, consoleContext, m_inspector);
371   String16 title = helper.firstArgToString(String16());
372   helper.forEachSession([&title](V8InspectorSessionImpl* session) {
373     session->profilerAgent()->consoleProfile(title);
374   });
375   TRACE_EVENT_END1(TRACE_DISABLED_BY_DEFAULT("v8.inspector"),
376                    "V8Console::Profile", "title",
377                    TRACE_STR_COPY(title.utf8().c_str()));
378 }
379 
ProfileEnd(const v8::debug::ConsoleCallArguments & info,const v8::debug::ConsoleContext & consoleContext)380 void V8Console::ProfileEnd(const v8::debug::ConsoleCallArguments& info,
381                            const v8::debug::ConsoleContext& consoleContext) {
382   TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("v8.inspector"),
383                      "V8Console::ProfileEnd");
384   ConsoleHelper helper(info, consoleContext, m_inspector);
385   String16 title = helper.firstArgToString(String16());
386   helper.forEachSession([&title](V8InspectorSessionImpl* session) {
387     session->profilerAgent()->consoleProfileEnd(title);
388   });
389   TRACE_EVENT_END1(TRACE_DISABLED_BY_DEFAULT("v8.inspector"),
390                    "V8Console::ProfileEnd", "title",
391                    TRACE_STR_COPY(title.utf8().c_str()));
392 }
393 
timeFunction(const v8::debug::ConsoleCallArguments & info,const v8::debug::ConsoleContext & consoleContext,bool timelinePrefix,V8InspectorImpl * inspector)394 static void timeFunction(const v8::debug::ConsoleCallArguments& info,
395                          const v8::debug::ConsoleContext& consoleContext,
396                          bool timelinePrefix, V8InspectorImpl* inspector) {
397   ConsoleHelper helper(info, consoleContext, inspector);
398   String16 protocolTitle = helper.firstArgToString("default", false);
399   if (timelinePrefix) protocolTitle = "Timeline '" + protocolTitle + "'";
400   const String16& timerId =
401       protocolTitle + "@" +
402       consoleContextToString(inspector->isolate(), consoleContext);
403   if (helper.consoleMessageStorage()->hasTimer(helper.contextId(), timerId)) {
404     helper.reportCallWithArgument(
405         ConsoleAPIType::kWarning,
406         "Timer '" + protocolTitle + "' already exists");
407     return;
408   }
409   inspector->client()->consoleTime(toStringView(protocolTitle));
410   helper.consoleMessageStorage()->time(helper.contextId(), timerId);
411 }
412 
timeEndFunction(const v8::debug::ConsoleCallArguments & info,const v8::debug::ConsoleContext & consoleContext,bool timeLog,V8InspectorImpl * inspector)413 static void timeEndFunction(const v8::debug::ConsoleCallArguments& info,
414                             const v8::debug::ConsoleContext& consoleContext,
415                             bool timeLog, V8InspectorImpl* inspector) {
416   ConsoleHelper helper(info, consoleContext, inspector);
417   String16 protocolTitle = helper.firstArgToString("default", false);
418   const String16& timerId =
419       protocolTitle + "@" +
420       consoleContextToString(inspector->isolate(), consoleContext);
421   if (!helper.consoleMessageStorage()->hasTimer(helper.contextId(), timerId)) {
422     helper.reportCallWithArgument(
423         ConsoleAPIType::kWarning,
424         "Timer '" + protocolTitle + "' does not exist");
425     return;
426   }
427   inspector->client()->consoleTimeEnd(toStringView(protocolTitle));
428   String16 title = protocolTitle + "@" +
429                    consoleContextToString(inspector->isolate(), consoleContext);
430   double elapsed;
431   if (timeLog) {
432     elapsed =
433         helper.consoleMessageStorage()->timeLog(helper.contextId(), title);
434   } else {
435     elapsed =
436         helper.consoleMessageStorage()->timeEnd(helper.contextId(), title);
437   }
438   String16 message =
439       protocolTitle + ": " + String16::fromDouble(elapsed) + " ms";
440   if (timeLog)
441     helper.reportCallAndReplaceFirstArgument(ConsoleAPIType::kLog, message);
442   else
443     helper.reportCallWithArgument(ConsoleAPIType::kTimeEnd, message);
444 }
445 
Time(const v8::debug::ConsoleCallArguments & info,const v8::debug::ConsoleContext & consoleContext)446 void V8Console::Time(const v8::debug::ConsoleCallArguments& info,
447                      const v8::debug::ConsoleContext& consoleContext) {
448   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.inspector"), "V8Console::Time");
449   timeFunction(info, consoleContext, false, m_inspector);
450 }
451 
TimeLog(const v8::debug::ConsoleCallArguments & info,const v8::debug::ConsoleContext & consoleContext)452 void V8Console::TimeLog(const v8::debug::ConsoleCallArguments& info,
453                         const v8::debug::ConsoleContext& consoleContext) {
454   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.inspector"), "V8Console::TimeLog");
455   timeEndFunction(info, consoleContext, true, m_inspector);
456 }
457 
TimeEnd(const v8::debug::ConsoleCallArguments & info,const v8::debug::ConsoleContext & consoleContext)458 void V8Console::TimeEnd(const v8::debug::ConsoleCallArguments& info,
459                         const v8::debug::ConsoleContext& consoleContext) {
460   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.inspector"), "V8Console::TimeEnd");
461   timeEndFunction(info, consoleContext, false, m_inspector);
462 }
463 
TimeStamp(const v8::debug::ConsoleCallArguments & info,const v8::debug::ConsoleContext & consoleContext)464 void V8Console::TimeStamp(const v8::debug::ConsoleCallArguments& info,
465                           const v8::debug::ConsoleContext& consoleContext) {
466   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.inspector"),
467                "V8Console::TimeStamp");
468   ConsoleHelper helper(info, consoleContext, m_inspector);
469   String16 title = helper.firstArgToString(String16());
470   m_inspector->client()->consoleTimeStamp(toStringView(title));
471 }
472 
memoryGetterCallback(const v8::FunctionCallbackInfo<v8::Value> & info)473 void V8Console::memoryGetterCallback(
474     const v8::FunctionCallbackInfo<v8::Value>& info) {
475   v8::Local<v8::Value> memoryValue;
476   if (!m_inspector->client()
477            ->memoryInfo(info.GetIsolate(),
478                         info.GetIsolate()->GetCurrentContext())
479            .ToLocal(&memoryValue))
480     return;
481   info.GetReturnValue().Set(memoryValue);
482 }
483 
memorySetterCallback(const v8::FunctionCallbackInfo<v8::Value> & info)484 void V8Console::memorySetterCallback(
485     const v8::FunctionCallbackInfo<v8::Value>& info) {
486   // We can't make the attribute readonly as it breaks existing code that relies
487   // on being able to assign to console.memory in strict mode. Instead, the
488   // setter just ignores the passed value.  http://crbug.com/468611
489 }
490 
ValidateAndGetTaskId(const v8::FunctionCallbackInfo<v8::Value> & info)491 v8::Maybe<int64_t> V8Console::ValidateAndGetTaskId(
492     const v8::FunctionCallbackInfo<v8::Value>& info) {
493   if (info.Length() != 1) {
494     info.GetIsolate()->ThrowError("Unexpected arguments");
495     return v8::Nothing<int64_t>();
496   }
497 
498   int64_t argId;
499   if (!info[0]->IsNumber() ||
500       !v8::Just(info[0].As<v8::Integer>()->Value()).To(&argId)) {
501     info.GetIsolate()->ThrowError("Task ID should be an integer");
502     return v8::Nothing<int64_t>();
503   }
504 
505   auto it = m_asyncTaskIds.find(argId);
506   if (it == m_asyncTaskIds.end()) {
507     info.GetIsolate()->ThrowError("Task with ID doesn't exist");
508     return v8::Nothing<int64_t>();
509   }
510 
511   return v8::Just(argId);
512 }
513 
scheduleAsyncTask(const v8::FunctionCallbackInfo<v8::Value> & info)514 void V8Console::scheduleAsyncTask(
515     const v8::FunctionCallbackInfo<v8::Value>& info) {
516   if (info.Length() != 1 && info.Length() != 2) {
517     info.GetIsolate()->ThrowError("Unexpected arguments");
518     return;
519   }
520   if (info.Length() == 2 && !info[1]->IsBoolean()) {
521     info.GetIsolate()->ThrowError("Unexpected arguments");
522     return;
523   }
524 
525   v8::debug::ConsoleCallArguments args(info);
526   ConsoleHelper helper(args, v8::debug::ConsoleContext(), m_inspector);
527   String16 argName = helper.firstArgToString(String16());
528   bool recurring =
529       info.Length() == 2 ? info[1].As<v8::Boolean>()->Value() : false;
530 
531   int64_t id = m_taskIdCounter++;
532   auto it = m_asyncTaskIds.find(id);
533   if (it != m_asyncTaskIds.end()) {
534     info.GetIsolate()->ThrowError("Task with ID already exists");
535     return;
536   }
537 
538   AsyncTaskInfo taskInfo;
539   taskInfo.ptr = new int();
540   taskInfo.recurring = recurring;
541   m_asyncTaskIds.emplace(id, taskInfo);
542 
543   StringView taskName = StringView(argName.characters16(), argName.length());
544   m_inspector->asyncTaskScheduled(taskName, taskInfo.ptr, recurring);
545 
546   info.GetReturnValue().Set(v8::Number::New(info.GetIsolate(), id));
547 }
548 
startAsyncTask(const v8::FunctionCallbackInfo<v8::Value> & info)549 void V8Console::startAsyncTask(
550     const v8::FunctionCallbackInfo<v8::Value>& info) {
551   v8::Maybe<int64_t> maybeArgId = ValidateAndGetTaskId(info);
552   if (maybeArgId.IsNothing()) return;
553 
554   int64_t taskId = maybeArgId.FromJust();
555   AsyncTaskInfo taskInfo = m_asyncTaskIds[taskId];
556   m_inspector->asyncTaskStarted(taskInfo.ptr);
557 }
558 
finishAsyncTask(const v8::FunctionCallbackInfo<v8::Value> & info)559 void V8Console::finishAsyncTask(
560     const v8::FunctionCallbackInfo<v8::Value>& info) {
561   v8::Maybe<int64_t> maybeArgId = ValidateAndGetTaskId(info);
562   if (maybeArgId.IsNothing()) return;
563 
564   int64_t taskId = maybeArgId.FromJust();
565   AsyncTaskInfo taskInfo = m_asyncTaskIds[taskId];
566   m_inspector->asyncTaskFinished(taskInfo.ptr);
567 
568   if (taskInfo.recurring) {
569     return;
570   }
571 
572   delete taskInfo.ptr;
573   m_asyncTaskIds.erase(taskId);
574 }
575 
cancelAsyncTask(const v8::FunctionCallbackInfo<v8::Value> & info)576 void V8Console::cancelAsyncTask(
577     const v8::FunctionCallbackInfo<v8::Value>& info) {
578   v8::Maybe<int64_t> maybeArgId = ValidateAndGetTaskId(info);
579   if (maybeArgId.IsNothing()) return;
580 
581   int64_t taskId = maybeArgId.FromJust();
582   AsyncTaskInfo taskInfo = m_asyncTaskIds[taskId];
583   m_inspector->asyncTaskCanceled(taskInfo.ptr);
584 
585   delete taskInfo.ptr;
586   m_asyncTaskIds.erase(taskId);
587 }
588 
keysCallback(const v8::FunctionCallbackInfo<v8::Value> & info,int sessionId)589 void V8Console::keysCallback(const v8::FunctionCallbackInfo<v8::Value>& info,
590                              int sessionId) {
591   v8::Isolate* isolate = info.GetIsolate();
592   info.GetReturnValue().Set(v8::Array::New(isolate));
593 
594   v8::debug::ConsoleCallArguments args(info);
595   ConsoleHelper helper(args, v8::debug::ConsoleContext(), m_inspector);
596   v8::Local<v8::Object> obj;
597   if (!helper.firstArgAsObject().ToLocal(&obj)) return;
598   v8::Local<v8::Array> names;
599   if (!obj->GetOwnPropertyNames(isolate->GetCurrentContext()).ToLocal(&names))
600     return;
601   info.GetReturnValue().Set(names);
602 }
603 
valuesCallback(const v8::FunctionCallbackInfo<v8::Value> & info,int sessionId)604 void V8Console::valuesCallback(const v8::FunctionCallbackInfo<v8::Value>& info,
605                                int sessionId) {
606   v8::Isolate* isolate = info.GetIsolate();
607   info.GetReturnValue().Set(v8::Array::New(isolate));
608 
609   v8::debug::ConsoleCallArguments args(info);
610   ConsoleHelper helper(args, v8::debug::ConsoleContext(), m_inspector);
611   v8::Local<v8::Object> obj;
612   if (!helper.firstArgAsObject().ToLocal(&obj)) return;
613   v8::Local<v8::Array> names;
614   v8::Local<v8::Context> context = isolate->GetCurrentContext();
615   if (!obj->GetOwnPropertyNames(context).ToLocal(&names)) return;
616   v8::Local<v8::Array> values = v8::Array::New(isolate, names->Length());
617   for (uint32_t i = 0; i < names->Length(); ++i) {
618     v8::Local<v8::Value> key;
619     if (!names->Get(context, i).ToLocal(&key)) continue;
620     v8::Local<v8::Value> value;
621     if (!obj->Get(context, key).ToLocal(&value)) continue;
622     createDataProperty(context, values, i, value);
623   }
624   info.GetReturnValue().Set(values);
625 }
626 
setFunctionBreakpoint(ConsoleHelper & helper,int sessionId,v8::Local<v8::Function> function,V8DebuggerAgentImpl::BreakpointSource source,v8::Local<v8::String> condition,bool enable)627 static void setFunctionBreakpoint(ConsoleHelper& helper, int sessionId,
628                                   v8::Local<v8::Function> function,
629                                   V8DebuggerAgentImpl::BreakpointSource source,
630                                   v8::Local<v8::String> condition,
631                                   bool enable) {
632   V8InspectorSessionImpl* session = helper.session(sessionId);
633   if (session == nullptr) return;
634   if (!session->debuggerAgent()->enabled()) return;
635   if (enable) {
636     session->debuggerAgent()->setBreakpointFor(function, condition, source);
637   } else {
638     session->debuggerAgent()->removeBreakpointFor(function, source);
639   }
640 }
641 
debugFunctionCallback(const v8::FunctionCallbackInfo<v8::Value> & info,int sessionId)642 void V8Console::debugFunctionCallback(
643     const v8::FunctionCallbackInfo<v8::Value>& info, int sessionId) {
644   v8::debug::ConsoleCallArguments args(info);
645   ConsoleHelper helper(args, v8::debug::ConsoleContext(), m_inspector);
646   v8::Local<v8::Function> function;
647   v8::Local<v8::String> condition;
648   if (!helper.firstArgAsFunction().ToLocal(&function)) return;
649   if (args.Length() > 1 && args[1]->IsString()) {
650     condition = args[1].As<v8::String>();
651   }
652   setFunctionBreakpoint(helper, sessionId, function,
653                         V8DebuggerAgentImpl::DebugCommandBreakpointSource,
654                         condition, true);
655 }
656 
undebugFunctionCallback(const v8::FunctionCallbackInfo<v8::Value> & info,int sessionId)657 void V8Console::undebugFunctionCallback(
658     const v8::FunctionCallbackInfo<v8::Value>& info, int sessionId) {
659   v8::debug::ConsoleCallArguments args(info);
660   ConsoleHelper helper(args, v8::debug::ConsoleContext(), m_inspector);
661   v8::Local<v8::Function> function;
662   if (!helper.firstArgAsFunction().ToLocal(&function)) return;
663   setFunctionBreakpoint(helper, sessionId, function,
664                         V8DebuggerAgentImpl::DebugCommandBreakpointSource,
665                         v8::Local<v8::String>(), false);
666 }
667 
monitorFunctionCallback(const v8::FunctionCallbackInfo<v8::Value> & info,int sessionId)668 void V8Console::monitorFunctionCallback(
669     const v8::FunctionCallbackInfo<v8::Value>& info, int sessionId) {
670   v8::debug::ConsoleCallArguments args(info);
671   ConsoleHelper helper(args, v8::debug::ConsoleContext(), m_inspector);
672   v8::Local<v8::Function> function;
673   if (!helper.firstArgAsFunction().ToLocal(&function)) return;
674   v8::Local<v8::Value> name = function->GetName();
675   if (!name->IsString() || !name.As<v8::String>()->Length())
676     name = function->GetInferredName();
677   String16 functionName =
678       toProtocolStringWithTypeCheck(info.GetIsolate(), name);
679   String16Builder builder;
680   builder.append("console.log(\"function ");
681   if (functionName.isEmpty())
682     builder.append("(anonymous function)");
683   else
684     builder.append(functionName);
685   builder.append(
686       " called\" + (typeof arguments !== \"undefined\" && arguments.length > 0 "
687       "? \" with arguments: \" + Array.prototype.join.call(arguments, \", \") "
688       ": \"\")) && false");
689   setFunctionBreakpoint(helper, sessionId, function,
690                         V8DebuggerAgentImpl::MonitorCommandBreakpointSource,
691                         toV8String(info.GetIsolate(), builder.toString()),
692                         true);
693 }
694 
unmonitorFunctionCallback(const v8::FunctionCallbackInfo<v8::Value> & info,int sessionId)695 void V8Console::unmonitorFunctionCallback(
696     const v8::FunctionCallbackInfo<v8::Value>& info, int sessionId) {
697   v8::debug::ConsoleCallArguments args(info);
698   ConsoleHelper helper(args, v8::debug::ConsoleContext(), m_inspector);
699   v8::Local<v8::Function> function;
700   if (!helper.firstArgAsFunction().ToLocal(&function)) return;
701   setFunctionBreakpoint(helper, sessionId, function,
702                         V8DebuggerAgentImpl::MonitorCommandBreakpointSource,
703                         v8::Local<v8::String>(), false);
704 }
705 
lastEvaluationResultCallback(const v8::FunctionCallbackInfo<v8::Value> & info,int sessionId)706 void V8Console::lastEvaluationResultCallback(
707     const v8::FunctionCallbackInfo<v8::Value>& info, int sessionId) {
708   v8::debug::ConsoleCallArguments args(info);
709   ConsoleHelper helper(args, v8::debug::ConsoleContext(), m_inspector);
710   InjectedScript* injectedScript = helper.injectedScript(sessionId);
711   if (!injectedScript) return;
712   info.GetReturnValue().Set(injectedScript->lastEvaluationResult());
713 }
714 
inspectImpl(const v8::FunctionCallbackInfo<v8::Value> & info,v8::Local<v8::Value> value,int sessionId,InspectRequest request,V8InspectorImpl * inspector)715 static void inspectImpl(const v8::FunctionCallbackInfo<v8::Value>& info,
716                         v8::Local<v8::Value> value, int sessionId,
717                         InspectRequest request, V8InspectorImpl* inspector) {
718   if (request == kRegular) info.GetReturnValue().Set(value);
719 
720   v8::debug::ConsoleCallArguments args(info);
721   ConsoleHelper helper(args, v8::debug::ConsoleContext(), inspector);
722   InjectedScript* injectedScript = helper.injectedScript(sessionId);
723   if (!injectedScript) return;
724   std::unique_ptr<protocol::Runtime::RemoteObject> wrappedObject;
725   protocol::Response response = injectedScript->wrapObject(
726       value, "", WrapMode::kNoPreview, &wrappedObject);
727   if (!response.IsSuccess()) return;
728 
729   std::unique_ptr<protocol::DictionaryValue> hints =
730       protocol::DictionaryValue::create();
731   if (request == kCopyToClipboard) {
732     hints->setBoolean("copyToClipboard", true);
733   } else if (request == kQueryObjects) {
734     hints->setBoolean("queryObjects", true);
735   }
736   if (V8InspectorSessionImpl* session = helper.session(sessionId)) {
737     session->runtimeAgent()->inspect(std::move(wrappedObject), std::move(hints),
738                                      helper.contextId());
739   }
740 }
741 
inspectCallback(const v8::FunctionCallbackInfo<v8::Value> & info,int sessionId)742 void V8Console::inspectCallback(const v8::FunctionCallbackInfo<v8::Value>& info,
743                                 int sessionId) {
744   if (info.Length() < 1) return;
745   inspectImpl(info, info[0], sessionId, kRegular, m_inspector);
746 }
747 
copyCallback(const v8::FunctionCallbackInfo<v8::Value> & info,int sessionId)748 void V8Console::copyCallback(const v8::FunctionCallbackInfo<v8::Value>& info,
749                              int sessionId) {
750   if (info.Length() < 1) return;
751   inspectImpl(info, info[0], sessionId, kCopyToClipboard, m_inspector);
752 }
753 
queryObjectsCallback(const v8::FunctionCallbackInfo<v8::Value> & info,int sessionId)754 void V8Console::queryObjectsCallback(
755     const v8::FunctionCallbackInfo<v8::Value>& info, int sessionId) {
756   if (info.Length() < 1) return;
757   v8::Local<v8::Value> arg = info[0];
758   if (arg->IsFunction()) {
759     v8::Isolate* isolate = info.GetIsolate();
760     v8::TryCatch tryCatch(isolate);
761     v8::Local<v8::Value> prototype;
762     if (arg.As<v8::Function>()
763             ->Get(isolate->GetCurrentContext(),
764                   toV8StringInternalized(isolate, "prototype"))
765             .ToLocal(&prototype) &&
766         prototype->IsObject()) {
767       arg = prototype;
768     }
769     if (tryCatch.HasCaught()) {
770       tryCatch.ReThrow();
771       return;
772     }
773   }
774   inspectImpl(info, arg, sessionId, kQueryObjects, m_inspector);
775 }
776 
inspectedObject(const v8::FunctionCallbackInfo<v8::Value> & info,int sessionId,unsigned num)777 void V8Console::inspectedObject(const v8::FunctionCallbackInfo<v8::Value>& info,
778                                 int sessionId, unsigned num) {
779   DCHECK_GT(V8InspectorSessionImpl::kInspectedObjectBufferSize, num);
780   v8::debug::ConsoleCallArguments args(info);
781   ConsoleHelper helper(args, v8::debug::ConsoleContext(), m_inspector);
782   if (V8InspectorSessionImpl* session = helper.session(sessionId)) {
783     V8InspectorSession::Inspectable* object = session->inspectedObject(num);
784     v8::Isolate* isolate = info.GetIsolate();
785     if (object)
786       info.GetReturnValue().Set(object->get(isolate->GetCurrentContext()));
787     else
788       info.GetReturnValue().Set(v8::Undefined(isolate));
789   }
790 }
791 
installMemoryGetter(v8::Local<v8::Context> context,v8::Local<v8::Object> console)792 void V8Console::installMemoryGetter(v8::Local<v8::Context> context,
793                                     v8::Local<v8::Object> console) {
794   v8::Isolate* isolate = context->GetIsolate();
795   v8::Local<v8::External> data = v8::External::New(isolate, this);
796   console->SetAccessorProperty(
797       toV8StringInternalized(isolate, "memory"),
798       v8::Function::New(
799           context, &V8Console::call<&V8Console::memoryGetterCallback>, data, 0,
800           v8::ConstructorBehavior::kThrow, v8::SideEffectType::kHasNoSideEffect)
801           .ToLocalChecked(),
802       v8::Function::New(context,
803                         &V8Console::call<&V8Console::memorySetterCallback>,
804                         data, 0, v8::ConstructorBehavior::kThrow)
805           .ToLocalChecked(),
806       static_cast<v8::PropertyAttribute>(v8::None), v8::DEFAULT);
807 }
808 
installAsyncStackTaggingAPI(v8::Local<v8::Context> context,v8::Local<v8::Object> console)809 void V8Console::installAsyncStackTaggingAPI(v8::Local<v8::Context> context,
810                                             v8::Local<v8::Object> console) {
811   v8::Isolate* isolate = context->GetIsolate();
812   v8::Local<v8::External> data = v8::External::New(isolate, this);
813 
814   v8::MicrotasksScope microtasksScope(isolate,
815                                       v8::MicrotasksScope::kDoNotRunMicrotasks);
816 
817   console
818       ->Set(context, toV8StringInternalized(isolate, "scheduleAsyncTask"),
819             v8::Function::New(context,
820                               &V8Console::call<&V8Console::scheduleAsyncTask>,
821                               data, 0, v8::ConstructorBehavior::kThrow,
822                               v8::SideEffectType::kHasSideEffect)
823                 .ToLocalChecked())
824       .Check();
825   console
826       ->Set(context, toV8StringInternalized(isolate, "startAsyncTask"),
827             v8::Function::New(context,
828                               &V8Console::call<&V8Console::startAsyncTask>,
829                               data, 0, v8::ConstructorBehavior::kThrow,
830                               v8::SideEffectType::kHasSideEffect)
831                 .ToLocalChecked())
832       .Check();
833   console
834       ->Set(context, toV8StringInternalized(isolate, "finishAsyncTask"),
835             v8::Function::New(context,
836                               &V8Console::call<&V8Console::finishAsyncTask>,
837                               data, 0, v8::ConstructorBehavior::kThrow,
838                               v8::SideEffectType::kHasSideEffect)
839                 .ToLocalChecked())
840       .Check();
841   console
842       ->Set(context, toV8StringInternalized(isolate, "cancelAsyncTask"),
843             v8::Function::New(context,
844                               &V8Console::call<&V8Console::cancelAsyncTask>,
845                               data, 0, v8::ConstructorBehavior::kThrow,
846                               v8::SideEffectType::kHasSideEffect)
847                 .ToLocalChecked())
848       .Check();
849 }
850 
createCommandLineAPI(v8::Local<v8::Context> context,int sessionId)851 v8::Local<v8::Object> V8Console::createCommandLineAPI(
852     v8::Local<v8::Context> context, int sessionId) {
853   v8::Isolate* isolate = context->GetIsolate();
854   v8::MicrotasksScope microtasksScope(isolate,
855                                       v8::MicrotasksScope::kDoNotRunMicrotasks);
856 
857   v8::Local<v8::Object> commandLineAPI = v8::Object::New(isolate);
858   bool success =
859       commandLineAPI->SetPrototype(context, v8::Null(isolate)).FromMaybe(false);
860   DCHECK(success);
861   USE(success);
862 
863   v8::Local<v8::ArrayBuffer> data =
864       v8::ArrayBuffer::New(isolate, sizeof(CommandLineAPIData));
865   *static_cast<CommandLineAPIData*>(data->GetBackingStore()->Data()) =
866       CommandLineAPIData(this, sessionId);
867   createBoundFunctionProperty(context, commandLineAPI, data, "dir",
868                               &V8Console::call<&V8Console::Dir>);
869   createBoundFunctionProperty(context, commandLineAPI, data, "dirxml",
870                               &V8Console::call<&V8Console::DirXml>);
871   createBoundFunctionProperty(context, commandLineAPI, data, "profile",
872                               &V8Console::call<&V8Console::Profile>);
873   createBoundFunctionProperty(context, commandLineAPI, data, "profileEnd",
874                               &V8Console::call<&V8Console::ProfileEnd>);
875   createBoundFunctionProperty(context, commandLineAPI, data, "clear",
876                               &V8Console::call<&V8Console::Clear>);
877   createBoundFunctionProperty(context, commandLineAPI, data, "table",
878                               &V8Console::call<&V8Console::Table>);
879 
880   createBoundFunctionProperty(context, commandLineAPI, data, "keys",
881                               &V8Console::call<&V8Console::keysCallback>,
882                               v8::SideEffectType::kHasNoSideEffect);
883   createBoundFunctionProperty(context, commandLineAPI, data, "values",
884                               &V8Console::call<&V8Console::valuesCallback>,
885                               v8::SideEffectType::kHasNoSideEffect);
886   createBoundFunctionProperty(
887       context, commandLineAPI, data, "debug",
888       &V8Console::call<&V8Console::debugFunctionCallback>);
889   createBoundFunctionProperty(
890       context, commandLineAPI, data, "undebug",
891       &V8Console::call<&V8Console::undebugFunctionCallback>);
892   createBoundFunctionProperty(
893       context, commandLineAPI, data, "monitor",
894       &V8Console::call<&V8Console::monitorFunctionCallback>);
895   createBoundFunctionProperty(
896       context, commandLineAPI, data, "unmonitor",
897       &V8Console::call<&V8Console::unmonitorFunctionCallback>);
898   createBoundFunctionProperty(context, commandLineAPI, data, "inspect",
899                               &V8Console::call<&V8Console::inspectCallback>);
900   createBoundFunctionProperty(context, commandLineAPI, data, "copy",
901                               &V8Console::call<&V8Console::copyCallback>);
902   createBoundFunctionProperty(
903       context, commandLineAPI, data, "queryObjects",
904       &V8Console::call<&V8Console::queryObjectsCallback>);
905   createBoundFunctionProperty(
906       context, commandLineAPI, data, "$_",
907       &V8Console::call<&V8Console::lastEvaluationResultCallback>,
908       v8::SideEffectType::kHasNoSideEffect);
909   createBoundFunctionProperty(context, commandLineAPI, data, "$0",
910                               &V8Console::call<&V8Console::inspectedObject0>,
911                               v8::SideEffectType::kHasNoSideEffect);
912   createBoundFunctionProperty(context, commandLineAPI, data, "$1",
913                               &V8Console::call<&V8Console::inspectedObject1>,
914                               v8::SideEffectType::kHasNoSideEffect);
915   createBoundFunctionProperty(context, commandLineAPI, data, "$2",
916                               &V8Console::call<&V8Console::inspectedObject2>,
917                               v8::SideEffectType::kHasNoSideEffect);
918   createBoundFunctionProperty(context, commandLineAPI, data, "$3",
919                               &V8Console::call<&V8Console::inspectedObject3>,
920                               v8::SideEffectType::kHasNoSideEffect);
921   createBoundFunctionProperty(context, commandLineAPI, data, "$4",
922                               &V8Console::call<&V8Console::inspectedObject4>,
923                               v8::SideEffectType::kHasNoSideEffect);
924 
925   m_inspector->client()->installAdditionalCommandLineAPI(context,
926                                                          commandLineAPI);
927   return commandLineAPI;
928 }
929 
isCommandLineAPIGetter(const String16 & name)930 static bool isCommandLineAPIGetter(const String16& name) {
931   if (name.length() != 2) return false;
932   // $0 ... $4, $_
933   return name[0] == '$' &&
934          ((name[1] >= '0' && name[1] <= '4') || name[1] == '_');
935 }
936 
accessorGetterCallback(v8::Local<v8::Name> name,const v8::PropertyCallbackInfo<v8::Value> & info)937 void V8Console::CommandLineAPIScope::accessorGetterCallback(
938     v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
939   CommandLineAPIScope* scope = *static_cast<CommandLineAPIScope**>(
940       info.Data().As<v8::ArrayBuffer>()->GetBackingStore()->Data());
941   v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
942   if (scope == nullptr) {
943     USE(info.Holder()->Delete(context, name).FromMaybe(false));
944     return;
945   }
946   v8::Local<v8::Object> commandLineAPI = scope->m_commandLineAPI;
947 
948   v8::Local<v8::Value> value;
949   if (!commandLineAPI->Get(context, name).ToLocal(&value)) return;
950   if (isCommandLineAPIGetter(
951           toProtocolStringWithTypeCheck(info.GetIsolate(), name))) {
952     DCHECK(value->IsFunction());
953     v8::MicrotasksScope microtasks(info.GetIsolate(),
954                                    v8::MicrotasksScope::kDoNotRunMicrotasks);
955     if (value.As<v8::Function>()
956             ->Call(context, commandLineAPI, 0, nullptr)
957             .ToLocal(&value))
958       info.GetReturnValue().Set(value);
959   } else {
960     info.GetReturnValue().Set(value);
961   }
962 }
963 
accessorSetterCallback(v8::Local<v8::Name> name,v8::Local<v8::Value> value,const v8::PropertyCallbackInfo<void> & info)964 void V8Console::CommandLineAPIScope::accessorSetterCallback(
965     v8::Local<v8::Name> name, v8::Local<v8::Value> value,
966     const v8::PropertyCallbackInfo<void>& info) {
967   CommandLineAPIScope* scope = *static_cast<CommandLineAPIScope**>(
968       info.Data().As<v8::ArrayBuffer>()->GetBackingStore()->Data());
969   if (scope == nullptr) return;
970   v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
971   if (!info.Holder()->Delete(context, name).FromMaybe(false)) return;
972   if (!info.Holder()->CreateDataProperty(context, name, value).FromMaybe(false))
973     return;
974   USE(scope->m_installedMethods->Delete(context, name).FromMaybe(false));
975 }
976 
CommandLineAPIScope(v8::Local<v8::Context> context,v8::Local<v8::Object> commandLineAPI,v8::Local<v8::Object> global)977 V8Console::CommandLineAPIScope::CommandLineAPIScope(
978     v8::Local<v8::Context> context, v8::Local<v8::Object> commandLineAPI,
979     v8::Local<v8::Object> global)
980     : m_context(context),
981       m_commandLineAPI(commandLineAPI),
982       m_global(global),
983       m_installedMethods(v8::Set::New(context->GetIsolate())) {
984   v8::MicrotasksScope microtasksScope(context->GetIsolate(),
985                                       v8::MicrotasksScope::kDoNotRunMicrotasks);
986   v8::Local<v8::Array> names;
987   if (!m_commandLineAPI->GetOwnPropertyNames(context).ToLocal(&names)) return;
988   m_thisReference =
989       v8::ArrayBuffer::New(context->GetIsolate(), sizeof(CommandLineAPIScope*));
990   *static_cast<CommandLineAPIScope**>(
991       m_thisReference->GetBackingStore()->Data()) = this;
992   for (uint32_t i = 0; i < names->Length(); ++i) {
993     v8::Local<v8::Value> name;
994     if (!names->Get(context, i).ToLocal(&name) || !name->IsName()) continue;
995     if (m_global->Has(context, name).FromMaybe(true)) continue;
996     if (!m_installedMethods->Add(context, name).ToLocal(&m_installedMethods))
997       continue;
998     if (!m_global
999              ->SetAccessor(context, name.As<v8::Name>(),
1000                            CommandLineAPIScope::accessorGetterCallback,
1001                            CommandLineAPIScope::accessorSetterCallback,
1002                            m_thisReference, v8::DEFAULT, v8::DontEnum,
1003                            v8::SideEffectType::kHasNoSideEffect)
1004              .FromMaybe(false)) {
1005       bool removed = m_installedMethods->Delete(context, name).FromMaybe(false);
1006       DCHECK(removed);
1007       USE(removed);
1008       continue;
1009     }
1010   }
1011 }
1012 
~CommandLineAPIScope()1013 V8Console::CommandLineAPIScope::~CommandLineAPIScope() {
1014   v8::MicrotasksScope microtasksScope(m_context->GetIsolate(),
1015                                       v8::MicrotasksScope::kDoNotRunMicrotasks);
1016   *static_cast<CommandLineAPIScope**>(
1017       m_thisReference->GetBackingStore()->Data()) = nullptr;
1018   v8::Local<v8::Array> names = m_installedMethods->AsArray();
1019   for (uint32_t i = 0; i < names->Length(); ++i) {
1020     v8::Local<v8::Value> name;
1021     if (!names->Get(m_context, i).ToLocal(&name) || !name->IsName()) continue;
1022     if (name->IsString()) {
1023       v8::Local<v8::Value> descriptor;
1024       bool success =
1025           m_global->GetOwnPropertyDescriptor(m_context, name.As<v8::String>())
1026               .ToLocal(&descriptor);
1027       USE(success);
1028     }
1029   }
1030 }
1031 
1032 }  // namespace v8_inspector
1033