| Index: src/runtime.cc
|
| ===================================================================
|
| --- src/runtime.cc (revision 3964)
|
| +++ src/runtime.cc (working copy)
|
| @@ -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,19 +4085,84 @@
|
| }
|
|
|
|
|
| -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()) {
|
| // Retry with correct length.
|
| @@ -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,23 +4839,11 @@
|
| 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);
|
| }
|
|
|
|
|
| @@ -4818,7 +5030,23 @@
|
| }
|
| }
|
|
|
| +// 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);
|
| @@ -4862,6 +5090,58 @@
|
| }
|
|
|
|
|
| +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);
|
| @@ -5463,6 +5743,7 @@
|
| }
|
| args[0]->Print();
|
| if (args[0]->IsHeapObject()) {
|
| + PrintF("\n");
|
| HeapObject::cast(args[0])->map()->Print();
|
| }
|
| #else
|
| @@ -8011,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
|
|
|