[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:
parent
920025f523
commit
6b0bf1659e
@ -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);
|
||||||
|
@ -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,
|
||||||
|
@ -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();
|
||||||
|
if (catch_exceptions) {
|
||||||
MaybeHandle<Object> maybe_exception;
|
MaybeHandle<Object> maybe_exception;
|
||||||
return Execution::TryCall(isolate_, fun, undefined, argc, args,
|
return Execution::TryCall(isolate_, fun, undefined, argc, args,
|
||||||
Execution::MessageHandling::kReport,
|
Execution::MessageHandling::kReport,
|
||||||
&maybe_exception);
|
&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;
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
|
||||||
})
|
})
|
||||||
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
@ -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,
|
||||||
|
@ -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();
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
73
test/inspector/debugger/set-script-source-2-expected.txt
Normal file
73
test/inspector/debugger/set-script-source-2-expected.txt
Normal 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;
|
||||||
|
|
97
test/inspector/debugger/set-script-source-2.js
Normal file
97
test/inspector/debugger/set-script-source-2.js
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user