Explorar el Código

Implement Array.prototype.flat(Map) with some basic tests (#240)

* Implement Array.prototype.flat(Map) with some basic tests

* Apply suggestions from code review

Co-authored-by: Dmitry Panov <[email protected]>

* Add flat/flatMap to unscopables

* Don't call the map function on the already mapped elements

This is exactly as the specification says it needs to be ... but I
either glossed over it or fixed it because of how I understood it should
work :(

* Check for property existance as string

This is so
https://github.com/tc39/test262/blob/42bf3a9f7aed3790dc1d829f290dee033df41c5b/test/built-ins/Array/prototype/flat/proxy-access-count.js
passes and also because this is what the spec says

* Update builtin_array.go

Co-authored-by: Dmitry Panov <[email protected]>

Co-authored-by: Dmitry Panov <[email protected]>
Mihail Stoykov hace 4 años
padre
commit
8b568f3b8b
Se han modificado 2 ficheros con 89 adiciones y 0 borrados
  1. 63 0
      builtin_array.go
  2. 26 0
      builtin_arrray_test.go

+ 63 - 0
builtin_array.go

@@ -1023,6 +1023,65 @@ func (r *Runtime) arrayproto_findIndex(call FunctionCall) Value {
 	return intToValue(-1)
 	return intToValue(-1)
 }
 }
 
 
+func (r *Runtime) arrayproto_flat(call FunctionCall) Value {
+	o := call.This.ToObject(r)
+	l := toLength(o.self.getStr("length", nil))
+	depthNum := int64(1)
+	if len(call.Arguments) > 0 {
+		depthNum = call.Argument(0).ToInteger()
+	}
+	a := arraySpeciesCreate(o, 0)
+	r.flattenIntoArray(a, o, l, 0, depthNum, nil, nil)
+	return a
+}
+
+func (r *Runtime) flattenIntoArray(target, source *Object, sourceLen, start, depth int64, mapperFunction func(FunctionCall) Value, thisArg Value) int64 {
+	targetIndex, sourceIndex := start, int64(0)
+	for sourceIndex < sourceLen {
+		p := intToValue(sourceIndex)
+		if source.hasProperty(p.toString()) {
+			element := source.get(p, source)
+			if mapperFunction != nil {
+				element = mapperFunction(FunctionCall{
+					This:      thisArg,
+					Arguments: []Value{element, p, source},
+				})
+			}
+			var elementArray *Object
+			if depth > 0 {
+				if elementObj, ok := element.(*Object); ok && isArray(elementObj) {
+					elementArray = elementObj
+				}
+			}
+			if elementArray != nil {
+				elementLen := toLength(elementArray.self.getStr("length", nil))
+				targetIndex = r.flattenIntoArray(target, elementArray, elementLen, targetIndex, depth-1, nil, nil)
+			} else {
+				if targetIndex >= maxInt-1 {
+					panic(r.NewTypeError("Invalid array length"))
+				}
+				createDataPropertyOrThrow(target, intToValue(targetIndex), element)
+				targetIndex++
+			}
+		}
+		sourceIndex++
+	}
+	return targetIndex
+}
+
+func (r *Runtime) arrayproto_flatMap(call FunctionCall) Value {
+	o := call.This.ToObject(r)
+	l := toLength(o.self.getStr("length", nil))
+	callbackFn := r.toCallable(call.Argument(0))
+	thisArg := Undefined()
+	if len(call.Arguments) > 1 {
+		thisArg = call.Argument(1)
+	}
+	a := arraySpeciesCreate(o, 0)
+	r.flattenIntoArray(a, o, l, 0, 1, callbackFn, thisArg)
+	return a
+}
+
 func (r *Runtime) checkStdArrayObj(obj *Object) *arrayObject {
 func (r *Runtime) checkStdArrayObj(obj *Object) *arrayObject {
 	if arr, ok := obj.self.(*arrayObject); ok &&
 	if arr, ok := obj.self.(*arrayObject); ok &&
 		arr.propValueCount == 0 &&
 		arr.propValueCount == 0 &&
@@ -1204,6 +1263,8 @@ func (r *Runtime) createArrayProto(val *Object) objectImpl {
 	o._putProp("filter", r.newNativeFunc(r.arrayproto_filter, nil, "filter", nil, 1), true, false, true)
 	o._putProp("filter", r.newNativeFunc(r.arrayproto_filter, nil, "filter", nil, 1), true, false, true)
 	o._putProp("find", r.newNativeFunc(r.arrayproto_find, nil, "find", nil, 1), true, false, true)
 	o._putProp("find", r.newNativeFunc(r.arrayproto_find, nil, "find", nil, 1), true, false, true)
 	o._putProp("findIndex", r.newNativeFunc(r.arrayproto_findIndex, nil, "findIndex", nil, 1), true, false, true)
 	o._putProp("findIndex", r.newNativeFunc(r.arrayproto_findIndex, nil, "findIndex", nil, 1), true, false, true)
+	o._putProp("flat", r.newNativeFunc(r.arrayproto_flat, nil, "flat", nil, 0), true, false, true)
+	o._putProp("flatMap", r.newNativeFunc(r.arrayproto_flatMap, nil, "flatMap", nil, 1), true, false, true)
 	o._putProp("forEach", r.newNativeFunc(r.arrayproto_forEach, nil, "forEach", nil, 1), true, false, true)
 	o._putProp("forEach", r.newNativeFunc(r.arrayproto_forEach, nil, "forEach", nil, 1), true, false, true)
 	o._putProp("includes", r.newNativeFunc(r.arrayproto_includes, nil, "includes", nil, 1), true, false, true)
 	o._putProp("includes", r.newNativeFunc(r.arrayproto_includes, nil, "includes", nil, 1), true, false, true)
 	o._putProp("indexOf", r.newNativeFunc(r.arrayproto_indexOf, nil, "indexOf", nil, 1), true, false, true)
 	o._putProp("indexOf", r.newNativeFunc(r.arrayproto_indexOf, nil, "indexOf", nil, 1), true, false, true)
@@ -1236,6 +1297,8 @@ func (r *Runtime) createArrayProto(val *Object) objectImpl {
 	bl.setOwnStr("fill", valueTrue, true)
 	bl.setOwnStr("fill", valueTrue, true)
 	bl.setOwnStr("find", valueTrue, true)
 	bl.setOwnStr("find", valueTrue, true)
 	bl.setOwnStr("findIndex", valueTrue, true)
 	bl.setOwnStr("findIndex", valueTrue, true)
+	bl.setOwnStr("flat", valueTrue, true)
+	bl.setOwnStr("flatMap", valueTrue, true)
 	bl.setOwnStr("includes", valueTrue, true)
 	bl.setOwnStr("includes", valueTrue, true)
 	bl.setOwnStr("keys", valueTrue, true)
 	bl.setOwnStr("keys", valueTrue, true)
 	bl.setOwnStr("values", valueTrue, true)
 	bl.setOwnStr("values", valueTrue, true)

+ 26 - 0
builtin_arrray_test.go

@@ -265,3 +265,29 @@ func TestArrayConcat(t *testing.T) {
 	`
 	`
 	testScript1(TESTLIBX+SCRIPT, _undefined, t)
 	testScript1(TESTLIBX+SCRIPT, _undefined, t)
 }
 }
+
+func TestArrayFlat(t *testing.T) {
+	const SCRIPT = `
+	var array = [1, [2,3,[4,5,6]], [[[[7,8,9]]]]];
+	assert(deepEqual(array.flat(), [1,2,3,[4,5,6],[[[7,8,9]]]]), '#1');
+	assert(deepEqual(array.flat(1), [1,2,3,[4,5,6],[[[7,8,9]]]]), '#2');
+	assert(deepEqual(array.flat(3), [1,2,3,4,5,6,[7,8,9]]), '#3');
+	assert(deepEqual(array.flat(4), [1,2,3,4,5,6,7,8,9]), '#4');
+	assert(deepEqual(array.flat(10), [1,2,3,4,5,6,7,8,9]), '#5');
+	`
+	testScript1(TESTLIBX+SCRIPT, _undefined, t)
+}
+
+func TestArrayFlatMap(t *testing.T) {
+	const SCRIPT = `
+	var double = function(x) {
+		if (isNaN(x)) {
+			return x
+		}
+		return x * 2
+	}
+	var array = [1, [2,3,[4,5,6]], [[[[7,8,9]]]]];
+	assert(deepEqual(array.flatMap(double), [2,2,3,[4,5,6],[[[7,8,9]]]]), '#1');
+	`
+	testScript1(TESTLIBX+SCRIPT, _undefined, t)
+}