Browse Source

Array.prototype.filter() and find()

Dmitry Panov 5 years ago
parent
commit
3274eaa601
2 changed files with 88 additions and 38 deletions
  1. 60 17
      builtin_array.go
  2. 28 21
      tc39_test.go

+ 60 - 17
builtin_array.go

@@ -261,24 +261,28 @@ func (r *Runtime) arrayproto_concat_append(a *Object, item Value) {
 	a.self.putStr("length", intToValue(aLength), true)
 }
 
-func arraySpeciesCreate(obj *Object, size int) *Object {
+func arraySpeciesCreate(obj *Object, size int64) *Object {
 	if isArray(obj) {
 		v := obj.self.getStr("constructor")
 		if constructObj, ok := v.(*Object); ok {
-			species := constructObj.self.get(symSpecies)
-			if species != nil && !IsUndefined(species) && !IsNull(species) {
-				constructObj, _ = species.(*Object)
-				if constructObj != nil {
-					constructor := getConstructor(constructObj)
-					if constructor != nil {
-						return constructor([]Value{intToValue(int64(size))})
-					}
+			v = constructObj.self.get(symSpecies)
+			if v == _null {
+				v = nil
+			}
+		}
+
+		if v != nil && v != _undefined {
+			constructObj, _ := v.(*Object)
+			if constructObj != nil {
+				constructor := getConstructor(constructObj)
+				if constructor != nil {
+					return constructor([]Value{intToValue(size)})
 				}
-				panic(obj.runtime.NewTypeError())
 			}
+			panic(obj.runtime.NewTypeError("Species is not a constructor"))
 		}
 	}
-	return obj.runtime.newArrayValues(nil)
+	return obj.runtime.newArrayLength(size)
 }
 
 func (r *Runtime) arrayproto_concat(call FunctionCall) Value {
@@ -624,24 +628,42 @@ func (r *Runtime) arrayproto_filter(call FunctionCall) Value {
 	length := toLength(o.self.getStr("length"))
 	callbackFn := call.Argument(0).ToObject(r)
 	if callbackFn, ok := callbackFn.self.assertCallable(); ok {
-		a := r.newArrayObject()
+		a := arraySpeciesCreate(o, 0)
 		fc := FunctionCall{
 			This:      call.Argument(1),
 			Arguments: []Value{nil, nil, o},
 		}
+		if _, stdSrc := o.self.(*arrayObject); stdSrc {
+			if arr := r.checkStdArrayObj(a); arr != nil {
+				var values []Value
+				for k := int64(0); k < length; k++ {
+					idx := intToValue(k)
+					if val := o.self.get(idx); val != nil {
+						fc.Arguments[0] = val
+						fc.Arguments[1] = idx
+						if callbackFn(fc).ToBoolean() {
+							values = append(values, val)
+						}
+					}
+				}
+				setArrayValues(arr, values)
+				return a
+			}
+		}
+
+		to := int64(0)
 		for k := int64(0); k < length; k++ {
 			idx := intToValue(k)
 			if val := o.self.get(idx); val != nil {
 				fc.Arguments[0] = val
 				fc.Arguments[1] = idx
 				if callbackFn(fc).ToBoolean() {
-					a.values = append(a.values, val)
+					defineDataPropertyOrThrow(a, intToValue(to), val)
+					to++
 				}
 			}
 		}
-		a.length = int64(len(a.values))
-		a.objCount = a.length
-		return a.val
+		return a
 	} else {
 		r.typeErrorResult(true, "%s is not a function", call.Argument(0))
 	}
@@ -896,6 +918,26 @@ func (r *Runtime) arrayproto_fill(call FunctionCall) Value {
 	return o
 }
 
+func (r *Runtime) arrayproto_find(call FunctionCall) Value {
+	o := call.This.ToObject(r)
+	l := toLength(o.self.getStr("length"))
+	predicate := r.toCallable(call.Argument(0))
+	fc := FunctionCall{
+		This:      call.Argument(1),
+		Arguments: []Value{nil, nil, o},
+	}
+	for k := int64(0); k < l; k++ {
+		idx := intToValue(k)
+		kValue := o.self.get(idx)
+		fc.Arguments[0], fc.Arguments[1] = kValue, idx
+		if predicate(fc).ToBoolean() {
+			return kValue
+		}
+	}
+
+	return _undefined
+}
+
 func (r *Runtime) checkStdArrayObj(obj *Object) *arrayObject {
 	if arr, ok := obj.self.(*arrayObject); ok &&
 		arr.propValueCount == 0 &&
@@ -1072,6 +1114,8 @@ func (r *Runtime) createArrayProto(val *Object) objectImpl {
 	o._putProp("copyWithin", r.newNativeFunc(r.arrayproto_copyWithin, nil, "copyWithin", nil, 2), true, false, true)
 	o._putProp("entries", r.newNativeFunc(r.arrayproto_entries, nil, "entries", nil, 0), true, false, true)
 	o._putProp("fill", r.newNativeFunc(r.arrayproto_fill, nil, "fill", 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("pop", r.newNativeFunc(r.arrayproto_pop, nil, "pop", nil, 0), true, false, true)
 	o._putProp("push", r.newNativeFunc(r.arrayproto_push, nil, "push", nil, 1), true, false, true)
 	o._putProp("join", r.newNativeFunc(r.arrayproto_join, nil, "join", nil, 1), true, false, true)
@@ -1090,7 +1134,6 @@ func (r *Runtime) createArrayProto(val *Object) objectImpl {
 	o._putProp("some", r.newNativeFunc(r.arrayproto_some, nil, "some", nil, 1), true, false, true)
 	o._putProp("forEach", r.newNativeFunc(r.arrayproto_forEach, nil, "forEach", nil, 1), true, false, true)
 	o._putProp("map", r.newNativeFunc(r.arrayproto_map, nil, "map", nil, 1), true, false, true)
-	o._putProp("filter", r.newNativeFunc(r.arrayproto_filter, nil, "filter", nil, 1), true, false, true)
 	o._putProp("reduce", r.newNativeFunc(r.arrayproto_reduce, nil, "reduce", nil, 1), true, false, true)
 	o._putProp("reduceRight", r.newNativeFunc(r.arrayproto_reduceRight, nil, "reduceRight", nil, 1), true, false, true)
 	valuesFunc := r.newNativeFunc(r.arrayproto_values, nil, "values", nil, 0)

+ 28 - 21
tc39_test.go

@@ -33,26 +33,30 @@ var (
 		"test/annexB/built-ins/escape/escape-above-astral.js":         true, // \u{xxxxx}
 
 		// cross-realm
-		"test/built-ins/Symbol/unscopables/cross-realm.js":        true,
-		"test/built-ins/Symbol/toStringTag/cross-realm.js":        true,
-		"test/built-ins/Symbol/toPrimitive/cross-realm.js":        true,
-		"test/built-ins/Symbol/split/cross-realm.js":              true,
-		"test/built-ins/Symbol/species/cross-realm.js":            true,
-		"test/built-ins/Symbol/search/cross-realm.js":             true,
-		"test/built-ins/Symbol/replace/cross-realm.js":            true,
-		"test/built-ins/Symbol/match/cross-realm.js":              true,
-		"test/built-ins/Symbol/keyFor/cross-realm.js":             true,
-		"test/built-ins/Symbol/iterator/cross-realm.js":           true,
-		"test/built-ins/Symbol/isConcatSpreadable/cross-realm.js": true,
-		"test/built-ins/Symbol/hasInstance/cross-realm.js":        true,
-		"test/built-ins/Symbol/for/cross-realm.js":                true,
-		"test/built-ins/WeakSet/proto-from-ctor-realm.js":         true,
-		"test/built-ins/WeakMap/proto-from-ctor-realm.js":         true,
-		"test/built-ins/Map/proto-from-ctor-realm.js":             true,
-		"test/built-ins/Set/proto-from-ctor-realm.js":             true,
-		"test/built-ins/Object/proto-from-ctor.js":                true,
-		"test/built-ins/Array/from/proto-from-ctor-realm.js":      true,
-		"test/built-ins/Array/of/proto-from-ctor-realm.js":        true,
+		"test/built-ins/Symbol/unscopables/cross-realm.js":                                true,
+		"test/built-ins/Symbol/toStringTag/cross-realm.js":                                true,
+		"test/built-ins/Symbol/toPrimitive/cross-realm.js":                                true,
+		"test/built-ins/Symbol/split/cross-realm.js":                                      true,
+		"test/built-ins/Symbol/species/cross-realm.js":                                    true,
+		"test/built-ins/Symbol/search/cross-realm.js":                                     true,
+		"test/built-ins/Symbol/replace/cross-realm.js":                                    true,
+		"test/built-ins/Symbol/match/cross-realm.js":                                      true,
+		"test/built-ins/Symbol/keyFor/cross-realm.js":                                     true,
+		"test/built-ins/Symbol/iterator/cross-realm.js":                                   true,
+		"test/built-ins/Symbol/isConcatSpreadable/cross-realm.js":                         true,
+		"test/built-ins/Symbol/hasInstance/cross-realm.js":                                true,
+		"test/built-ins/Symbol/for/cross-realm.js":                                        true,
+		"test/built-ins/WeakSet/proto-from-ctor-realm.js":                                 true,
+		"test/built-ins/WeakMap/proto-from-ctor-realm.js":                                 true,
+		"test/built-ins/Map/proto-from-ctor-realm.js":                                     true,
+		"test/built-ins/Set/proto-from-ctor-realm.js":                                     true,
+		"test/built-ins/Object/proto-from-ctor.js":                                        true,
+		"test/built-ins/Array/from/proto-from-ctor-realm.js":                              true,
+		"test/built-ins/Array/of/proto-from-ctor-realm.js":                                true,
+		"test/built-ins/Array/prototype/concat/create-proto-from-ctor-realm-non-array.js": true,
+		"test/built-ins/Array/prototype/concat/create-proto-from-ctor-realm-array.js":     true,
+		"test/built-ins/Array/prototype/filter/create-proto-from-ctor-realm-non-array.js": true,
+		"test/built-ins/Array/prototype/filter/create-proto-from-ctor-realm-array.js":     true,
 
 		// class
 		"test/language/statements/class/subclass/builtin-objects/Symbol/symbol-valid-as-extends-value.js": true,
@@ -104,10 +108,13 @@ var (
 		"22.1.2.1",
 		"22.1.2.3",
 		"22.1.2.5",
-		//"22.1.3.1",
+		"22.1.3.1",
 		"22.1.3.3",
 		"22.1.3.4",
+		"22.1.3.5",
 		"22.1.3.6",
+		"22.1.3.7",
+		"22.1.3.8",
 		"22.1.3.29",
 		"23.1",
 		"23.2",