vendor/v8/src/runtime.cc in libv8-3.10.8.0 vs vendor/v8/src/runtime.cc in libv8-3.11.8.0

- old
+ new

@@ -206,12 +206,14 @@ // Deep copy local elements. // Pixel elements cannot be created using an object literal. ASSERT(!copy->HasExternalArrayElements()); switch (copy->GetElementsKind()) { - case FAST_SMI_ONLY_ELEMENTS: - case FAST_ELEMENTS: { + case FAST_SMI_ELEMENTS: + case FAST_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_ELEMENTS: { FixedArray* elements = FixedArray::cast(copy->elements()); if (elements->map() == heap->fixed_cow_array_map()) { isolate->counters()->cow_arrays_created_runtime()->Increment(); #ifdef DEBUG for (int i = 0; i < elements->length(); i++) { @@ -221,11 +223,11 @@ } else { for (int i = 0; i < elements->length(); i++) { Object* value = elements->get(i); ASSERT(value->IsSmi() || value->IsTheHole() || - (copy->GetElementsKind() == FAST_ELEMENTS)); + (IsFastObjectElementsKind(copy->GetElementsKind()))); if (value->IsJSObject()) { JSObject* js_object = JSObject::cast(value); { MaybeObject* maybe_result = DeepCopyBoilerplate(isolate, js_object); if (!maybe_result->ToObject(&result)) return maybe_result; @@ -266,10 +268,11 @@ case EXTERNAL_INT_ELEMENTS: case EXTERNAL_UNSIGNED_INT_ELEMENTS: case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: // No contained objects, nothing to do. break; } return copy; } @@ -450,11 +453,11 @@ } return isolate->ThrowIllegalOperation(); } -static const int kSmiOnlyLiteralMinimumLength = 1024; +static const int kSmiLiteralMinimumLength = 1024; Handle<Object> Runtime::CreateArrayLiteralBoilerplate( Isolate* isolate, Handle<FixedArray> literals, @@ -468,27 +471,26 @@ ElementsKind constant_elements_kind = static_cast<ElementsKind>(Smi::cast(elements->get(0))->value()); Handle<FixedArrayBase> constant_elements_values( FixedArrayBase::cast(elements->get(1))); + ASSERT(IsFastElementsKind(constant_elements_kind)); Context* global_context = isolate->context()->global_context(); - if (constant_elements_kind == FAST_SMI_ONLY_ELEMENTS) { - object->set_map(Map::cast(global_context->smi_js_array_map())); - } else if (constant_elements_kind == FAST_DOUBLE_ELEMENTS) { - object->set_map(Map::cast(global_context->double_js_array_map())); - } else { - object->set_map(Map::cast(global_context->object_js_array_map())); - } + Object* maybe_maps_array = global_context->js_array_maps(); + ASSERT(!maybe_maps_array->IsUndefined()); + Object* maybe_map = FixedArray::cast(maybe_maps_array)->get( + constant_elements_kind); + ASSERT(maybe_map->IsMap()); + object->set_map(Map::cast(maybe_map)); Handle<FixedArrayBase> copied_elements_values; - if (constant_elements_kind == FAST_DOUBLE_ELEMENTS) { + if (IsFastDoubleElementsKind(constant_elements_kind)) { ASSERT(FLAG_smi_only_arrays); copied_elements_values = isolate->factory()->CopyFixedDoubleArray( Handle<FixedDoubleArray>::cast(constant_elements_values)); } else { - ASSERT(constant_elements_kind == FAST_SMI_ONLY_ELEMENTS || - constant_elements_kind == FAST_ELEMENTS); + ASSERT(IsFastSmiOrObjectElementsKind(constant_elements_kind)); const bool is_cow = (constant_elements_values->map() == isolate->heap()->fixed_cow_array_map()); if (is_cow) { copied_elements_values = constant_elements_values; @@ -520,19 +522,26 @@ } } object->set_elements(*copied_elements_values); object->set_length(Smi::FromInt(copied_elements_values->length())); - // Ensure that the boilerplate object has FAST_ELEMENTS, unless the flag is + // Ensure that the boilerplate object has FAST_*_ELEMENTS, unless the flag is // on or the object is larger than the threshold. if (!FLAG_smi_only_arrays && - constant_elements_values->length() < kSmiOnlyLiteralMinimumLength) { - if (object->GetElementsKind() != FAST_ELEMENTS) { - CHECK(!TransitionElements(object, FAST_ELEMENTS, isolate)->IsFailure()); + constant_elements_values->length() < kSmiLiteralMinimumLength) { + ElementsKind elements_kind = object->GetElementsKind(); + if (!IsFastObjectElementsKind(elements_kind)) { + if (IsFastHoleyElementsKind(elements_kind)) { + CHECK(!TransitionElements(object, FAST_HOLEY_ELEMENTS, + isolate)->IsFailure()); + } else { + CHECK(!TransitionElements(object, FAST_ELEMENTS, isolate)->IsFailure()); + } } } + object->ValidateElements(); return object; } static Handle<Object> CreateLiteralBoilerplate( @@ -1728,11 +1737,11 @@ CONVERT_ARG_HANDLE_CHECKED(String, subject, 1); // Due to the way the JS calls are constructed this must be less than the // length of a string, i.e. it is always a Smi. We check anyway for security. CONVERT_SMI_ARG_CHECKED(index, 2); CONVERT_ARG_HANDLE_CHECKED(JSArray, last_match_info, 3); - RUNTIME_ASSERT(last_match_info->HasFastElements()); + RUNTIME_ASSERT(last_match_info->HasFastObjectElements()); RUNTIME_ASSERT(index >= 0); RUNTIME_ASSERT(index <= subject->length()); isolate->counters()->regexp_entry_runtime()->Increment(); Handle<Object> result = RegExpImpl::Exec(regexp, subject, @@ -3102,11 +3111,11 @@ // string and possibly suffix after last match. It is possible for // all components to use two elements when encoded as two smis. const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2); bool matched = true; do { - ASSERT(last_match_info_handle->HasFastElements()); + ASSERT(last_match_info_handle->HasFastObjectElements()); // Increase the capacity of the builder before entering local handle-scope, // so its internal buffer can safely allocate a new handle if it grows. builder.EnsureCapacity(parts_added_per_loop); HandleScope loop_scope(isolate); @@ -3199,11 +3208,11 @@ 0, last_match_info_handle); if (match.is_null()) return Failure::Exception(); if (match->IsNull()) return *subject_handle; - ASSERT(last_match_info_handle->HasFastElements()); + ASSERT(last_match_info_handle->HasFastObjectElements()); int start, end; { AssertNoAllocation match_info_array_is_not_in_a_handle; FixedArray* match_info_array = @@ -3273,11 +3282,11 @@ next, last_match_info_handle); if (match.is_null()) return Failure::Exception(); if (match->IsNull()) break; - ASSERT(last_match_info_handle->HasFastElements()); + ASSERT(last_match_info_handle->HasFastObjectElements()); HandleScope loop_scope(isolate); { AssertNoAllocation match_info_array_is_not_in_a_handle; FixedArray* match_info_array = FixedArray::cast(last_match_info_handle->elements()); @@ -3343,11 +3352,11 @@ } CONVERT_ARG_CHECKED(JSRegExp, regexp, 1); CONVERT_ARG_CHECKED(JSArray, last_match_info, 3); - ASSERT(last_match_info->HasFastElements()); + ASSERT(last_match_info->HasFastObjectElements()); if (replacement->length() == 0) { if (subject->HasOnlyAsciiChars()) { return StringReplaceRegExpWithEmptyString<SeqAsciiString>( isolate, subject, regexp, last_match_info); @@ -3793,66 +3802,77 @@ } return false; // No matches at all. } -static RegExpImpl::IrregexpResult SearchRegExpNoCaptureMultiple( +static int SearchRegExpNoCaptureMultiple( Isolate* isolate, Handle<String> subject, Handle<JSRegExp> regexp, Handle<JSArray> last_match_array, FixedArrayBuilder* builder) { ASSERT(subject->IsFlat()); + ASSERT(regexp->CaptureCount() == 0); int match_start = -1; int match_end = 0; int pos = 0; - int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject); - if (required_registers < 0) return RegExpImpl::RE_EXCEPTION; + int registers_per_match = RegExpImpl::IrregexpPrepare(regexp, subject); + if (registers_per_match < 0) return RegExpImpl::RE_EXCEPTION; - OffsetsVector registers(required_registers, isolate); + int max_matches; + int num_registers = RegExpImpl::GlobalOffsetsVectorSize(regexp, + registers_per_match, + &max_matches); + OffsetsVector registers(num_registers, isolate); Vector<int32_t> register_vector(registers.vector(), registers.length()); int subject_length = subject->length(); bool first = true; - for (;;) { // Break on failure, return on exception. - RegExpImpl::IrregexpResult result = - RegExpImpl::IrregexpExecOnce(regexp, - subject, - pos, - register_vector); - if (result == RegExpImpl::RE_SUCCESS) { - match_start = register_vector[0]; - builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch); - if (match_end < match_start) { - ReplacementStringBuilder::AddSubjectSlice(builder, - match_end, - match_start); + int num_matches = RegExpImpl::IrregexpExecRaw(regexp, + subject, + pos, + register_vector); + if (num_matches > 0) { + for (int match_index = 0; match_index < num_matches; match_index++) { + int32_t* current_match = &register_vector[match_index * 2]; + match_start = current_match[0]; + builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch); + if (match_end < match_start) { + ReplacementStringBuilder::AddSubjectSlice(builder, + match_end, + match_start); + } + match_end = current_match[1]; + HandleScope loop_scope(isolate); + if (!first) { + builder->Add(*isolate->factory()->NewProperSubString(subject, + match_start, + match_end)); + } else { + builder->Add(*isolate->factory()->NewSubString(subject, + match_start, + match_end)); + first = false; + } } - match_end = register_vector[1]; - HandleScope loop_scope(isolate); - if (!first) { - builder->Add(*isolate->factory()->NewProperSubString(subject, - match_start, - match_end)); - } else { - builder->Add(*isolate->factory()->NewSubString(subject, - match_start, - match_end)); - } + + // If we did not get the maximum number of matches, we can stop here + // since there are no matches left. + if (num_matches < max_matches) break; + if (match_start != match_end) { pos = match_end; } else { pos = match_end + 1; if (pos > subject_length) break; } - } else if (result == RegExpImpl::RE_FAILURE) { + } else if (num_matches == 0) { break; } else { - ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION); - return result; + ASSERT_EQ(num_matches, RegExpImpl::RE_EXCEPTION); + return RegExpImpl::RE_EXCEPTION; } - first = false; } if (match_start >= 0) { if (match_end < subject_length) { ReplacementStringBuilder::AddSubjectSlice(builder, @@ -3870,108 +3890,121 @@ } // Only called from Runtime_RegExpExecMultiple so it doesn't need to maintain // separate last match info. See comment on that function. -static RegExpImpl::IrregexpResult SearchRegExpMultiple( +static int SearchRegExpMultiple( Isolate* isolate, Handle<String> subject, Handle<JSRegExp> regexp, Handle<JSArray> last_match_array, FixedArrayBuilder* builder) { ASSERT(subject->IsFlat()); - int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject); - if (required_registers < 0) return RegExpImpl::RE_EXCEPTION; + int registers_per_match = RegExpImpl::IrregexpPrepare(regexp, subject); + if (registers_per_match < 0) return RegExpImpl::RE_EXCEPTION; - OffsetsVector registers(required_registers, isolate); + int max_matches; + int num_registers = RegExpImpl::GlobalOffsetsVectorSize(regexp, + registers_per_match, + &max_matches); + OffsetsVector registers(num_registers, isolate); Vector<int32_t> register_vector(registers.vector(), registers.length()); - RegExpImpl::IrregexpResult result = - RegExpImpl::IrregexpExecOnce(regexp, - subject, - 0, - register_vector); + int num_matches = RegExpImpl::IrregexpExecRaw(regexp, + subject, + 0, + register_vector); int capture_count = regexp->CaptureCount(); int subject_length = subject->length(); // Position to search from. int pos = 0; // End of previous match. Differs from pos if match was empty. int match_end = 0; - if (result == RegExpImpl::RE_SUCCESS) { - bool first = true; - do { - int match_start = register_vector[0]; - builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch); - if (match_end < match_start) { - ReplacementStringBuilder::AddSubjectSlice(builder, - match_end, - match_start); - } - match_end = register_vector[1]; + bool first = true; - { - // Avoid accumulating new handles inside loop. - HandleScope temp_scope(isolate); - // Arguments array to replace function is match, captures, index and - // subject, i.e., 3 + capture count in total. - Handle<FixedArray> elements = - isolate->factory()->NewFixedArray(3 + capture_count); - Handle<String> match; - if (!first) { - match = isolate->factory()->NewProperSubString(subject, - match_start, - match_end); - } else { - match = isolate->factory()->NewSubString(subject, - match_start, - match_end); + if (num_matches > 0) { + do { + int match_start = 0; + for (int match_index = 0; match_index < num_matches; match_index++) { + int32_t* current_match = + &register_vector[match_index * registers_per_match]; + match_start = current_match[0]; + builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch); + if (match_end < match_start) { + ReplacementStringBuilder::AddSubjectSlice(builder, + match_end, + match_start); } - elements->set(0, *match); - for (int i = 1; i <= capture_count; i++) { - int start = register_vector[i * 2]; - if (start >= 0) { - int end = register_vector[i * 2 + 1]; - ASSERT(start <= end); - Handle<String> substring; - if (!first) { - substring = isolate->factory()->NewProperSubString(subject, - start, - end); + match_end = current_match[1]; + + { + // Avoid accumulating new handles inside loop. + HandleScope temp_scope(isolate); + // Arguments array to replace function is match, captures, index and + // subject, i.e., 3 + capture count in total. + Handle<FixedArray> elements = + isolate->factory()->NewFixedArray(3 + capture_count); + Handle<String> match; + if (!first) { + match = isolate->factory()->NewProperSubString(subject, + match_start, + match_end); + } else { + match = isolate->factory()->NewSubString(subject, + match_start, + match_end); + } + elements->set(0, *match); + for (int i = 1; i <= capture_count; i++) { + int start = current_match[i * 2]; + if (start >= 0) { + int end = current_match[i * 2 + 1]; + ASSERT(start <= end); + Handle<String> substring; + if (!first) { + substring = + isolate->factory()->NewProperSubString(subject, start, end); + } else { + substring = + isolate->factory()->NewSubString(subject, start, end); + } + elements->set(i, *substring); } else { - substring = isolate->factory()->NewSubString(subject, start, end); + ASSERT(current_match[i * 2 + 1] < 0); + elements->set(i, isolate->heap()->undefined_value()); } - elements->set(i, *substring); - } else { - ASSERT(register_vector[i * 2 + 1] < 0); - elements->set(i, isolate->heap()->undefined_value()); } + elements->set(capture_count + 1, Smi::FromInt(match_start)); + elements->set(capture_count + 2, *subject); + builder->Add(*isolate->factory()->NewJSArrayWithElements(elements)); } - elements->set(capture_count + 1, Smi::FromInt(match_start)); - elements->set(capture_count + 2, *subject); - builder->Add(*isolate->factory()->NewJSArrayWithElements(elements)); + first = false; } + // If we did not get the maximum number of matches, we can stop here + // since there are no matches left. + if (num_matches < max_matches) break; + if (match_end > match_start) { pos = match_end; } else { pos = match_end + 1; if (pos > subject_length) { break; } } - result = RegExpImpl::IrregexpExecOnce(regexp, - subject, - pos, - register_vector); - first = false; - } while (result == RegExpImpl::RE_SUCCESS); + num_matches = RegExpImpl::IrregexpExecRaw(regexp, + subject, + pos, + register_vector); + } while (num_matches > 0); - if (result != RegExpImpl::RE_EXCEPTION) { + if (num_matches != RegExpImpl::RE_EXCEPTION) { // Finished matching, with at least one match. if (match_end < subject_length) { ReplacementStringBuilder::AddSubjectSlice(builder, match_end, subject_length); @@ -3991,11 +4024,11 @@ RegExpImpl::SetLastInput(elements, *subject); return RegExpImpl::RE_SUCCESS; } } // No matches at all, return failure or exception result directly. - return result; + return num_matches; } // This is only called for StringReplaceGlobalRegExpWithFunction. This sets // lastMatchInfoOverride to maintain the last match info, so we don't need to @@ -4008,14 +4041,14 @@ if (!subject->IsFlat()) FlattenString(subject); CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 0); CONVERT_ARG_HANDLE_CHECKED(JSArray, last_match_info, 2); CONVERT_ARG_HANDLE_CHECKED(JSArray, result_array, 3); - ASSERT(last_match_info->HasFastElements()); + ASSERT(last_match_info->HasFastObjectElements()); ASSERT(regexp->GetFlags().is_global()); Handle<FixedArray> result_elements; - if (result_array->HasFastElements()) { + if (result_array->HasFastObjectElements()) { result_elements = Handle<FixedArray>(FixedArray::cast(result_array->elements())); } if (result_elements.is_null() || result_elements->length() < 16) { result_elements = isolate->factory()->NewFixedArrayWithHoles(16); @@ -4033,11 +4066,11 @@ return isolate->heap()->null_value(); } ASSERT_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP); - RegExpImpl::IrregexpResult result; + int result; if (regexp->CaptureCount() == 0) { result = SearchRegExpNoCaptureMultiple(isolate, subject, regexp, last_match_info, @@ -4313,21 +4346,26 @@ } } else if (FLAG_smi_only_arrays && args.at<Object>(1)->IsSmi()) { // JSObject without a string key. If the key is a Smi, check for a // definite out-of-bounds access to elements, which is a strong indicator // that subsequent accesses will also call the runtime. Proactively - // transition elements to FAST_ELEMENTS to avoid excessive boxing of + // transition elements to FAST_*_ELEMENTS to avoid excessive boxing of // doubles for those future calls in the case that the elements would // become FAST_DOUBLE_ELEMENTS. Handle<JSObject> js_object(args.at<JSObject>(0)); ElementsKind elements_kind = js_object->GetElementsKind(); - if (elements_kind == FAST_SMI_ONLY_ELEMENTS || - elements_kind == FAST_DOUBLE_ELEMENTS) { + if (IsFastElementsKind(elements_kind) && + !IsFastObjectElementsKind(elements_kind)) { FixedArrayBase* elements = js_object->elements(); if (args.at<Smi>(1)->value() >= elements->length()) { + if (IsFastHoleyElementsKind(elements_kind)) { + elements_kind = FAST_HOLEY_ELEMENTS; + } else { + elements_kind = FAST_ELEMENTS; + } MaybeObject* maybe_object = TransitionElements(js_object, - FAST_ELEMENTS, + elements_kind, isolate); if (maybe_object->IsFailure()) return maybe_object; } } } @@ -4493,12 +4531,14 @@ // assignments. if (js_object->IsStringObjectWithCharacterAt(index)) { return *value; } + js_object->ValidateElements(); Handle<Object> result = JSObject::SetElement( js_object, index, value, attr, strict_mode, set_mode); + js_object->ValidateElements(); if (result.is_null()) return Failure::Exception(); return *value; } if (key->IsString()) { @@ -4652,19 +4692,35 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_TransitionElementsSmiToDouble) { NoHandleAllocation ha; RUNTIME_ASSERT(args.length() == 1); Handle<Object> object = args.at<Object>(0); - return TransitionElements(object, FAST_DOUBLE_ELEMENTS, isolate); + if (object->IsJSObject()) { + Handle<JSObject> js_object(Handle<JSObject>::cast(object)); + ElementsKind new_kind = js_object->HasFastHoleyElements() + ? FAST_HOLEY_DOUBLE_ELEMENTS + : FAST_DOUBLE_ELEMENTS; + return TransitionElements(object, new_kind, isolate); + } else { + return *object; + } } RUNTIME_FUNCTION(MaybeObject*, Runtime_TransitionElementsDoubleToObject) { NoHandleAllocation ha; RUNTIME_ASSERT(args.length() == 1); Handle<Object> object = args.at<Object>(0); - return TransitionElements(object, FAST_ELEMENTS, isolate); + if (object->IsJSObject()) { + Handle<JSObject> js_object(Handle<JSObject>::cast(object)); + ElementsKind new_kind = js_object->HasFastHoleyElements() + ? FAST_HOLEY_ELEMENTS + : FAST_ELEMENTS; + return TransitionElements(object, new_kind, isolate); + } else { + return *object; + } } // Set the native flag on the function. // This is used to decide if we should transform null and undefined @@ -4691,36 +4747,42 @@ CONVERT_ARG_HANDLE_CHECKED(FixedArray, literals, 3); CONVERT_SMI_ARG_CHECKED(literal_index, 4); HandleScope scope; Object* raw_boilerplate_object = literals->get(literal_index); - Handle<JSArray> boilerplate(JSArray::cast(raw_boilerplate_object)); -#if DEBUG + Handle<JSArray> boilerplate_object(JSArray::cast(raw_boilerplate_object)); ElementsKind elements_kind = object->GetElementsKind(); -#endif - ASSERT(elements_kind <= FAST_DOUBLE_ELEMENTS); + ASSERT(IsFastElementsKind(elements_kind)); // Smis should never trigger transitions. ASSERT(!value->IsSmi()); if (value->IsNumber()) { - ASSERT(elements_kind == FAST_SMI_ONLY_ELEMENTS); - JSObject::TransitionElementsKind(object, FAST_DOUBLE_ELEMENTS); - if (IsMoreGeneralElementsKindTransition(boilerplate->GetElementsKind(), - FAST_DOUBLE_ELEMENTS)) { - JSObject::TransitionElementsKind(boilerplate, FAST_DOUBLE_ELEMENTS); + ASSERT(IsFastSmiElementsKind(elements_kind)); + ElementsKind transitioned_kind = IsFastHoleyElementsKind(elements_kind) + ? FAST_HOLEY_DOUBLE_ELEMENTS + : FAST_DOUBLE_ELEMENTS; + if (IsMoreGeneralElementsKindTransition( + boilerplate_object->GetElementsKind(), + transitioned_kind)) { + JSObject::TransitionElementsKind(boilerplate_object, transitioned_kind); } - ASSERT(object->GetElementsKind() == FAST_DOUBLE_ELEMENTS); + JSObject::TransitionElementsKind(object, transitioned_kind); + ASSERT(IsFastDoubleElementsKind(object->GetElementsKind())); FixedDoubleArray* double_array = FixedDoubleArray::cast(object->elements()); HeapNumber* number = HeapNumber::cast(*value); double_array->set(store_index, number->Number()); } else { - ASSERT(elements_kind == FAST_SMI_ONLY_ELEMENTS || - elements_kind == FAST_DOUBLE_ELEMENTS); - JSObject::TransitionElementsKind(object, FAST_ELEMENTS); - if (IsMoreGeneralElementsKindTransition(boilerplate->GetElementsKind(), - FAST_ELEMENTS)) { - JSObject::TransitionElementsKind(boilerplate, FAST_ELEMENTS); + ASSERT(IsFastSmiElementsKind(elements_kind) || + IsFastDoubleElementsKind(elements_kind)); + ElementsKind transitioned_kind = IsFastHoleyElementsKind(elements_kind) + ? FAST_HOLEY_ELEMENTS + : FAST_ELEMENTS; + JSObject::TransitionElementsKind(object, transitioned_kind); + if (IsMoreGeneralElementsKindTransition( + boilerplate_object->GetElementsKind(), + transitioned_kind)) { + JSObject::TransitionElementsKind(boilerplate_object, transitioned_kind); } FixedArray* object_array = FixedArray::cast(object->elements()); object_array->set(store_index, *value); } return *object; @@ -5929,11 +5991,13 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_QuoteJSONStringArray) { NoHandleAllocation ha; ASSERT(args.length() == 1); CONVERT_ARG_CHECKED(JSArray, array, 0); - if (!array->HasFastElements()) return isolate->heap()->undefined_value(); + if (!array->HasFastObjectElements()) { + return isolate->heap()->undefined_value(); + } FixedArray* elements = FixedArray::cast(array->elements()); int n = elements->length(); bool ascii = true; int total_length = 0; @@ -6372,11 +6436,11 @@ Handle<JSArray> result = isolate->factory()->NewJSArray(part_count); MaybeObject* maybe_result = result->EnsureCanContainHeapObjectElements(); if (maybe_result->IsFailure()) return maybe_result; result->set_length(Smi::FromInt(part_count)); - ASSERT(result->HasFastElements()); + ASSERT(result->HasFastObjectElements()); if (part_count == 1 && indices.at(0) == subject_length) { FixedArray::cast(result->elements())->set(0, *subject); return *result; } @@ -6391,11 +6455,11 @@ elements->set(i, *substring); part_start = part_end + pattern_length; } if (limit == 0xffffffffu) { - if (result->HasFastElements()) { + if (result->HasFastObjectElements()) { StringSplitCache::Enter(isolate->heap(), isolate->heap()->string_split_cache(), *subject, *pattern, *elements); @@ -6748,11 +6812,11 @@ MaybeObject* maybe_result = array->EnsureCanContainHeapObjectElements(); if (maybe_result->IsFailure()) return maybe_result; int special_length = special->length(); - if (!array->HasFastElements()) { + if (!array->HasFastObjectElements()) { return isolate->Throw(isolate->heap()->illegal_argument_symbol()); } FixedArray* fixed_array = FixedArray::cast(array->elements()); if (fixed_array->length() < array_length) { array_length = fixed_array->length(); @@ -6858,11 +6922,11 @@ return Failure::OutOfMemoryException(); } int array_length = args.smi_at(1); CONVERT_ARG_CHECKED(String, separator, 2); - if (!array->HasFastElements()) { + if (!array->HasFastObjectElements()) { return isolate->Throw(isolate->heap()->illegal_argument_symbol()); } FixedArray* fixed_array = FixedArray::cast(array->elements()); if (fixed_array->length() < array_length) { array_length = fixed_array->length(); @@ -6975,12 +7039,11 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SparseJoinWithSeparator) { NoHandleAllocation ha; ASSERT(args.length() == 3); CONVERT_ARG_CHECKED(JSArray, elements_array, 0); - RUNTIME_ASSERT(elements_array->HasFastElements() || - elements_array->HasFastSmiOnlyElements()); + RUNTIME_ASSERT(elements_array->HasFastSmiOrObjectElements()); CONVERT_NUMBER_CHECKED(uint32_t, array_length, Uint32, args[1]); CONVERT_ARG_CHECKED(String, separator, 2); // elements_array is fast-mode JSarray of alternating positions // (increasing order) and strings. // array_length is length of original array (used to add separators); @@ -8275,10 +8338,23 @@ return isolate->heap()->undefined_value(); } +RUNTIME_FUNCTION(MaybeObject*, Runtime_ClearFunctionTypeFeedback) { + HandleScope scope(isolate); + ASSERT(args.length() == 1); + CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0); + Code* unoptimized = function->shared()->code(); + if (unoptimized->kind() == Code::FUNCTION) { + unoptimized->ClearInlineCaches(); + unoptimized->ClearTypeFeedbackCells(isolate->heap()); + } + return isolate->heap()->undefined_value(); +} + + RUNTIME_FUNCTION(MaybeObject*, Runtime_RunningInSimulator) { #if defined(USE_SIMULATOR) return isolate->heap()->true_value(); #else return isolate->heap()->false_value(); @@ -9124,11 +9200,11 @@ CONVERT_ARG_HANDLE_CHECKED(JSArray, output, 1); MaybeObject* maybe_result_array = output->EnsureCanContainHeapObjectElements(); if (maybe_result_array->IsFailure()) return maybe_result_array; - RUNTIME_ASSERT(output->HasFastElements()); + RUNTIME_ASSERT(output->HasFastObjectElements()); AssertNoAllocation no_allocation; FixedArray* output_array = FixedArray::cast(output->elements()); RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE); @@ -9356,11 +9432,11 @@ // false otherwise. RUNTIME_FUNCTION(MaybeObject*, Runtime_PushIfAbsent) { ASSERT(args.length() == 2); CONVERT_ARG_CHECKED(JSArray, array, 0); CONVERT_ARG_CHECKED(JSObject, element, 1); - RUNTIME_ASSERT(array->HasFastElements() || array->HasFastSmiOnlyElements()); + RUNTIME_ASSERT(array->HasFastSmiOrObjectElements()); int length = Smi::cast(array->length())->value(); FixedArray* elements = FixedArray::cast(array->elements()); for (int i = 0; i < length; i++) { if (elements->get(i) == element) return isolate->heap()->false_value(); } @@ -9441,11 +9517,11 @@ Handle<Object> length = isolate_->factory()->NewNumber(static_cast<double>(index_offset_)); Handle<Map> map; if (fast_elements_) { map = isolate_->factory()->GetElementsTransitionMap(array, - FAST_ELEMENTS); + FAST_HOLEY_ELEMENTS); } else { map = isolate_->factory()->GetElementsTransitionMap(array, DICTIONARY_ELEMENTS); } array->set_map(*map); @@ -9500,12 +9576,14 @@ static uint32_t EstimateElementCount(Handle<JSArray> array) { uint32_t length = static_cast<uint32_t>(array->length()->Number()); int element_count = 0; switch (array->GetElementsKind()) { - case FAST_SMI_ONLY_ELEMENTS: - case FAST_ELEMENTS: { + case FAST_SMI_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_ELEMENTS: + case FAST_HOLEY_ELEMENTS: { // Fast elements can't have lengths that are not representable by // a 32-bit signed integer. ASSERT(static_cast<int32_t>(FixedArray::kMaxLength) >= 0); int fast_length = static_cast<int>(length); Handle<FixedArray> elements(FixedArray::cast(array->elements())); @@ -9513,10 +9591,11 @@ if (!elements->get(i)->IsTheHole()) element_count++; } break; } case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: // TODO(1810): Decide if it's worthwhile to implement this. UNREACHABLE(); break; case DICTIONARY_ELEMENTS: { Handle<SeededNumberDictionary> dictionary( @@ -9603,22 +9682,25 @@ static void CollectElementIndices(Handle<JSObject> object, uint32_t range, List<uint32_t>* indices) { ElementsKind kind = object->GetElementsKind(); switch (kind) { - case FAST_SMI_ONLY_ELEMENTS: - case FAST_ELEMENTS: { + case FAST_SMI_ELEMENTS: + case FAST_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_ELEMENTS: { Handle<FixedArray> elements(FixedArray::cast(object->elements())); uint32_t length = static_cast<uint32_t>(elements->length()); if (range < length) length = range; for (uint32_t i = 0; i < length; i++) { if (!elements->get(i)->IsTheHole()) { indices->Add(i); } } break; } + case FAST_HOLEY_DOUBLE_ELEMENTS: case FAST_DOUBLE_ELEMENTS: { // TODO(1810): Decide if it's worthwhile to implement this. UNREACHABLE(); break; } @@ -9729,12 +9811,14 @@ static bool IterateElements(Isolate* isolate, Handle<JSArray> receiver, ArrayConcatVisitor* visitor) { uint32_t length = static_cast<uint32_t>(receiver->length()->Number()); switch (receiver->GetElementsKind()) { - case FAST_SMI_ONLY_ELEMENTS: - case FAST_ELEMENTS: { + case FAST_SMI_ELEMENTS: + case FAST_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_ELEMENTS: { // Run through the elements FixedArray and use HasElement and GetElement // to check the prototype for missing elements. Handle<FixedArray> elements(FixedArray::cast(receiver->elements())); int fast_length = static_cast<int>(length); ASSERT(fast_length <= elements->length()); @@ -9751,10 +9835,11 @@ visitor->visit(j, element_value); } } break; } + case FAST_HOLEY_DOUBLE_ELEMENTS: case FAST_DOUBLE_ELEMENTS: { // TODO(1810): Decide if it's worthwhile to implement this. UNREACHABLE(); break; } @@ -9848,11 +9933,11 @@ ASSERT(args.length() == 1); HandleScope handle_scope(isolate); CONVERT_ARG_HANDLE_CHECKED(JSArray, arguments, 0); int argument_count = static_cast<int>(arguments->length()->Number()); - RUNTIME_ASSERT(arguments->HasFastElements()); + RUNTIME_ASSERT(arguments->HasFastObjectElements()); Handle<FixedArray> elements(FixedArray::cast(arguments->elements())); // Pass 1: estimate the length and number of elements of the result. // The actual length can be larger if any of the arguments have getters // that mutate other arguments (but will otherwise be precise). @@ -9868,14 +9953,18 @@ uint32_t element_estimate; if (obj->IsJSArray()) { Handle<JSArray> array(Handle<JSArray>::cast(obj)); // TODO(1810): Find out if it's worthwhile to properly support // arbitrary ElementsKinds. For now, pessimistically transition to - // FAST_ELEMENTS. + // FAST_*_ELEMENTS. if (array->HasFastDoubleElements()) { + ElementsKind to_kind = FAST_ELEMENTS; + if (array->HasFastHoleyElements()) { + to_kind = FAST_HOLEY_ELEMENTS; + } array = Handle<JSArray>::cast( - JSObject::TransitionElementsKind(array, FAST_ELEMENTS)); + JSObject::TransitionElementsKind(array, to_kind)); } length_estimate = static_cast<uint32_t>(array->length()->Number()); element_estimate = EstimateElementCount(array); @@ -9968,33 +10057,26 @@ // Move contents of argument 0 (an array) to argument 1 (an array) RUNTIME_FUNCTION(MaybeObject*, Runtime_MoveArrayContents) { ASSERT(args.length() == 2); CONVERT_ARG_CHECKED(JSArray, from, 0); CONVERT_ARG_CHECKED(JSArray, to, 1); + from->ValidateElements(); + to->ValidateElements(); FixedArrayBase* new_elements = from->elements(); + ElementsKind from_kind = from->GetElementsKind(); MaybeObject* maybe_new_map; - ElementsKind elements_kind; - if (new_elements->map() == isolate->heap()->fixed_array_map() || - new_elements->map() == isolate->heap()->fixed_cow_array_map()) { - elements_kind = FAST_ELEMENTS; - } else if (new_elements->map() == - isolate->heap()->fixed_double_array_map()) { - elements_kind = FAST_DOUBLE_ELEMENTS; - } else { - elements_kind = DICTIONARY_ELEMENTS; - } - maybe_new_map = to->GetElementsTransitionMap(isolate, elements_kind); + maybe_new_map = to->GetElementsTransitionMap(isolate, from_kind); Object* new_map; if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map; - to->set_map(Map::cast(new_map)); - to->set_elements(new_elements); + to->set_map_and_elements(Map::cast(new_map), new_elements); to->set_length(from->length()); Object* obj; { MaybeObject* maybe_obj = from->ResetElements(); if (!maybe_obj->ToObject(&obj)) return maybe_obj; } from->set_length(Smi::FromInt(0)); + to->ValidateElements(); return to; } // How many elements does this object/array have? @@ -10011,40 +10093,10 @@ return Smi::FromInt(FixedArray::cast(elements)->length()); } } -RUNTIME_FUNCTION(MaybeObject*, Runtime_SwapElements) { - HandleScope handle_scope(isolate); - - ASSERT_EQ(3, args.length()); - - CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0); - Handle<Object> key1 = args.at<Object>(1); - Handle<Object> key2 = args.at<Object>(2); - - uint32_t index1, index2; - if (!key1->ToArrayIndex(&index1) - || !key2->ToArrayIndex(&index2)) { - return isolate->ThrowIllegalOperation(); - } - - Handle<JSObject> jsobject = Handle<JSObject>::cast(object); - Handle<Object> tmp1 = Object::GetElement(jsobject, index1); - RETURN_IF_EMPTY_HANDLE(isolate, tmp1); - Handle<Object> tmp2 = Object::GetElement(jsobject, index2); - RETURN_IF_EMPTY_HANDLE(isolate, tmp2); - - RETURN_IF_EMPTY_HANDLE( - isolate, JSObject::SetElement(jsobject, index1, tmp2, NONE, kStrictMode)); - RETURN_IF_EMPTY_HANDLE( - isolate, JSObject::SetElement(jsobject, index2, tmp1, NONE, kStrictMode)); - - return isolate->heap()->undefined_value(); -} - - // Returns an array that tells you where in the [0, length) interval an array // might have elements. Can either return keys (positive integers) or // intervals (pair of a negative integer (-start-1) followed by a // positive (length)) or undefined values. // Intervals can span over some keys that are not in the object. @@ -10070,12 +10122,11 @@ keys->set_undefined(i); } } return *isolate->factory()->NewJSArrayWithElements(keys); } else { - ASSERT(array->HasFastElements() || - array->HasFastSmiOnlyElements() || + ASSERT(array->HasFastSmiOrObjectElements() || array->HasFastDoubleElements()); Handle<FixedArray> single_interval = isolate->factory()->NewFixedArray(2); // -1 means start of array. single_interval->set(0, Smi::FromInt(-1)); FixedArrayBase* elements = FixedArrayBase::cast(array->elements()); @@ -13386,12 +13437,14 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_Has##Name) { \ CONVERT_ARG_CHECKED(JSObject, obj, 0); \ return isolate->heap()->ToBoolean(obj->Has##Name()); \ } -ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastSmiOnlyElements) -ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastElements) +ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastSmiElements) +ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastObjectElements) +ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastSmiOrObjectElements) ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastDoubleElements) +ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastHoleyElements) ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(DictionaryElements) ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(ExternalPixelElements) ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(ExternalArrayElements) ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(ExternalByteElements) ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(ExternalUnsignedByteElements)