Index: src/builtins.cc |
diff --git a/src/builtins.cc b/src/builtins.cc |
index b7f30433d5f35a33128b1494639b82679c1174b9..b82d08637a685e2ea3f32880d7413e02aeccd93e 100644 |
--- a/src/builtins.cc |
+++ b/src/builtins.cc |
@@ -627,28 +627,42 @@ namespace { |
*/ |
class ArrayConcatVisitor { |
public: |
- ArrayConcatVisitor(Isolate* isolate, Handle<FixedArray> storage, |
+ ArrayConcatVisitor(Isolate* isolate, Handle<Object> storage, |
bool fast_elements) |
: isolate_(isolate), |
- storage_(Handle<FixedArray>::cast( |
- isolate->global_handles()->Create(*storage))), |
+ storage_(isolate->global_handles()->Create(*storage)), |
index_offset_(0u), |
bit_field_(FastElementsField::encode(fast_elements) | |
- ExceedsLimitField::encode(false)) {} |
+ ExceedsLimitField::encode(false) | |
+ IsFixedArrayField::encode(storage->IsFixedArray())) { |
+ DCHECK(!(this->fast_elements() && !is_fixed_array())); |
+ } |
~ArrayConcatVisitor() { clear_storage(); } |
- void visit(uint32_t i, Handle<Object> elm) { |
+ bool visit(uint32_t i, Handle<Object> elm) { |
+ uint32_t index = index_offset_ + i; |
+ |
+ if (!is_fixed_array()) { |
+ Handle<Object> element_value; |
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
+ isolate_, element_value, |
+ Object::SetElement(isolate_, storage_, index, elm, STRICT), false); |
+ return true; |
+ } |
+ |
if (i >= JSObject::kMaxElementCount - index_offset_) { |
set_exceeds_array_limit(true); |
- return; |
+ // Exception hasn't been thrown at this point. Return true to |
+ // break out, and caller will throw. !visit would imply that |
+ // there is already a pending exception. |
+ return true; |
} |
- uint32_t index = index_offset_ + i; |
if (fast_elements()) { |
- if (index < static_cast<uint32_t>(storage_->length())) { |
- storage_->set(index, *elm); |
- return; |
+ if (index < static_cast<uint32_t>(storage_fixed_array()->length())) { |
+ storage_fixed_array()->set(index, *elm); |
+ return true; |
} |
// Our initial estimate of length was foiled, possibly by |
// getters on the arrays increasing the length of later arrays |
@@ -669,6 +683,7 @@ class ArrayConcatVisitor { |
clear_storage(); |
set_storage(*result); |
} |
+ return true; |
} |
void increase_index_offset(uint32_t delta) { |
@@ -692,6 +707,7 @@ class ArrayConcatVisitor { |
} |
Handle<JSArray> ToArray() { |
+ DCHECK(is_fixed_array()); |
Handle<JSArray> array = isolate_->factory()->NewJSArray(0); |
Handle<Object> length = |
isolate_->factory()->NewNumber(static_cast<double>(index_offset_)); |
@@ -699,15 +715,26 @@ class ArrayConcatVisitor { |
array, fast_elements() ? FAST_HOLEY_ELEMENTS : DICTIONARY_ELEMENTS); |
array->set_map(*map); |
array->set_length(*length); |
- array->set_elements(*storage_); |
+ array->set_elements(*storage_fixed_array()); |
return array; |
} |
+ // Storage is either a FixedArray (if is_fixed_array()) or a JSReciever |
+ // (otherwise) |
+ Handle<FixedArray> storage_fixed_array() { |
+ DCHECK(is_fixed_array()); |
+ return Handle<FixedArray>::cast(storage_); |
+ } |
+ Handle<JSReceiver> storage_jsreceiver() { |
+ DCHECK(!is_fixed_array()); |
+ return Handle<JSReceiver>::cast(storage_); |
+ } |
+ |
private: |
// Convert storage to dictionary mode. |
void SetDictionaryMode() { |
- DCHECK(fast_elements()); |
- Handle<FixedArray> current_storage(*storage_); |
+ DCHECK(fast_elements() && is_fixed_array()); |
+ Handle<FixedArray> current_storage = storage_fixed_array(); |
Handle<SeededNumberDictionary> slow_storage( |
SeededNumberDictionary::New(isolate_, current_storage->length())); |
uint32_t current_length = static_cast<uint32_t>(current_storage->length()); |
@@ -735,12 +762,13 @@ class ArrayConcatVisitor { |
} |
inline void set_storage(FixedArray* storage) { |
- storage_ = |
- Handle<FixedArray>::cast(isolate_->global_handles()->Create(storage)); |
+ DCHECK(is_fixed_array()); |
+ storage_ = isolate_->global_handles()->Create(storage); |
} |
class FastElementsField : public BitField<bool, 0, 1> {}; |
class ExceedsLimitField : public BitField<bool, 1, 1> {}; |
+ class IsFixedArrayField : public BitField<bool, 2, 1> {}; |
bool fast_elements() const { return FastElementsField::decode(bit_field_); } |
void set_fast_elements(bool fast) { |
@@ -749,9 +777,10 @@ class ArrayConcatVisitor { |
void set_exceeds_array_limit(bool exceeds) { |
bit_field_ = ExceedsLimitField::update(bit_field_, exceeds); |
} |
+ bool is_fixed_array() const { return IsFixedArrayField::decode(bit_field_); } |
Isolate* isolate_; |
- Handle<FixedArray> storage_; // Always a global handle. |
+ Handle<Object> storage_; // Always a global handle. |
// Index after last seen index. Always less than or equal to |
// JSObject::kMaxElementCount. |
uint32_t index_offset_; |
@@ -822,7 +851,7 @@ uint32_t EstimateElementCount(Handle<JSArray> array) { |
template <class ExternalArrayClass, class ElementType> |
-void IterateTypedArrayElements(Isolate* isolate, Handle<JSObject> receiver, |
+bool IterateTypedArrayElements(Isolate* isolate, Handle<JSObject> receiver, |
bool elements_are_ints, |
bool elements_are_guaranteed_smis, |
ArrayConcatVisitor* visitor) { |
@@ -837,7 +866,7 @@ void IterateTypedArrayElements(Isolate* isolate, Handle<JSObject> receiver, |
HandleScope loop_scope(isolate); |
Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get_scalar(j))), |
isolate); |
- visitor->visit(j, e); |
+ if (!visitor->visit(j, e)) return false; |
} |
} else { |
for (uint32_t j = 0; j < len; j++) { |
@@ -845,11 +874,11 @@ void IterateTypedArrayElements(Isolate* isolate, Handle<JSObject> receiver, |
int64_t val = static_cast<int64_t>(array->get_scalar(j)); |
if (Smi::IsValid(static_cast<intptr_t>(val))) { |
Handle<Smi> e(Smi::FromInt(static_cast<int>(val)), isolate); |
- visitor->visit(j, e); |
+ if (!visitor->visit(j, e)) return false; |
} else { |
Handle<Object> e = |
isolate->factory()->NewNumber(static_cast<ElementType>(val)); |
- visitor->visit(j, e); |
+ if (!visitor->visit(j, e)) return false; |
} |
} |
} |
@@ -857,9 +886,10 @@ void IterateTypedArrayElements(Isolate* isolate, Handle<JSObject> receiver, |
for (uint32_t j = 0; j < len; j++) { |
HandleScope loop_scope(isolate); |
Handle<Object> e = isolate->factory()->NewNumber(array->get_scalar(j)); |
- visitor->visit(j, e); |
+ if (!visitor->visit(j, e)) return false; |
} |
} |
+ return true; |
} |
@@ -976,7 +1006,7 @@ bool IterateElementsSlow(Isolate* isolate, Handle<JSReceiver> receiver, |
ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, element_value, |
Object::GetElement(isolate, receiver, i), |
false); |
- visitor->visit(i, element_value); |
+ if (!visitor->visit(i, element_value)) return false; |
} |
} |
visitor->increase_index_offset(length); |
@@ -1036,7 +1066,7 @@ bool IterateElements(Isolate* isolate, Handle<JSReceiver> receiver, |
HandleScope loop_scope(isolate); |
Handle<Object> element_value(elements->get(j), isolate); |
if (!element_value->IsTheHole()) { |
- visitor->visit(j, element_value); |
+ if (!visitor->visit(j, element_value)) return false; |
} else { |
Maybe<bool> maybe = JSReceiver::HasElement(array, j); |
if (!maybe.IsJust()) return false; |
@@ -1046,7 +1076,7 @@ bool IterateElements(Isolate* isolate, Handle<JSReceiver> receiver, |
ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
isolate, element_value, Object::GetElement(isolate, array, j), |
false); |
- visitor->visit(j, element_value); |
+ if (!visitor->visit(j, element_value)) return false; |
} |
} |
} |
@@ -1072,7 +1102,7 @@ bool IterateElements(Isolate* isolate, Handle<JSReceiver> receiver, |
double double_value = elements->get_scalar(j); |
Handle<Object> element_value = |
isolate->factory()->NewNumber(double_value); |
- visitor->visit(j, element_value); |
+ if (!visitor->visit(j, element_value)) return false; |
} else { |
Maybe<bool> maybe = JSReceiver::HasElement(array, j); |
if (!maybe.IsJust()) return false; |
@@ -1083,7 +1113,7 @@ bool IterateElements(Isolate* isolate, Handle<JSReceiver> receiver, |
ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
isolate, element_value, Object::GetElement(isolate, array, j), |
false); |
- visitor->visit(j, element_value); |
+ if (!visitor->visit(j, element_value)) return false; |
} |
} |
} |
@@ -1112,7 +1142,7 @@ bool IterateElements(Isolate* isolate, Handle<JSReceiver> receiver, |
Handle<Object> element; |
ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
isolate, element, Object::GetElement(isolate, array, index), false); |
- visitor->visit(index, element); |
+ if (!visitor->visit(index, element)) return false; |
// Skip to next different index (i.e., omit duplicates). |
do { |
j++; |
@@ -1130,43 +1160,51 @@ bool IterateElements(Isolate* isolate, Handle<JSReceiver> receiver, |
break; |
} |
case INT8_ELEMENTS: { |
- IterateTypedArrayElements<FixedInt8Array, int8_t>(isolate, array, true, |
- true, visitor); |
+ if (!IterateTypedArrayElements<FixedInt8Array, int8_t>( |
+ isolate, array, true, true, visitor)) |
+ return false; |
break; |
} |
case UINT8_ELEMENTS: { |
- IterateTypedArrayElements<FixedUint8Array, uint8_t>(isolate, array, true, |
- true, visitor); |
+ if (!IterateTypedArrayElements<FixedUint8Array, uint8_t>( |
+ isolate, array, true, true, visitor)) |
+ return false; |
break; |
} |
case INT16_ELEMENTS: { |
- IterateTypedArrayElements<FixedInt16Array, int16_t>(isolate, array, true, |
- true, visitor); |
+ if (!IterateTypedArrayElements<FixedInt16Array, int16_t>( |
+ isolate, array, true, true, visitor)) |
+ return false; |
break; |
} |
case UINT16_ELEMENTS: { |
- IterateTypedArrayElements<FixedUint16Array, uint16_t>( |
- isolate, array, true, true, visitor); |
+ if (!IterateTypedArrayElements<FixedUint16Array, uint16_t>( |
+ isolate, array, true, true, visitor)) |
+ return false; |
break; |
} |
case INT32_ELEMENTS: { |
- IterateTypedArrayElements<FixedInt32Array, int32_t>(isolate, array, true, |
- false, visitor); |
+ if (!IterateTypedArrayElements<FixedInt32Array, int32_t>( |
+ isolate, array, true, false, visitor)) |
+ return false; |
break; |
} |
case UINT32_ELEMENTS: { |
- IterateTypedArrayElements<FixedUint32Array, uint32_t>( |
- isolate, array, true, false, visitor); |
+ if (!IterateTypedArrayElements<FixedUint32Array, uint32_t>( |
+ isolate, array, true, false, visitor)) |
+ return false; |
break; |
} |
case FLOAT32_ELEMENTS: { |
- IterateTypedArrayElements<FixedFloat32Array, float>(isolate, array, false, |
- false, visitor); |
+ if (!IterateTypedArrayElements<FixedFloat32Array, float>( |
+ isolate, array, false, false, visitor)) |
+ return false; |
break; |
} |
case FLOAT64_ELEMENTS: { |
- IterateTypedArrayElements<FixedFloat64Array, double>( |
- isolate, array, false, false, visitor); |
+ if (!IterateTypedArrayElements<FixedFloat64Array, double>( |
+ isolate, array, false, false, visitor)) |
+ return false; |
break; |
} |
case FAST_SLOPPY_ARGUMENTS_ELEMENTS: |
@@ -1176,7 +1214,7 @@ bool IterateElements(Isolate* isolate, Handle<JSReceiver> receiver, |
Handle<Object> element; |
ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
isolate, element, Object::GetElement(isolate, array, index), false); |
- visitor->visit(index, element); |
+ if (!visitor->visit(index, element)) return false; |
} |
break; |
} |
@@ -1210,9 +1248,12 @@ static Maybe<bool> IsConcatSpreadable(Isolate* isolate, Handle<Object> obj) { |
} |
-Object* Slow_ArrayConcat(Arguments* args, Isolate* isolate) { |
+Object* Slow_ArrayConcat(Arguments* args, Handle<Object> species, |
+ Isolate* isolate) { |
int argument_count = args->length(); |
+ bool is_array_species = *species == isolate->context()->array_function(); |
+ |
// 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). |
@@ -1263,7 +1304,8 @@ Object* Slow_ArrayConcat(Arguments* args, Isolate* isolate) { |
// If estimated number of elements is more than half of length, a |
// fixed array (fast case) is more time and space-efficient than a |
// dictionary. |
- bool fast_case = (estimate_nof_elements * 2) >= estimate_result_length; |
+ bool fast_case = |
+ is_array_species && (estimate_nof_elements * 2) >= estimate_result_length; |
if (fast_case && kind == FAST_DOUBLE_ELEMENTS) { |
Handle<FixedArrayBase> storage = |
@@ -1347,18 +1389,25 @@ Object* Slow_ArrayConcat(Arguments* args, Isolate* isolate) { |
// In case of failure, fall through. |
} |
- Handle<FixedArray> storage; |
+ Handle<Object> storage; |
if (fast_case) { |
// The backing storage array must have non-existing elements to preserve |
// holes across concat operations. |
storage = |
isolate->factory()->NewFixedArrayWithHoles(estimate_result_length); |
- } else { |
+ } else if (is_array_species) { |
// TODO(126): move 25% pre-allocation logic into Dictionary::Allocate |
uint32_t at_least_space_for = |
estimate_nof_elements + (estimate_nof_elements >> 2); |
- storage = Handle<FixedArray>::cast( |
- SeededNumberDictionary::New(isolate, at_least_space_for)); |
+ storage = SeededNumberDictionary::New(isolate, at_least_space_for); |
+ } else { |
+ DCHECK(species->IsConstructor()); |
+ Handle<Object> length(Smi::FromInt(0), isolate); |
+ Handle<Object> storage_object; |
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
+ isolate, storage_object, |
+ Execution::New(isolate, species, species, 1, &length)); |
+ storage = storage_object; |
} |
ArrayConcatVisitor visitor(isolate, storage, fast_case); |
@@ -1382,7 +1431,12 @@ Object* Slow_ArrayConcat(Arguments* args, Isolate* isolate) { |
THROW_NEW_ERROR_RETURN_FAILURE( |
isolate, NewRangeError(MessageTemplate::kInvalidArrayLength)); |
} |
- return *visitor.ToArray(); |
+ |
+ if (is_array_species) { |
+ return *visitor.ToArray(); |
+ } else { |
+ return *visitor.storage_jsreceiver(); |
+ } |
} |
@@ -1446,11 +1500,19 @@ BUILTIN(ArrayConcat) { |
args[0] = *receiver; |
Handle<JSArray> result_array; |
- if (Fast_ArrayConcat(isolate, &args).ToHandle(&result_array)) { |
- return *result_array; |
+ |
+ // Reading @@species happens before anything else with a side effect, so |
+ // we can do it here to determine whether to take the fast path. |
+ Handle<Object> species; |
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
+ isolate, species, Object::ArraySpeciesConstructor(isolate, receiver)); |
+ if (*species == isolate->context()->native_context()->array_function()) { |
+ if (Fast_ArrayConcat(isolate, &args).ToHandle(&result_array)) { |
+ return *result_array; |
+ } |
+ if (isolate->has_pending_exception()) return isolate->heap()->exception(); |
} |
- if (isolate->has_pending_exception()) return isolate->heap()->exception(); |
- return Slow_ArrayConcat(&args, isolate); |
+ return Slow_ArrayConcat(&args, species, isolate); |
} |