From 258da539bcf0d8c0cb9f7bde97c0eada283166ba Mon Sep 17 00:00:00 2001
From: Leo Balter <leonardo.balter@gmail.com>
Date: Tue, 18 Dec 2018 18:33:56 -0200
Subject: [PATCH] refactor coverage for flatMap

---
 .../flatMap/array-like-objects-nested.js      |  68 +++++++++
 .../array-like-objects-poisoned-length.js     |  60 ++++++++
 .../flatMap/array-like-objects-typedarrays.js |  50 +++++++
 .../prototype/flatMap/array-like-objects.js   |  82 ++++++++--
 .../flatMap/non-callable-argument-throws.js   |  42 +++++-
 .../flatMap/non-object-ctor-throws.js         |  35 -----
 .../flatMap/this-value-ctor-non-object.js     |  74 +++++++++
 ...is-value-ctor-object-species-bad-throws.js | 129 ++++++++++++++++
 ...ect-species-custom-ctor-poisoned-throws.js |  58 +++++++
 ...s-value-ctor-object-species-custom-ctor.js |  94 ++++++++++++
 .../flatMap/this-value-ctor-object-species.js | 141 ++++++++++++++++++
 ...js => this-value-null-undefined-throws.js} |  17 ++-
 .../prototype/flatMap/thisArg-argument.js     |  39 +++--
 13 files changed, 815 insertions(+), 74 deletions(-)
 create mode 100644 test/built-ins/Array/prototype/flatMap/array-like-objects-nested.js
 create mode 100644 test/built-ins/Array/prototype/flatMap/array-like-objects-poisoned-length.js
 create mode 100644 test/built-ins/Array/prototype/flatMap/array-like-objects-typedarrays.js
 delete mode 100644 test/built-ins/Array/prototype/flatMap/non-object-ctor-throws.js
 create mode 100644 test/built-ins/Array/prototype/flatMap/this-value-ctor-non-object.js
 create mode 100644 test/built-ins/Array/prototype/flatMap/this-value-ctor-object-species-bad-throws.js
 create mode 100644 test/built-ins/Array/prototype/flatMap/this-value-ctor-object-species-custom-ctor-poisoned-throws.js
 create mode 100644 test/built-ins/Array/prototype/flatMap/this-value-ctor-object-species-custom-ctor.js
 create mode 100644 test/built-ins/Array/prototype/flatMap/this-value-ctor-object-species.js
 rename test/built-ins/Array/prototype/flatMap/{null-undefined-input-throws.js => this-value-null-undefined-throws.js} (60%)

diff --git a/test/built-ins/Array/prototype/flatMap/array-like-objects-nested.js b/test/built-ins/Array/prototype/flatMap/array-like-objects-nested.js
new file mode 100644
index 0000000000..82f5a2dc4a
--- /dev/null
+++ b/test/built-ins/Array/prototype/flatMap/array-like-objects-nested.js
@@ -0,0 +1,68 @@
+// Copyright (C) 2018 Leo Balter. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-array.prototype.flatMap
+description: >
+  Does not flatten array-like objects nested into the main array
+info: |
+  FlattenIntoArray(target, source, sourceLen, start, depth [ , mapperFunction, thisArg ])
+
+  1. Let targetIndex be start.
+  2. Let sourceIndex be 0.
+  3. Repeat, while sourceIndex < sourceLen
+    a. Let P be ! ToString(sourceIndex).
+    b. Let exists be ? HasProperty(source, P).
+    c. If exists is true, then
+      i. Let element be ? Get(source, P).
+      ii. If mapperFunction is present, then
+        1. Assert: thisArg is present.
+        2. Set element to ? Call(mapperFunction, thisArg , « element, sourceIndex, source »).
+      iii. Let shouldFlatten be false.
+      iv. If depth > 0, then
+        1. Set shouldFlatten to ? IsArray(element).
+      v. If shouldFlatten is true, then
+        1. Let elementLen be ? ToLength(? Get(element, "length")).
+        2. Set targetIndex to ? FlattenIntoArray(target, element, elementLen, targetIndex, depth - 1).
+      vi. Else,
+        1. If targetIndex ≥ 253-1, throw a TypeError exception.
+        2. Perform ? CreateDataPropertyOrThrow(target, ! ToString(targetIndex), element).
+        3. Increase targetIndex by 1.
+includes: [compareArray.js]
+features: [Array.prototype.flatMap, Int32Array]
+---*/
+
+function fn(e) {
+  return e;
+}
+
+var obj1 = {
+  length: 1,
+  0: 'a',
+  toString() { return 'obj1'; }
+};
+
+var obj2 = new Int32Array(2);
+
+var obj3 = {
+  get length() { throw "should not even consider the length property" },
+  toString() { return 'obj3'; }
+};
+
+var arr = [obj1, obj2, obj3];
+var actual = arr.flatMap(fn);
+assert.compareArray(actual, arr, 'returns a similar array');
+assert.notSameValue(actual, arr, 'not the same array');
+
+var arrLike = {
+  length: 4,
+  0: obj1,
+  1: obj2,
+  2: obj3,
+  get 3() { return arrLike },
+  toString() { return 'obj4'; }
+};
+
+actual = [].flatMap.call(arrLike, fn);
+assert.compareArray(actual, [obj1, obj2, obj3, arrLike], 'returns a similar array');
+assert.notSameValue(actual, arrLike, 'not the same object');
+assert.sameValue(Object.getPrototypeOf(actual), Array.prototype);
diff --git a/test/built-ins/Array/prototype/flatMap/array-like-objects-poisoned-length.js b/test/built-ins/Array/prototype/flatMap/array-like-objects-poisoned-length.js
new file mode 100644
index 0000000000..a4e8e8684f
--- /dev/null
+++ b/test/built-ins/Array/prototype/flatMap/array-like-objects-poisoned-length.js
@@ -0,0 +1,60 @@
+// Copyright (C) 2018 Leo Balter. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-array.prototype.flatMap
+description: >
+  Observe abrupt completion in poisoned lengths of array-like objects
+info: |
+  Array.prototype.flatMap ( mapperFunction [ , thisArg ] )
+
+  1. Let O be ? ToObject(this value).
+  2. Let sourceLen be ? ToLength(? Get(O, "length")).
+features: [Array.prototype.flatMap, Symbol.toPrimitive]
+---*/
+
+assert.sameValue(typeof Array.prototype.flatMap, 'function');
+
+function fn(e) {
+  return e;
+}
+
+var arr = {
+  length: Symbol(),
+};
+assert.throws(TypeError, function() {
+  [].flatMap.call(arr, fn);
+}, 'length is a symbol');
+
+arr = {
+  get length() { throw new Test262Error() }
+};
+assert.throws(Test262Error, function() {
+  [].flatMap.call(arr, fn);
+}, 'custom get error');
+
+arr = {
+  length: {
+    valueOf() { throw new Test262Error() }
+  }
+};
+assert.throws(Test262Error, function() {
+  [].flatMap.call(arr, fn);
+}, 'custom valueOf error');
+
+arr = {
+  length: {
+    toString() { throw new Test262Error() }
+  }
+};
+assert.throws(Test262Error, function() {
+  [].flatMap.call(arr, fn);
+}, 'custom toString error');
+
+arr = {
+  length: {
+    [Symbol.toPrimitive]() { throw new Test262Error() }
+  }
+};
+assert.throws(Test262Error, function() {
+  [].flatMap.call(arr, fn);
+}, 'custom Symbol.toPrimitive error');
diff --git a/test/built-ins/Array/prototype/flatMap/array-like-objects-typedarrays.js b/test/built-ins/Array/prototype/flatMap/array-like-objects-typedarrays.js
new file mode 100644
index 0000000000..fd0dd5a192
--- /dev/null
+++ b/test/built-ins/Array/prototype/flatMap/array-like-objects-typedarrays.js
@@ -0,0 +1,50 @@
+// Copyright (C) 2018 Leo Balter. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-array.prototype.flatMap
+description: >
+  array-like objects can be flattened (typedArrays)
+info: |
+  Array.prototype.flatMap ( mapperFunction [ , thisArg ] )
+
+  1. Let O be ? ToObject(this value).
+  2. Let sourceLen be ? ToLength(? Get(O, "length")).
+  ...
+  5. Let A be ? ArraySpeciesCreate(O, 0).
+  ...
+
+  ArraySpeciesCreate ( originalArray, length )
+
+  3. Let isArray be ? IsArray(originalArray).
+  4. If isArray is false, return ? ArrayCreate(length).
+  5. Let C be ? Get(originalArray, "constructor").
+includes: [compareArray.js]
+features: [Array.prototype.flatMap, Int32Array]
+---*/
+
+function same(e) {
+  return e;
+}
+
+var ta;
+var actual;
+
+ta = new Int32Array([1, 0, 42]);
+
+Object.defineProperty(ta, 'constructor', {
+  get() { throw "it should not object the typedarray ctor"; }
+});
+actual = [].flatMap.call(ta, same);
+assert.compareArray(actual, [1, 0, 42], 'compare returned array');
+assert.sameValue(Object.getPrototypeOf(actual), Array.prototype, 'returned object is an array #1');
+assert.sameValue(actual instanceof Int32Array, false, 'returned object is not an instance of Int32Array #1');
+
+ta = new Int32Array(0);
+
+Object.defineProperty(ta, 'constructor', {
+  get() { throw "it should not object the typedarray ctor"; }
+});
+actual = [].flatMap.call(ta, same);
+assert.compareArray(actual, [], 'compare returned empty array');
+assert.sameValue(Object.getPrototypeOf(actual), Array.prototype, 'returned object is an array #2');
+assert.sameValue(actual instanceof Int32Array, false, 'returned object is not an instance of Int32Array #2');
diff --git a/test/built-ins/Array/prototype/flatMap/array-like-objects.js b/test/built-ins/Array/prototype/flatMap/array-like-objects.js
index 8b2c597a84..5a8a2fca33 100644
--- a/test/built-ins/Array/prototype/flatMap/array-like-objects.js
+++ b/test/built-ins/Array/prototype/flatMap/array-like-objects.js
@@ -3,33 +3,83 @@
 /*---
 esid: sec-array.prototype.flatMap
 description: >
-    array-like objects can be flattened
+  array-like objects can be flattened
+info: |
+  Array.prototype.flatMap ( mapperFunction [ , thisArg ] )
+
+  1. Let O be ? ToObject(this value).
+  2. Let sourceLen be ? ToLength(? Get(O, "length")).
+  ...
+  5. Let A be ? ArraySpeciesCreate(O, 0).
+  ...
+
+  ArraySpeciesCreate ( originalArray, length )
+
+  3. Let isArray be ? IsArray(originalArray).
+  4. If isArray is false, return ? ArrayCreate(length).
+
+  FlattenIntoArray(target, source, sourceLen, start, depth [ , mapperFunction, thisArg ])
+
+  1. Let targetIndex be start.
+  2. Let sourceIndex be 0.
+  3. Repeat, while sourceIndex < sourceLen
+    a. Let P be ! ToString(sourceIndex).
+    b. Let exists be ? HasProperty(source, P).
+    c. If exists is true, then
+      ...
+    ** Skip if property does not exist **
 includes: [compareArray.js]
 features: [Array.prototype.flatMap]
 ---*/
 
-function getArgumentsObject() {
-  return arguments;
-}
-
-function double(e) {
-  return [e * 2];
+function fn(e) {
+  return [39, e * 2]; // returns an array to observe it being flattened after
 }
 
-var a = getArgumentsObject(1, 2);
-var actual = [].flatMap.call(a, double);
-assert.compareArray(actual, [2, 4], 'arguments objects');
+var a;
+var actual;
 
 a = {
-  length: 1,
+  length: 3,
   0: 1,
+  // property 1 will be fully skipped
+  2: 21,
+  get 3() { throw 'it should not get this property'; }
 };
-actual = [].flatMap.call(a, double);
-assert.compareArray(actual, [2], 'array-like objects');
+actual = [].flatMap.call(a, fn);
+assert.compareArray(actual, [39, 2, 39, 42], 'array-like flattened object, number length');
+assert.sameValue(Object.getPrototypeOf(actual), Array.prototype, 'returned object is an array #1');
 
 a = {
-  length: void 0,
-  0: 1,
+  length: undefined,
+  get 0() { throw 'it should not get this property'; },
 };
-actual = [].flatMap.call(a, double);
+actual = [].flatMap.call(a, fn);
 assert.compareArray(actual, [], 'array-like objects; undefined length');
+assert.sameValue(Object.getPrototypeOf(actual), Array.prototype, 'returned object is an array #2');
+
+var called = false;
+a = {
+  get length() {
+    if (!called) {
+      called = true;
+      return 2;
+    } else {
+      throw 'is should get the length only once';
+    }
+  },
+  0: 21,
+  1: 19.5,
+  get 2() { throw 'it should not get this property'; },
+};
+actual = [].flatMap.call(a, fn);
+assert.compareArray(actual, [39, 42, 39, 39], 'array-like flattened objects; custom get length');
+assert.sameValue(Object.getPrototypeOf(actual), Array.prototype, 'returned object is an array #3');
+
+a = {
+  length: 10001,
+  [10000]: 7,
+};
+actual = [].flatMap.call(a, fn);
+assert.compareArray(actual, [39, 14], 'array-like flattened object, long length simulating shallow array');
+assert.sameValue(Object.getPrototypeOf(actual), Array.prototype, 'returned object is an array #4');
diff --git a/test/built-ins/Array/prototype/flatMap/non-callable-argument-throws.js b/test/built-ins/Array/prototype/flatMap/non-callable-argument-throws.js
index c9b7144135..a094feb5b4 100644
--- a/test/built-ins/Array/prototype/flatMap/non-callable-argument-throws.js
+++ b/test/built-ins/Array/prototype/flatMap/non-callable-argument-throws.js
@@ -3,12 +3,48 @@
 /*---
 esid: sec-array.prototype.flatMap
 description: >
-    non callable argument should throw TypeError Exception
-features: [Array.prototype.flatMap]
+  non callable argument should throw TypeError Exception
+info: |
+  Array.prototype.flatMap ( mapperFunction [ , thisArg ] )
+
+  1. Let O be ? ToObject(this value).
+  2. Let sourceLen be ? ToLength(? Get(O, "length")).
+  3. If IsCallable(mapperFunction) is false, throw a TypeError exception.
+  ...
+features: [Array.prototype.flatMap, Symbol]
 ---*/
 
 assert.sameValue(typeof Array.prototype.flatMap, "function");
 
 assert.throws(TypeError, function() {
   [].flatMap({});
-}, 'non callable argument');
+}, 'non callable argument, object');
+
+assert.throws(TypeError, function() {
+  [].flatMap(0);
+}, 'non callable argument, number');
+
+assert.throws(TypeError, function() {
+  [].flatMap();
+}, 'non callable argument, implict undefined');
+
+assert.throws(TypeError, function() {
+  [].flatMap(undefined);
+}, 'non callable argument, undefined');
+
+assert.throws(TypeError, function() {
+  [].flatMap(null);
+}, 'non callable argument, null');
+
+assert.throws(TypeError, function() {
+  [].flatMap(false);
+}, 'non callable argument, boolean');
+
+assert.throws(TypeError, function() {
+  [].flatMap('');
+}, 'non callable argument, string');
+
+var s = Symbol();
+assert.throws(TypeError, function() {
+  [].flatMap(s);
+}, 'non callable argument, symbol');
diff --git a/test/built-ins/Array/prototype/flatMap/non-object-ctor-throws.js b/test/built-ins/Array/prototype/flatMap/non-object-ctor-throws.js
deleted file mode 100644
index 39cc66e97d..0000000000
--- a/test/built-ins/Array/prototype/flatMap/non-object-ctor-throws.js
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (C) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
-// This code is governed by the BSD license found in the LICENSE file.
-/*---
-esid: sec-array.prototype.flatMap
-description: >
-    Behavior when `constructor` property is neither an Object nor undefined
-    - if IsConstructor(C) is false, throw a TypeError exception.
-features: [Array.prototype.flatMap]
----*/
-
-assert.sameValue(typeof Array.prototype.flatMap, 'function');
-
-var a = [];
-a.constructor = null;
-assert.throws(TypeError, function() {
-  a.flatMap();
-}, 'null value');
-
-a = [];
-a.constructor = 1;
-assert.throws(TypeError, function() {
-  a.flatMap();
-}, 'number value');
-
-a = [];
-a.constructor = 'string';
-assert.throws(TypeError, function() {
-  a.flatMap();
-}, 'string value');
-
-a = [];
-a.constructor = true;
-assert.throws(TypeError, function() {
-  a.flatMap();
-}, 'boolean value');
diff --git a/test/built-ins/Array/prototype/flatMap/this-value-ctor-non-object.js b/test/built-ins/Array/prototype/flatMap/this-value-ctor-non-object.js
new file mode 100644
index 0000000000..a71ad5a4ed
--- /dev/null
+++ b/test/built-ins/Array/prototype/flatMap/this-value-ctor-non-object.js
@@ -0,0 +1,74 @@
+// Copyright (C) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-array.prototype.flatMap
+description: >
+  Assert behavior if this value has a custom non-object constructor property
+info: |
+  Array.prototype.flatMap ( mapperFunction [ , thisArg ] )
+
+  1. Let O be ? ToObject(this value).
+  2. Let sourceLen be ? ToLength(? Get(O, "length")).
+  ...
+  5. Let A be ? ArraySpeciesCreate(O, 0).
+  ...
+
+  ArraySpeciesCreate ( originalArray, length )
+
+  3. Let isArray be ? IsArray(originalArray).
+  4. If isArray is false, return ? ArrayCreate(length).
+  5. Let C be ? Get(originalArray, "constructor").
+  6. If IsConstructor(C) is true, then
+    a. Let thisRealm be the current Realm Record.
+    b. Let realmC be ? GetFunctionRealm(C).
+    c. If thisRealm and realmC are not the same Realm Record, then
+      i. If SameValue(C, realmC.[[Intrinsics]].[[%Array%]]) is true, set C to undefined.
+  7. If Type(C) is Object, then
+    a. Set C to ? Get(C, @@species).
+    b. If C is null, set C to undefined.
+  8. If C is undefined, return ? ArrayCreate(length).
+  9. If IsConstructor(C) is false, throw a TypeError exception.
+features: [Array.prototype.flatMap, Symbol]
+includes: [compareArray.js]
+---*/
+
+assert.sameValue(typeof Array.prototype.flatMap, 'function');
+
+var a = [];
+var mapperFn = function() {};
+
+a.constructor = null;
+assert.throws(TypeError, function() {
+  a.flatMap(mapperFn);
+}, 'null value');
+
+a = [];
+a.constructor = 1;
+assert.throws(TypeError, function() {
+  a.flatMap(mapperFn);
+}, 'number value');
+
+a = [];
+a.constructor = 'string';
+assert.throws(TypeError, function() {
+  a.flatMap(mapperFn);
+}, 'string value');
+
+a = [];
+a.constructor = true;
+assert.throws(TypeError, function() {
+  a.flatMap(mapperFn);
+}, 'boolean value');
+
+a = [];
+a.constructor = Symbol();
+assert.throws(TypeError, function() {
+  a.flatMap(mapperFn);
+}, 'symbol value');
+
+a = [];
+a.constructor = undefined;
+var actual = a.flatMap(mapperFn);
+assert.compareArray(actual, [], 'undefined value does not throw');
+assert.sameValue(Object.getPrototypeOf(actual), Array.prototype);
+assert.notSameValue(actual, a, 'returns a new array');
diff --git a/test/built-ins/Array/prototype/flatMap/this-value-ctor-object-species-bad-throws.js b/test/built-ins/Array/prototype/flatMap/this-value-ctor-object-species-bad-throws.js
new file mode 100644
index 0000000000..64fdb6ea10
--- /dev/null
+++ b/test/built-ins/Array/prototype/flatMap/this-value-ctor-object-species-bad-throws.js
@@ -0,0 +1,129 @@
+// Copyright (C) 2018 Leo Balter. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-array.prototype.flatMap
+description: >
+  Assert behavior if this value has a custom object constructor property species
+info: |
+  Array.prototype.flatMap ( mapperFunction [ , thisArg ] )
+
+  1. Let O be ? ToObject(this value).
+  2. Let sourceLen be ? ToLength(? Get(O, "length")).
+  ...
+  5. Let A be ? ArraySpeciesCreate(O, 0).
+  ...
+
+  ArraySpeciesCreate ( originalArray, length )
+
+  3. Let isArray be ? IsArray(originalArray).
+  4. If isArray is false, return ? ArrayCreate(length).
+  5. Let C be ? Get(originalArray, "constructor").
+  6. If IsConstructor(C) is true, then
+    a. Let thisRealm be the current Realm Record.
+    b. Let realmC be ? GetFunctionRealm(C).
+    c. If thisRealm and realmC are not the same Realm Record, then
+      i. If SameValue(C, realmC.[[Intrinsics]].[[%Array%]]) is true, set C to undefined.
+  7. If Type(C) is Object, then
+    a. Set C to ? Get(C, @@species).
+    b. If C is null, set C to undefined.
+  8. If C is undefined, return ? ArrayCreate(length).
+  9. If IsConstructor(C) is false, throw a TypeError exception.
+  10. Return ? Construct(C, « length »).
+features: [Array.prototype.flatMap, Symbol, Symbol.species]
+includes: [compareArray.js]
+---*/
+
+assert.sameValue(typeof Array.prototype.flatMap, 'function');
+
+var arr = [[42, 1], [42, 2]];
+var mapperFn = function(e) { return e; };
+
+arr.constructor = {};
+var actual = arr.flatMap(mapperFn);
+assert.compareArray(actual, [42, 1, 42, 2], 'undefined species does not throw');
+assert.sameValue(Object.getPrototypeOf(actual), Array.prototype);
+
+var called = 0;
+arr.constructor = {
+  get [Symbol.species]() {
+    called++;
+    return 0;
+  }
+};
+assert.throws(TypeError, function() {
+  arr.flatMap(mapperFn);
+}, 'throw TypeError if @@species is a number');
+assert.sameValue(called, 1, 'got species once');
+
+called = 0;
+arr.constructor = {
+  get [Symbol.species]() {
+    called++;
+    return '';
+  }
+};
+assert.throws(TypeError, function() {
+  arr.flatMap(mapperFn);
+}, 'throw TypeError if @@species is a string');
+assert.sameValue(called, 1, 'got species once');
+
+called = 0;
+arr.constructor = {
+  get [Symbol.species]() {
+    called++;
+    return false;
+  }
+};
+assert.throws(TypeError, function() {
+  arr.flatMap(mapperFn);
+}, 'throw TypeError if @@species is a boolean');
+assert.sameValue(called, 1, 'got species once');
+
+called = 0;
+arr.constructor = {
+  get [Symbol.species]() {
+    called++;
+    return {};
+  }
+};
+assert.throws(TypeError, function() {
+  arr.flatMap(mapperFn);
+}, 'throw TypeError if @@species is an object');
+assert.sameValue(called, 1, 'got species once');
+
+called = 0;
+arr.constructor = {
+  get [Symbol.species]() {
+    called++;
+    return [];
+  }
+};
+assert.throws(TypeError, function() {
+  arr.flatMap(mapperFn);
+}, 'throw TypeError if @@species is an array');
+assert.sameValue(called, 1, 'got species once');
+
+called = 0;
+arr.constructor = {
+  get [Symbol.species]() {
+    called++;
+    return Symbol();
+  }
+};
+assert.throws(TypeError, function() {
+  arr.flatMap(mapperFn);
+}, 'throw TypeError if @@species is a symbol');
+assert.sameValue(called, 1, 'got species once');
+
+called = 0;
+arr.constructor = {
+  get [Symbol.species]() {
+    called++;
+    throw new Test262Error
+  }
+};
+assert.throws(Test262Error, function() {
+  arr.flatMap(mapperFn);
+}, 'Return abrupt completion from getting the @@species');
+assert.sameValue(called, 1, 'got species once');
\ No newline at end of file
diff --git a/test/built-ins/Array/prototype/flatMap/this-value-ctor-object-species-custom-ctor-poisoned-throws.js b/test/built-ins/Array/prototype/flatMap/this-value-ctor-object-species-custom-ctor-poisoned-throws.js
new file mode 100644
index 0000000000..3ecf17e32a
--- /dev/null
+++ b/test/built-ins/Array/prototype/flatMap/this-value-ctor-object-species-custom-ctor-poisoned-throws.js
@@ -0,0 +1,58 @@
+// Copyright (C) 2018 Leo Balter. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-array.prototype.flatMap
+description: >
+  Assert behavior if this value has a poisoned custom species constructor
+info: |
+  Array.prototype.flatMap ( mapperFunction [ , thisArg ] )
+
+  1. Let O be ? ToObject(this value).
+  2. Let sourceLen be ? ToLength(? Get(O, "length")).
+  ...
+  5. Let A be ? ArraySpeciesCreate(O, 0).
+  6. Perform ? FlattenIntoArray(A, O, sourceLen, 0, 1, mapperFunction, T).
+  7. Return A.
+
+  ArraySpeciesCreate ( originalArray, length )
+
+  3. Let isArray be ? IsArray(originalArray).
+  4. If isArray is false, return ? ArrayCreate(length).
+  5. Let C be ? Get(originalArray, "constructor").
+  6. If IsConstructor(C) is true, then
+    ...
+  7. If Type(C) is Object, then
+    a. Set C to ? Get(C, @@species).
+    b. If C is null, set C to undefined.
+  8. If C is undefined, return ? ArrayCreate(length).
+  9. If IsConstructor(C) is false, throw a TypeError exception.
+  10. Return ? Construct(C, « length »).
+features: [Array.prototype.flatMap, Symbol, Symbol.species]
+---*/
+
+assert.sameValue(typeof Array.prototype.flatMap, 'function');
+
+var arr = [];
+var mapperFn = function(e) { return e; };
+
+var called = 0;
+var ctorCalled = 0;
+function ctor(len) {
+  assert.sameValue(new.target, ctor, 'new target is defined');
+  assert.sameValue(len, 0, 'first argument is always 0');
+  ctorCalled++;
+  throw new Test262Error();
+}
+
+arr.constructor = {
+  get [Symbol.species]() {
+    called++;
+    return ctor;
+  }
+};
+assert.throws(Test262Error, function() {
+  arr.flatMap(mapperFn);
+}, 'Return abrupt completion from species custom ctor');
+assert.sameValue(called, 1, 'got species once');
+assert.sameValue(ctorCalled, 1, 'called custom ctor once');
diff --git a/test/built-ins/Array/prototype/flatMap/this-value-ctor-object-species-custom-ctor.js b/test/built-ins/Array/prototype/flatMap/this-value-ctor-object-species-custom-ctor.js
new file mode 100644
index 0000000000..c23c605f63
--- /dev/null
+++ b/test/built-ins/Array/prototype/flatMap/this-value-ctor-object-species-custom-ctor.js
@@ -0,0 +1,94 @@
+// Copyright (C) 2018 Leo Balter. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-array.prototype.flatMap
+description: >
+  Assert behavior if this value has a custom species constructor
+info: |
+  Array.prototype.flatMap ( mapperFunction [ , thisArg ] )
+
+  1. Let O be ? ToObject(this value).
+  2. Let sourceLen be ? ToLength(? Get(O, "length")).
+  ...
+  5. Let A be ? ArraySpeciesCreate(O, 0).
+  6. Perform ? FlattenIntoArray(A, O, sourceLen, 0, 1, mapperFunction, T).
+  7. Return A.
+
+  ArraySpeciesCreate ( originalArray, length )
+
+  3. Let isArray be ? IsArray(originalArray).
+  4. If isArray is false, return ? ArrayCreate(length).
+  5. Let C be ? Get(originalArray, "constructor").
+  6. If IsConstructor(C) is true, then
+    ...
+  7. If Type(C) is Object, then
+    a. Set C to ? Get(C, @@species).
+    b. If C is null, set C to undefined.
+  8. If C is undefined, return ? ArrayCreate(length).
+  9. If IsConstructor(C) is false, throw a TypeError exception.
+  10. Return ? Construct(C, « length »).
+
+  FlattenIntoArray(target, source, sourceLen, start, depth [ , mapperFunction, thisArg ])
+
+  3. Repeat, while sourceIndex < sourceLen
+    a. Let P be ! ToString(sourceIndex).
+    b. Let exists be ? HasProperty(source, P).
+    c. If exists is true, then
+      ...
+      vi. Else,
+        ...
+        2. Perform ? CreateDataPropertyOrThrow(target, ! ToString(targetIndex), element).
+features: [Array.prototype.flatMap, Symbol, Symbol.species]
+includes: [propertyHelper.js]
+---*/
+
+assert.sameValue(typeof Array.prototype.flatMap, 'function');
+
+var arr = [[42, 1], [42, 2]];
+var mapperFn = function(e) { return e; };
+
+var called = 0;
+var ctorCalled = 0;
+function ctor(len) {
+  assert.sameValue(new.target, ctor, 'new target is defined');
+  assert.sameValue(len, 0, 'first argument is always 0');
+  ctorCalled++;
+}
+
+arr.constructor = {
+  get [Symbol.species]() {
+    called++;
+    return ctor;
+  }
+};
+var actual = arr.flatMap(mapperFn);
+assert(actual instanceof ctor, 'returned value is an instance of custom ctor');
+assert.sameValue(called, 1, 'got species once');
+assert.sameValue(ctorCalled, 1, 'called custom ctor once');
+
+assert.sameValue(Object.prototype.hasOwnProperty.call(actual, 'length'), false, 'it does not define an own length property');
+verifyProperty(actual, '0', {
+  configurable: true,
+  writable: true,
+  enumerable: true,
+  value: 42
+});
+verifyProperty(actual, '1', {
+  configurable: true,
+  writable: true,
+  enumerable: true,
+  value: 1
+});
+verifyProperty(actual, '2', {
+  configurable: true,
+  writable: true,
+  enumerable: true,
+  value: 42
+});
+verifyProperty(actual, '3', {
+  configurable: true,
+  writable: true,
+  enumerable: true,
+  value: 2
+});
diff --git a/test/built-ins/Array/prototype/flatMap/this-value-ctor-object-species.js b/test/built-ins/Array/prototype/flatMap/this-value-ctor-object-species.js
new file mode 100644
index 0000000000..a8d560bff2
--- /dev/null
+++ b/test/built-ins/Array/prototype/flatMap/this-value-ctor-object-species.js
@@ -0,0 +1,141 @@
+// Copyright (C) 2018 Leo Balter. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-array.prototype.flatMap
+description: >
+  Assert behavior if this value has a custom object constructor property
+info: |
+  Array.prototype.flatMap ( mapperFunction [ , thisArg ] )
+
+  1. Let O be ? ToObject(this value).
+  2. Let sourceLen be ? ToLength(? Get(O, "length")).
+  ...
+  5. Let A be ? ArraySpeciesCreate(O, 0).
+  ...
+
+  ArraySpeciesCreate ( originalArray, length )
+
+  3. Let isArray be ? IsArray(originalArray).
+  4. If isArray is false, return ? ArrayCreate(length).
+  5. Let C be ? Get(originalArray, "constructor").
+  6. If IsConstructor(C) is true, then
+    a. Let thisRealm be the current Realm Record.
+    b. Let realmC be ? GetFunctionRealm(C).
+    c. If thisRealm and realmC are not the same Realm Record, then
+      i. If SameValue(C, realmC.[[Intrinsics]].[[%Array%]]) is true, set C to undefined.
+  7. If Type(C) is Object, then
+    a. Set C to ? Get(C, @@species).
+    b. If C is null, set C to undefined.
+  8. If C is undefined, return ? ArrayCreate(length).
+  9. If IsConstructor(C) is false, throw a TypeError exception.
+  10. Return ? Construct(C, « length »).
+features: [Array.prototype.flatMap, Symbol, Symbol.species]
+includes: [compareArray.js]
+---*/
+
+assert.sameValue(typeof Array.prototype.flatMap, 'function');
+
+var arr = [[42, 1], [42, 2]];
+var mapperFn = function(e) { return e; };
+
+arr.constructor = {};
+var actual = arr.flatMap(mapperFn);
+assert.compareArray(actual, [42, 1, 42, 2], 'undefined species does not throw');
+assert.sameValue(Object.getPrototypeOf(actual), Array.prototype);
+
+var called = 0;
+arr.constructor = {
+  get [Symbol.species]() {
+    called++;
+    return null;
+  }
+};
+actual = arr.flatMap(mapperFn);
+assert.compareArray(actual, [42, 1, 42, 2], 'null species value does not throw');
+assert.sameValue(Object.getPrototypeOf(actual), Array.prototype);
+assert.sameValue(called, 1, 'got species once');
+
+called = 0;
+arr.constructor = {
+  get [Symbol.species]() {
+    called++;
+    return undefined;
+  }
+};
+actual = arr.flatMap(mapperFn);
+assert.compareArray(actual, [42, 1, 42, 2], 'undefined species value does not throw');
+assert.sameValue(Object.getPrototypeOf(actual), Array.prototype);
+assert.sameValue(called, 1, 'got species once');
+
+called = 0;
+arr.constructor = {
+  get [Symbol.species]() {
+    called++;
+    return 0;
+  }
+};
+assert.throws(TypeError, function() {
+  arr.flatMap(mapperFn);
+}, 'throw TypeError if @@species is a number');
+assert.sameValue(called, 1, 'got species once');
+
+called = 0;
+arr.constructor = {
+  get [Symbol.species]() {
+    called++;
+    return '';
+  }
+};
+assert.throws(TypeError, function() {
+  arr.flatMap(mapperFn);
+}, 'throw TypeError if @@species is a string');
+assert.sameValue(called, 1, 'got species once');
+
+called = 0;
+arr.constructor = {
+  get [Symbol.species]() {
+    called++;
+    return false;
+  }
+};
+assert.throws(TypeError, function() {
+  arr.flatMap(mapperFn);
+}, 'throw TypeError if @@species is a boolean');
+assert.sameValue(called, 1, 'got species once');
+
+called = 0;
+arr.constructor = {
+  get [Symbol.species]() {
+    called++;
+    return {};
+  }
+};
+assert.throws(TypeError, function() {
+  arr.flatMap(mapperFn);
+}, 'throw TypeError if @@species is an object');
+assert.sameValue(called, 1, 'got species once');
+
+called = 0;
+arr.constructor = {
+  get [Symbol.species]() {
+    called++;
+    return [];
+  }
+};
+assert.throws(TypeError, function() {
+  arr.flatMap(mapperFn);
+}, 'throw TypeError if @@species is an array');
+assert.sameValue(called, 1, 'got species once');
+
+called = 0;
+arr.constructor = {
+  get [Symbol.species]() {
+    called++;
+    return Symbol();
+  }
+};
+assert.throws(TypeError, function() {
+  arr.flatMap(mapperFn);
+}, 'throw TypeError if @@species is a symbol');
+assert.sameValue(called, 1, 'got species once');
diff --git a/test/built-ins/Array/prototype/flatMap/null-undefined-input-throws.js b/test/built-ins/Array/prototype/flatMap/this-value-null-undefined-throws.js
similarity index 60%
rename from test/built-ins/Array/prototype/flatMap/null-undefined-input-throws.js
rename to test/built-ins/Array/prototype/flatMap/this-value-null-undefined-throws.js
index 44630d9644..b7b9845603 100644
--- a/test/built-ins/Array/prototype/flatMap/null-undefined-input-throws.js
+++ b/test/built-ins/Array/prototype/flatMap/this-value-null-undefined-throws.js
@@ -3,20 +3,23 @@
 /*---
 esid: sec-array.prototype.flatMap
 description: >
-    null or undefined should throw TypeError Exception
+  Throw a TypeError if this value is null or undefined
+info: |
+  Array.prototype.flatMap ( mapperFunction [ , thisArg ] )
+
+  1. Let O be ? ToObject(this value).
+  ...
 features: [Array.prototype.flatMap]
 ---*/
 
 assert.sameValue(typeof Array.prototype.flatMap, 'function');
 
-assert.throws(TypeError, function() {
-  [].flatMap.call(null);
-}, 'null value');
+var mapperFn = function() {};
 
 assert.throws(TypeError, function() {
-  [].flatMap.call();
-}, 'missing');
+  [].flatMap.call(null, mapperFn);
+}, 'null value');
 
 assert.throws(TypeError, function() {
-  [].flatMap.call(void 0);
+  [].flatMap.call(undefined, mapperFn);
 }, 'undefined');
diff --git a/test/built-ins/Array/prototype/flatMap/thisArg-argument.js b/test/built-ins/Array/prototype/flatMap/thisArg-argument.js
index 34db60abdc..24b52af747 100644
--- a/test/built-ins/Array/prototype/flatMap/thisArg-argument.js
+++ b/test/built-ins/Array/prototype/flatMap/thisArg-argument.js
@@ -5,28 +5,41 @@ esid: sec-array.prototype.flatMap
 description: >
     Behavior when thisArg is provided
     Array.prototype.flatMap ( mapperFunction [ , thisArg ] )
-includes: [compareArray.js]
 flags: [onlyStrict]
+includes: [compareArray.js]
 features: [Array.prototype.flatMap]
 ---*/
 
 var a = {};
+var actual;
 
-assert(compareArray([1].flatMap(function() {
+actual = [1].flatMap(function() {
   return [this];
-}, "TestString"), ["TestString"]));
-assert(compareArray([1].flatMap(function() {
+}, "TestString");
+assert.compareArray(actual, ["TestString"]);
+
+actual = [1].flatMap(function() {
   return [this];
-}, 1), [1]));
-assert(compareArray([1].flatMap(function() {
+}, 1);
+assert.compareArray(actual, [1]);
+
+actual = [1].flatMap(function() {
   return [this];
-}, null), [null]));
-assert(compareArray([1].flatMap(function() {
+}, null);
+assert.compareArray(actual, [null]);
+
+actual = [1].flatMap(function() {
   return [this];
-}, true), [true]));
-assert(compareArray([1].flatMap(function() {
+}, true);
+assert.compareArray(actual, [true]);
+
+actual = [1].flatMap(function() {
   return [this];
-}, a), [a]));
-assert(compareArray([1].flatMap(function() {
+}, a);
+assert.compareArray(actual, [a]);
+
+actual = [1].flatMap(function() {
   return [this];
-}, void 0), [undefined]));
+}, void 0);
+assert.compareArray(actual, [undefined]);
+
-- 
GitLab