| Index: src/liveedit.cc
|
| diff --git a/src/liveedit.cc b/src/liveedit.cc
|
| index f0cd5ddcf55531e6aede19e0044a6e31d6ae903a..09970ccf9950843aa86b85e4de88f00d312af290 100644
|
| --- a/src/liveedit.cc
|
| +++ b/src/liveedit.cc
|
| @@ -39,50 +39,39 @@ namespace v8 {
|
| namespace internal {
|
|
|
|
|
| -class FunctionInfoListener {
|
| - public:
|
| - void FunctionStarted(FunctionLiteral* fun) {
|
| - // Implementation follows.
|
| - }
|
| +#ifdef ENABLE_DEBUGGER_SUPPORT
|
|
|
| - void FunctionDone() {
|
| - // Implementation follows.
|
| - }
|
| +static void CompileScriptForTracker(Handle<Script> script) {
|
| + const bool is_eval = false;
|
| + const bool is_global = true;
|
| + // TODO: support extensions.
|
| + Extension* extension = NULL;
|
|
|
| - void FunctionScope(Scope* scope) {
|
| - // Implementation follows.
|
| - }
|
| + PostponeInterruptsScope postpone;
|
|
|
| - void FunctionCode(Handle<Code> function_code) {
|
| - // Implementation follows.
|
| - }
|
| -};
|
| + // Only allow non-global compiles for eval.
|
| + ASSERT(is_eval || is_global);
|
|
|
| -static FunctionInfoListener* active_function_info_listener = NULL;
|
| + // Build AST.
|
| + ScriptDataImpl* pre_data = NULL;
|
| + FunctionLiteral* lit = MakeAST(is_global, script, extension, pre_data);
|
|
|
| -LiveEditFunctionTracker::LiveEditFunctionTracker(FunctionLiteral* fun) {
|
| - if (active_function_info_listener != NULL) {
|
| - active_function_info_listener->FunctionStarted(fun);
|
| - }
|
| -}
|
| -LiveEditFunctionTracker::~LiveEditFunctionTracker() {
|
| - if (active_function_info_listener != NULL) {
|
| - active_function_info_listener->FunctionDone();
|
| - }
|
| -}
|
| -void LiveEditFunctionTracker::RecordFunctionCode(Handle<Code> code) {
|
| - if (active_function_info_listener != NULL) {
|
| - active_function_info_listener->FunctionCode(code);
|
| + // Check for parse errors.
|
| + if (lit == NULL) {
|
| + ASSERT(Top::has_pending_exception());
|
| + return;
|
| }
|
| -}
|
| -void LiveEditFunctionTracker::RecordFunctionScope(Scope* scope) {
|
| - if (active_function_info_listener != NULL) {
|
| - active_function_info_listener->FunctionScope(scope);
|
| +
|
| + // Compile the code.
|
| + CompilationInfo info(lit, script, is_eval);
|
| + Handle<Code> code = MakeCodeForLiveEdit(&info);
|
| +
|
| + // Check for stack-overflow exceptions.
|
| + if (code.is_null()) {
|
| + Top::StackOverflow();
|
| + return;
|
| }
|
| }
|
| -bool LiveEditFunctionTracker::IsActive() {
|
| - return active_function_info_listener != NULL;
|
| -}
|
|
|
| // Unwraps JSValue object, returning its field "value"
|
| static Handle<Object> UnwrapJSValue(Handle<JSValue> jsValue) {
|
| @@ -186,6 +175,8 @@ class FunctionInfoWrapper : public JSArrayBasedStruct<FunctionInfoWrapper> {
|
| static const int kScopeInfoOffset_ = 5;
|
| static const int kParentIndexOffset_ = 6;
|
| static const int kSize_ = 7;
|
| +
|
| + friend class JSArrayBasedStruct<FunctionInfoWrapper>;
|
| };
|
|
|
| // Wraps SharedFunctionInfo along with some of its fields for passing it
|
| @@ -219,8 +210,274 @@ class SharedInfoWrapper : public JSArrayBasedStruct<SharedInfoWrapper> {
|
| static const int kEndPositionOffset_ = 2;
|
| static const int kSharedInfoOffset_ = 3;
|
| static const int kSize_ = 4;
|
| +
|
| + friend class JSArrayBasedStruct<SharedInfoWrapper>;
|
| };
|
|
|
| +class FunctionInfoListener {
|
| + public:
|
| + FunctionInfoListener() {
|
| + current_parent_index_ = -1;
|
| + len_ = 0;
|
| + result_ = Factory::NewJSArray(10);
|
| + }
|
| +
|
| + void FunctionStarted(FunctionLiteral* fun) {
|
| + HandleScope scope;
|
| + FunctionInfoWrapper info = FunctionInfoWrapper::Create();
|
| + info.SetInitialProperties(fun->name(), fun->start_position(),
|
| + fun->end_position(), fun->num_parameters(),
|
| + current_parent_index_);
|
| + current_parent_index_ = len_;
|
| + SetElement(result_, len_, info.GetJSArray());
|
| + len_++;
|
| + }
|
| +
|
| + void FunctionDone() {
|
| + HandleScope scope;
|
| + FunctionInfoWrapper info =
|
| + FunctionInfoWrapper::cast(result_->GetElement(current_parent_index_));
|
| + current_parent_index_ = info.GetParentIndex();
|
| + }
|
| +
|
| + void FunctionScope(Scope* scope) {
|
| + HandleScope handle_scope;
|
| +
|
| + Handle<JSArray> scope_info_list = Factory::NewJSArray(10);
|
| + int scope_info_length = 0;
|
| +
|
| + // Saves some description of scope. It stores name and indexes of
|
| + // variables in the whole scope chain. Null-named slots delimit
|
| + // scopes of this chain.
|
| + Scope* outer_scope = scope->outer_scope();
|
| + if (outer_scope == NULL) {
|
| + return;
|
| + }
|
| + do {
|
| + ZoneList<Variable*> list(10);
|
| + outer_scope->CollectUsedVariables(&list);
|
| + int j = 0;
|
| + for (int i = 0; i < list.length(); i++) {
|
| + Variable* var1 = list[i];
|
| + Slot* slot = var1->slot();
|
| + if (slot != NULL && slot->type() == Slot::CONTEXT) {
|
| + if (j != i) {
|
| + list[j] = var1;
|
| + }
|
| + j++;
|
| + }
|
| + }
|
| +
|
| + // Sort it.
|
| + for (int k = 1; k < j; k++) {
|
| + int l = k;
|
| + for (int m = k + 1; m < j; m++) {
|
| + if (list[l]->slot()->index() > list[m]->slot()->index()) {
|
| + l = m;
|
| + }
|
| + }
|
| + list[k] = list[l];
|
| + }
|
| + for (int i = 0; i < j; i++) {
|
| + SetElement(scope_info_list, scope_info_length, list[i]->name());
|
| + scope_info_length++;
|
| + SetElement(scope_info_list, scope_info_length,
|
| + Handle<Smi>(Smi::FromInt(list[i]->slot()->index())));
|
| + scope_info_length++;
|
| + }
|
| + SetElement(scope_info_list, scope_info_length,
|
| + Handle<Object>(Heap::null_value()));
|
| + scope_info_length++;
|
| +
|
| + outer_scope = outer_scope->outer_scope();
|
| + } while (outer_scope != NULL);
|
| +
|
| + FunctionInfoWrapper info =
|
| + FunctionInfoWrapper::cast(result_->GetElement(current_parent_index_));
|
| + info.SetScopeInfo(scope_info_list);
|
| + }
|
| +
|
| + void FunctionCode(Handle<Code> function_code) {
|
| + FunctionInfoWrapper info =
|
| + FunctionInfoWrapper::cast(result_->GetElement(current_parent_index_));
|
| + info.SetFunctionCode(function_code);
|
| + }
|
| +
|
| + Handle<JSArray> GetResult() {
|
| + return result_;
|
| + }
|
| +
|
| + private:
|
| + Handle<JSArray> result_;
|
| + int len_;
|
| + int current_parent_index_;
|
| +};
|
| +
|
| +static FunctionInfoListener* active_function_info_listener = NULL;
|
| +
|
| +JSArray* LiveEdit::GatherCompileInfo(Handle<Script> script,
|
| + Handle<String> source) {
|
| + CompilationZoneScope zone_scope(DELETE_ON_EXIT);
|
| +
|
| + FunctionInfoListener listener;
|
| + script->set_source(*source);
|
| + active_function_info_listener = &listener;
|
| + Handle<Object> original_source = Handle<Object>(script->source());
|
| + CompileScriptForTracker(script);
|
| + active_function_info_listener = NULL;
|
| + script->set_source(*original_source);
|
| +
|
| + return *(listener.GetResult());
|
| +}
|
| +
|
| +
|
| +void LiveEdit::WrapSharedFunctionInfos(Handle<JSArray> array) {
|
| + HandleScope scope;
|
| + int len = Smi::cast(array->length())->value();
|
| + for (int i = 0; i < len; i++) {
|
| + Handle<SharedFunctionInfo> info(
|
| + SharedFunctionInfo::cast(array->GetElement(i)));
|
| + SharedInfoWrapper info_wrapper = SharedInfoWrapper::Create();
|
| + Handle<String> name_handle(String::cast(info->name()));
|
| + info_wrapper.SetProperties(name_handle, info->start_position(),
|
| + info->end_position(), info);
|
| + array->SetElement(i, *(info_wrapper.GetJSArray()));
|
| + }
|
| +}
|
| +
|
| +
|
| +void LiveEdit::ReplaceFunctionCode(Handle<JSArray> new_compile_info_array,
|
| + Handle<JSArray> shared_info_array) {
|
| + HandleScope scope;
|
| +
|
| + FunctionInfoWrapper compile_info_wrapper(new_compile_info_array);
|
| + SharedInfoWrapper shared_info_wrapper(shared_info_array);
|
| +
|
| + Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo();
|
| +
|
| + shared_info->set_code(*(compile_info_wrapper.GetFunctionCode()),
|
| + UPDATE_WRITE_BARRIER);
|
| + shared_info->set_start_position(compile_info_wrapper.GetStartPosition());
|
| + shared_info->set_end_position(compile_info_wrapper.GetEndPosition());
|
| + // update breakpoints, original code, constructor stub
|
| +}
|
| +
|
| +
|
| +void LiveEdit::RelinkFunctionToScript(Handle<JSArray> shared_info_array,
|
| + Handle<Script> script_handle) {
|
| + SharedInfoWrapper shared_info_wrapper(shared_info_array);
|
| + Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo();
|
| +
|
| + shared_info->set_script(*script_handle);
|
| +}
|
| +
|
| +
|
| +// For a script text change (defined as position_change_array), translates
|
| +// position in unchanged text to position in changed text.
|
| +// Text change is a set of non-overlapping regions in text, that have changed
|
| +// their contents and length. It is specified as array of groups of 3 numbers:
|
| +// (change_begin, change_end, change_end_new_position).
|
| +// Each group describes a change in text; groups are sorted by change_begin.
|
| +// Only position in text beyond any changes may be successfully translated.
|
| +// If a positions is inside some region that changed, result is currently
|
| +// undefined.
|
| +static int TranslatePosition(int original_position,
|
| + Handle<JSArray> position_change_array) {
|
| + int position_diff = 0;
|
| + int array_len = Smi::cast(position_change_array->length())->value();
|
| + // TODO: binary search may be used here
|
| + for (int i = 0; i < array_len; i += 3) {
|
| + int chunk_start =
|
| + Smi::cast(position_change_array->GetElement(i))->value();
|
| + int chunk_end =
|
| + Smi::cast(position_change_array->GetElement(i + 1))->value();
|
| + int chunk_changed_end =
|
| + Smi::cast(position_change_array->GetElement(i + 2))->value();
|
| + position_diff = chunk_changed_end - chunk_end;
|
| + if (original_position < chunk_start) {
|
| + break;
|
| + }
|
| + // Position mustn't be inside a chunk.
|
| + ASSERT(original_position >= chunk_end);
|
| + }
|
| +
|
| + return original_position + position_diff;
|
| +}
|
| +
|
| +
|
| +void LiveEdit::PatchFunctionPositions(Handle<JSArray> shared_info_array,
|
| + Handle<JSArray> position_change_array) {
|
| + SharedInfoWrapper shared_info_wrapper(shared_info_array);
|
| + Handle<SharedFunctionInfo> info = shared_info_wrapper.GetInfo();
|
| +
|
| + info->set_start_position(TranslatePosition(info->start_position(),
|
| + position_change_array));
|
| + info->set_end_position(TranslatePosition(info->end_position(),
|
| + position_change_array));
|
| +
|
| + // Also patch rinfos (both in working code and original code), breakpoints.
|
| +}
|
| +
|
| +
|
| +LiveEditFunctionTracker::LiveEditFunctionTracker(FunctionLiteral* fun) {
|
| + if (active_function_info_listener != NULL) {
|
| + active_function_info_listener->FunctionStarted(fun);
|
| + }
|
| +}
|
| +
|
| +
|
| +LiveEditFunctionTracker::~LiveEditFunctionTracker() {
|
| + if (active_function_info_listener != NULL) {
|
| + active_function_info_listener->FunctionDone();
|
| + }
|
| +}
|
| +
|
| +
|
| +void LiveEditFunctionTracker::RecordFunctionCode(Handle<Code> code) {
|
| + if (active_function_info_listener != NULL) {
|
| + active_function_info_listener->FunctionCode(code);
|
| + }
|
| +}
|
| +
|
| +
|
| +void LiveEditFunctionTracker::RecordFunctionScope(Scope* scope) {
|
| + if (active_function_info_listener != NULL) {
|
| + active_function_info_listener->FunctionScope(scope);
|
| + }
|
| +}
|
| +
|
| +
|
| +bool LiveEditFunctionTracker::IsActive() {
|
| + return active_function_info_listener != NULL;
|
| +}
|
| +
|
| +
|
| +#else // ENABLE_DEBUGGER_SUPPORT
|
| +
|
| +// This ifdef-else-endif section provides working or stub implementation of
|
| +// LiveEditFunctionTracker.
|
| +LiveEditFunctionTracker::LiveEditFunctionTracker(FunctionLiteral* fun) {
|
| +}
|
| +
|
| +
|
| +LiveEditFunctionTracker::~LiveEditFunctionTracker() {
|
| +}
|
| +
|
| +
|
| +void LiveEditFunctionTracker::RecordFunctionCode(Handle<Code> code) {
|
| +}
|
| +
|
| +
|
| +void LiveEditFunctionTracker::RecordFunctionScope(Scope* scope) {
|
| +}
|
| +
|
| +
|
| +bool LiveEditFunctionTracker::IsActive() {
|
| + return false;
|
| +}
|
| +
|
| +#endif // ENABLE_DEBUGGER_SUPPORT
|
| +
|
|
|
|
|
| } } // namespace v8::internal
|
|
|