Index: src/liveedit.cc |
diff --git a/src/liveedit.cc b/src/liveedit.cc |
index dc7d4b157d01ec1a9b405e36d8ed5373ac87a091..574a37691c0a0e99e0e46617455d734ff7e50d2c 100644 |
--- a/src/liveedit.cc |
+++ b/src/liveedit.cc |
@@ -703,12 +703,14 @@ class FunctionInfoWrapper : public JSArrayBasedStruct<FunctionInfoWrapper> { |
: JSArrayBasedStruct<FunctionInfoWrapper>(array) { |
} |
void SetInitialProperties(Handle<String> name, int start_position, |
- int end_position, int param_num, int parent_index) { |
+ int end_position, int param_num, |
+ int literal_count, int parent_index) { |
HandleScope scope; |
this->SetField(kFunctionNameOffset_, name); |
this->SetSmiValueField(kStartPositionOffset_, start_position); |
this->SetSmiValueField(kEndPositionOffset_, end_position); |
this->SetSmiValueField(kParamNumOffset_, param_num); |
+ this->SetSmiValueField(kLiteralNumOffset_, literal_count); |
this->SetSmiValueField(kParentIndexOffset_, parent_index); |
} |
void SetFunctionCode(Handle<Code> function_code, |
@@ -726,6 +728,9 @@ class FunctionInfoWrapper : public JSArrayBasedStruct<FunctionInfoWrapper> { |
Handle<JSValue> info_holder = WrapInJSValue(info); |
this->SetField(kSharedFunctionInfoOffset_, info_holder); |
} |
+ int GetLiteralCount() { |
+ return this->GetSmiValueField(kLiteralNumOffset_); |
+ } |
int GetParentIndex() { |
return this->GetSmiValueField(kParentIndexOffset_); |
} |
@@ -759,7 +764,8 @@ class FunctionInfoWrapper : public JSArrayBasedStruct<FunctionInfoWrapper> { |
static const int kOuterScopeInfoOffset_ = 6; |
static const int kParentIndexOffset_ = 7; |
static const int kSharedFunctionInfoOffset_ = 8; |
- static const int kSize_ = 9; |
+ static const int kLiteralNumOffset_ = 9; |
+ static const int kSize_ = 10; |
friend class JSArrayBasedStruct<FunctionInfoWrapper>; |
}; |
@@ -819,6 +825,7 @@ class FunctionInfoListener { |
FunctionInfoWrapper info = FunctionInfoWrapper::Create(); |
info.SetInitialProperties(fun->name(), fun->start_position(), |
fun->end_position(), fun->parameter_count(), |
+ fun->materialized_literal_count(), |
current_parent_index_); |
current_parent_index_ = len_; |
SetElementNonStrict(result_, len_, info.GetJSArray()); |
@@ -1014,6 +1021,129 @@ static void ReplaceCodeObject(Handle<Code> original, |
} |
+// Patch function literals. |
+// Name 'literals' is a misnomer. Rather it's a cache for complex object |
+// boilerplates and for a native context. We must clean cached values. |
+// Additionally we may need to allocate a new array if number of literals |
+// changed. |
+class LiteralFixer { |
+ public: |
+ static void PatchLiterals(FunctionInfoWrapper* compile_info_wrapper, |
+ Handle<SharedFunctionInfo> shared_info, |
+ Isolate* isolate) { |
+ int new_literal_count = compile_info_wrapper->GetLiteralCount(); |
+ if (new_literal_count > 0) { |
+ new_literal_count += JSFunction::kLiteralsPrefixSize; |
+ } |
+ int old_literal_count = shared_info->num_literals(); |
+ |
+ if (old_literal_count == new_literal_count) { |
+ // If literal count didn't change, simply go over all functions |
+ // and clear literal arrays. |
+ ClearValuesVisitor visitor; |
+ IterateJSFunctions(*shared_info, &visitor); |
+ } else { |
+ // When literal count changes, we have to create new array instances. |
+ // Since we cannot create instances when iterating heap, we should first |
+ // collect all functions and fix their literal arrays. |
+ Handle<FixedArray> function_instances = |
+ CollectJSFunctions(shared_info, isolate); |
+ for (int i = 0; i < function_instances->length(); i++) { |
+ Handle<JSFunction> fun(JSFunction::cast(function_instances->get(i))); |
+ Handle<FixedArray> old_literals(fun->literals()); |
+ Handle<FixedArray> new_literals = |
+ isolate->factory()->NewFixedArray(new_literal_count); |
+ if (new_literal_count > 0) { |
+ Handle<Context> native_context; |
+ if (old_literals->length() > |
+ JSFunction::kLiteralNativeContextIndex) { |
+ native_context = Handle<Context>( |
+ JSFunction::NativeContextFromLiterals(fun->literals())); |
+ } else { |
+ native_context = Handle<Context>(fun->context()->native_context()); |
+ } |
+ new_literals->set(JSFunction::kLiteralNativeContextIndex, |
+ *native_context); |
+ } |
+ fun->set_literals(*new_literals); |
+ } |
+ |
+ shared_info->set_num_literals(new_literal_count); |
+ } |
+ } |
+ |
+ private: |
+ // Iterates all function instances in the HEAP that refers to the |
+ // provided shared_info. |
+ template<typename Visitor> |
+ static void IterateJSFunctions(SharedFunctionInfo* shared_info, |
+ Visitor* visitor) { |
+ AssertNoAllocation no_allocations_please; |
+ |
+ HeapIterator iterator; |
+ for (HeapObject* obj = iterator.next(); obj != NULL; |
+ obj = iterator.next()) { |
+ if (obj->IsJSFunction()) { |
+ JSFunction* function = JSFunction::cast(obj); |
+ if (function->shared() == shared_info) { |
+ visitor->visit(function); |
+ } |
+ } |
+ } |
+ } |
+ |
+ // Finds all instances of JSFunction that refers to the provided shared_info |
+ // and returns array with them. |
+ static Handle<FixedArray> CollectJSFunctions( |
+ Handle<SharedFunctionInfo> shared_info, Isolate* isolate) { |
+ CountVisitor count_visitor; |
+ count_visitor.count = 0; |
+ IterateJSFunctions(*shared_info, &count_visitor); |
+ int size = count_visitor.count; |
+ |
+ Handle<FixedArray> result = isolate->factory()->NewFixedArray(size); |
+ if (size > 0) { |
+ CollectVisitor collect_visitor(result); |
+ IterateJSFunctions(*shared_info, &collect_visitor); |
+ } |
+ return result; |
+ } |
+ |
+ class ClearValuesVisitor { |
+ public: |
+ void visit(JSFunction* fun) { |
+ FixedArray* literals = fun->literals(); |
+ int len = literals->length(); |
+ for (int j = JSFunction::kLiteralsPrefixSize; j < len; j++) { |
+ literals->set_undefined(j); |
+ } |
+ } |
+ }; |
+ |
+ class CountVisitor { |
+ public: |
+ void visit(JSFunction* fun) { |
+ count++; |
+ } |
+ int count; |
+ }; |
+ |
+ class CollectVisitor { |
+ public: |
+ explicit CollectVisitor(Handle<FixedArray> output) |
+ : m_output(output), m_pos(0) {} |
+ |
+ void visit(JSFunction* fun) { |
+ m_output->set(m_pos, fun); |
+ m_pos++; |
+ } |
+ private: |
+ Handle<FixedArray> m_output; |
+ int m_pos; |
+ }; |
+}; |
+ |
+ |
// Check whether the code is natural function code (not a lazy-compile stub |
// code). |
static bool IsJSFunctionCode(Code* code) { |
@@ -1080,9 +1210,10 @@ MaybeObject* LiveEdit::ReplaceFunctionCode( |
Handle<JSArray> new_compile_info_array, |
Handle<JSArray> shared_info_array) { |
HandleScope scope; |
+ Isolate* isolate = Isolate::Current(); |
if (!SharedInfoWrapper::IsInstance(shared_info_array)) { |
- return Isolate::Current()->ThrowIllegalOperation(); |
+ return isolate->ThrowIllegalOperation(); |
} |
FunctionInfoWrapper compile_info_wrapper(new_compile_info_array); |
@@ -1113,6 +1244,8 @@ MaybeObject* LiveEdit::ReplaceFunctionCode( |
shared_info->set_start_position(start_position); |
shared_info->set_end_position(end_position); |
+ LiteralFixer::PatchLiterals(&compile_info_wrapper, shared_info, isolate); |
+ |
shared_info->set_construct_stub( |
Isolate::Current()->builtins()->builtin( |
Builtins::kJSConstructStubGeneric)); |