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