Commit 48054170 authored by caitpotter88's avatar caitpotter88 Committed by Commit bot

Implement ES6 @@isConcatSpreadable / Array.prototype.concat

Add support for Symbol.isConcatSpreadable in Array.prototype.concat. This enables spreading non-Array objects with the symbol.

LOG=N
R=dslomov@chromium.org
BUG=

Review URL: https://codereview.chromium.org/771483002

Cr-Commit-Position: refs/heads/master@{#25808}
parent 10b38df2
......@@ -192,6 +192,17 @@ function ArrayOf() {
// -------------------------------------------------------------------
function HarmonyArrayExtendSymbolPrototype() {
%CheckIsBootstrapping();
InstallConstants($Symbol, $Array(
// TODO(dslomov, caitp): Move to symbol.js when shipping
"isConcatSpreadable", symbolIsConcatSpreadable
));
}
HarmonyArrayExtendSymbolPrototype();
function HarmonyArrayExtendArrayPrototype() {
%CheckIsBootstrapping();
......
......@@ -626,6 +626,15 @@ function IsPrimitive(x) {
}
// ES6, draft 10-14-14, section 22.1.3.1.1
function IsConcatSpreadable(O) {
if (!IS_SPEC_OBJECT(O)) return false;
var spreadable = O[symbolIsConcatSpreadable];
if (IS_UNDEFINED(spreadable)) return IS_ARRAY(O);
return ToBoolean(spreadable);
}
// ECMA-262, section 8.6.2.6, page 28.
function DefaultNumber(x) {
if (!IS_SYMBOL_WRAPPER(x)) {
......
......@@ -289,11 +289,11 @@ static uint32_t EstimateElementCount(Handle<JSArray> array) {
template <class ExternalArrayClass, class ElementType>
static void IterateExternalArrayElements(Isolate* isolate,
Handle<JSObject> receiver,
bool elements_are_ints,
bool elements_are_guaranteed_smis,
ArrayConcatVisitor* visitor) {
static void IterateTypedArrayElements(Isolate* isolate,
Handle<JSObject> receiver,
bool elements_are_ints,
bool elements_are_guaranteed_smis,
ArrayConcatVisitor* visitor) {
Handle<ExternalArrayClass> array(
ExternalArrayClass::cast(receiver->elements()));
uint32_t len = static_cast<uint32_t>(array->length());
......@@ -440,7 +440,7 @@ static void CollectElementIndices(Handle<JSObject> object, uint32_t range,
/**
* A helper function that visits elements of a JSArray in numerical
* A helper function that visits elements of a JSObject in numerical
* order.
*
* The visitor argument called for each existing element in the array
......@@ -449,9 +449,22 @@ static void CollectElementIndices(Handle<JSObject> object, uint32_t range,
* length.
* Returns false if any access threw an exception, otherwise true.
*/
static bool IterateElements(Isolate* isolate, Handle<JSArray> receiver,
static bool IterateElements(Isolate* isolate, Handle<JSObject> receiver,
ArrayConcatVisitor* visitor) {
uint32_t length = static_cast<uint32_t>(receiver->length()->Number());
uint32_t length = 0;
if (receiver->IsJSArray()) {
Handle<JSArray> array(Handle<JSArray>::cast(receiver));
length = static_cast<uint32_t>(array->length()->Number());
} else {
Handle<Object> val;
Handle<Object> key(isolate->heap()->length_string(), isolate);
ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, val,
Runtime::GetObjectProperty(isolate, receiver, key), false);
// TODO(caitp): implement ToLength() abstract operation for C++
val->ToUint32(&length);
}
switch (receiver->GetElementsKind()) {
case FAST_SMI_ELEMENTS:
case FAST_ELEMENTS:
......@@ -552,55 +565,132 @@ static bool IterateElements(Isolate* isolate, Handle<JSArray> receiver,
}
break;
}
case UINT8_CLAMPED_ELEMENTS: {
Handle<FixedUint8ClampedArray> pixels(
FixedUint8ClampedArray::cast(receiver->elements()));
for (uint32_t j = 0; j < length; j++) {
Handle<Smi> e(Smi::FromInt(pixels->get_scalar(j)), isolate);
visitor->visit(j, e);
}
break;
}
case EXTERNAL_INT8_ELEMENTS: {
IterateExternalArrayElements<ExternalInt8Array, int8_t>(
IterateTypedArrayElements<ExternalInt8Array, int8_t>(
isolate, receiver, true, true, visitor);
break;
}
case INT8_ELEMENTS: {
IterateTypedArrayElements<FixedInt8Array, int8_t>(
isolate, receiver, true, true, visitor);
break;
}
case EXTERNAL_UINT8_ELEMENTS: {
IterateExternalArrayElements<ExternalUint8Array, uint8_t>(
IterateTypedArrayElements<ExternalUint8Array, uint8_t>(
isolate, receiver, true, true, visitor);
break;
}
case UINT8_ELEMENTS: {
IterateTypedArrayElements<FixedUint8Array, uint8_t>(
isolate, receiver, true, true, visitor);
break;
}
case EXTERNAL_INT16_ELEMENTS: {
IterateExternalArrayElements<ExternalInt16Array, int16_t>(
IterateTypedArrayElements<ExternalInt16Array, int16_t>(
isolate, receiver, true, true, visitor);
break;
}
case INT16_ELEMENTS: {
IterateTypedArrayElements<FixedInt16Array, int16_t>(
isolate, receiver, true, true, visitor);
break;
}
case EXTERNAL_UINT16_ELEMENTS: {
IterateExternalArrayElements<ExternalUint16Array, uint16_t>(
IterateTypedArrayElements<ExternalUint16Array, uint16_t>(
isolate, receiver, true, true, visitor);
break;
}
case UINT16_ELEMENTS: {
IterateTypedArrayElements<FixedUint16Array, uint16_t>(
isolate, receiver, true, true, visitor);
break;
}
case EXTERNAL_INT32_ELEMENTS: {
IterateExternalArrayElements<ExternalInt32Array, int32_t>(
IterateTypedArrayElements<ExternalInt32Array, int32_t>(
isolate, receiver, true, false, visitor);
break;
}
case INT32_ELEMENTS: {
IterateTypedArrayElements<FixedInt32Array, int32_t>(
isolate, receiver, true, false, visitor);
break;
}
case EXTERNAL_UINT32_ELEMENTS: {
IterateExternalArrayElements<ExternalUint32Array, uint32_t>(
IterateTypedArrayElements<ExternalUint32Array, uint32_t>(
isolate, receiver, true, false, visitor);
break;
}
case UINT32_ELEMENTS: {
IterateTypedArrayElements<FixedUint32Array, uint32_t>(
isolate, receiver, true, false, visitor);
break;
}
case EXTERNAL_FLOAT32_ELEMENTS: {
IterateExternalArrayElements<ExternalFloat32Array, float>(
IterateTypedArrayElements<ExternalFloat32Array, float>(
isolate, receiver, false, false, visitor);
break;
}
case FLOAT32_ELEMENTS: {
IterateTypedArrayElements<FixedFloat32Array, float>(
isolate, receiver, false, false, visitor);
break;
}
case EXTERNAL_FLOAT64_ELEMENTS: {
IterateExternalArrayElements<ExternalFloat64Array, double>(
IterateTypedArrayElements<ExternalFloat64Array, double>(
isolate, receiver, false, false, visitor);
break;
}
default:
UNREACHABLE();
case FLOAT64_ELEMENTS: {
IterateTypedArrayElements<FixedFloat64Array, double>(
isolate, receiver, false, false, visitor);
break;
}
case SLOPPY_ARGUMENTS_ELEMENTS: {
ElementsAccessor* accessor = receiver->GetElementsAccessor();
for (uint32_t index = 0; index < length; index++) {
HandleScope loop_scope(isolate);
if (accessor->HasElement(receiver, receiver, index)) {
Handle<Object> element;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, element, accessor->Get(receiver, receiver, index),
false);
visitor->visit(index, element);
}
}
break;
}
}
visitor->increase_index_offset(length);
return true;
}
static bool IsConcatSpreadable(Isolate* isolate, Handle<Object> obj) {
HandleScope handle_scope(isolate);
if (!obj->IsSpecObject()) return false;
if (obj->IsJSArray()) return true;
if (FLAG_harmony_arrays) {
Handle<Symbol> key(isolate->factory()->is_concat_spreadable_symbol());
Handle<Object> value;
MaybeHandle<Object> maybeValue =
i::Runtime::GetObjectProperty(isolate, obj, key);
if (maybeValue.ToHandle(&value)) {
return value->BooleanValue();
}
}
return false;
}
/**
* Array::concat implementation.
* See ECMAScript 262, 15.4.4.4.
......@@ -771,9 +861,11 @@ RUNTIME_FUNCTION(Runtime_ArrayConcat) {
for (int i = 0; i < argument_count; i++) {
Handle<Object> obj(elements->get(i), isolate);
if (obj->IsJSArray()) {
Handle<JSArray> array = Handle<JSArray>::cast(obj);
if (!IterateElements(isolate, array, &visitor)) {
bool spreadable = IsConcatSpreadable(isolate, obj);
if (isolate->has_pending_exception()) return isolate->heap()->exception();
if (spreadable) {
Handle<JSObject> object = Handle<JSObject>::cast(obj);
if (!IterateElements(isolate, object, &visitor)) {
return isolate->heap()->exception();
}
} else {
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment