Explorar el Código

Merge branch 'master' into es6

Dmitry Panov hace 5 años
padre
commit
5a4756e201
Se han modificado 5 ficheros con 129 adiciones y 7 borrados
  1. 1 1
      object_goreflect.go
  2. 46 0
      object_goreflect_test.go
  3. 39 5
      object_goslice_reflect.go
  4. 38 0
      object_goslice_reflect_test.go
  5. 5 1
      runtime.go

+ 1 - 1
object_goreflect.go

@@ -135,7 +135,7 @@ func (o *objectGoReflect) getOwnPropStr(name string) Value {
 	if o.value.Kind() == reflect.Struct {
 		if v := o._getField(name); v.IsValid() {
 			canSet := v.CanSet()
-			if v.Kind() == reflect.Struct && v.CanAddr() {
+			if (v.Kind() == reflect.Struct || v.Kind() == reflect.Slice) && v.CanAddr() {
 				v = v.Addr()
 			}
 			return &valueProperty{

+ 46 - 0
object_goreflect_test.go

@@ -540,6 +540,52 @@ func TestGoReflectCustomNaming(t *testing.T) {
 	})
 }
 
+func TestGoReflectCustomObjNaming(t *testing.T) {
+
+	type testStructWithJsonTags struct {
+		A string `json:"b"` // <-- script sees field "A" as property "b"
+	}
+
+	r := New()
+	r.SetFieldNameMapper(&jsonTagNamer{})
+
+	t.Run("Set object in slice", func(t *testing.T) {
+		testSlice := &[]testStructWithJsonTags{{"Hello world"}}
+		r.Set("testslice", testSlice)
+		_, err := r.RunString(`testslice[0] = {b:"setted"}`)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if (*testSlice)[0].A != "setted" {
+			t.Fatalf("Expected \"setted\", got %q", (*testSlice)[0])
+		}
+	})
+
+	t.Run("Set object in map", func(t *testing.T) {
+		testMap := map[string]testStructWithJsonTags{"key": {"Hello world"}}
+		r.Set("testmap", testMap)
+		_, err := r.RunString(`testmap["key"] = {b:"setted"}`)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if testMap["key"].A != "setted" {
+			t.Fatalf("Expected \"setted\", got %q", testMap["key"])
+		}
+	})
+
+	t.Run("Add object to map", func(t *testing.T) {
+		testMap := map[string]testStructWithJsonTags{}
+		r.Set("testmap", testMap)
+		_, err := r.RunString(`testmap["newkey"] = {b:"setted"}`)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if testMap["newkey"].A != "setted" {
+			t.Fatalf("Expected \"setted\", got %q", testMap["newkey"])
+		}
+	})
+}
+
 type fieldNameMapper1 struct{}
 
 func (fieldNameMapper1) FieldName(t reflect.Type, f reflect.StructField) string {

+ 39 - 5
object_goslice_reflect.go

@@ -7,14 +7,16 @@ import (
 
 type objectGoSliceReflect struct {
 	objectGoReflect
-	lengthProp valueProperty
+	lengthProp      valueProperty
+	sliceExtensible bool
 }
 
 func (o *objectGoSliceReflect) init() {
 	o.objectGoReflect.init()
 	o.class = classArray
 	o.prototype = o.val.runtime.global.ArrayPrototype
-	o.lengthProp.writable = false
+	o.sliceExtensible = o.value.CanSet()
+	o.lengthProp.writable = o.sliceExtensible
 	o._setLen()
 	o.baseObject._put("length", &o.lengthProp)
 }
@@ -95,8 +97,11 @@ func (o *objectGoSliceReflect) getOwnPropStr(name string) Value {
 
 func (o *objectGoSliceReflect) putIdx(idx int64, v Value, throw bool) {
 	if idx >= int64(o.value.Len()) {
-		o.val.runtime.typeErrorResult(throw, "Cannot extend a Go reflect slice")
-		return
+		if !o.sliceExtensible {
+			o.val.runtime.typeErrorResult(throw, "Cannot extend a Go unaddressable reflect slice")
+			return
+		}
+		o.grow(int(idx + 1))
 	}
 	val, err := o.val.runtime.toReflectValue(v, o.value.Type().Elem())
 	if err != nil {
@@ -106,6 +111,32 @@ func (o *objectGoSliceReflect) putIdx(idx int64, v Value, throw bool) {
 	o.value.Index(int(idx)).Set(val)
 }
 
+func (o *objectGoSliceReflect) grow(size int) {
+	newcap := o.value.Cap()
+	if newcap < size {
+		// Use the same algorithm as in runtime.growSlice
+		doublecap := newcap + newcap
+		if size > doublecap {
+			newcap = size
+		} else {
+			if o.value.Len() < 1024 {
+				newcap = doublecap
+			} else {
+				for newcap < size {
+					newcap += newcap / 4
+				}
+			}
+		}
+
+		n := reflect.MakeSlice(o.value.Type(), size, newcap)
+		reflect.Copy(n, o.value)
+		o.value.Set(n)
+	} else {
+		o.value.SetLen(size)
+	}
+	o._setLen()
+}
+
 func (o *objectGoSliceReflect) put(n Value, val Value, throw bool) {
 	if idx := toIdx(n); idx >= 0 {
 		o.putIdx(idx, val, throw)
@@ -120,7 +151,10 @@ func (o *objectGoSliceReflect) putStr(name string, val Value, throw bool) {
 		o.putIdx(idx, val, throw)
 		return
 	}
-	// TODO: length
+	if name == "length" {
+		o.baseObject.putStr(name, val, throw)
+		return
+	}
 	o.objectGoReflect.putStr(name, val, throw)
 }
 

+ 38 - 0
object_goslice_reflect_test.go

@@ -71,6 +71,44 @@ func TestGoSliceReflectSet(t *testing.T) {
 	}
 }
 
+func TestGoSliceReflectPush(t *testing.T) {
+
+	r := New()
+
+	t.Run("Can push to array by array ptr", func(t *testing.T) {
+		a := []int8{1}
+		r.Set("a", &a)
+		_, err := r.RunString(`a.push (10)`)
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		if a[1] != 10 {
+			t.Fatalf("a[1] = %d, expected 10", a[1])
+		}
+	})
+
+	t.Run("Can push to array by struct ptr", func(t *testing.T) {
+		type testStr struct {
+			A []int
+		}
+		a := testStr{
+			A: []int{2},
+		}
+
+		r.Set("a", &a)
+		_, err := r.RunString(`a.A.push (10)`)
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		if a.A[1] != 10 {
+			t.Fatalf("a[1] = %v, expected 10", a)
+		}
+	})
+
+}
+
 func TestGoSliceReflectProto(t *testing.T) {
 	const SCRIPT = `
 	a.join(",")

+ 5 - 1
runtime.go

@@ -1353,11 +1353,15 @@ func (r *Runtime) toReflectValue(v Value, typ reflect.Type) (reflect.Value, erro
 			for i := 0; i < typ.NumField(); i++ {
 				field := typ.Field(i)
 				if ast.IsExported(field.Name) {
+					name := field.Name
+					if r.fieldNameMapper != nil {
+						name = r.fieldNameMapper.FieldName(typ, field)
+					}
 					var v Value
 					if field.Anonymous {
 						v = o
 					} else {
-						v = o.self.getStr(field.Name)
+						v = o.self.getStr(name)
 					}
 
 					if v != nil {