Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
For-ACGN opened this issue Dec 31, 2020 · 0 comments
Open

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

For-ACGN opened this issue Dec 31, 2020 · 0 comments

Comments

@For-ACGN
Copy link

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 added a commit to For-ACGN/anko that referenced this issue Dec 31, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant