anko icon indicating copy to clipboard operation
anko copied to clipboard

when "new" and "make" structure, field will not be zero value.

Open For-ACGN opened this issue 3 years ago • 0 comments

test code:

type FooStruct struct {
	function  func(string)
	Pointer   *int
	Slice     []string
	Map       map[string]string
	Channel   chan string
	Function  func(string)
	Interface interface{}
	Transport http.RoundTripper
	Str2      FooStruct2
	Str2p     *FooStruct2
}

type FooStruct2 struct {
	Pointer *int
}

// Println is used to check structure fields are Zero Value.
func (f *FooStruct) Println() {
	fmt.Println("func(unexported):", f.function == nil)
	fmt.Println("pointer:", f.Pointer == nil)
	fmt.Println("slice:", f.Slice == nil)
	fmt.Println("map:", f.Map == nil)
	fmt.Println("chan:", f.Channel == nil)
	fmt.Println("func:", f.Function == nil)
	fmt.Println("interface{}:", f.Interface == nil)
	fmt.Println("interface:", f.Transport == nil)
	fmt.Println("str2:", f.Str2.Pointer == nil)
	fmt.Println("str2p:", f.Str2p == nil)
	fmt.Println()
}

func TestAnkoMakeStruct(t *testing.T) {
	// Zero Value
	fs1 := new(FooStruct)
	fs1.Println()
	fs2 := FooStruct{}
	fs2.Println()

	// some fields not Zero Value
	e := env.NewEnv()
	err := e.DefineType("FooStruct", reflect.TypeOf(fs1).Elem())
	require.NoError(t, err)
	src := `
fs1 = new(FooStruct)
fs1.Println()

fs2 = make(FooStruct)
fs2.Println()
`
	stmt, err := parser.ParseSrc(src)
	require.NoError(t, err)
	_, err = vm.Run(e, nil, stmt)
	require.NoError(t, err)
}

output:

func(unexported): true
pointer: true
slice: true
map: true
chan: true
func: true
interface{}: true
interface: true
str2: true
str2p: true

func(unexported): true
pointer: true
slice: true
map: true
chan: true
func: true
interface{}: true
interface: true
str2: true
str2p: true

func(unexported): true
pointer: true
slice: false
map: false
chan: false
func: false
interface{}: true
interface: true
str2: true
str2p: true

func(unexported): true
pointer: true
slice: false
map: false
chan: false
func: false
interface{}: true
interface: true
str2: true
str2p: true
slice, map, chan and func will not be set zero value, but pointer is zero value.
it will occur some package panic like net/http.Client
// code in src/net/http
func (c *Client) checkRedirect(req *Request, via []*Request) error {
    fn := c.CheckRedirect
    if fn == nil {       // [error] in anko, this if is unexpected.
        fn = defaultCheckRedirect
    }
    return fn(req, via)
}

func TestAnkoMakeHTTPClient(t *testing.T) {
	e := env.NewEnv()

	src := `
http = import("net/http")

// must 302
url = "http://example.com/302"
req, err = http.NewRequest(http.MethodGet, url, nil)
if err != nil {
    return false, err
}
client = new(http.Client)
resp, err = client.Do(req) // will return a nil pointer error
if err != nil {
    return false, err
}

println(client.CheckRedirect == nil) // false
println(client.CheckRedirect) // invalid function

// ok
client.CheckRedirect = nil
resp, err = client.Do(req)
if err != nil {
    return false, err
}
`
	stmt, err := parser.ParseSrc(src)
	require.NoError(t, err)
	_, err = vm.Run(e, nil, stmt)
	require.NoError(t, err)
}

file anko/vm/vm.go:

line 394:

func makeValue(t reflect.Type) (reflect.Value, error) {
	switch t.Kind() {
	case reflect.Chan:
		return reflect.MakeChan(t, 0), nil
	case reflect.Func:
		return reflect.MakeFunc(t, nil), nil
	case reflect.Map:
		// note creating slice as work around to create map
		// just doing MakeMap can give incorrect type for defined types
		value := reflect.MakeSlice(reflect.SliceOf(t), 0, 1)
		value = reflect.Append(value, reflect.MakeMap(reflect.MapOf(t.Key(), t.Elem())))
		return value.Index(0), nil
	case reflect.Ptr:
		ptrV := reflect.New(t.Elem())
		v, err := makeValue(t.Elem())
		if err != nil {
			return nilValue, err
		}
		
		ptrV.Elem().Set(v)
		return ptrV, nil
	case reflect.Slice:
		return reflect.MakeSlice(t, 0, 0), nil
	case reflect.Struct:
		 structV := reflect.New(t).Elem()
		 for i := 0; i < structV.NumField(); i++ {
                        // here Pointer will be zero value, but other type is not
		 	if structV.Field(i).Kind() == reflect.Ptr {
		 		continue
		 	}
		 	v, err := makeValue(structV.Field(i).Type())
			if err != nil {
				return nilValue, err
			}
			if structV.Field(i).CanSet() {
				structV.Field(i).Set(v)
			}
		 }
		 return structV, nil

	
	        // only this code is ok
		// return reflect.New(t).Elem(), nil
	}
	return reflect.New(t).Elem(), nil
}

For-ACGN avatar Dec 31 '20 03:12 For-ACGN