Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(92)

Side by Side Diff: third_party/WebKit/Source/platform/v8_inspector/V8Debugger.cpp

Issue 2295913003: [DevTools] Switch from platform/v8_inspector to v8/v8-inspector.h. (Closed)
Patch Set: rebase Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium 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 "platform/v8_inspector/V8Debugger.h"
6
7 #include "platform/v8_inspector/DebuggerScript.h"
8 #include "platform/v8_inspector/ScriptBreakpoint.h"
9 #include "platform/v8_inspector/StringUtil.h"
10 #include "platform/v8_inspector/V8Compat.h"
11 #include "platform/v8_inspector/V8DebuggerAgentImpl.h"
12 #include "platform/v8_inspector/V8InspectorImpl.h"
13 #include "platform/v8_inspector/V8InternalValueType.h"
14 #include "platform/v8_inspector/V8StackTraceImpl.h"
15 #include "platform/v8_inspector/V8ValueCopier.h"
16 #include "platform/v8_inspector/protocol/Protocol.h"
17 #include "platform/v8_inspector/public/V8InspectorClient.h"
18
19 namespace v8_inspector {
20
21 namespace {
22 const char stepIntoV8MethodName[] = "stepIntoStatement";
23 const char stepOutV8MethodName[] = "stepOutOfFunction";
24 static const char v8AsyncTaskEventEnqueue[] = "enqueue";
25 static const char v8AsyncTaskEventWillHandle[] = "willHandle";
26 static const char v8AsyncTaskEventDidHandle[] = "didHandle";
27
28 inline v8::Local<v8::Boolean> v8Boolean(bool value, v8::Isolate* isolate)
29 {
30 return value ? v8::True(isolate) : v8::False(isolate);
31 }
32
33 } // namespace
34
35 static bool inLiveEditScope = false;
36
37 v8::MaybeLocal<v8::Value> V8Debugger::callDebuggerMethod(const char* functionNam e, int argc, v8::Local<v8::Value> argv[])
38 {
39 v8::MicrotasksScope microtasks(m_isolate, v8::MicrotasksScope::kDoNotRunMicr otasks);
40 v8::Local<v8::Object> debuggerScript = m_debuggerScript.Get(m_isolate);
41 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(debuggerScr ipt->Get(toV8StringInternalized(m_isolate, functionName)));
42 DCHECK(m_isolate->InContext());
43 return function->Call(m_isolate->GetCurrentContext(), debuggerScript, argc, argv);
44 }
45
46 V8Debugger::V8Debugger(v8::Isolate* isolate, V8InspectorImpl* inspector)
47 : m_isolate(isolate)
48 , m_inspector(inspector)
49 , m_lastContextId(0)
50 , m_enableCount(0)
51 , m_breakpointsActivated(true)
52 , m_runningNestedMessageLoop(false)
53 , m_ignoreScriptParsedEventsCounter(0)
54 , m_maxAsyncCallStackDepth(0)
55 {
56 }
57
58 V8Debugger::~V8Debugger()
59 {
60 }
61
62 void V8Debugger::enable()
63 {
64 if (m_enableCount++)
65 return;
66 DCHECK(!enabled());
67 v8::HandleScope scope(m_isolate);
68 v8::Debug::SetDebugEventListener(m_isolate, &V8Debugger::v8DebugEventCallbac k, v8::External::New(m_isolate, this));
69 m_debuggerContext.Reset(m_isolate, v8::Debug::GetDebugContext(m_isolate));
70 compileDebuggerScript();
71 }
72
73 void V8Debugger::disable()
74 {
75 if (--m_enableCount)
76 return;
77 DCHECK(enabled());
78 clearBreakpoints();
79 m_debuggerScript.Reset();
80 m_debuggerContext.Reset();
81 allAsyncTasksCanceled();
82 v8::Debug::SetDebugEventListener(m_isolate, nullptr);
83 }
84
85 bool V8Debugger::enabled() const
86 {
87 return !m_debuggerScript.IsEmpty();
88 }
89
90 // static
91 int V8Debugger::contextId(v8::Local<v8::Context> context)
92 {
93 v8::Local<v8::Value> data = context->GetEmbedderData(static_cast<int>(v8::Co ntext::kDebugIdIndex));
94 if (data.IsEmpty() || !data->IsString())
95 return 0;
96 String16 dataString = toProtocolString(data.As<v8::String>());
97 if (dataString.isEmpty())
98 return 0;
99 size_t commaPos = dataString.find(",");
100 if (commaPos == String16::kNotFound)
101 return 0;
102 size_t commaPos2 = dataString.find(",", commaPos + 1);
103 if (commaPos2 == String16::kNotFound)
104 return 0;
105 return dataString.substring(commaPos + 1, commaPos2 - commaPos - 1).toIntege r();
106 }
107
108 // static
109 int V8Debugger::getGroupId(v8::Local<v8::Context> context)
110 {
111 v8::Local<v8::Value> data = context->GetEmbedderData(static_cast<int>(v8::Co ntext::kDebugIdIndex));
112 if (data.IsEmpty() || !data->IsString())
113 return 0;
114 String16 dataString = toProtocolString(data.As<v8::String>());
115 if (dataString.isEmpty())
116 return 0;
117 size_t commaPos = dataString.find(",");
118 if (commaPos == String16::kNotFound)
119 return 0;
120 return dataString.substring(0, commaPos).toInteger();
121 }
122
123 void V8Debugger::getCompiledScripts(int contextGroupId, std::vector<std::unique_ ptr<V8DebuggerScript>>& result)
124 {
125 v8::HandleScope scope(m_isolate);
126 v8::MicrotasksScope microtasks(m_isolate, v8::MicrotasksScope::kDoNotRunMicr otasks);
127 v8::Local<v8::Object> debuggerScript = m_debuggerScript.Get(m_isolate);
128 DCHECK(!debuggerScript->IsUndefined());
129 v8::Local<v8::Function> getScriptsFunction = v8::Local<v8::Function>::Cast(d ebuggerScript->Get(toV8StringInternalized(m_isolate, "getScripts")));
130 v8::Local<v8::Value> argv[] = { v8::Integer::New(m_isolate, contextGroupId) };
131 v8::Local<v8::Value> value;
132 if (!getScriptsFunction->Call(debuggerContext(), debuggerScript, V8_INSPECTO R_ARRAY_LENGTH(argv), argv).ToLocal(&value))
133 return;
134 DCHECK(value->IsArray());
135 v8::Local<v8::Array> scriptsArray = v8::Local<v8::Array>::Cast(value);
136 result.reserve(scriptsArray->Length());
137 for (unsigned i = 0; i < scriptsArray->Length(); ++i) {
138 v8::Local<v8::Object> scriptObject = v8::Local<v8::Object>::Cast(scripts Array->Get(v8::Integer::New(m_isolate, i)));
139 result.push_back(wrapUnique(new V8DebuggerScript(m_isolate, scriptObject , inLiveEditScope)));
140 }
141 }
142
143 String16 V8Debugger::setBreakpoint(const String16& sourceID, const ScriptBreakpo int& scriptBreakpoint, int* actualLineNumber, int* actualColumnNumber)
144 {
145 v8::HandleScope scope(m_isolate);
146 v8::Context::Scope contextScope(debuggerContext());
147
148 v8::Local<v8::Object> info = v8::Object::New(m_isolate);
149 info->Set(toV8StringInternalized(m_isolate, "sourceID"), toV8String(m_isolat e, sourceID));
150 info->Set(toV8StringInternalized(m_isolate, "lineNumber"), v8::Integer::New( m_isolate, scriptBreakpoint.lineNumber));
151 info->Set(toV8StringInternalized(m_isolate, "columnNumber"), v8::Integer::Ne w(m_isolate, scriptBreakpoint.columnNumber));
152 info->Set(toV8StringInternalized(m_isolate, "condition"), toV8String(m_isola te, scriptBreakpoint.condition));
153
154 v8::Local<v8::Function> setBreakpointFunction = v8::Local<v8::Function>::Cas t(m_debuggerScript.Get(m_isolate)->Get(toV8StringInternalized(m_isolate, "setBre akpoint")));
155 v8::Local<v8::Value> breakpointId = v8::Debug::Call(debuggerContext(), setBr eakpointFunction, info).ToLocalChecked();
156 if (!breakpointId->IsString())
157 return "";
158 *actualLineNumber = info->Get(toV8StringInternalized(m_isolate, "lineNumber" ))->Int32Value();
159 *actualColumnNumber = info->Get(toV8StringInternalized(m_isolate, "columnNum ber"))->Int32Value();
160 return toProtocolString(breakpointId.As<v8::String>());
161 }
162
163 void V8Debugger::removeBreakpoint(const String16& breakpointId)
164 {
165 v8::HandleScope scope(m_isolate);
166 v8::Context::Scope contextScope(debuggerContext());
167
168 v8::Local<v8::Object> info = v8::Object::New(m_isolate);
169 info->Set(toV8StringInternalized(m_isolate, "breakpointId"), toV8String(m_is olate, breakpointId));
170
171 v8::Local<v8::Function> removeBreakpointFunction = v8::Local<v8::Function>:: Cast(m_debuggerScript.Get(m_isolate)->Get(toV8StringInternalized(m_isolate, "rem oveBreakpoint")));
172 v8::Debug::Call(debuggerContext(), removeBreakpointFunction, info).ToLocalCh ecked();
173 }
174
175 void V8Debugger::clearBreakpoints()
176 {
177 v8::HandleScope scope(m_isolate);
178 v8::Context::Scope contextScope(debuggerContext());
179
180 v8::Local<v8::Function> clearBreakpoints = v8::Local<v8::Function>::Cast(m_d ebuggerScript.Get(m_isolate)->Get(toV8StringInternalized(m_isolate, "clearBreakp oints")));
181 v8::Debug::Call(debuggerContext(), clearBreakpoints).ToLocalChecked();
182 }
183
184 void V8Debugger::setBreakpointsActivated(bool activated)
185 {
186 if (!enabled()) {
187 NOTREACHED();
188 return;
189 }
190 v8::HandleScope scope(m_isolate);
191 v8::Context::Scope contextScope(debuggerContext());
192
193 v8::Local<v8::Object> info = v8::Object::New(m_isolate);
194 info->Set(toV8StringInternalized(m_isolate, "enabled"), v8::Boolean::New(m_i solate, activated));
195 v8::Local<v8::Function> setBreakpointsActivated = v8::Local<v8::Function>::C ast(m_debuggerScript.Get(m_isolate)->Get(toV8StringInternalized(m_isolate, "setB reakpointsActivated")));
196 v8::Debug::Call(debuggerContext(), setBreakpointsActivated, info).ToLocalChe cked();
197
198 m_breakpointsActivated = activated;
199 }
200
201 V8Debugger::PauseOnExceptionsState V8Debugger::getPauseOnExceptionsState()
202 {
203 DCHECK(enabled());
204 v8::HandleScope scope(m_isolate);
205 v8::Context::Scope contextScope(debuggerContext());
206
207 v8::Local<v8::Value> argv[] = { v8::Undefined(m_isolate) };
208 v8::Local<v8::Value> result = callDebuggerMethod("pauseOnExceptionsState", 0 , argv).ToLocalChecked();
209 return static_cast<V8Debugger::PauseOnExceptionsState>(result->Int32Value()) ;
210 }
211
212 void V8Debugger::setPauseOnExceptionsState(PauseOnExceptionsState pauseOnExcepti onsState)
213 {
214 DCHECK(enabled());
215 v8::HandleScope scope(m_isolate);
216 v8::Context::Scope contextScope(debuggerContext());
217
218 v8::Local<v8::Value> argv[] = { v8::Int32::New(m_isolate, pauseOnExceptionsS tate) };
219 callDebuggerMethod("setPauseOnExceptionsState", 1, argv);
220 }
221
222 void V8Debugger::setPauseOnNextStatement(bool pause)
223 {
224 if (m_runningNestedMessageLoop)
225 return;
226 if (pause)
227 v8::Debug::DebugBreak(m_isolate);
228 else
229 v8::Debug::CancelDebugBreak(m_isolate);
230 }
231
232 bool V8Debugger::canBreakProgram()
233 {
234 if (!m_breakpointsActivated)
235 return false;
236 return m_isolate->InContext();
237 }
238
239 void V8Debugger::breakProgram()
240 {
241 if (isPaused()) {
242 DCHECK(!m_runningNestedMessageLoop);
243 v8::Local<v8::Value> exception;
244 v8::Local<v8::Array> hitBreakpoints;
245 handleProgramBreak(m_pausedContext, m_executionState, exception, hitBrea kpoints);
246 return;
247 }
248
249 if (!canBreakProgram())
250 return;
251
252 v8::HandleScope scope(m_isolate);
253 v8::Local<v8::Function> breakFunction;
254 if (!V8_FUNCTION_NEW_REMOVE_PROTOTYPE(m_isolate->GetCurrentContext(), &V8Deb ugger::breakProgramCallback, v8::External::New(m_isolate, this), 0).ToLocal(&bre akFunction))
255 return;
256 v8::Debug::Call(debuggerContext(), breakFunction).ToLocalChecked();
257 }
258
259 void V8Debugger::continueProgram()
260 {
261 if (isPaused())
262 m_inspector->client()->quitMessageLoopOnPause();
263 m_pausedContext.Clear();
264 m_executionState.Clear();
265 }
266
267 void V8Debugger::stepIntoStatement()
268 {
269 DCHECK(isPaused());
270 DCHECK(!m_executionState.IsEmpty());
271 v8::HandleScope handleScope(m_isolate);
272 v8::Local<v8::Value> argv[] = { m_executionState };
273 callDebuggerMethod(stepIntoV8MethodName, 1, argv);
274 continueProgram();
275 }
276
277 void V8Debugger::stepOverStatement()
278 {
279 DCHECK(isPaused());
280 DCHECK(!m_executionState.IsEmpty());
281 v8::HandleScope handleScope(m_isolate);
282 v8::Local<v8::Value> argv[] = { m_executionState };
283 callDebuggerMethod("stepOverStatement", 1, argv);
284 continueProgram();
285 }
286
287 void V8Debugger::stepOutOfFunction()
288 {
289 DCHECK(isPaused());
290 DCHECK(!m_executionState.IsEmpty());
291 v8::HandleScope handleScope(m_isolate);
292 v8::Local<v8::Value> argv[] = { m_executionState };
293 callDebuggerMethod(stepOutV8MethodName, 1, argv);
294 continueProgram();
295 }
296
297 void V8Debugger::clearStepping()
298 {
299 DCHECK(enabled());
300 v8::HandleScope scope(m_isolate);
301 v8::Context::Scope contextScope(debuggerContext());
302
303 v8::Local<v8::Value> argv[] = { v8::Undefined(m_isolate) };
304 callDebuggerMethod("clearStepping", 0, argv);
305 }
306
307 bool V8Debugger::setScriptSource(const String16& sourceID, v8::Local<v8::String> newSource, bool dryRun, ErrorString* error, Maybe<protocol::Runtime::ExceptionD etails>* exceptionDetails, JavaScriptCallFrames* newCallFrames, Maybe<bool>* sta ckChanged)
308 {
309 class EnableLiveEditScope {
310 public:
311 explicit EnableLiveEditScope(v8::Isolate* isolate) : m_isolate(isolate)
312 {
313 v8::Debug::SetLiveEditEnabled(m_isolate, true);
314 inLiveEditScope = true;
315 }
316 ~EnableLiveEditScope()
317 {
318 v8::Debug::SetLiveEditEnabled(m_isolate, false);
319 inLiveEditScope = false;
320 }
321 private:
322 v8::Isolate* m_isolate;
323 };
324
325 DCHECK(enabled());
326 v8::HandleScope scope(m_isolate);
327
328 std::unique_ptr<v8::Context::Scope> contextScope;
329 if (!isPaused())
330 contextScope = wrapUnique(new v8::Context::Scope(debuggerContext()));
331
332 v8::Local<v8::Value> argv[] = { toV8String(m_isolate, sourceID), newSource, v8Boolean(dryRun, m_isolate) };
333
334 v8::Local<v8::Value> v8result;
335 {
336 EnableLiveEditScope enableLiveEditScope(m_isolate);
337 v8::TryCatch tryCatch(m_isolate);
338 tryCatch.SetVerbose(false);
339 v8::MaybeLocal<v8::Value> maybeResult = callDebuggerMethod("liveEditScri ptSource", 3, argv);
340 if (tryCatch.HasCaught()) {
341 v8::Local<v8::Message> message = tryCatch.Message();
342 if (!message.IsEmpty())
343 *error = toProtocolStringWithTypeCheck(message->Get());
344 else
345 *error = "Unknown error.";
346 return false;
347 }
348 v8result = maybeResult.ToLocalChecked();
349 }
350 DCHECK(!v8result.IsEmpty());
351 v8::Local<v8::Object> resultTuple = v8result->ToObject(m_isolate);
352 int code = static_cast<int>(resultTuple->Get(0)->ToInteger(m_isolate)->Value ());
353 switch (code) {
354 case 0:
355 {
356 *stackChanged = resultTuple->Get(1)->BooleanValue();
357 // Call stack may have changed after if the edited function was on t he stack.
358 if (!dryRun && isPaused()) {
359 JavaScriptCallFrames frames = currentCallFrames();
360 newCallFrames->swap(frames);
361 }
362 return true;
363 }
364 // Compile error.
365 case 1:
366 {
367 *exceptionDetails = protocol::Runtime::ExceptionDetails::create()
368 .setExceptionId(m_inspector->nextExceptionId())
369 .setText(toProtocolStringWithTypeCheck(resultTuple->Get(2)))
370 .setLineNumber(resultTuple->Get(3)->ToInteger(m_isolate)->Value( ) - 1)
371 .setColumnNumber(resultTuple->Get(4)->ToInteger(m_isolate)->Valu e() - 1).build();
372 return false;
373 }
374 }
375 *error = "Unknown error.";
376 return false;
377 }
378
379 JavaScriptCallFrames V8Debugger::currentCallFrames(int limit)
380 {
381 if (!m_isolate->InContext())
382 return JavaScriptCallFrames();
383 v8::Local<v8::Value> currentCallFramesV8;
384 if (m_executionState.IsEmpty()) {
385 v8::Local<v8::Function> currentCallFramesFunction = v8::Local<v8::Functi on>::Cast(m_debuggerScript.Get(m_isolate)->Get(toV8StringInternalized(m_isolate, "currentCallFrames")));
386 currentCallFramesV8 = v8::Debug::Call(debuggerContext(), currentCallFram esFunction, v8::Integer::New(m_isolate, limit)).ToLocalChecked();
387 } else {
388 v8::Local<v8::Value> argv[] = { m_executionState, v8::Integer::New(m_iso late, limit) };
389 currentCallFramesV8 = callDebuggerMethod("currentCallFrames", V8_INSPECT OR_ARRAY_LENGTH(argv), argv).ToLocalChecked();
390 }
391 DCHECK(!currentCallFramesV8.IsEmpty());
392 if (!currentCallFramesV8->IsArray())
393 return JavaScriptCallFrames();
394 v8::Local<v8::Array> callFramesArray = currentCallFramesV8.As<v8::Array>();
395 JavaScriptCallFrames callFrames;
396 for (size_t i = 0; i < callFramesArray->Length(); ++i) {
397 v8::Local<v8::Value> callFrameValue;
398 if (!callFramesArray->Get(debuggerContext(), i).ToLocal(&callFrameValue) )
399 return JavaScriptCallFrames();
400 if (!callFrameValue->IsObject())
401 return JavaScriptCallFrames();
402 v8::Local<v8::Object> callFrameObject = callFrameValue.As<v8::Object>();
403 callFrames.push_back(JavaScriptCallFrame::create(debuggerContext(), v8:: Local<v8::Object>::Cast(callFrameObject)));
404 }
405 return callFrames;
406 }
407
408 static V8Debugger* toV8Debugger(v8::Local<v8::Value> data)
409 {
410 void* p = v8::Local<v8::External>::Cast(data)->Value();
411 return static_cast<V8Debugger*>(p);
412 }
413
414 void V8Debugger::breakProgramCallback(const v8::FunctionCallbackInfo<v8::Value>& info)
415 {
416 DCHECK_EQ(info.Length(), 2);
417 V8Debugger* thisPtr = toV8Debugger(info.Data());
418 if (!thisPtr->enabled())
419 return;
420 v8::Local<v8::Context> pausedContext = thisPtr->m_isolate->GetCurrentContext ();
421 v8::Local<v8::Value> exception;
422 v8::Local<v8::Array> hitBreakpoints;
423 thisPtr->handleProgramBreak(pausedContext, v8::Local<v8::Object>::Cast(info[ 0]), exception, hitBreakpoints);
424 }
425
426 void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext, v8::Lo cal<v8::Object> executionState, v8::Local<v8::Value> exception, v8::Local<v8::Ar ray> hitBreakpointNumbers, bool isPromiseRejection)
427 {
428 // Don't allow nested breaks.
429 if (m_runningNestedMessageLoop)
430 return;
431
432 V8DebuggerAgentImpl* agent = m_inspector->enabledDebuggerAgentForGroup(getGr oupId(pausedContext));
433 if (!agent)
434 return;
435
436 std::vector<String16> breakpointIds;
437 if (!hitBreakpointNumbers.IsEmpty()) {
438 breakpointIds.reserve(hitBreakpointNumbers->Length());
439 for (size_t i = 0; i < hitBreakpointNumbers->Length(); i++) {
440 v8::Local<v8::Value> hitBreakpointNumber = hitBreakpointNumbers->Get (i);
441 DCHECK(!hitBreakpointNumber.IsEmpty() && hitBreakpointNumber->IsInt3 2());
442 breakpointIds.push_back(String16::fromInteger(hitBreakpointNumber->I nt32Value()));
443 }
444 }
445
446 m_pausedContext = pausedContext;
447 m_executionState = executionState;
448 V8DebuggerAgentImpl::SkipPauseRequest result = agent->didPause(pausedContext , exception, breakpointIds, isPromiseRejection);
449 if (result == V8DebuggerAgentImpl::RequestNoSkip) {
450 m_runningNestedMessageLoop = true;
451 int groupId = getGroupId(pausedContext);
452 DCHECK(groupId);
453 m_inspector->client()->runMessageLoopOnPause(groupId);
454 // The agent may have been removed in the nested loop.
455 agent = m_inspector->enabledDebuggerAgentForGroup(getGroupId(pausedConte xt));
456 if (agent)
457 agent->didContinue();
458 m_runningNestedMessageLoop = false;
459 }
460 m_pausedContext.Clear();
461 m_executionState.Clear();
462
463 if (result == V8DebuggerAgentImpl::RequestStepFrame) {
464 v8::Local<v8::Value> argv[] = { executionState };
465 callDebuggerMethod("stepFrameStatement", 1, argv);
466 } else if (result == V8DebuggerAgentImpl::RequestStepInto) {
467 v8::Local<v8::Value> argv[] = { executionState };
468 callDebuggerMethod(stepIntoV8MethodName, 1, argv);
469 } else if (result == V8DebuggerAgentImpl::RequestStepOut) {
470 v8::Local<v8::Value> argv[] = { executionState };
471 callDebuggerMethod(stepOutV8MethodName, 1, argv);
472 }
473 }
474
475 void V8Debugger::v8DebugEventCallback(const v8::Debug::EventDetails& eventDetail s)
476 {
477 V8Debugger* thisPtr = toV8Debugger(eventDetails.GetCallbackData());
478 thisPtr->handleV8DebugEvent(eventDetails);
479 }
480
481 v8::Local<v8::Value> V8Debugger::callInternalGetterFunction(v8::Local<v8::Object > object, const char* functionName)
482 {
483 v8::MicrotasksScope microtasks(m_isolate, v8::MicrotasksScope::kDoNotRunMicr otasks);
484 v8::Local<v8::Value> getterValue = object->Get(toV8StringInternalized(m_isol ate, functionName));
485 DCHECK(!getterValue.IsEmpty() && getterValue->IsFunction());
486 return v8::Local<v8::Function>::Cast(getterValue)->Call(m_isolate->GetCurren tContext(), object, 0, 0).ToLocalChecked();
487 }
488
489 void V8Debugger::handleV8DebugEvent(const v8::Debug::EventDetails& eventDetails)
490 {
491 if (!enabled())
492 return;
493 v8::DebugEvent event = eventDetails.GetEvent();
494 if (event != v8::AsyncTaskEvent && event != v8::Break && event != v8::Except ion && event != v8::AfterCompile && event != v8::BeforeCompile && event != v8::C ompileError)
495 return;
496
497 v8::Local<v8::Context> eventContext = eventDetails.GetEventContext();
498 DCHECK(!eventContext.IsEmpty());
499
500 if (event == v8::AsyncTaskEvent) {
501 v8::HandleScope scope(m_isolate);
502 handleV8AsyncTaskEvent(eventContext, eventDetails.GetExecutionState(), e ventDetails.GetEventData());
503 return;
504 }
505
506 V8DebuggerAgentImpl* agent = m_inspector->enabledDebuggerAgentForGroup(getGr oupId(eventContext));
507 if (agent) {
508 v8::HandleScope scope(m_isolate);
509 if (m_ignoreScriptParsedEventsCounter == 0 && (event == v8::AfterCompile || event == v8::CompileError)) {
510 v8::Context::Scope contextScope(debuggerContext());
511 v8::Local<v8::Value> argv[] = { eventDetails.GetEventData() };
512 v8::Local<v8::Value> value = callDebuggerMethod("getAfterCompileScri pt", 1, argv).ToLocalChecked();
513 if (value->IsNull())
514 return;
515 DCHECK(value->IsObject());
516 v8::Local<v8::Object> scriptObject = v8::Local<v8::Object>::Cast(val ue);
517 agent->didParseSource(wrapUnique(new V8DebuggerScript(m_isolate, scr iptObject, inLiveEditScope)), event == v8::AfterCompile);
518 } else if (event == v8::Exception) {
519 v8::Local<v8::Object> eventData = eventDetails.GetEventData();
520 v8::Local<v8::Value> exception = callInternalGetterFunction(eventDat a, "exception");
521 v8::Local<v8::Value> promise = callInternalGetterFunction(eventData, "promise");
522 bool isPromiseRejection = !promise.IsEmpty() && promise->IsObject();
523 handleProgramBreak(eventContext, eventDetails.GetExecutionState(), e xception, v8::Local<v8::Array>(), isPromiseRejection);
524 } else if (event == v8::Break) {
525 v8::Local<v8::Value> argv[] = { eventDetails.GetEventData() };
526 v8::Local<v8::Value> hitBreakpoints = callDebuggerMethod("getBreakpo intNumbers", 1, argv).ToLocalChecked();
527 DCHECK(hitBreakpoints->IsArray());
528 handleProgramBreak(eventContext, eventDetails.GetExecutionState(), v 8::Local<v8::Value>(), hitBreakpoints.As<v8::Array>());
529 }
530 }
531 }
532
533 void V8Debugger::handleV8AsyncTaskEvent(v8::Local<v8::Context> context, v8::Loca l<v8::Object> executionState, v8::Local<v8::Object> eventData)
534 {
535 if (!m_maxAsyncCallStackDepth)
536 return;
537
538 String16 type = toProtocolStringWithTypeCheck(callInternalGetterFunction(eve ntData, "type"));
539 String16 name = toProtocolStringWithTypeCheck(callInternalGetterFunction(eve ntData, "name"));
540 int id = callInternalGetterFunction(eventData, "id")->ToInteger(m_isolate)-> Value();
541 // The scopes for the ids are defined by the eventData.name namespaces. Ther e are currently two namespaces: "Object." and "Promise.".
542 void* ptr = reinterpret_cast<void*>(id * 4 + (name[0] == 'P' ? 2 : 0) + 1);
543 if (type == v8AsyncTaskEventEnqueue)
544 asyncTaskScheduled(name, ptr, false);
545 else if (type == v8AsyncTaskEventWillHandle)
546 asyncTaskStarted(ptr);
547 else if (type == v8AsyncTaskEventDidHandle)
548 asyncTaskFinished(ptr);
549 else
550 NOTREACHED();
551 }
552
553 V8StackTraceImpl* V8Debugger::currentAsyncCallChain()
554 {
555 if (!m_currentStacks.size())
556 return nullptr;
557 return m_currentStacks.back().get();
558 }
559
560 void V8Debugger::compileDebuggerScript()
561 {
562 if (!m_debuggerScript.IsEmpty()) {
563 NOTREACHED();
564 return;
565 }
566
567 v8::HandleScope scope(m_isolate);
568 v8::Context::Scope contextScope(debuggerContext());
569
570 v8::Local<v8::String> scriptValue = v8::String::NewFromUtf8(m_isolate, Debug gerScript_js, v8::NewStringType::kInternalized, sizeof(DebuggerScript_js)).ToLoc alChecked();
571 v8::Local<v8::Value> value;
572 if (!m_inspector->compileAndRunInternalScript(debuggerContext(), scriptValue ).ToLocal(&value)) {
573 NOTREACHED();
574 return;
575 }
576 DCHECK(value->IsObject());
577 m_debuggerScript.Reset(m_isolate, value.As<v8::Object>());
578 }
579
580 v8::Local<v8::Context> V8Debugger::debuggerContext() const
581 {
582 DCHECK(!m_debuggerContext.IsEmpty());
583 return m_debuggerContext.Get(m_isolate);
584 }
585
586 v8::MaybeLocal<v8::Value> V8Debugger::functionScopes(v8::Local<v8::Context> cont ext, v8::Local<v8::Function> function)
587 {
588 if (!enabled()) {
589 NOTREACHED();
590 return v8::Local<v8::Value>::New(m_isolate, v8::Undefined(m_isolate));
591 }
592 v8::Local<v8::Value> argv[] = { function };
593 v8::Local<v8::Value> scopesValue;
594 if (!callDebuggerMethod("getFunctionScopes", 1, argv).ToLocal(&scopesValue))
595 return v8::MaybeLocal<v8::Value>();
596 v8::Local<v8::Value> copied;
597 if (!copyValueFromDebuggerContext(m_isolate, debuggerContext(), context, sco pesValue).ToLocal(&copied) || !copied->IsArray())
598 return v8::MaybeLocal<v8::Value>();
599 if (!markAsInternal(context, v8::Local<v8::Array>::Cast(copied), V8InternalV alueType::kScopeList))
600 return v8::MaybeLocal<v8::Value>();
601 if (!markArrayEntriesAsInternal(context, v8::Local<v8::Array>::Cast(copied), V8InternalValueType::kScope))
602 return v8::MaybeLocal<v8::Value>();
603 return copied;
604 }
605
606 v8::MaybeLocal<v8::Array> V8Debugger::internalProperties(v8::Local<v8::Context> context, v8::Local<v8::Value> value)
607 {
608 v8::Local<v8::Array> properties;
609 if (!v8::Debug::GetInternalProperties(m_isolate, value).ToLocal(&properties) )
610 return v8::MaybeLocal<v8::Array>();
611 if (value->IsFunction()) {
612 v8::Local<v8::Function> function = value.As<v8::Function>();
613 v8::Local<v8::Value> location = functionLocation(context, function);
614 if (location->IsObject()) {
615 createDataProperty(context, properties, properties->Length(), toV8St ringInternalized(m_isolate, "[[FunctionLocation]]"));
616 createDataProperty(context, properties, properties->Length(), locati on);
617 }
618 if (function->IsGeneratorFunction()) {
619 createDataProperty(context, properties, properties->Length(), toV8St ringInternalized(m_isolate, "[[IsGenerator]]"));
620 createDataProperty(context, properties, properties->Length(), v8::Tr ue(m_isolate));
621 }
622 }
623 if (!enabled())
624 return properties;
625 if (value->IsMap() || value->IsWeakMap() || value->IsSet() || value->IsWeakS et() || value->IsSetIterator() || value->IsMapIterator()) {
626 v8::Local<v8::Value> entries = collectionEntries(context, v8::Local<v8:: Object>::Cast(value));
627 if (entries->IsArray()) {
628 createDataProperty(context, properties, properties->Length(), toV8St ringInternalized(m_isolate, "[[Entries]]"));
629 createDataProperty(context, properties, properties->Length(), entrie s);
630 }
631 }
632 if (value->IsGeneratorObject()) {
633 v8::Local<v8::Value> location = generatorObjectLocation(context, v8::Loc al<v8::Object>::Cast(value));
634 if (location->IsObject()) {
635 createDataProperty(context, properties, properties->Length(), toV8St ringInternalized(m_isolate, "[[GeneratorLocation]]"));
636 createDataProperty(context, properties, properties->Length(), locati on);
637 }
638 }
639 if (value->IsFunction()) {
640 v8::Local<v8::Function> function = value.As<v8::Function>();
641 v8::Local<v8::Value> boundFunction = function->GetBoundFunction();
642 v8::Local<v8::Value> scopes;
643 if (boundFunction->IsUndefined() && functionScopes(context, function).To Local(&scopes)) {
644 createDataProperty(context, properties, properties->Length(), toV8St ringInternalized(m_isolate, "[[Scopes]]"));
645 createDataProperty(context, properties, properties->Length(), scopes );
646 }
647 }
648 return properties;
649 }
650
651 v8::Local<v8::Value> V8Debugger::collectionEntries(v8::Local<v8::Context> contex t, v8::Local<v8::Object> object)
652 {
653 if (!enabled()) {
654 NOTREACHED();
655 return v8::Undefined(m_isolate);
656 }
657 v8::Local<v8::Value> argv[] = { object };
658 v8::Local<v8::Value> entriesValue = callDebuggerMethod("getCollectionEntries ", 1, argv).ToLocalChecked();
659 v8::Local<v8::Value> copied;
660 if (!copyValueFromDebuggerContext(m_isolate, debuggerContext(), context, ent riesValue).ToLocal(&copied) || !copied->IsArray())
661 return v8::Undefined(m_isolate);
662 if (!markArrayEntriesAsInternal(context, v8::Local<v8::Array>::Cast(copied), V8InternalValueType::kEntry))
663 return v8::Undefined(m_isolate);
664 return copied;
665 }
666
667 v8::Local<v8::Value> V8Debugger::generatorObjectLocation(v8::Local<v8::Context> context, v8::Local<v8::Object> object)
668 {
669 if (!enabled()) {
670 NOTREACHED();
671 return v8::Null(m_isolate);
672 }
673 v8::Local<v8::Value> argv[] = { object };
674 v8::Local<v8::Value> location = callDebuggerMethod("getGeneratorObjectLocati on", 1, argv).ToLocalChecked();
675 v8::Local<v8::Value> copied;
676 if (!copyValueFromDebuggerContext(m_isolate, debuggerContext(), context, loc ation).ToLocal(&copied) || !copied->IsObject())
677 return v8::Null(m_isolate);
678 if (!markAsInternal(context, v8::Local<v8::Object>::Cast(copied), V8Internal ValueType::kLocation))
679 return v8::Null(m_isolate);
680 return copied;
681 }
682
683 v8::Local<v8::Value> V8Debugger::functionLocation(v8::Local<v8::Context> context , v8::Local<v8::Function> function)
684 {
685 int scriptId = function->ScriptId();
686 if (scriptId == v8::UnboundScript::kNoScriptId)
687 return v8::Null(m_isolate);
688 int lineNumber = function->GetScriptLineNumber();
689 int columnNumber = function->GetScriptColumnNumber();
690 if (lineNumber == v8::Function::kLineOffsetNotFound || columnNumber == v8::F unction::kLineOffsetNotFound)
691 return v8::Null(m_isolate);
692 v8::Local<v8::Object> location = v8::Object::New(m_isolate);
693 if (!location->SetPrototype(context, v8::Null(m_isolate)).FromMaybe(false))
694 return v8::Null(m_isolate);
695 if (!createDataProperty(context, location, toV8StringInternalized(m_isolate, "scriptId"), toV8String(m_isolate, String16::fromInteger(scriptId))).FromMaybe( false))
696 return v8::Null(m_isolate);
697 if (!createDataProperty(context, location, toV8StringInternalized(m_isolate, "lineNumber"), v8::Integer::New(m_isolate, lineNumber)).FromMaybe(false))
698 return v8::Null(m_isolate);
699 if (!createDataProperty(context, location, toV8StringInternalized(m_isolate, "columnNumber"), v8::Integer::New(m_isolate, columnNumber)).FromMaybe(false))
700 return v8::Null(m_isolate);
701 if (!markAsInternal(context, location, V8InternalValueType::kLocation))
702 return v8::Null(m_isolate);
703 return location;
704 }
705
706 bool V8Debugger::isPaused()
707 {
708 return !m_pausedContext.IsEmpty();
709 }
710
711 std::unique_ptr<V8StackTraceImpl> V8Debugger::createStackTrace(v8::Local<v8::Sta ckTrace> stackTrace)
712 {
713 int contextGroupId = m_isolate->InContext() ? getGroupId(m_isolate->GetCurre ntContext()) : 0;
714 return V8StackTraceImpl::create(this, contextGroupId, stackTrace, V8StackTra ceImpl::maxCallStackSizeToCapture);
715 }
716
717 int V8Debugger::markContext(const V8ContextInfo& info)
718 {
719 DCHECK(info.context->GetIsolate() == m_isolate);
720 int contextId = ++m_lastContextId;
721 String16 debugData = String16::fromInteger(info.contextGroupId) + "," + Stri ng16::fromInteger(contextId) + "," + toString16(info.auxData);
722 v8::Context::Scope contextScope(info.context);
723 info.context->SetEmbedderData(static_cast<int>(v8::Context::kDebugIdIndex), toV8String(m_isolate, debugData));
724 return contextId;
725 }
726
727 void V8Debugger::setAsyncCallStackDepth(V8DebuggerAgentImpl* agent, int depth)
728 {
729 if (depth <= 0)
730 m_maxAsyncCallStackDepthMap.erase(agent);
731 else
732 m_maxAsyncCallStackDepthMap[agent] = depth;
733
734 int maxAsyncCallStackDepth = 0;
735 for (const auto& pair : m_maxAsyncCallStackDepthMap) {
736 if (pair.second > maxAsyncCallStackDepth)
737 maxAsyncCallStackDepth = pair.second;
738 }
739
740 if (m_maxAsyncCallStackDepth == maxAsyncCallStackDepth)
741 return;
742 m_maxAsyncCallStackDepth = maxAsyncCallStackDepth;
743 if (!maxAsyncCallStackDepth)
744 allAsyncTasksCanceled();
745 }
746
747 void V8Debugger::asyncTaskScheduled(const StringView& taskName, void* task, bool recurring)
748 {
749 if (!m_maxAsyncCallStackDepth)
750 return;
751 asyncTaskScheduled(toString16(taskName), task, recurring);
752 }
753
754 void V8Debugger::asyncTaskScheduled(const String16& taskName, void* task, bool r ecurring)
755 {
756 if (!m_maxAsyncCallStackDepth)
757 return;
758 v8::HandleScope scope(m_isolate);
759 int contextGroupId = m_isolate->InContext() ? getGroupId(m_isolate->GetCurre ntContext()) : 0;
760 std::unique_ptr<V8StackTraceImpl> chain = V8StackTraceImpl::capture(this, co ntextGroupId, V8StackTraceImpl::maxCallStackSizeToCapture, taskName);
761 if (chain) {
762 m_asyncTaskStacks[task] = std::move(chain);
763 if (recurring)
764 m_recurringTasks.insert(task);
765 }
766 }
767
768 void V8Debugger::asyncTaskCanceled(void* task)
769 {
770 if (!m_maxAsyncCallStackDepth)
771 return;
772 m_asyncTaskStacks.erase(task);
773 m_recurringTasks.erase(task);
774 }
775
776 void V8Debugger::asyncTaskStarted(void* task)
777 {
778 if (!m_maxAsyncCallStackDepth)
779 return;
780 m_currentTasks.push_back(task);
781 AsyncTaskToStackTrace::iterator stackIt = m_asyncTaskStacks.find(task);
782 // Needs to support following order of events:
783 // - asyncTaskScheduled
784 // <-- attached here -->
785 // - asyncTaskStarted
786 // - asyncTaskCanceled <-- canceled before finished
787 // <-- async stack requested here -->
788 // - asyncTaskFinished
789 std::unique_ptr<V8StackTraceImpl> stack;
790 if (stackIt != m_asyncTaskStacks.end() && stackIt->second)
791 stack = stackIt->second->cloneImpl();
792 m_currentStacks.push_back(std::move(stack));
793 }
794
795 void V8Debugger::asyncTaskFinished(void* task)
796 {
797 if (!m_maxAsyncCallStackDepth)
798 return;
799 // We could start instrumenting half way and the stack is empty.
800 if (!m_currentStacks.size())
801 return;
802
803 DCHECK(m_currentTasks.back() == task);
804 m_currentTasks.pop_back();
805
806 m_currentStacks.pop_back();
807 if (m_recurringTasks.find(task) == m_recurringTasks.end())
808 m_asyncTaskStacks.erase(task);
809 }
810
811 void V8Debugger::allAsyncTasksCanceled()
812 {
813 m_asyncTaskStacks.clear();
814 m_recurringTasks.clear();
815 m_currentStacks.clear();
816 m_currentTasks.clear();
817 }
818
819 void V8Debugger::muteScriptParsedEvents()
820 {
821 ++m_ignoreScriptParsedEventsCounter;
822 }
823
824 void V8Debugger::unmuteScriptParsedEvents()
825 {
826 --m_ignoreScriptParsedEventsCounter;
827 DCHECK_GE(m_ignoreScriptParsedEventsCounter, 0);
828 }
829
830 std::unique_ptr<V8StackTraceImpl> V8Debugger::captureStackTrace(bool fullStack)
831 {
832 if (!m_isolate->InContext())
833 return nullptr;
834
835 v8::HandleScope handles(m_isolate);
836 int contextGroupId = getGroupId(m_isolate->GetCurrentContext());
837 if (!contextGroupId)
838 return nullptr;
839
840 size_t stackSize = fullStack ? V8StackTraceImpl::maxCallStackSizeToCapture : 1;
841 if (m_inspector->enabledRuntimeAgentForGroup(contextGroupId))
842 stackSize = V8StackTraceImpl::maxCallStackSizeToCapture;
843
844 return V8StackTraceImpl::capture(this, contextGroupId, stackSize);
845 }
846
847 } // namespace v8_inspector
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698