go/ssa/interp: fix goroutines and channels leaks

rangeIter leaks goroutines and channels
on each incomplete map iteration.
So remove goroutines and channels iterators
and use reflect.(Value).MapRange() iteration instead.

Resolves TODO left by @adonovan

Change-Id: I8cc24b264ae782376c392fd9497be81bf50a415f
GitHub-Last-Rev: 5bba70b8658575b3e2e33b8941fce3ef75199a4f
GitHub-Pull-Request: golang/tools#198
Reviewed-on: https://go-review.googlesource.com/c/tools/+/215457
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Alan Donovan <adonovan@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/go/ssa/interp/interp.go b/go/ssa/interp/interp.go
index 76b7ca3..d776594 100644
--- a/go/ssa/interp/interp.go
+++ b/go/ssa/interp/interp.go
@@ -32,8 +32,6 @@
 // makes no attempt to distinguish target panics from interpreter
 // crashes.
 //
-// * map iteration is asymptotically inefficient.
-//
 // * the sizes of the int, uint and uintptr types in the target
 // program are assumed to be the same as those of the interpreter
 // itself.
diff --git a/go/ssa/interp/ops.go b/go/ssa/interp/ops.go
index ca004d7..fdc0161 100644
--- a/go/ssa/interp/ops.go
+++ b/go/ssa/interp/ops.go
@@ -11,6 +11,7 @@
 	"go/token"
 	"go/types"
 	"os"
+	"reflect"
 	"strings"
 	"sync"
 	"unsafe"
@@ -1077,34 +1078,9 @@
 func rangeIter(x value, t types.Type) iter {
 	switch x := x.(type) {
 	case map[value]value:
-		// TODO(adonovan): fix: leaks goroutines and channels
-		// on each incomplete map iteration.  We need to open
-		// up an iteration interface using the
-		// reflect.(Value).MapKeys machinery.
-		it := make(mapIter)
-		go func() {
-			for k, v := range x {
-				it <- [2]value{k, v}
-			}
-			close(it)
-		}()
-		return it
+		return &mapIter{iter: reflect.ValueOf(x).MapRange()}
 	case *hashmap:
-		// TODO(adonovan): fix: leaks goroutines and channels
-		// on each incomplete map iteration.  We need to open
-		// up an iteration interface using the
-		// reflect.(Value).MapKeys machinery.
-		it := make(mapIter)
-		go func() {
-			for _, e := range x.entries() {
-				for e != nil {
-					it <- [2]value{e.key, e.value}
-					e = e.next
-				}
-			}
-			close(it)
-		}()
-		return it
+		return &hashmapIter{iter: reflect.ValueOf(x.entries()).MapRange()}
 	case string:
 		return &stringIter{Reader: strings.NewReader(x)}
 	}
diff --git a/go/ssa/interp/value.go b/go/ssa/interp/value.go
index 4faa7b8..94018b5 100644
--- a/go/ssa/interp/value.go
+++ b/go/ssa/interp/value.go
@@ -489,9 +489,37 @@
 	return okv
 }
 
-type mapIter chan [2]value
+type mapIter struct {
+	iter *reflect.MapIter
+	ok   bool
+}
 
-func (it mapIter) next() tuple {
-	kv, ok := <-it
-	return tuple{ok, kv[0], kv[1]}
+func (it *mapIter) next() tuple {
+	it.ok = it.iter.Next()
+	if !it.ok {
+		return []value{false, nil, nil}
+	}
+	k, v := it.iter.Key().Interface(), it.iter.Value().Interface()
+	return []value{true, k, v}
+}
+
+type hashmapIter struct {
+	iter *reflect.MapIter
+	ok   bool
+	cur  *entry
+}
+
+func (it *hashmapIter) next() tuple {
+	for {
+		if it.cur != nil {
+			k, v := it.cur.key, it.cur.value
+			it.cur = it.cur.next
+			return []value{true, k, v}
+		}
+		it.ok = it.iter.Next()
+		if !it.ok {
+			return []value{false, nil, nil}
+		}
+		it.cur = it.iter.Value().Interface().(*entry)
+	}
 }