-
Notifications
You must be signed in to change notification settings - Fork 356
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The empty interface (interface{}), and its variants (such as []interface{} and map[string]interface{}), are commonly used in Go to (json) Unmarshal arbitrary data. Within Yaegi, all interface types are wrapped in a valueInterface struct in order to retain all the information needed for a consistent internal state (as reflect is not enough to achieve that). However, this wrapping ends up being problematic when it comes to the type assertions related to the aforementioned Unmarshaling. Therefore, this PR is an attempt to consider the empty interface (and its variants) as an exception within Yaegi, that should never be wrapped within a valueInterface, and to treat it similarly to the other basic Go types. The assumption is that the wrapping should not be needed, as there is no information about implemented methods to maintain. Fixes #984 Fixes #829 Fixes #1015
- Loading branch information
Showing
20 changed files
with
800 additions
and
156 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,33 @@ | ||
package main | ||
|
||
func main() { | ||
b := 2 | ||
var a interface{} = 5 + b | ||
b := 2 // int | ||
|
||
var c int = 5 + b | ||
println(c) | ||
|
||
var d int32 = 6 + int32(b) | ||
println(d) | ||
|
||
var a interface{} = 7 + b | ||
println(a.(int)) | ||
|
||
var e int32 = 2 | ||
var f interface{} = 8 + e | ||
println(f.(int32)) | ||
|
||
a = 9 + e | ||
println(a.(int32)) | ||
|
||
var g int = 2 | ||
a = 10 + g | ||
println(a.(int)) | ||
} | ||
|
||
// Output: | ||
// 7 | ||
// 8 | ||
// 9 | ||
// 10 | ||
// 11 | ||
// 12 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ package main | |
|
||
import ( | ||
"encoding/xml" | ||
"errors" | ||
"fmt" | ||
) | ||
|
||
|
@@ -10,7 +11,30 @@ type Email struct { | |
Addr string | ||
} | ||
|
||
func f(s string, r interface{}) error { | ||
func f(r interface{}) error { | ||
return withPointerAsInterface(&r) | ||
} | ||
|
||
func withPointerAsInterface(r interface{}) error { | ||
_ = (r).(*interface{}) | ||
rp, ok := (r).(*interface{}) | ||
if !ok { | ||
return errors.New("cannot assert to *interface{}") | ||
} | ||
em, ok := (*rp).(*Email) | ||
if !ok { | ||
return errors.New("cannot assert to *Email") | ||
} | ||
em.Where = "work" | ||
em.Addr = "[email protected]" | ||
return nil | ||
} | ||
|
||
func ff(s string, r interface{}) error { | ||
return xml.Unmarshal([]byte(s), r) | ||
} | ||
|
||
func fff(s string, r interface{}) error { | ||
return xml.Unmarshal([]byte(s), &r) | ||
} | ||
|
||
|
@@ -21,9 +45,19 @@ func main() { | |
</Email> | ||
` | ||
v := Email{} | ||
err := f(data, &v) | ||
err := f(&v) | ||
fmt.Println(err, v) | ||
|
||
vv := Email{} | ||
err = ff(data, &vv) | ||
fmt.Println(err, vv) | ||
|
||
vvv := Email{} | ||
err = ff(data, &vvv) | ||
fmt.Println(err, vvv) | ||
} | ||
|
||
// Ouput: | ||
// <nil> {work [email protected]} | ||
// <nil> {work [email protected]} | ||
// <nil> {work [email protected]} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
) | ||
|
||
func main() { | ||
var a interface{} | ||
a = 2 | ||
fmt.Println(a) | ||
|
||
var b *interface{} | ||
b = &a | ||
fmt.Println(*b) | ||
|
||
var c **interface{} | ||
c = &b | ||
fmt.Println(**c) | ||
} | ||
|
||
// Output: | ||
// 2 | ||
// 2 | ||
// 2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"log" | ||
) | ||
|
||
const jsonData = `[ | ||
"foo", | ||
"bar" | ||
]` | ||
|
||
const jsonData2 = `[ | ||
{"foo": "foo"}, | ||
{"bar": "bar"} | ||
]` | ||
|
||
const jsonData3 = `{ | ||
"foo": "foo", | ||
"bar": "bar" | ||
}` | ||
|
||
func fromSlice() { | ||
var a []interface{} | ||
var c, d interface{} | ||
c = 2 | ||
d = 3 | ||
a = []interface{}{c, d} | ||
|
||
if err := json.Unmarshal([]byte(jsonData), &a); err != nil { | ||
log.Fatalln(err) | ||
} | ||
|
||
for k, v := range a { | ||
fmt.Println(k, ":", v) | ||
} | ||
} | ||
|
||
func fromEmpty() { | ||
var a interface{} | ||
var c, d interface{} | ||
c = 2 | ||
d = 3 | ||
a = []interface{}{c, d} | ||
|
||
if err := json.Unmarshal([]byte(jsonData), &a); err != nil { | ||
log.Fatalln(err) | ||
} | ||
|
||
b := a.([]interface{}) | ||
|
||
for k, v := range b { | ||
fmt.Println(k, ":", v) | ||
} | ||
} | ||
|
||
func sliceOfObjects() { | ||
var a interface{} | ||
|
||
if err := json.Unmarshal([]byte(jsonData2), &a); err != nil { | ||
log.Fatalln(err) | ||
} | ||
|
||
b := a.([]interface{}) | ||
|
||
for k, v := range b { | ||
fmt.Println(k, ":", v) | ||
} | ||
} | ||
|
||
func intoMap() { | ||
var a interface{} | ||
|
||
if err := json.Unmarshal([]byte(jsonData3), &a); err != nil { | ||
log.Fatalln(err) | ||
} | ||
|
||
b := a.(map[string]interface{}) | ||
|
||
seenFoo := false | ||
for k, v := range b { | ||
vv := v.(string) | ||
if vv != "foo" { | ||
if seenFoo { | ||
fmt.Println(k, ":", vv) | ||
break | ||
} | ||
kk := k | ||
vvv := vv | ||
defer fmt.Println(kk, ":", vvv) | ||
continue | ||
} | ||
seenFoo = true | ||
fmt.Println(k, ":", vv) | ||
} | ||
} | ||
|
||
func main() { | ||
fromSlice() | ||
fromEmpty() | ||
sliceOfObjects() | ||
intoMap() | ||
} | ||
|
||
// Ouput: | ||
// 0 : foo | ||
// 1 : bar | ||
// 0 : foo | ||
// 1 : bar | ||
// 0 : map[foo:foo] | ||
// 1 : map[bar:bar] | ||
// foo : foo | ||
// bar : bar |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"net/url" | ||
) | ||
|
||
func main() { | ||
body := []byte(`{ | ||
"BODY_1": "VALUE_1", | ||
"BODY_2": "VALUE_2", | ||
"BODY_3": null, | ||
"BODY_4": { | ||
"BODY_1": "VALUE_1", | ||
"BODY_2": "VALUE_2", | ||
"BODY_3": null | ||
}, | ||
"BODY_5": [ | ||
"VALUE_1", | ||
"VALUE_2", | ||
"VALUE_3" | ||
] | ||
}`) | ||
|
||
values := url.Values{} | ||
|
||
var rawData map[string]interface{} | ||
err := json.Unmarshal(body, &rawData) | ||
if err != nil { | ||
fmt.Println("can't parse body") | ||
return | ||
} | ||
|
||
for key, val := range rawData { | ||
switch val.(type) { | ||
case string, bool, float64: | ||
values.Add(key, fmt.Sprint(val)) | ||
case nil: | ||
values.Add(key, "") | ||
case map[string]interface{}, []interface{}: | ||
jsonVal, err := json.Marshal(val) | ||
if err != nil { | ||
fmt.Println("can't encode json") | ||
return | ||
} | ||
values.Add(key, string(jsonVal)) | ||
} | ||
} | ||
fmt.Println(values.Get("BODY_1")) | ||
fmt.Println(values.Get("BODY_2")) | ||
fmt.Println(values.Get("BODY_3")) | ||
fmt.Println(values.Get("BODY_4")) | ||
fmt.Println(values.Get("BODY_5")) | ||
} | ||
|
||
// Output: | ||
// VALUE_1 | ||
// VALUE_2 | ||
// | ||
// {"BODY_1":"VALUE_1","BODY_2":"VALUE_2","BODY_3":null} | ||
// ["VALUE_1","VALUE_2","VALUE_3"] |
Oops, something went wrong.