Improved string hash-code distribution by excluding bit-field bits from the hash-code.
Changed string search algorithm used in indexOf from KMP to Boyer-Moore.
Improved the generated code for the instanceof operator.
Improved performance of slow-case string equality checks by specializing the code based on the string representation.
Improve the handling of out-of-memory situations (issue 70).
Improved performance of strict equality checks.
Improved profiler output to make it easier to see anonymous functions.
Improved performance of slow-case keyed loads.
Improved property access performance by allocating a number of properties in the front object.
Changed the toString behavior on the built-in object constructors to print [native code] instead of the actual source. Some web applications do not like constructors with complex toString results.
git-svn-id: http://v8.googlecode.com/svn/trunk@511 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/src/runtime.cc b/src/runtime.cc
index 25ac6f3..8e329b0 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -42,6 +42,7 @@
#include "runtime.h"
#include "scopeinfo.h"
#include "v8threads.h"
+#include "smart-pointer.h"
namespace v8 { namespace internal {
@@ -220,6 +221,30 @@
return JSObject::cast(obj)->class_name();
}
+inline static Object* IsSpecificClassOf(Arguments args, String* name) {
+ NoHandleAllocation ha;
+ ASSERT(args.length() == 1);
+ Object* obj = args[0];
+ if (obj->IsJSObject() && (JSObject::cast(obj)->class_name() == name)) {
+ return Heap::true_value();
+ }
+ return Heap::false_value();
+}
+
+static Object* Runtime_IsStringClass(Arguments args) {
+ return IsSpecificClassOf(args, Heap::String_symbol());
+}
+
+
+static Object* Runtime_IsDateClass(Arguments args) {
+ return IsSpecificClassOf(args, Heap::Date_symbol());
+}
+
+
+static Object* Runtime_IsArrayClass(Arguments args) {
+ return IsSpecificClassOf(args, Heap::Array_symbol());
+}
+
static Object* Runtime_IsInPrototypeChain(Arguments args) {
NoHandleAllocation ha;
@@ -402,22 +427,21 @@
IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
}
}
- // Done.
+
return Heap::undefined_value();
}
static Object* Runtime_DeclareContextSlot(Arguments args) {
HandleScope scope;
- ASSERT(args.length() == 5);
+ ASSERT(args.length() == 4);
- // args[0] is result (TOS)
- CONVERT_ARG_CHECKED(Context, context, 1);
- Handle<String> name(String::cast(args[2]));
+ CONVERT_ARG_CHECKED(Context, context, 0);
+ Handle<String> name(String::cast(args[1]));
PropertyAttributes mode =
- static_cast<PropertyAttributes>(Smi::cast(args[3])->value());
+ static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
ASSERT(mode == READ_ONLY || mode == NONE);
- Handle<Object> initial_value(args[4]);
+ Handle<Object> initial_value(args[3]);
// Declarations are always done in the function context.
context = Handle<Context>(context->fcontext());
@@ -456,32 +480,35 @@
SetProperty(context_ext, name, initial_value, mode);
}
}
- return args[0]; // return TOS
- }
- // The property is not in the function context. It needs to be "declared"
- // in the function context's extension context, or in the global context.
- Handle<JSObject> context_ext;
- if (context->extension() != NULL) {
- // The function context's extension context exists - use it.
- context_ext = Handle<JSObject>(context->extension());
} else {
- // The function context's extension context does not exists - allocate it.
- context_ext = Factory::NewJSObject(Top::context_extension_function());
- // And store it in the extension slot.
- context->set_extension(*context_ext);
- }
- ASSERT(*context_ext != NULL);
+ // The property is not in the function context. It needs to be
+ // "declared" in the function context's extension context, or in the
+ // global context.
+ Handle<JSObject> context_ext;
+ if (context->extension() != NULL) {
+ // The function context's extension context exists - use it.
+ context_ext = Handle<JSObject>(context->extension());
+ } else {
+ // The function context's extension context does not exists - allocate
+ // it.
+ context_ext = Factory::NewJSObject(Top::context_extension_function());
+ // And store it in the extension slot.
+ context->set_extension(*context_ext);
+ }
+ ASSERT(*context_ext != NULL);
- // Declare the property by setting it to the initial value if provided,
- // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
- // constant declarations).
- ASSERT(!context_ext->HasLocalProperty(*name));
- Handle<Object> value(Heap::undefined_value());
- if (*initial_value != NULL) value = initial_value;
- SetProperty(context_ext, name, value, mode);
- ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
- return args[0]; // return TOS
+ // Declare the property by setting it to the initial value if provided,
+ // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
+ // constant declarations).
+ ASSERT(!context_ext->HasLocalProperty(*name));
+ Handle<Object> value(Heap::undefined_value());
+ if (*initial_value != NULL) value = initial_value;
+ SetProperty(context_ext, name, value, mode);
+ ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
+ }
+
+ return Heap::undefined_value();
}
@@ -864,10 +891,11 @@
target->shared()->set_length(fun->shared()->length());
target->shared()->set_formal_parameter_count(
fun->shared()->formal_parameter_count());
- // Set the source code of the target function.
- target->shared()->set_script(fun->shared()->script());
- target->shared()->set_start_position(fun->shared()->start_position());
- target->shared()->set_end_position(fun->shared()->end_position());
+ // Set the source code of the target function to undefined.
+ // SetCode is only used for built-in constructors like String,
+ // Array, and Object, and some web code
+ // doesn't like seeing source code for constructors.
+ target->shared()->set_script(Heap::undefined_value());
context = Handle<Context>(fun->context());
// Make sure we get a fresh copy of the literal vector to avoid
@@ -925,110 +953,320 @@
}
-static inline void ComputeKMPNextTable(String* pattern, int next_table[]) {
- int i = 0;
- int j = -1;
- next_table[0] = -1;
+// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
+// limit, we can fix the size of tables.
+static const int kBMMaxShift = 0xff;
+static const int kBMAlphabetSize = 0x100; // Reduce alphabet to this size.
- Access<StringInputBuffer> buffer(&string_input_buffer);
- buffer->Reset(pattern);
- int length = pattern->length();
- uint16_t p = buffer->GetNext();
- while (i < length - 1) {
- while (j > -1 && p != pattern->Get(j)) {
- j = next_table[j];
+// Holds the two buffers used by Boyer-Moore string search's Good Suffix
+// shift. Only allows the last kBMMaxShift characters of the needle
+// to be indexed.
+class BMGoodSuffixBuffers: public AllStatic {
+ public:
+ BMGoodSuffixBuffers() {}
+ inline void init(int needle_length) {
+ ASSERT(needle_length > 1);
+ int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
+ int len = needle_length - start;
+ biased_suffixes_ = suffixes_ - start;
+ biased_good_suffix_shift_ = good_suffix_shift_ - start;
+ for (int i = 0; i <= len; i++) {
+ good_suffix_shift_[i] = len;
}
- i++;
- j++;
- p = buffer->GetNext();
- if (p == pattern->Get(j)) {
- next_table[i] = next_table[j];
- } else {
- next_table[i] = j;
+ }
+ inline int& suffix(int index) {
+ ASSERT(biased_suffixes_ + index >= suffixes_);
+ return biased_suffixes_[index];
+ }
+ inline int& shift(int index) {
+ ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
+ return biased_good_suffix_shift_[index];
+ }
+ private:
+ int suffixes_[kBMMaxShift + 1];
+ int good_suffix_shift_[kBMMaxShift + 1];
+ int *biased_suffixes_;
+ int *biased_good_suffix_shift_;
+ DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
+};
+
+// buffers reused by BoyerMoore
+static int bad_char_occurence[kBMAlphabetSize];
+static BMGoodSuffixBuffers bmgs_buffers;
+
+// Compute the bad-char table for Boyer-Moore in the static buffer.
+// Return false if the pattern contains non-ASCII characters that cannot be
+// in the searched string.
+template <typename pchar>
+static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern,
+ int start) {
+ // Run forwards to populate bad_char_table, so that *last* instance
+ // of character equivalence class is the one registered.
+ // Notice: Doesn't include the last character.
+ for (int i = 0; i < kBMAlphabetSize; i++) {
+ bad_char_occurence[i] = start - 1;
+ }
+ for (int i = start; i < pattern.length(); i++) {
+ bad_char_occurence[pattern[i] % kBMAlphabetSize] = i;
+ }
+}
+
+template <typename pchar>
+static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern,
+ int start,
+ int len) {
+ int m = pattern.length();
+ // Compute Good Suffix tables.
+ bmgs_buffers.init(m);
+
+ bmgs_buffers.shift(m-1) = 1;
+ bmgs_buffers.suffix(m) = m + 1;
+ pchar last_char = pattern[m - 1];
+ int suffix = m + 1;
+ for (int i = m; i > start;) {
+ for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
+ if (bmgs_buffers.shift(suffix) == len) {
+ bmgs_buffers.shift(suffix) = suffix - i;
+ }
+ suffix = bmgs_buffers.suffix(suffix);
+ }
+ i--;
+ suffix--;
+ bmgs_buffers.suffix(i) = suffix;
+ if (suffix == m) {
+ // No suffix to extend, so we check against last_char only.
+ while (i > start && pattern[i - 1] != last_char) {
+ if (bmgs_buffers.shift(m) == len) {
+ bmgs_buffers.shift(m) = m - i;
+ }
+ i--;
+ bmgs_buffers.suffix(i) = m;
+ }
+ if (i > start) {
+ i--;
+ suffix--;
+ bmgs_buffers.suffix(i) = suffix;
+ }
+ }
+ }
+ if (suffix < m) {
+ for (int i = start; i <= m; i++) {
+ if (bmgs_buffers.shift(i) == len) {
+ bmgs_buffers.shift(i) = suffix - start;
+ }
+ if (i == suffix) {
+ suffix = bmgs_buffers.suffix(suffix);
+ }
}
}
}
+// Restricted Boyer-Moore string matching. Restricts tables to a
+// suffix of long pattern strings and handles only equivalence classes
+// of the full alphabet. This allows us to ensure that tables take only
+// a fixed amount of space.
+template <typename schar, typename pchar>
+static int BoyerMooreIndexOf(Vector<const schar> subject,
+ Vector<const pchar> pattern,
+ int start_index) {
+ int m = pattern.length();
+ int n = subject.length();
-int Runtime::StringMatchKmp(String* sub, String* pat, int start_index) {
- sub->TryFlatten();
- pat->TryFlatten();
+ // Only preprocess at most kBMMaxShift last characters of pattern.
+ int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
+ int len = m - start;
- int subject_length = sub->length();
+ BoyerMoorePopulateBadCharTable(pattern, start);
+
+ int badness = 0; // How bad we are doing without a good-suffix table.
+ int idx; // No matches found prior to this index.
+ // Perform search
+ for (idx = start_index; idx <= n - m;) {
+ int j = m - 1;
+ schar c;
+ while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
+ if (j < 0) {
+ return idx;
+ } else {
+ int bc_occ = bad_char_occurence[c % kBMAlphabetSize];
+ int shift = bc_occ < j ? j - bc_occ : 1;
+ idx += shift;
+ // Badness increases by the number of characters we have
+ // checked, and decreases by the number of characters we
+ // can skip by shifting. It's a measure of how we are doing
+ // compared to reading each character exactly once.
+ badness += (m - j) - shift;
+ if (badness > m) break;
+ }
+ }
+
+ // If we are not done, we got here because we should build the Good Suffix
+ // table and continue searching.
+ if (idx <= n - m) {
+ BoyerMoorePopulateGoodSuffixTable(pattern, start, len);
+ // Continue search from i.
+ do {
+ int j = m - 1;
+ schar c;
+ while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
+ if (j < 0) {
+ return idx;
+ } else if (j < start) {
+ // we have matched more than our tables allow us to be smart about.
+ idx += 1;
+ } else {
+ int gs_shift = bmgs_buffers.shift(j + 1);
+ int bc_occ = bad_char_occurence[c % kBMAlphabetSize];
+ int bc_shift = j - bc_occ;
+ idx += (gs_shift > bc_shift) ? gs_shift : bc_shift;
+ }
+ } while (idx <= n - m);
+ }
+
+ return -1;
+}
+
+template <typename schar, typename pchar>
+static int SingleCharIndexOf(Vector<const schar> string,
+ pchar pattern_char,
+ int start_index) {
+ for (int i = start_index, n = string.length(); i < n; i++) {
+ if (pattern_char == string[i]) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+// Trivial string search for shorter strings.
+// On return, if "complete" is set to true, the return value is the
+// final result of searching for the patter in the subject.
+// If "complete" is set to false, the return value is the index where
+// further checking should start, i.e., it's guaranteed that the pattern
+// does not occur at a position prior to the returned index.
+template <typename pchar, typename schar>
+static int SimpleIndexOf(Vector<const schar> subject,
+ Vector<const pchar> pattern,
+ int start_index,
+ bool &complete) {
+ int pattern_length = pattern.length();
+ int subject_length = subject.length();
+ // Badness is a count of how many extra times the same character
+ // is checked. We compare it to the index counter, so we start
+ // it at the start_index, and give it a little discount to avoid
+ // very early bail-outs.
+ int badness = start_index - pattern_length;
+ // We know our pattern is at least 2 characters, we cache the first so
+ // the common case of the first character not matching is faster.
+ pchar pattern_first_char = pattern[0];
+
+ for (int i = start_index, n = subject_length - pattern_length; i <= n; i++) {
+ if (subject[i] != pattern_first_char) continue;
+ int j = 1;
+ do {
+ if (pattern[j] != subject[i+j]) {
+ break;
+ }
+ j++;
+ } while (j < pattern_length);
+ if (j == pattern_length) {
+ complete = true;
+ return i;
+ }
+ badness += j;
+ if (badness > i) { // More than one extra character on average.
+ complete = false;
+ return (i + 1); // No matches up to index i+1.
+ }
+ }
+ complete = true;
+ return -1;
+}
+
+// Dispatch to different algorithms for different length of pattern/subject
+template <typename schar, typename pchar>
+static int StringMatchStrategy(Vector<const schar> sub,
+ Vector<const pchar> pat,
+ int start_index) {
+ ASSERT(pat.length() > 1);
+
+ // We have an ASCII haystack and a non-ASCII needle. Check if there
+ // really is a non-ASCII character in the needle and bail out if there
+ // is.
+ if (sizeof(pchar) > 1 && sizeof(schar) == 1) {
+ for (int i = 0; i < pat.length(); i++) {
+ uc16 c = pat[i];
+ if (c > String::kMaxAsciiCharCode) {
+ return -1;
+ }
+ }
+ }
+ // For small searches, a complex sort is not worth the setup overhead.
+ bool complete;
+ int idx = SimpleIndexOf(sub, pat, start_index, complete);
+ if (complete) return idx;
+ return BoyerMooreIndexOf(sub, pat, idx);
+}
+
+// Perform string match of pattern on subject, starting at start index.
+// Caller must ensure that 0 <= start_index <= sub->length(),
+// and should check that pat->length() + start_index <= sub->length()
+int Runtime::StringMatch(Handle<String> sub,
+ Handle<String> pat,
+ int start_index) {
+ ASSERT(0 <= start_index);
+ ASSERT(start_index <= sub->length());
+
int pattern_length = pat->length();
-
- if (start_index > subject_length) return -1;
if (pattern_length == 0) return start_index;
+ int subject_length = sub->length();
+ if (start_index + pattern_length > subject_length) return -1;
+
+ FlattenString(sub);
// Searching for one specific character is common. For one
- // character patterns the KMP algorithm is guaranteed to slow down
- // the search, so we just run through the subject string.
+ // character patterns linear search is necessary, so any smart
+ // algorithm is unnecessary overhead.
if (pattern_length == 1) {
- uint16_t pattern_char = pat->Get(0);
- for (int i = start_index; i < subject_length; i++) {
- if (sub->Get(i) == pattern_char) {
- return i;
- }
+ AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
+ if (sub->is_ascii_representation()) {
+ return SingleCharIndexOf(sub->ToAsciiVector(), pat->Get(0), start_index);
}
- return -1;
+ return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
}
- // For small searches, KMP is not worth the setup overhead.
- if (subject_length < 100) {
- // We know our pattern is at least 2 characters, we cache the first so
- // the common case of the first character not matching is faster.
- uint16_t pattern_first_char = pat->Get(0);
- for (int i = start_index; i + pattern_length <= subject_length; i++) {
- if (sub->Get(i) != pattern_first_char) continue;
+ FlattenString(pat);
- for (int j = 1; j < pattern_length; j++) {
- if (pat->Get(j) != sub->Get(j + i)) break;
- if (j == pattern_length - 1) return i;
- }
+ AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
+ // dispatch on type of strings
+ if (pat->is_ascii_representation()) {
+ Vector<const char> pat_vector = pat->ToAsciiVector();
+ if (sub->is_ascii_representation()) {
+ return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
}
- return -1;
+ return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
}
-
- // For patterns with a larger length we use the KMP algorithm.
- //
- // Compute the 'next' table.
- int* next_table = NewArray<int>(pattern_length);
- ComputeKMPNextTable(pat, next_table);
- // Search using the 'next' table.
- int pattern_index = 0;
- // We would like to use StringInputBuffer here, but it does not have
- // the ability to start anywhere but the first character of a
- // string. It would be nice to have efficient forward-seeking
- // support on StringInputBuffers.
- int subject_index = start_index;
- while (subject_index < subject_length) {
- uint16_t subject_char = sub->Get(subject_index);
- while (pattern_index > -1 && pat->Get(pattern_index) != subject_char) {
- pattern_index = next_table[pattern_index];
- }
- pattern_index++;
- subject_index++;
- if (pattern_index >= pattern_length) {
- DeleteArray(next_table);
- return subject_index - pattern_index;
- }
+ Vector<const uc16> pat_vector = pat->ToUC16Vector();
+ if (sub->is_ascii_representation()) {
+ return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
}
- DeleteArray(next_table);
- return -1;
+ return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
}
static Object* Runtime_StringIndexOf(Arguments args) {
- NoHandleAllocation ha;
+ HandleScope scope; // create a new handle scope
ASSERT(args.length() == 3);
- CONVERT_CHECKED(String, sub, args[0]);
- CONVERT_CHECKED(String, pat, args[1]);
+ CONVERT_ARG_CHECKED(String, sub, 0);
+ CONVERT_ARG_CHECKED(String, pat, 1);
+
Object* index = args[2];
uint32_t start_index;
if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
- return Smi::FromInt(Runtime::StringMatchKmp(sub, pat, start_index));
+ int position = Runtime::StringMatch(sub, pat, start_index);
+ return Smi::FromInt(position);
}
@@ -1314,6 +1552,29 @@
}
+
+// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric
+static Object* Runtime_KeyedGetProperty(Arguments args) {
+ NoHandleAllocation ha;
+ ASSERT(args.length() == 2);
+
+ Object* receiver = args[0];
+ Object* key = args[1];
+ if (receiver->IsJSObject() &&
+ key->IsString() &&
+ !JSObject::cast(receiver)->HasFastProperties()) {
+ Dictionary* dictionary = JSObject::cast(receiver)->property_dictionary();
+ int entry = dictionary->FindStringEntry(String::cast(key));
+ if ((entry != DescriptorArray::kNotFound)
+ && (dictionary->DetailsAt(entry).type() == NORMAL)) {
+ return dictionary->ValueAt(entry);
+ }
+ }
+ return Runtime::GetObjectProperty(args.at<Object>(0),
+ args.at<Object>(1));
+}
+
+
Object* Runtime::SetObjectProperty(Handle<Object> object,
Handle<Object> key,
Handle<Object> value,
@@ -1954,7 +2215,7 @@
// 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.
- Object* o = s->IsAscii()
+ Object* o = s->IsAsciiRepresentation()
? Heap::AllocateRawAsciiString(length)
: Heap::AllocateRawTwoByteString(length);
if (o->IsFailure()) return o;
@@ -1970,7 +2231,8 @@
// We can assume that the string is not empty
uc32 current = buffer->GetNext();
while (i < length) {
- uc32 next = buffer->has_more() ? buffer->GetNext() : 0;
+ bool has_next = buffer->has_more();
+ uc32 next = has_next ? buffer->GetNext() : 0;
int char_length = mapping->get(current, next, chars);
if (char_length == 0) {
// The case conversion of this character is the character itself.
@@ -1994,12 +2256,21 @@
// "realloc" it and probably, in the vast majority of cases,
// extend the existing string to be able to hold the full
// result.
- int current_length = i + char_length + mapping->get(next, 0, chars);
+ int next_length = 0;
+ if (has_next) {
+ next_length = mapping->get(next, 0, chars);
+ if (next_length == 0) next_length = 1;
+ }
+ int current_length = i + char_length + next_length;
while (buffer->has_more()) {
current = buffer->GetNext();
+ // NOTE: we use 0 as the next character here because, while
+ // the next character may affect what a character converts to,
+ // it does not in any case affect the length of what it convert
+ // to.
int char_length = mapping->get(current, 0, chars);
if (char_length == 0) char_length = 1;
- current += char_length;
+ current_length += char_length;
}
length = current_length;
goto try_convert;
@@ -2231,7 +2502,7 @@
if (first->IsString()) return first;
}
- bool ascii = special->IsAscii();
+ bool ascii = special->IsAsciiRepresentation();
int position = 0;
for (int i = 0; i < array_length; i++) {
Object* elt = fixed_array->get(i);
@@ -2251,7 +2522,7 @@
return Failure::OutOfMemoryException();
}
position += element_length;
- if (ascii && !element->IsAscii()) {
+ if (ascii && !element->IsAsciiRepresentation()) {
ascii = false;
}
} else {
@@ -2395,8 +2666,12 @@
int len = x->length();
if (len != y->length()) return Smi::FromInt(NOT_EQUAL);
if (len == 0) return Smi::FromInt(EQUAL);
- // Fast case: First, middle and last characters.
+
+ // Handle one elment strings.
if (x->Get(0) != y->Get(0)) return Smi::FromInt(NOT_EQUAL);
+ if (len == 1) return Smi::FromInt(EQUAL);
+
+ // Fast case: First, middle and last characters.
if (x->Get(len>>1) != y->Get(len>>1)) return Smi::FromInt(NOT_EQUAL);
if (x->Get(len - 1) != y->Get(len - 1)) return Smi::FromInt(NOT_EQUAL);
@@ -2774,7 +3049,7 @@
if (Debug::StepInActive()) {
StackFrameIterator it;
it.Advance();
- ASSERT(InternalFrame::cast(it.frame())->is_construct_trampoline());
+ ASSERT(it.frame()->is_construct());
it.Advance();
if (it.frame()->fp() == Debug::step_in_fp()) {
HandleScope scope;
@@ -3704,7 +3979,7 @@
case FIELD:
value =
JSObject::cast(
- result->holder())->properties()->get(result->GetFieldIndex());
+ result->holder())->FastPropertyAt(result->GetFieldIndex());
if (value->IsTheHole()) {
return Heap::undefined_value();
}
@@ -4697,8 +4972,14 @@
// Convert the script objects to proper JS objects.
for (int i = 0; i < count; i++) {
- Handle<Script> script(Script::cast(instances->get(i)));
- instances->set(i, *GetScriptWrapper(script));
+ Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
+ // Get the script wrapper in a local handle before calling GetScriptWrapper,
+ // because using
+ // instances->set(i, *GetScriptWrapper(script))
+ // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
+ // already have deferenced the instances handle.
+ Handle<JSValue> wrapper = GetScriptWrapper(script);
+ instances->set(i, *wrapper);
}
// Return result as a JS array.