Browse Source

Array, Array.prototype

Dmitry Panov 5 years ago
parent
commit
d71b886827
2 changed files with 299 additions and 237 deletions
  1. 280 227
      builtin_array.go
  2. 19 10
      tc39_test.go

+ 280 - 227
builtin_array.go

@@ -1,7 +1,6 @@
 package goja
 package goja
 
 
 import (
 import (
-	"bytes"
 	"sort"
 	"sort"
 	"strings"
 	"strings"
 )
 )
@@ -35,6 +34,44 @@ func setArrayLength(a *arrayObject, l int64) *arrayObject {
 	return a
 	return a
 }
 }
 
 
+func arraySpeciesCreate(obj *Object, size int64) *Object {
+	if isArray(obj) {
+		v := obj.self.getStr("constructor")
+		if constructObj, ok := v.(*Object); ok {
+			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("Species is not a constructor"))
+		}
+	}
+	return obj.runtime.newArrayLength(size)
+}
+
+func max(a, b int64) int64 {
+	if a > b {
+		return a
+	}
+	return b
+}
+
+func min(a, b int64) int64 {
+	if a < b {
+		return a
+	}
+	return b
+}
+
 func relToIdx(rel, l int64) int64 {
 func relToIdx(rel, l int64) int64 {
 	if rel >= 0 {
 	if rel >= 0 {
 		return min(rel, l)
 		return min(rel, l)
@@ -147,7 +184,7 @@ func (r *Runtime) arrayproto_join(call FunctionCall) Value {
 		return stringEmpty
 		return stringEmpty
 	}
 	}
 
 
-	var buf bytes.Buffer
+	var buf strings.Builder
 
 
 	element0 := o.self.get(intToValue(0))
 	element0 := o.self.get(intToValue(0))
 	if element0 != nil && element0 != _undefined && element0 != _null {
 	if element0 != nil && element0 != _undefined && element0 != _null {
@@ -180,59 +217,43 @@ func (r *Runtime) arrayproto_toString(call FunctionCall) Value {
 	})
 	})
 }
 }
 
 
-func (r *Runtime) writeItemLocaleString(item Value, buf *bytes.Buffer) {
+func (r *Runtime) writeItemLocaleString(item Value, buf *strings.Builder) {
 	if item != nil && item != _undefined && item != _null {
 	if item != nil && item != _undefined && item != _null {
-		itemObj := item.ToObject(r)
-		if f, ok := itemObj.self.getStr("toLocaleString").(*Object); ok {
+		if f, ok := r.getVStr(item, "toLocaleString").(*Object); ok {
 			if c, ok := f.self.assertCallable(); ok {
 			if c, ok := f.self.assertCallable(); ok {
 				strVal := c(FunctionCall{
 				strVal := c(FunctionCall{
-					This: itemObj,
+					This: item,
 				})
 				})
-				buf.WriteString(strVal.String())
+				buf.WriteString(strVal.ToPrimitiveString().String())
 				return
 				return
 			}
 			}
 		}
 		}
-		r.typeErrorResult(true, "Property 'toLocaleString' of object %s is not a function", itemObj)
+		r.typeErrorResult(true, "Property 'toLocaleString' of object %s is not a function", item)
 	}
 	}
 }
 }
 
 
-func (r *Runtime) arrayproto_toLocaleString_generic(obj *Object, start int64, buf *bytes.Buffer) Value {
-	length := toLength(obj.self.getStr("length"))
-	for i := int64(start); i < length; i++ {
-		if i > 0 {
-			buf.WriteByte(',')
-		}
-		item := obj.self.get(intToValue(i))
-		r.writeItemLocaleString(item, buf)
-	}
-	return newStringValue(buf.String())
-}
-
 func (r *Runtime) arrayproto_toLocaleString(call FunctionCall) Value {
 func (r *Runtime) arrayproto_toLocaleString(call FunctionCall) Value {
 	array := call.This.ToObject(r)
 	array := call.This.ToObject(r)
-	if a, ok := array.self.(*arrayObject); ok {
-		var buf bytes.Buffer
-		for i := int64(0); i < a.length; i++ {
-			var item Value
-			if i < int64(len(a.values)) {
-				item = a.values[i]
-			}
-			if item == nil {
-				return r.arrayproto_toLocaleString_generic(array, i, &buf)
-			}
-			if prop, ok := item.(*valueProperty); ok {
-				item = prop.get(array)
-			}
+	var buf strings.Builder
+	if a := r.checkStdArrayObj(array); a != nil {
+		for i, item := range a.values {
 			if i > 0 {
 			if i > 0 {
 				buf.WriteByte(',')
 				buf.WriteByte(',')
 			}
 			}
 			r.writeItemLocaleString(item, &buf)
 			r.writeItemLocaleString(item, &buf)
 		}
 		}
-		return newStringValue(buf.String())
 	} else {
 	} else {
-		return r.arrayproto_toLocaleString_generic(array, 0, bytes.NewBuffer(nil))
+		length := toLength(array.self.getStr("length"))
+		for i := int64(0); i < length; i++ {
+			if i > 0 {
+				buf.WriteByte(',')
+			}
+			item := array.self.get(intToValue(i))
+			r.writeItemLocaleString(item, &buf)
+		}
 	}
 	}
 
 
+	return newStringValue(buf.String())
 }
 }
 
 
 func isConcatSpreadable(obj *Object) bool {
 func isConcatSpreadable(obj *Object) bool {
@@ -261,30 +282,6 @@ func (r *Runtime) arrayproto_concat_append(a *Object, item Value) {
 	a.self.putStr("length", intToValue(aLength), true)
 	a.self.putStr("length", intToValue(aLength), true)
 }
 }
 
 
-func arraySpeciesCreate(obj *Object, size int64) *Object {
-	if isArray(obj) {
-		v := obj.self.getStr("constructor")
-		if constructObj, ok := v.(*Object); ok {
-			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("Species is not a constructor"))
-		}
-	}
-	return obj.runtime.newArrayLength(size)
-}
-
 func (r *Runtime) arrayproto_concat(call FunctionCall) Value {
 func (r *Runtime) arrayproto_concat(call FunctionCall) Value {
 	obj := call.This.ToObject(r)
 	obj := call.This.ToObject(r)
 	a := arraySpeciesCreate(obj, 0)
 	a := arraySpeciesCreate(obj, 0)
@@ -295,20 +292,6 @@ func (r *Runtime) arrayproto_concat(call FunctionCall) Value {
 	return a
 	return a
 }
 }
 
 
-func max(a, b int64) int64 {
-	if a > b {
-		return a
-	}
-	return b
-}
-
-func min(a, b int64) int64 {
-	if a < b {
-		return a
-	}
-	return b
-}
-
 func (r *Runtime) arrayproto_slice(call FunctionCall) Value {
 func (r *Runtime) arrayproto_slice(call FunctionCall) Value {
 	o := call.This.ToObject(r)
 	o := call.This.ToObject(r)
 	length := toLength(o.self.getStr("length"))
 	length := toLength(o.self.getStr("length"))
@@ -326,13 +309,16 @@ func (r *Runtime) arrayproto_slice(call FunctionCall) Value {
 		count = 0
 		count = 0
 	}
 	}
 
 
-	if arr := r.checkStdArrayObj(o); arr != nil {
-		values := make([]Value, count)
-		copy(values, arr.values[start:])
-		return r.newArrayValues(values)
+	a := arraySpeciesCreate(o, count)
+	if src := r.checkStdArrayObj(o); src != nil {
+		if dst, ok := a.self.(*arrayObject); ok {
+			values := make([]Value, count)
+			copy(values, src.values[start:])
+			setArrayValues(dst, values)
+			return a
+		}
 	}
 	}
 
 
-	a := r.newArrayLength(count)
 	n := int64(0)
 	n := int64(0)
 	for start < end {
 	for start < end {
 		p := o.self.get(intToValue(start))
 		p := o.self.get(intToValue(start))
@@ -365,53 +351,97 @@ func (r *Runtime) arrayproto_sort(call FunctionCall) Value {
 
 
 func (r *Runtime) arrayproto_splice(call FunctionCall) Value {
 func (r *Runtime) arrayproto_splice(call FunctionCall) Value {
 	o := call.This.ToObject(r)
 	o := call.This.ToObject(r)
-	a := r.newArrayValues(nil)
 	length := toLength(o.self.getStr("length"))
 	length := toLength(o.self.getStr("length"))
 	actualStart := relToIdx(call.Argument(0).ToInteger(), length)
 	actualStart := relToIdx(call.Argument(0).ToInteger(), length)
-
-	actualDeleteCount := min(max(call.Argument(1).ToInteger(), 0), length-actualStart)
-
-	for k := int64(0); k < actualDeleteCount; k++ {
-		from := intToValue(k + actualStart)
-		if o.self.hasProperty(from) {
-			a.self.put(intToValue(k), o.self.get(from), false)
-		}
-	}
-
+	var actualDeleteCount int64
+	switch len(call.Arguments) {
+	case 0:
+	case 1:
+		actualDeleteCount = length - actualStart
+	default:
+		actualDeleteCount = min(max(call.Argument(1).ToInteger(), 0), length-actualStart)
+	}
+	a := arraySpeciesCreate(o, actualDeleteCount)
 	itemCount := max(int64(len(call.Arguments)-2), 0)
 	itemCount := max(int64(len(call.Arguments)-2), 0)
-	if itemCount < actualDeleteCount {
-		for k := actualStart; k < length-actualDeleteCount; k++ {
-			from := intToValue(k + actualDeleteCount)
-			to := intToValue(k + itemCount)
-			if o.self.hasProperty(from) {
-				o.self.put(to, o.self.get(from), true)
+	newLength := length - actualDeleteCount + itemCount
+	if src := r.checkStdArrayObj(o); src != nil {
+		if dst, ok := a.self.(*arrayObject); ok {
+			values := make([]Value, actualDeleteCount)
+			copy(values, src.values[actualStart:])
+			setArrayValues(dst, values)
+		} else {
+			for k := int64(0); k < actualDeleteCount; k++ {
+				defineDataPropertyOrThrow(a, intToValue(k), src.values[k+actualStart])
+			}
+		}
+		var values []Value
+		if itemCount < actualDeleteCount {
+			values = src.values
+			copy(values[actualStart+itemCount:], values[actualStart+actualDeleteCount:])
+			tail := values[newLength:]
+			for k := range tail {
+				tail[k] = nil
+			}
+			values = values[:newLength]
+		} else if itemCount > actualDeleteCount {
+			if int64(cap(src.values)) >= newLength {
+				values = src.values[:newLength]
+				copy(values[actualStart+itemCount:], values[actualStart+actualDeleteCount:length])
 			} else {
 			} else {
-				o.self.delete(to, true)
+				values = make([]Value, newLength)
+				copy(values, src.values[:actualStart])
+				copy(values[actualStart+itemCount:], src.values[actualStart+actualDeleteCount:])
 			}
 			}
+		} else {
+			values = src.values
 		}
 		}
-
-		for k := length; k > length-actualDeleteCount+itemCount; k-- {
-			o.self.delete(intToValue(k-1), true)
+		if itemCount > 0 {
+			copy(values[actualStart:], call.Arguments[2:])
 		}
 		}
-	} else if itemCount > actualDeleteCount {
-		for k := length - actualDeleteCount; k > actualStart; k-- {
-			from := intToValue(k + actualDeleteCount - 1)
-			to := intToValue(k + itemCount - 1)
+		src.values = values
+		src.objCount = int64(len(values))
+	} else {
+		for k := int64(0); k < actualDeleteCount; k++ {
+			from := intToValue(k + actualStart)
 			if o.self.hasProperty(from) {
 			if o.self.hasProperty(from) {
-				o.self.put(to, o.self.get(from), true)
-			} else {
-				o.self.delete(to, true)
+				defineDataPropertyOrThrow(a, intToValue(k), o.self.get(from))
 			}
 			}
 		}
 		}
-	}
 
 
-	if itemCount > 0 {
-		for i, item := range call.Arguments[2:] {
-			o.self.put(intToValue(actualStart+int64(i)), item, true)
+		if itemCount < actualDeleteCount {
+			for k := actualStart; k < length-actualDeleteCount; k++ {
+				from := intToValue(k + actualDeleteCount)
+				to := intToValue(k + itemCount)
+				if o.self.hasProperty(from) {
+					o.self.put(to, o.self.get(from), true)
+				} else {
+					o.self.delete(to, true)
+				}
+			}
+
+			for k := length; k > length-actualDeleteCount+itemCount; k-- {
+				o.self.delete(intToValue(k-1), true)
+			}
+		} else if itemCount > actualDeleteCount {
+			for k := length - actualDeleteCount; k > actualStart; k-- {
+				from := intToValue(k + actualDeleteCount - 1)
+				to := intToValue(k + itemCount - 1)
+				if o.self.hasProperty(from) {
+					o.self.put(to, o.self.get(from), true)
+				} else {
+					o.self.delete(to, true)
+				}
+			}
+		}
+
+		if itemCount > 0 {
+			for i, item := range call.Arguments[2:] {
+				o.self.put(intToValue(actualStart+int64(i)), item, true)
+			}
 		}
 		}
 	}
 	}
 
 
-	o.self.putStr("length", intToValue(length-actualDeleteCount+itemCount), true)
+	o.self.putStr("length", intToValue(newLength), true)
 
 
 	return a
 	return a
 }
 }
@@ -420,21 +450,35 @@ func (r *Runtime) arrayproto_unshift(call FunctionCall) Value {
 	o := call.This.ToObject(r)
 	o := call.This.ToObject(r)
 	length := toLength(o.self.getStr("length"))
 	length := toLength(o.self.getStr("length"))
 	argCount := int64(len(call.Arguments))
 	argCount := int64(len(call.Arguments))
-	for k := length - 1; k >= 0; k-- {
-		from := intToValue(k)
-		to := intToValue(k + argCount)
-		if o.self.hasProperty(from) {
-			o.self.put(to, o.self.get(from), true)
+	newLen := intToValue(length + argCount)
+	if arr := r.checkStdArrayObj(o); arr != nil {
+		newSize := length + argCount
+		if int64(cap(arr.values)) >= newSize {
+			arr.values = arr.values[:newSize]
+			copy(arr.values[argCount:], arr.values[:length])
 		} else {
 		} else {
-			o.self.delete(to, true)
+			values := make([]Value, newSize)
+			copy(values[argCount:], arr.values)
+			arr.values = values
+		}
+		copy(arr.values, call.Arguments)
+		arr.objCount = arr.length
+	} else {
+		for k := length - 1; k >= 0; k-- {
+			from := intToValue(k)
+			to := intToValue(k + argCount)
+			if o.self.hasProperty(from) {
+				o.self.put(to, o.self.get(from), true)
+			} else {
+				o.self.delete(to, true)
+			}
 		}
 		}
-	}
 
 
-	for k, arg := range call.Arguments {
-		o.self.put(intToValue(int64(k)), arg, true)
+		for k, arg := range call.Arguments {
+			o.self.put(intToValue(int64(k)), arg, true)
+		}
 	}
 	}
 
 
-	newLen := intToValue(length + argCount)
 	o.self.putStr("length", newLen, true)
 	o.self.putStr("length", newLen, true)
 	return newLen
 	return newLen
 }
 }
@@ -525,24 +569,20 @@ func (r *Runtime) arrayproto_lastIndexOf(call FunctionCall) Value {
 func (r *Runtime) arrayproto_every(call FunctionCall) Value {
 func (r *Runtime) arrayproto_every(call FunctionCall) Value {
 	o := call.This.ToObject(r)
 	o := call.This.ToObject(r)
 	length := toLength(o.self.getStr("length"))
 	length := toLength(o.self.getStr("length"))
-	callbackFn := call.Argument(0).ToObject(r)
-	if callbackFn, ok := callbackFn.self.assertCallable(); ok {
-		fc := FunctionCall{
-			This:      call.Argument(1),
-			Arguments: []Value{nil, nil, o},
-		}
-		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() {
-					return valueFalse
-				}
+	callbackFn := r.toCallable(call.Argument(0))
+	fc := FunctionCall{
+		This:      call.Argument(1),
+		Arguments: []Value{nil, nil, o},
+	}
+	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() {
+				return valueFalse
 			}
 			}
 		}
 		}
-	} else {
-		r.typeErrorResult(true, "%s is not a function", call.Argument(0))
 	}
 	}
 	return valueTrue
 	return valueTrue
 }
 }
@@ -550,24 +590,20 @@ func (r *Runtime) arrayproto_every(call FunctionCall) Value {
 func (r *Runtime) arrayproto_some(call FunctionCall) Value {
 func (r *Runtime) arrayproto_some(call FunctionCall) Value {
 	o := call.This.ToObject(r)
 	o := call.This.ToObject(r)
 	length := toLength(o.self.getStr("length"))
 	length := toLength(o.self.getStr("length"))
-	callbackFn := call.Argument(0).ToObject(r)
-	if callbackFn, ok := callbackFn.self.assertCallable(); ok {
-		fc := FunctionCall{
-			This:      call.Argument(1),
-			Arguments: []Value{nil, nil, o},
-		}
-		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() {
-					return valueTrue
-				}
+	callbackFn := r.toCallable(call.Argument(0))
+	fc := FunctionCall{
+		This:      call.Argument(1),
+		Arguments: []Value{nil, nil, o},
+	}
+	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() {
+				return valueTrue
 			}
 			}
 		}
 		}
-	} else {
-		r.typeErrorResult(true, "%s is not a function", call.Argument(0))
 	}
 	}
 	return valueFalse
 	return valueFalse
 }
 }
@@ -575,22 +611,18 @@ func (r *Runtime) arrayproto_some(call FunctionCall) Value {
 func (r *Runtime) arrayproto_forEach(call FunctionCall) Value {
 func (r *Runtime) arrayproto_forEach(call FunctionCall) Value {
 	o := call.This.ToObject(r)
 	o := call.This.ToObject(r)
 	length := toLength(o.self.getStr("length"))
 	length := toLength(o.self.getStr("length"))
-	callbackFn := call.Argument(0).ToObject(r)
-	if callbackFn, ok := callbackFn.self.assertCallable(); ok {
-		fc := FunctionCall{
-			This:      call.Argument(1),
-			Arguments: []Value{nil, nil, o},
-		}
-		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
-				callbackFn(fc)
-			}
+	callbackFn := r.toCallable(call.Argument(0))
+	fc := FunctionCall{
+		This:      call.Argument(1),
+		Arguments: []Value{nil, nil, o},
+	}
+	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
+			callbackFn(fc)
 		}
 		}
-	} else {
-		r.typeErrorResult(true, "%s is not a function", call.Argument(0))
 	}
 	}
 	return _undefined
 	return _undefined
 }
 }
@@ -598,29 +630,36 @@ func (r *Runtime) arrayproto_forEach(call FunctionCall) Value {
 func (r *Runtime) arrayproto_map(call FunctionCall) Value {
 func (r *Runtime) arrayproto_map(call FunctionCall) Value {
 	o := call.This.ToObject(r)
 	o := call.This.ToObject(r)
 	length := toLength(o.self.getStr("length"))
 	length := toLength(o.self.getStr("length"))
-	callbackFn := call.Argument(0).ToObject(r)
-	if callbackFn, ok := callbackFn.self.assertCallable(); ok {
-		fc := FunctionCall{
-			This:      call.Argument(1),
-			Arguments: []Value{nil, nil, o},
-		}
-		a := r.newArrayObject()
-		a._setLengthInt(length, true)
-		a.values = make([]Value, length)
-		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
-				a.values[k] = callbackFn(fc)
-				a.objCount++
+	callbackFn := r.toCallable(call.Argument(0))
+	fc := FunctionCall{
+		This:      call.Argument(1),
+		Arguments: []Value{nil, nil, o},
+	}
+	a := arraySpeciesCreate(o, length)
+	if _, stdSrc := o.self.(*arrayObject); stdSrc {
+		if arr, ok := a.self.(*arrayObject); ok {
+			values := make([]Value, length)
+			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
+					values[k] = callbackFn(fc)
+				}
 			}
 			}
+			setArrayValues(arr, values)
+			return a
 		}
 		}
-		return a.val
-	} else {
-		r.typeErrorResult(true, "%s is not a function", call.Argument(0))
 	}
 	}
-	panic("unreachable")
+	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
+			defineDataPropertyOrThrow(a, idx, callbackFn(fc))
+		}
+	}
+	return a
 }
 }
 
 
 func (r *Runtime) arrayproto_filter(call FunctionCall) Value {
 func (r *Runtime) arrayproto_filter(call FunctionCall) Value {
@@ -785,35 +824,12 @@ func (r *Runtime) arrayproto_reverse_generic(o *Object, start int64) {
 
 
 func (r *Runtime) arrayproto_reverse(call FunctionCall) Value {
 func (r *Runtime) arrayproto_reverse(call FunctionCall) Value {
 	o := call.This.ToObject(r)
 	o := call.This.ToObject(r)
-	if a, ok := o.self.(*arrayObject); ok {
+	if a := r.checkStdArrayObj(o); a != nil {
 		l := a.length
 		l := a.length
 		middle := l / 2
 		middle := l / 2
-		al := int64(len(a.values))
 		for lower := int64(0); lower != middle; lower++ {
 		for lower := int64(0); lower != middle; lower++ {
 			upper := l - lower - 1
 			upper := l - lower - 1
-			var lowerValue, upperValue Value
-			if upper >= al || lower >= al {
-				goto bailout
-			}
-			lowerValue = a.values[lower]
-			if lowerValue == nil {
-				goto bailout
-			}
-			if _, ok := lowerValue.(*valueProperty); ok {
-				goto bailout
-			}
-			upperValue = a.values[upper]
-			if upperValue == nil {
-				goto bailout
-			}
-			if _, ok := upperValue.(*valueProperty); ok {
-				goto bailout
-			}
-
-			a.values[lower], a.values[upper] = upperValue, lowerValue
-			continue
-		bailout:
-			arrayproto_reverse_generic_step(o, lower, upper)
+			a.values[lower], a.values[upper] = a.values[upper], a.values[lower]
 		}
 		}
 		//TODO: go arrays
 		//TODO: go arrays
 	} else {
 	} else {
@@ -832,7 +848,7 @@ func (r *Runtime) arrayproto_shift(call FunctionCall) Value {
 	first := o.self.get(intToValue(0))
 	first := o.self.get(intToValue(0))
 	for i := int64(1); i < length; i++ {
 	for i := int64(1); i < length; i++ {
 		v := o.self.get(intToValue(i))
 		v := o.self.get(intToValue(i))
-		if v != nil && v != _undefined {
+		if v != nil {
 			o.self.put(intToValue(i-1), v, true)
 			o.self.put(intToValue(i-1), v, true)
 		} else {
 		} else {
 			o.self.delete(intToValue(i-1), true)
 			o.self.delete(intToValue(i-1), true)
@@ -850,6 +866,10 @@ func (r *Runtime) arrayproto_values(call FunctionCall) Value {
 	return r.createArrayIterator(call.This.ToObject(r), iterationKindValue)
 	return r.createArrayIterator(call.This.ToObject(r), iterationKindValue)
 }
 }
 
 
+func (r *Runtime) arrayproto_keys(call FunctionCall) Value {
+	return r.createArrayIterator(call.This.ToObject(r), iterationKindKey)
+}
+
 func (r *Runtime) arrayproto_copyWithin(call FunctionCall) Value {
 func (r *Runtime) arrayproto_copyWithin(call FunctionCall) Value {
 	o := call.This.ToObject(r)
 	o := call.This.ToObject(r)
 	l := toLength(o.self.getStr("length"))
 	l := toLength(o.self.getStr("length"))
@@ -938,6 +958,26 @@ func (r *Runtime) arrayproto_find(call FunctionCall) Value {
 	return _undefined
 	return _undefined
 }
 }
 
 
+func (r *Runtime) arrayproto_findIndex(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 idx
+		}
+	}
+
+	return intToValue(-1)
+}
+
 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 &&
@@ -1111,35 +1151,48 @@ func (r *Runtime) createArrayProto(val *Object) objectImpl {
 	o.init()
 	o.init()
 
 
 	o._putProp("constructor", r.global.Array, true, false, true)
 	o._putProp("constructor", r.global.Array, true, false, true)
+	o._putProp("concat", r.newNativeFunc(r.arrayproto_concat, nil, "concat", nil, 1), true, false, true)
 	o._putProp("copyWithin", r.newNativeFunc(r.arrayproto_copyWithin, nil, "copyWithin", nil, 2), true, false, true)
 	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("entries", r.newNativeFunc(r.arrayproto_entries, nil, "entries", nil, 0), true, false, true)
+	o._putProp("every", r.newNativeFunc(r.arrayproto_every, nil, "every", nil, 1), true, false, true)
 	o._putProp("fill", r.newNativeFunc(r.arrayproto_fill, nil, "fill", nil, 1), 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("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("forEach", r.newNativeFunc(r.arrayproto_forEach, nil, "forEach", nil, 1), true, false, true)
+	o._putProp("indexOf", r.newNativeFunc(r.arrayproto_indexOf, nil, "indexOf", nil, 1), true, false, true)
+	o._putProp("join", r.newNativeFunc(r.arrayproto_join, nil, "join", nil, 1), true, false, true)
+	o._putProp("keys", r.newNativeFunc(r.arrayproto_keys, nil, "keys", nil, 0), true, false, true)
+	o._putProp("lastIndexOf", r.newNativeFunc(r.arrayproto_lastIndexOf, nil, "lastIndexOf", nil, 1), true, false, true)
+	o._putProp("map", r.newNativeFunc(r.arrayproto_map, nil, "map", nil, 1), true, false, true)
 	o._putProp("pop", r.newNativeFunc(r.arrayproto_pop, nil, "pop", nil, 0), 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("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)
-	o._putProp("toString", r.newNativeFunc(r.arrayproto_toString, nil, "toString", nil, 0), true, false, true)
-	o._putProp("toLocaleString", r.newNativeFunc(r.arrayproto_toLocaleString, nil, "toLocaleString", nil, 0), true, false, true)
-	o._putProp("concat", r.newNativeFunc(r.arrayproto_concat, nil, "concat", 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)
 	o._putProp("reverse", r.newNativeFunc(r.arrayproto_reverse, nil, "reverse", nil, 0), true, false, true)
 	o._putProp("reverse", r.newNativeFunc(r.arrayproto_reverse, nil, "reverse", nil, 0), true, false, true)
 	o._putProp("shift", r.newNativeFunc(r.arrayproto_shift, nil, "shift", nil, 0), true, false, true)
 	o._putProp("shift", r.newNativeFunc(r.arrayproto_shift, nil, "shift", nil, 0), true, false, true)
 	o._putProp("slice", r.newNativeFunc(r.arrayproto_slice, nil, "slice", nil, 2), true, false, true)
 	o._putProp("slice", r.newNativeFunc(r.arrayproto_slice, nil, "slice", nil, 2), true, false, true)
+	o._putProp("some", r.newNativeFunc(r.arrayproto_some, nil, "some", nil, 1), true, false, true)
 	o._putProp("sort", r.newNativeFunc(r.arrayproto_sort, nil, "sort", nil, 1), true, false, true)
 	o._putProp("sort", r.newNativeFunc(r.arrayproto_sort, nil, "sort", nil, 1), true, false, true)
 	o._putProp("splice", r.newNativeFunc(r.arrayproto_splice, nil, "splice", nil, 2), true, false, true)
 	o._putProp("splice", r.newNativeFunc(r.arrayproto_splice, nil, "splice", nil, 2), true, false, true)
+	o._putProp("toLocaleString", r.newNativeFunc(r.arrayproto_toLocaleString, nil, "toLocaleString", nil, 0), true, false, true)
+	o._putProp("toString", r.newNativeFunc(r.arrayproto_toString, nil, "toString", nil, 0), true, false, true)
 	o._putProp("unshift", r.newNativeFunc(r.arrayproto_unshift, nil, "unshift", nil, 1), true, false, true)
 	o._putProp("unshift", r.newNativeFunc(r.arrayproto_unshift, nil, "unshift", nil, 1), true, false, true)
-	o._putProp("indexOf", r.newNativeFunc(r.arrayproto_indexOf, nil, "indexOf", nil, 1), true, false, true)
-	o._putProp("lastIndexOf", r.newNativeFunc(r.arrayproto_lastIndexOf, nil, "lastIndexOf", nil, 1), true, false, true)
-	o._putProp("every", r.newNativeFunc(r.arrayproto_every, nil, "every", nil, 1), true, false, true)
-	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("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)
 	valuesFunc := r.newNativeFunc(r.arrayproto_values, nil, "values", nil, 0)
+	r.global.arrayValues = valuesFunc
 	o._putProp("values", valuesFunc, true, false, true)
 	o._putProp("values", valuesFunc, true, false, true)
+
 	o.put(symIterator, valueProp(valuesFunc, true, false, true), true)
 	o.put(symIterator, valueProp(valuesFunc, true, false, true), true)
-	r.global.arrayValues = valuesFunc
+
+	bl := r.NewObject()
+	bl.self.putStr("copyWithin", valueTrue, true)
+	bl.self.putStr("entries", valueTrue, true)
+	bl.self.putStr("fill", valueTrue, true)
+	bl.self.putStr("find", valueTrue, true)
+	bl.self.putStr("findIndex", valueTrue, true)
+	bl.self.putStr("keys", valueTrue, true)
+	bl.self.putStr("values", valueTrue, true)
+	o.put(symUnscopables, valueProp(bl, false, false, true), true)
 
 
 	return o
 	return o
 }
 }

+ 19 - 10
tc39_test.go

@@ -32,6 +32,9 @@ var (
 		"test/built-ins/Date/prototype/toISOString/15.9.5.43-0-10.js": true, // timezone
 		"test/built-ins/Date/prototype/toISOString/15.9.5.43-0-10.js": true, // timezone
 		"test/annexB/built-ins/escape/escape-above-astral.js":         true, // \u{xxxxx}
 		"test/annexB/built-ins/escape/escape-above-astral.js":         true, // \u{xxxxx}
 
 
+		// utf-16
+		"test/built-ins/Array/prototype/concat/Array.prototype.concat_spreadable-string-wrapper.js": true,
+
 		// cross-realm
 		// cross-realm
 		"test/built-ins/Symbol/unscopables/cross-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/toStringTag/cross-realm.js":                                true,
@@ -57,6 +60,12 @@ var (
 		"test/built-ins/Array/prototype/concat/create-proto-from-ctor-realm-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-non-array.js": true,
 		"test/built-ins/Array/prototype/filter/create-proto-from-ctor-realm-array.js":     true,
 		"test/built-ins/Array/prototype/filter/create-proto-from-ctor-realm-array.js":     true,
+		"test/built-ins/Array/prototype/map/create-proto-from-ctor-realm-non-array.js":    true,
+		"test/built-ins/Array/prototype/map/create-proto-from-ctor-realm-array.js":        true,
+		"test/built-ins/Array/prototype/slice/create-proto-from-ctor-realm-non-array.js":  true,
+		"test/built-ins/Array/prototype/slice/create-proto-from-ctor-realm-array.js":      true,
+		"test/built-ins/Array/prototype/splice/create-proto-from-ctor-realm-non-array.js": true,
+		"test/built-ins/Array/prototype/splice/create-proto-from-ctor-realm-array.js":     true,
 
 
 		// class
 		// class
 		"test/language/statements/class/subclass/builtin-objects/Symbol/symbol-valid-as-extends-value.js": true,
 		"test/language/statements/class/subclass/builtin-objects/Symbol/symbol-valid-as-extends-value.js": true,
@@ -71,6 +80,8 @@ var (
 		"test/language/statements/class/subclass/builtin-objects/Set/regular-subclassing.js":              true,
 		"test/language/statements/class/subclass/builtin-objects/Set/regular-subclassing.js":              true,
 		"test/language/statements/class/subclass/builtin-objects/Object/replacing-prototype.js":           true,
 		"test/language/statements/class/subclass/builtin-objects/Object/replacing-prototype.js":           true,
 		"test/language/statements/class/subclass/builtin-objects/Object/regular-subclassing.js":           true,
 		"test/language/statements/class/subclass/builtin-objects/Object/regular-subclassing.js":           true,
+		"test/built-ins/Array/prototype/concat/Array.prototype.concat_non-array.js":                       true,
+		"test/language/statements/class/subclass/builtin-objects/Array/length.js":                         true,
 
 
 		// full unicode regexp flag
 		// full unicode regexp flag
 		"test/built-ins/RegExp/prototype/Symbol.match/u-advance-after-empty.js":               true,
 		"test/built-ins/RegExp/prototype/Symbol.match/u-advance-after-empty.js":               true,
@@ -83,10 +94,14 @@ var (
 		"test/built-ins/Array/from/source-object-iterator-2.js": true,
 		"test/built-ins/Array/from/source-object-iterator-2.js": true,
 
 
 		// Typed arrays
 		// Typed arrays
-		"test/built-ins/Array/from/items-is-arraybuffer.js": true,
+		"test/built-ins/Array/from/items-is-arraybuffer.js":                                 true,
+		"test/built-ins/Array/prototype/concat/Array.prototype.concat_small-typed-array.js": true,
+		"test/built-ins/Array/prototype/concat/Array.prototype.concat_large-typed-array.js": true,
 
 
 		// for-of
 		// for-of
-		"test/language/statements/for-of/Array.prototype.entries.js": true,
+		"test/language/statements/for-of/Array.prototype.keys.js":            true,
+		"test/language/statements/for-of/Array.prototype.entries.js":         true,
+		"test/language/statements/for-of/Array.prototype.Symbol.iterator.js": true,
 	}
 	}
 
 
 	featuresBlackList = []string{
 	featuresBlackList = []string{
@@ -108,14 +123,8 @@ var (
 		"22.1.2.1",
 		"22.1.2.1",
 		"22.1.2.3",
 		"22.1.2.3",
 		"22.1.2.5",
 		"22.1.2.5",
-		"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",
+		"22.1.3",
+		"22.1.4",
 		"23.1",
 		"23.1",
 		"23.2",
 		"23.2",
 		"23.3",
 		"23.3",