Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(416)

Unified Diff: src/liveedit.cc

Issue 652027: Basic implementation of liveedit feature (Closed)
Patch Set: add proper source headers Created 10 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/liveedit.h ('k') | src/liveedit-delay.js » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « src/liveedit.h ('k') | src/liveedit-delay.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698