OLD | NEW |
| (Empty) |
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/V8ConsoleMessage.h" | |
6 | |
7 #include "src/inspector/InspectedContext.h" | |
8 #include "src/inspector/StringUtil.h" | |
9 #include "src/inspector/V8ConsoleAgentImpl.h" | |
10 #include "src/inspector/V8InspectorImpl.h" | |
11 #include "src/inspector/V8InspectorSessionImpl.h" | |
12 #include "src/inspector/V8RuntimeAgentImpl.h" | |
13 #include "src/inspector/V8StackTraceImpl.h" | |
14 #include "src/inspector/protocol/Protocol.h" | |
15 #include "src/inspector/public/V8InspectorClient.h" | |
16 | |
17 namespace v8_inspector { | |
18 | |
19 namespace { | |
20 | |
21 String16 consoleAPITypeValue(ConsoleAPIType type) | |
22 { | |
23 switch (type) { | |
24 case ConsoleAPIType::kLog: return protocol::Runtime::ConsoleAPICalled::TypeE
num::Log; | |
25 case ConsoleAPIType::kDebug: return protocol::Runtime::ConsoleAPICalled::Typ
eEnum::Debug; | |
26 case ConsoleAPIType::kInfo: return protocol::Runtime::ConsoleAPICalled::Type
Enum::Info; | |
27 case ConsoleAPIType::kError: return protocol::Runtime::ConsoleAPICalled::Typ
eEnum::Error; | |
28 case ConsoleAPIType::kWarning: return protocol::Runtime::ConsoleAPICalled::T
ypeEnum::Warning; | |
29 case ConsoleAPIType::kClear: return protocol::Runtime::ConsoleAPICalled::Typ
eEnum::Clear; | |
30 case ConsoleAPIType::kDir: return protocol::Runtime::ConsoleAPICalled::TypeE
num::Dir; | |
31 case ConsoleAPIType::kDirXML: return protocol::Runtime::ConsoleAPICalled::Ty
peEnum::Dirxml; | |
32 case ConsoleAPIType::kTable: return protocol::Runtime::ConsoleAPICalled::Typ
eEnum::Table; | |
33 case ConsoleAPIType::kTrace: return protocol::Runtime::ConsoleAPICalled::Typ
eEnum::Trace; | |
34 case ConsoleAPIType::kStartGroup: return protocol::Runtime::ConsoleAPICalled
::TypeEnum::StartGroup; | |
35 case ConsoleAPIType::kStartGroupCollapsed: return protocol::Runtime::Console
APICalled::TypeEnum::StartGroupCollapsed; | |
36 case ConsoleAPIType::kEndGroup: return protocol::Runtime::ConsoleAPICalled::
TypeEnum::EndGroup; | |
37 case ConsoleAPIType::kAssert: return protocol::Runtime::ConsoleAPICalled::Ty
peEnum::Assert; | |
38 case ConsoleAPIType::kTimeEnd: return protocol::Runtime::ConsoleAPICalled::T
ypeEnum::Debug; | |
39 case ConsoleAPIType::kCount: return protocol::Runtime::ConsoleAPICalled::Typ
eEnum::Debug; | |
40 } | |
41 return protocol::Runtime::ConsoleAPICalled::TypeEnum::Log; | |
42 } | |
43 | |
44 const unsigned maxConsoleMessageCount = 1000; | |
45 const unsigned maxArrayItemsLimit = 10000; | |
46 const unsigned maxStackDepthLimit = 32; | |
47 | |
48 class V8ValueStringBuilder { | |
49 public: | |
50 static String16 toString(v8::Local<v8::Value> value, v8::Isolate* isolate) | |
51 { | |
52 V8ValueStringBuilder builder(isolate); | |
53 if (!builder.append(value)) | |
54 return String16(); | |
55 return builder.toString(); | |
56 } | |
57 | |
58 private: | |
59 enum { | |
60 IgnoreNull = 1 << 0, | |
61 IgnoreUndefined = 1 << 1, | |
62 }; | |
63 | |
64 V8ValueStringBuilder(v8::Isolate* isolate) | |
65 : m_arrayLimit(maxArrayItemsLimit) | |
66 , m_isolate(isolate) | |
67 , m_tryCatch(isolate) | |
68 { | |
69 } | |
70 | |
71 bool append(v8::Local<v8::Value> value, unsigned ignoreOptions = 0) | |
72 { | |
73 if (value.IsEmpty()) | |
74 return true; | |
75 if ((ignoreOptions & IgnoreNull) && value->IsNull()) | |
76 return true; | |
77 if ((ignoreOptions & IgnoreUndefined) && value->IsUndefined()) | |
78 return true; | |
79 if (value->IsString()) | |
80 return append(v8::Local<v8::String>::Cast(value)); | |
81 if (value->IsStringObject()) | |
82 return append(v8::Local<v8::StringObject>::Cast(value)->ValueOf()); | |
83 if (value->IsSymbol()) | |
84 return append(v8::Local<v8::Symbol>::Cast(value)); | |
85 if (value->IsSymbolObject()) | |
86 return append(v8::Local<v8::SymbolObject>::Cast(value)->ValueOf()); | |
87 if (value->IsNumberObject()) { | |
88 m_builder.append(String16::fromDoublePrecision6(v8::Local<v8::Number
Object>::Cast(value)->ValueOf())); | |
89 return true; | |
90 } | |
91 if (value->IsBooleanObject()) { | |
92 m_builder.append(v8::Local<v8::BooleanObject>::Cast(value)->ValueOf(
) ? "true" : "false"); | |
93 return true; | |
94 } | |
95 if (value->IsArray()) | |
96 return append(v8::Local<v8::Array>::Cast(value)); | |
97 if (value->IsProxy()) { | |
98 m_builder.append("[object Proxy]"); | |
99 return true; | |
100 } | |
101 if (value->IsObject() | |
102 && !value->IsDate() | |
103 && !value->IsFunction() | |
104 && !value->IsNativeError() | |
105 && !value->IsRegExp()) { | |
106 v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(value); | |
107 v8::Local<v8::String> stringValue; | |
108 if (object->ObjectProtoToString(m_isolate->GetCurrentContext()).ToLo
cal(&stringValue)) | |
109 return append(stringValue); | |
110 } | |
111 v8::Local<v8::String> stringValue; | |
112 if (!value->ToString(m_isolate->GetCurrentContext()).ToLocal(&stringValu
e)) | |
113 return false; | |
114 return append(stringValue); | |
115 } | |
116 | |
117 bool append(v8::Local<v8::Array> array) | |
118 { | |
119 for (const auto& it : m_visitedArrays) { | |
120 if (it == array) | |
121 return true; | |
122 } | |
123 uint32_t length = array->Length(); | |
124 if (length > m_arrayLimit) | |
125 return false; | |
126 if (m_visitedArrays.size() > maxStackDepthLimit) | |
127 return false; | |
128 | |
129 bool result = true; | |
130 m_arrayLimit -= length; | |
131 m_visitedArrays.push_back(array); | |
132 for (uint32_t i = 0; i < length; ++i) { | |
133 if (i) | |
134 m_builder.append(','); | |
135 if (!append(array->Get(i), IgnoreNull | IgnoreUndefined)) { | |
136 result = false; | |
137 break; | |
138 } | |
139 } | |
140 m_visitedArrays.pop_back(); | |
141 return result; | |
142 } | |
143 | |
144 bool append(v8::Local<v8::Symbol> symbol) | |
145 { | |
146 m_builder.append("Symbol("); | |
147 bool result = append(symbol->Name(), IgnoreUndefined); | |
148 m_builder.append(')'); | |
149 return result; | |
150 } | |
151 | |
152 bool append(v8::Local<v8::String> string) | |
153 { | |
154 if (m_tryCatch.HasCaught()) | |
155 return false; | |
156 if (!string.IsEmpty()) | |
157 m_builder.append(toProtocolString(string)); | |
158 return true; | |
159 } | |
160 | |
161 String16 toString() | |
162 { | |
163 if (m_tryCatch.HasCaught()) | |
164 return String16(); | |
165 return m_builder.toString(); | |
166 } | |
167 | |
168 uint32_t m_arrayLimit; | |
169 v8::Isolate* m_isolate; | |
170 String16Builder m_builder; | |
171 std::vector<v8::Local<v8::Array>> m_visitedArrays; | |
172 v8::TryCatch m_tryCatch; | |
173 }; | |
174 | |
175 } // namespace | |
176 | |
177 V8ConsoleMessage::V8ConsoleMessage(V8MessageOrigin origin, double timestamp, con
st String16& message) | |
178 : m_origin(origin) | |
179 , m_timestamp(timestamp) | |
180 , m_message(message) | |
181 , m_lineNumber(0) | |
182 , m_columnNumber(0) | |
183 , m_scriptId(0) | |
184 , m_contextId(0) | |
185 , m_type(ConsoleAPIType::kLog) | |
186 , m_exceptionId(0) | |
187 , m_revokedExceptionId(0) | |
188 { | |
189 } | |
190 | |
191 V8ConsoleMessage::~V8ConsoleMessage() | |
192 { | |
193 } | |
194 | |
195 void V8ConsoleMessage::setLocation(const String16& url, unsigned lineNumber, uns
igned columnNumber, std::unique_ptr<V8StackTraceImpl> stackTrace, int scriptId) | |
196 { | |
197 m_url = url; | |
198 m_lineNumber = lineNumber; | |
199 m_columnNumber = columnNumber; | |
200 m_stackTrace = std::move(stackTrace); | |
201 m_scriptId = scriptId; | |
202 } | |
203 | |
204 void V8ConsoleMessage::reportToFrontend(protocol::Console::Frontend* frontend) c
onst | |
205 { | |
206 DCHECK(m_origin == V8MessageOrigin::kConsole); | |
207 String16 level = protocol::Console::ConsoleMessage::LevelEnum::Log; | |
208 if (m_type == ConsoleAPIType::kDebug || m_type == ConsoleAPIType::kCount ||
m_type == ConsoleAPIType::kTimeEnd) | |
209 level = protocol::Console::ConsoleMessage::LevelEnum::Debug; | |
210 else if (m_type == ConsoleAPIType::kError || m_type == ConsoleAPIType::kAsse
rt) | |
211 level = protocol::Console::ConsoleMessage::LevelEnum::Error; | |
212 else if (m_type == ConsoleAPIType::kWarning) | |
213 level = protocol::Console::ConsoleMessage::LevelEnum::Warning; | |
214 else if (m_type == ConsoleAPIType::kInfo) | |
215 level = protocol::Console::ConsoleMessage::LevelEnum::Info; | |
216 std::unique_ptr<protocol::Console::ConsoleMessage> result = | |
217 protocol::Console::ConsoleMessage::create() | |
218 .setSource(protocol::Console::ConsoleMessage::SourceEnum::ConsoleApi) | |
219 .setLevel(level) | |
220 .setText(m_message) | |
221 .build(); | |
222 result->setLine(static_cast<int>(m_lineNumber)); | |
223 result->setColumn(static_cast<int>(m_columnNumber)); | |
224 result->setUrl(m_url); | |
225 frontend->messageAdded(std::move(result)); | |
226 } | |
227 | |
228 std::unique_ptr<protocol::Array<protocol::Runtime::RemoteObject>> V8ConsoleMessa
ge::wrapArguments(V8InspectorSessionImpl* session, bool generatePreview) const | |
229 { | |
230 if (!m_arguments.size() || !m_contextId) | |
231 return nullptr; | |
232 InspectedContext* inspectedContext = session->inspector()->getContext(sessio
n->contextGroupId(), m_contextId); | |
233 if (!inspectedContext) | |
234 return nullptr; | |
235 | |
236 v8::Isolate* isolate = inspectedContext->isolate(); | |
237 v8::HandleScope handles(isolate); | |
238 v8::Local<v8::Context> context = inspectedContext->context(); | |
239 | |
240 std::unique_ptr<protocol::Array<protocol::Runtime::RemoteObject>> args = pro
tocol::Array<protocol::Runtime::RemoteObject>::create(); | |
241 if (m_type == ConsoleAPIType::kTable && generatePreview) { | |
242 v8::Local<v8::Value> table = m_arguments[0]->Get(isolate); | |
243 v8::Local<v8::Value> columns = m_arguments.size() > 1 ? m_arguments[1]->
Get(isolate) : v8::Local<v8::Value>(); | |
244 std::unique_ptr<protocol::Runtime::RemoteObject> wrapped = session->wrap
Table(context, table, columns); | |
245 if (wrapped) | |
246 args->addItem(std::move(wrapped)); | |
247 else | |
248 args = nullptr; | |
249 } else { | |
250 for (size_t i = 0; i < m_arguments.size(); ++i) { | |
251 std::unique_ptr<protocol::Runtime::RemoteObject> wrapped = session->
wrapObject(context, m_arguments[i]->Get(isolate), "console", generatePreview); | |
252 if (!wrapped) { | |
253 args = nullptr; | |
254 break; | |
255 } | |
256 args->addItem(std::move(wrapped)); | |
257 } | |
258 } | |
259 return args; | |
260 } | |
261 | |
262 void V8ConsoleMessage::reportToFrontend(protocol::Runtime::Frontend* frontend, V
8InspectorSessionImpl* session, bool generatePreview) const | |
263 { | |
264 if (m_origin == V8MessageOrigin::kException) { | |
265 std::unique_ptr<protocol::Runtime::RemoteObject> exception = wrapExcepti
on(session, generatePreview); | |
266 std::unique_ptr<protocol::Runtime::ExceptionDetails> exceptionDetails =
protocol::Runtime::ExceptionDetails::create() | |
267 .setExceptionId(m_exceptionId) | |
268 .setText(exception ? m_message : m_detailedMessage) | |
269 .setLineNumber(m_lineNumber ? m_lineNumber - 1 : 0) | |
270 .setColumnNumber(m_columnNumber ? m_columnNumber - 1 : 0) | |
271 .build(); | |
272 if (m_scriptId) | |
273 exceptionDetails->setScriptId(String16::fromInteger(m_scriptId)); | |
274 if (!m_url.isEmpty()) | |
275 exceptionDetails->setUrl(m_url); | |
276 if (m_stackTrace) | |
277 exceptionDetails->setStackTrace(m_stackTrace->buildInspectorObjectIm
pl()); | |
278 if (m_contextId) | |
279 exceptionDetails->setExecutionContextId(m_contextId); | |
280 if (exception) | |
281 exceptionDetails->setException(std::move(exception)); | |
282 frontend->exceptionThrown(m_timestamp, std::move(exceptionDetails)); | |
283 return; | |
284 } | |
285 if (m_origin == V8MessageOrigin::kRevokedException) { | |
286 frontend->exceptionRevoked(m_message, m_revokedExceptionId); | |
287 return; | |
288 } | |
289 if (m_origin == V8MessageOrigin::kConsole) { | |
290 std::unique_ptr<protocol::Array<protocol::Runtime::RemoteObject>> argume
nts = wrapArguments(session, generatePreview); | |
291 if (!arguments) { | |
292 arguments = protocol::Array<protocol::Runtime::RemoteObject>::create
(); | |
293 if (!m_message.isEmpty()) { | |
294 std::unique_ptr<protocol::Runtime::RemoteObject> messageArg = pr
otocol::Runtime::RemoteObject::create().setType(protocol::Runtime::RemoteObject:
:TypeEnum::String).build(); | |
295 messageArg->setValue(protocol::StringValue::create(m_message)); | |
296 arguments->addItem(std::move(messageArg)); | |
297 } | |
298 } | |
299 frontend->consoleAPICalled(consoleAPITypeValue(m_type), std::move(argume
nts), m_contextId, m_timestamp, m_stackTrace ? m_stackTrace->buildInspectorObjec
tImpl() : nullptr); | |
300 return; | |
301 } | |
302 NOTREACHED(); | |
303 } | |
304 | |
305 std::unique_ptr<protocol::Runtime::RemoteObject> V8ConsoleMessage::wrapException
(V8InspectorSessionImpl* session, bool generatePreview) const | |
306 { | |
307 if (!m_arguments.size() || !m_contextId) | |
308 return nullptr; | |
309 DCHECK_EQ(1u, m_arguments.size()); | |
310 InspectedContext* inspectedContext = session->inspector()->getContext(sessio
n->contextGroupId(), m_contextId); | |
311 if (!inspectedContext) | |
312 return nullptr; | |
313 | |
314 v8::Isolate* isolate = inspectedContext->isolate(); | |
315 v8::HandleScope handles(isolate); | |
316 // TODO(dgozman): should we use different object group? | |
317 return session->wrapObject(inspectedContext->context(), m_arguments[0]->Get(
isolate), "console", generatePreview); | |
318 } | |
319 | |
320 V8MessageOrigin V8ConsoleMessage::origin() const | |
321 { | |
322 return m_origin; | |
323 } | |
324 | |
325 ConsoleAPIType V8ConsoleMessage::type() const | |
326 { | |
327 return m_type; | |
328 } | |
329 | |
330 // static | |
331 std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForConsoleAPI(double t
imestamp, ConsoleAPIType type, const std::vector<v8::Local<v8::Value>>& argument
s, std::unique_ptr<V8StackTraceImpl> stackTrace, InspectedContext* context) | |
332 { | |
333 std::unique_ptr<V8ConsoleMessage> message = wrapUnique(new V8ConsoleMessage(
V8MessageOrigin::kConsole, timestamp, String16())); | |
334 if (stackTrace && !stackTrace->isEmpty()) { | |
335 message->m_url = toString16(stackTrace->topSourceURL()); | |
336 message->m_lineNumber = stackTrace->topLineNumber(); | |
337 message->m_columnNumber = stackTrace->topColumnNumber(); | |
338 } | |
339 message->m_stackTrace = std::move(stackTrace); | |
340 message->m_type = type; | |
341 message->m_contextId = context->contextId(); | |
342 for (size_t i = 0; i < arguments.size(); ++i) | |
343 message->m_arguments.push_back(wrapUnique(new v8::Global<v8::Value>(cont
ext->isolate(), arguments.at(i)))); | |
344 if (arguments.size()) | |
345 message->m_message = V8ValueStringBuilder::toString(arguments[0], contex
t->isolate()); | |
346 | |
347 V8ConsoleAPIType clientType = V8ConsoleAPIType::kLog; | |
348 if (type == ConsoleAPIType::kDebug || type == ConsoleAPIType::kCount || type
== ConsoleAPIType::kTimeEnd) | |
349 clientType = V8ConsoleAPIType::kDebug; | |
350 else if (type == ConsoleAPIType::kError || type == ConsoleAPIType::kAssert) | |
351 clientType = V8ConsoleAPIType::kError; | |
352 else if (type == ConsoleAPIType::kWarning) | |
353 clientType = V8ConsoleAPIType::kWarning; | |
354 else if (type == ConsoleAPIType::kInfo) | |
355 clientType = V8ConsoleAPIType::kInfo; | |
356 else if (type == ConsoleAPIType::kClear) | |
357 clientType = V8ConsoleAPIType::kClear; | |
358 context->inspector()->client()->consoleAPIMessage(context->contextGroupId(),
clientType, toStringView(message->m_message), toStringView(message->m_url), mes
sage->m_lineNumber, message->m_columnNumber, message->m_stackTrace.get()); | |
359 | |
360 return message; | |
361 } | |
362 | |
363 // static | |
364 std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForException(double ti
mestamp, const String16& detailedMessage, const String16& url, unsigned lineNumb
er, unsigned columnNumber, std::unique_ptr<V8StackTraceImpl> stackTrace, int scr
iptId, v8::Isolate* isolate, const String16& message, int contextId, v8::Local<v
8::Value> exception, unsigned exceptionId) | |
365 { | |
366 std::unique_ptr<V8ConsoleMessage> consoleMessage = wrapUnique(new V8ConsoleM
essage(V8MessageOrigin::kException, timestamp, message)); | |
367 consoleMessage->setLocation(url, lineNumber, columnNumber, std::move(stackTr
ace), scriptId); | |
368 consoleMessage->m_exceptionId = exceptionId; | |
369 consoleMessage->m_detailedMessage = detailedMessage; | |
370 if (contextId && !exception.IsEmpty()) { | |
371 consoleMessage->m_contextId = contextId; | |
372 consoleMessage->m_arguments.push_back(wrapUnique(new v8::Global<v8::Valu
e>(isolate, exception))); | |
373 } | |
374 return consoleMessage; | |
375 } | |
376 | |
377 // static | |
378 std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForRevokedException(do
uble timestamp, const String16& messageText, unsigned revokedExceptionId) | |
379 { | |
380 std::unique_ptr<V8ConsoleMessage> message = wrapUnique(new V8ConsoleMessage(
V8MessageOrigin::kRevokedException, timestamp, messageText)); | |
381 message->m_revokedExceptionId = revokedExceptionId; | |
382 return message; | |
383 } | |
384 | |
385 void V8ConsoleMessage::contextDestroyed(int contextId) | |
386 { | |
387 if (contextId != m_contextId) | |
388 return; | |
389 m_contextId = 0; | |
390 if (m_message.isEmpty()) | |
391 m_message = "<message collected>"; | |
392 Arguments empty; | |
393 m_arguments.swap(empty); | |
394 } | |
395 | |
396 // ------------------------ V8ConsoleMessageStorage ---------------------------- | |
397 | |
398 V8ConsoleMessageStorage::V8ConsoleMessageStorage(V8InspectorImpl* inspector, int
contextGroupId) | |
399 : m_inspector(inspector) | |
400 , m_contextGroupId(contextGroupId) | |
401 , m_expiredCount(0) | |
402 { | |
403 } | |
404 | |
405 V8ConsoleMessageStorage::~V8ConsoleMessageStorage() | |
406 { | |
407 clear(); | |
408 } | |
409 | |
410 void V8ConsoleMessageStorage::addMessage(std::unique_ptr<V8ConsoleMessage> messa
ge) | |
411 { | |
412 if (message->type() == ConsoleAPIType::kClear) | |
413 clear(); | |
414 | |
415 V8InspectorSessionImpl* session = m_inspector->sessionForContextGroup(m_cont
extGroupId); | |
416 if (session) { | |
417 if (message->origin() == V8MessageOrigin::kConsole) | |
418 session->consoleAgent()->messageAdded(message.get()); | |
419 session->runtimeAgent()->messageAdded(message.get()); | |
420 } | |
421 | |
422 DCHECK(m_messages.size() <= maxConsoleMessageCount); | |
423 if (m_messages.size() == maxConsoleMessageCount) { | |
424 ++m_expiredCount; | |
425 m_messages.pop_front(); | |
426 } | |
427 m_messages.push_back(std::move(message)); | |
428 } | |
429 | |
430 void V8ConsoleMessageStorage::clear() | |
431 { | |
432 m_messages.clear(); | |
433 m_expiredCount = 0; | |
434 if (V8InspectorSessionImpl* session = m_inspector->sessionForContextGroup(m_
contextGroupId)) | |
435 session->releaseObjectGroup("console"); | |
436 } | |
437 | |
438 void V8ConsoleMessageStorage::contextDestroyed(int contextId) | |
439 { | |
440 for (size_t i = 0; i < m_messages.size(); ++i) | |
441 m_messages[i]->contextDestroyed(contextId); | |
442 } | |
443 | |
444 } // namespace v8_inspector | |
OLD | NEW |