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-copier.h"
19 :
20 : #include "include/v8-inspector.h"
21 :
22 : namespace v8_inspector {
23 :
24 : namespace {
25 :
26 : class ConsoleHelper {
27 : public:
28 7640 : ConsoleHelper(const v8::debug::ConsoleCallArguments& info,
29 7640 : V8InspectorImpl* inspector)
30 : : m_info(info),
31 : m_isolate(inspector->isolate()),
32 : m_context(m_isolate->GetCurrentContext()),
33 : m_inspector(inspector),
34 7640 : m_contextId(InspectedContext::contextId(m_context)),
35 22920 : m_groupId(m_inspector->contextGroupId(m_contextId)) {}
36 :
37 : int contextId() const { return m_contextId; }
38 : int groupId() const { return m_groupId; }
39 :
40 : InjectedScript* injectedScript() {
41 96 : InspectedContext* context = m_inspector->getContext(m_groupId, m_contextId);
42 96 : if (!context) return nullptr;
43 : return context->getInjectedScript();
44 : }
45 :
46 : V8ConsoleMessageStorage* consoleMessageStorage() {
47 7440 : return m_inspector->ensureConsoleMessageStorage(m_groupId);
48 : }
49 :
50 6642 : void reportCall(ConsoleAPIType type) {
51 13284 : if (!m_info.Length()) return;
52 : std::vector<v8::Local<v8::Value>> arguments;
53 33210 : for (int i = 0; i < m_info.Length(); ++i) arguments.push_back(m_info[i]);
54 6642 : reportCall(type, arguments);
55 : }
56 :
57 462 : void reportCallWithDefaultArgument(ConsoleAPIType type,
58 : const String16& message) {
59 : std::vector<v8::Local<v8::Value>> arguments;
60 2184 : for (int i = 0; i < m_info.Length(); ++i) arguments.push_back(m_info[i]);
61 504 : if (!m_info.Length()) arguments.push_back(toV8String(m_isolate, message));
62 462 : reportCall(type, arguments);
63 462 : }
64 :
65 84 : void reportCallWithArgument(ConsoleAPIType type, const String16& message) {
66 : std::vector<v8::Local<v8::Value>> arguments(1,
67 168 : toV8String(m_isolate, message));
68 84 : reportCall(type, arguments);
69 84 : }
70 :
71 7254 : void reportCall(ConsoleAPIType type,
72 7254 : const std::vector<v8::Local<v8::Value>>& arguments) {
73 7254 : if (!m_groupId) return;
74 : std::unique_ptr<V8ConsoleMessage> message =
75 : V8ConsoleMessage::createForConsoleAPI(
76 : m_context, m_contextId, m_groupId, m_inspector,
77 14508 : m_inspector->client()->currentTimeMS(), type, arguments,
78 36270 : m_inspector->debugger()->captureStackTrace(false));
79 14508 : consoleMessageStorage()->addMessage(std::move(message));
80 : }
81 :
82 60 : void reportDeprecatedCall(const char* id, const String16& message) {
83 30 : if (!consoleMessageStorage()->shouldReportDeprecationMessage(m_contextId,
84 90 : id)) {
85 12 : return;
86 : }
87 : std::vector<v8::Local<v8::Value>> arguments(1,
88 36 : toV8String(m_isolate, message));
89 18 : reportCall(ConsoleAPIType::kWarning, arguments);
90 : }
91 :
92 : bool firstArgToBoolean(bool defaultValue) {
93 : if (m_info.Length() < 1) return defaultValue;
94 : if (m_info[0]->IsBoolean()) return m_info[0].As<v8::Boolean>()->Value();
95 : return m_info[0]->BooleanValue(m_context).FromMaybe(defaultValue);
96 : }
97 :
98 218 : String16 firstArgToString(const String16& defaultValue) {
99 436 : if (m_info.Length() < 1) return defaultValue;
100 : v8::Local<v8::String> titleValue;
101 191 : if (m_info[0]->IsObject()) {
102 0 : if (!m_info[0].As<v8::Object>()->ObjectProtoToString(m_context).ToLocal(
103 0 : &titleValue))
104 : return defaultValue;
105 : } else {
106 573 : if (!m_info[0]->ToString(m_context).ToLocal(&titleValue))
107 : return defaultValue;
108 : }
109 191 : return toProtocolString(titleValue);
110 : }
111 :
112 12 : v8::MaybeLocal<v8::Object> firstArgAsObject() {
113 36 : if (m_info.Length() < 1 || !m_info[0]->IsObject())
114 0 : return v8::MaybeLocal<v8::Object>();
115 24 : return m_info[0].As<v8::Object>();
116 : }
117 :
118 126 : v8::MaybeLocal<v8::Function> firstArgAsFunction() {
119 378 : if (m_info.Length() < 1 || !m_info[0]->IsFunction())
120 0 : return v8::MaybeLocal<v8::Function>();
121 126 : v8::Local<v8::Function> func = m_info[0].As<v8::Function>();
122 360 : while (func->GetBoundFunction()->IsFunction())
123 54 : func = func->GetBoundFunction().As<v8::Function>();
124 126 : return func;
125 : }
126 :
127 56 : V8ProfilerAgentImpl* profilerAgent() {
128 56 : if (V8InspectorSessionImpl* session = currentSession()) {
129 112 : if (session && session->profilerAgent()->enabled())
130 56 : return session->profilerAgent();
131 : }
132 : return nullptr;
133 : }
134 :
135 174 : V8DebuggerAgentImpl* debuggerAgent() {
136 174 : if (V8InspectorSessionImpl* session = currentSession()) {
137 348 : if (session && session->debuggerAgent()->enabled())
138 150 : return session->debuggerAgent();
139 : }
140 : return nullptr;
141 : }
142 :
143 : V8InspectorSessionImpl* currentSession() {
144 266 : return m_inspector->sessionForContextGroup(m_groupId);
145 : }
146 :
147 : private:
148 : const v8::debug::ConsoleCallArguments& m_info;
149 : v8::Isolate* m_isolate;
150 : v8::Local<v8::Context> m_context;
151 : V8InspectorImpl* m_inspector = nullptr;
152 : int m_contextId;
153 : int m_groupId;
154 :
155 : DISALLOW_COPY_AND_ASSIGN(ConsoleHelper);
156 : };
157 :
158 672 : void returnDataCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
159 : info.GetReturnValue().Set(info.Data());
160 672 : }
161 :
162 1080 : void createBoundFunctionProperty(v8::Local<v8::Context> context,
163 : v8::Local<v8::Object> console,
164 : v8::Local<v8::Value> data, const char* name,
165 : v8::FunctionCallback callback,
166 : const char* description = nullptr) {
167 : v8::Local<v8::String> funcName =
168 1080 : toV8StringInternalized(context->GetIsolate(), name);
169 : v8::Local<v8::Function> func;
170 1080 : if (!v8::Function::New(context, callback, data, 0,
171 : v8::ConstructorBehavior::kThrow)
172 2160 : .ToLocal(&func))
173 0 : return;
174 1080 : func->SetName(funcName);
175 1080 : if (description) {
176 : v8::Local<v8::String> returnValue =
177 1512 : toV8String(context->GetIsolate(), description);
178 : v8::Local<v8::Function> toStringFunction;
179 756 : if (v8::Function::New(context, returnDataCallback, returnValue, 0,
180 756 : v8::ConstructorBehavior::kThrow)
181 1512 : .ToLocal(&toStringFunction))
182 : createDataProperty(context, func, toV8StringInternalized(
183 : context->GetIsolate(), "toString"),
184 1512 : toStringFunction);
185 : }
186 1080 : createDataProperty(context, console, funcName, func);
187 : }
188 :
189 : } // namespace
190 :
191 9086 : V8Console::V8Console(V8InspectorImpl* inspector) : m_inspector(inspector) {}
192 :
193 6 : void V8Console::Debug(const v8::debug::ConsoleCallArguments& info) {
194 6 : ConsoleHelper(info, m_inspector).reportCall(ConsoleAPIType::kDebug);
195 6 : }
196 :
197 12 : void V8Console::Error(const v8::debug::ConsoleCallArguments& info) {
198 12 : ConsoleHelper(info, m_inspector).reportCall(ConsoleAPIType::kError);
199 12 : }
200 :
201 6 : void V8Console::Info(const v8::debug::ConsoleCallArguments& info) {
202 6 : ConsoleHelper(info, m_inspector).reportCall(ConsoleAPIType::kInfo);
203 6 : }
204 :
205 6450 : void V8Console::Log(const v8::debug::ConsoleCallArguments& info) {
206 6450 : ConsoleHelper(info, m_inspector).reportCall(ConsoleAPIType::kLog);
207 6450 : }
208 :
209 6 : void V8Console::Warn(const v8::debug::ConsoleCallArguments& info) {
210 6 : ConsoleHelper(info, m_inspector).reportCall(ConsoleAPIType::kWarning);
211 6 : }
212 :
213 24 : void V8Console::Dir(const v8::debug::ConsoleCallArguments& info) {
214 24 : ConsoleHelper(info, m_inspector).reportCall(ConsoleAPIType::kDir);
215 24 : }
216 :
217 18 : void V8Console::DirXml(const v8::debug::ConsoleCallArguments& info) {
218 18 : ConsoleHelper(info, m_inspector).reportCall(ConsoleAPIType::kDirXML);
219 18 : }
220 :
221 120 : void V8Console::Table(const v8::debug::ConsoleCallArguments& info) {
222 120 : ConsoleHelper(info, m_inspector).reportCall(ConsoleAPIType::kTable);
223 120 : }
224 :
225 420 : void V8Console::Trace(const v8::debug::ConsoleCallArguments& info) {
226 : ConsoleHelper(info, m_inspector)
227 : .reportCallWithDefaultArgument(ConsoleAPIType::kTrace,
228 840 : String16("console.trace"));
229 420 : }
230 :
231 6 : void V8Console::Group(const v8::debug::ConsoleCallArguments& info) {
232 : ConsoleHelper(info, m_inspector)
233 : .reportCallWithDefaultArgument(ConsoleAPIType::kStartGroup,
234 12 : String16("console.group"));
235 6 : }
236 :
237 6 : void V8Console::GroupCollapsed(const v8::debug::ConsoleCallArguments& info) {
238 : ConsoleHelper(info, m_inspector)
239 : .reportCallWithDefaultArgument(ConsoleAPIType::kStartGroupCollapsed,
240 12 : String16("console.groupCollapsed"));
241 6 : }
242 :
243 6 : void V8Console::GroupEnd(const v8::debug::ConsoleCallArguments& info) {
244 : ConsoleHelper(info, m_inspector)
245 : .reportCallWithDefaultArgument(ConsoleAPIType::kEndGroup,
246 12 : String16("console.groupEnd"));
247 6 : }
248 :
249 24 : void V8Console::Clear(const v8::debug::ConsoleCallArguments& info) {
250 48 : ConsoleHelper helper(info, m_inspector);
251 24 : if (!helper.groupId()) return;
252 48 : m_inspector->client()->consoleClear(helper.groupId());
253 : helper.reportCallWithDefaultArgument(ConsoleAPIType::kClear,
254 48 : String16("console.clear"));
255 : }
256 :
257 24 : void V8Console::Count(const v8::debug::ConsoleCallArguments& info) {
258 24 : ConsoleHelper helper(info, m_inspector);
259 48 : String16 title = helper.firstArgToString(String16());
260 : String16 identifier;
261 24 : if (title.isEmpty()) {
262 : std::unique_ptr<V8StackTraceImpl> stackTrace =
263 36 : V8StackTraceImpl::capture(m_inspector->debugger(), helper.groupId(), 1);
264 18 : if (stackTrace && !stackTrace->isEmpty()) {
265 84 : identifier = toString16(stackTrace->topSourceURL()) + ":" +
266 12 : String16::fromInteger(stackTrace->topLineNumber());
267 : }
268 : } else {
269 18 : identifier = title + "@";
270 : }
271 :
272 : int count =
273 48 : helper.consoleMessageStorage()->count(helper.contextId(), identifier);
274 24 : String16 countString = String16::fromInteger(count);
275 : helper.reportCallWithArgument(
276 : ConsoleAPIType::kCount,
277 66 : title.isEmpty() ? countString : (title + ": " + countString));
278 24 : }
279 :
280 48 : void V8Console::Assert(const v8::debug::ConsoleCallArguments& info) {
281 84 : ConsoleHelper helper(info, m_inspector);
282 : DCHECK(!helper.firstArgToBoolean(false));
283 :
284 : std::vector<v8::Local<v8::Value>> arguments;
285 150 : for (int i = 1; i < info.Length(); ++i) arguments.push_back(info[i]);
286 48 : if (info.Length() < 2)
287 : arguments.push_back(
288 144 : toV8String(m_inspector->isolate(), String16("console.assert")));
289 48 : helper.reportCall(ConsoleAPIType::kAssert, arguments);
290 :
291 48 : if (V8DebuggerAgentImpl* debuggerAgent = helper.debuggerAgent())
292 : debuggerAgent->breakProgramOnException(
293 72 : protocol::Debugger::Paused::ReasonEnum::Assert, nullptr);
294 48 : }
295 :
296 6 : void V8Console::MarkTimeline(const v8::debug::ConsoleCallArguments& info) {
297 : ConsoleHelper(info, m_inspector)
298 : .reportDeprecatedCall("V8Console#markTimelineDeprecated",
299 : "'console.markTimeline' is "
300 : "deprecated. Please use "
301 12 : "'console.timeStamp' instead.");
302 6 : TimeStamp(info);
303 6 : }
304 :
305 28 : void V8Console::Profile(const v8::debug::ConsoleCallArguments& info) {
306 28 : ConsoleHelper helper(info, m_inspector);
307 28 : if (V8ProfilerAgentImpl* profilerAgent = helper.profilerAgent())
308 84 : profilerAgent->consoleProfile(helper.firstArgToString(String16()));
309 28 : }
310 :
311 28 : void V8Console::ProfileEnd(const v8::debug::ConsoleCallArguments& info) {
312 28 : ConsoleHelper helper(info, m_inspector);
313 28 : if (V8ProfilerAgentImpl* profilerAgent = helper.profilerAgent())
314 84 : profilerAgent->consoleProfileEnd(helper.firstArgToString(String16()));
315 28 : }
316 :
317 72 : static void timeFunction(const v8::debug::ConsoleCallArguments& info,
318 72 : bool timelinePrefix, V8InspectorImpl* inspector) {
319 72 : ConsoleHelper helper(info, inspector);
320 144 : String16 protocolTitle = helper.firstArgToString("default");
321 126 : if (timelinePrefix) protocolTitle = "Timeline '" + protocolTitle + "'";
322 72 : inspector->client()->consoleTime(toStringView(protocolTitle));
323 144 : helper.consoleMessageStorage()->time(helper.contextId(), protocolTitle);
324 72 : }
325 :
326 60 : static void timeEndFunction(const v8::debug::ConsoleCallArguments& info,
327 60 : bool timelinePrefix, V8InspectorImpl* inspector) {
328 60 : ConsoleHelper helper(info, inspector);
329 120 : String16 protocolTitle = helper.firstArgToString("default");
330 78 : if (timelinePrefix) protocolTitle = "Timeline '" + protocolTitle + "'";
331 60 : inspector->client()->consoleTimeEnd(toStringView(protocolTitle));
332 : double elapsed = helper.consoleMessageStorage()->timeEnd(helper.contextId(),
333 120 : protocolTitle);
334 : String16 message =
335 360 : protocolTitle + ": " + String16::fromDouble(elapsed) + "ms";
336 60 : helper.reportCallWithArgument(ConsoleAPIType::kTimeEnd, message);
337 60 : }
338 :
339 18 : void V8Console::Timeline(const v8::debug::ConsoleCallArguments& info) {
340 : ConsoleHelper(info, m_inspector)
341 : .reportDeprecatedCall("V8Console#timeline",
342 : "'console.timeline' is deprecated. Please use "
343 36 : "'console.time' instead.");
344 18 : timeFunction(info, true, m_inspector);
345 18 : }
346 :
347 6 : void V8Console::TimelineEnd(const v8::debug::ConsoleCallArguments& info) {
348 : ConsoleHelper(info, m_inspector)
349 : .reportDeprecatedCall("V8Console#timelineEnd",
350 : "'console.timelineEnd' is "
351 : "deprecated. Please use "
352 12 : "'console.timeEnd' instead.");
353 6 : timeEndFunction(info, true, m_inspector);
354 6 : }
355 :
356 54 : void V8Console::Time(const v8::debug::ConsoleCallArguments& info) {
357 54 : timeFunction(info, false, m_inspector);
358 54 : }
359 :
360 54 : void V8Console::TimeEnd(const v8::debug::ConsoleCallArguments& info) {
361 54 : timeEndFunction(info, false, m_inspector);
362 54 : }
363 :
364 6 : void V8Console::TimeStamp(const v8::debug::ConsoleCallArguments& info) {
365 12 : ConsoleHelper helper(info, m_inspector);
366 12 : String16 title = helper.firstArgToString(String16());
367 12 : m_inspector->client()->consoleTimeStamp(toStringView(title));
368 6 : }
369 :
370 24 : void V8Console::memoryGetterCallback(
371 60 : const v8::FunctionCallbackInfo<v8::Value>& info) {
372 : v8::Local<v8::Value> memoryValue;
373 48 : if (!m_inspector->client()
374 : ->memoryInfo(info.GetIsolate(),
375 48 : info.GetIsolate()->GetCurrentContext())
376 48 : .ToLocal(&memoryValue))
377 24 : return;
378 : info.GetReturnValue().Set(memoryValue);
379 : }
380 :
381 0 : void V8Console::memorySetterCallback(
382 : const v8::FunctionCallbackInfo<v8::Value>& info) {
383 : // We can't make the attribute readonly as it breaks existing code that relies
384 : // on being able to assign to console.memory in strict mode. Instead, the
385 : // setter just ignores the passed value. http://crbug.com/468611
386 0 : }
387 :
388 36 : void V8Console::keysCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
389 : v8::Isolate* isolate = info.GetIsolate();
390 12 : info.GetReturnValue().Set(v8::Array::New(isolate));
391 :
392 12 : v8::debug::ConsoleCallArguments args(info);
393 12 : ConsoleHelper helper(args, m_inspector);
394 : v8::Local<v8::Object> obj;
395 24 : if (!helper.firstArgAsObject().ToLocal(&obj)) return;
396 : v8::Local<v8::Array> names;
397 24 : if (!obj->GetOwnPropertyNames(isolate->GetCurrentContext()).ToLocal(&names))
398 : return;
399 : info.GetReturnValue().Set(names);
400 : }
401 :
402 0 : void V8Console::valuesCallback(
403 0 : const v8::FunctionCallbackInfo<v8::Value>& info) {
404 : v8::Isolate* isolate = info.GetIsolate();
405 0 : info.GetReturnValue().Set(v8::Array::New(isolate));
406 :
407 0 : v8::debug::ConsoleCallArguments args(info);
408 0 : ConsoleHelper helper(args, m_inspector);
409 : v8::Local<v8::Object> obj;
410 0 : if (!helper.firstArgAsObject().ToLocal(&obj)) return;
411 : v8::Local<v8::Array> names;
412 0 : v8::Local<v8::Context> context = isolate->GetCurrentContext();
413 0 : if (!obj->GetOwnPropertyNames(context).ToLocal(&names)) return;
414 0 : v8::Local<v8::Array> values = v8::Array::New(isolate, names->Length());
415 0 : for (uint32_t i = 0; i < names->Length(); ++i) {
416 : v8::Local<v8::Value> key;
417 0 : if (!names->Get(context, i).ToLocal(&key)) continue;
418 : v8::Local<v8::Value> value;
419 0 : if (!obj->Get(context, key).ToLocal(&value)) continue;
420 0 : createDataProperty(context, values, i, value);
421 : }
422 : info.GetReturnValue().Set(values);
423 : }
424 :
425 126 : static void setFunctionBreakpoint(ConsoleHelper& helper,
426 : v8::Local<v8::Function> function,
427 : V8DebuggerAgentImpl::BreakpointSource source,
428 : const String16& condition, bool enable) {
429 126 : V8DebuggerAgentImpl* debuggerAgent = helper.debuggerAgent();
430 126 : if (!debuggerAgent) return;
431 126 : String16 scriptId = String16::fromInteger(function->ScriptId());
432 126 : int lineNumber = function->GetScriptLineNumber();
433 126 : int columnNumber = function->GetScriptColumnNumber();
434 126 : if (lineNumber == v8::Function::kLineOffsetNotFound ||
435 : columnNumber == v8::Function::kLineOffsetNotFound)
436 : return;
437 126 : if (enable)
438 : debuggerAgent->setBreakpointAt(scriptId, lineNumber, columnNumber, source,
439 66 : condition);
440 : else
441 : debuggerAgent->removeBreakpointAt(scriptId, lineNumber, columnNumber,
442 60 : source);
443 : }
444 :
445 36 : void V8Console::debugFunctionCallback(
446 : const v8::FunctionCallbackInfo<v8::Value>& info) {
447 36 : v8::debug::ConsoleCallArguments args(info);
448 36 : ConsoleHelper helper(args, m_inspector);
449 : v8::Local<v8::Function> function;
450 72 : if (!helper.firstArgAsFunction().ToLocal(&function)) return;
451 : setFunctionBreakpoint(helper, function,
452 : V8DebuggerAgentImpl::DebugCommandBreakpointSource,
453 72 : String16(), true);
454 : }
455 :
456 30 : void V8Console::undebugFunctionCallback(
457 : const v8::FunctionCallbackInfo<v8::Value>& info) {
458 30 : v8::debug::ConsoleCallArguments args(info);
459 30 : ConsoleHelper helper(args, m_inspector);
460 : v8::Local<v8::Function> function;
461 60 : if (!helper.firstArgAsFunction().ToLocal(&function)) return;
462 : setFunctionBreakpoint(helper, function,
463 : V8DebuggerAgentImpl::DebugCommandBreakpointSource,
464 60 : String16(), false);
465 : }
466 :
467 30 : void V8Console::monitorFunctionCallback(
468 : const v8::FunctionCallbackInfo<v8::Value>& info) {
469 30 : v8::debug::ConsoleCallArguments args(info);
470 30 : ConsoleHelper helper(args, m_inspector);
471 : v8::Local<v8::Function> function;
472 60 : if (!helper.firstArgAsFunction().ToLocal(&function)) return;
473 30 : v8::Local<v8::Value> name = function->GetName();
474 30 : if (!name->IsString() || !v8::Local<v8::String>::Cast(name)->Length())
475 0 : name = function->GetInferredName();
476 30 : String16 functionName = toProtocolStringWithTypeCheck(name);
477 30 : String16Builder builder;
478 60 : builder.append("console.log(\"function ");
479 30 : if (functionName.isEmpty())
480 0 : builder.append("(anonymous function)");
481 : else
482 30 : builder.append(functionName);
483 : builder.append(
484 : " called\" + (arguments.length > 0 ? \" with arguments: \" + "
485 60 : "Array.prototype.join.call(arguments, \", \") : \"\")) && false");
486 : setFunctionBreakpoint(helper, function,
487 : V8DebuggerAgentImpl::MonitorCommandBreakpointSource,
488 60 : builder.toString(), true);
489 : }
490 :
491 30 : void V8Console::unmonitorFunctionCallback(
492 : const v8::FunctionCallbackInfo<v8::Value>& info) {
493 30 : v8::debug::ConsoleCallArguments args(info);
494 30 : ConsoleHelper helper(args, m_inspector);
495 : v8::Local<v8::Function> function;
496 60 : if (!helper.firstArgAsFunction().ToLocal(&function)) return;
497 : setFunctionBreakpoint(helper, function,
498 : V8DebuggerAgentImpl::MonitorCommandBreakpointSource,
499 60 : String16(), false);
500 : }
501 :
502 66 : void V8Console::lastEvaluationResultCallback(
503 66 : const v8::FunctionCallbackInfo<v8::Value>& info) {
504 66 : v8::debug::ConsoleCallArguments args(info);
505 66 : ConsoleHelper helper(args, m_inspector);
506 66 : InjectedScript* injectedScript = helper.injectedScript();
507 66 : if (!injectedScript) return;
508 66 : info.GetReturnValue().Set(injectedScript->lastEvaluationResult());
509 : }
510 :
511 54 : static void inspectImpl(const v8::FunctionCallbackInfo<v8::Value>& info,
512 : bool copyToClipboard, V8InspectorImpl* inspector) {
513 30 : if (info.Length() < 1) return;
514 30 : if (!copyToClipboard) info.GetReturnValue().Set(info[0]);
515 :
516 30 : v8::debug::ConsoleCallArguments args(info);
517 30 : ConsoleHelper helper(args, inspector);
518 30 : InjectedScript* injectedScript = helper.injectedScript();
519 30 : if (!injectedScript) return;
520 30 : std::unique_ptr<protocol::Runtime::RemoteObject> wrappedObject;
521 : protocol::Response response =
522 : injectedScript->wrapObject(info[0], "", false /** forceValueType */,
523 90 : false /** generatePreview */, &wrappedObject);
524 30 : if (!response.isSuccess()) return;
525 :
526 : std::unique_ptr<protocol::DictionaryValue> hints =
527 30 : protocol::DictionaryValue::create();
528 42 : if (copyToClipboard) hints->setBoolean("copyToClipboard", true);
529 60 : if (V8InspectorSessionImpl* session = helper.currentSession()) {
530 : session->runtimeAgent()->inspect(std::move(wrappedObject),
531 90 : std::move(hints));
532 : }
533 : }
534 :
535 0 : void V8Console::inspectCallback(
536 : const v8::FunctionCallbackInfo<v8::Value>& info) {
537 24 : inspectImpl(info, false, m_inspector);
538 0 : }
539 :
540 0 : void V8Console::copyCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
541 6 : inspectImpl(info, true, m_inspector);
542 0 : }
543 :
544 12 : void V8Console::inspectedObject(const v8::FunctionCallbackInfo<v8::Value>& info,
545 : unsigned num) {
546 : DCHECK(num < V8InspectorSessionImpl::kInspectedObjectBufferSize);
547 6 : v8::debug::ConsoleCallArguments args(info);
548 6 : ConsoleHelper helper(args, m_inspector);
549 12 : if (V8InspectorSessionImpl* session = helper.currentSession()) {
550 6 : V8InspectorSession::Inspectable* object = session->inspectedObject(num);
551 : v8::Isolate* isolate = info.GetIsolate();
552 6 : if (object)
553 0 : info.GetReturnValue().Set(object->get(isolate->GetCurrentContext()));
554 : else
555 : info.GetReturnValue().Set(v8::Undefined(isolate));
556 : }
557 6 : }
558 :
559 753 : void V8Console::installMemoryGetter(v8::Local<v8::Context> context,
560 : v8::Local<v8::Object> console) {
561 753 : v8::Isolate* isolate = context->GetIsolate();
562 753 : v8::Local<v8::External> data = v8::External::New(isolate, this);
563 : console->SetAccessorProperty(
564 : toV8StringInternalized(isolate, "memory"),
565 : v8::Function::New(context,
566 : &V8Console::call<&V8Console::memoryGetterCallback>,
567 753 : data, 0, v8::ConstructorBehavior::kThrow)
568 : .ToLocalChecked(),
569 : v8::Function::New(context,
570 : &V8Console::call<&V8Console::memorySetterCallback>,
571 753 : data, 0, v8::ConstructorBehavior::kThrow)
572 : .ToLocalChecked(),
573 3012 : static_cast<v8::PropertyAttribute>(v8::None), v8::DEFAULT);
574 753 : }
575 :
576 54 : v8::Local<v8::Object> V8Console::createCommandLineAPI(
577 : v8::Local<v8::Context> context) {
578 54 : v8::Isolate* isolate = context->GetIsolate();
579 : v8::MicrotasksScope microtasksScope(isolate,
580 54 : v8::MicrotasksScope::kDoNotRunMicrotasks);
581 :
582 54 : v8::Local<v8::Object> commandLineAPI = v8::Object::New(isolate);
583 : bool success =
584 54 : commandLineAPI->SetPrototype(context, v8::Null(isolate)).FromMaybe(false);
585 : DCHECK(success);
586 : USE(success);
587 :
588 54 : v8::Local<v8::External> data = v8::External::New(isolate, this);
589 : createBoundFunctionProperty(context, commandLineAPI, data, "dir",
590 : &V8Console::call<&V8Console::Dir>,
591 54 : "function dir(value) { [Command Line API] }");
592 : createBoundFunctionProperty(context, commandLineAPI, data, "dirxml",
593 : &V8Console::call<&V8Console::DirXml>,
594 54 : "function dirxml(value) { [Command Line API] }");
595 : createBoundFunctionProperty(context, commandLineAPI, data, "profile",
596 : &V8Console::call<&V8Console::Profile>,
597 54 : "function profile(title) { [Command Line API] }");
598 : createBoundFunctionProperty(
599 : context, commandLineAPI, data, "profileEnd",
600 : &V8Console::call<&V8Console::ProfileEnd>,
601 54 : "function profileEnd(title) { [Command Line API] }");
602 : createBoundFunctionProperty(context, commandLineAPI, data, "clear",
603 : &V8Console::call<&V8Console::Clear>,
604 54 : "function clear() { [Command Line API] }");
605 : createBoundFunctionProperty(
606 : context, commandLineAPI, data, "table",
607 : &V8Console::call<&V8Console::Table>,
608 54 : "function table(data, [columns]) { [Command Line API] }");
609 :
610 : createBoundFunctionProperty(context, commandLineAPI, data, "keys",
611 : &V8Console::call<&V8Console::keysCallback>,
612 54 : "function keys(object) { [Command Line API] }");
613 : createBoundFunctionProperty(context, commandLineAPI, data, "values",
614 : &V8Console::call<&V8Console::valuesCallback>,
615 54 : "function values(object) { [Command Line API] }");
616 : createBoundFunctionProperty(
617 : context, commandLineAPI, data, "debug",
618 : &V8Console::call<&V8Console::debugFunctionCallback>,
619 54 : "function debug(function) { [Command Line API] }");
620 : createBoundFunctionProperty(
621 : context, commandLineAPI, data, "undebug",
622 : &V8Console::call<&V8Console::undebugFunctionCallback>,
623 54 : "function undebug(function) { [Command Line API] }");
624 : createBoundFunctionProperty(
625 : context, commandLineAPI, data, "monitor",
626 : &V8Console::call<&V8Console::monitorFunctionCallback>,
627 54 : "function monitor(function) { [Command Line API] }");
628 : createBoundFunctionProperty(
629 : context, commandLineAPI, data, "unmonitor",
630 : &V8Console::call<&V8Console::unmonitorFunctionCallback>,
631 54 : "function unmonitor(function) { [Command Line API] }");
632 : createBoundFunctionProperty(
633 : context, commandLineAPI, data, "inspect",
634 : &V8Console::call<&V8Console::inspectCallback>,
635 54 : "function inspect(object) { [Command Line API] }");
636 : createBoundFunctionProperty(context, commandLineAPI, data, "copy",
637 : &V8Console::call<&V8Console::copyCallback>,
638 54 : "function copy(value) { [Command Line API] }");
639 : createBoundFunctionProperty(
640 : context, commandLineAPI, data, "$_",
641 54 : &V8Console::call<&V8Console::lastEvaluationResultCallback>);
642 : createBoundFunctionProperty(context, commandLineAPI, data, "$0",
643 54 : &V8Console::call<&V8Console::inspectedObject0>);
644 : createBoundFunctionProperty(context, commandLineAPI, data, "$1",
645 54 : &V8Console::call<&V8Console::inspectedObject1>);
646 : createBoundFunctionProperty(context, commandLineAPI, data, "$2",
647 54 : &V8Console::call<&V8Console::inspectedObject2>);
648 : createBoundFunctionProperty(context, commandLineAPI, data, "$3",
649 54 : &V8Console::call<&V8Console::inspectedObject3>);
650 : createBoundFunctionProperty(context, commandLineAPI, data, "$4",
651 54 : &V8Console::call<&V8Console::inspectedObject4>);
652 :
653 54 : m_inspector->client()->installAdditionalCommandLineAPI(context,
654 54 : commandLineAPI);
655 54 : return commandLineAPI;
656 : }
657 :
658 : static bool isCommandLineAPIGetter(const String16& name) {
659 1896 : if (name.length() != 2) return false;
660 : // $0 ... $4, $_
661 144 : return name[0] == '$' &&
662 72 : ((name[1] >= '0' && name[1] <= '4') || name[1] == '_');
663 : }
664 :
665 8778 : void V8Console::CommandLineAPIScope::accessorGetterCallback(
666 : v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
667 : CommandLineAPIScope* scope = static_cast<CommandLineAPIScope*>(
668 8778 : info.Data().As<v8::External>()->Value());
669 : DCHECK(scope);
670 :
671 8778 : v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
672 8778 : if (scope->m_cleanup) {
673 6882 : bool removed = info.Holder()->Delete(context, name).FromMaybe(false);
674 : DCHECK(removed);
675 : USE(removed);
676 6882 : return;
677 : }
678 1896 : v8::Local<v8::Object> commandLineAPI = scope->m_commandLineAPI;
679 :
680 : v8::Local<v8::Value> value;
681 3792 : if (!commandLineAPI->Get(context, name).ToLocal(&value)) return;
682 3792 : if (isCommandLineAPIGetter(toProtocolStringWithTypeCheck(name))) {
683 : DCHECK(value->IsFunction());
684 : v8::MicrotasksScope microtasks(info.GetIsolate(),
685 72 : v8::MicrotasksScope::kDoNotRunMicrotasks);
686 72 : if (value.As<v8::Function>()
687 72 : ->Call(context, commandLineAPI, 0, nullptr)
688 144 : .ToLocal(&value))
689 72 : info.GetReturnValue().Set(value);
690 : } else {
691 : info.GetReturnValue().Set(value);
692 : }
693 : }
694 :
695 66 : void V8Console::CommandLineAPIScope::accessorSetterCallback(
696 : v8::Local<v8::Name> name, v8::Local<v8::Value> value,
697 : const v8::PropertyCallbackInfo<void>& info) {
698 : CommandLineAPIScope* scope = static_cast<CommandLineAPIScope*>(
699 66 : info.Data().As<v8::External>()->Value());
700 66 : v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
701 132 : if (!info.Holder()->Delete(context, name).FromMaybe(false)) return;
702 132 : if (!info.Holder()->CreateDataProperty(context, name, value).FromMaybe(false))
703 : return;
704 : bool removed =
705 66 : scope->m_installedMethods->Delete(context, name).FromMaybe(false);
706 : DCHECK(removed);
707 : USE(removed);
708 : }
709 :
710 408 : V8Console::CommandLineAPIScope::CommandLineAPIScope(
711 : v8::Local<v8::Context> context, v8::Local<v8::Object> commandLineAPI,
712 : v8::Local<v8::Object> global)
713 : : m_context(context),
714 : m_commandLineAPI(commandLineAPI),
715 : m_global(global),
716 408 : m_installedMethods(v8::Set::New(context->GetIsolate())),
717 816 : m_cleanup(false) {
718 : v8::Local<v8::Array> names;
719 816 : if (!m_commandLineAPI->GetOwnPropertyNames(context).ToLocal(&names)) return;
720 : v8::Local<v8::External> externalThis =
721 408 : v8::External::New(context->GetIsolate(), this);
722 8568 : for (uint32_t i = 0; i < names->Length(); ++i) {
723 : v8::Local<v8::Value> name;
724 16320 : if (!names->Get(context, i).ToLocal(&name) || !name->IsName()) continue;
725 16320 : if (m_global->Has(context, name).FromMaybe(true)) continue;
726 13896 : if (!m_installedMethods->Add(context, name).ToLocal(&m_installedMethods))
727 : continue;
728 6948 : if (!m_global
729 : ->SetAccessor(context, v8::Local<v8::Name>::Cast(name),
730 : CommandLineAPIScope::accessorGetterCallback,
731 : CommandLineAPIScope::accessorSetterCallback,
732 6948 : externalThis, v8::DEFAULT, v8::DontEnum)
733 13896 : .FromMaybe(false)) {
734 0 : bool removed = m_installedMethods->Delete(context, name).FromMaybe(false);
735 : DCHECK(removed);
736 : USE(removed);
737 : continue;
738 : }
739 : }
740 : }
741 :
742 408 : V8Console::CommandLineAPIScope::~CommandLineAPIScope() {
743 408 : m_cleanup = true;
744 408 : v8::Local<v8::Array> names = m_installedMethods->AsArray();
745 7290 : for (uint32_t i = 0; i < names->Length(); ++i) {
746 : v8::Local<v8::Value> name;
747 13764 : if (!names->Get(m_context, i).ToLocal(&name) || !name->IsName()) continue;
748 6882 : if (name->IsString()) {
749 : v8::Local<v8::Value> descriptor;
750 : bool success = m_global
751 : ->GetOwnPropertyDescriptor(
752 : m_context, v8::Local<v8::String>::Cast(name))
753 6882 : .ToLocal(&descriptor);
754 : DCHECK(success);
755 : USE(success);
756 : }
757 : }
758 408 : }
759 :
760 : } // namespace v8_inspector
|