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