
This change extends defer support to all supported builitin functions. Not all of them make sense (such as len, cap, real, imag, etc) but this change for example adds support for `defer(delete(m, key))` which is used in the Go 1.15 encoding/json package.
213 строки
3,5 КиБ
Go
213 строки
3,5 КиБ
Go
package main
|
|
|
|
type Thing struct {
|
|
name string
|
|
}
|
|
|
|
type ThingOption func(*Thing)
|
|
|
|
func WithName(name string) ThingOption {
|
|
return func(t *Thing) {
|
|
t.name = name
|
|
}
|
|
}
|
|
|
|
func NewThing(opts ...ThingOption) *Thing {
|
|
t := &Thing{}
|
|
for _, opt := range opts {
|
|
opt(t)
|
|
}
|
|
return t
|
|
}
|
|
|
|
func (t Thing) String() string {
|
|
return t.name
|
|
}
|
|
|
|
func (t Thing) Print(arg string) {
|
|
println("Thing.Print:", t.name, "arg:", arg)
|
|
}
|
|
|
|
type Printer interface {
|
|
Print(string)
|
|
}
|
|
|
|
func main() {
|
|
thing := &Thing{"foo"}
|
|
|
|
// function pointers
|
|
runFunc(hello, 5) // must be indirect to avoid obvious inlining
|
|
|
|
// deferred functions
|
|
testDefer()
|
|
|
|
// defers in loop
|
|
testDeferLoop()
|
|
|
|
//defer func variable call
|
|
testDeferFuncVar()
|
|
|
|
//More complicated func variable call
|
|
testMultiFuncVar()
|
|
|
|
// Take a bound method and use it as a function pointer.
|
|
// This function pointer needs a context pointer.
|
|
testBound(thing.String)
|
|
|
|
// closures
|
|
func() {
|
|
println("thing inside closure:", thing.String())
|
|
}()
|
|
runFunc(func(i int) {
|
|
println("inside fp closure:", thing.String(), i)
|
|
}, 3)
|
|
|
|
// functional arguments
|
|
thingFunctionalArgs1 := NewThing()
|
|
thingFunctionalArgs1.Print("functional args 1")
|
|
thingFunctionalArgs2 := NewThing(WithName("named thing"))
|
|
thingFunctionalArgs2.Print("functional args 2")
|
|
|
|
// regression testing
|
|
regression1033()
|
|
|
|
//Test deferred builtins
|
|
testDeferBuiltinClose()
|
|
testDeferBuiltinDelete()
|
|
}
|
|
|
|
func runFunc(f func(int), arg int) {
|
|
f(arg)
|
|
}
|
|
|
|
func hello(n int) {
|
|
println("hello from function pointer:", n)
|
|
}
|
|
|
|
func testDefer() {
|
|
defer exportedDefer()
|
|
|
|
i := 1
|
|
defer deferred("...run as defer", i)
|
|
i++
|
|
defer func() {
|
|
println("...run closure deferred:", i)
|
|
}()
|
|
i++
|
|
defer deferred("...run as defer", i)
|
|
i++
|
|
|
|
var t Printer = &Thing{"foo"}
|
|
defer t.Print("bar")
|
|
|
|
println("deferring...")
|
|
d := dumb{}
|
|
defer d.Value(0)
|
|
}
|
|
|
|
func testDeferLoop() {
|
|
for j := 0; j < 4; j++ {
|
|
defer deferred("loop", j)
|
|
}
|
|
}
|
|
|
|
func testDeferFuncVar() {
|
|
dummy, f := deferFunc()
|
|
dummy++
|
|
defer f(1)
|
|
}
|
|
|
|
func testMultiFuncVar() {
|
|
f := multiFuncDefer()
|
|
defer f(1)
|
|
}
|
|
|
|
func testDeferBuiltinClose() {
|
|
i := make(chan int)
|
|
func() {
|
|
defer close(i)
|
|
}()
|
|
if n, ok := <-i; n != 0 || ok {
|
|
println("expected to read 0 from closed channel")
|
|
}
|
|
}
|
|
|
|
func testDeferBuiltinDelete() {
|
|
m := map[int]int{3: 30, 5: 50}
|
|
func() {
|
|
defer delete(m, 3)
|
|
if m[3] != 30 {
|
|
println("expected m[3] to be 30")
|
|
}
|
|
}()
|
|
if m[3] != 0 {
|
|
println("expected m[3] to be 0")
|
|
}
|
|
}
|
|
|
|
type dumb struct {
|
|
}
|
|
|
|
func (*dumb) Value(key interface{}) interface{} {
|
|
return nil
|
|
}
|
|
|
|
func deferred(msg string, i int) {
|
|
println(msg, i)
|
|
}
|
|
|
|
//export __exportedDefer
|
|
func exportedDefer() {
|
|
println("...exported defer")
|
|
}
|
|
|
|
func deferFunc() (int, func(int)) {
|
|
return 0, func(i int) { println("...extracted defer func ", i) }
|
|
}
|
|
|
|
func multiFuncDefer() func(int) {
|
|
i := 0
|
|
|
|
if i > 0 {
|
|
return func(i int) { println("Should not have gotten here. i = ", i) }
|
|
}
|
|
|
|
return func(i int) { println("Called the correct function. i = ", i) }
|
|
}
|
|
|
|
func testBound(f func() string) {
|
|
println("bound method:", f())
|
|
}
|
|
|
|
// regression1033 is a regression test for https://github.com/tinygo-org/tinygo/issues/1033.
|
|
// In previous versions of the compiler, a deferred call to an interface would create an instruction that did not dominate its uses.
|
|
func regression1033() {
|
|
foo(&Bar{})
|
|
}
|
|
|
|
type Bar struct {
|
|
empty bool
|
|
}
|
|
|
|
func (b *Bar) Close() error {
|
|
return nil
|
|
}
|
|
|
|
type Closer interface {
|
|
Close() error
|
|
}
|
|
|
|
func foo(bar *Bar) error {
|
|
var a int
|
|
if !bar.empty {
|
|
a = 10
|
|
if a != 5 {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
var c Closer = bar
|
|
defer c.Close()
|
|
|
|
return nil
|
|
}
|