Commit d81b7aa0 authored by Yang Guo's avatar Yang Guo Committed by Commit Bot

[debug] materialize arguments and receiver for break-at-entry condition.

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

Bug: v8:178
Change-Id: I6f180c5539935cd91cfbbb777d5900a55c681b1f
Reviewed-on: https://chromium-review.googlesource.com/957094
Commit-Queue: Yang Guo <yangguo@chromium.org>
Reviewed-by: 's avatarClemens Hammacher <clemensh@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#51867}
parent e58bc018
......@@ -74,6 +74,48 @@ MaybeHandle<Object> DebugEvaluate::Local(Isolate* isolate,
return maybe_result;
}
MaybeHandle<Object> DebugEvaluate::WithTopmostArguments(Isolate* isolate,
Handle<String> source) {
// Handle the processing of break.
DisableBreak disable_break_scope(isolate->debug());
Factory* factory = isolate->factory();
JavaScriptFrameIterator it(isolate);
// Get context and receiver.
Handle<Context> native_context(
Context::cast(it.frame()->context())->native_context(), isolate);
// Materialize arguments as property on an extension object.
Handle<JSObject> materialized = factory->NewJSObjectWithNullProto();
Handle<String> arguments_str = factory->arguments_string();
JSObject::SetOwnPropertyIgnoreAttributes(
materialized, arguments_str,
Accessors::FunctionGetArguments(it.frame(), 0), NONE)
.Check();
// Materialize receiver.
Handle<String> this_str = factory->this_string();
JSObject::SetOwnPropertyIgnoreAttributes(
materialized, this_str, Handle<Object>(it.frame()->receiver(), isolate),
NONE)
.Check();
// Use extension object in a debug-evaluate scope.
Handle<ScopeInfo> scope_info =
ScopeInfo::CreateForWithScope(isolate, Handle<ScopeInfo>::null());
scope_info->SetIsDebugEvaluateScope();
Handle<Context> evaluation_context =
factory->NewDebugEvaluateContext(native_context, scope_info, materialized,
Handle<Context>(), Handle<StringSet>());
Handle<SharedFunctionInfo> outer_info(native_context->closure()->shared(),
isolate);
Handle<JSObject> receiver(native_context->global_proxy());
const bool throw_on_side_effect = false;
MaybeHandle<Object> maybe_result =
Evaluate(isolate, outer_info, evaluation_context, receiver, source,
throw_on_side_effect);
return maybe_result;
}
// Compile and evaluate source for the given context.
MaybeHandle<Object> DebugEvaluate::Evaluate(
......
......@@ -31,6 +31,12 @@ class DebugEvaluate : public AllStatic {
Handle<String> source,
bool throw_on_side_effect);
// This is used for break-at-entry for builtins and API functions.
// Evaluate a piece of JavaScript in the native context, but with the
// materialized arguments object and receiver of the current call.
static MaybeHandle<Object> WithTopmostArguments(Isolate* isolate,
Handle<String> source);
static bool FunctionHasNoSideEffect(Handle<SharedFunctionInfo> info);
static bool CallbackHasNoSideEffect(Object* callback_info);
......
......@@ -512,9 +512,7 @@ MaybeHandle<FixedArray> Debug::CheckBreakPoints(Handle<DebugInfo> debug_info,
if (has_break_points) *has_break_points = has_break_points_to_check;
if (!has_break_points_to_check) return {};
Handle<Object> break_points =
debug_info->GetBreakPoints(location->position());
return Debug::GetHitBreakPoints(break_points);
return Debug::GetHitBreakPoints(debug_info, location->position());
}
......@@ -569,17 +567,28 @@ MaybeHandle<Object> Debug::CallFunction(const char* name, int argc,
// Check whether a single break point object is triggered.
bool Debug::CheckBreakPoint(Handle<BreakPoint> break_point) {
bool Debug::CheckBreakPoint(Handle<BreakPoint> break_point,
bool is_break_at_entry) {
HandleScope scope(isolate_);
if (!break_point->condition()->length()) return true;
Handle<String> condition(break_point->condition());
MaybeHandle<Object> maybe_result;
Handle<Object> result;
// Since we call CheckBreakpoint only for deoptimized frame on top of stack,
// we can use 0 as index of inlined frame.
if (!DebugEvaluate::Local(isolate_, break_frame_id(),
/* inlined_jsframe_index */ 0, condition, false)
.ToHandle(&result)) {
if (is_break_at_entry) {
maybe_result = DebugEvaluate::WithTopmostArguments(isolate_, condition);
} else {
// Since we call CheckBreakpoint only for deoptimized frame on top of stack,
// we can use 0 as index of inlined frame.
const int inlined_jsframe_index = 0;
const bool throw_on_side_effect = false;
maybe_result =
DebugEvaluate::Local(isolate_, break_frame_id(), inlined_jsframe_index,
condition, throw_on_side_effect);
}
if (!maybe_result.ToHandle(&result)) {
if (isolate_->has_pending_exception()) {
isolate_->clear_pending_exception();
}
......@@ -783,10 +792,16 @@ bool Debug::IsBreakOnException(ExceptionBreakType type) {
}
}
MaybeHandle<FixedArray> Debug::GetHitBreakPoints(Handle<Object> break_points) {
MaybeHandle<FixedArray> Debug::GetHitBreakPoints(Handle<DebugInfo> debug_info,
int position) {
Handle<Object> break_points = debug_info->GetBreakPoints(position);
bool is_break_at_entry = debug_info->BreakAtEntry();
DCHECK(!break_points->IsUndefined(isolate_));
if (!break_points->IsFixedArray()) {
if (!CheckBreakPoint(Handle<BreakPoint>::cast(break_points))) return {};
if (!CheckBreakPoint(Handle<BreakPoint>::cast(break_points),
is_break_at_entry)) {
return {};
}
Handle<FixedArray> break_points_hit = isolate_->factory()->NewFixedArray(1);
break_points_hit->set(0, *break_points);
return break_points_hit;
......@@ -799,7 +814,8 @@ MaybeHandle<FixedArray> Debug::GetHitBreakPoints(Handle<Object> break_points) {
int break_points_hit_count = 0;
for (int i = 0; i < num_objects; ++i) {
Handle<Object> break_point(array->get(i), isolate_);
if (CheckBreakPoint(Handle<BreakPoint>::cast(break_point))) {
if (CheckBreakPoint(Handle<BreakPoint>::cast(break_point),
is_break_at_entry)) {
break_points_hit->set(break_points_hit_count++, *break_point);
}
}
......
......@@ -253,11 +253,11 @@ class Debug {
int* offset, int* id);
void RemoveBreakpoint(int id);
// The parameter is either a BreakPoint object, or a FixedArray of
// BreakPoint objects.
// Returns an empty handle if no breakpoint is hit, or a FixedArray with all
// Find breakpoints from the debug info and the break location and check
// whether they are hit. Return an empty handle if not, or a FixedArray with
// hit BreakPoint objects.
MaybeHandle<FixedArray> GetHitBreakPoints(Handle<Object> break_points);
MaybeHandle<FixedArray> GetHitBreakPoints(Handle<DebugInfo> debug_info,
int position);
// Stepping handling.
void PrepareStep(StepAction step_action);
......@@ -469,7 +469,9 @@ class Debug {
BreakLocation* location,
bool* has_break_points = nullptr);
bool IsMutedAtCurrentLocation(JavaScriptFrame* frame);
bool CheckBreakPoint(Handle<BreakPoint> break_point);
// Check whether a BreakPoint object is hit. Evaluate condition depending
// on whether this is a regular break location or a break at function entry.
bool CheckBreakPoint(Handle<BreakPoint> break_point, bool is_break_at_entry);
MaybeHandle<Object> CallFunction(const char* name, int argc,
Handle<Object> args[],
bool catch_exceptions = true);
......
......@@ -1349,8 +1349,15 @@ MaybeHandle<FixedArray> WasmSharedModuleData::CheckBreakPoints(
Handle<BreakPointInfo>::cast(maybe_breakpoint_info);
if (breakpoint_info->source_position() != position) return {};
// There is no support for conditional break points. Just assume that every
// break point always hits.
Handle<Object> break_points(breakpoint_info->break_points(), isolate);
return isolate->debug()->GetHitBreakPoints(break_points);
if (break_points->IsFixedArray()) {
return Handle<FixedArray>::cast(break_points);
}
Handle<FixedArray> break_points_hit = isolate->factory()->NewFixedArray(1);
break_points_hit->set(0, *break_points);
return break_points_hit;
}
Handle<WasmCompiledModule> WasmCompiledModule::New(
......
......@@ -1530,44 +1530,63 @@ TEST(BreakPointConditionBuiltin) {
CompileRun("'b'.repeat(10)");
CHECK_EQ(1, break_point_hit_count);
// === Test parameter ===
// === Test arguments ===
break_point_hit_count = 0;
builtin = CompileRun("String.prototype.repeat").As<v8::Function>();
CompileRun("function f(x) { x.repeat(10); }");
CompileRun("function f(x) { return 'a'.repeat(x * 2); }");
CHECK_EQ(0, break_point_hit_count);
// Run with breakpoint.
bp = SetBreakPoint(builtin, 0, "x == 'b'");
CompileRun("f('a')");
bp = SetBreakPoint(builtin, 0, "arguments[0] == 20");
ExpectString("f(5)", "aaaaaaaaaa");
CHECK_EQ(0, break_point_hit_count);
CompileRun("f('b')");
ExpectString("f(10)", "aaaaaaaaaaaaaaaaaaaa");
CHECK_EQ(1, break_point_hit_count);
// Run without breakpoints.
ClearBreakPoint(bp);
CompileRun("f('b')");
ExpectString("f(10)", "aaaaaaaaaaaaaaaaaaaa");
CHECK_EQ(1, break_point_hit_count);
// === Test arguments ===
// TODO(178): implement this.
// break_point_hit_count = 0;
// builtin = CompileRun("String.prototype.repeat").As<v8::Function>();
// CompileRun("function f(x) { 'a'.repeat(x * 2, 3); }");
// CHECK_EQ(0, break_point_hit_count);
//
// // Run with breakpoint.
// bp = SetBreakPoint(builtin, 0, "arguments[0] == 20");
// CompileRun("f(5)");
// CHECK_EQ(0, break_point_hit_count);
//
// CompileRun("f(10)");
// CHECK_EQ(1, break_point_hit_count);
//
// // Run without breakpoints.
// ClearBreakPoint(bp);
// CompileRun("f(10)");
// CHECK_EQ(1, break_point_hit_count);
// === Test adapted arguments ===
break_point_hit_count = 0;
builtin = CompileRun("String.prototype.repeat").As<v8::Function>();
CompileRun("function f(x) { return 'a'.repeat(x * 2, x); }");
CHECK_EQ(0, break_point_hit_count);
// Run with breakpoint.
bp = SetBreakPoint(builtin, 0,
"arguments[1] == 10 && arguments[2] == undefined");
ExpectString("f(5)", "aaaaaaaaaa");
CHECK_EQ(0, break_point_hit_count);
ExpectString("f(10)", "aaaaaaaaaaaaaaaaaaaa");
CHECK_EQ(1, break_point_hit_count);
// Run without breakpoints.
ClearBreakPoint(bp);
ExpectString("f(10)", "aaaaaaaaaaaaaaaaaaaa");
CHECK_EQ(1, break_point_hit_count);
// === Test receiver ===
break_point_hit_count = 0;
builtin = CompileRun("String.prototype.repeat").As<v8::Function>();
CompileRun("function f(x) { return x.repeat(10); }");
CHECK_EQ(0, break_point_hit_count);
// Run with breakpoint.
bp = SetBreakPoint(builtin, 0, "this == 'a'");
ExpectString("f('b')", "bbbbbbbbbb");
CHECK_EQ(0, break_point_hit_count);
ExpectString("f('a')", "aaaaaaaaaa");
CHECK_EQ(1, break_point_hit_count);
// Run without breakpoints.
ClearBreakPoint(bp);
ExpectString("f('a')", "aaaaaaaaaa");
CHECK_EQ(1, break_point_hit_count);
SetDebugEventListener(env->GetIsolate(), nullptr);
CheckDebuggerUnloaded();
......
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