}
+bool FunctionLiteral::AllowsLazyCompilationWithoutContext() {
+ return scope()->AllowsLazyCompilationWithoutContext();
+}
+
+
int FunctionLiteral::start_position() const {
return scope()->start_position();
}
int parameter_count() { return parameter_count_; }
bool AllowsLazyCompilation();
+ bool AllowsLazyCompilationWithoutContext();
Handle<String> debug_name() const {
if (name_->length() > 0) return name_;
FLAG_crankshaft &&
!function()->flags()->Contains(kDontSelfOptimize) &&
!function()->flags()->Contains(kDontOptimize) &&
- function()->scope()->AllowsLazyRecompilation() &&
+ function()->scope()->AllowsLazyCompilation() &&
(shared_info().is_null() || !shared_info()->optimization_disabled());
}
// all. However crankshaft support recompilation of functions, so in this case
// the full compiler need not be be used if a debugger is attached, but only if
// break points has actually been set.
-static bool is_debugging_active() {
+static bool IsDebuggerActive(Isolate* isolate) {
#ifdef ENABLE_DEBUGGER_SUPPORT
- Isolate* isolate = Isolate::Current();
return V8::UseCrankshaft() ?
isolate->debug()->has_break_points() :
isolate->debugger()->IsDebuggerActive();
}
-static bool AlwaysFullCompiler() {
- return FLAG_always_full_compiler || is_debugging_active();
+static bool AlwaysFullCompiler(Isolate* isolate) {
+ return FLAG_always_full_compiler || IsDebuggerActive(isolate);
}
// Fall back to using the full code generator if it's not possible
// to use the Hydrogen-based optimizing compiler. We already have
// generated code for this from the shared function object.
- if (AlwaysFullCompiler()) {
+ if (AlwaysFullCompiler(info->isolate())) {
info->SetCode(code);
return true;
}
// builtins cannot be handled lazily by the parser, since we have to know
// if a function uses the special natives syntax, which is something the
// parser records.
+ // If the debugger requests compilation for break points, we cannot be
+ // aggressive about lazy compilation, because it might trigger compilation
+ // of functions without an outer context when setting a breakpoint through
+ // Runtime::FindSharedFunctionInfoInScript.
+ bool allow_lazy_without_ctx = literal->AllowsLazyCompilationWithoutContext();
bool allow_lazy = literal->AllowsLazyCompilation() &&
- !LiveEditFunctionTracker::IsActive(info.isolate());
+ !LiveEditFunctionTracker::IsActive(info.isolate()) &&
+ (!info.isolate()->DebuggerHasBreakPoints() || allow_lazy_without_ctx);
Handle<ScopeInfo> scope_info(ScopeInfo::Empty());
SetFunctionInfo(result, literal, false, script);
RecordFunctionCompilation(Logger::FUNCTION_TAG, &info, result);
result->set_allows_lazy_compilation(allow_lazy);
+ result->set_allows_lazy_compilation_without_context(allow_lazy_without_ctx);
// Set the expected number of properties for instances and return
// the resulting function.
lit->has_only_simple_this_property_assignments(),
*lit->this_property_assignments());
function_info->set_allows_lazy_compilation(lit->AllowsLazyCompilation());
+ function_info->set_allows_lazy_compilation_without_context(
+ lit->AllowsLazyCompilationWithoutContext());
function_info->set_language_mode(lit->language_mode());
function_info->set_uses_arguments(lit->scope()->arguments() != NULL);
function_info->set_has_duplicate_parameters(lit->has_duplicate_parameters());
}
-void Debug::SetBreakPoint(Handle<SharedFunctionInfo> shared,
+void Debug::SetBreakPoint(Handle<JSFunction> function,
Handle<Object> break_point_object,
int* source_position) {
HandleScope scope(isolate_);
PrepareForBreakPoints();
- if (!EnsureDebugInfo(shared)) {
+ // Make sure the function is compiled and has set up the debug info.
+ Handle<SharedFunctionInfo> shared(function->shared());
+ if (!EnsureDebugInfo(shared, function)) {
// Return if retrieving debug info failed.
return;
}
}
+bool Debug::SetBreakPointForScript(Handle<Script> script,
+ Handle<Object> break_point_object,
+ int* source_position) {
+ HandleScope scope(isolate_);
+
+ // No need to call PrepareForBreakPoints because it will be called
+ // implicitly by Runtime::FindSharedFunctionInfoInScript.
+ Object* result = Runtime::FindSharedFunctionInfoInScript(isolate_,
+ script,
+ *source_position);
+ if (result->IsUndefined()) return false;
+
+ // Make sure the function has set up the debug info.
+ Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
+ if (!EnsureDebugInfo(shared, Handle<JSFunction>::null())) {
+ // Return if retrieving debug info failed.
+ return false;
+ }
+
+ // Find position within function. The script position might be before the
+ // source position of the first function.
+ int position;
+ if (shared->start_position() > *source_position) {
+ position = 0;
+ } else {
+ position = *source_position - shared->start_position();
+ }
+
+ Handle<DebugInfo> debug_info = GetDebugInfo(shared);
+ // Source positions starts with zero.
+ ASSERT(position >= 0);
+
+ // Find the break point and change it.
+ BreakLocationIterator it(debug_info, SOURCE_BREAK_LOCATIONS);
+ it.FindBreakLocationFromPosition(position);
+ it.SetBreakPoint(break_point_object);
+
+ *source_position = it.position() + shared->start_position();
+
+ // At least one active break point now.
+ ASSERT(debug_info->GetBreakPointCount() > 0);
+ return true;
+}
+
+
void Debug::ClearBreakPoint(Handle<Object> break_point_object) {
HandleScope scope(isolate_);
}
-void Debug::FloodWithOneShot(Handle<SharedFunctionInfo> shared) {
+void Debug::FloodWithOneShot(Handle<JSFunction> function) {
PrepareForBreakPoints();
- // Make sure the function has set up the debug info.
- if (!EnsureDebugInfo(shared)) {
+
+ // Make sure the function is compiled and has set up the debug info.
+ Handle<SharedFunctionInfo> shared(function->shared());
+ if (!EnsureDebugInfo(shared, function)) {
// Return if we failed to retrieve the debug info.
return;
}
if (!bindee.is_null() && bindee->IsJSFunction() &&
!JSFunction::cast(*bindee)->IsBuiltin()) {
- Handle<SharedFunctionInfo> shared_info(JSFunction::cast(*bindee)->shared());
- Debug::FloodWithOneShot(shared_info);
+ Handle<JSFunction> bindee_function(JSFunction::cast(*bindee));
+ Debug::FloodWithOneShot(bindee_function);
}
}
for (JavaScriptFrameIterator it(isolate_, id); !it.done(); it.Advance()) {
JavaScriptFrame* frame = it.frame();
if (frame->HasHandler()) {
- Handle<SharedFunctionInfo> shared =
- Handle<SharedFunctionInfo>(
- JSFunction::cast(frame->function())->shared());
// Flood the function with the catch block with break points
- FloodWithOneShot(shared);
+ JSFunction* function = JSFunction::cast(frame->function());
+ FloodWithOneShot(Handle<JSFunction>(function));
return;
}
}
frames_it.Advance();
// Fill the function to return to with one-shot break points.
JSFunction* function = JSFunction::cast(frames_it.frame()->function());
- FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared()));
+ FloodWithOneShot(Handle<JSFunction>(function));
return;
}
// Get the debug info (create it if it does not exist).
- Handle<SharedFunctionInfo> shared =
- Handle<SharedFunctionInfo>(JSFunction::cast(frame->function())->shared());
- if (!EnsureDebugInfo(shared)) {
+ Handle<JSFunction> function(JSFunction::cast(frame->function()));
+ Handle<SharedFunctionInfo> shared(function->shared());
+ if (!EnsureDebugInfo(shared, function)) {
// Return if ensuring debug info failed.
return;
}
if (!frames_it.done()) {
// Fill the function to return to with one-shot break points.
JSFunction* function = JSFunction::cast(frames_it.frame()->function());
- FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared()));
+ FloodWithOneShot(Handle<JSFunction>(function));
// Set target frame pointer.
ActivateStepOut(frames_it.frame());
}
// Step next or step min.
// Fill the current function with one-shot break points.
- FloodWithOneShot(shared);
+ FloodWithOneShot(function);
// Remember source position and frame to handle step next.
thread_local_.last_statement_position_ =
if (is_at_restarted_function) {
Handle<JSFunction> restarted_function(
JSFunction::cast(*thread_local_.restarter_frame_function_pointer_));
- Handle<SharedFunctionInfo> restarted_shared(
- restarted_function->shared());
- FloodWithOneShot(restarted_shared);
+ FloodWithOneShot(restarted_function);
} else if (!call_function_stub.is_null()) {
// If it's CallFunction stub ensure target function is compiled and flood
// it with one shot breakpoints.
} else if (!js_function->IsBuiltin()) {
// Don't step into builtins.
// It will also compile target function if it's not compiled yet.
- FloodWithOneShot(Handle<SharedFunctionInfo>(js_function->shared()));
+ FloodWithOneShot(js_function);
}
}
}
// a call target as the function called might be a native function for
// which step in will not stop. It also prepares for stepping in
// getters/setters.
- FloodWithOneShot(shared);
+ FloodWithOneShot(function);
if (is_load_or_store) {
// Remember source position and frame to handle step in getter/setter. If
// function.
if (!holder.is_null() && holder->IsJSFunction() &&
!JSFunction::cast(*holder)->IsBuiltin()) {
- Handle<SharedFunctionInfo> shared_info(
- JSFunction::cast(*holder)->shared());
- Debug::FloodWithOneShot(shared_info);
+ Handle<JSFunction> js_function = Handle<JSFunction>::cast(holder);
+ Debug::FloodWithOneShot(js_function);
}
} else {
- Debug::FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared()));
+ Debug::FloodWithOneShot(function);
}
}
}
Handle<Code> lazy_compile =
Handle<Code>(isolate_->builtins()->builtin(Builtins::kLazyCompile));
+ // There will be at least one break point when we are done.
+ has_break_points_ = true;
+
// Keep the list of activated functions in a handlified list as it
// is used both in GC and non-GC code.
List<Handle<JSFunction> > active_functions(100);
// Ensures the debug information is present for shared.
-bool Debug::EnsureDebugInfo(Handle<SharedFunctionInfo> shared) {
+bool Debug::EnsureDebugInfo(Handle<SharedFunctionInfo> shared,
+ Handle<JSFunction> function) {
// Return if we already have the debug info for shared.
if (HasDebugInfo(shared)) {
ASSERT(shared->is_compiled());
return true;
}
- // Ensure shared in compiled. Return false if this failed.
- if (!SharedFunctionInfo::EnsureCompiled(shared, CLEAR_EXCEPTION)) {
+ // There will be at least one break point when we are done.
+ has_break_points_ = true;
+
+ // Ensure function is compiled. Return false if this failed.
+ if (!function.is_null() &&
+ !JSFunction::EnsureCompiled(function, CLEAR_EXCEPTION)) {
return false;
}
node->set_next(debug_info_list_);
debug_info_list_ = node;
- // Now there is at least one break point.
- has_break_points_ = true;
-
return true;
}
PrepareForBreakPoints();
// Get the executing function in which the debug break occurred.
- Handle<SharedFunctionInfo> shared =
- Handle<SharedFunctionInfo>(JSFunction::cast(frame->function())->shared());
- if (!EnsureDebugInfo(shared)) {
+ Handle<JSFunction> function(JSFunction::cast(frame->function()));
+ Handle<SharedFunctionInfo> shared(function->shared());
+ if (!EnsureDebugInfo(shared, function)) {
// Return if we failed to retrieve the debug info.
return;
}
PrepareForBreakPoints();
// Get the executing function in which the debug break occurred.
- Handle<SharedFunctionInfo> shared =
- Handle<SharedFunctionInfo>(JSFunction::cast(frame->function())->shared());
- if (!EnsureDebugInfo(shared)) {
+ Handle<JSFunction> function(JSFunction::cast(frame->function()));
+ Handle<SharedFunctionInfo> shared(function->shared());
+ if (!EnsureDebugInfo(shared, function)) {
// Return if we failed to retrieve the debug info.
return false;
}
int count,
int end));
Object* Break(Arguments args);
- void SetBreakPoint(Handle<SharedFunctionInfo> shared,
+ void SetBreakPoint(Handle<JSFunction> function,
Handle<Object> break_point_object,
int* source_position);
+ bool SetBreakPointForScript(Handle<Script> script,
+ Handle<Object> break_point_object,
+ int* source_position);
void ClearBreakPoint(Handle<Object> break_point_object);
void ClearAllBreakPoints();
- void FloodWithOneShot(Handle<SharedFunctionInfo> shared);
+ void FloodWithOneShot(Handle<JSFunction> function);
void FloodBoundFunctionWithOneShot(Handle<JSFunction> function);
void FloodHandlerWithOneShot();
void ChangeBreakOnException(ExceptionBreakType type, bool enable);
void PrepareForBreakPoints();
- // Returns whether the operation succeeded.
- bool EnsureDebugInfo(Handle<SharedFunctionInfo> shared);
+ // Returns whether the operation succeeded. Compilation can only be triggered
+ // if a valid closure is passed as the second argument, otherwise the shared
+ // function needs to be compiled already.
+ bool EnsureDebugInfo(Handle<SharedFunctionInfo> shared,
+ Handle<JSFunction> function);
// Returns true if the current stub call is patched to call the debugger.
static bool IsDebugBreak(Address addr);
Handle<Code> code = CodeGenerator::MakeCodeEpilogue(&masm, flags, info);
code->set_optimizable(info->IsOptimizable() &&
!info->function()->flags()->Contains(kDontOptimize) &&
- info->function()->scope()->AllowsLazyRecompilation());
+ info->function()->scope()->AllowsLazyCompilation());
cgen.PopulateDeoptimizationData(code);
cgen.PopulateTypeFeedbackInfo(code);
cgen.PopulateTypeFeedbackCells(code);
kAllowLazyCompilation)
BOOL_ACCESSORS(SharedFunctionInfo,
compiler_hints,
+ allows_lazy_compilation_without_context,
+ kAllowLazyCompilationWithoutContext)
+BOOL_ACCESSORS(SharedFunctionInfo,
+ compiler_hints,
uses_arguments,
kUsesArguments)
BOOL_ACCESSORS(SharedFunctionInfo,
}
-bool SharedFunctionInfo::EnsureCompiled(Handle<SharedFunctionInfo> shared,
- ClearExceptionFlag flag) {
- return shared->is_compiled() || CompileLazy(shared, flag);
-}
-
-
static bool CompileLazyHelper(CompilationInfo* info,
ClearExceptionFlag flag) {
// Compile the source information to a code object.
bool SharedFunctionInfo::CompileLazy(Handle<SharedFunctionInfo> shared,
ClearExceptionFlag flag) {
+ ASSERT(shared->allows_lazy_compilation_without_context());
CompilationInfo info(shared);
return CompileLazyHelper(&info, flag);
}
function->ReplaceCode(function->shared()->code());
function->shared()->set_code_age(0);
} else {
+ ASSERT(function->shared()->allows_lazy_compilation());
CompilationInfo info(function);
result = CompileLazyHelper(&info, flag);
ASSERT(!result || function->is_compiled());
}
+bool JSFunction::EnsureCompiled(Handle<JSFunction> function,
+ ClearExceptionFlag flag) {
+ return function->is_compiled() || CompileLazy(function, flag);
+}
+
+
bool JSFunction::IsInlineable() {
if (IsBuiltin()) return false;
SharedFunctionInfo* shared_info = shared();
// when doing GC if we expect that the function will no longer be used.
DECL_BOOLEAN_ACCESSORS(allows_lazy_compilation)
+ // Indicates if this function can be lazy compiled without a context.
+ // This is used to determine if we can force compilation without reaching
+ // the function through program execution but through other means (e.g. heap
+ // iteration by the debugger).
+ DECL_BOOLEAN_ACCESSORS(allows_lazy_compilation_without_context)
+
// Indicates how many full GCs this function has survived with assigned
// code object. Used to determine when it is relatively safe to flush
// this code object and replace it with lazy compilation stub.
void ResetForNewContext(int new_ic_age);
- // Helpers to compile the shared code. Returns true on success, false on
- // failure (e.g., stack overflow during compilation).
- static bool EnsureCompiled(Handle<SharedFunctionInfo> shared,
- ClearExceptionFlag flag);
+ // Helper to compile the shared code. Returns true on success, false on
+ // failure (e.g., stack overflow during compilation). This is only used by
+ // the debugger, it is not possible to compile without a context otherwise.
static bool CompileLazy(Handle<SharedFunctionInfo> shared,
ClearExceptionFlag flag);
enum CompilerHints {
kHasOnlySimpleThisPropertyAssignments,
kAllowLazyCompilation,
+ kAllowLazyCompilationWithoutContext,
kLiveObjectsMayExist,
kCodeAgeShift,
kOptimizationDisabled = kCodeAgeShift + kCodeAgeSize,
// Helpers to compile this function. Returns true on success, false on
// failure (e.g., stack overflow during compilation).
+ static bool EnsureCompiled(Handle<JSFunction> function,
+ ClearExceptionFlag flag);
static bool CompileLazy(Handle<JSFunction> function,
ClearExceptionFlag flag);
static bool CompileOptimized(Handle<JSFunction> function,
// The heuristics are:
// - It must not have been prohibited by the caller to Parse (some callers
// need a full AST).
- // - The outer scope must be trivial (only global variables in scope).
// - The function mustn't be a function expression with an open parenthesis
// before; we consider that a hint that the function will be called
// immediately, and it would be a waste of time to make it lazily
// These are all things we can know at this point, without looking at the
// function itself.
bool is_lazily_compiled = (mode() == PARSE_LAZILY &&
- top_scope_->outer_scope()->is_global_scope() &&
- top_scope_->HasTrivialOuterContext() &&
!parenthesized_function_);
parenthesized_function_ = false; // The bit was set for this function only.
Handle<SharedFunctionInfo> target_shared(target->shared());
Handle<SharedFunctionInfo> source_shared(source->shared());
- if (!source->is_compiled() &&
- !JSFunction::CompileLazy(source, KEEP_EXCEPTION)) {
+ if (!JSFunction::EnsureCompiled(source, KEEP_EXCEPTION)) {
return Failure::Exception();
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugPrepareStepInIfStepping) {
Debug* debug = isolate->debug();
if (!debug->IsStepping()) return NULL;
- CONVERT_ARG_CHECKED(Object, callback, 0);
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, callback, 0);
HandleScope scope(isolate);
- Handle<SharedFunctionInfo> shared_info(JSFunction::cast(callback)->shared());
// When leaving the callback, step out has been activated, but not performed
// if we do not leave the builtin. To be able to step into the callback
// again, we need to clear the step out at this point.
debug->ClearStepOut();
- debug->FloodWithOneShot(shared_info);
+ debug->FloodWithOneShot(callback);
return NULL;
}
}
// The function should be compiled for the optimization hints to be
- // available. We cannot use EnsureCompiled because that forces a
- // compilation through the shared function info which makes it
- // impossible for us to optimize.
- if (!function->is_compiled()) {
- JSFunction::CompileLazy(function, CLEAR_EXCEPTION);
- }
+ // available.
+ JSFunction::EnsureCompiled(function, CLEAR_EXCEPTION);
Handle<SharedFunctionInfo> shared(function->shared(), isolate);
if (!function->has_initial_map() &&
}
// Get the debug info (create it if it does not exist).
- if (!isolate->debug()->EnsureDebugInfo(shared_info)) {
+ if (!isolate->debug()->EnsureDebugInfo(shared_info, function_)) {
// Return if ensuring debug info failed.
return;
}
}
-// Set a break point in a function
-// args[0]: function
-// args[1]: number: break source position (within the function source)
-// args[2]: number: break point object
-RUNTIME_FUNCTION(MaybeObject*, Runtime_SetFunctionBreakPoint) {
- HandleScope scope(isolate);
- ASSERT(args.length() == 3);
- CONVERT_ARG_HANDLE_CHECKED(JSFunction, fun, 0);
- Handle<SharedFunctionInfo> shared(fun->shared());
- CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
- RUNTIME_ASSERT(source_position >= 0);
- Handle<Object> break_point_object_arg = args.at<Object>(2);
-
- // Set break point.
- isolate->debug()->SetBreakPoint(shared, break_point_object_arg,
- &source_position);
-
- return Smi::FromInt(source_position);
-}
-
-
Object* Runtime::FindSharedFunctionInfoInScript(Isolate* isolate,
Handle<Script> script,
int position) {
+ // The below fix-point iteration depends on all functions that cannot be
+ // compiled lazily without a context to not be compiled at all. Compilation
+ // will be triggered at points where we do not need a context.
+ isolate->debug()->PrepareForBreakPoints();
+
// Iterate the heap looking for SharedFunctionInfo generated from the
// script. The inner most SharedFunctionInfo containing the source position
// for the requested break point is found.
bool done = false;
// The current candidate for the source position:
int target_start_position = RelocInfo::kNoPosition;
+ Handle<JSFunction> target_function;
Handle<SharedFunctionInfo> target;
while (!done) {
{ // Extra scope for iterator and no-allocation.
HeapIterator iterator;
for (HeapObject* obj = iterator.next();
obj != NULL; obj = iterator.next()) {
- if (obj->IsSharedFunctionInfo()) {
- Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
- if (shared->script() == *script) {
- // If the SharedFunctionInfo found has the requested script data and
- // contains the source position it is a candidate.
- int start_position = shared->function_token_position();
- if (start_position == RelocInfo::kNoPosition) {
- start_position = shared->start_position();
- }
- if (start_position <= position &&
- position <= shared->end_position()) {
- // If there is no candidate or this function is within the current
- // candidate this is the new candidate.
- if (target.is_null()) {
- target_start_position = start_position;
- target = shared;
- } else {
- if (target_start_position == start_position &&
- shared->end_position() == target->end_position()) {
- // If a top-level function contain only one function
- // declartion the source for the top-level and the
- // function is the same. In that case prefer the non
- // top-level function.
- if (!shared->is_toplevel()) {
- target_start_position = start_position;
- target = shared;
- }
- } else if (target_start_position <= start_position &&
- shared->end_position() <= target->end_position()) {
- // This containment check includes equality as a function
- // inside a top-level function can share either start or end
- // position with the top-level function.
+ bool found_next_candidate = false;
+ Handle<JSFunction> function;
+ Handle<SharedFunctionInfo> shared;
+ if (obj->IsJSFunction()) {
+ function = Handle<JSFunction>(JSFunction::cast(obj));
+ shared = Handle<SharedFunctionInfo>(function->shared());
+ ASSERT(shared->allows_lazy_compilation() || shared->is_compiled());
+ found_next_candidate = true;
+ } else if (obj->IsSharedFunctionInfo()) {
+ shared = Handle<SharedFunctionInfo>(SharedFunctionInfo::cast(obj));
+ // Skip functions that we cannot compile lazily without a context,
+ // which is not available here, because there is no closure.
+ found_next_candidate = shared->is_compiled() ||
+ shared->allows_lazy_compilation_without_context();
+ }
+ if (!found_next_candidate) continue;
+ if (shared->script() == *script) {
+ // If the SharedFunctionInfo found has the requested script data and
+ // contains the source position it is a candidate.
+ int start_position = shared->function_token_position();
+ if (start_position == RelocInfo::kNoPosition) {
+ start_position = shared->start_position();
+ }
+ if (start_position <= position &&
+ position <= shared->end_position()) {
+ // If there is no candidate or this function is within the current
+ // candidate this is the new candidate.
+ if (target.is_null()) {
+ target_start_position = start_position;
+ target_function = function;
+ target = shared;
+ } else {
+ if (target_start_position == start_position &&
+ shared->end_position() == target->end_position()) {
+ // If a top-level function contains only one function
+ // declaration the source for the top-level and the function
+ // is the same. In that case prefer the non top-level function.
+ if (!shared->is_toplevel()) {
target_start_position = start_position;
+ target_function = function;
target = shared;
}
+ } else if (target_start_position <= start_position &&
+ shared->end_position() <= target->end_position()) {
+ // This containment check includes equality as a function
+ // inside a top-level function can share either start or end
+ // position with the top-level function.
+ target_start_position = start_position;
+ target_function = function;
+ target = shared;
}
}
}
}
} // End for loop.
- } // End No allocation scope.
+ } // End no-allocation scope.
if (target.is_null()) {
return isolate->heap()->undefined_value();
}
- // If the candidate found is compiled we are done. NOTE: when lazy
- // compilation of inner functions is introduced some additional checking
- // needs to be done here to compile inner functions.
+ // If the candidate found is compiled we are done.
done = target->is_compiled();
if (!done) {
- // If the candidate is not compiled compile it to reveal any inner
- // functions which might contain the requested source position.
- SharedFunctionInfo::CompileLazy(target, KEEP_EXCEPTION);
+ // If the candidate is not compiled, compile it to reveal any inner
+ // functions which might contain the requested source position. This
+ // will compile all inner functions that cannot be compiled without a
+ // context, because Compiler::BuildFunctionInfo checks whether the
+ // debugger is active.
+ if (target_function.is_null()) {
+ SharedFunctionInfo::CompileLazy(target, KEEP_EXCEPTION);
+ } else {
+ JSFunction::CompileLazy(target_function, KEEP_EXCEPTION);
+ }
}
} // End while loop.
}
+// Set a break point in a function.
+// args[0]: function
+// args[1]: number: break source position (within the function source)
+// args[2]: number: break point object
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetFunctionBreakPoint) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 3);
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
+ CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
+ RUNTIME_ASSERT(source_position >= 0);
+ Handle<Object> break_point_object_arg = args.at<Object>(2);
+
+ // Set break point.
+ isolate->debug()->SetBreakPoint(function, break_point_object_arg,
+ &source_position);
+
+ return Smi::FromInt(source_position);
+}
+
+
// Changes the state of a break point in a script and returns source position
// where break point was set. NOTE: Regarding performance see the NOTE for
// GetScriptFromScriptData.
RUNTIME_ASSERT(wrapper->value()->IsScript());
Handle<Script> script(Script::cast(wrapper->value()));
- Object* result = Runtime::FindSharedFunctionInfoInScript(
- isolate, script, source_position);
- if (!result->IsUndefined()) {
- Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
- // Find position within function. The script position might be before the
- // source position of the first function.
- int position;
- if (shared->start_position() > source_position) {
- position = 0;
- } else {
- position = source_position - shared->start_position();
- }
- isolate->debug()->SetBreakPoint(shared, break_point_object_arg, &position);
- position += shared->start_position();
- return Smi::FromInt(position);
+ // Set break point.
+ if (!isolate->debug()->SetBreakPointForScript(script, break_point_object_arg,
+ &source_position)) {
+ return isolate->heap()->undefined_value();
}
- return isolate->heap()->undefined_value();
+
+ return Smi::FromInt(source_position);
}
ASSERT(args.length() == 1);
// Get the function and make sure it is compiled.
CONVERT_ARG_HANDLE_CHECKED(JSFunction, func, 0);
- if (!JSFunction::CompileLazy(func, KEEP_EXCEPTION)) {
+ if (!JSFunction::EnsureCompiled(func, KEEP_EXCEPTION)) {
return Failure::Exception();
}
func->code()->PrintLn();
ASSERT(args.length() == 1);
// Get the function and make sure it is compiled.
CONVERT_ARG_HANDLE_CHECKED(JSFunction, func, 0);
- if (!JSFunction::CompileLazy(func, KEEP_EXCEPTION)) {
+ if (!JSFunction::EnsureCompiled(func, KEEP_EXCEPTION)) {
return Failure::Exception();
}
func->shared()->construct_stub()->PrintLn();
}
-bool Scope::AllowsLazyCompilation() const {
- return !force_eager_compilation_ && HasTrivialOuterContext();
-}
-
-
bool Scope::HasTrivialContext() const {
// A function scope has a trivial context if it always is the global
// context. We iteratively scan out the context chain to see if
}
-bool Scope::AllowsLazyRecompilation() const {
+bool Scope::AllowsLazyCompilation() const {
return !force_eager_compilation_ &&
!TrivialDeclarationScopesBeforeWithScope();
}
+bool Scope::AllowsLazyCompilationWithoutContext() const {
+ return !force_eager_compilation_ && HasTrivialOuterContext();
+}
+
+
bool Scope::TrivialDeclarationScopesBeforeWithScope() const {
Scope* outer = outer_scope_;
if (outer == NULL) return false;
// Determine if we can use lazy compilation for this scope.
bool AllowsLazyCompilation() const;
- // True if we can lazily recompile functions with this scope.
- bool AllowsLazyRecompilation() const;
+ // Determine if we can use lazy compilation for this scope without a context.
+ bool AllowsLazyCompilationWithoutContext() const;
// True if the outer context of this scope is always the global context.
bool HasTrivialOuterContext() const;
// number.
static int SetBreakPoint(Handle<v8::internal::JSFunction> fun, int position) {
static int break_point = 0;
- Handle<v8::internal::SharedFunctionInfo> shared(fun->shared());
v8::internal::Debug* debug = v8::internal::Isolate::Current()->debug();
debug->SetBreakPoint(
- shared,
+ fun,
Handle<Object>(v8::internal::Smi::FromInt(++break_point)),
&position);
return break_point;
// there
ClearBreakPoint(bp);
CHECK(!debug->HasDebugInfo(shared));
- CHECK(debug->EnsureDebugInfo(shared));
+ CHECK(debug->EnsureDebugInfo(shared, fun));
TestBreakLocationIterator it2(Debug::GetDebugInfo(shared));
it2.FindBreakLocationFromPosition(position);
actual_mode = it2.it()->rinfo()->rmode();
HEAP->CollectAllGarbage(Heap::kNoGCFlags);
HEAP->CollectAllGarbage(Heap::kNoGCFlags);
HEAP->CollectAllGarbage(Heap::kNoGCFlags);
+ HEAP->CollectAllGarbage(Heap::kNoGCFlags);
CHECK(HEAP->old_pointer_space()->IsSweepingComplete());
int initial_size = static_cast<int>(HEAP->SizeOfObjects());
--- /dev/null
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --expose-debug-as debug
+// Get the Debug object exposed from the debug context global object.
+Debug = debug.Debug
+
+// Simple debug event handler which just counts the number of break points hit.
+var break_point_hit_count = 0;
+
+function listener(event, exec_state, event_data, data) {
+ if (event == Debug.DebugEvent.Break) {
+ break_point_hit_count++;
+ }
+};
+
+// Add the debug event listener.
+Debug.setListener(listener);
+
+function makeClosure() {
+ var x;
+ return function() {
+ return x; // Breakpoint line ( #47 )
+ };
+}
+
+// Create closure before break point is set.
+var closure = makeClosure();
+
+// The debugger triggers re-compilation.
+assertEquals(0, Debug.scriptBreakPoints().length);
+var scr = Debug.findScript(makeClosure);
+var sbp = Debug.setScriptBreakPointById(scr.id, 47);
+assertEquals(1, Debug.scriptBreakPoints().length);
+
+// Ensure the closure actually triggers a break point hit.
+closure();
+assertEquals(1, break_point_hit_count);
+
+// Remove script break point.
+assertEquals(1, Debug.scriptBreakPoints().length);
+Debug.clearBreakPoint(sbp);
+assertEquals(0, Debug.scriptBreakPoints().length);
--- /dev/null
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --expose-debug-as debug
+// Get the Debug object exposed from the debug context global object.
+Debug = debug.Debug
+
+// Simple debug event handler which just counts the number of break points hit.
+var break_point_hit_count = 0;
+
+function listener(event, exec_state, event_data, data) {
+ if (event == Debug.DebugEvent.Break) {
+ break_point_hit_count++;
+ }
+};
+
+// Add the debug event listener.
+Debug.setListener(listener);
+
+eval(
+ "var inner;\n" +
+ "function outer() {\n" + // Non-trivial outer closure.
+ " var x = 5;\n" +
+ " function a() {\n" +
+ " var foo = 0, y = 7;\n" +
+ " function b() {\n" +
+ " var bar = 0, baz = 0, z = 11;\n" +
+ " function c() {\n" +
+ " return x + y + z;\n" + // Breakpoint line ( #8 )
+ " }\n" +
+ " inner = c;\n" +
+ " return c();\n" +
+ " }\n" +
+ " return b();\n" +
+ " }\n" +
+ " return a();\n" +
+ "}"
+);
+
+var script = Debug.findScript(outer);
+
+// The debugger triggers compilation of inner closures.
+assertEquals(0, Debug.scriptBreakPoints().length);
+var sbp = Debug.setScriptBreakPointById(script.id, 8);
+assertEquals(1, Debug.scriptBreakPoints().length);
+
+// The compiled outer closure should behave correctly.
+assertEquals(23, outer());
+assertEquals(1, break_point_hit_count);
+
+// The compiled inner closure should behave correctly.
+assertEquals(23, inner());
+assertEquals(2, break_point_hit_count);
+
+// Remove script break point.
+assertEquals(1, Debug.scriptBreakPoints().length);
+Debug.clearBreakPoint(sbp);
+assertEquals(0, Debug.scriptBreakPoints().length);