Browse Source

Fixed Export() of arrays with property values. Fixes #270.

Dmitry Panov 4 years ago
parent
commit
6fc852574a
7 changed files with 205 additions and 93 deletions
  1. 12 3
      array.go
  2. 23 2
      array_sparse.go
  3. 58 0
      array_sparse_test.go
  4. 108 0
      array_test.go
  5. 1 0
      go.mod
  6. 3 0
      go.sum
  7. 0 88
      object_test.go

+ 12 - 3
array.go

@@ -486,9 +486,18 @@ func (a *arrayObject) export(ctx *objectExportCtx) interface{} {
 	}
 	arr := make([]interface{}, a.length)
 	ctx.put(a, arr)
-	for i, v := range a.values {
-		if v != nil {
-			arr[i] = exportValue(v, ctx)
+	if a.propValueCount == 0 && a.length == uint32(len(a.values)) && uint32(a.objCount) == a.length {
+		for i, v := range a.values {
+			if v != nil {
+				arr[i] = exportValue(v, ctx)
+			}
+		}
+	} else {
+		for i := uint32(0); i < a.length; i++ {
+			v := a.getIdx(valueInt(i), nil)
+			if v != nil {
+				arr[i] = exportValue(v, ctx)
+			}
 		}
 	}
 	return arr

+ 23 - 2
array_sparse.go

@@ -433,9 +433,30 @@ func (a *sparseArrayObject) export(ctx *objectExportCtx) interface{} {
 	}
 	arr := make([]interface{}, a.length)
 	ctx.put(a, arr)
+	var prevIdx uint32
 	for _, item := range a.items {
-		if item.value != nil {
-			arr[item.idx] = exportValue(item.value, ctx)
+		idx := item.idx
+		for i := prevIdx; i < idx; i++ {
+			if a.prototype != nil {
+				if v := a.prototype.self.getIdx(valueInt(i), nil); v != nil {
+					arr[i] = exportValue(v, ctx)
+				}
+			}
+		}
+		v := item.value
+		if v != nil {
+			if prop, ok := v.(*valueProperty); ok {
+				v = prop.get(a.val)
+			}
+			arr[idx] = exportValue(v, ctx)
+		}
+		prevIdx = idx + 1
+	}
+	for i := prevIdx; i < a.length; i++ {
+		if a.prototype != nil {
+			if v := a.prototype.self.getIdx(valueInt(i), nil); v != nil {
+				arr[i] = exportValue(v, ctx)
+			}
 		}
 	}
 	return arr

+ 58 - 0
array_sparse_test.go

@@ -172,3 +172,61 @@ func TestArraySparseMaxLength(t *testing.T) {
 
 	testScript1(SCRIPT, valueTrue, t)
 }
+
+func TestArraySparseExportProps(t *testing.T) {
+	vm := New()
+	proto := vm.NewArray()
+	for _, idx := range []string{"0", "500", "9999", "10001", "20471"} {
+		err := proto.Set(idx, true)
+		if err != nil {
+			t.Fatal(err)
+		}
+	}
+
+	arr := vm.NewArray()
+	err := arr.SetPrototype(proto)
+	if err != nil {
+		t.Fatal(err)
+	}
+	err = arr.DefineDataProperty("20470", vm.ToValue(true), FLAG_TRUE, FLAG_FALSE, FLAG_TRUE)
+	if err != nil {
+		t.Fatal(err)
+	}
+	err = arr.DefineDataProperty("10000", vm.ToValue(true), FLAG_TRUE, FLAG_FALSE, FLAG_TRUE)
+	if err != nil {
+		t.Fatal(err)
+	}
+	err = arr.Set("length", 20472)
+	if err != nil {
+		t.Fatal(err)
+	}
+	actual := arr.Export()
+	if actualArr, ok := actual.([]interface{}); ok {
+		if len(actualArr) == 20472 {
+			expectedIdx := map[int]struct{}{
+				0:     {},
+				500:   {},
+				9999:  {},
+				10000: {},
+				10001: {},
+				20470: {},
+				20471: {},
+			}
+			for i, v := range actualArr {
+				if _, exists := expectedIdx[i]; exists {
+					if v != true {
+						t.Fatalf("Expected true at %d, got %v", i, v)
+					}
+				} else {
+					if v != nil {
+						t.Fatalf("Expected nil at %d, got %v", i, v)
+					}
+				}
+			}
+		} else {
+			t.Fatalf("Expected len 20471, actual: %d", len(actualArr))
+		}
+	} else {
+		t.Fatalf("Invalid export type: %T", actual)
+	}
+}

+ 108 - 0
array_test.go

@@ -0,0 +1,108 @@
+package goja
+
+import (
+	"reflect"
+	"testing"
+)
+
+func TestArray1(t *testing.T) {
+	r := &Runtime{}
+	a := r.newArray(nil)
+	a.setOwnIdx(valueInt(0), asciiString("test"), true)
+	if l := a.getStr("length", nil).ToInteger(); l != 1 {
+		t.Fatalf("Unexpected length: %d", l)
+	}
+}
+
+func TestArrayExportProps(t *testing.T) {
+	vm := New()
+	arr := vm.NewArray()
+	err := arr.DefineDataProperty("0", vm.ToValue(true), FLAG_TRUE, FLAG_FALSE, FLAG_TRUE)
+	if err != nil {
+		t.Fatal(err)
+	}
+	actual := arr.Export()
+	expected := []interface{}{true}
+	if !reflect.DeepEqual(actual, expected) {
+		t.Fatalf("Expected: %#v, actual: %#v", expected, actual)
+	}
+}
+
+func BenchmarkArrayGetStr(b *testing.B) {
+	b.StopTimer()
+	r := New()
+	v := &Object{runtime: r}
+
+	a := &arrayObject{
+		baseObject: baseObject{
+			val:        v,
+			extensible: true,
+		},
+	}
+	v.self = a
+
+	a.init()
+
+	v.setOwn(valueInt(0), asciiString("test"), false)
+	b.StartTimer()
+
+	for i := 0; i < b.N; i++ {
+		a.getStr("0", nil)
+	}
+
+}
+
+func BenchmarkArrayGet(b *testing.B) {
+	b.StopTimer()
+	r := New()
+	v := &Object{runtime: r}
+
+	a := &arrayObject{
+		baseObject: baseObject{
+			val:        v,
+			extensible: true,
+		},
+	}
+	v.self = a
+
+	a.init()
+
+	var idx Value = valueInt(0)
+
+	v.setOwn(idx, asciiString("test"), false)
+
+	b.StartTimer()
+
+	for i := 0; i < b.N; i++ {
+		v.get(idx, nil)
+	}
+
+}
+
+func BenchmarkArrayPut(b *testing.B) {
+	b.StopTimer()
+	r := New()
+
+	v := &Object{runtime: r}
+
+	a := &arrayObject{
+		baseObject: baseObject{
+			val:        v,
+			extensible: true,
+		},
+	}
+
+	v.self = a
+
+	a.init()
+
+	var idx Value = valueInt(0)
+	var val Value = asciiString("test")
+
+	b.StartTimer()
+
+	for i := 0; i < b.N; i++ {
+		v.setOwn(idx, val, false)
+	}
+
+}

+ 1 - 0
go.mod

@@ -6,6 +6,7 @@ require (
 	github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91
 	github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7
 	github.com/go-sourcemap/sourcemap v2.1.3+incompatible
+	github.com/kr/text v0.2.0 // indirect
 	golang.org/x/text v0.3.5
 	gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
 	gopkg.in/yaml.v2 v2.4.0

+ 3 - 0
go.sum

@@ -1,3 +1,4 @@
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
 github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
 github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 h1:Izz0+t1Z5nI16/II7vuEo/nHjodOg0p7+OiDpjX5t1E=
@@ -11,6 +12,8 @@ github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
 golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

+ 0 - 88
object_test.go

@@ -6,15 +6,6 @@ import (
 	"testing"
 )
 
-func TestArray1(t *testing.T) {
-	r := &Runtime{}
-	a := r.newArray(nil)
-	a.setOwnIdx(valueInt(0), asciiString("test"), true)
-	if l := a.getStr("length", nil).ToInteger(); l != 1 {
-		t.Fatalf("Unexpected length: %d", l)
-	}
-}
-
 func TestDefineProperty(t *testing.T) {
 	r := New()
 	o := r.NewObject()
@@ -425,85 +416,6 @@ func BenchmarkConv(b *testing.B) {
 	}
 }
 
-func BenchmarkArrayGetStr(b *testing.B) {
-	b.StopTimer()
-	r := New()
-	v := &Object{runtime: r}
-
-	a := &arrayObject{
-		baseObject: baseObject{
-			val:        v,
-			extensible: true,
-		},
-	}
-	v.self = a
-
-	a.init()
-
-	v.setOwn(valueInt(0), asciiString("test"), false)
-	b.StartTimer()
-
-	for i := 0; i < b.N; i++ {
-		a.getStr("0", nil)
-	}
-
-}
-
-func BenchmarkArrayGet(b *testing.B) {
-	b.StopTimer()
-	r := New()
-	v := &Object{runtime: r}
-
-	a := &arrayObject{
-		baseObject: baseObject{
-			val:        v,
-			extensible: true,
-		},
-	}
-	v.self = a
-
-	a.init()
-
-	var idx Value = valueInt(0)
-
-	v.setOwn(idx, asciiString("test"), false)
-
-	b.StartTimer()
-
-	for i := 0; i < b.N; i++ {
-		v.get(idx, nil)
-	}
-
-}
-
-func BenchmarkArrayPut(b *testing.B) {
-	b.StopTimer()
-	r := New()
-
-	v := &Object{runtime: r}
-
-	a := &arrayObject{
-		baseObject: baseObject{
-			val:        v,
-			extensible: true,
-		},
-	}
-
-	v.self = a
-
-	a.init()
-
-	var idx Value = valueInt(0)
-	var val Value = asciiString("test")
-
-	b.StartTimer()
-
-	for i := 0; i < b.N; i++ {
-		v.setOwn(idx, val, false)
-	}
-
-}
-
 func BenchmarkToUTF8String(b *testing.B) {
 	var s valueString = asciiString("test")
 	for i := 0; i < b.N; i++ {