Browse Source

Array optimisations

Dmitry Panov 5 years ago
parent
commit
d3dfac6c0a
5 changed files with 90 additions and 38 deletions
  1. 48 27
      builtin_array.go
  2. 16 6
      builtin_arrray_test.go
  3. 1 4
      builtin_weakset.go
  4. 10 1
      compiler_expr.go
  5. 15 0
      vm.go

+ 48 - 27
builtin_array.go

@@ -237,32 +237,21 @@ func isConcatSpreadable(obj *Object) bool {
 }
 }
 
 
 func (r *Runtime) arrayproto_concat_append(a *Object, item Value) {
 func (r *Runtime) arrayproto_concat_append(a *Object, item Value) {
-	descr := propertyDescr{
-		Writable:     FLAG_TRUE,
-		Enumerable:   FLAG_TRUE,
-		Configurable: FLAG_TRUE,
-	}
-
 	aLength := toLength(a.self.getStr("length"))
 	aLength := toLength(a.self.getStr("length"))
-	if obj, ok := item.(*Object); ok {
-		if isConcatSpreadable(obj) {
-			length := toLength(obj.self.getStr("length"))
-			for i := int64(0); i < length; i++ {
-				v := obj.self.get(intToValue(i))
-				if v != nil {
-					descr.Value = v
-					a.self.defineOwnProperty(intToValue(aLength), descr, false)
-					aLength++
-				} else {
-					aLength++
-					a.self.putStr("length", intToValue(aLength), false)
-				}
+	if obj, ok := item.(*Object); ok && isConcatSpreadable(obj) {
+		length := toLength(obj.self.getStr("length"))
+		for i := int64(0); i < length; i++ {
+			v := obj.self.get(intToValue(i))
+			if v != nil {
+				defineDataPropertyOrThrow(a, intToValue(aLength), v)
 			}
 			}
-			return
+			aLength++
 		}
 		}
+	} else {
+		defineDataPropertyOrThrow(a, intToValue(aLength), item)
+		aLength++
 	}
 	}
-	descr.Value = item
-	a.self.defineOwnProperty(intToValue(aLength), descr, false)
+	a.self.putStr("length", intToValue(aLength), true)
 }
 }
 
 
 func arraySpeciesCreate(obj *Object, size int) *Object {
 func arraySpeciesCreate(obj *Object, size int) *Object {
@@ -334,8 +323,14 @@ func (r *Runtime) arrayproto_slice(call FunctionCall) Value {
 	if count < 0 {
 	if count < 0 {
 		count = 0
 		count = 0
 	}
 	}
-	a := r.newArrayLength(count)
 
 
+	if arr := r.checkStdArrayObj(o); arr != nil {
+		values := make([]Value, count)
+		copy(values, arr.values[start:])
+		return r.newArrayValues(values)
+	}
+
+	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))
@@ -466,6 +461,15 @@ func (r *Runtime) arrayproto_indexOf(call FunctionCall) Value {
 
 
 	searchElement := call.Argument(0)
 	searchElement := call.Argument(0)
 
 
+	if arr := r.checkStdArrayObj(o); arr != nil {
+		for i, val := range arr.values[n:] {
+			if searchElement.StrictEquals(val) {
+				return intToValue(n + int64(i))
+			}
+		}
+		return intToValue(-1)
+	}
+
 	for ; n < length; n++ {
 	for ; n < length; n++ {
 		idx := intToValue(n)
 		idx := intToValue(n)
 		if val := o.self.get(idx); val != nil {
 		if val := o.self.get(idx); val != nil {
@@ -500,6 +504,16 @@ func (r *Runtime) arrayproto_lastIndexOf(call FunctionCall) Value {
 
 
 	searchElement := call.Argument(0)
 	searchElement := call.Argument(0)
 
 
+	if arr := r.checkStdArrayObj(o); arr != nil {
+		vals := arr.values
+		for k := fromIndex; k >= 0; k-- {
+			if v := vals[k]; v != nil && searchElement.StrictEquals(v) {
+				return intToValue(k)
+			}
+		}
+		return intToValue(-1)
+	}
+
 	for k := fromIndex; k >= 0; k-- {
 	for k := fromIndex; k >= 0; k-- {
 		idx := intToValue(k)
 		idx := intToValue(k)
 		if val := o.self.get(idx); val != nil {
 		if val := o.self.get(idx); val != nil {
@@ -849,6 +863,12 @@ func (r *Runtime) arrayproto_copyWithin(call FunctionCall) Value {
 		final = min(relEnd, l)
 		final = min(relEnd, l)
 	}
 	}
 	count := min(final-from, l-to)
 	count := min(final-from, l-to)
+	if arr := r.checkStdArrayObj(o); arr != nil {
+		if count > 0 {
+			copy(arr.values[to:to+count], arr.values[from:from+count])
+		}
+		return o
+	}
 	if from < to && to < from+count {
 	if from < to && to < from+count {
 		dir = -1
 		dir = -1
 		from = from + count - 1
 		from = from + count - 1
@@ -873,7 +893,8 @@ func (r *Runtime) arrayproto_copyWithin(call FunctionCall) Value {
 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 &&
-		arr.length == int64(len(arr.values)) {
+		arr.length == int64(len(arr.values)) &&
+		arr.objCount == arr.length {
 
 
 		return arr
 		return arr
 	}
 	}
@@ -916,11 +937,11 @@ func (r *Runtime) array_from(call FunctionCall) Value {
 	if mapFn == nil && call.This == r.global.Array { // mapFn may mutate the array
 	if mapFn == nil && call.This == r.global.Array { // mapFn may mutate the array
 		if arr := r.checkStdArrayIter(items); arr != nil {
 		if arr := r.checkStdArrayIter(items); arr != nil {
 			items := make([]Value, len(arr.values))
 			items := make([]Value, len(arr.values))
-			for i, item := range arr.values {
+			copy(items, arr.values)
+			for i, item := range items {
 				if item == nil {
 				if item == nil {
-					item = nilSafe(arr.get(intToValue(int64(i))))
+					items[i] = _undefined
 				}
 				}
-				items[i] = item
 			}
 			}
 			return r.newArrayValues(items)
 			return r.newArrayValues(items)
 		}
 		}

+ 16 - 6
builtin_arrray_test.go

@@ -101,7 +101,7 @@ func TestArraySetLengthWithPropItems(t *testing.T) {
 
 
 func TestArrayFrom(t *testing.T) {
 func TestArrayFrom(t *testing.T) {
 	const SCRIPT = `
 	const SCRIPT = `
-	function checkDest(dest, prefix) {
+	function checkDestHoles(dest, prefix) {
 		assert(dest !== source, prefix + ": dest !== source");
 		assert(dest !== source, prefix + ": dest !== source");
 		assert.sameValue(dest.length, 3, prefix + ": dest.length");
 		assert.sameValue(dest.length, 3, prefix + ": dest.length");
 		assert.sameValue(dest[0], 1, prefix + ": [0]");
 		assert.sameValue(dest[0], 1, prefix + ": [0]");
@@ -110,10 +110,19 @@ func TestArrayFrom(t *testing.T) {
 		assert.sameValue(dest[2], 3, prefix + ": [2]");
 		assert.sameValue(dest[2], 3, prefix + ": [2]");
 	}
 	}
 
 
-	var source = [];
-	source[0] = 1;
-	source[2] = 3;
+	function checkDest(dest, prefix) {
+		assert(dest !== source, prefix + ": dest !== source");
+		assert.sameValue(dest.length, 3, prefix + ": dest.length");
+		assert.sameValue(dest[0], 1, prefix + ": [0]");
+		assert.sameValue(dest[1], 2, prefix + ": [1]");
+		assert.sameValue(dest[2], 3, prefix + ": [2]");
+	}
+
+	var source = [1,2,3];
+	var srcHoles = [1,,3];
+
 	checkDest(Array.from(source), "std source/std dest");
 	checkDest(Array.from(source), "std source/std dest");
+	checkDestHoles(Array.from(srcHoles), "std source (holes)/std dest");
 
 
 	function Iter() {
 	function Iter() {
 		this.idx = 0;
 		this.idx = 0;
@@ -133,13 +142,14 @@ func TestArrayFrom(t *testing.T) {
 	checkDest(Array.from(src), "iter src/std dest");
 	checkDest(Array.from(src), "iter src/std dest");
 
 
 	src = {0: 1, 2: 3, length: 3};
 	src = {0: 1, 2: 3, length: 3};
-	checkDest(Array.from(src), "arrayLike src/std dest");
+	checkDestHoles(Array.from(src), "arrayLike src/std dest");
 
 
 	function A() {}
 	function A() {}
 	A.from = Array.from;
 	A.from = Array.from;
 
 
 	checkDest(A.from(source), "std src/cust dest");
 	checkDest(A.from(source), "std src/cust dest");
-	checkDest(A.from(src), "arrayLike src/cust dest");
+	checkDestHoles(A.from(srcHoles), "std src (holes)/cust dest");
+	checkDestHoles(A.from(src), "arrayLike src/cust dest");
 
 
 	function T2() {
 	function T2() {
 	  Object.defineProperty(this, 0, {
 	  Object.defineProperty(this, 0, {

+ 1 - 4
builtin_weakset.go

@@ -128,10 +128,7 @@ func (r *Runtime) builtin_newWeakSet(args []Value) *Object {
 			adder := wso.getStr("add")
 			adder := wso.getStr("add")
 			if adder == r.global.weakSetAdder {
 			if adder == r.global.weakSetAdder {
 				if arr := r.checkStdArrayIter(arg); arr != nil {
 				if arr := r.checkStdArrayIter(arg); arr != nil {
-					for i, v := range arr.values {
-						if v == nil {
-							v = arr.get(intToValue(int64(i)))
-						}
+					for _, v := range arr.values {
 						wso.set.add(r.toObject(v))
 						wso.set.add(r.toObject(v))
 					}
 					}
 					return o
 					return o

+ 10 - 1
compiler_expr.go

@@ -1365,14 +1365,23 @@ func (c *compiler) compileObjectLiteral(v *ast.ObjectLiteral) compiledExpr {
 
 
 func (e *compiledArrayLiteral) emitGetter(putOnStack bool) {
 func (e *compiledArrayLiteral) emitGetter(putOnStack bool) {
 	e.addSrcMap()
 	e.addSrcMap()
+	objCount := uint32(0)
 	for _, v := range e.expr.Value {
 	for _, v := range e.expr.Value {
 		if v != nil {
 		if v != nil {
 			e.c.compileExpression(v).emitGetter(true)
 			e.c.compileExpression(v).emitGetter(true)
+			objCount++
 		} else {
 		} else {
 			e.c.emit(loadNil)
 			e.c.emit(loadNil)
 		}
 		}
 	}
 	}
-	e.c.emit(newArray(len(e.expr.Value)))
+	if objCount == uint32(len(e.expr.Value)) {
+		e.c.emit(newArray(objCount))
+	} else {
+		e.c.emit(&newArraySparse{
+			l:        uint32(len(e.expr.Value)),
+			objCount: objCount,
+		})
+	}
 	if !putOnStack {
 	if !putOnStack {
 		e.c.emit(pop)
 		e.c.emit(pop)
 	}
 	}

+ 15 - 0
vm.go

@@ -1248,6 +1248,21 @@ func (l newArray) exec(vm *vm) {
 	vm.pc++
 	vm.pc++
 }
 }
 
 
+type newArraySparse struct {
+	l, objCount uint32
+}
+
+func (n *newArraySparse) exec(vm *vm) {
+	values := make([]Value, n.l)
+	copy(values, vm.stack[vm.sp-int(n.l):vm.sp])
+	arr := vm.r.newArrayObject()
+	setArrayValues(arr, values)
+	arr.objCount = int64(n.objCount)
+	vm.sp -= int(n.l) - 1
+	vm.stack[vm.sp-1] = arr.val
+	vm.pc++
+}
+
 type newRegexp struct {
 type newRegexp struct {
 	pattern regexpPattern
 	pattern regexpPattern
 	src     valueString
 	src     valueString