Skip to content

関数名の末尾に-fmとあるものはメソッドのwrapperを意味する

関数アドレスから名前を取り出すと、メソッド名の末尾に -fm という謎の文字列が付いている。

package main
import (
"fmt"
"reflect"
"runtime"
)
type Str string
func (s Str) String() string { return "hello" }
type StrP string
func (s *StrP) String() string { return "hello" }
func main() {
fmt.Println(runtime.FuncForPC(reflect.ValueOf(main).Pointer()).Name())
// Output: main.main
var s Str
fmt.Println(runtime.FuncForPC(reflect.ValueOf(s.String).Pointer()).Name())
// Output: main.Str.String-fm
var p StrP
fmt.Println(runtime.FuncForPC(reflect.ValueOf(p.String).Pointer()).Name())
// Output: main.(*StrP).String-fm
}

runtime.FuncForPC はシンボルテーブルに入っている値を取り出しているだけなので、ビルド時に埋め込まれている。

これはcmd/compile/internal/walk/closure.go:methodValueWrapperのコメントを読む限りではメソッドラッパーを意味するらしい。

methodValueWrapper returns the ONAME node representing the wrapper function (*-fm) needed for the given method value. If the wrapper function hasn’t already been created yet, it’s created and added to typecheck.Target.Decls.

Goでは

type T
f := T.Method

とすると第一引数にレシーバを受け取る関数となるが、

var v T
f := v.Method

ではレシーバを受け取らず内部で持った状態の関数を扱えるので、この仕様に関連するのだろう。

Goのコンテキストレジスタも関係がある。