Skip to content

Commit

Permalink
Implement ES6 @@isConcatSpreadable / Array.prototype.concat
Browse files Browse the repository at this point in the history
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}
  • Loading branch information
caitp authored and Commit bot committed Dec 12, 2014
1 parent 10b38df commit 4805417
Show file tree
Hide file tree
Showing 4 changed files with 546 additions and 21 deletions.
11 changes: 11 additions & 0 deletions src/harmony-array.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
9 changes: 9 additions & 0 deletions src/runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down
134 changes: 113 additions & 21 deletions src/runtime/runtime-array.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit 4805417

Please sign in to comment.