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