anko
anko copied to clipboard
when "new" and "make" structure, field will not be zero value.
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
}