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