Merge pull request #193 from pkg/fixedbugs/188

Return errors.Frame to a uintptr
diff --git a/format_test.go b/format_test.go
index fd35ca3..cb1df82 100644
--- a/format_test.go
+++ b/format_test.go
@@ -385,6 +385,7 @@
 }
 
 func testFormatRegexp(t *testing.T, n int, arg interface{}, format, want string) {
+	t.Helper()
 	got := fmt.Sprintf(format, arg)
 	gotLines := strings.SplitN(got, "\n", -1)
 	wantLines := strings.SplitN(want, "\n", -1)
diff --git a/stack.go b/stack.go
index 5be4627..ddc8c30 100644
--- a/stack.go
+++ b/stack.go
@@ -11,7 +11,42 @@
 )
 
 // Frame represents a program counter inside a stack frame.
-type Frame runtime.Frame
+type Frame uintptr
+
+// pc returns the program counter for this frame;
+// multiple frames may have the same PC value.
+func (f Frame) pc() uintptr { return uintptr(f) - 1 }
+
+// file returns the full path to the file that contains the
+// function for this Frame's pc.
+func (f Frame) file() string {
+	fn := runtime.FuncForPC(f.pc())
+	if fn == nil {
+		return "unknown"
+	}
+	file, _ := fn.FileLine(f.pc())
+	return file
+}
+
+// line returns the line number of source code of the
+// function for this Frame's pc.
+func (f Frame) line() int {
+	fn := runtime.FuncForPC(f.pc())
+	if fn == nil {
+		return 0
+	}
+	_, line := fn.FileLine(f.pc())
+	return line
+}
+
+// name returns the name of this function, if known.
+func (f Frame) name() string {
+	fn := runtime.FuncForPC(f.pc())
+	if fn == nil {
+		return "unknown"
+	}
+	return fn.Name()
+}
 
 // Format formats the frame according to the fmt.Formatter interface.
 //
@@ -35,25 +70,16 @@
 	case 's':
 		switch {
 		case s.Flag('+'):
-			if f.Function == "" {
-				io.WriteString(w, "unknown")
-			} else {
-				io.WriteString(w, f.Function)
-				io.WriteString(w, "\n\t")
-				io.WriteString(w, f.File)
-			}
+			io.WriteString(w, f.name())
+			io.WriteString(w, "\n\t")
+			io.WriteString(w, f.file())
 		default:
-			file := f.File
-			if file == "" {
-				file = "unknown"
-			}
-			io.WriteString(w, path.Base(file))
+			io.WriteString(w, path.Base(f.file()))
 		}
 	case 'd':
-		io.WriteString(w, strconv.Itoa(f.Line))
+		io.WriteString(w, strconv.Itoa(f.line()))
 	case 'n':
-		name := f.Function
-		io.WriteString(s, funcname(name))
+		io.WriteString(w, funcname(f.name()))
 	case 'v':
 		f.format(w, s, 's')
 		io.WriteString(w, ":")
@@ -79,9 +105,9 @@
 		switch {
 		case s.Flag('+'):
 			b.Grow(len(st) * stackMinLen)
-			for _, fr := range st {
+			for _, f := range st {
 				b.WriteByte('\n')
-				fr.format(&b, s, verb)
+				f.format(&b, s, verb)
 			}
 		case s.Flag('#'):
 			fmt.Fprintf(&b, "%#v", []Frame(st))
@@ -125,29 +151,20 @@
 	case 'v':
 		switch {
 		case st.Flag('+'):
-			frames := runtime.CallersFrames(*s)
-			for {
-				frame, more := frames.Next()
-				fmt.Fprintf(st, "\n%+v", Frame(frame))
-				if !more {
-					break
-				}
+			for _, pc := range *s {
+				f := Frame(pc)
+				fmt.Fprintf(st, "\n%+v", f)
 			}
 		}
 	}
 }
 
 func (s *stack) StackTrace() StackTrace {
-	var st []Frame
-	frames := runtime.CallersFrames(*s)
-	for {
-		frame, more := frames.Next()
-		st = append(st, Frame(frame))
-		if !more {
-			break
-		}
+	f := make([]Frame, len(*s))
+	for i := 0; i < len(f); i++ {
+		f[i] = Frame((*s)[i])
 	}
-	return st
+	return f
 }
 
 func callers() *stack {
diff --git a/stack_test.go b/stack_test.go
index 7f76694..172a57d 100644
--- a/stack_test.go
+++ b/stack_test.go
@@ -35,11 +35,11 @@
 		"github.com/pkg/errors.init\n" +
 			"\t.+/github.com/pkg/errors/stack_test.go",
 	}, {
-		Frame{},
+		0,
 		"%s",
 		"unknown",
 	}, {
-		Frame{},
+		0,
 		"%+s",
 		"unknown",
 	}, {
@@ -47,7 +47,7 @@
 		"%d",
 		"9",
 	}, {
-		Frame{},
+		0,
 		"%d",
 		"0",
 	}, {
@@ -69,7 +69,7 @@
 		"%n",
 		"X.val",
 	}, {
-		Frame{},
+		0,
 		"%n",
 		"",
 	}, {
@@ -82,7 +82,7 @@
 		"github.com/pkg/errors.init\n" +
 			"\t.+/github.com/pkg/errors/stack_test.go:9",
 	}, {
-		Frame{},
+		0,
 		"%v",
 		"unknown:0",
 	}}
@@ -246,7 +246,7 @@
 	n := runtime.Callers(2, pcs[:])
 	frames := runtime.CallersFrames(pcs[:n])
 	frame, _ := frames.Next()
-	return Frame(frame)
+	return Frame(frame.PC)
 }
 
 //go:noinline