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

Side by Side Diff: src/inspector/V8Debugger.cpp

Issue 2338413003: [inspector] change implementation file extension from cpp to cc (Closed)
Patch Set: string16 -> string-16 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
« no previous file with comments | « src/inspector/V8Debugger.h ('k') | src/inspector/V8DebuggerAgentImpl.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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/V8Debugger.h"
6
7 #include "src/inspector/DebuggerScript.h"
8 #include "src/inspector/ScriptBreakpoint.h"
9 #include "src/inspector/StringUtil.h"
10 #include "src/inspector/V8DebuggerAgentImpl.h"
11 #include "src/inspector/V8InspectorImpl.h"
12 #include "src/inspector/V8InternalValueType.h"
13 #include "src/inspector/V8StackTraceImpl.h"
14 #include "src/inspector/V8ValueCopier.h"
15 #include "src/inspector/protocol/Protocol.h"
16
17 namespace v8_inspector {
18
19 namespace {
20 const char stepIntoV8MethodName[] = "stepIntoStatement";
21 const char stepOutV8MethodName[] = "stepOutOfFunction";
22 static const char v8AsyncTaskEventEnqueue[] = "enqueue";
23 static const char v8AsyncTaskEventWillHandle[] = "willHandle";
24 static const char v8AsyncTaskEventDidHandle[] = "didHandle";
25
26 inline v8::Local<v8::Boolean> v8Boolean(bool value, v8::Isolate* isolate) {
27 return value ? v8::True(isolate) : v8::False(isolate);
28 }
29
30 } // namespace
31
32 static bool inLiveEditScope = false;
33
34 v8::MaybeLocal<v8::Value> V8Debugger::callDebuggerMethod(
35 const char* functionName, int argc, v8::Local<v8::Value> argv[]) {
36 v8::MicrotasksScope microtasks(m_isolate,
37 v8::MicrotasksScope::kDoNotRunMicrotasks);
38 DCHECK(m_isolate->InContext());
39 v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
40 v8::Local<v8::Object> debuggerScript = m_debuggerScript.Get(m_isolate);
41 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
42 debuggerScript
43 ->Get(context, toV8StringInternalized(m_isolate, functionName))
44 .ToLocalChecked());
45 return function->Call(context, debuggerScript, argc, argv);
46 }
47
48 V8Debugger::V8Debugger(v8::Isolate* isolate, V8InspectorImpl* inspector)
49 : m_isolate(isolate),
50 m_inspector(inspector),
51 m_lastContextId(0),
52 m_enableCount(0),
53 m_breakpointsActivated(true),
54 m_runningNestedMessageLoop(false),
55 m_ignoreScriptParsedEventsCounter(0),
56 m_maxAsyncCallStackDepth(0) {}
57
58 V8Debugger::~V8Debugger() {}
59
60 void V8Debugger::enable() {
61 if (m_enableCount++) return;
62 DCHECK(!enabled());
63 v8::HandleScope scope(m_isolate);
64 v8::Debug::SetDebugEventListener(m_isolate, &V8Debugger::v8DebugEventCallback,
65 v8::External::New(m_isolate, this));
66 m_debuggerContext.Reset(m_isolate, v8::Debug::GetDebugContext(m_isolate));
67 compileDebuggerScript();
68 }
69
70 void V8Debugger::disable() {
71 if (--m_enableCount) return;
72 DCHECK(enabled());
73 clearBreakpoints();
74 m_debuggerScript.Reset();
75 m_debuggerContext.Reset();
76 allAsyncTasksCanceled();
77 v8::Debug::SetDebugEventListener(m_isolate, nullptr);
78 }
79
80 bool V8Debugger::enabled() const { return !m_debuggerScript.IsEmpty(); }
81
82 // static
83 int V8Debugger::contextId(v8::Local<v8::Context> context) {
84 v8::Local<v8::Value> data =
85 context->GetEmbedderData(static_cast<int>(v8::Context::kDebugIdIndex));
86 if (data.IsEmpty() || !data->IsString()) return 0;
87 String16 dataString = toProtocolString(data.As<v8::String>());
88 if (dataString.isEmpty()) return 0;
89 size_t commaPos = dataString.find(",");
90 if (commaPos == String16::kNotFound) return 0;
91 size_t commaPos2 = dataString.find(",", commaPos + 1);
92 if (commaPos2 == String16::kNotFound) return 0;
93 return dataString.substring(commaPos + 1, commaPos2 - commaPos - 1)
94 .toInteger();
95 }
96
97 // static
98 int V8Debugger::getGroupId(v8::Local<v8::Context> context) {
99 v8::Local<v8::Value> data =
100 context->GetEmbedderData(static_cast<int>(v8::Context::kDebugIdIndex));
101 if (data.IsEmpty() || !data->IsString()) return 0;
102 String16 dataString = toProtocolString(data.As<v8::String>());
103 if (dataString.isEmpty()) return 0;
104 size_t commaPos = dataString.find(",");
105 if (commaPos == String16::kNotFound) return 0;
106 return dataString.substring(0, commaPos).toInteger();
107 }
108
109 void V8Debugger::getCompiledScripts(
110 int contextGroupId,
111 std::vector<std::unique_ptr<V8DebuggerScript>>& result) {
112 v8::HandleScope scope(m_isolate);
113 v8::MicrotasksScope microtasks(m_isolate,
114 v8::MicrotasksScope::kDoNotRunMicrotasks);
115 v8::Local<v8::Context> context = debuggerContext();
116 v8::Local<v8::Object> debuggerScript = m_debuggerScript.Get(m_isolate);
117 DCHECK(!debuggerScript->IsUndefined());
118 v8::Local<v8::Function> getScriptsFunction = v8::Local<v8::Function>::Cast(
119 debuggerScript
120 ->Get(context, toV8StringInternalized(m_isolate, "getScripts"))
121 .ToLocalChecked());
122 v8::Local<v8::Value> argv[] = {v8::Integer::New(m_isolate, contextGroupId)};
123 v8::Local<v8::Value> value;
124 if (!getScriptsFunction->Call(context, debuggerScript, arraysize(argv), argv)
125 .ToLocal(&value))
126 return;
127 DCHECK(value->IsArray());
128 v8::Local<v8::Array> scriptsArray = v8::Local<v8::Array>::Cast(value);
129 result.reserve(scriptsArray->Length());
130 for (unsigned i = 0; i < scriptsArray->Length(); ++i) {
131 v8::Local<v8::Object> scriptObject = v8::Local<v8::Object>::Cast(
132 scriptsArray->Get(context, v8::Integer::New(m_isolate, i))
133 .ToLocalChecked());
134 result.push_back(wrapUnique(
135 new V8DebuggerScript(context, scriptObject, inLiveEditScope)));
136 }
137 }
138
139 String16 V8Debugger::setBreakpoint(const String16& sourceID,
140 const ScriptBreakpoint& scriptBreakpoint,
141 int* actualLineNumber,
142 int* actualColumnNumber) {
143 v8::HandleScope scope(m_isolate);
144 v8::Local<v8::Context> context = debuggerContext();
145 v8::Context::Scope contextScope(context);
146
147 v8::Local<v8::Object> info = v8::Object::New(m_isolate);
148 bool success = false;
149 success = info->Set(context, toV8StringInternalized(m_isolate, "sourceID"),
150 toV8String(m_isolate, sourceID))
151 .FromMaybe(false);
152 DCHECK(success);
153 success = info->Set(context, toV8StringInternalized(m_isolate, "lineNumber"),
154 v8::Integer::New(m_isolate, scriptBreakpoint.lineNumber))
155 .FromMaybe(false);
156 DCHECK(success);
157 success =
158 info->Set(context, toV8StringInternalized(m_isolate, "columnNumber"),
159 v8::Integer::New(m_isolate, scriptBreakpoint.columnNumber))
160 .FromMaybe(false);
161 DCHECK(success);
162 success = info->Set(context, toV8StringInternalized(m_isolate, "condition"),
163 toV8String(m_isolate, scriptBreakpoint.condition))
164 .FromMaybe(false);
165 DCHECK(success);
166
167 v8::Local<v8::Function> setBreakpointFunction = v8::Local<v8::Function>::Cast(
168 m_debuggerScript.Get(m_isolate)
169 ->Get(context, toV8StringInternalized(m_isolate, "setBreakpoint"))
170 .ToLocalChecked());
171 v8::Local<v8::Value> breakpointId =
172 v8::Debug::Call(debuggerContext(), setBreakpointFunction, info)
173 .ToLocalChecked();
174 if (!breakpointId->IsString()) return "";
175 *actualLineNumber =
176 info->Get(context, toV8StringInternalized(m_isolate, "lineNumber"))
177 .ToLocalChecked()
178 ->Int32Value(context)
179 .FromJust();
180 *actualColumnNumber =
181 info->Get(context, toV8StringInternalized(m_isolate, "columnNumber"))
182 .ToLocalChecked()
183 ->Int32Value(context)
184 .FromJust();
185 return toProtocolString(breakpointId.As<v8::String>());
186 }
187
188 void V8Debugger::removeBreakpoint(const String16& breakpointId) {
189 v8::HandleScope scope(m_isolate);
190 v8::Local<v8::Context> context = debuggerContext();
191 v8::Context::Scope contextScope(context);
192
193 v8::Local<v8::Object> info = v8::Object::New(m_isolate);
194 bool success = false;
195 success =
196 info->Set(context, toV8StringInternalized(m_isolate, "breakpointId"),
197 toV8String(m_isolate, breakpointId))
198 .FromMaybe(false);
199 DCHECK(success);
200
201 v8::Local<v8::Function> removeBreakpointFunction =
202 v8::Local<v8::Function>::Cast(
203 m_debuggerScript.Get(m_isolate)
204 ->Get(context,
205 toV8StringInternalized(m_isolate, "removeBreakpoint"))
206 .ToLocalChecked());
207 v8::Debug::Call(debuggerContext(), removeBreakpointFunction, info)
208 .ToLocalChecked();
209 }
210
211 void V8Debugger::clearBreakpoints() {
212 v8::HandleScope scope(m_isolate);
213 v8::Local<v8::Context> context = debuggerContext();
214 v8::Context::Scope contextScope(context);
215
216 v8::Local<v8::Function> clearBreakpoints = v8::Local<v8::Function>::Cast(
217 m_debuggerScript.Get(m_isolate)
218 ->Get(context, toV8StringInternalized(m_isolate, "clearBreakpoints"))
219 .ToLocalChecked());
220 v8::Debug::Call(debuggerContext(), clearBreakpoints).ToLocalChecked();
221 }
222
223 void V8Debugger::setBreakpointsActivated(bool activated) {
224 if (!enabled()) {
225 UNREACHABLE();
226 return;
227 }
228 v8::HandleScope scope(m_isolate);
229 v8::Local<v8::Context> context = debuggerContext();
230 v8::Context::Scope contextScope(context);
231
232 v8::Local<v8::Object> info = v8::Object::New(m_isolate);
233 bool success = false;
234 success = info->Set(context, toV8StringInternalized(m_isolate, "enabled"),
235 v8::Boolean::New(m_isolate, activated))
236 .FromMaybe(false);
237 DCHECK(success);
238 v8::Local<v8::Function> setBreakpointsActivated =
239 v8::Local<v8::Function>::Cast(
240 m_debuggerScript.Get(m_isolate)
241 ->Get(context, toV8StringInternalized(m_isolate,
242 "setBreakpointsActivated"))
243 .ToLocalChecked());
244 v8::Debug::Call(debuggerContext(), setBreakpointsActivated, info)
245 .ToLocalChecked();
246
247 m_breakpointsActivated = activated;
248 }
249
250 V8Debugger::PauseOnExceptionsState V8Debugger::getPauseOnExceptionsState() {
251 DCHECK(enabled());
252 v8::HandleScope scope(m_isolate);
253 v8::Local<v8::Context> context = debuggerContext();
254 v8::Context::Scope contextScope(context);
255
256 v8::Local<v8::Value> argv[] = {v8::Undefined(m_isolate)};
257 v8::Local<v8::Value> result =
258 callDebuggerMethod("pauseOnExceptionsState", 0, argv).ToLocalChecked();
259 return static_cast<V8Debugger::PauseOnExceptionsState>(
260 result->Int32Value(context).FromJust());
261 }
262
263 void V8Debugger::setPauseOnExceptionsState(
264 PauseOnExceptionsState pauseOnExceptionsState) {
265 DCHECK(enabled());
266 v8::HandleScope scope(m_isolate);
267 v8::Context::Scope contextScope(debuggerContext());
268
269 v8::Local<v8::Value> argv[] = {
270 v8::Int32::New(m_isolate, pauseOnExceptionsState)};
271 callDebuggerMethod("setPauseOnExceptionsState", 1, argv);
272 }
273
274 void V8Debugger::setPauseOnNextStatement(bool pause) {
275 if (m_runningNestedMessageLoop) return;
276 if (pause)
277 v8::Debug::DebugBreak(m_isolate);
278 else
279 v8::Debug::CancelDebugBreak(m_isolate);
280 }
281
282 bool V8Debugger::canBreakProgram() {
283 if (!m_breakpointsActivated) return false;
284 return m_isolate->InContext();
285 }
286
287 void V8Debugger::breakProgram() {
288 if (isPaused()) {
289 DCHECK(!m_runningNestedMessageLoop);
290 v8::Local<v8::Value> exception;
291 v8::Local<v8::Array> hitBreakpoints;
292 handleProgramBreak(m_pausedContext, m_executionState, exception,
293 hitBreakpoints);
294 return;
295 }
296
297 if (!canBreakProgram()) return;
298
299 v8::HandleScope scope(m_isolate);
300 v8::Local<v8::Function> breakFunction;
301 if (!v8::Function::New(m_isolate->GetCurrentContext(),
302 &V8Debugger::breakProgramCallback,
303 v8::External::New(m_isolate, this), 0,
304 v8::ConstructorBehavior::kThrow)
305 .ToLocal(&breakFunction))
306 return;
307 v8::Debug::Call(debuggerContext(), breakFunction).ToLocalChecked();
308 }
309
310 void V8Debugger::continueProgram() {
311 if (isPaused()) m_inspector->client()->quitMessageLoopOnPause();
312 m_pausedContext.Clear();
313 m_executionState.Clear();
314 }
315
316 void V8Debugger::stepIntoStatement() {
317 DCHECK(isPaused());
318 DCHECK(!m_executionState.IsEmpty());
319 v8::HandleScope handleScope(m_isolate);
320 v8::Local<v8::Value> argv[] = {m_executionState};
321 callDebuggerMethod(stepIntoV8MethodName, 1, argv);
322 continueProgram();
323 }
324
325 void V8Debugger::stepOverStatement() {
326 DCHECK(isPaused());
327 DCHECK(!m_executionState.IsEmpty());
328 v8::HandleScope handleScope(m_isolate);
329 v8::Local<v8::Value> argv[] = {m_executionState};
330 callDebuggerMethod("stepOverStatement", 1, argv);
331 continueProgram();
332 }
333
334 void V8Debugger::stepOutOfFunction() {
335 DCHECK(isPaused());
336 DCHECK(!m_executionState.IsEmpty());
337 v8::HandleScope handleScope(m_isolate);
338 v8::Local<v8::Value> argv[] = {m_executionState};
339 callDebuggerMethod(stepOutV8MethodName, 1, argv);
340 continueProgram();
341 }
342
343 void V8Debugger::clearStepping() {
344 DCHECK(enabled());
345 v8::HandleScope scope(m_isolate);
346 v8::Context::Scope contextScope(debuggerContext());
347
348 v8::Local<v8::Value> argv[] = {v8::Undefined(m_isolate)};
349 callDebuggerMethod("clearStepping", 0, argv);
350 }
351
352 bool V8Debugger::setScriptSource(
353 const String16& sourceID, v8::Local<v8::String> newSource, bool dryRun,
354 ErrorString* error,
355 Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails,
356 JavaScriptCallFrames* newCallFrames, Maybe<bool>* stackChanged) {
357 class EnableLiveEditScope {
358 public:
359 explicit EnableLiveEditScope(v8::Isolate* isolate) : m_isolate(isolate) {
360 v8::Debug::SetLiveEditEnabled(m_isolate, true);
361 inLiveEditScope = true;
362 }
363 ~EnableLiveEditScope() {
364 v8::Debug::SetLiveEditEnabled(m_isolate, false);
365 inLiveEditScope = false;
366 }
367
368 private:
369 v8::Isolate* m_isolate;
370 };
371
372 DCHECK(enabled());
373 v8::HandleScope scope(m_isolate);
374
375 std::unique_ptr<v8::Context::Scope> contextScope;
376 if (!isPaused())
377 contextScope = wrapUnique(new v8::Context::Scope(debuggerContext()));
378
379 v8::Local<v8::Value> argv[] = {toV8String(m_isolate, sourceID), newSource,
380 v8Boolean(dryRun, m_isolate)};
381
382 v8::Local<v8::Value> v8result;
383 {
384 EnableLiveEditScope enableLiveEditScope(m_isolate);
385 v8::TryCatch tryCatch(m_isolate);
386 tryCatch.SetVerbose(false);
387 v8::MaybeLocal<v8::Value> maybeResult =
388 callDebuggerMethod("liveEditScriptSource", 3, argv);
389 if (tryCatch.HasCaught()) {
390 v8::Local<v8::Message> message = tryCatch.Message();
391 if (!message.IsEmpty())
392 *error = toProtocolStringWithTypeCheck(message->Get());
393 else
394 *error = "Unknown error.";
395 return false;
396 }
397 v8result = maybeResult.ToLocalChecked();
398 }
399 DCHECK(!v8result.IsEmpty());
400 v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
401 v8::Local<v8::Object> resultTuple =
402 v8result->ToObject(context).ToLocalChecked();
403 int code = static_cast<int>(resultTuple->Get(context, 0)
404 .ToLocalChecked()
405 ->ToInteger(context)
406 .ToLocalChecked()
407 ->Value());
408 switch (code) {
409 case 0: {
410 *stackChanged = resultTuple->Get(context, 1)
411 .ToLocalChecked()
412 ->BooleanValue(context)
413 .FromJust();
414 // Call stack may have changed after if the edited function was on the
415 // stack.
416 if (!dryRun && isPaused()) {
417 JavaScriptCallFrames frames = currentCallFrames();
418 newCallFrames->swap(frames);
419 }
420 return true;
421 }
422 // Compile error.
423 case 1: {
424 *exceptionDetails =
425 protocol::Runtime::ExceptionDetails::create()
426 .setExceptionId(m_inspector->nextExceptionId())
427 .setText(toProtocolStringWithTypeCheck(
428 resultTuple->Get(context, 2).ToLocalChecked()))
429 .setLineNumber(resultTuple->Get(context, 3)
430 .ToLocalChecked()
431 ->ToInteger(context)
432 .ToLocalChecked()
433 ->Value() -
434 1)
435 .setColumnNumber(resultTuple->Get(context, 4)
436 .ToLocalChecked()
437 ->ToInteger(context)
438 .ToLocalChecked()
439 ->Value() -
440 1)
441 .build();
442 return false;
443 }
444 }
445 *error = "Unknown error.";
446 return false;
447 }
448
449 JavaScriptCallFrames V8Debugger::currentCallFrames(int limit) {
450 if (!m_isolate->InContext()) return JavaScriptCallFrames();
451 v8::Local<v8::Value> currentCallFramesV8;
452 if (m_executionState.IsEmpty()) {
453 v8::Local<v8::Function> currentCallFramesFunction =
454 v8::Local<v8::Function>::Cast(
455 m_debuggerScript.Get(m_isolate)
456 ->Get(debuggerContext(),
457 toV8StringInternalized(m_isolate, "currentCallFrames"))
458 .ToLocalChecked());
459 currentCallFramesV8 =
460 v8::Debug::Call(debuggerContext(), currentCallFramesFunction,
461 v8::Integer::New(m_isolate, limit))
462 .ToLocalChecked();
463 } else {
464 v8::Local<v8::Value> argv[] = {m_executionState,
465 v8::Integer::New(m_isolate, limit)};
466 currentCallFramesV8 =
467 callDebuggerMethod("currentCallFrames", arraysize(argv), argv)
468 .ToLocalChecked();
469 }
470 DCHECK(!currentCallFramesV8.IsEmpty());
471 if (!currentCallFramesV8->IsArray()) return JavaScriptCallFrames();
472 v8::Local<v8::Array> callFramesArray = currentCallFramesV8.As<v8::Array>();
473 JavaScriptCallFrames callFrames;
474 for (size_t i = 0; i < callFramesArray->Length(); ++i) {
475 v8::Local<v8::Value> callFrameValue;
476 if (!callFramesArray->Get(debuggerContext(), i).ToLocal(&callFrameValue))
477 return JavaScriptCallFrames();
478 if (!callFrameValue->IsObject()) return JavaScriptCallFrames();
479 v8::Local<v8::Object> callFrameObject = callFrameValue.As<v8::Object>();
480 callFrames.push_back(JavaScriptCallFrame::create(
481 debuggerContext(), v8::Local<v8::Object>::Cast(callFrameObject)));
482 }
483 return callFrames;
484 }
485
486 static V8Debugger* toV8Debugger(v8::Local<v8::Value> data) {
487 void* p = v8::Local<v8::External>::Cast(data)->Value();
488 return static_cast<V8Debugger*>(p);
489 }
490
491 void V8Debugger::breakProgramCallback(
492 const v8::FunctionCallbackInfo<v8::Value>& info) {
493 DCHECK_EQ(info.Length(), 2);
494 V8Debugger* thisPtr = toV8Debugger(info.Data());
495 if (!thisPtr->enabled()) return;
496 v8::Local<v8::Context> pausedContext =
497 thisPtr->m_isolate->GetCurrentContext();
498 v8::Local<v8::Value> exception;
499 v8::Local<v8::Array> hitBreakpoints;
500 thisPtr->handleProgramBreak(pausedContext,
501 v8::Local<v8::Object>::Cast(info[0]), exception,
502 hitBreakpoints);
503 }
504
505 void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext,
506 v8::Local<v8::Object> executionState,
507 v8::Local<v8::Value> exception,
508 v8::Local<v8::Array> hitBreakpointNumbers,
509 bool isPromiseRejection) {
510 // Don't allow nested breaks.
511 if (m_runningNestedMessageLoop) return;
512
513 V8DebuggerAgentImpl* agent =
514 m_inspector->enabledDebuggerAgentForGroup(getGroupId(pausedContext));
515 if (!agent) return;
516
517 std::vector<String16> breakpointIds;
518 if (!hitBreakpointNumbers.IsEmpty()) {
519 breakpointIds.reserve(hitBreakpointNumbers->Length());
520 for (size_t i = 0; i < hitBreakpointNumbers->Length(); i++) {
521 v8::Local<v8::Value> hitBreakpointNumber =
522 hitBreakpointNumbers->Get(debuggerContext(), i).ToLocalChecked();
523 DCHECK(hitBreakpointNumber->IsInt32());
524 breakpointIds.push_back(String16::fromInteger(
525 hitBreakpointNumber->Int32Value(debuggerContext()).FromJust()));
526 }
527 }
528
529 m_pausedContext = pausedContext;
530 m_executionState = executionState;
531 V8DebuggerAgentImpl::SkipPauseRequest result = agent->didPause(
532 pausedContext, exception, breakpointIds, isPromiseRejection);
533 if (result == V8DebuggerAgentImpl::RequestNoSkip) {
534 m_runningNestedMessageLoop = true;
535 int groupId = getGroupId(pausedContext);
536 DCHECK(groupId);
537 m_inspector->client()->runMessageLoopOnPause(groupId);
538 // The agent may have been removed in the nested loop.
539 agent =
540 m_inspector->enabledDebuggerAgentForGroup(getGroupId(pausedContext));
541 if (agent) agent->didContinue();
542 m_runningNestedMessageLoop = false;
543 }
544 m_pausedContext.Clear();
545 m_executionState.Clear();
546
547 if (result == V8DebuggerAgentImpl::RequestStepFrame) {
548 v8::Local<v8::Value> argv[] = {executionState};
549 callDebuggerMethod("stepFrameStatement", 1, argv);
550 } else if (result == V8DebuggerAgentImpl::RequestStepInto) {
551 v8::Local<v8::Value> argv[] = {executionState};
552 callDebuggerMethod(stepIntoV8MethodName, 1, argv);
553 } else if (result == V8DebuggerAgentImpl::RequestStepOut) {
554 v8::Local<v8::Value> argv[] = {executionState};
555 callDebuggerMethod(stepOutV8MethodName, 1, argv);
556 }
557 }
558
559 void V8Debugger::v8DebugEventCallback(
560 const v8::Debug::EventDetails& eventDetails) {
561 V8Debugger* thisPtr = toV8Debugger(eventDetails.GetCallbackData());
562 thisPtr->handleV8DebugEvent(eventDetails);
563 }
564
565 v8::Local<v8::Value> V8Debugger::callInternalGetterFunction(
566 v8::Local<v8::Object> object, const char* functionName) {
567 v8::MicrotasksScope microtasks(m_isolate,
568 v8::MicrotasksScope::kDoNotRunMicrotasks);
569 v8::Local<v8::Value> getterValue =
570 object
571 ->Get(m_isolate->GetCurrentContext(),
572 toV8StringInternalized(m_isolate, functionName))
573 .ToLocalChecked();
574 DCHECK(!getterValue.IsEmpty() && getterValue->IsFunction());
575 return v8::Local<v8::Function>::Cast(getterValue)
576 ->Call(m_isolate->GetCurrentContext(), object, 0, 0)
577 .ToLocalChecked();
578 }
579
580 void V8Debugger::handleV8DebugEvent(
581 const v8::Debug::EventDetails& eventDetails) {
582 if (!enabled()) return;
583 v8::DebugEvent event = eventDetails.GetEvent();
584 if (event != v8::AsyncTaskEvent && event != v8::Break &&
585 event != v8::Exception && event != v8::AfterCompile &&
586 event != v8::BeforeCompile && event != v8::CompileError)
587 return;
588
589 v8::Local<v8::Context> eventContext = eventDetails.GetEventContext();
590 DCHECK(!eventContext.IsEmpty());
591
592 if (event == v8::AsyncTaskEvent) {
593 v8::HandleScope scope(m_isolate);
594 handleV8AsyncTaskEvent(eventContext, eventDetails.GetExecutionState(),
595 eventDetails.GetEventData());
596 return;
597 }
598
599 V8DebuggerAgentImpl* agent =
600 m_inspector->enabledDebuggerAgentForGroup(getGroupId(eventContext));
601 if (agent) {
602 v8::HandleScope scope(m_isolate);
603 if (m_ignoreScriptParsedEventsCounter == 0 &&
604 (event == v8::AfterCompile || event == v8::CompileError)) {
605 v8::Context::Scope contextScope(debuggerContext());
606 v8::Local<v8::Value> argv[] = {eventDetails.GetEventData()};
607 v8::Local<v8::Value> value =
608 callDebuggerMethod("getAfterCompileScript", 1, argv).ToLocalChecked();
609 if (value->IsNull()) return;
610 DCHECK(value->IsObject());
611 v8::Local<v8::Object> scriptObject = v8::Local<v8::Object>::Cast(value);
612 agent->didParseSource(
613 wrapUnique(new V8DebuggerScript(debuggerContext(), scriptObject,
614 inLiveEditScope)),
615 event == v8::AfterCompile);
616 } else if (event == v8::Exception) {
617 v8::Local<v8::Object> eventData = eventDetails.GetEventData();
618 v8::Local<v8::Value> exception =
619 callInternalGetterFunction(eventData, "exception");
620 v8::Local<v8::Value> promise =
621 callInternalGetterFunction(eventData, "promise");
622 bool isPromiseRejection = !promise.IsEmpty() && promise->IsObject();
623 handleProgramBreak(eventContext, eventDetails.GetExecutionState(),
624 exception, v8::Local<v8::Array>(), isPromiseRejection);
625 } else if (event == v8::Break) {
626 v8::Local<v8::Value> argv[] = {eventDetails.GetEventData()};
627 v8::Local<v8::Value> hitBreakpoints =
628 callDebuggerMethod("getBreakpointNumbers", 1, argv).ToLocalChecked();
629 DCHECK(hitBreakpoints->IsArray());
630 handleProgramBreak(eventContext, eventDetails.GetExecutionState(),
631 v8::Local<v8::Value>(),
632 hitBreakpoints.As<v8::Array>());
633 }
634 }
635 }
636
637 void V8Debugger::handleV8AsyncTaskEvent(v8::Local<v8::Context> context,
638 v8::Local<v8::Object> executionState,
639 v8::Local<v8::Object> eventData) {
640 if (!m_maxAsyncCallStackDepth) return;
641
642 String16 type = toProtocolStringWithTypeCheck(
643 callInternalGetterFunction(eventData, "type"));
644 String16 name = toProtocolStringWithTypeCheck(
645 callInternalGetterFunction(eventData, "name"));
646 int id = callInternalGetterFunction(eventData, "id")
647 ->ToInteger(context)
648 .ToLocalChecked()
649 ->Value();
650 // Async task events from Promises are given misaligned pointers to prevent
651 // from overlapping with other Blink task identifiers. There is a single
652 // namespace of such ids, managed by src/js/promise.js.
653 void* ptr = reinterpret_cast<void*>(id * 2 + 1);
654 if (type == v8AsyncTaskEventEnqueue)
655 asyncTaskScheduled(name, ptr, false);
656 else if (type == v8AsyncTaskEventWillHandle)
657 asyncTaskStarted(ptr);
658 else if (type == v8AsyncTaskEventDidHandle)
659 asyncTaskFinished(ptr);
660 else
661 UNREACHABLE();
662 }
663
664 V8StackTraceImpl* V8Debugger::currentAsyncCallChain() {
665 if (!m_currentStacks.size()) return nullptr;
666 return m_currentStacks.back().get();
667 }
668
669 void V8Debugger::compileDebuggerScript() {
670 if (!m_debuggerScript.IsEmpty()) {
671 UNREACHABLE();
672 return;
673 }
674
675 v8::HandleScope scope(m_isolate);
676 v8::Context::Scope contextScope(debuggerContext());
677
678 v8::Local<v8::String> scriptValue =
679 v8::String::NewFromUtf8(m_isolate, DebuggerScript_js,
680 v8::NewStringType::kInternalized,
681 sizeof(DebuggerScript_js))
682 .ToLocalChecked();
683 v8::Local<v8::Value> value;
684 if (!m_inspector->compileAndRunInternalScript(debuggerContext(), scriptValue)
685 .ToLocal(&value)) {
686 UNREACHABLE();
687 return;
688 }
689 DCHECK(value->IsObject());
690 m_debuggerScript.Reset(m_isolate, value.As<v8::Object>());
691 }
692
693 v8::Local<v8::Context> V8Debugger::debuggerContext() const {
694 DCHECK(!m_debuggerContext.IsEmpty());
695 return m_debuggerContext.Get(m_isolate);
696 }
697
698 v8::MaybeLocal<v8::Value> V8Debugger::functionScopes(
699 v8::Local<v8::Context> context, v8::Local<v8::Function> function) {
700 if (!enabled()) {
701 UNREACHABLE();
702 return v8::Local<v8::Value>::New(m_isolate, v8::Undefined(m_isolate));
703 }
704 v8::Local<v8::Value> argv[] = {function};
705 v8::Local<v8::Value> scopesValue;
706 if (!callDebuggerMethod("getFunctionScopes", 1, argv).ToLocal(&scopesValue))
707 return v8::MaybeLocal<v8::Value>();
708 v8::Local<v8::Value> copied;
709 if (!copyValueFromDebuggerContext(m_isolate, debuggerContext(), context,
710 scopesValue)
711 .ToLocal(&copied) ||
712 !copied->IsArray())
713 return v8::MaybeLocal<v8::Value>();
714 if (!markAsInternal(context, v8::Local<v8::Array>::Cast(copied),
715 V8InternalValueType::kScopeList))
716 return v8::MaybeLocal<v8::Value>();
717 if (!markArrayEntriesAsInternal(context, v8::Local<v8::Array>::Cast(copied),
718 V8InternalValueType::kScope))
719 return v8::MaybeLocal<v8::Value>();
720 return copied;
721 }
722
723 v8::MaybeLocal<v8::Array> V8Debugger::internalProperties(
724 v8::Local<v8::Context> context, v8::Local<v8::Value> value) {
725 v8::Local<v8::Array> properties;
726 if (!v8::Debug::GetInternalProperties(m_isolate, value).ToLocal(&properties))
727 return v8::MaybeLocal<v8::Array>();
728 if (value->IsFunction()) {
729 v8::Local<v8::Function> function = value.As<v8::Function>();
730 v8::Local<v8::Value> location = functionLocation(context, function);
731 if (location->IsObject()) {
732 createDataProperty(
733 context, properties, properties->Length(),
734 toV8StringInternalized(m_isolate, "[[FunctionLocation]]"));
735 createDataProperty(context, properties, properties->Length(), location);
736 }
737 if (function->IsGeneratorFunction()) {
738 createDataProperty(context, properties, properties->Length(),
739 toV8StringInternalized(m_isolate, "[[IsGenerator]]"));
740 createDataProperty(context, properties, properties->Length(),
741 v8::True(m_isolate));
742 }
743 }
744 if (!enabled()) return properties;
745 if (value->IsMap() || value->IsWeakMap() || value->IsSet() ||
746 value->IsWeakSet() || value->IsSetIterator() || value->IsMapIterator()) {
747 v8::Local<v8::Value> entries =
748 collectionEntries(context, v8::Local<v8::Object>::Cast(value));
749 if (entries->IsArray()) {
750 createDataProperty(context, properties, properties->Length(),
751 toV8StringInternalized(m_isolate, "[[Entries]]"));
752 createDataProperty(context, properties, properties->Length(), entries);
753 }
754 }
755 if (value->IsGeneratorObject()) {
756 v8::Local<v8::Value> location =
757 generatorObjectLocation(context, v8::Local<v8::Object>::Cast(value));
758 if (location->IsObject()) {
759 createDataProperty(
760 context, properties, properties->Length(),
761 toV8StringInternalized(m_isolate, "[[GeneratorLocation]]"));
762 createDataProperty(context, properties, properties->Length(), location);
763 }
764 }
765 if (value->IsFunction()) {
766 v8::Local<v8::Function> function = value.As<v8::Function>();
767 v8::Local<v8::Value> boundFunction = function->GetBoundFunction();
768 v8::Local<v8::Value> scopes;
769 if (boundFunction->IsUndefined() &&
770 functionScopes(context, function).ToLocal(&scopes)) {
771 createDataProperty(context, properties, properties->Length(),
772 toV8StringInternalized(m_isolate, "[[Scopes]]"));
773 createDataProperty(context, properties, properties->Length(), scopes);
774 }
775 }
776 return properties;
777 }
778
779 v8::Local<v8::Value> V8Debugger::collectionEntries(
780 v8::Local<v8::Context> context, v8::Local<v8::Object> object) {
781 if (!enabled()) {
782 UNREACHABLE();
783 return v8::Undefined(m_isolate);
784 }
785 v8::Local<v8::Value> argv[] = {object};
786 v8::Local<v8::Value> entriesValue =
787 callDebuggerMethod("getCollectionEntries", 1, argv).ToLocalChecked();
788 v8::Local<v8::Value> copied;
789 if (!copyValueFromDebuggerContext(m_isolate, debuggerContext(), context,
790 entriesValue)
791 .ToLocal(&copied) ||
792 !copied->IsArray())
793 return v8::Undefined(m_isolate);
794 if (!markArrayEntriesAsInternal(context, v8::Local<v8::Array>::Cast(copied),
795 V8InternalValueType::kEntry))
796 return v8::Undefined(m_isolate);
797 return copied;
798 }
799
800 v8::Local<v8::Value> V8Debugger::generatorObjectLocation(
801 v8::Local<v8::Context> context, v8::Local<v8::Object> object) {
802 if (!enabled()) {
803 UNREACHABLE();
804 return v8::Null(m_isolate);
805 }
806 v8::Local<v8::Value> argv[] = {object};
807 v8::Local<v8::Value> location =
808 callDebuggerMethod("getGeneratorObjectLocation", 1, argv)
809 .ToLocalChecked();
810 v8::Local<v8::Value> copied;
811 if (!copyValueFromDebuggerContext(m_isolate, debuggerContext(), context,
812 location)
813 .ToLocal(&copied) ||
814 !copied->IsObject())
815 return v8::Null(m_isolate);
816 if (!markAsInternal(context, v8::Local<v8::Object>::Cast(copied),
817 V8InternalValueType::kLocation))
818 return v8::Null(m_isolate);
819 return copied;
820 }
821
822 v8::Local<v8::Value> V8Debugger::functionLocation(
823 v8::Local<v8::Context> context, v8::Local<v8::Function> function) {
824 int scriptId = function->ScriptId();
825 if (scriptId == v8::UnboundScript::kNoScriptId) return v8::Null(m_isolate);
826 int lineNumber = function->GetScriptLineNumber();
827 int columnNumber = function->GetScriptColumnNumber();
828 if (lineNumber == v8::Function::kLineOffsetNotFound ||
829 columnNumber == v8::Function::kLineOffsetNotFound)
830 return v8::Null(m_isolate);
831 v8::Local<v8::Object> location = v8::Object::New(m_isolate);
832 if (!location->SetPrototype(context, v8::Null(m_isolate)).FromMaybe(false))
833 return v8::Null(m_isolate);
834 if (!createDataProperty(
835 context, location, toV8StringInternalized(m_isolate, "scriptId"),
836 toV8String(m_isolate, String16::fromInteger(scriptId)))
837 .FromMaybe(false))
838 return v8::Null(m_isolate);
839 if (!createDataProperty(context, location,
840 toV8StringInternalized(m_isolate, "lineNumber"),
841 v8::Integer::New(m_isolate, lineNumber))
842 .FromMaybe(false))
843 return v8::Null(m_isolate);
844 if (!createDataProperty(context, location,
845 toV8StringInternalized(m_isolate, "columnNumber"),
846 v8::Integer::New(m_isolate, columnNumber))
847 .FromMaybe(false))
848 return v8::Null(m_isolate);
849 if (!markAsInternal(context, location, V8InternalValueType::kLocation))
850 return v8::Null(m_isolate);
851 return location;
852 }
853
854 bool V8Debugger::isPaused() { return !m_pausedContext.IsEmpty(); }
855
856 std::unique_ptr<V8StackTraceImpl> V8Debugger::createStackTrace(
857 v8::Local<v8::StackTrace> stackTrace) {
858 int contextGroupId =
859 m_isolate->InContext() ? getGroupId(m_isolate->GetCurrentContext()) : 0;
860 return V8StackTraceImpl::create(this, contextGroupId, stackTrace,
861 V8StackTraceImpl::maxCallStackSizeToCapture);
862 }
863
864 int V8Debugger::markContext(const V8ContextInfo& info) {
865 DCHECK(info.context->GetIsolate() == m_isolate);
866 int contextId = ++m_lastContextId;
867 String16 debugData = String16::fromInteger(info.contextGroupId) + "," +
868 String16::fromInteger(contextId) + "," +
869 toString16(info.auxData);
870 v8::Context::Scope contextScope(info.context);
871 info.context->SetEmbedderData(static_cast<int>(v8::Context::kDebugIdIndex),
872 toV8String(m_isolate, debugData));
873 return contextId;
874 }
875
876 void V8Debugger::setAsyncCallStackDepth(V8DebuggerAgentImpl* agent, int depth) {
877 if (depth <= 0)
878 m_maxAsyncCallStackDepthMap.erase(agent);
879 else
880 m_maxAsyncCallStackDepthMap[agent] = depth;
881
882 int maxAsyncCallStackDepth = 0;
883 for (const auto& pair : m_maxAsyncCallStackDepthMap) {
884 if (pair.second > maxAsyncCallStackDepth)
885 maxAsyncCallStackDepth = pair.second;
886 }
887
888 if (m_maxAsyncCallStackDepth == maxAsyncCallStackDepth) return;
889 m_maxAsyncCallStackDepth = maxAsyncCallStackDepth;
890 if (!maxAsyncCallStackDepth) allAsyncTasksCanceled();
891 }
892
893 void V8Debugger::asyncTaskScheduled(const StringView& taskName, void* task,
894 bool recurring) {
895 if (!m_maxAsyncCallStackDepth) return;
896 asyncTaskScheduled(toString16(taskName), task, recurring);
897 }
898
899 void V8Debugger::asyncTaskScheduled(const String16& taskName, void* task,
900 bool recurring) {
901 if (!m_maxAsyncCallStackDepth) return;
902 v8::HandleScope scope(m_isolate);
903 int contextGroupId =
904 m_isolate->InContext() ? getGroupId(m_isolate->GetCurrentContext()) : 0;
905 std::unique_ptr<V8StackTraceImpl> chain = V8StackTraceImpl::capture(
906 this, contextGroupId, V8StackTraceImpl::maxCallStackSizeToCapture,
907 taskName);
908 if (chain) {
909 m_asyncTaskStacks[task] = std::move(chain);
910 if (recurring) m_recurringTasks.insert(task);
911 }
912 }
913
914 void V8Debugger::asyncTaskCanceled(void* task) {
915 if (!m_maxAsyncCallStackDepth) return;
916 m_asyncTaskStacks.erase(task);
917 m_recurringTasks.erase(task);
918 }
919
920 void V8Debugger::asyncTaskStarted(void* task) {
921 if (!m_maxAsyncCallStackDepth) return;
922 m_currentTasks.push_back(task);
923 AsyncTaskToStackTrace::iterator stackIt = m_asyncTaskStacks.find(task);
924 // Needs to support following order of events:
925 // - asyncTaskScheduled
926 // <-- attached here -->
927 // - asyncTaskStarted
928 // - asyncTaskCanceled <-- canceled before finished
929 // <-- async stack requested here -->
930 // - asyncTaskFinished
931 std::unique_ptr<V8StackTraceImpl> stack;
932 if (stackIt != m_asyncTaskStacks.end() && stackIt->second)
933 stack = stackIt->second->cloneImpl();
934 m_currentStacks.push_back(std::move(stack));
935 }
936
937 void V8Debugger::asyncTaskFinished(void* task) {
938 if (!m_maxAsyncCallStackDepth) return;
939 // We could start instrumenting half way and the stack is empty.
940 if (!m_currentStacks.size()) return;
941
942 DCHECK(m_currentTasks.back() == task);
943 m_currentTasks.pop_back();
944
945 m_currentStacks.pop_back();
946 if (m_recurringTasks.find(task) == m_recurringTasks.end())
947 m_asyncTaskStacks.erase(task);
948 }
949
950 void V8Debugger::allAsyncTasksCanceled() {
951 m_asyncTaskStacks.clear();
952 m_recurringTasks.clear();
953 m_currentStacks.clear();
954 m_currentTasks.clear();
955 }
956
957 void V8Debugger::muteScriptParsedEvents() {
958 ++m_ignoreScriptParsedEventsCounter;
959 }
960
961 void V8Debugger::unmuteScriptParsedEvents() {
962 --m_ignoreScriptParsedEventsCounter;
963 DCHECK_GE(m_ignoreScriptParsedEventsCounter, 0);
964 }
965
966 std::unique_ptr<V8StackTraceImpl> V8Debugger::captureStackTrace(
967 bool fullStack) {
968 if (!m_isolate->InContext()) return nullptr;
969
970 v8::HandleScope handles(m_isolate);
971 int contextGroupId = getGroupId(m_isolate->GetCurrentContext());
972 if (!contextGroupId) return nullptr;
973
974 size_t stackSize =
975 fullStack ? V8StackTraceImpl::maxCallStackSizeToCapture : 1;
976 if (m_inspector->enabledRuntimeAgentForGroup(contextGroupId))
977 stackSize = V8StackTraceImpl::maxCallStackSizeToCapture;
978
979 return V8StackTraceImpl::capture(this, contextGroupId, stackSize);
980 }
981
982 } // namespace v8_inspector
OLDNEW
« no previous file with comments | « src/inspector/V8Debugger.h ('k') | src/inspector/V8DebuggerAgentImpl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698