[inspector] move SetScriptSource call to native

To avoid using debugging context and debugger-script.js on inspector side we can move SetScriptSource call to v8::internal::Debug. Theoretically we can move live edit implementation to native completely but since it will be reimplemented it looks redundant.

R=yangguo@chromium.org,jgruber@chromium.org

Bug: chromium:652939
Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel;master.tryserver.chromium.linux:linux_chromium_rel_ng
Change-Id: Id09492c2d2a93efbde429c9cc1bc181d5fdda19b
Reviewed-on: https://chromium-review.googlesource.com/590736
Commit-Queue: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46985}
This commit is contained in:
Alexey Kozyatinskiy 2017-07-28 11:48:22 -07:00 committed by Commit Bot
parent 920025f523
commit 6b0bf1659e
15 changed files with 340 additions and 195 deletions

View File

@ -9666,6 +9666,14 @@ v8::debug::Location debug::Script::GetSourceLocation(int offset) const {
return debug::Location(info.line, info.column); return debug::Location(info.line, info.column);
} }
bool debug::Script::SetScriptSource(v8::Local<v8::String> newSource,
bool preview, bool* stack_changed) const {
i::Handle<i::Script> script = Utils::OpenHandle(this);
i::Isolate* isolate = script->GetIsolate();
return isolate->debug()->SetScriptSource(
script, Utils::OpenHandle(*newSource), preview, stack_changed);
}
debug::WasmScript* debug::WasmScript::Cast(debug::Script* script) { debug::WasmScript* debug::WasmScript::Cast(debug::Script* script) {
CHECK(script->IsWasm()); CHECK(script->IsWasm());
return static_cast<WasmScript*>(script); return static_cast<WasmScript*>(script);

View File

@ -146,6 +146,8 @@ class V8_EXPORT_PRIVATE Script {
std::vector<debug::BreakLocation>* locations) const; std::vector<debug::BreakLocation>* locations) const;
int GetSourceOffset(const debug::Location& location) const; int GetSourceOffset(const debug::Location& location) const;
v8::debug::Location GetSourceLocation(int offset) const; v8::debug::Location GetSourceLocation(int offset) const;
bool SetScriptSource(v8::Local<v8::String> newSource, bool preview,
bool* stack_changed) const;
}; };
// Specialization for wasm Scripts. // Specialization for wasm Scripts.
@ -171,7 +173,7 @@ class DebugDelegate {
virtual ~DebugDelegate() {} virtual ~DebugDelegate() {}
virtual void PromiseEventOccurred(debug::PromiseDebugActionType type, int id, virtual void PromiseEventOccurred(debug::PromiseDebugActionType type, int id,
int parent_id, bool created_by_user) {} int parent_id, bool created_by_user) {}
virtual void ScriptCompiled(v8::Local<Script> script, virtual void ScriptCompiled(v8::Local<Script> script, bool is_live_edited,
bool has_compile_error) {} bool has_compile_error) {}
virtual void BreakProgramRequested(v8::Local<v8::Context> paused_context, virtual void BreakProgramRequested(v8::Local<v8::Context> paused_context,
v8::Local<v8::Object> exec_state, v8::Local<v8::Object> exec_state,

View File

@ -612,9 +612,9 @@ bool Debug::IsMutedAtCurrentLocation(JavaScriptFrame* frame) {
return has_break_points_at_all; return has_break_points_at_all;
} }
MaybeHandle<Object> Debug::CallFunction(const char* name, int argc, MaybeHandle<Object> Debug::CallFunction(const char* name, int argc,
Handle<Object> args[]) { Handle<Object> args[],
bool catch_exceptions) {
AllowJavascriptExecutionDebugOnly allow_script(isolate_); AllowJavascriptExecutionDebugOnly allow_script(isolate_);
PostponeInterruptsScope no_interrupts(isolate_); PostponeInterruptsScope no_interrupts(isolate_);
AssertDebugContext(); AssertDebugContext();
@ -623,10 +623,14 @@ MaybeHandle<Object> Debug::CallFunction(const char* name, int argc,
Handle<JSFunction> fun = Handle<JSFunction>::cast( Handle<JSFunction> fun = Handle<JSFunction>::cast(
JSReceiver::GetProperty(isolate_, holder, name).ToHandleChecked()); JSReceiver::GetProperty(isolate_, holder, name).ToHandleChecked());
Handle<Object> undefined = isolate_->factory()->undefined_value(); Handle<Object> undefined = isolate_->factory()->undefined_value();
MaybeHandle<Object> maybe_exception; if (catch_exceptions) {
return Execution::TryCall(isolate_, fun, undefined, argc, args, MaybeHandle<Object> maybe_exception;
Execution::MessageHandling::kReport, return Execution::TryCall(isolate_, fun, undefined, argc, args,
&maybe_exception); Execution::MessageHandling::kReport,
&maybe_exception);
} else {
return Execution::Call(isolate_, fun, undefined, argc, args);
}
} }
@ -2025,6 +2029,30 @@ bool Debug::AllFramesOnStackAreBlackboxed() {
return true; return true;
} }
bool Debug::SetScriptSource(Handle<Script> script, Handle<String> source,
bool preview, bool* stack_changed) {
DebugScope debug_scope(this);
set_live_edit_enabled(true);
Handle<Object> script_wrapper = Script::GetWrapper(script);
Handle<Object> argv[] = {script_wrapper, source,
isolate_->factory()->ToBoolean(preview),
isolate_->factory()->NewJSArray(0)};
Handle<Object> result;
if (!CallFunction("SetScriptSource", arraysize(argv), argv, false)
.ToHandle(&result)) {
isolate_->OptionalRescheduleException(false);
set_live_edit_enabled(false);
return false;
}
set_live_edit_enabled(false);
Handle<Object> stack_changed_value =
JSReceiver::GetProperty(isolate_, Handle<JSObject>::cast(result),
"stack_modified")
.ToHandleChecked();
*stack_changed = stack_changed_value->IsTrue(isolate_);
return true;
}
void Debug::OnAsyncTaskEvent(debug::PromiseDebugActionType type, int id, void Debug::OnAsyncTaskEvent(debug::PromiseDebugActionType type, int id,
int parent_id) { int parent_id) {
if (in_debug_scope() || ignore_events()) return; if (in_debug_scope() || ignore_events()) return;
@ -2060,6 +2088,7 @@ void Debug::ProcessCompileEvent(v8::DebugEvent event, Handle<Script> script) {
PostponeInterruptsScope postpone(isolate_); PostponeInterruptsScope postpone(isolate_);
DisableBreak no_recursive_break(this); DisableBreak no_recursive_break(this);
debug_delegate_->ScriptCompiled(ToApiHandle<debug::Script>(script), debug_delegate_->ScriptCompiled(ToApiHandle<debug::Script>(script),
live_edit_enabled(),
event != v8::AfterCompile); event != v8::AfterCompile);
} }
@ -2338,6 +2367,7 @@ void LegacyDebugDelegate::PromiseEventOccurred(
} }
void LegacyDebugDelegate::ScriptCompiled(v8::Local<v8::debug::Script> script, void LegacyDebugDelegate::ScriptCompiled(v8::Local<v8::debug::Script> script,
bool is_live_edited,
bool is_compile_error) { bool is_compile_error) {
Handle<Object> event_data; Handle<Object> event_data;
v8::DebugEvent event = is_compile_error ? v8::CompileError : v8::AfterCompile; v8::DebugEvent event = is_compile_error ? v8::CompileError : v8::AfterCompile;

View File

@ -349,6 +349,13 @@ class Debug {
bool AllFramesOnStackAreBlackboxed(); bool AllFramesOnStackAreBlackboxed();
// Set new script source, throw an exception if error occurred. When preview
// is true: try to set source, throw exception if any without actual script
// change. stack_changed is true if after editing script on pause stack is
// changed and client should request stack trace again.
bool SetScriptSource(Handle<Script> script, Handle<String> source,
bool preview, bool* stack_changed);
// Threading support. // Threading support.
char* ArchiveDebug(char* to); char* ArchiveDebug(char* to);
char* RestoreDebug(char* from); char* RestoreDebug(char* from);
@ -492,7 +499,8 @@ class Debug {
bool IsMutedAtCurrentLocation(JavaScriptFrame* frame); bool IsMutedAtCurrentLocation(JavaScriptFrame* frame);
bool CheckBreakPoint(Handle<Object> break_point_object); bool CheckBreakPoint(Handle<Object> break_point_object);
MaybeHandle<Object> CallFunction(const char* name, int argc, MaybeHandle<Object> CallFunction(const char* name, int argc,
Handle<Object> args[]); Handle<Object> args[],
bool catch_exceptions = true);
inline void AssertDebugContext() { inline void AssertDebugContext() {
DCHECK(isolate_->context() == *debug_context()); DCHECK(isolate_->context() == *debug_context());
@ -614,7 +622,7 @@ class LegacyDebugDelegate : public v8::debug::DebugDelegate {
explicit LegacyDebugDelegate(Isolate* isolate) : isolate_(isolate) {} explicit LegacyDebugDelegate(Isolate* isolate) : isolate_(isolate) {}
void PromiseEventOccurred(v8::debug::PromiseDebugActionType type, int id, void PromiseEventOccurred(v8::debug::PromiseDebugActionType type, int id,
int parent_id, bool created_by_user) override; int parent_id, bool created_by_user) override;
void ScriptCompiled(v8::Local<v8::debug::Script> script, void ScriptCompiled(v8::Local<v8::debug::Script> script, bool is_live_edited,
bool has_compile_error) override; bool has_compile_error) override;
void BreakProgramRequested(v8::Local<v8::Context> paused_context, void BreakProgramRequested(v8::Local<v8::Context> paused_context,
v8::Local<v8::Object> exec_state, v8::Local<v8::Object> exec_state,

View File

@ -1048,6 +1048,11 @@
ApplySingleChunkPatch: ApplySingleChunkPatch ApplySingleChunkPatch: ApplySingleChunkPatch
}; };
// Functions needed by the debugger runtime.
utils.InstallConstants(utils, [
"SetScriptSource", LiveEdit.SetScriptSource,
]);
global.Debug.LiveEdit = LiveEdit; global.Debug.LiveEdit = LiveEdit;
}) })

View File

@ -58,45 +58,6 @@ DebuggerScript.removeBreakpoint = function(execState, info)
Debug.findBreakPoint(info.breakpointId, true); Debug.findBreakPoint(info.breakpointId, true);
} }
// Returns array in form:
// [ 0, <v8_result_report> ] in case of success
// or [ 1, <general_error_message>, <compiler_message>, <line_number>, <column_number> ] in case of compile error, numbers are 1-based.
// or throws exception with message.
/**
* @param {number} scriptId
* @param {string} newSource
* @param {boolean} preview
* @return {!Array<*>}
*/
DebuggerScript.liveEditScriptSource = function(scriptId, newSource, preview)
{
var scripts = Debug.scripts();
var scriptToEdit = null;
for (var i = 0; i < scripts.length; i++) {
if (scripts[i].id == scriptId) {
scriptToEdit = scripts[i];
break;
}
}
if (!scriptToEdit)
throw("Script not found");
var changeLog = [];
try {
var result = Debug.LiveEdit.SetScriptSource(scriptToEdit, newSource, preview, changeLog);
return [0, result.stack_modified];
} catch (e) {
if (e instanceof Debug.LiveEdit.Failure && "details" in e) {
var details = /** @type {!LiveEditErrorDetails} */(e.details);
if (details.type === "liveedit_compile_error") {
var startPosition = details.position.start;
return [1, String(e), String(details.syntaxErrorMessage), Number(startPosition.line), Number(startPosition.column)];
}
}
throw e;
}
}
/** /**
* @param {!ExecutionState} execState * @param {!ExecutionState} execState
*/ */

View File

@ -45,31 +45,6 @@ Debug.findBreakPointActualLocations = function(breakId) {}
*/ */
Debug.findBreakPoint = function(breakId, remove) {} Debug.findBreakPoint = function(breakId, remove) {}
/** @const */
var LiveEdit = {}
/**
* @param {!Script} script
* @param {string} newSource
* @param {boolean} previewOnly
* @return {!{stack_modified: (boolean|undefined)}}
*/
LiveEdit.SetScriptSource = function(script, newSource, previewOnly, change_log) {}
/** @constructor */
function Failure() {}
LiveEdit.Failure = Failure;
Debug.LiveEdit = LiveEdit;
/** @typedef {{
* type: string,
* syntaxErrorMessage: string,
* position: !{start: !{line: number, column: number}},
* }}
*/
var LiveEditErrorDetails;
/** @typedef {{ /** @typedef {{
* breakpointId: number, * breakpointId: number,
* sourceID: number, * sourceID: number,

View File

@ -248,6 +248,62 @@ Response buildScopes(v8::debug::ScopeIterator* iterator,
return Response::OK(); return Response::OK();
} }
bool liveEditExceptionToDetails(
V8InspectorImpl* inspector, v8::Local<v8::Context> context,
v8::Local<v8::Value> exceptionValue,
Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
if (!exceptionValue->IsObject()) return false;
v8::Isolate* isolate = context->GetIsolate();
v8::Local<v8::Object> exception = exceptionValue.As<v8::Object>();
v8::Local<v8::Value> detailsValue;
if (!exception->Get(context, toV8String(isolate, "details"))
.ToLocal(&detailsValue) ||
!detailsValue->IsObject()) {
return false;
}
v8::Local<v8::Object> details = detailsValue.As<v8::Object>();
v8::Local<v8::Value> message;
if (!details->Get(context, toV8String(isolate, "syntaxErrorMessage"))
.ToLocal(&message) ||
!message->IsString()) {
return false;
}
v8::Local<v8::Value> positionValue;
if (!details->Get(context, toV8String(isolate, "position"))
.ToLocal(&positionValue) ||
!positionValue->IsObject()) {
return false;
}
v8::Local<v8::Value> startPositionValue;
if (!positionValue.As<v8::Object>()
->Get(context, toV8String(isolate, "start"))
.ToLocal(&startPositionValue) ||
!startPositionValue->IsObject()) {
return false;
}
v8::Local<v8::Object> startPosition = startPositionValue.As<v8::Object>();
v8::Local<v8::Value> lineValue;
if (!startPosition->Get(context, toV8String(isolate, "line"))
.ToLocal(&lineValue) ||
!lineValue->IsInt32()) {
return false;
}
v8::Local<v8::Value> columnValue;
if (!startPosition->Get(context, toV8String(isolate, "column"))
.ToLocal(&columnValue) ||
!columnValue->IsInt32()) {
return false;
}
*exceptionDetails =
protocol::Runtime::ExceptionDetails::create()
.setExceptionId(inspector->nextExceptionId())
.setText(toProtocolString(message.As<v8::String>()))
.setLineNumber(lineValue->Int32Value(context).FromJust() - 1)
.setColumnNumber(columnValue->Int32Value(context).FromJust() - 1)
.build();
return true;
}
} // namespace } // namespace
V8DebuggerAgentImpl::V8DebuggerAgentImpl( V8DebuggerAgentImpl::V8DebuggerAgentImpl(
@ -675,18 +731,36 @@ Response V8DebuggerAgentImpl::setScriptSource(
// TODO(kozyatinskiy): LiveEdit should support ES6 module // TODO(kozyatinskiy): LiveEdit should support ES6 module
return Response::Error("Editing module's script is not supported."); return Response::Error("Editing module's script is not supported.");
} }
int contextId = it->second->executionContextId();
int contextGroupId = m_inspector->contextGroupId(contextId);
InspectedContext* inspected =
m_inspector->getContext(contextGroupId, contextId);
if (!inspected) {
return Response::InternalError();
}
v8::HandleScope handleScope(m_isolate);
v8::Local<v8::Context> context = inspected->context();
v8::Context::Scope contextScope(context);
v8::TryCatch tryCatch(m_isolate);
v8::HandleScope handles(m_isolate); bool stackChangedValue = false;
v8::Local<v8::String> newSource = toV8String(m_isolate, newContent); it->second->setSource(newContent, dryRun.fromMaybe(false),
bool compileError = false; &stackChangedValue);
Response response = m_debugger->setScriptSource( if (tryCatch.HasCaught()) {
scriptId, newSource, dryRun.fromMaybe(false), optOutCompileError, if (liveEditExceptionToDetails(m_inspector, context, tryCatch.Exception(),
stackChanged, &compileError); optOutCompileError)) {
if (!response.isSuccess() || compileError) return response; return Response::OK();
}
it->second->setSource(newContent); v8::Local<v8::Message> message = tryCatch.Message();
if (!message.IsEmpty())
return Response::Error(toProtocolStringWithTypeCheck(message->Get()));
else
return Response::InternalError();
} else {
*stackChanged = stackChangedValue;
}
std::unique_ptr<Array<CallFrame>> callFrames; std::unique_ptr<Array<CallFrame>> callFrames;
response = currentCallFrames(&callFrames); Response response = currentCallFrames(&callFrames);
if (!response.isSuccess()) return response; if (!response.isSuccess()) return response;
*newCallFrames = std::move(callFrames); *newCallFrames = std::move(callFrames);
*asyncStackTrace = currentAsyncStackTrace(); *asyncStackTrace = currentAsyncStackTrace();

View File

@ -160,6 +160,20 @@ class ActualScript : public V8DebuggerScript {
m_sourceMappingURL = sourceMappingURL; m_sourceMappingURL = sourceMappingURL;
} }
void setSource(const String16& newSource, bool preview,
bool* stackChanged) override {
DCHECK(!isModule());
v8::HandleScope scope(m_isolate);
v8::Local<v8::String> v8Source = toV8String(m_isolate, newSource);
if (!m_script.Get(m_isolate)->SetScriptSource(v8Source, preview,
stackChanged)) {
return;
}
if (preview) return;
m_source = newSource;
m_hash = String16();
}
bool getPossibleBreakpoints( bool getPossibleBreakpoints(
const v8::debug::Location& start, const v8::debug::Location& end, const v8::debug::Location& start, const v8::debug::Location& end,
bool restrictToFunction, bool restrictToFunction,
@ -256,6 +270,7 @@ class WasmVirtualScript : public V8DebuggerScript {
bool isLiveEdit() const override { return false; } bool isLiveEdit() const override { return false; }
bool isModule() const override { return false; } bool isModule() const override { return false; }
void setSourceMappingURL(const String16&) override {} void setSourceMappingURL(const String16&) override {}
void setSource(const String16&, bool, bool*) override { UNREACHABLE(); }
bool getPossibleBreakpoints( bool getPossibleBreakpoints(
const v8::debug::Location& start, const v8::debug::Location& end, const v8::debug::Location& start, const v8::debug::Location& end,

View File

@ -71,10 +71,8 @@ class V8DebuggerScript {
void setSourceURL(const String16&); void setSourceURL(const String16&);
virtual void setSourceMappingURL(const String16&) = 0; virtual void setSourceMappingURL(const String16&) = 0;
void setSource(const String16& source) { virtual void setSource(const String16& source, bool preview,
m_source = source; bool* stackChanged) = 0;
m_hash = String16();
}
virtual bool getPossibleBreakpoints( virtual bool getPossibleBreakpoints(
const v8::debug::Location& start, const v8::debug::Location& end, const v8::debug::Location& start, const v8::debug::Location& end,

View File

@ -25,10 +25,6 @@ namespace {
static const int kMaxAsyncTaskStacks = 128 * 1024; static const int kMaxAsyncTaskStacks = 128 * 1024;
inline v8::Local<v8::Boolean> v8Boolean(bool value, v8::Isolate* isolate) {
return value ? v8::True(isolate) : v8::False(isolate);
}
v8::MaybeLocal<v8::Array> collectionsEntries(v8::Local<v8::Context> context, v8::MaybeLocal<v8::Array> collectionsEntries(v8::Local<v8::Context> context,
v8::Local<v8::Value> value) { v8::Local<v8::Value> value) {
v8::Isolate* isolate = context->GetIsolate(); v8::Isolate* isolate = context->GetIsolate();
@ -161,8 +157,6 @@ String16 scopeType(v8::debug::ScopeIterator::ScopeType type) {
} // namespace } // namespace
static bool inLiveEditScope = false;
v8::MaybeLocal<v8::Value> V8Debugger::callDebuggerMethod( v8::MaybeLocal<v8::Value> V8Debugger::callDebuggerMethod(
const char* functionName, int argc, v8::Local<v8::Value> argv[], const char* functionName, int argc, v8::Local<v8::Value> argv[],
bool catchExceptions) { bool catchExceptions) {
@ -499,96 +493,6 @@ void V8Debugger::clearContinueToLocation() {
m_continueToLocationStack.reset(); m_continueToLocationStack.reset();
} }
Response V8Debugger::setScriptSource(
const String16& sourceID, v8::Local<v8::String> newSource, bool dryRun,
Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails,
Maybe<bool>* stackChanged, bool* compileError) {
class EnableLiveEditScope {
public:
explicit EnableLiveEditScope(v8::Isolate* isolate) : m_isolate(isolate) {
v8::debug::SetLiveEditEnabled(m_isolate, true);
inLiveEditScope = true;
}
~EnableLiveEditScope() {
v8::debug::SetLiveEditEnabled(m_isolate, false);
inLiveEditScope = false;
}
private:
v8::Isolate* m_isolate;
};
*compileError = false;
DCHECK(enabled());
v8::HandleScope scope(m_isolate);
std::unique_ptr<v8::Context::Scope> contextScope;
if (!isPaused())
contextScope.reset(new v8::Context::Scope(debuggerContext()));
v8::Local<v8::Value> argv[] = {toV8String(m_isolate, sourceID), newSource,
v8Boolean(dryRun, m_isolate)};
v8::Local<v8::Value> v8result;
{
EnableLiveEditScope enableLiveEditScope(m_isolate);
v8::TryCatch tryCatch(m_isolate);
tryCatch.SetVerbose(false);
v8::MaybeLocal<v8::Value> maybeResult =
callDebuggerMethod("liveEditScriptSource", 3, argv, false);
if (tryCatch.HasCaught()) {
v8::Local<v8::Message> message = tryCatch.Message();
if (!message.IsEmpty())
return Response::Error(toProtocolStringWithTypeCheck(message->Get()));
else
return Response::InternalError();
}
v8result = maybeResult.ToLocalChecked();
}
DCHECK(!v8result.IsEmpty());
v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
v8::Local<v8::Object> resultTuple =
v8result->ToObject(context).ToLocalChecked();
int code = static_cast<int>(resultTuple->Get(context, 0)
.ToLocalChecked()
->ToInteger(context)
.ToLocalChecked()
->Value());
switch (code) {
case 0: {
*stackChanged = resultTuple->Get(context, 1)
.ToLocalChecked()
->BooleanValue(context)
.FromJust();
return Response::OK();
}
// Compile error.
case 1: {
*exceptionDetails =
protocol::Runtime::ExceptionDetails::create()
.setExceptionId(m_inspector->nextExceptionId())
.setText(toProtocolStringWithTypeCheck(
resultTuple->Get(context, 2).ToLocalChecked()))
.setLineNumber(static_cast<int>(resultTuple->Get(context, 3)
.ToLocalChecked()
->ToInteger(context)
.ToLocalChecked()
->Value()) -
1)
.setColumnNumber(static_cast<int>(resultTuple->Get(context, 4)
.ToLocalChecked()
->ToInteger(context)
.ToLocalChecked()
->Value()) -
1)
.build();
*compileError = true;
return Response::OK();
}
}
return Response::InternalError();
}
void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext, void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext,
v8::Local<v8::Object> executionState, v8::Local<v8::Object> executionState,
v8::Local<v8::Value> exception, v8::Local<v8::Value> exception,
@ -687,7 +591,7 @@ void V8Debugger::v8OOMCallback(void* data) {
} }
void V8Debugger::ScriptCompiled(v8::Local<v8::debug::Script> script, void V8Debugger::ScriptCompiled(v8::Local<v8::debug::Script> script,
bool has_compile_error) { bool is_live_edited, bool has_compile_error) {
int contextId; int contextId;
if (!script->ContextId().To(&contextId)) return; if (!script->ContextId().To(&contextId)) return;
if (script->IsWasm()) { if (script->IsWasm()) {
@ -703,11 +607,11 @@ void V8Debugger::ScriptCompiled(v8::Local<v8::debug::Script> script,
v8::Isolate* isolate = m_isolate; v8::Isolate* isolate = m_isolate;
m_inspector->forEachSession( m_inspector->forEachSession(
m_inspector->contextGroupId(contextId), m_inspector->contextGroupId(contextId),
[&isolate, &script, [&isolate, &script, &has_compile_error,
&has_compile_error](V8InspectorSessionImpl* session) { &is_live_edited](V8InspectorSessionImpl* session) {
if (!session->debuggerAgent()->enabled()) return; if (!session->debuggerAgent()->enabled()) return;
session->debuggerAgent()->didParseSource( session->debuggerAgent()->didParseSource(
V8DebuggerScript::Create(isolate, script, inLiveEditScope), V8DebuggerScript::Create(isolate, script, is_live_edited),
!has_compile_error); !has_compile_error);
}); });
} }

View File

@ -64,11 +64,6 @@ class V8Debugger : public v8::debug::DebugDelegate {
std::unique_ptr<protocol::Debugger::Location>, std::unique_ptr<protocol::Debugger::Location>,
const String16& targetCallFramess); const String16& targetCallFramess);
Response setScriptSource(
const String16& sourceID, v8::Local<v8::String> newSource, bool dryRun,
protocol::Maybe<protocol::Runtime::ExceptionDetails>*,
protocol::Maybe<bool>* stackChanged, bool* compileError);
// Each script inherits debug data from v8::Context where it has been // Each script inherits debug data from v8::Context where it has been
// compiled. // compiled.
// Only scripts whose debug data matches |contextGroupId| will be reported. // Only scripts whose debug data matches |contextGroupId| will be reported.
@ -161,7 +156,7 @@ class V8Debugger : public v8::debug::DebugDelegate {
// v8::debug::DebugEventListener implementation. // v8::debug::DebugEventListener implementation.
void PromiseEventOccurred(v8::debug::PromiseDebugActionType type, int id, void PromiseEventOccurred(v8::debug::PromiseDebugActionType type, int id,
int parentId, bool createdByUser) override; int parentId, bool createdByUser) override;
void ScriptCompiled(v8::Local<v8::debug::Script> script, void ScriptCompiled(v8::Local<v8::debug::Script> script, bool is_live_edited,
bool has_compile_error) override; bool has_compile_error) override;
void BreakProgramRequested(v8::Local<v8::Context> paused_context, void BreakProgramRequested(v8::Local<v8::Context> paused_context,
v8::Local<v8::Object> exec_state, v8::Local<v8::Object> exec_state,

View File

@ -0,0 +1,73 @@
Tests Debugger.setScriptSource
Running test: addLineAfter
var x = 1;
#debugger;
return x + 2;
---
Break location after LiveEdit:
var x = 1;
#debugger;
var x = 3;
stackChanged: true
Protocol.Debugger.stepInto
function foo() {
var x = #1;
debugger;
Running test: addLineBefore
var x = 1;
#debugger;
return x + 2;
---
Break location after LiveEdit:
var x = 1;
var x = #3;
debugger;
stackChanged: true
Protocol.Debugger.stepInto
function foo() {
var x = #1;
var x = 3;
Running test: breakAtFirstLineAddLineAfter
function boo() {
#debugger;
var x = 1;
---
Break location after LiveEdit:
function boo() {
#debugger;
var x = 3;
stackChanged: true
Protocol.Debugger.stepInto
function boo() {
#debugger;
var x = 3;
Running test: breakAtFirstLineAddLineBefore
function boo() {
#debugger;
var x = 1;
---
Break location after LiveEdit:
function boo() {
var x = #3;
debugger;
stackChanged: true
Protocol.Debugger.stepInto
var x = 3;
#debugger;
var x = 1;

View File

@ -0,0 +1,97 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
let {session, contextGroup, Protocol} =
InspectorTest.start('Tests Debugger.setScriptSource');
session.setupScriptMap();
function foo() {
var x = 1;
debugger;
return x + 2;
}
function boo() {
debugger;
var x = 1;
return x + 2;
}
InspectorTest.runAsyncTestSuite([
async function addLineAfter() {
await Protocol.Debugger.enable();
Protocol.Runtime.evaluate({expression: foo.toString()});
let {params:{scriptId}} = await Protocol.Debugger.onceScriptParsed();
Protocol.Runtime.evaluate({
expression: 'setTimeout(foo, 0)//# sourceURL=test.js'});
let {params:{callFrames}} = await Protocol.Debugger.oncePaused();
await session.logSourceLocation(callFrames[0].location);
await replaceInSource(scriptId, 'debugger;', 'debugger;\nvar x = 3;');
Protocol.Debugger.resume();
await Protocol.Debugger.oncePaused();
await Protocol.Debugger.disable();
},
async function addLineBefore() {
await Protocol.Debugger.enable();
Protocol.Runtime.evaluate({expression: foo.toString()});
let {params:{scriptId}} = await Protocol.Debugger.onceScriptParsed();
Protocol.Runtime.evaluate({
expression: 'setTimeout(foo, 0)//# sourceURL=test.js'});
let {params:{callFrames}} = await Protocol.Debugger.oncePaused();
await session.logSourceLocation(callFrames[0].location);
await replaceInSource(scriptId, 'debugger;', 'var x = 3;\ndebugger;');
Protocol.Debugger.resume();
await Protocol.Debugger.oncePaused();
await Protocol.Debugger.disable();
},
async function breakAtFirstLineAddLineAfter() {
await Protocol.Debugger.enable();
Protocol.Runtime.evaluate({expression: boo.toString()});
let {params:{scriptId}} = await Protocol.Debugger.onceScriptParsed();
Protocol.Runtime.evaluate({
expression: 'setTimeout(boo, 0)//# sourceURL=test.js'});
let {params:{callFrames}} = await Protocol.Debugger.oncePaused();
await session.logSourceLocation(callFrames[0].location);
await replaceInSource(scriptId, 'debugger;', 'debugger;\nvar x = 3;');
await Protocol.Debugger.disable();
},
async function breakAtFirstLineAddLineBefore() {
await Protocol.Debugger.enable();
Protocol.Runtime.evaluate({expression: boo.toString()});
let {params:{scriptId}} = await Protocol.Debugger.onceScriptParsed();
Protocol.Runtime.evaluate({
expression: 'setTimeout(boo, 0)//# sourceURL=test.js'});
let {params:{callFrames}} = await Protocol.Debugger.oncePaused();
await session.logSourceLocation(callFrames[0].location);
await replaceInSource(scriptId, 'debugger;', 'var x = 3;\ndebugger;');
await Protocol.Debugger.disable();
}
]);
async function replaceInSource(scriptId, oldString, newString) {
InspectorTest.log('---');
let {result:{scriptSource}} =
await Protocol.Debugger.getScriptSource({scriptId});
let {result} = await Protocol.Debugger.setScriptSource({
scriptId,
scriptSource: scriptSource.replace(oldString, newString)
});
InspectorTest.log('Break location after LiveEdit:');
await session.logSourceLocation(result.callFrames[0].location, true);
InspectorTest.log('stackChanged: ' + result.stackChanged);
if (result.stackChanged) {
InspectorTest.log('Protocol.Debugger.stepInto');
Protocol.Debugger.stepInto();
var {params:{callFrames}} = await Protocol.Debugger.oncePaused();
await session.logSourceLocation(callFrames[0].location);
}
}

View File

@ -213,14 +213,14 @@ InspectorTest.Session = class {
} }
} }
logSourceLocation(location) { logSourceLocation(location, forceSourceRequest) {
var scriptId = location.scriptId; var scriptId = location.scriptId;
if (!this._scriptMap || !this._scriptMap.has(scriptId)) { if (!this._scriptMap || !this._scriptMap.has(scriptId)) {
InspectorTest.log("setupScriptMap should be called before Protocol.Debugger.enable."); InspectorTest.log("setupScriptMap should be called before Protocol.Debugger.enable.");
InspectorTest.completeTest(); InspectorTest.completeTest();
} }
var script = this._scriptMap.get(scriptId); var script = this._scriptMap.get(scriptId);
if (!script.scriptSource) { if (!script.scriptSource || forceSourceRequest) {
return this.Protocol.Debugger.getScriptSource({ scriptId }) return this.Protocol.Debugger.getScriptSource({ scriptId })
.then(message => script.scriptSource = message.result.scriptSource) .then(message => script.scriptSource = message.result.scriptSource)
.then(dumpSourceWithLocation); .then(dumpSourceWithLocation);