Skip to content

Commit

Permalink
Implement Array.prototype.flat(Map) with some basic tests (dop251#240)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
mstoykov and dop251 authored Jan 11, 2021
1 parent 27b0a7d commit 8b568f3
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 0 deletions.
63 changes: 63 additions & 0 deletions builtin_array.go
Original file line number Diff line number Diff line change
Expand Up @@ -1023,6 +1023,65 @@ func (r *Runtime) arrayproto_findIndex(call FunctionCall) Value {
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 {
if arr, ok := obj.self.(*arrayObject); ok &&
arr.propValueCount == 0 &&
Expand Down Expand Up @@ -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("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("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("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)
Expand Down Expand Up @@ -1236,6 +1297,8 @@ func (r *Runtime) createArrayProto(val *Object) objectImpl {
bl.setOwnStr("fill", valueTrue, true)
bl.setOwnStr("find", valueTrue, true)
bl.setOwnStr("findIndex", valueTrue, true)
bl.setOwnStr("flat", valueTrue, true)
bl.setOwnStr("flatMap", valueTrue, true)
bl.setOwnStr("includes", valueTrue, true)
bl.setOwnStr("keys", valueTrue, true)
bl.setOwnStr("values", valueTrue, true)
Expand Down
26 changes: 26 additions & 0 deletions builtin_arrray_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,3 +265,29 @@ func TestArrayConcat(t *testing.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)
}

0 comments on commit 8b568f3

Please sign in to comment.