Reviewers: cbruni,

Message:
I am a little hesitant to push ahead with this strategy until we have benchmarks
to show it doesn't give a regression. In particular, I wanted to test the
performance of this patch against the baseline, and also against just not having the builtin at all and going straight to the JS version. cbruni, do you have any
tests that could be used for this purpose?

Description:
Fix @@isConcatSpreadable in ArrayConcat builtin

The ArrayConcat builtin didn't respect @@isConcatSpreadable, which,
surprisingly, is observable if you modify an Array's Symbol.isConcatSpreadable
property. This patch bails out of the ArrayConcat builtin if any argument's
Symbol.isConcatSpreadable property is set to false, but continues otherwise,
meeting the spec's requirements.

BUG=v8:4317
LOG=N

Please review this at https://codereview.chromium.org/1247243003/

Base URL: https://chromium.googlesource.com/v8/v8.git@master

Affected files (+37, -19 lines):
  M src/builtins.cc
  M src/elements.h
  M src/elements.cc
  M src/runtime/runtime-array.cc
  M test/mjsunit/harmony/array-concat.js


Index: src/builtins.cc
diff --git a/src/builtins.cc b/src/builtins.cc
index 69c2871650e87b16abea5b6c8abd3f8522247236..ccdec3cfd385c7bc5dfbc61d9187ab94757e4f44 100644
--- a/src/builtins.cc
+++ b/src/builtins.cc
@@ -944,9 +944,14 @@ BUILTIN(ArrayConcat) {
     bool is_holey = false;
     for (int i = 0; i < n_arguments; i++) {
       Object* arg = args[i];
+      bool fallback = false;
       PrototypeIterator iter(isolate, arg);
+      if (FLAG_harmony_concat_spreadable) {
+        Handle<Object> obj(arg, isolate);
+        fallback = !IsConcatSpreadable(isolate, obj);
+      }
       if (!arg->IsJSArray() || !JSArray::cast(arg)->HasFastElements() ||
-          iter.GetCurrent() != array_proto) {
+          iter.GetCurrent() != array_proto || fallback) {
         AllowHeapAllocation allow_allocation;
         return CallJsBuiltin(isolate, "$arrayConcat", args);
       }
Index: src/elements.cc
diff --git a/src/elements.cc b/src/elements.cc
index 85eb3f266ecdb135e0ac8081d2b0d08bdd330477..917a31d0b5c85f3a1d493f820ccea2e0b6c3780f 100644
--- a/src/elements.cc
+++ b/src/elements.cc
@@ -1946,5 +1946,23 @@ void ElementsAccessor::TearDown() {


 ElementsAccessor** ElementsAccessor::elements_accessors_ = NULL;
+
+
+bool IsConcatSpreadable(Isolate* isolate, Handle<Object> obj) {
+  HandleScope handle_scope(isolate);
+  if (!obj->IsSpecObject()) return false;
+  if (FLAG_harmony_concat_spreadable) {
+    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)) {
+      if (!value->IsUndefined()) {
+        return value->BooleanValue();
+      }
+    }
+  }
+  return obj->IsJSArray();
+}
 }  // namespace internal
 }  // namespace v8
Index: src/elements.h
diff --git a/src/elements.h b/src/elements.h
index 511b636c3de8082ed2c5dfe16251e7d5e8da08c1..df283889bf9ad985e83b2aaf6ebfecc5b6d1d95c 100644
--- a/src/elements.h
+++ b/src/elements.h
@@ -156,6 +156,8 @@ MUST_USE_RESULT MaybeHandle<Object> ArrayConstructInitializeElements(
     Handle<JSArray> array,
     Arguments* args);

+bool IsConcatSpreadable(Isolate* isolate, Handle<Object> obj);
+
 } }  // namespace v8::internal

 #endif  // V8_ELEMENTS_H_
Index: src/runtime/runtime-array.cc
diff --git a/src/runtime/runtime-array.cc b/src/runtime/runtime-array.cc
index af2d4c54813104a5e9566293951e9c488822b9fd..9cdf9237e0c52ace1caa54d88d082ed22966d360 100644
--- a/src/runtime/runtime-array.cc
+++ b/src/runtime/runtime-array.cc
@@ -726,24 +726,6 @@ static bool IterateElements(Isolate* isolate, Handle<JSObject> receiver,
 }


-static bool IsConcatSpreadable(Isolate* isolate, Handle<Object> obj) {
-  HandleScope handle_scope(isolate);
-  if (!obj->IsSpecObject()) return false;
-  if (FLAG_harmony_concat_spreadable) {
-    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)) {
-      if (!value->IsUndefined()) {
-        return value->BooleanValue();
-      }
-    }
-  }
-  return obj->IsJSArray();
-}
-
-
 /**
  * Array::concat implementation.
  * See ECMAScript 262, 15.4.4.4.
Index: test/mjsunit/harmony/array-concat.js
diff --git a/test/mjsunit/harmony/array-concat.js b/test/mjsunit/harmony/array-concat.js index 71b6790bc7b13e238930ed824d165d76ec903c2c..87bd246cbad7381d42f2a8c2ca294aa9023d0c61 100644
--- a/test/mjsunit/harmony/array-concat.js
+++ b/test/mjsunit/harmony/array-concat.js
@@ -706,3 +706,14 @@ function testConcatTypedArray(type, elems, modulo) {
   assertEquals(1 + arr3.length * 2, r4.length);
   assertEquals(expectedTrace, trace);
 })();
+
+
+(function testConcatArrayNonSpreadable() {
+  "use strict";
+  var b = [];
+  b[Symbol.isConcatSpreadable] = false;
+  assertEquals(2, b.concat(b).length);
+  assertEquals(1, [].concat(b).length);
+  assertEquals(1, [].concat(b, new Array).length);
+  assertEquals(2, [].concat(b, new Object).length);
+})();


--
--
v8-dev mailing list
v8-dev@googlegroups.com
http://groups.google.com/group/v8-dev
--- You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to v8-dev+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to