Преглед изворни кода

Update the length property when it's accessed, rather than on grow/shrink. Maintain pointer type of a wrapped *[]any. Fixes #521.

Dmitry Panov пре 2 година
родитељ
комит
7749907a8a
7 измењених фајлова са 81 додато и 19 уклоњено
  1. 7 1
      object_goarray_reflect.go
  2. 12 5
      object_goslice.go
  3. 0 2
      object_goslice_reflect.go
  4. 25 0
      object_goslice_reflect_test.go
  5. 25 0
      object_goslice_test.go
  6. 2 2
      runtime.go
  7. 10 9
      value.go

+ 7 - 1
object_goarray_reflect.go

@@ -60,12 +60,12 @@ func (o *objectGoArrayReflect) _init() {
 	o.objectGoReflect.init()
 	o.class = classArray
 	o.prototype = o.val.runtime.global.ArrayPrototype
-	o.updateLen()
 	o.baseObject._put("length", &o.lengthProp)
 }
 
 func (o *objectGoArrayReflect) init() {
 	o._init()
+	o.updateLen()
 	o.putIdx = o._putIdx
 }
 
@@ -114,6 +114,9 @@ func (o *objectGoArrayReflect) getStr(name unistring.String, receiver Value) Val
 	if idx := strToGoIdx(name); idx >= 0 && idx < o.fieldsValue.Len() {
 		ownProp = o._getIdx(idx)
 	} else if name == "length" {
+		if o.fieldsValue.Kind() == reflect.Slice {
+			o.updateLen()
+		}
 		ownProp = &o.lengthProp
 	} else {
 		ownProp = o.objectGoReflect.getOwnPropStr(name)
@@ -133,6 +136,9 @@ func (o *objectGoArrayReflect) getOwnPropStr(name unistring.String) Value {
 		return nil
 	}
 	if name == "length" {
+		if o.fieldsValue.Kind() == reflect.Slice {
+			o.updateLen()
+		}
 		return &o.lengthProp
 	}
 	return o.objectGoReflect.getOwnPropStr(name)

+ 12 - 5
object_goslice.go

@@ -13,15 +13,17 @@ type objectGoSlice struct {
 	baseObject
 	data       *[]interface{}
 	lengthProp valueProperty
+	origIsPtr  bool
 }
 
-func (r *Runtime) newObjectGoSlice(data *[]interface{}) *objectGoSlice {
+func (r *Runtime) newObjectGoSlice(data *[]interface{}, isPtr bool) *objectGoSlice {
 	obj := &Object{runtime: r}
 	a := &objectGoSlice{
 		baseObject: baseObject{
 			val: obj,
 		},
-		data: data,
+		data:      data,
+		origIsPtr: isPtr,
 	}
 	obj.self = a
 	a.init()
@@ -35,7 +37,6 @@ func (o *objectGoSlice) init() {
 	o.prototype = o.val.runtime.global.ArrayPrototype
 	o.lengthProp.writable = true
 	o.extensible = true
-	o.updateLen()
 	o.baseObject._put("length", &o.lengthProp)
 }
 
@@ -52,6 +53,7 @@ func (o *objectGoSlice) getStr(name unistring.String, receiver Value) Value {
 	if idx := strToGoIdx(name); idx >= 0 && idx < len(*o.data) {
 		ownProp = o._getIdx(idx)
 	} else if name == "length" {
+		o.updateLen()
 		ownProp = &o.lengthProp
 	}
 
@@ -83,6 +85,7 @@ func (o *objectGoSlice) getOwnPropStr(name unistring.String) Value {
 		return nil
 	}
 	if name == "length" {
+		o.updateLen()
 		return &o.lengthProp
 	}
 	return nil
@@ -112,7 +115,6 @@ func (o *objectGoSlice) grow(size int) {
 		}
 		*o.data = (*o.data)[:size]
 	}
-	o.updateLen()
 }
 
 func (o *objectGoSlice) shrink(size int) {
@@ -121,7 +123,6 @@ func (o *objectGoSlice) shrink(size int) {
 		tail[k] = nil
 	}
 	*o.data = (*o.data)[:size]
-	o.updateLen()
 }
 
 func (o *objectGoSlice) putIdx(idx int, v Value, throw bool) {
@@ -297,10 +298,16 @@ func (o *objectGoSlice) stringKeys(_ bool, accum []Value) []Value {
 }
 
 func (o *objectGoSlice) export(*objectExportCtx) interface{} {
+	if o.origIsPtr {
+		return o.data
+	}
 	return *o.data
 }
 
 func (o *objectGoSlice) exportType() reflect.Type {
+	if o.origIsPtr {
+		return reflectTypeArrayPtr
+	}
 	return reflectTypeArray
 }
 

+ 0 - 2
object_goslice_reflect.go

@@ -48,7 +48,6 @@ func (o *objectGoSliceReflect) grow(size int) {
 		}
 		o.fieldsValue.SetLen(size)
 	}
-	o.updateLen()
 }
 
 func (o *objectGoSliceReflect) shrink(size int) {
@@ -59,7 +58,6 @@ func (o *objectGoSliceReflect) shrink(size int) {
 		tail.Index(i).Set(zero)
 	}
 	o.fieldsValue.SetLen(size)
-	o.updateLen()
 }
 
 func (o *objectGoSliceReflect) putLength(v uint32, throw bool) bool {

+ 25 - 0
object_goslice_reflect_test.go

@@ -484,6 +484,31 @@ func TestGoSliceReflect111(t *testing.T) {
 	t.Log(a[0])
 }
 
+func TestGoSliceReflectExternalLenUpdate(t *testing.T) {
+	data := &[]int{1}
+
+	vm := New()
+	vm.Set("data", data)
+	vm.Set("append", func(a *[]int, v int) {
+		if a != data {
+			panic(vm.NewTypeError("a != data"))
+		}
+		*a = append(*a, v)
+	})
+
+	vm.testScriptWithTestLib(`
+		assert.sameValue(data.length, 1);
+
+        // modify with js
+        data.push(1);
+		assert.sameValue(data.length, 2);
+
+        // modify with go
+        append(data, 2);
+		assert.sameValue(data.length, 3);
+    `, _undefined, t)
+}
+
 func BenchmarkGoSliceReflectSet(b *testing.B) {
 	vm := New()
 	a := vm.ToValue([]int{1}).(*Object)

+ 25 - 0
object_goslice_test.go

@@ -271,3 +271,28 @@ func TestGoSliceToString(t *testing.T) {
 		t.Fatal(exp)
 	}
 }
+
+func TestGoSliceExternalLenUpdate(t *testing.T) {
+	data := &[]interface{}{1}
+
+	vm := New()
+	vm.Set("data", data)
+	vm.Set("append", func(a *[]interface{}, v int) {
+		if a != data {
+			panic(vm.NewTypeError("a != data"))
+		}
+		*a = append(*a, v)
+	})
+
+	vm.testScriptWithTestLib(`
+		assert.sameValue(data.length, 1);
+
+        // modify with js
+        data.push(1);
+		assert.sameValue(data.length, 2);
+
+        // modify with go
+        append(data, 2);
+		assert.sameValue(data.length, 3);
+    `, _undefined, t)
+}

+ 2 - 2
runtime.go

@@ -1914,12 +1914,12 @@ func (r *Runtime) toValue(i interface{}, origValue reflect.Value) Value {
 		m.init()
 		return obj
 	case []interface{}:
-		return r.newObjectGoSlice(&i).val
+		return r.newObjectGoSlice(&i, false).val
 	case *[]interface{}:
 		if i == nil {
 			return _null
 		}
-		return r.newObjectGoSlice(i).val
+		return r.newObjectGoSlice(i, true).val
 	}
 
 	if !origValue.IsValid() {

+ 10 - 9
value.go

@@ -43,15 +43,16 @@ var (
 )
 
 var (
-	reflectTypeInt    = reflect.TypeOf(int64(0))
-	reflectTypeBool   = reflect.TypeOf(false)
-	reflectTypeNil    = reflect.TypeOf(nil)
-	reflectTypeFloat  = reflect.TypeOf(float64(0))
-	reflectTypeMap    = reflect.TypeOf(map[string]interface{}{})
-	reflectTypeArray  = reflect.TypeOf([]interface{}{})
-	reflectTypeString = reflect.TypeOf("")
-	reflectTypeFunc   = reflect.TypeOf((func(FunctionCall) Value)(nil))
-	reflectTypeError  = reflect.TypeOf((*error)(nil)).Elem()
+	reflectTypeInt      = reflect.TypeOf(int64(0))
+	reflectTypeBool     = reflect.TypeOf(false)
+	reflectTypeNil      = reflect.TypeOf(nil)
+	reflectTypeFloat    = reflect.TypeOf(float64(0))
+	reflectTypeMap      = reflect.TypeOf(map[string]interface{}{})
+	reflectTypeArray    = reflect.TypeOf([]interface{}{})
+	reflectTypeArrayPtr = reflect.TypeOf((*[]interface{})(nil))
+	reflectTypeString   = reflect.TypeOf("")
+	reflectTypeFunc     = reflect.TypeOf((func(FunctionCall) Value)(nil))
+	reflectTypeError    = reflect.TypeOf((*error)(nil)).Elem()
 )
 
 var intCache [256]Value