Commit 1a989bde authored by yangguo's avatar yangguo Committed by Commit bot

[debugger] expose side-effect free evaluate to inspector.

R=jgruber@chromium.org, kozyatinskiy@chromium.org
BUG=v8:5821

Review-Url: https://codereview.chromium.org/2685483002
Cr-Commit-Position: refs/heads/master@{#43049}
parent fa7d1f8f
...@@ -41,13 +41,14 @@ MaybeHandle<Object> DebugEvaluate::Global(Isolate* isolate, ...@@ -41,13 +41,14 @@ MaybeHandle<Object> DebugEvaluate::Global(Isolate* isolate,
Handle<Context> context = isolate->native_context(); Handle<Context> context = isolate->native_context();
Handle<JSObject> receiver(context->global_proxy()); Handle<JSObject> receiver(context->global_proxy());
Handle<SharedFunctionInfo> outer_info(context->closure()->shared(), isolate); Handle<SharedFunctionInfo> outer_info(context->closure()->shared(), isolate);
return Evaluate(isolate, outer_info, context, receiver, source); return Evaluate(isolate, outer_info, context, receiver, source, false);
} }
MaybeHandle<Object> DebugEvaluate::Local(Isolate* isolate, MaybeHandle<Object> DebugEvaluate::Local(Isolate* isolate,
StackFrame::Id frame_id, StackFrame::Id frame_id,
int inlined_jsframe_index, int inlined_jsframe_index,
Handle<String> source) { Handle<String> source,
bool throw_on_side_effect) {
// Handle the processing of break. // Handle the processing of break.
DisableBreak disable_break_scope(isolate->debug()); DisableBreak disable_break_scope(isolate->debug());
...@@ -74,8 +75,9 @@ MaybeHandle<Object> DebugEvaluate::Local(Isolate* isolate, ...@@ -74,8 +75,9 @@ MaybeHandle<Object> DebugEvaluate::Local(Isolate* isolate,
Handle<Context> context = context_builder.evaluation_context(); Handle<Context> context = context_builder.evaluation_context();
Handle<JSObject> receiver(context->global_proxy()); Handle<JSObject> receiver(context->global_proxy());
MaybeHandle<Object> maybe_result = Evaluate( MaybeHandle<Object> maybe_result =
isolate, context_builder.outer_info(), context, receiver, source); Evaluate(isolate, context_builder.outer_info(), context, receiver, source,
throw_on_side_effect);
if (!maybe_result.is_null()) context_builder.UpdateValues(); if (!maybe_result.is_null()) context_builder.UpdateValues();
return maybe_result; return maybe_result;
} }
...@@ -84,7 +86,8 @@ MaybeHandle<Object> DebugEvaluate::Local(Isolate* isolate, ...@@ -84,7 +86,8 @@ MaybeHandle<Object> DebugEvaluate::Local(Isolate* isolate,
// Compile and evaluate source for the given context. // Compile and evaluate source for the given context.
MaybeHandle<Object> DebugEvaluate::Evaluate( MaybeHandle<Object> DebugEvaluate::Evaluate(
Isolate* isolate, Handle<SharedFunctionInfo> outer_info, Isolate* isolate, Handle<SharedFunctionInfo> outer_info,
Handle<Context> context, Handle<Object> receiver, Handle<String> source) { Handle<Context> context, Handle<Object> receiver, Handle<String> source,
bool throw_on_side_effect) {
Handle<JSFunction> eval_fun; Handle<JSFunction> eval_fun;
ASSIGN_RETURN_ON_EXCEPTION( ASSIGN_RETURN_ON_EXCEPTION(
isolate, eval_fun, isolate, eval_fun,
...@@ -95,8 +98,7 @@ MaybeHandle<Object> DebugEvaluate::Evaluate( ...@@ -95,8 +98,7 @@ MaybeHandle<Object> DebugEvaluate::Evaluate(
Handle<Object> result; Handle<Object> result;
{ {
NoSideEffectScope no_side_effect(isolate, NoSideEffectScope no_side_effect(isolate, throw_on_side_effect);
FLAG_side_effect_free_debug_evaluate);
ASSIGN_RETURN_ON_EXCEPTION( ASSIGN_RETURN_ON_EXCEPTION(
isolate, result, Execution::Call(isolate, eval_fun, receiver, 0, NULL), isolate, result, Execution::Call(isolate, eval_fun, receiver, 0, NULL),
Object); Object);
......
...@@ -22,7 +22,8 @@ class DebugEvaluate : public AllStatic { ...@@ -22,7 +22,8 @@ class DebugEvaluate : public AllStatic {
// - The arguments object needs to materialized. // - The arguments object needs to materialized.
static MaybeHandle<Object> Local(Isolate* isolate, StackFrame::Id frame_id, static MaybeHandle<Object> Local(Isolate* isolate, StackFrame::Id frame_id,
int inlined_jsframe_index, int inlined_jsframe_index,
Handle<String> source); Handle<String> source,
bool throw_on_side_effect);
static bool FunctionHasNoSideEffect(Handle<SharedFunctionInfo> info); static bool FunctionHasNoSideEffect(Handle<SharedFunctionInfo> info);
static bool CallbackHasNoSideEffect(Address function_addr); static bool CallbackHasNoSideEffect(Address function_addr);
...@@ -86,7 +87,8 @@ class DebugEvaluate : public AllStatic { ...@@ -86,7 +87,8 @@ class DebugEvaluate : public AllStatic {
Handle<SharedFunctionInfo> outer_info, Handle<SharedFunctionInfo> outer_info,
Handle<Context> context, Handle<Context> context,
Handle<Object> receiver, Handle<Object> receiver,
Handle<String> source); Handle<String> source,
bool throw_on_side_effect);
}; };
......
...@@ -1919,11 +1919,12 @@ FrameMirror.prototype.allScopes = function(opt_ignore_nested_scopes) { ...@@ -1919,11 +1919,12 @@ FrameMirror.prototype.allScopes = function(opt_ignore_nested_scopes) {
}; };
FrameMirror.prototype.evaluate = function(source) { FrameMirror.prototype.evaluate = function(source, throw_on_side_effect = false) {
return MakeMirror(%DebugEvaluate(this.break_id_, return MakeMirror(%DebugEvaluate(this.break_id_,
this.details_.frameId(), this.details_.frameId(),
this.details_.inlinedFrameIndex(), this.details_.inlinedFrameIndex(),
source)); source,
throw_on_side_effect));
}; };
......
...@@ -718,8 +718,6 @@ DEFINE_IMPLICATION(trace_array_abuse, trace_external_array_abuse) ...@@ -718,8 +718,6 @@ DEFINE_IMPLICATION(trace_array_abuse, trace_external_array_abuse)
// debugger // debugger
DEFINE_BOOL(trace_debug_json, false, "trace debugging JSON request/response") DEFINE_BOOL(trace_debug_json, false, "trace debugging JSON request/response")
DEFINE_BOOL(enable_liveedit, true, "enable liveedit experimental feature") DEFINE_BOOL(enable_liveedit, true, "enable liveedit experimental feature")
DEFINE_BOOL(side_effect_free_debug_evaluate, false,
"use side-effect-free debug-evaluate for testing")
DEFINE_BOOL( DEFINE_BOOL(
trace_side_effect_free_debug_evaluate, false, trace_side_effect_free_debug_evaluate, false,
"print debug messages for side-effect-free debug-evaluate for testing") "print debug messages for side-effect-free debug-evaluate for testing")
......
...@@ -420,11 +420,12 @@ DebuggerScript._frameMirrorToJSCallFrame = function(frameMirror) ...@@ -420,11 +420,12 @@ DebuggerScript._frameMirrorToJSCallFrame = function(frameMirror)
/** /**
* @param {string} expression * @param {string} expression
* @param {boolean} throwOnSideEffect
* @return {*} * @return {*}
*/ */
function evaluate(expression) function evaluate(expression, throwOnSideEffect)
{ {
return frameMirror.evaluate(expression).value(); return frameMirror.evaluate(expression, throwOnSideEffect).value();
} }
/** @return {undefined} */ /** @return {undefined} */
......
...@@ -31,7 +31,7 @@ var JavaScriptCallFrameDetails; ...@@ -31,7 +31,7 @@ var JavaScriptCallFrameDetails;
/** @typedef {{ /** @typedef {{
contextId: function():number, contextId: function():number,
thisObject: !Object, thisObject: !Object,
evaluate: function(string):*, evaluate: function(string, boolean):*,
restart: function():undefined, restart: function():undefined,
setVariableValue: function(number, string, *):undefined, setVariableValue: function(number, string, *):undefined,
isAtReturn: boolean, isAtReturn: boolean,
...@@ -351,8 +351,9 @@ FrameMirror.prototype.script = function() {} ...@@ -351,8 +351,9 @@ FrameMirror.prototype.script = function() {}
/** /**
* @param {string} source * @param {string} source
* @param {boolean} throwOnSideEffect
*/ */
FrameMirror.prototype.evaluate = function(source) {} FrameMirror.prototype.evaluate = function(source, throwOnSideEffect) {}
FrameMirror.prototype.restart = function() {} FrameMirror.prototype.restart = function() {}
......
...@@ -98,7 +98,7 @@ v8::MaybeLocal<v8::Object> JavaScriptCallFrame::details() const { ...@@ -98,7 +98,7 @@ v8::MaybeLocal<v8::Object> JavaScriptCallFrame::details() const {
} }
v8::MaybeLocal<v8::Value> JavaScriptCallFrame::evaluate( v8::MaybeLocal<v8::Value> JavaScriptCallFrame::evaluate(
v8::Local<v8::Value> expression) { v8::Local<v8::Value> expression, bool throwOnSideEffect) {
v8::MicrotasksScope microtasks(m_isolate, v8::MicrotasksScope microtasks(m_isolate,
v8::MicrotasksScope::kRunMicrotasks); v8::MicrotasksScope::kRunMicrotasks);
v8::Local<v8::Context> context = v8::Local<v8::Context> context =
...@@ -108,7 +108,9 @@ v8::MaybeLocal<v8::Value> JavaScriptCallFrame::evaluate( ...@@ -108,7 +108,9 @@ v8::MaybeLocal<v8::Value> JavaScriptCallFrame::evaluate(
v8::Local<v8::Function> evalFunction = v8::Local<v8::Function>::Cast( v8::Local<v8::Function> evalFunction = v8::Local<v8::Function>::Cast(
callFrame->Get(context, toV8StringInternalized(m_isolate, "evaluate")) callFrame->Get(context, toV8StringInternalized(m_isolate, "evaluate"))
.ToLocalChecked()); .ToLocalChecked());
return evalFunction->Call(context, callFrame, 1, &expression); v8::Local<v8::Value> argv[] = {
expression, v8::Boolean::New(m_isolate, throwOnSideEffect)};
return evalFunction->Call(context, callFrame, arraysize(argv), argv);
} }
v8::MaybeLocal<v8::Value> JavaScriptCallFrame::restart() { v8::MaybeLocal<v8::Value> JavaScriptCallFrame::restart() {
......
...@@ -54,7 +54,8 @@ class JavaScriptCallFrame { ...@@ -54,7 +54,8 @@ class JavaScriptCallFrame {
bool isAtReturn() const; bool isAtReturn() const;
v8::MaybeLocal<v8::Object> details() const; v8::MaybeLocal<v8::Object> details() const;
v8::MaybeLocal<v8::Value> evaluate(v8::Local<v8::Value> expression); v8::MaybeLocal<v8::Value> evaluate(v8::Local<v8::Value> expression,
bool throwOnSideEffect);
v8::MaybeLocal<v8::Value> restart(); v8::MaybeLocal<v8::Value> restart();
v8::MaybeLocal<v8::Value> setVariableValue(int scopeNumber, v8::MaybeLocal<v8::Value> setVariableValue(int scopeNumber,
v8::Local<v8::Value> variableName, v8::Local<v8::Value> variableName,
......
...@@ -635,7 +635,8 @@ ...@@ -635,7 +635,8 @@
{ "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Specifies whether command line API should be available to the evaluated expression, defaults to false." }, { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Specifies whether command line API should be available to the evaluated expression, defaults to false." },
{ "name": "silent", "type": "boolean", "optional": true, "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause execution. Overrides <code>setPauseOnException</code> state." }, { "name": "silent", "type": "boolean", "optional": true, "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause execution. Overrides <code>setPauseOnException</code> state." },
{ "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }, { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." },
{ "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the result." } { "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the result." },
{ "name": "throwOnSideEffect", "type": "boolean", "optional": true, "experimental": true, "description": "Whether to throw an exception if side effect cannot be ruled out during evaluation." }
], ],
"returns": [ "returns": [
{ "name": "result", "$ref": "Runtime.RemoteObject", "description": "Object wrapper for the evaluation result." }, { "name": "result", "$ref": "Runtime.RemoteObject", "description": "Object wrapper for the evaluation result." },
......
...@@ -703,7 +703,7 @@ Response V8DebuggerAgentImpl::evaluateOnCallFrame( ...@@ -703,7 +703,7 @@ Response V8DebuggerAgentImpl::evaluateOnCallFrame(
const String16& callFrameId, const String16& expression, const String16& callFrameId, const String16& expression,
Maybe<String16> objectGroup, Maybe<bool> includeCommandLineAPI, Maybe<String16> objectGroup, Maybe<bool> includeCommandLineAPI,
Maybe<bool> silent, Maybe<bool> returnByValue, Maybe<bool> generatePreview, Maybe<bool> silent, Maybe<bool> returnByValue, Maybe<bool> generatePreview,
std::unique_ptr<RemoteObject>* result, Maybe<bool> throwOnSideEffect, std::unique_ptr<RemoteObject>* result,
Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) { Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
if (!isPaused()) return Response::Error(kDebuggerNotPaused); if (!isPaused()) return Response::Error(kDebuggerNotPaused);
InjectedScript::CallFrameScope scope(m_inspector, m_session->contextGroupId(), InjectedScript::CallFrameScope scope(m_inspector, m_session->contextGroupId(),
...@@ -718,7 +718,8 @@ Response V8DebuggerAgentImpl::evaluateOnCallFrame( ...@@ -718,7 +718,8 @@ Response V8DebuggerAgentImpl::evaluateOnCallFrame(
v8::MaybeLocal<v8::Value> maybeResultValue = v8::MaybeLocal<v8::Value> maybeResultValue =
m_pausedCallFrames[scope.frameOrdinal()]->evaluate( m_pausedCallFrames[scope.frameOrdinal()]->evaluate(
toV8String(m_isolate, expression)); toV8String(m_isolate, expression),
throwOnSideEffect.fromMaybe(false));
// Re-initialize after running client's code, as it could have destroyed // Re-initialize after running client's code, as it could have destroyed
// context or session. // context or session.
......
...@@ -93,7 +93,7 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend { ...@@ -93,7 +93,7 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
const String16& callFrameId, const String16& expression, const String16& callFrameId, const String16& expression,
Maybe<String16> objectGroup, Maybe<bool> includeCommandLineAPI, Maybe<String16> objectGroup, Maybe<bool> includeCommandLineAPI,
Maybe<bool> silent, Maybe<bool> returnByValue, Maybe<bool> silent, Maybe<bool> returnByValue,
Maybe<bool> generatePreview, Maybe<bool> generatePreview, Maybe<bool> throwOnSideEffect,
std::unique_ptr<protocol::Runtime::RemoteObject>* result, std::unique_ptr<protocol::Runtime::RemoteObject>* result,
Maybe<protocol::Runtime::ExceptionDetails>*) override; Maybe<protocol::Runtime::ExceptionDetails>*) override;
Response setVariableValue( Response setVariableValue(
......
...@@ -1204,19 +1204,20 @@ RUNTIME_FUNCTION(Runtime_DebugEvaluate) { ...@@ -1204,19 +1204,20 @@ RUNTIME_FUNCTION(Runtime_DebugEvaluate) {
// Check the execution state and decode arguments frame and source to be // Check the execution state and decode arguments frame and source to be
// evaluated. // evaluated.
DCHECK_EQ(4, args.length()); DCHECK_EQ(5, args.length());
CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]); CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
CHECK(isolate->debug()->CheckExecutionState(break_id)); CHECK(isolate->debug()->CheckExecutionState(break_id));
CONVERT_SMI_ARG_CHECKED(wrapped_id, 1); CONVERT_SMI_ARG_CHECKED(wrapped_id, 1);
CONVERT_NUMBER_CHECKED(int, inlined_jsframe_index, Int32, args[2]); CONVERT_NUMBER_CHECKED(int, inlined_jsframe_index, Int32, args[2]);
CONVERT_ARG_HANDLE_CHECKED(String, source, 3); CONVERT_ARG_HANDLE_CHECKED(String, source, 3);
CONVERT_BOOLEAN_ARG_CHECKED(throw_on_side_effect, 4);
StackFrame::Id id = DebugFrameHelper::UnwrapFrameId(wrapped_id); StackFrame::Id id = DebugFrameHelper::UnwrapFrameId(wrapped_id);
RETURN_RESULT_OR_FAILURE( RETURN_RESULT_OR_FAILURE(
isolate, isolate, DebugEvaluate::Local(isolate, id, inlined_jsframe_index, source,
DebugEvaluate::Local(isolate, id, inlined_jsframe_index, source)); throw_on_side_effect));
} }
......
...@@ -172,7 +172,7 @@ namespace internal { ...@@ -172,7 +172,7 @@ namespace internal {
F(IsBreakOnException, 1, 1) \ F(IsBreakOnException, 1, 1) \
F(PrepareStep, 2, 1) \ F(PrepareStep, 2, 1) \
F(ClearStepping, 0, 1) \ F(ClearStepping, 0, 1) \
F(DebugEvaluate, 4, 1) \ F(DebugEvaluate, 5, 1) \
F(DebugEvaluateGlobal, 2, 1) \ F(DebugEvaluateGlobal, 2, 1) \
F(DebugGetLoadedScripts, 0, 1) \ F(DebugGetLoadedScripts, 0, 1) \
F(DebugReferencedBy, 3, 1) \ F(DebugReferencedBy, 3, 1) \
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// Flags: --ignition --side-effect-free-debug-evaluate // Flags: --ignition
Debug = debug.Debug Debug = debug.Debug
...@@ -12,10 +12,12 @@ function listener(event, exec_state, event_data, data) { ...@@ -12,10 +12,12 @@ function listener(event, exec_state, event_data, data) {
if (event != Debug.DebugEvent.Break) return; if (event != Debug.DebugEvent.Break) return;
try { try {
function success(expectation, source) { function success(expectation, source) {
assertEquals(expectation, exec_state.frame(0).evaluate(source).value()); assertEquals(expectation,
exec_state.frame(0).evaluate(source, true).value());
} }
function fail(source) { function fail(source) {
assertThrows(() => exec_state.frame(0).evaluate(source), EvalError); assertThrows(() => exec_state.frame(0).evaluate(source, true),
EvalError);
} }
// Test Math functions. // Test Math functions.
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// Flags: --ignition --side-effect-free-debug-evaluate // Flags: --ignition
Debug = debug.Debug Debug = debug.Debug
...@@ -24,10 +24,12 @@ function listener(event, exec_state, event_data, data) { ...@@ -24,10 +24,12 @@ function listener(event, exec_state, event_data, data) {
if (event != Debug.DebugEvent.Break) return; if (event != Debug.DebugEvent.Break) return;
try { try {
function success(expectation, source) { function success(expectation, source) {
assertEquals(expectation, exec_state.frame(0).evaluate(source).value()); assertEquals(expectation,
exec_state.frame(0).evaluate(source, true).value());
} }
function fail(source) { function fail(source) {
assertThrows(() => exec_state.frame(0).evaluate(source), EvalError); assertThrows(() => exec_state.frame(0).evaluate(source, true),
EvalError);
} }
// Simple test. // Simple test.
success(3, "1 + 2"); success(3, "1 + 2");
......
...@@ -677,12 +677,13 @@ class DebugWrapper { ...@@ -677,12 +677,13 @@ class DebugWrapper {
}; };
} }
evaluateOnCallFrame(frame, expr) { evaluateOnCallFrame(frame, expr, throw_on_side_effect = false) {
const frameid = frame.callFrameId; const frameid = frame.callFrameId;
const {msgid, msg} = this.createMessage( const {msgid, msg} = this.createMessage(
"Debugger.evaluateOnCallFrame", "Debugger.evaluateOnCallFrame",
{ callFrameId : frameid, { callFrameId : frameid,
expression : expr expression : expr,
throwOnSideEffect : throw_on_side_effect,
}); });
this.sendMessage(msg); this.sendMessage(msg);
const reply = this.takeReplyChecked(msgid); const reply = this.takeReplyChecked(msgid);
...@@ -727,7 +728,8 @@ class DebugWrapper { ...@@ -727,7 +728,8 @@ class DebugWrapper {
sourceLine : () => line + 1, sourceLine : () => line + 1,
sourceLineText : () => loc.sourceText, sourceLineText : () => loc.sourceText,
sourcePosition : () => loc.position, sourcePosition : () => loc.position,
evaluate : (expr) => this.evaluateOnCallFrame(frame, expr), evaluate : (expr, throw_on_side_effect) =>
this.evaluateOnCallFrame(frame, expr, throw_on_side_effect),
functionName : () => frame.functionName, functionName : () => frame.functionName,
func : () => func, func : () => func,
index : () => index, index : () => index,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment