Push version 2.1.3 to trunk.
Added API method for context-disposal notifications.
Added API method for accessing elements by integer index.
Added missing implementation of Uint32::Value and Value::IsUint32 API methods.
Added IsExecutionTerminating API method.
Disabled strict aliasing for GCC 4.4.
Fixed string-concatenation bug (issue 636).
Performance improvements on all platforms.
git-svn-id: http://v8.googlecode.com/svn/trunk@4079 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/src/runtime.cc b/src/runtime.cc
index 6459aa7..6d3a158 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -38,6 +38,7 @@
#include "debug.h"
#include "execution.h"
#include "jsregexp.h"
+#include "liveedit.h"
#include "parser.h"
#include "platform.h"
#include "runtime.h"
@@ -1230,6 +1231,17 @@
}
+static Object* Runtime_FinishArrayPrototypeSetup(Arguments args) {
+ HandleScope scope;
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSArray, prototype, 0);
+ // This is necessary to enable fast checks for absence of elements
+ // on Array.prototype and below.
+ prototype->set_elements(Heap::empty_fixed_array());
+ return Smi::FromInt(0);
+}
+
+
static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
HandleScope scope;
ASSERT(args.length() == 4);
@@ -2608,8 +2620,8 @@
int d = str1->Get(0) - str2->Get(0);
if (d != 0) return Smi::FromInt(d);
- str1->TryFlattenIfNotFlat();
- str2->TryFlattenIfNotFlat();
+ str1->TryFlatten();
+ str2->TryFlatten();
static StringInputBuffer buf1;
static StringInputBuffer buf2;
@@ -2818,7 +2830,7 @@
// string->Get(index).
static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
if (index < static_cast<uint32_t>(string->length())) {
- string->TryFlattenIfNotFlat();
+ string->TryFlatten();
return LookupSingleCharacterStringFromCode(
string->Get(index));
}
@@ -2846,6 +2858,11 @@
return prototype->GetElement(index);
}
+ return GetElement(object, index);
+}
+
+
+Object* Runtime::GetElement(Handle<Object> object, uint32_t index) {
return object->GetElement(index);
}
@@ -3072,7 +3089,7 @@
result = SetElement(js_object, index, value);
} else {
Handle<String> key_string = Handle<String>::cast(key);
- key_string->TryFlattenIfNotFlat();
+ key_string->TryFlatten();
result = SetProperty(js_object, key_string, value, attr);
}
if (result.is_null()) return Failure::Exception();
@@ -3121,7 +3138,7 @@
return js_object->SetElement(index, *value);
} else {
Handle<String> key_string = Handle<String>::cast(key);
- key_string->TryFlattenIfNotFlat();
+ key_string->TryFlatten();
return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
*value,
attr);
@@ -3173,7 +3190,7 @@
key_string = Handle<String>::cast(converted);
}
- key_string->TryFlattenIfNotFlat();
+ key_string->TryFlatten();
return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
}
@@ -3669,7 +3686,7 @@
NoHandleAllocation ha;
ASSERT(args.length() == 1);
CONVERT_CHECKED(String, subject, args[0]);
- subject->TryFlattenIfNotFlat();
+ subject->TryFlatten();
return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
}
@@ -3751,7 +3768,7 @@
ASSERT(args.length() == 1);
CONVERT_CHECKED(String, source, args[0]);
- source->TryFlattenIfNotFlat();
+ source->TryFlatten();
int escaped_length = 0;
int length = source->length();
@@ -3864,7 +3881,7 @@
ASSERT(args.length() == 1);
CONVERT_CHECKED(String, source, args[0]);
- source->TryFlattenIfNotFlat();
+ source->TryFlatten();
bool ascii = true;
int length = source->length();
@@ -3904,7 +3921,7 @@
CONVERT_CHECKED(String, s, args[0]);
CONVERT_SMI_CHECKED(radix, args[1]);
- s->TryFlattenIfNotFlat();
+ s->TryFlatten();
int len = s->length();
int i;
@@ -4068,18 +4085,83 @@
}
-template <class Converter>
-static Object* ConvertCase(Arguments args,
- unibrow::Mapping<Converter, 128>* mapping) {
+static inline SeqAsciiString* TryGetSeqAsciiString(String* s) {
+ if (!s->IsFlat() || !s->IsAsciiRepresentation()) return NULL;
+ if (s->IsConsString()) {
+ ASSERT(ConsString::cast(s)->second()->length() == 0);
+ return SeqAsciiString::cast(ConsString::cast(s)->first());
+ }
+ return SeqAsciiString::cast(s);
+}
+
+
+namespace {
+
+struct ToLowerTraits {
+ typedef unibrow::ToLowercase UnibrowConverter;
+
+ static bool ConvertAscii(char* dst, char* src, int length) {
+ bool changed = false;
+ for (int i = 0; i < length; ++i) {
+ char c = src[i];
+ if ('A' <= c && c <= 'Z') {
+ c += ('a' - 'A');
+ changed = true;
+ }
+ dst[i] = c;
+ }
+ return changed;
+ }
+};
+
+
+struct ToUpperTraits {
+ typedef unibrow::ToUppercase UnibrowConverter;
+
+ static bool ConvertAscii(char* dst, char* src, int length) {
+ bool changed = false;
+ for (int i = 0; i < length; ++i) {
+ char c = src[i];
+ if ('a' <= c && c <= 'z') {
+ c -= ('a' - 'A');
+ changed = true;
+ }
+ dst[i] = c;
+ }
+ return changed;
+ }
+};
+
+} // namespace
+
+
+template <typename ConvertTraits>
+static Object* ConvertCase(
+ Arguments args,
+ unibrow::Mapping<typename ConvertTraits::UnibrowConverter, 128>* mapping) {
NoHandleAllocation ha;
-
CONVERT_CHECKED(String, s, args[0]);
- s->TryFlattenIfNotFlat();
+ s->TryFlatten();
- int input_string_length = s->length();
+ const int length = s->length();
// Assume that the string is not empty; we need this assumption later
- if (input_string_length == 0) return s;
- int length = input_string_length;
+ if (length == 0) return s;
+
+ // Simpler handling of ascii strings.
+ //
+ // NOTE: This assumes that the upper/lower case of an ascii
+ // character is also ascii. This is currently the case, but it
+ // might break in the future if we implement more context and locale
+ // dependent upper/lower conversions.
+ SeqAsciiString* seq_ascii = TryGetSeqAsciiString(s);
+ if (seq_ascii != NULL) {
+ Object* o = Heap::AllocateRawAsciiString(length);
+ if (o->IsFailure()) return o;
+ SeqAsciiString* result = SeqAsciiString::cast(o);
+ bool has_changed_character = ConvertTraits::ConvertAscii(
+ result->GetChars(), seq_ascii->GetChars(), length);
+ return has_changed_character ? result : s;
+ }
Object* answer = ConvertCaseHelper(s, length, length, mapping);
if (answer->IsSmi()) {
@@ -4091,18 +4173,20 @@
static Object* Runtime_StringToLowerCase(Arguments args) {
- return ConvertCase<unibrow::ToLowercase>(args, &to_lower_mapping);
+ return ConvertCase<ToLowerTraits>(args, &to_lower_mapping);
}
static Object* Runtime_StringToUpperCase(Arguments args) {
- return ConvertCase<unibrow::ToUppercase>(args, &to_upper_mapping);
+ return ConvertCase<ToUpperTraits>(args, &to_upper_mapping);
}
+
static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
return unibrow::WhiteSpace::Is(c) || c == 0x200b;
}
+
static Object* Runtime_StringTrim(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 3);
@@ -4111,7 +4195,7 @@
CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
- s->TryFlattenIfNotFlat();
+ s->TryFlatten();
int length = s->length();
int left = 0;
@@ -4130,6 +4214,82 @@
return s->SubString(left, right);
}
+
+// Copies ascii characters to the given fixed array looking up
+// one-char strings in the cache. Gives up on the first char that is
+// not in the cache and fills the remainder with smi zeros. Returns
+// the length of the successfully copied prefix.
+static int CopyCachedAsciiCharsToArray(const char* chars,
+ FixedArray* elements,
+ int length) {
+ AssertNoAllocation nogc;
+ FixedArray* ascii_cache = Heap::single_character_string_cache();
+ Object* undefined = Heap::undefined_value();
+ int i;
+ for (i = 0; i < length; ++i) {
+ Object* value = ascii_cache->get(chars[i]);
+ if (value == undefined) break;
+ ASSERT(!Heap::InNewSpace(value));
+ elements->set(i, value, SKIP_WRITE_BARRIER);
+ }
+ if (i < length) {
+ ASSERT(Smi::FromInt(0) == 0);
+ memset(elements->data_start() + i, 0, kPointerSize * (length - i));
+ }
+#ifdef DEBUG
+ for (int j = 0; j < length; ++j) {
+ Object* element = elements->get(j);
+ ASSERT(element == Smi::FromInt(0) ||
+ (element->IsString() && String::cast(element)->LooksValid()));
+ }
+#endif
+ return i;
+}
+
+
+// Converts a String to JSArray.
+// For example, "foo" => ["f", "o", "o"].
+static Object* Runtime_StringToArray(Arguments args) {
+ HandleScope scope;
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(String, s, 0);
+
+ s->TryFlatten();
+ const int length = s->length();
+
+ Handle<FixedArray> elements;
+ if (s->IsFlat() && s->IsAsciiRepresentation()) {
+ Object* obj = Heap::AllocateUninitializedFixedArray(length);
+ if (obj->IsFailure()) return obj;
+ elements = Handle<FixedArray>(FixedArray::cast(obj));
+
+ Vector<const char> chars = s->ToAsciiVector();
+ // Note, this will initialize all elements (not only the prefix)
+ // to prevent GC from seeing partially initialized array.
+ int num_copied_from_cache = CopyCachedAsciiCharsToArray(chars.start(),
+ *elements,
+ length);
+
+ for (int i = num_copied_from_cache; i < length; ++i) {
+ elements->set(i, *LookupSingleCharacterStringFromCode(chars[i]));
+ }
+ } else {
+ elements = Factory::NewFixedArray(length);
+ for (int i = 0; i < length; ++i) {
+ elements->set(i, *LookupSingleCharacterStringFromCode(s->Get(i)));
+ }
+ }
+
+#ifdef DEBUG
+ for (int i = 0; i < length; ++i) {
+ ASSERT(String::cast(elements->get(i))->length() == 1);
+ }
+#endif
+
+ return *Factory::NewJSArrayWithElements(elements);
+}
+
+
bool Runtime::IsUpperCaseChar(uint16_t ch) {
unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
int char_length = to_upper_mapping.get(ch, 0, chars);
@@ -4152,9 +4312,12 @@
NoHandleAllocation ha;
ASSERT(args.length() == 1);
- Object* obj = args[0];
- if (obj->IsSmi()) return obj;
- CONVERT_DOUBLE_CHECKED(number, obj);
+ CONVERT_DOUBLE_CHECKED(number, args[0]);
+
+ // We do not include 0 so that we don't have to treat +0 / -0 cases.
+ if (number > 0 && number <= Smi::kMaxValue) {
+ return Smi::FromInt(static_cast<int>(number));
+ }
return Heap::NumberFromDouble(DoubleToInteger(number));
}
@@ -4163,9 +4326,7 @@
NoHandleAllocation ha;
ASSERT(args.length() == 1);
- Object* obj = args[0];
- if (obj->IsSmi() && Smi::cast(obj)->value() >= 0) return obj;
- CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, obj);
+ CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, args[0]);
return Heap::NumberFromUint32(number);
}
@@ -4174,9 +4335,12 @@
NoHandleAllocation ha;
ASSERT(args.length() == 1);
- Object* obj = args[0];
- if (obj->IsSmi()) return obj;
- CONVERT_DOUBLE_CHECKED(number, obj);
+ CONVERT_DOUBLE_CHECKED(number, args[0]);
+
+ // We do not include 0 so that we don't have to treat +0 / -0 cases.
+ if (number > 0 && number <= Smi::kMaxValue) {
+ return Smi::FromInt(static_cast<int>(number));
+ }
return Heap::NumberFromInt32(DoubleToInt32(number));
}
@@ -4593,6 +4757,66 @@
}
+static Object* StringInputBufferCompare(String* x, String* y) {
+ static StringInputBuffer bufx;
+ static StringInputBuffer bufy;
+ bufx.Reset(x);
+ bufy.Reset(y);
+ while (bufx.has_more() && bufy.has_more()) {
+ int d = bufx.GetNext() - bufy.GetNext();
+ if (d < 0) return Smi::FromInt(LESS);
+ else if (d > 0) return Smi::FromInt(GREATER);
+ }
+
+ // x is (non-trivial) prefix of y:
+ if (bufy.has_more()) return Smi::FromInt(LESS);
+ // y is prefix of x:
+ return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
+}
+
+
+static Object* FlatStringCompare(String* x, String* y) {
+ ASSERT(x->IsFlat());
+ ASSERT(y->IsFlat());
+ Object* equal_prefix_result = Smi::FromInt(EQUAL);
+ int prefix_length = x->length();
+ if (y->length() < prefix_length) {
+ prefix_length = y->length();
+ equal_prefix_result = Smi::FromInt(GREATER);
+ } else if (y->length() > prefix_length) {
+ equal_prefix_result = Smi::FromInt(LESS);
+ }
+ int r;
+ if (x->IsAsciiRepresentation()) {
+ Vector<const char> x_chars = x->ToAsciiVector();
+ if (y->IsAsciiRepresentation()) {
+ Vector<const char> y_chars = y->ToAsciiVector();
+ r = memcmp(x_chars.start(), y_chars.start(), prefix_length);
+ } else {
+ Vector<const uc16> y_chars = y->ToUC16Vector();
+ r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
+ }
+ } else {
+ Vector<const uc16> x_chars = x->ToUC16Vector();
+ if (y->IsAsciiRepresentation()) {
+ Vector<const char> y_chars = y->ToAsciiVector();
+ r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
+ } else {
+ Vector<const uc16> y_chars = y->ToUC16Vector();
+ r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
+ }
+ }
+ Object* result;
+ if (r == 0) {
+ result = equal_prefix_result;
+ } else {
+ result = (r < 0) ? Smi::FromInt(LESS) : Smi::FromInt(GREATER);
+ }
+ ASSERT(result == StringInputBufferCompare(x, y));
+ return result;
+}
+
+
static Object* Runtime_StringCompare(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -4615,29 +4839,18 @@
if (d < 0) return Smi::FromInt(LESS);
else if (d > 0) return Smi::FromInt(GREATER);
- x->TryFlattenIfNotFlat();
- y->TryFlattenIfNotFlat();
+ x->TryFlatten();
+ y->TryFlatten();
- static StringInputBuffer bufx;
- static StringInputBuffer bufy;
- bufx.Reset(x);
- bufy.Reset(y);
- while (bufx.has_more() && bufy.has_more()) {
- int d = bufx.GetNext() - bufy.GetNext();
- if (d < 0) return Smi::FromInt(LESS);
- else if (d > 0) return Smi::FromInt(GREATER);
- }
-
- // x is (non-trivial) prefix of y:
- if (bufy.has_more()) return Smi::FromInt(LESS);
- // y is prefix of x:
- return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
+ return (x->IsFlat() && y->IsFlat()) ? FlatStringCompare(x, y)
+ : StringInputBufferCompare(x, y);
}
static Object* Runtime_Math_abs(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_abs.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return Heap::AllocateHeapNumber(fabs(x));
@@ -4647,6 +4860,7 @@
static Object* Runtime_Math_acos(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_acos.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
@@ -4656,6 +4870,7 @@
static Object* Runtime_Math_asin(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_asin.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
@@ -4665,6 +4880,7 @@
static Object* Runtime_Math_atan(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_atan.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
@@ -4674,6 +4890,7 @@
static Object* Runtime_Math_atan2(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
+ Counters::math_atan2.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
CONVERT_DOUBLE_CHECKED(y, args[1]);
@@ -4697,6 +4914,7 @@
static Object* Runtime_Math_ceil(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_ceil.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return Heap::NumberFromDouble(ceiling(x));
@@ -4706,6 +4924,7 @@
static Object* Runtime_Math_cos(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_cos.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return TranscendentalCache::Get(TranscendentalCache::COS, x);
@@ -4715,6 +4934,7 @@
static Object* Runtime_Math_exp(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_exp.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return TranscendentalCache::Get(TranscendentalCache::EXP, x);
@@ -4724,6 +4944,7 @@
static Object* Runtime_Math_floor(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_floor.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return Heap::NumberFromDouble(floor(x));
@@ -4733,6 +4954,7 @@
static Object* Runtime_Math_log(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_log.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return TranscendentalCache::Get(TranscendentalCache::LOG, x);
@@ -4773,6 +4995,7 @@
static Object* Runtime_Math_pow(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
+ Counters::math_pow.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
@@ -4807,10 +5030,27 @@
}
}
+// Fast version of Math.pow if we know that y is not an integer and
+// y is not -0.5 or 0.5. Used as slowcase from codegen.
+static Object* Runtime_Math_pow_cfunction(Arguments args) {
+ NoHandleAllocation ha;
+ ASSERT(args.length() == 2);
+ CONVERT_DOUBLE_CHECKED(x, args[0]);
+ CONVERT_DOUBLE_CHECKED(y, args[1]);
+ if (y == 0) {
+ return Smi::FromInt(1);
+ } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
+ return Heap::nan_value();
+ } else {
+ return Heap::AllocateHeapNumber(pow(x, y));
+ }
+}
+
static Object* Runtime_Math_round(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_round.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
@@ -4823,6 +5063,7 @@
static Object* Runtime_Math_sin(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_sin.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return TranscendentalCache::Get(TranscendentalCache::SIN, x);
@@ -4832,6 +5073,7 @@
static Object* Runtime_Math_sqrt(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_sqrt.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return Heap::AllocateHeapNumber(sqrt(x));
@@ -4841,12 +5083,65 @@
static Object* Runtime_Math_tan(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_tan.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return TranscendentalCache::Get(TranscendentalCache::TAN, x);
}
+static Object* Runtime_DateMakeDay(Arguments args) {
+ NoHandleAllocation ha;
+ ASSERT(args.length() == 3);
+
+ CONVERT_SMI_CHECKED(year, args[0]);
+ CONVERT_SMI_CHECKED(month, args[1]);
+ CONVERT_SMI_CHECKED(date, args[2]);
+
+ static const int day_from_month[] = {0, 31, 59, 90, 120, 151,
+ 181, 212, 243, 273, 304, 334};
+ static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152,
+ 182, 213, 244, 274, 305, 335};
+
+ year += month / 12;
+ month %= 12;
+ if (month < 0) {
+ year--;
+ month += 12;
+ }
+
+ ASSERT(month >= 0);
+ ASSERT(month < 12);
+
+ // year_delta is an arbitrary number such that:
+ // a) year_delta = -1 (mod 400)
+ // b) year + year_delta > 0 for years in the range defined by
+ // ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
+ // Jan 1 1970. This is required so that we don't run into integer
+ // division of negative numbers.
+ // c) there shouldn't be overflow for 32-bit integers in the following
+ // operations.
+ static const int year_delta = 399999;
+ static const int base_day = 365 * (1970 + year_delta) +
+ (1970 + year_delta) / 4 -
+ (1970 + year_delta) / 100 +
+ (1970 + year_delta) / 400;
+
+ int year1 = year + year_delta;
+ int day_from_year = 365 * year1 +
+ year1 / 4 -
+ year1 / 100 +
+ year1 / 400 -
+ base_day;
+
+ if (year % 4 || (year % 100 == 0 && year % 400 != 0)) {
+ return Smi::FromInt(day_from_year + day_from_month[month] + date - 1);
+ }
+
+ return Smi::FromInt(day_from_year + day_from_month_leap[month] + date - 1);
+}
+
+
static Object* Runtime_NewArgumentsFast(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 3);
@@ -5448,6 +5743,7 @@
}
args[0]->Print();
if (args[0]->IsHeapObject()) {
+ PrintF("\n");
HeapObject::cast(args[0])->map()->Print();
}
#else
@@ -7996,6 +8292,195 @@
return f->shared()->inferred_name();
}
+
+static int FindSharedFunctionInfosForScript(Script* script,
+ FixedArray* buffer) {
+ AssertNoAllocation no_allocations;
+
+ int counter = 0;
+ int buffer_size = buffer->length();
+ HeapIterator iterator;
+ for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
+ ASSERT(obj != NULL);
+ if (!obj->IsSharedFunctionInfo()) {
+ continue;
+ }
+ SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
+ if (shared->script() != script) {
+ continue;
+ }
+ if (counter < buffer_size) {
+ buffer->set(counter, shared);
+ }
+ counter++;
+ }
+ return counter;
+}
+
+// For a script finds all SharedFunctionInfo's in the heap that points
+// to this script. Returns JSArray of SharedFunctionInfo wrapped
+// in OpaqueReferences.
+static Object* Runtime_LiveEditFindSharedFunctionInfosForScript(
+ Arguments args) {
+ ASSERT(args.length() == 1);
+ HandleScope scope;
+ CONVERT_CHECKED(JSValue, script_value, args[0]);
+
+ Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
+
+ const int kBufferSize = 32;
+
+ Handle<FixedArray> array;
+ array = Factory::NewFixedArray(kBufferSize);
+ int number = FindSharedFunctionInfosForScript(*script, *array);
+ if (number > kBufferSize) {
+ array = Factory::NewFixedArray(number);
+ FindSharedFunctionInfosForScript(*script, *array);
+ }
+
+ Handle<JSArray> result = Factory::NewJSArrayWithElements(array);
+ result->set_length(Smi::FromInt(number));
+
+ LiveEdit::WrapSharedFunctionInfos(result);
+
+ return *result;
+}
+
+// For a script calculates compilation information about all its functions.
+// The script source is explicitly specified by the second argument.
+// The source of the actual script is not used, however it is important that
+// all generated code keeps references to this particular instance of script.
+// Returns a JSArray of compilation infos. The array is ordered so that
+// each function with all its descendant is always stored in a continues range
+// with the function itself going first. The root function is a script function.
+static Object* Runtime_LiveEditGatherCompileInfo(Arguments args) {
+ ASSERT(args.length() == 2);
+ HandleScope scope;
+ CONVERT_CHECKED(JSValue, script, args[0]);
+ CONVERT_ARG_CHECKED(String, source, 1);
+ Handle<Script> script_handle = Handle<Script>(Script::cast(script->value()));
+
+ JSArray* result = LiveEdit::GatherCompileInfo(script_handle, source);
+
+ if (Top::has_pending_exception()) {
+ return Failure::Exception();
+ }
+
+ return result;
+}
+
+// Changes the source of the script to a new_source and creates a new
+// script representing the old version of the script source.
+static Object* Runtime_LiveEditReplaceScript(Arguments args) {
+ ASSERT(args.length() == 3);
+ HandleScope scope;
+ CONVERT_CHECKED(JSValue, original_script_value, args[0]);
+ CONVERT_ARG_CHECKED(String, new_source, 1);
+ CONVERT_ARG_CHECKED(String, old_script_name, 2);
+ Handle<Script> original_script =
+ Handle<Script>(Script::cast(original_script_value->value()));
+
+ Handle<String> original_source(String::cast(original_script->source()));
+
+ original_script->set_source(*new_source);
+ Handle<Script> old_script = Factory::NewScript(original_source);
+ old_script->set_name(*old_script_name);
+ old_script->set_line_offset(original_script->line_offset());
+ old_script->set_column_offset(original_script->column_offset());
+ old_script->set_data(original_script->data());
+ old_script->set_type(original_script->type());
+ old_script->set_context_data(original_script->context_data());
+ old_script->set_compilation_type(original_script->compilation_type());
+ old_script->set_eval_from_shared(original_script->eval_from_shared());
+ old_script->set_eval_from_instructions_offset(
+ original_script->eval_from_instructions_offset());
+
+
+ Debugger::OnAfterCompile(old_script, Debugger::SEND_WHEN_DEBUGGING);
+
+ return *(GetScriptWrapper(old_script));
+}
+
+// Replaces code of SharedFunctionInfo with a new one.
+static Object* Runtime_LiveEditReplaceFunctionCode(Arguments args) {
+ ASSERT(args.length() == 2);
+ HandleScope scope;
+ CONVERT_ARG_CHECKED(JSArray, new_compile_info, 0);
+ CONVERT_ARG_CHECKED(JSArray, shared_info, 1);
+
+ LiveEdit::ReplaceFunctionCode(new_compile_info, shared_info);
+
+ return Heap::undefined_value();
+}
+
+// Connects SharedFunctionInfo to another script.
+static Object* Runtime_LiveEditRelinkFunctionToScript(Arguments args) {
+ ASSERT(args.length() == 2);
+ HandleScope scope;
+ CONVERT_ARG_CHECKED(JSArray, shared_info_array, 0);
+ CONVERT_ARG_CHECKED(JSValue, script_value, 1);
+ Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
+
+ LiveEdit::RelinkFunctionToScript(shared_info_array, script);
+
+ return Heap::undefined_value();
+}
+
+// Updates positions of a shared function info (first parameter) according
+// to script source change. Text change is described in second parameter 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.
+static Object* Runtime_LiveEditPatchFunctionPositions(Arguments args) {
+ ASSERT(args.length() == 2);
+ HandleScope scope;
+ CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
+ CONVERT_ARG_CHECKED(JSArray, position_change_array, 1);
+
+ LiveEdit::PatchFunctionPositions(shared_array, position_change_array);
+
+ return Heap::undefined_value();
+}
+
+
+static LiveEdit::FunctionPatchabilityStatus FindFunctionCodeOnStacks(
+ Handle<SharedFunctionInfo> shared) {
+ // TODO(635): check all threads, not only the current one.
+ for (StackFrameIterator it; !it.done(); it.Advance()) {
+ StackFrame* frame = it.frame();
+ if (frame->code() == shared->code()) {
+ return LiveEdit::FUNCTION_BLOCKED_ON_STACK;
+ }
+ }
+ return LiveEdit::FUNCTION_AVAILABLE_FOR_PATCH;
+}
+
+// For array of SharedFunctionInfo's (each wrapped in JSValue)
+// checks that none of them have activations on stacks (of any thread).
+// Returns array of the same length with corresponding results of
+// LiveEdit::FunctionPatchabilityStatus type.
+static Object* Runtime_LiveEditCheckStackActivations(Arguments args) {
+ ASSERT(args.length() == 1);
+ HandleScope scope;
+ CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
+
+
+ int len = Smi::cast(shared_array->length())->value();
+ Handle<JSArray> result = Factory::NewJSArray(len);
+
+ for (int i = 0; i < len; i++) {
+ JSValue* wrapper = JSValue::cast(shared_array->GetElement(i));
+ Handle<SharedFunctionInfo> shared(
+ SharedFunctionInfo::cast(wrapper->value()));
+ LiveEdit::FunctionPatchabilityStatus check_res =
+ FindFunctionCodeOnStacks(shared);
+ SetElement(result, i, Handle<Smi>(Smi::FromInt(check_res)));
+ }
+
+ return *result;
+}
+
+
#endif // ENABLE_DEBUGGER_SUPPORT
#ifdef ENABLE_LOGGING_AND_PROFILING