diff --git a/chore/gentests/gentests.go b/chore/gentests/gentests.go index 1c094b46d8..819dfaf5ed 100644 --- a/chore/gentests/gentests.go +++ b/chore/gentests/gentests.go @@ -40,6 +40,7 @@ func main() { llgenDir(dir + "/cl/_testgo") llgenDir(dir + "/cl/_testpy") llgenDir(dir + "/cl/_testdata") + genMetaDir(dir + "/cl/_testmeta") genExpects(dir) } @@ -77,6 +78,34 @@ func genExpects(root string) { runExpectDir(root, "cl/_testdata") } +func genMetaDir(dir string) { + fis, err := os.ReadDir(dir) + check(err) + for _, fi := range fis { + name := fi.Name() + if !fi.IsDir() || strings.HasPrefix(name, "_") { + continue + } + testDir := filepath.Join(dir, name) + if _, err := os.Stat(filepath.Join(testDir, "in.go")); err != nil { + if os.IsNotExist(err) { + continue + } + check(err) + } + relPath, err := filepath.Rel(filepath.Dir(filepath.Dir(dir)), testDir) + check(err) + relPath = filepath.ToSlash(relPath) + fmt.Fprintln(os.Stderr, "meta", relPath) + meta, err := cltest.CaptureMeta("./"+relPath, testDir) + if err != nil { + fmt.Fprintln(os.Stderr, "error:", relPath, err) + continue + } + check(os.WriteFile(filepath.Join(testDir, "meta-expect.txt"), []byte(meta), 0644)) + } +} + func runExpectDir(root, relDir string, configure ...func(*build.Config)) { dir := filepath.Join(root, relDir) fis, err := os.ReadDir(dir) diff --git a/chore/metadump/main.go b/chore/metadump/main.go new file mode 100644 index 0000000000..c639336dfa --- /dev/null +++ b/chore/metadump/main.go @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2024 The XGo Authors (xgo.dev). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "flag" + "fmt" + "os" + + "github.com/goplus/llgo/internal/meta" +) + +func main() { + var out string + flag.StringVar(&out, "o", "", "write decoded metadata to file") + flag.Usage = func() { + fmt.Fprintf(flag.CommandLine.Output(), "Usage: metadump [-o output] file.meta\n") + flag.PrintDefaults() + } + flag.Parse() + + if flag.NArg() != 1 { + flag.Usage() + os.Exit(2) + } + + if err := run(flag.Arg(0), out); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func run(input, output string) error { + pm, err := meta.Open(input) + if err != nil { + return fmt.Errorf("read %s: %w", input, err) + } + defer pm.Close() + text := pm.String() + + if output == "" { + fmt.Print(text) + return nil + } + if err := os.WriteFile(output, []byte(text), 0o644); err != nil { + return fmt.Errorf("write %s: %w", output, err) + } + return nil +} diff --git a/cl/_testmeta/ifaceuse_basic/in.go b/cl/_testmeta/ifaceuse_basic/in.go new file mode 100644 index 0000000000..ab6ca52475 --- /dev/null +++ b/cl/_testmeta/ifaceuse_basic/in.go @@ -0,0 +1,11 @@ +package main + +type T struct{} + +func sink(v any) { + _ = v +} + +func main() { + sink(T{}) +} diff --git a/cl/_testmeta/ifaceuse_basic/meta-expect.txt b/cl/_testmeta/ifaceuse_basic/meta-expect.txt new file mode 100644 index 0000000000..434b667c9e --- /dev/null +++ b/cl/_testmeta/ifaceuse_basic/meta-expect.txt @@ -0,0 +1,26 @@ +[TypeChildren] +*_llgo_github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.T: + _llgo_github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.T + +[OrdinaryEdges] +*_llgo_github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.T: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.T +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0: + github.com/goplus/llgo/runtime/internal/runtime.memequal0 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: + github.com/goplus/llgo/runtime/internal/runtime.memequalptr +_llgo_github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.T: + *_llgo_github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.T + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 +github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.init: + github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.init$guard +github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.main: + _llgo_github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.T + github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.sink + github.com/goplus/llgo/runtime/internal/runtime.AllocU + +[UseIface] +github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.main: + _llgo_github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.T + diff --git a/cl/_testmeta/interface_anyonmous/in.go b/cl/_testmeta/interface_anyonmous/in.go new file mode 100644 index 0000000000..01c93e8514 --- /dev/null +++ b/cl/_testmeta/interface_anyonmous/in.go @@ -0,0 +1,18 @@ +package main + +type T struct{} + +func (T) M() {} +func (T) N() {} + +func use(v interface { + M() + N() +}) { + v.M() + v.N() +} + +func main() { + use(T{}) +} diff --git a/cl/_testmeta/interface_anyonmous/meta-expect.txt b/cl/_testmeta/interface_anyonmous/meta-expect.txt new file mode 100644 index 0000000000..e41f4b12d9 --- /dev/null +++ b/cl/_testmeta/interface_anyonmous/meta-expect.txt @@ -0,0 +1,84 @@ +[TypeChildren] +*_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T +*_llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo: + _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo + +[OrdinaryEdges] +*_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T +*_llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).M: + github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).M +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).N: + github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).N +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T.M: + github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T.M +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T.N: + github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T.N +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal: + github.com/goplus/llgo/runtime/internal/runtime.interequal +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0: + github.com/goplus/llgo/runtime/internal/runtime.memequal0 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: + github.com/goplus/llgo/runtime/internal/runtime.memequalptr +_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T: + *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 +_llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo: + *_llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo$imethods +_llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo$imethods: + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).M: + github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T.M + github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref + github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer +github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).N: + github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T.N + github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref + github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer +github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.init: + github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.init$guard +github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.main: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T + _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo + github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.use + github.com/goplus/llgo/runtime/internal/runtime.AllocU + github.com/goplus/llgo/runtime/internal/runtime.NewItab +github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.use: + github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData + +[UseIface] +github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.main: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T + +[UseIfaceMethod] +github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.use: + _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo N _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + +[MethodInfo] +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T: + 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).M + 1 N _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).N __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).N +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T: + 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T.M + 1 N _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).N __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T.N + +[InterfaceInfo] +_llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo: + M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + N _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + diff --git a/cl/_testmeta/interface_exported_var/in.go b/cl/_testmeta/interface_exported_var/in.go new file mode 100644 index 0000000000..b8ab613bac --- /dev/null +++ b/cl/_testmeta/interface_exported_var/in.go @@ -0,0 +1,8 @@ +package main + +import "encoding/binary" + +func main() { + var order binary.ByteOrder = binary.LittleEndian + _ = order.Uint16([]byte{1, 2}) +} diff --git a/cl/_testmeta/interface_exported_var/meta-expect.txt b/cl/_testmeta/interface_exported_var/meta-expect.txt new file mode 100644 index 0000000000..b0ee1eaf19 --- /dev/null +++ b/cl/_testmeta/interface_exported_var/meta-expect.txt @@ -0,0 +1,348 @@ +[TypeChildren] +*[]_llgo_uint8: + []_llgo_uint8 +*_llgo_encoding/binary.littleEndian: + _llgo_encoding/binary.littleEndian +*_llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs: + _llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs +*_llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc: + _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc +*_llgo_func$JXgl4jz35cOkksEkyj-ImVTqIDNL16JrZ25HHa-Yekw: + _llgo_func$JXgl4jz35cOkksEkyj-ImVTqIDNL16JrZ25HHa-Yekw +*_llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg: + _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg +*_llgo_func$_JswjMs_mFNKWtFb56TJlZa479nBYWhoAxYBwUTwOyc: + _llgo_func$_JswjMs_mFNKWtFb56TJlZa479nBYWhoAxYBwUTwOyc +*_llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus: + _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus +*_llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU: + _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU +*_llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8: + _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 +*_llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE: + _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE +*_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +*_llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw: + _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw +*_llgo_string: + _llgo_string +*_llgo_uint16: + _llgo_uint16 +*_llgo_uint32: + _llgo_uint32 +*_llgo_uint64: + _llgo_uint64 +*_llgo_uint8: + _llgo_uint8 +[]_llgo_uint8: + _llgo_uint8 +_llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs: + []_llgo_uint8 + _llgo_uint64 +_llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc: + []_llgo_uint8 + _llgo_uint32 +_llgo_func$JXgl4jz35cOkksEkyj-ImVTqIDNL16JrZ25HHa-Yekw: + []_llgo_uint8 + _llgo_uint16 +_llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg: + []_llgo_uint8 + _llgo_uint32 +_llgo_func$_JswjMs_mFNKWtFb56TJlZa479nBYWhoAxYBwUTwOyc: + []_llgo_uint8 + _llgo_uint32 +_llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus: + []_llgo_uint8 + _llgo_uint16 +_llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU: + []_llgo_uint8 + _llgo_uint16 +_llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8: + []_llgo_uint8 + _llgo_uint64 +_llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE: + []_llgo_uint8 + _llgo_uint64 +_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + _llgo_string + +[OrdinaryEdges] +*[]_llgo_uint8: + []_llgo_uint8 + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr +*_llgo_encoding/binary.littleEndian: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_encoding/binary.littleEndian +*_llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs +*_llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc +*_llgo_func$JXgl4jz35cOkksEkyj-ImVTqIDNL16JrZ25HHa-Yekw: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$JXgl4jz35cOkksEkyj-ImVTqIDNL16JrZ25HHa-Yekw +*_llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg +*_llgo_func$_JswjMs_mFNKWtFb56TJlZa479nBYWhoAxYBwUTwOyc: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$_JswjMs_mFNKWtFb56TJlZa479nBYWhoAxYBwUTwOyc +*_llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus +*_llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU +*_llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 +*_llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE +*_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +*_llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw +*_llgo_string: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_string +*_llgo_uint16: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_uint16 +*_llgo_uint32: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_uint32 +*_llgo_uint64: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_uint64 +*_llgo_uint8: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_uint8 +[]_llgo_uint8: + *[]_llgo_uint8 + _llgo_uint8 +__llgo_stub.encoding/binary.(*littleEndian).AppendUint16: + encoding/binary.(*littleEndian).AppendUint16 +__llgo_stub.encoding/binary.(*littleEndian).AppendUint32: + encoding/binary.(*littleEndian).AppendUint32 +__llgo_stub.encoding/binary.(*littleEndian).AppendUint64: + encoding/binary.(*littleEndian).AppendUint64 +__llgo_stub.encoding/binary.(*littleEndian).GoString: + encoding/binary.(*littleEndian).GoString +__llgo_stub.encoding/binary.(*littleEndian).PutUint16: + encoding/binary.(*littleEndian).PutUint16 +__llgo_stub.encoding/binary.(*littleEndian).PutUint32: + encoding/binary.(*littleEndian).PutUint32 +__llgo_stub.encoding/binary.(*littleEndian).PutUint64: + encoding/binary.(*littleEndian).PutUint64 +__llgo_stub.encoding/binary.(*littleEndian).String: + encoding/binary.(*littleEndian).String +__llgo_stub.encoding/binary.(*littleEndian).Uint16: + encoding/binary.(*littleEndian).Uint16 +__llgo_stub.encoding/binary.(*littleEndian).Uint32: + encoding/binary.(*littleEndian).Uint32 +__llgo_stub.encoding/binary.(*littleEndian).Uint64: + encoding/binary.(*littleEndian).Uint64 +__llgo_stub.encoding/binary.littleEndian.AppendUint16: + encoding/binary.littleEndian.AppendUint16 +__llgo_stub.encoding/binary.littleEndian.AppendUint32: + encoding/binary.littleEndian.AppendUint32 +__llgo_stub.encoding/binary.littleEndian.AppendUint64: + encoding/binary.littleEndian.AppendUint64 +__llgo_stub.encoding/binary.littleEndian.GoString: + encoding/binary.littleEndian.GoString +__llgo_stub.encoding/binary.littleEndian.PutUint16: + encoding/binary.littleEndian.PutUint16 +__llgo_stub.encoding/binary.littleEndian.PutUint32: + encoding/binary.littleEndian.PutUint32 +__llgo_stub.encoding/binary.littleEndian.PutUint64: + encoding/binary.littleEndian.PutUint64 +__llgo_stub.encoding/binary.littleEndian.String: + encoding/binary.littleEndian.String +__llgo_stub.encoding/binary.littleEndian.Uint16: + encoding/binary.littleEndian.Uint16 +__llgo_stub.encoding/binary.littleEndian.Uint32: + encoding/binary.littleEndian.Uint32 +__llgo_stub.encoding/binary.littleEndian.Uint64: + encoding/binary.littleEndian.Uint64 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal: + github.com/goplus/llgo/runtime/internal/runtime.interequal +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0: + github.com/goplus/llgo/runtime/internal/runtime.memequal0 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal16: + github.com/goplus/llgo/runtime/internal/runtime.memequal16 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal32: + github.com/goplus/llgo/runtime/internal/runtime.memequal32 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64: + github.com/goplus/llgo/runtime/internal/runtime.memequal64 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal8: + github.com/goplus/llgo/runtime/internal/runtime.memequal8 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: + github.com/goplus/llgo/runtime/internal/runtime.memequalptr +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal: + github.com/goplus/llgo/runtime/internal/runtime.strequal +_llgo_encoding/binary.littleEndian: + *_llgo_encoding/binary.littleEndian + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 +_llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs: + *_llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs + _llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs$in + _llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs$out +_llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs$in: + []_llgo_uint8 + _llgo_uint64 +_llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs$out: + []_llgo_uint8 +_llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc: + *_llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc + _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc$in + _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc$out +_llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc$in: + []_llgo_uint8 +_llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc$out: + _llgo_uint32 +_llgo_func$JXgl4jz35cOkksEkyj-ImVTqIDNL16JrZ25HHa-Yekw: + *_llgo_func$JXgl4jz35cOkksEkyj-ImVTqIDNL16JrZ25HHa-Yekw + _llgo_func$JXgl4jz35cOkksEkyj-ImVTqIDNL16JrZ25HHa-Yekw$in + _llgo_func$JXgl4jz35cOkksEkyj-ImVTqIDNL16JrZ25HHa-Yekw$out +_llgo_func$JXgl4jz35cOkksEkyj-ImVTqIDNL16JrZ25HHa-Yekw$in: + []_llgo_uint8 + _llgo_uint16 +_llgo_func$JXgl4jz35cOkksEkyj-ImVTqIDNL16JrZ25HHa-Yekw$out: + []_llgo_uint8 +_llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg: + *_llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg + _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg$in +_llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg$in: + []_llgo_uint8 + _llgo_uint32 +_llgo_func$_JswjMs_mFNKWtFb56TJlZa479nBYWhoAxYBwUTwOyc: + *_llgo_func$_JswjMs_mFNKWtFb56TJlZa479nBYWhoAxYBwUTwOyc + _llgo_func$_JswjMs_mFNKWtFb56TJlZa479nBYWhoAxYBwUTwOyc$in + _llgo_func$_JswjMs_mFNKWtFb56TJlZa479nBYWhoAxYBwUTwOyc$out +_llgo_func$_JswjMs_mFNKWtFb56TJlZa479nBYWhoAxYBwUTwOyc$in: + []_llgo_uint8 + _llgo_uint32 +_llgo_func$_JswjMs_mFNKWtFb56TJlZa479nBYWhoAxYBwUTwOyc$out: + []_llgo_uint8 +_llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus: + *_llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus + _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus$in + _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus$out +_llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus$in: + []_llgo_uint8 +_llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus$out: + _llgo_uint16 +_llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU: + *_llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU + _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU$in +_llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU$in: + []_llgo_uint8 + _llgo_uint16 +_llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8: + *_llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 + _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8$in +_llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8$in: + []_llgo_uint8 + _llgo_uint64 +_llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE: + *_llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE + _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE$in + _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE$out +_llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE$in: + []_llgo_uint8 +_llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE$out: + _llgo_uint64 +_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + *_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to$out +_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to$out: + _llgo_string +_llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw: + *_llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw$imethods +_llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw$imethods: + _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc + _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg + _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus + _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU + _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 + _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +_llgo_string: + *_llgo_string + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal +_llgo_uint16: + *_llgo_uint16 + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal16 +_llgo_uint32: + *_llgo_uint32 + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal32 +_llgo_uint64: + *_llgo_uint64 + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64 +_llgo_uint8: + *_llgo_uint8 + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal8 +github.com/goplus/llgo/cl/_testmeta/interface_exported_var.init: + encoding/binary.init + github.com/goplus/llgo/cl/_testmeta/interface_exported_var.init$guard +github.com/goplus/llgo/cl/_testmeta/interface_exported_var.main: + _llgo_encoding/binary.littleEndian + _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw + encoding/binary.LittleEndian + github.com/goplus/llgo/runtime/internal/runtime.AllocU + github.com/goplus/llgo/runtime/internal/runtime.AllocZ + github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData + github.com/goplus/llgo/runtime/internal/runtime.NewItab + github.com/goplus/llgo/runtime/internal/runtime.Typedmemmove + +[UseIface] +github.com/goplus/llgo/cl/_testmeta/interface_exported_var.main: + _llgo_encoding/binary.littleEndian + +[UseIfaceMethod] +github.com/goplus/llgo/cl/_testmeta/interface_exported_var.main: + _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw Uint16 _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus + +[MethodInfo] +*_llgo_encoding/binary.littleEndian: + 0 AppendUint16 _llgo_func$JXgl4jz35cOkksEkyj-ImVTqIDNL16JrZ25HHa-Yekw encoding/binary.(*littleEndian).AppendUint16 __llgo_stub.encoding/binary.(*littleEndian).AppendUint16 + 1 AppendUint32 _llgo_func$_JswjMs_mFNKWtFb56TJlZa479nBYWhoAxYBwUTwOyc encoding/binary.(*littleEndian).AppendUint32 __llgo_stub.encoding/binary.(*littleEndian).AppendUint32 + 2 AppendUint64 _llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs encoding/binary.(*littleEndian).AppendUint64 __llgo_stub.encoding/binary.(*littleEndian).AppendUint64 + 3 GoString _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to encoding/binary.(*littleEndian).GoString __llgo_stub.encoding/binary.(*littleEndian).GoString + 4 PutUint16 _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU encoding/binary.(*littleEndian).PutUint16 __llgo_stub.encoding/binary.(*littleEndian).PutUint16 + 5 PutUint32 _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg encoding/binary.(*littleEndian).PutUint32 __llgo_stub.encoding/binary.(*littleEndian).PutUint32 + 6 PutUint64 _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 encoding/binary.(*littleEndian).PutUint64 __llgo_stub.encoding/binary.(*littleEndian).PutUint64 + 7 String _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to encoding/binary.(*littleEndian).String __llgo_stub.encoding/binary.(*littleEndian).String + 8 Uint16 _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus encoding/binary.(*littleEndian).Uint16 __llgo_stub.encoding/binary.(*littleEndian).Uint16 + 9 Uint32 _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc encoding/binary.(*littleEndian).Uint32 __llgo_stub.encoding/binary.(*littleEndian).Uint32 + 10 Uint64 _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE encoding/binary.(*littleEndian).Uint64 __llgo_stub.encoding/binary.(*littleEndian).Uint64 +_llgo_encoding/binary.littleEndian: + 0 AppendUint16 _llgo_func$JXgl4jz35cOkksEkyj-ImVTqIDNL16JrZ25HHa-Yekw encoding/binary.(*littleEndian).AppendUint16 __llgo_stub.encoding/binary.littleEndian.AppendUint16 + 1 AppendUint32 _llgo_func$_JswjMs_mFNKWtFb56TJlZa479nBYWhoAxYBwUTwOyc encoding/binary.(*littleEndian).AppendUint32 __llgo_stub.encoding/binary.littleEndian.AppendUint32 + 2 AppendUint64 _llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs encoding/binary.(*littleEndian).AppendUint64 __llgo_stub.encoding/binary.littleEndian.AppendUint64 + 3 GoString _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to encoding/binary.(*littleEndian).GoString __llgo_stub.encoding/binary.littleEndian.GoString + 4 PutUint16 _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU encoding/binary.(*littleEndian).PutUint16 __llgo_stub.encoding/binary.littleEndian.PutUint16 + 5 PutUint32 _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg encoding/binary.(*littleEndian).PutUint32 __llgo_stub.encoding/binary.littleEndian.PutUint32 + 6 PutUint64 _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 encoding/binary.(*littleEndian).PutUint64 __llgo_stub.encoding/binary.littleEndian.PutUint64 + 7 String _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to encoding/binary.(*littleEndian).String __llgo_stub.encoding/binary.littleEndian.String + 8 Uint16 _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus encoding/binary.(*littleEndian).Uint16 __llgo_stub.encoding/binary.littleEndian.Uint16 + 9 Uint32 _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc encoding/binary.(*littleEndian).Uint32 __llgo_stub.encoding/binary.littleEndian.Uint32 + 10 Uint64 _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE encoding/binary.(*littleEndian).Uint64 __llgo_stub.encoding/binary.littleEndian.Uint64 + +[InterfaceInfo] +_llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw: + PutUint16 _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU + PutUint32 _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg + PutUint64 _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 + String _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to + Uint16 _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus + Uint32 _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc + Uint64 _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE + diff --git a/cl/_testmeta/interface_generic/in.go b/cl/_testmeta/interface_generic/in.go new file mode 100644 index 0000000000..ebf78edde5 --- /dev/null +++ b/cl/_testmeta/interface_generic/in.go @@ -0,0 +1,21 @@ +package main + +type Box[T any] struct { + value T +} + +func (b *Box[T]) Value() T { + return b.value +} + +type I[T any] interface { + Value() T +} + +func useInt(v I[int]) int { + return v.Value() +} + +func main() { + _ = useInt(&Box[int]{value: 42}) +} diff --git a/cl/_testmeta/interface_generic/meta-expect.txt b/cl/_testmeta/interface_generic/meta-expect.txt new file mode 100644 index 0000000000..fd61226e68 --- /dev/null +++ b/cl/_testmeta/interface_generic/meta-expect.txt @@ -0,0 +1,82 @@ +[TypeChildren] +*_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA: + _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic.Box[int]: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic.Box[int] +*_llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8: + _llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8 +*_llgo_int: + _llgo_int +_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA: + _llgo_int +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic.Box[int]: + _llgo_int + +[OrdinaryEdges] +*_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic.Box[int]: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic.Box[int] +*_llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8 +*_llgo_int: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_int +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_generic.(*Box[int]).Value: + github.com/goplus/llgo/cl/_testmeta/interface_generic.(*Box[int]).Value +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal: + github.com/goplus/llgo/runtime/internal/runtime.interequal +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64: + github.com/goplus/llgo/runtime/internal/runtime.memequal64 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: + github.com/goplus/llgo/runtime/internal/runtime.memequalptr +_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA: + *_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA$out +_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA$out: + _llgo_int +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic.Box[int]: + *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic.Box[int] + github.com/goplus/llgo/cl/_testmeta/interface_generic.struct$lOhriNu2BrWBR2Mh8k-KggMYlAw0Wx-2ftE2_gNyubg$fields + github.com/goplus/llgo/runtime/internal/runtime.structequal +_llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8: + *_llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8 + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + _llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8$imethods +_llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8$imethods: + _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA +_llgo_int: + *_llgo_int + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64 +github.com/goplus/llgo/cl/_testmeta/interface_generic.init: + github.com/goplus/llgo/cl/_testmeta/interface_generic.init$guard +github.com/goplus/llgo/cl/_testmeta/interface_generic.main: + *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic.Box[int] + _llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8 + github.com/goplus/llgo/cl/_testmeta/interface_generic.useInt + github.com/goplus/llgo/runtime/internal/runtime.AllocZ + github.com/goplus/llgo/runtime/internal/runtime.NewItab +github.com/goplus/llgo/cl/_testmeta/interface_generic.struct$lOhriNu2BrWBR2Mh8k-KggMYlAw0Wx-2ftE2_gNyubg$fields: + _llgo_int +github.com/goplus/llgo/cl/_testmeta/interface_generic.useInt: + github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData + +[UseIface] +github.com/goplus/llgo/cl/_testmeta/interface_generic.main: + *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic.Box[int] + +[UseIfaceMethod] +github.com/goplus/llgo/cl/_testmeta/interface_generic.useInt: + _llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8 Value _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + +[MethodInfo] +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic.Box[int]: + 0 Value _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA github.com/goplus/llgo/cl/_testmeta/interface_generic.(*Box[int]).Value __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_generic.(*Box[int]).Value + +[InterfaceInfo] +_llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8: + Value _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + diff --git a/cl/_testmeta/interface_generic_crosspkg/api/api.go b/cl/_testmeta/interface_generic_crosspkg/api/api.go new file mode 100644 index 0000000000..7e9c6a4ae1 --- /dev/null +++ b/cl/_testmeta/interface_generic_crosspkg/api/api.go @@ -0,0 +1,9 @@ +package api + +type I[T any] interface { + Value() T +} + +func UseInt(v I[int]) int { + return v.Value() +} diff --git a/cl/_testmeta/interface_generic_crosspkg/api/meta-expect.txt b/cl/_testmeta/interface_generic_crosspkg/api/meta-expect.txt new file mode 100644 index 0000000000..69a0b8ba7a --- /dev/null +++ b/cl/_testmeta/interface_generic_crosspkg/api/meta-expect.txt @@ -0,0 +1,14 @@ +[OrdinaryEdges] +github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/api.UseInt: + github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData +github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/api.init: + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/api.init$guard + +[UseIfaceMethod] +github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/api.UseInt: + _llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8 Value _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + +[InterfaceInfo] +_llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8: + Value _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + diff --git a/cl/_testmeta/interface_generic_crosspkg/in.go b/cl/_testmeta/interface_generic_crosspkg/in.go new file mode 100644 index 0000000000..bee45b9fbe --- /dev/null +++ b/cl/_testmeta/interface_generic_crosspkg/in.go @@ -0,0 +1,15 @@ +package main + +import ( + "github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/api" + "github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model" +) + +var sink any + +func main() { + n := api.UseInt(model.NewIntBox(40)) + text := model.NewStringBox("go") + sink = text + println(n + model.UseStringBox(text)) +} diff --git a/cl/_testmeta/interface_generic_crosspkg/meta-expect.txt b/cl/_testmeta/interface_generic_crosspkg/meta-expect.txt new file mode 100644 index 0000000000..20d6bae52f --- /dev/null +++ b/cl/_testmeta/interface_generic_crosspkg/meta-expect.txt @@ -0,0 +1,138 @@ +[TypeChildren] +*_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA: + _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA +*_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[int]: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[int] +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[string]: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[string] +*_llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8: + _llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8 +*_llgo_int: + _llgo_int +*_llgo_string: + _llgo_string +_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA: + _llgo_int +_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + _llgo_string +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[int]: + _llgo_int +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[string]: + _llgo_string + +[OrdinaryEdges] +*_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA +*_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[int]: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[int] +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[string]: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[string] +*_llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8 +*_llgo_int: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_int +*_llgo_string: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_string +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[int]).Drop: + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[int]).Drop +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[int]).Value: + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[int]).Value +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[string]).Drop: + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[string]).Drop +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[string]).Value: + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[string]).Value +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal: + github.com/goplus/llgo/runtime/internal/runtime.interequal +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64: + github.com/goplus/llgo/runtime/internal/runtime.memequal64 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: + github.com/goplus/llgo/runtime/internal/runtime.memequalptr +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal: + github.com/goplus/llgo/runtime/internal/runtime.strequal +_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA: + *_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA$out +_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA$out: + _llgo_int +_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + *_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to$out +_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to$out: + _llgo_string +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[int]: + *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[int] + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.struct$lOhriNu2BrWBR2Mh8k-KggMYlAw0Wx-2ftE2_gNyubg$fields + github.com/goplus/llgo/runtime/internal/runtime.structequal +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[string]: + *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[string] + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.struct$XNpHe-3-A25gBmHjYdF74H8M4k3Eik680huOzY6OtaU$fields + github.com/goplus/llgo/runtime/internal/runtime.structequal +_llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8: + *_llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8 + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + _llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8$imethods +_llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8$imethods: + _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA +_llgo_int: + *_llgo_int + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64 +_llgo_string: + *_llgo_string + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal +github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg.init: + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg.init$guard + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/api.init + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.init +github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg.main: + *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[int] + *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[string] + _llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8 + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg.sink + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/api.UseInt + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.NewIntBox + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.NewStringBox + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.UseStringBox + github.com/goplus/llgo/runtime/internal/runtime.NewItab + github.com/goplus/llgo/runtime/internal/runtime.PrintByte + github.com/goplus/llgo/runtime/internal/runtime.PrintInt +github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[int]).Drop: + _llgo_string + github.com/goplus/llgo/runtime/internal/runtime.AllocU + github.com/goplus/llgo/runtime/internal/runtime.Panic +github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[string]).Drop: + _llgo_string + github.com/goplus/llgo/runtime/internal/runtime.AllocU + github.com/goplus/llgo/runtime/internal/runtime.Panic +github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.struct$XNpHe-3-A25gBmHjYdF74H8M4k3Eik680huOzY6OtaU$fields: + _llgo_string +github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.struct$lOhriNu2BrWBR2Mh8k-KggMYlAw0Wx-2ftE2_gNyubg$fields: + _llgo_int + +[UseIface] +github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg.main: + *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[int] + *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[string] +github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[int]).Drop: + _llgo_string +github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[string]).Drop: + _llgo_string + +[MethodInfo] +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[int]: + 0 Drop _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[int]).Drop __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[int]).Drop + 1 Value _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[int]).Value __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[int]).Value +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[string]: + 0 Drop _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[string]).Drop __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[string]).Drop + 1 Value _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[string]).Value __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[string]).Value + diff --git a/cl/_testmeta/interface_generic_crosspkg/model/meta-expect.txt b/cl/_testmeta/interface_generic_crosspkg/model/meta-expect.txt new file mode 100644 index 0000000000..094b8c759e --- /dev/null +++ b/cl/_testmeta/interface_generic_crosspkg/model/meta-expect.txt @@ -0,0 +1,8 @@ +[OrdinaryEdges] +github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.NewIntBox: + github.com/goplus/llgo/runtime/internal/runtime.AllocZ +github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.NewStringBox: + github.com/goplus/llgo/runtime/internal/runtime.AllocZ +github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.init: + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.init$guard + diff --git a/cl/_testmeta/interface_generic_crosspkg/model/model.go b/cl/_testmeta/interface_generic_crosspkg/model/model.go new file mode 100644 index 0000000000..d9182b41f7 --- /dev/null +++ b/cl/_testmeta/interface_generic_crosspkg/model/model.go @@ -0,0 +1,27 @@ +package model + +type Box[T any] struct { + value T +} + +func NewIntBox(v int) *Box[int] { + return &Box[int]{value: v} +} + +func NewStringBox(v string) *Box[string] { + return &Box[string]{value: v} +} + +func UseStringBox(v *Box[string]) int { + return len(v.value) +} + +//go:noinline +func (b *Box[T]) Value() T { + return b.value +} + +//go:noinline +func (b *Box[T]) Drop() T { + panic("Box.Drop should be unreachable") +} diff --git a/cl/_testmeta/interface_imported/api/api.go b/cl/_testmeta/interface_imported/api/api.go new file mode 100644 index 0000000000..e6ce2af1bc --- /dev/null +++ b/cl/_testmeta/interface_imported/api/api.go @@ -0,0 +1,15 @@ +package api + +type Reader interface { + Read([]byte) (int, error) +} + +type Source struct{} + +func (Source) Read([]byte) (int, error) { + return 7, nil +} + +func (Source) Close() error { + return nil +} diff --git a/cl/_testmeta/interface_imported/api/meta-expect.txt b/cl/_testmeta/interface_imported/api/meta-expect.txt new file mode 100644 index 0000000000..e30a53db60 --- /dev/null +++ b/cl/_testmeta/interface_imported/api/meta-expect.txt @@ -0,0 +1,12 @@ +[OrdinaryEdges] +github.com/goplus/llgo/cl/_testmeta/interface_imported/api.(*Source).Close: + github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source.Close + github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref + github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer +github.com/goplus/llgo/cl/_testmeta/interface_imported/api.(*Source).Read: + github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source.Read + github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref + github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer +github.com/goplus/llgo/cl/_testmeta/interface_imported/api.init: + github.com/goplus/llgo/cl/_testmeta/interface_imported/api.init$guard + diff --git a/cl/_testmeta/interface_imported/in.go b/cl/_testmeta/interface_imported/in.go new file mode 100644 index 0000000000..d54efd2f07 --- /dev/null +++ b/cl/_testmeta/interface_imported/in.go @@ -0,0 +1,12 @@ +package main + +import "github.com/goplus/llgo/cl/_testmeta/interface_imported/api" + +func use(r api.Reader) int { + n, _ := r.Read(nil) + return n +} + +func main() { + _ = use(api.Source{}) +} diff --git a/cl/_testmeta/interface_imported/meta-expect.txt b/cl/_testmeta/interface_imported/meta-expect.txt new file mode 100644 index 0000000000..852e5cb45b --- /dev/null +++ b/cl/_testmeta/interface_imported/meta-expect.txt @@ -0,0 +1,161 @@ +[TypeChildren] +*[]_llgo_uint8: + []_llgo_uint8 +*_llgo_error: + _llgo_error +*_llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w: + _llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w +*_llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk: + _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk +*_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source +*_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: + _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw +*_llgo_int: + _llgo_int +*_llgo_string: + _llgo_string +*_llgo_uint8: + _llgo_uint8 +[]_llgo_uint8: + _llgo_uint8 +_llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w: + _llgo_error +_llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk: + []_llgo_uint8 + _llgo_error + _llgo_int +_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + _llgo_string + +[OrdinaryEdges] +*[]_llgo_uint8: + []_llgo_uint8 + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr +*_llgo_error: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_error +*_llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w +*_llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk +*_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source +*_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw +*_llgo_int: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_int +*_llgo_string: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_string +*_llgo_uint8: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_uint8 +[]_llgo_uint8: + *[]_llgo_uint8 + _llgo_uint8 +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_imported/api.(*Source).Close: + github.com/goplus/llgo/cl/_testmeta/interface_imported/api.(*Source).Close +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_imported/api.(*Source).Read: + github.com/goplus/llgo/cl/_testmeta/interface_imported/api.(*Source).Read +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source.Close: + github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source.Close +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source.Read: + github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source.Read +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal: + github.com/goplus/llgo/runtime/internal/runtime.interequal +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0: + github.com/goplus/llgo/runtime/internal/runtime.memequal0 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64: + github.com/goplus/llgo/runtime/internal/runtime.memequal64 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal8: + github.com/goplus/llgo/runtime/internal/runtime.memequal8 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: + github.com/goplus/llgo/runtime/internal/runtime.memequalptr +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal: + github.com/goplus/llgo/runtime/internal/runtime.strequal +_llgo_error: + *_llgo_error + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + _llgo_iface$Fh8eUJ-Gw4e6TYuajcFIOSCuqSPKAt5nS4ow7xeGXEU$imethods +_llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w: + *_llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w + _llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w$out +_llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w$out: + _llgo_error +_llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk: + *_llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk + _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk$in + _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk$out +_llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk$in: + []_llgo_uint8 +_llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk$out: + _llgo_error + _llgo_int +_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + *_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to$out +_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to$out: + _llgo_string +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source: + *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 +_llgo_iface$Fh8eUJ-Gw4e6TYuajcFIOSCuqSPKAt5nS4ow7xeGXEU$imethods: + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: + *_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw$imethods +_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw$imethods: + _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk +_llgo_int: + *_llgo_int + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64 +_llgo_string: + *_llgo_string + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal +_llgo_uint8: + *_llgo_uint8 + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal8 +github.com/goplus/llgo/cl/_testmeta/interface_imported.init: + github.com/goplus/llgo/cl/_testmeta/interface_imported.init$guard + github.com/goplus/llgo/cl/_testmeta/interface_imported/api.init +github.com/goplus/llgo/cl/_testmeta/interface_imported.main: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source + _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw + github.com/goplus/llgo/cl/_testmeta/interface_imported.use + github.com/goplus/llgo/runtime/internal/runtime.AllocU + github.com/goplus/llgo/runtime/internal/runtime.NewItab +github.com/goplus/llgo/cl/_testmeta/interface_imported.use: + github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData + +[UseIface] +github.com/goplus/llgo/cl/_testmeta/interface_imported.main: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source + +[UseIfaceMethod] +github.com/goplus/llgo/cl/_testmeta/interface_imported.use: + _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk + +[MethodInfo] +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source: + 0 Close _llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w github.com/goplus/llgo/cl/_testmeta/interface_imported/api.(*Source).Close __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_imported/api.(*Source).Close + 1 Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk github.com/goplus/llgo/cl/_testmeta/interface_imported/api.(*Source).Read __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_imported/api.(*Source).Read +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source: + 0 Close _llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w github.com/goplus/llgo/cl/_testmeta/interface_imported/api.(*Source).Close __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source.Close + 1 Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk github.com/goplus/llgo/cl/_testmeta/interface_imported/api.(*Source).Read __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source.Read + +[InterfaceInfo] +_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: + Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk + diff --git a/cl/_testmeta/interface_named/in.go b/cl/_testmeta/interface_named/in.go new file mode 100644 index 0000000000..dc91d644f0 --- /dev/null +++ b/cl/_testmeta/interface_named/in.go @@ -0,0 +1,17 @@ +package main + +type I interface { + M() +} + +type T struct{} + +func (T) M() {} + +func use(v I) { + v.M() +} + +func main() { + use(T{}) +} diff --git a/cl/_testmeta/interface_named/meta-expect.txt b/cl/_testmeta/interface_named/meta-expect.txt new file mode 100644 index 0000000000..8aa2b41b6c --- /dev/null +++ b/cl/_testmeta/interface_named/meta-expect.txt @@ -0,0 +1,72 @@ +[TypeChildren] +*_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T +*_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88: + _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88 + +[OrdinaryEdges] +*_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T +*_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88 +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M: + github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_named.T.M: + github.com/goplus/llgo/cl/_testmeta/interface_named.T.M +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal: + github.com/goplus/llgo/runtime/internal/runtime.interequal +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0: + github.com/goplus/llgo/runtime/internal/runtime.memequal0 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: + github.com/goplus/llgo/runtime/internal/runtime.memequalptr +_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: + *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 +_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88: + *_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88 + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88$imethods +_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88$imethods: + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M: + github.com/goplus/llgo/cl/_testmeta/interface_named.T.M + github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref + github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer +github.com/goplus/llgo/cl/_testmeta/interface_named.init: + github.com/goplus/llgo/cl/_testmeta/interface_named.init$guard +github.com/goplus/llgo/cl/_testmeta/interface_named.main: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T + _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88 + github.com/goplus/llgo/cl/_testmeta/interface_named.use + github.com/goplus/llgo/runtime/internal/runtime.AllocU + github.com/goplus/llgo/runtime/internal/runtime.NewItab +github.com/goplus/llgo/cl/_testmeta/interface_named.use: + github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData + +[UseIface] +github.com/goplus/llgo/cl/_testmeta/interface_named.main: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T + +[UseIfaceMethod] +github.com/goplus/llgo/cl/_testmeta/interface_named.use: + _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + +[MethodInfo] +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: + 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: + 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_named.T.M + +[InterfaceInfo] +_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88: + M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + diff --git a/cl/_testmeta/interface_unexported/in.go b/cl/_testmeta/interface_unexported/in.go new file mode 100644 index 0000000000..18778f7891 --- /dev/null +++ b/cl/_testmeta/interface_unexported/in.go @@ -0,0 +1,17 @@ +package main + +type I interface { + m() +} + +type T struct{} + +func (T) m() {} + +func use(v I) { + v.m() +} + +func main() { + use(T{}) +} diff --git a/cl/_testmeta/interface_unexported/meta-expect.txt b/cl/_testmeta/interface_unexported/meta-expect.txt new file mode 100644 index 0000000000..becdcd1ece --- /dev/null +++ b/cl/_testmeta/interface_unexported/meta-expect.txt @@ -0,0 +1,72 @@ +[TypeChildren] +*_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T +*github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo: + github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo + +[OrdinaryEdges] +*_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T +*github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m: + github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_unexported.T.m: + github.com/goplus/llgo/cl/_testmeta/interface_unexported.T.m +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal: + github.com/goplus/llgo/runtime/internal/runtime.interequal +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0: + github.com/goplus/llgo/runtime/internal/runtime.memequal0 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: + github.com/goplus/llgo/runtime/internal/runtime.memequalptr +_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: + *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 +github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m: + github.com/goplus/llgo/cl/_testmeta/interface_unexported.T.m + github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref + github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer +github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo: + *github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo$imethods +github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo$imethods: + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +github.com/goplus/llgo/cl/_testmeta/interface_unexported.init: + github.com/goplus/llgo/cl/_testmeta/interface_unexported.init$guard +github.com/goplus/llgo/cl/_testmeta/interface_unexported.main: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T + github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo + github.com/goplus/llgo/cl/_testmeta/interface_unexported.use + github.com/goplus/llgo/runtime/internal/runtime.AllocU + github.com/goplus/llgo/runtime/internal/runtime.NewItab +github.com/goplus/llgo/cl/_testmeta/interface_unexported.use: + github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData + +[UseIface] +github.com/goplus/llgo/cl/_testmeta/interface_unexported.main: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T + +[UseIfaceMethod] +github.com/goplus/llgo/cl/_testmeta/interface_unexported.use: + github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + +[MethodInfo] +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: + 0 github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: + 0 github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_unexported.T.m + +[InterfaceInfo] +github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo: + github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + diff --git a/cl/_testmeta/methodinfo_imported/in.go b/cl/_testmeta/methodinfo_imported/in.go new file mode 100644 index 0000000000..b243bd18fb --- /dev/null +++ b/cl/_testmeta/methodinfo_imported/in.go @@ -0,0 +1,11 @@ +package main + +import ( + "bytes" + "io" +) + +func main() { + var r io.Reader = bytes.NewBuffer(nil) + _, _ = r.Read(nil) +} diff --git a/cl/_testmeta/methodinfo_imported/meta-expect.txt b/cl/_testmeta/methodinfo_imported/meta-expect.txt new file mode 100644 index 0000000000..456092f18f --- /dev/null +++ b/cl/_testmeta/methodinfo_imported/meta-expect.txt @@ -0,0 +1,545 @@ +[TypeChildren] +*[]_llgo_uint8: + []_llgo_uint8 +*_llgo_bool: + _llgo_bool +*_llgo_bytes.Buffer: + _llgo_bytes.Buffer +*_llgo_bytes.readOp: + _llgo_bytes.readOp +*_llgo_error: + _llgo_error +*_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +*_llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w: + _llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w +*_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA: + _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA +*_llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk: + _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk +*_llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o: + _llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o +*_llgo_func$VZ-8VPNF1RaLICwxc1Ghn7BbgyFX3v762OCdx127EkA: + _llgo_func$VZ-8VPNF1RaLICwxc1Ghn7BbgyFX3v762OCdx127EkA +*_llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk: + _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk +*_llgo_func$Z_-7GWzB37LCYRTQLsSYmEihg_hqBK8o_GbT88pqnPY: + _llgo_func$Z_-7GWzB37LCYRTQLsSYmEihg_hqBK8o_GbT88pqnPY +*_llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0: + _llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0 +*_llgo_func$d4kMA_oCkLwnd1j8nVlv1hwRarEVuCIrDCpnHhDz9UY: + _llgo_func$d4kMA_oCkLwnd1j8nVlv1hwRarEVuCIrDCpnHhDz9UY +*_llgo_func$ekGNsrYBSzltfAjxbl6T8H6Yq8j16wzqS3nDj2xxGMU: + _llgo_func$ekGNsrYBSzltfAjxbl6T8H6Yq8j16wzqS3nDj2xxGMU +*_llgo_func$lukqSsfDYBoIp_R8GMojGkZnrYDqaq2iHn8RkCjW7iQ: + _llgo_func$lukqSsfDYBoIp_R8GMojGkZnrYDqaq2iHn8RkCjW7iQ +*_llgo_func$q-bw-_pPYBCXnr1TXIF8sOD4fVVzzIlpHqD-A13AB4Y: + _llgo_func$q-bw-_pPYBCXnr1TXIF8sOD4fVVzzIlpHqD-A13AB4Y +*_llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M: + _llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M +*_llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw: + _llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw +*_llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8: + _llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8 +*_llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw: + _llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw +*_llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M: + _llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M +*_llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg: + _llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg +*_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +*_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: + _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw +*_llgo_int: + _llgo_int +*_llgo_int32: + _llgo_int32 +*_llgo_int64: + _llgo_int64 +*_llgo_io.Reader: + _llgo_io.Reader +*_llgo_io.Writer: + _llgo_io.Writer +*_llgo_string: + _llgo_string +*_llgo_uint8: + _llgo_uint8 +[]_llgo_uint8: + _llgo_uint8 +_llgo_bytes.Buffer: + []_llgo_uint8 + _llgo_bytes.readOp + _llgo_int +_llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w: + _llgo_error +_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA: + _llgo_int +_llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk: + []_llgo_uint8 + _llgo_error + _llgo_int +_llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o: + _llgo_error + _llgo_string + _llgo_uint8 +_llgo_func$VZ-8VPNF1RaLICwxc1Ghn7BbgyFX3v762OCdx127EkA: + _llgo_int +_llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk: + _llgo_bool +_llgo_func$Z_-7GWzB37LCYRTQLsSYmEihg_hqBK8o_GbT88pqnPY: + []_llgo_uint8 +_llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0: + []_llgo_uint8 + _llgo_error + _llgo_uint8 +_llgo_func$d4kMA_oCkLwnd1j8nVlv1hwRarEVuCIrDCpnHhDz9UY: + []_llgo_uint8 + _llgo_int +_llgo_func$ekGNsrYBSzltfAjxbl6T8H6Yq8j16wzqS3nDj2xxGMU: + _llgo_int +_llgo_func$lukqSsfDYBoIp_R8GMojGkZnrYDqaq2iHn8RkCjW7iQ: + _llgo_error + _llgo_uint8 +_llgo_func$q-bw-_pPYBCXnr1TXIF8sOD4fVVzzIlpHqD-A13AB4Y: + _llgo_error + _llgo_int + _llgo_int32 +_llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M: + _llgo_bool + _llgo_int +_llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw: + _llgo_error + _llgo_int + _llgo_string +_llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8: + _llgo_error + _llgo_int64 + _llgo_io.Reader +_llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw: + _llgo_error + _llgo_int + _llgo_int32 +_llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M: + _llgo_error + _llgo_int64 + _llgo_io.Writer +_llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg: + _llgo_error + _llgo_uint8 +_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + _llgo_string + +[OrdinaryEdges] +*[]_llgo_uint8: + []_llgo_uint8 + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr +*_llgo_bool: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_bool +*_llgo_bytes.Buffer: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_bytes.Buffer +*_llgo_bytes.readOp: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_bytes.readOp +*_llgo_error: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_error +*_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +*_llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w +*_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA +*_llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk +*_llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o +*_llgo_func$VZ-8VPNF1RaLICwxc1Ghn7BbgyFX3v762OCdx127EkA: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$VZ-8VPNF1RaLICwxc1Ghn7BbgyFX3v762OCdx127EkA +*_llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk +*_llgo_func$Z_-7GWzB37LCYRTQLsSYmEihg_hqBK8o_GbT88pqnPY: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$Z_-7GWzB37LCYRTQLsSYmEihg_hqBK8o_GbT88pqnPY +*_llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0 +*_llgo_func$d4kMA_oCkLwnd1j8nVlv1hwRarEVuCIrDCpnHhDz9UY: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$d4kMA_oCkLwnd1j8nVlv1hwRarEVuCIrDCpnHhDz9UY +*_llgo_func$ekGNsrYBSzltfAjxbl6T8H6Yq8j16wzqS3nDj2xxGMU: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$ekGNsrYBSzltfAjxbl6T8H6Yq8j16wzqS3nDj2xxGMU +*_llgo_func$lukqSsfDYBoIp_R8GMojGkZnrYDqaq2iHn8RkCjW7iQ: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$lukqSsfDYBoIp_R8GMojGkZnrYDqaq2iHn8RkCjW7iQ +*_llgo_func$q-bw-_pPYBCXnr1TXIF8sOD4fVVzzIlpHqD-A13AB4Y: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$q-bw-_pPYBCXnr1TXIF8sOD4fVVzzIlpHqD-A13AB4Y +*_llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M +*_llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw +*_llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8 +*_llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw +*_llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M +*_llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg +*_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +*_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw +*_llgo_int: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_int +*_llgo_int32: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_int32 +*_llgo_int64: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_int64 +*_llgo_io.Reader: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_io.Reader +*_llgo_io.Writer: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_io.Writer +*_llgo_string: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_string +*_llgo_uint8: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_uint8 +[]_llgo_uint8: + *[]_llgo_uint8 + _llgo_uint8 +__llgo_stub.bytes.(*Buffer).Available: + bytes.(*Buffer).Available +__llgo_stub.bytes.(*Buffer).AvailableBuffer: + bytes.(*Buffer).AvailableBuffer +__llgo_stub.bytes.(*Buffer).Bytes: + bytes.(*Buffer).Bytes +__llgo_stub.bytes.(*Buffer).Cap: + bytes.(*Buffer).Cap +__llgo_stub.bytes.(*Buffer).Grow: + bytes.(*Buffer).Grow +__llgo_stub.bytes.(*Buffer).Len: + bytes.(*Buffer).Len +__llgo_stub.bytes.(*Buffer).Next: + bytes.(*Buffer).Next +__llgo_stub.bytes.(*Buffer).Read: + bytes.(*Buffer).Read +__llgo_stub.bytes.(*Buffer).ReadByte: + bytes.(*Buffer).ReadByte +__llgo_stub.bytes.(*Buffer).ReadBytes: + bytes.(*Buffer).ReadBytes +__llgo_stub.bytes.(*Buffer).ReadFrom: + bytes.(*Buffer).ReadFrom +__llgo_stub.bytes.(*Buffer).ReadRune: + bytes.(*Buffer).ReadRune +__llgo_stub.bytes.(*Buffer).ReadString: + bytes.(*Buffer).ReadString +__llgo_stub.bytes.(*Buffer).Reset: + bytes.(*Buffer).Reset +__llgo_stub.bytes.(*Buffer).String: + bytes.(*Buffer).String +__llgo_stub.bytes.(*Buffer).Truncate: + bytes.(*Buffer).Truncate +__llgo_stub.bytes.(*Buffer).UnreadByte: + bytes.(*Buffer).UnreadByte +__llgo_stub.bytes.(*Buffer).UnreadRune: + bytes.(*Buffer).UnreadRune +__llgo_stub.bytes.(*Buffer).Write: + bytes.(*Buffer).Write +__llgo_stub.bytes.(*Buffer).WriteByte: + bytes.(*Buffer).WriteByte +__llgo_stub.bytes.(*Buffer).WriteRune: + bytes.(*Buffer).WriteRune +__llgo_stub.bytes.(*Buffer).WriteString: + bytes.(*Buffer).WriteString +__llgo_stub.bytes.(*Buffer).WriteTo: + bytes.(*Buffer).WriteTo +__llgo_stub.bytes.(*Buffer).empty: + bytes.(*Buffer).empty +__llgo_stub.bytes.(*Buffer).grow: + bytes.(*Buffer).grow +__llgo_stub.bytes.(*Buffer).readSlice: + bytes.(*Buffer).readSlice +__llgo_stub.bytes.(*Buffer).tryGrowByReslice: + bytes.(*Buffer).tryGrowByReslice +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal: + github.com/goplus/llgo/runtime/internal/runtime.interequal +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal32: + github.com/goplus/llgo/runtime/internal/runtime.memequal32 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64: + github.com/goplus/llgo/runtime/internal/runtime.memequal64 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal8: + github.com/goplus/llgo/runtime/internal/runtime.memequal8 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: + github.com/goplus/llgo/runtime/internal/runtime.memequalptr +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal: + github.com/goplus/llgo/runtime/internal/runtime.strequal +_llgo_bool: + *_llgo_bool + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal8 +_llgo_bytes.Buffer: + *_llgo_bytes.Buffer + bytes.struct$8M6lRFZ7Fk2XCr2laNI9Y7uQtk2A8VDBrezMuq2Fkuo$fields +_llgo_bytes.readOp: + *_llgo_bytes.readOp + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal8 +_llgo_error: + *_llgo_error + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + _llgo_iface$Fh8eUJ-Gw4e6TYuajcFIOSCuqSPKAt5nS4ow7xeGXEU$imethods +_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +_llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w: + *_llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w + _llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w$out +_llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w$out: + _llgo_error +_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA: + *_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA$out +_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA$out: + _llgo_int +_llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk: + *_llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk + _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk$in + _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk$out +_llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk$in: + []_llgo_uint8 +_llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk$out: + _llgo_error + _llgo_int +_llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o: + *_llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o + _llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o$in + _llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o$out +_llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o$in: + _llgo_uint8 +_llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o$out: + _llgo_error + _llgo_string +_llgo_func$VZ-8VPNF1RaLICwxc1Ghn7BbgyFX3v762OCdx127EkA: + *_llgo_func$VZ-8VPNF1RaLICwxc1Ghn7BbgyFX3v762OCdx127EkA + _llgo_func$VZ-8VPNF1RaLICwxc1Ghn7BbgyFX3v762OCdx127EkA$in +_llgo_func$VZ-8VPNF1RaLICwxc1Ghn7BbgyFX3v762OCdx127EkA$in: + _llgo_int +_llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk: + *_llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk + _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk$out +_llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk$out: + _llgo_bool +_llgo_func$Z_-7GWzB37LCYRTQLsSYmEihg_hqBK8o_GbT88pqnPY: + *_llgo_func$Z_-7GWzB37LCYRTQLsSYmEihg_hqBK8o_GbT88pqnPY + _llgo_func$Z_-7GWzB37LCYRTQLsSYmEihg_hqBK8o_GbT88pqnPY$out +_llgo_func$Z_-7GWzB37LCYRTQLsSYmEihg_hqBK8o_GbT88pqnPY$out: + []_llgo_uint8 +_llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0: + *_llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0 + _llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0$in + _llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0$out +_llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0$in: + _llgo_uint8 +_llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0$out: + []_llgo_uint8 + _llgo_error +_llgo_func$d4kMA_oCkLwnd1j8nVlv1hwRarEVuCIrDCpnHhDz9UY: + *_llgo_func$d4kMA_oCkLwnd1j8nVlv1hwRarEVuCIrDCpnHhDz9UY + _llgo_func$d4kMA_oCkLwnd1j8nVlv1hwRarEVuCIrDCpnHhDz9UY$in + _llgo_func$d4kMA_oCkLwnd1j8nVlv1hwRarEVuCIrDCpnHhDz9UY$out +_llgo_func$d4kMA_oCkLwnd1j8nVlv1hwRarEVuCIrDCpnHhDz9UY$in: + _llgo_int +_llgo_func$d4kMA_oCkLwnd1j8nVlv1hwRarEVuCIrDCpnHhDz9UY$out: + []_llgo_uint8 +_llgo_func$ekGNsrYBSzltfAjxbl6T8H6Yq8j16wzqS3nDj2xxGMU: + *_llgo_func$ekGNsrYBSzltfAjxbl6T8H6Yq8j16wzqS3nDj2xxGMU + _llgo_func$ekGNsrYBSzltfAjxbl6T8H6Yq8j16wzqS3nDj2xxGMU$in + _llgo_func$ekGNsrYBSzltfAjxbl6T8H6Yq8j16wzqS3nDj2xxGMU$out +_llgo_func$ekGNsrYBSzltfAjxbl6T8H6Yq8j16wzqS3nDj2xxGMU$in: + _llgo_int +_llgo_func$ekGNsrYBSzltfAjxbl6T8H6Yq8j16wzqS3nDj2xxGMU$out: + _llgo_int +_llgo_func$lukqSsfDYBoIp_R8GMojGkZnrYDqaq2iHn8RkCjW7iQ: + *_llgo_func$lukqSsfDYBoIp_R8GMojGkZnrYDqaq2iHn8RkCjW7iQ + _llgo_func$lukqSsfDYBoIp_R8GMojGkZnrYDqaq2iHn8RkCjW7iQ$out +_llgo_func$lukqSsfDYBoIp_R8GMojGkZnrYDqaq2iHn8RkCjW7iQ$out: + _llgo_error + _llgo_uint8 +_llgo_func$q-bw-_pPYBCXnr1TXIF8sOD4fVVzzIlpHqD-A13AB4Y: + *_llgo_func$q-bw-_pPYBCXnr1TXIF8sOD4fVVzzIlpHqD-A13AB4Y + _llgo_func$q-bw-_pPYBCXnr1TXIF8sOD4fVVzzIlpHqD-A13AB4Y$out +_llgo_func$q-bw-_pPYBCXnr1TXIF8sOD4fVVzzIlpHqD-A13AB4Y$out: + _llgo_error + _llgo_int + _llgo_int32 +_llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M: + *_llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M + _llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M$in + _llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M$out +_llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M$in: + _llgo_int +_llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M$out: + _llgo_bool + _llgo_int +_llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw: + *_llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw + _llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw$in + _llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw$out +_llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw$in: + _llgo_string +_llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw$out: + _llgo_error + _llgo_int +_llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8: + *_llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8 + _llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8$in + _llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8$out +_llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8$in: + _llgo_io.Reader +_llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8$out: + _llgo_error + _llgo_int64 +_llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw: + *_llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw + _llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw$in + _llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw$out +_llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw$in: + _llgo_int32 +_llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw$out: + _llgo_error + _llgo_int +_llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M: + *_llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M + _llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M$in + _llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M$out +_llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M$in: + _llgo_io.Writer +_llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M$out: + _llgo_error + _llgo_int64 +_llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg: + *_llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg + _llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg$in + _llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg$out +_llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg$in: + _llgo_uint8 +_llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg$out: + _llgo_error +_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + *_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to$out +_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to$out: + _llgo_string +_llgo_iface$Fh8eUJ-Gw4e6TYuajcFIOSCuqSPKAt5nS4ow7xeGXEU$imethods: + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +_llgo_iface$kr1iSWwMezh0B9LdQN0MhEZUNZvBlHPhlst95jAyxE0$imethods: + _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk +_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: + *_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw$imethods +_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw$imethods: + _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk +_llgo_int: + *_llgo_int + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64 +_llgo_int32: + *_llgo_int32 + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal32 +_llgo_int64: + *_llgo_int64 + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64 +_llgo_io.Reader: + *_llgo_io.Reader + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw$imethods +_llgo_io.Writer: + *_llgo_io.Writer + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + _llgo_iface$kr1iSWwMezh0B9LdQN0MhEZUNZvBlHPhlst95jAyxE0$imethods +_llgo_string: + *_llgo_string + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal +_llgo_uint8: + *_llgo_uint8 + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal8 +bytes.struct$8M6lRFZ7Fk2XCr2laNI9Y7uQtk2A8VDBrezMuq2Fkuo$fields: + []_llgo_uint8 + _llgo_bytes.readOp + _llgo_int +github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.init: + bytes.init + github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.init$guard + io.init +github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.main: + *_llgo_bytes.Buffer + _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw + bytes.NewBuffer + github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData + github.com/goplus/llgo/runtime/internal/runtime.NewItab + +[UseIface] +github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.main: + *_llgo_bytes.Buffer + +[UseIfaceMethod] +github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.main: + _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk + +[MethodInfo] +*_llgo_bytes.Buffer: + 0 Available _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA bytes.(*Buffer).Available __llgo_stub.bytes.(*Buffer).Available + 1 AvailableBuffer _llgo_func$Z_-7GWzB37LCYRTQLsSYmEihg_hqBK8o_GbT88pqnPY bytes.(*Buffer).AvailableBuffer __llgo_stub.bytes.(*Buffer).AvailableBuffer + 2 Bytes _llgo_func$Z_-7GWzB37LCYRTQLsSYmEihg_hqBK8o_GbT88pqnPY bytes.(*Buffer).Bytes __llgo_stub.bytes.(*Buffer).Bytes + 3 Cap _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA bytes.(*Buffer).Cap __llgo_stub.bytes.(*Buffer).Cap + 4 Grow _llgo_func$VZ-8VPNF1RaLICwxc1Ghn7BbgyFX3v762OCdx127EkA bytes.(*Buffer).Grow __llgo_stub.bytes.(*Buffer).Grow + 5 Len _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA bytes.(*Buffer).Len __llgo_stub.bytes.(*Buffer).Len + 6 Next _llgo_func$d4kMA_oCkLwnd1j8nVlv1hwRarEVuCIrDCpnHhDz9UY bytes.(*Buffer).Next __llgo_stub.bytes.(*Buffer).Next + 7 Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk bytes.(*Buffer).Read __llgo_stub.bytes.(*Buffer).Read + 8 ReadByte _llgo_func$lukqSsfDYBoIp_R8GMojGkZnrYDqaq2iHn8RkCjW7iQ bytes.(*Buffer).ReadByte __llgo_stub.bytes.(*Buffer).ReadByte + 9 ReadBytes _llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0 bytes.(*Buffer).ReadBytes __llgo_stub.bytes.(*Buffer).ReadBytes + 10 ReadFrom _llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8 bytes.(*Buffer).ReadFrom __llgo_stub.bytes.(*Buffer).ReadFrom + 11 ReadRune _llgo_func$q-bw-_pPYBCXnr1TXIF8sOD4fVVzzIlpHqD-A13AB4Y bytes.(*Buffer).ReadRune __llgo_stub.bytes.(*Buffer).ReadRune + 12 ReadString _llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o bytes.(*Buffer).ReadString __llgo_stub.bytes.(*Buffer).ReadString + 13 Reset _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac bytes.(*Buffer).Reset __llgo_stub.bytes.(*Buffer).Reset + 14 String _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to bytes.(*Buffer).String __llgo_stub.bytes.(*Buffer).String + 15 Truncate _llgo_func$VZ-8VPNF1RaLICwxc1Ghn7BbgyFX3v762OCdx127EkA bytes.(*Buffer).Truncate __llgo_stub.bytes.(*Buffer).Truncate + 16 UnreadByte _llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w bytes.(*Buffer).UnreadByte __llgo_stub.bytes.(*Buffer).UnreadByte + 17 UnreadRune _llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w bytes.(*Buffer).UnreadRune __llgo_stub.bytes.(*Buffer).UnreadRune + 18 Write _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk bytes.(*Buffer).Write __llgo_stub.bytes.(*Buffer).Write + 19 WriteByte _llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg bytes.(*Buffer).WriteByte __llgo_stub.bytes.(*Buffer).WriteByte + 20 WriteRune _llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw bytes.(*Buffer).WriteRune __llgo_stub.bytes.(*Buffer).WriteRune + 21 WriteString _llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw bytes.(*Buffer).WriteString __llgo_stub.bytes.(*Buffer).WriteString + 22 WriteTo _llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M bytes.(*Buffer).WriteTo __llgo_stub.bytes.(*Buffer).WriteTo + 23 bytes.empty _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk bytes.(*Buffer).empty __llgo_stub.bytes.(*Buffer).empty + 24 bytes.grow _llgo_func$ekGNsrYBSzltfAjxbl6T8H6Yq8j16wzqS3nDj2xxGMU bytes.(*Buffer).grow __llgo_stub.bytes.(*Buffer).grow + 25 bytes.readSlice _llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0 bytes.(*Buffer).readSlice __llgo_stub.bytes.(*Buffer).readSlice + 26 bytes.tryGrowByReslice _llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M bytes.(*Buffer).tryGrowByReslice __llgo_stub.bytes.(*Buffer).tryGrowByReslice + +[InterfaceInfo] +_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: + Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk + diff --git a/cl/_testmeta/reflect_dynamic/in.go b/cl/_testmeta/reflect_dynamic/in.go new file mode 100644 index 0000000000..15ed1f1a5f --- /dev/null +++ b/cl/_testmeta/reflect_dynamic/in.go @@ -0,0 +1,15 @@ +package main + +import "reflect" + +type T struct{} + +func (T) M() {} + +func use(name string) { + _ = reflect.ValueOf(T{}).MethodByName(name) +} + +func main() { + use("M") +} diff --git a/cl/_testmeta/reflect_dynamic/meta-expect.txt b/cl/_testmeta/reflect_dynamic/meta-expect.txt new file mode 100644 index 0000000000..7b3a5180a6 --- /dev/null +++ b/cl/_testmeta/reflect_dynamic/meta-expect.txt @@ -0,0 +1,54 @@ +[TypeChildren] +*_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +*_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T: + _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T + +[OrdinaryEdges] +*_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +*_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.(*T).M: + github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.(*T).M +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T.M: + github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T.M +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0: + github.com/goplus/llgo/runtime/internal/runtime.memequal0 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: + github.com/goplus/llgo/runtime/internal/runtime.memequalptr +_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T: + *_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 +github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.(*T).M: + github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T.M + github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref + github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer +github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.init: + github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.init$guard + reflect.init +github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.main: + github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.use +github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.use: + _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T + github.com/goplus/llgo/runtime/internal/runtime.AllocU + reflect.Value.MethodByName + reflect.ValueOf + +[UseIface] +github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.use: + _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T + +[MethodInfo] +*_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T: + 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.(*T).M +_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T: + 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T.M + +[Reflect] + github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.use + diff --git a/cl/_testmeta/reflect_named/in.go b/cl/_testmeta/reflect_named/in.go new file mode 100644 index 0000000000..28c989074d --- /dev/null +++ b/cl/_testmeta/reflect_named/in.go @@ -0,0 +1,13 @@ +package main + +import "reflect" + +type T struct{} + +func (T) M() {} +func (T) m() {} + +func main() { + _, _ = reflect.TypeOf(T{}).MethodByName("M") + _, _ = reflect.TypeOf(T{}).MethodByName("m") +} diff --git a/cl/_testmeta/reflect_named/meta-expect.txt b/cl/_testmeta/reflect_named/meta-expect.txt new file mode 100644 index 0000000000..9633334568 --- /dev/null +++ b/cl/_testmeta/reflect_named/meta-expect.txt @@ -0,0 +1,110 @@ +[TypeChildren] +*_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +*_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T: + _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T + +[OrdinaryEdges] +*_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +*_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).M: + github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).M +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).m: + github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).m +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_named.T.M: + github.com/goplus/llgo/cl/_testmeta/reflect_named.T.M +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_named.T.m: + github.com/goplus/llgo/cl/_testmeta/reflect_named.T.m +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0: + github.com/goplus/llgo/runtime/internal/runtime.memequal0 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: + github.com/goplus/llgo/runtime/internal/runtime.memequalptr +_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T: + *_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 +github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).M: + github.com/goplus/llgo/cl/_testmeta/reflect_named.T.M + github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref + github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer +github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).m: + github.com/goplus/llgo/cl/_testmeta/reflect_named.T.m + github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref + github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer +github.com/goplus/llgo/cl/_testmeta/reflect_named.init: + github.com/goplus/llgo/cl/_testmeta/reflect_named.init$guard + reflect.init +github.com/goplus/llgo/cl/_testmeta/reflect_named.main: + _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T + github.com/goplus/llgo/runtime/internal/runtime.AllocU + github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData + reflect.TypeOf + +[UseIface] +github.com/goplus/llgo/cl/_testmeta/reflect_named.main: + _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T + _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T + +[UseIfaceMethod] +github.com/goplus/llgo/cl/_testmeta/reflect_named.main: + github.com/goplus/llgo/runtime/internal/lib/reflect.iface$iKaf3qt2OaMUwQszh7IENnt26Bv3ufKgLDSVmXKN7gI MethodByName _llgo_func$aM2cVUtLQbPq1YHtnabQiM7XJ5Cg5RyV6BIDWrqey7E + github.com/goplus/llgo/runtime/internal/lib/reflect.iface$iKaf3qt2OaMUwQszh7IENnt26Bv3ufKgLDSVmXKN7gI MethodByName _llgo_func$aM2cVUtLQbPq1YHtnabQiM7XJ5Cg5RyV6BIDWrqey7E + +[UseNamedMethod] +github.com/goplus/llgo/cl/_testmeta/reflect_named.main: + M + m + +[MethodInfo] +*_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T: + 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).M + 1 github.com/goplus/llgo/cl/_testmeta/reflect_named.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).m __llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).m +_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T: + 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_named.T.M + 1 github.com/goplus/llgo/cl/_testmeta/reflect_named.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).m __llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_named.T.m + +[InterfaceInfo] +github.com/goplus/llgo/runtime/internal/lib/reflect.iface$iKaf3qt2OaMUwQszh7IENnt26Bv3ufKgLDSVmXKN7gI: + Align _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + AssignableTo _llgo_func$Kxk9fspGkjXcoNWf2ucHG1vOQ5VHxVtYionfm-DnvWE + Bits _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + CanSeq _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk + CanSeq2 _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk + ChanDir _llgo_func$JO3khPIbANSMBmoN6P7ybYAeUBd3Gv6toVUqNeE7qbE + Comparable _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk + ConvertibleTo _llgo_func$Kxk9fspGkjXcoNWf2ucHG1vOQ5VHxVtYionfm-DnvWE + Elem _llgo_func$b6KOG2Oj7wt8ogb9H8QPbhEfXhxMMjdxRZgPLK_UOwI + Field _llgo_func$Q3NYrysaKgu1MtMuLQwb-k5QcKGHihnt-tV_NlNJQFA + FieldAlign _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + FieldByIndex _llgo_func$LPPtiM49dEPl48CC3WRhXm3YPnfUJEZE_k8Tx3rMuSk + FieldByName _llgo_func$dEvABJ5r0MMUlf4smWpIDG5dO8AuGklGdNJ1xneL3UM + FieldByNameFunc _llgo_func$xySrXVFC_2LK2oP71R2UryKi6UmdEJUo9k6aQuz4TvI + Implements _llgo_func$Kxk9fspGkjXcoNWf2ucHG1vOQ5VHxVtYionfm-DnvWE + In _llgo_func$dPYu3A0LoGTV2Hd8PW4KPw2ITiUSo9q-4Bg9ZrPITnY + IsVariadic _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk + Key _llgo_func$b6KOG2Oj7wt8ogb9H8QPbhEfXhxMMjdxRZgPLK_UOwI + Kind _llgo_func$w8Mj2LK8G5p7MIiGWR6MYjyXy3L8SVVzYlT1bb6KNXk + Len _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + Method _llgo_func$FmJJGomlX5kINJGxQdQDCAkD89ySoMslAYFrziWInVc + MethodByName _llgo_func$aM2cVUtLQbPq1YHtnabQiM7XJ5Cg5RyV6BIDWrqey7E + Name _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to + NumField _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + NumIn _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + NumMethod _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + NumOut _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + Out _llgo_func$dPYu3A0LoGTV2Hd8PW4KPw2ITiUSo9q-4Bg9ZrPITnY + OverflowComplex _llgo_func$cGkbH-2LQOLoq64Rqj3WeO56U8al7FfVkf5K1FFbPpE + OverflowFloat _llgo_func$uk7PgUVap9GZdvS8R_mZCDbAbqnAbcNryqybtDogUNI + OverflowInt _llgo_func$odFOIClZoEVGbTP_BEfZxVM5ex3r8Fj1afUEeP_awp8 + OverflowUint _llgo_func$7I97sofX8UqJA96mVIy89KPUfSM_efkrR-mJQ9qaHfk + PkgPath _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to + Size _llgo_func$1kITCsyu7hFLMxHLR7kDlvu4SOra_HtrtdFUQH9P13s + String _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to + reflect.common _llgo_func$w6XuV-1SmW103DbauPseXBpW50HpxXAEsUsGFibl0Uw + reflect.uncommon _llgo_func$iG49bujiXjI2lVflYdE0hPXlCAABL-XKRANSNJEKOio + diff --git a/cl/_testmeta/typechildren_basic/in.go b/cl/_testmeta/typechildren_basic/in.go new file mode 100644 index 0000000000..03536eead9 --- /dev/null +++ b/cl/_testmeta/typechildren_basic/in.go @@ -0,0 +1,16 @@ +package main + +type Inner struct { + S string +} + +type Outer struct { + I Inner + N int +} + +func use(any) {} + +func main() { + use(Outer{}) +} diff --git a/cl/_testmeta/typechildren_basic/meta-expect.txt b/cl/_testmeta/typechildren_basic/meta-expect.txt new file mode 100644 index 0000000000..80ebfa64ca --- /dev/null +++ b/cl/_testmeta/typechildren_basic/meta-expect.txt @@ -0,0 +1,64 @@ +[TypeChildren] +*_llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Inner: + _llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Inner +*_llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Outer: + _llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Outer +*_llgo_int: + _llgo_int +*_llgo_string: + _llgo_string +_llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Inner: + _llgo_string +_llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Outer: + _llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Inner + _llgo_int + +[OrdinaryEdges] +*_llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Inner: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Inner +*_llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Outer: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Outer +*_llgo_int: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_int +*_llgo_string: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_string +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64: + github.com/goplus/llgo/runtime/internal/runtime.memequal64 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: + github.com/goplus/llgo/runtime/internal/runtime.memequalptr +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal: + github.com/goplus/llgo/runtime/internal/runtime.strequal +_llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Inner: + *_llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Inner + _llgo_struct$MTAKiWNMPODH0G9-y5PI3BUGLd6hRciSbX1QTpCDOZg$fields + github.com/goplus/llgo/runtime/internal/runtime.structequal +_llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Outer: + *_llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Outer + _llgo_struct$VaHvQdd7oPMFznC6BSA9M_OXdmuO77w_Ys1GnWZpjRM$fields + github.com/goplus/llgo/runtime/internal/runtime.structequal +_llgo_int: + *_llgo_int + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64 +_llgo_string: + *_llgo_string + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal +_llgo_struct$MTAKiWNMPODH0G9-y5PI3BUGLd6hRciSbX1QTpCDOZg$fields: + _llgo_string +_llgo_struct$VaHvQdd7oPMFznC6BSA9M_OXdmuO77w_Ys1GnWZpjRM$fields: + _llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Inner + _llgo_int +github.com/goplus/llgo/cl/_testmeta/typechildren_basic.init: + github.com/goplus/llgo/cl/_testmeta/typechildren_basic.init$guard +github.com/goplus/llgo/cl/_testmeta/typechildren_basic.main: + _llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Outer + github.com/goplus/llgo/cl/_testmeta/typechildren_basic.use + github.com/goplus/llgo/runtime/internal/runtime.AllocU + +[UseIface] +github.com/goplus/llgo/cl/_testmeta/typechildren_basic.main: + _llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Outer + diff --git a/cl/cltest/cltest.go b/cl/cltest/cltest.go index e4b7499230..1888dfb227 100644 --- a/cl/cltest/cltest.go +++ b/cl/cltest/cltest.go @@ -69,6 +69,7 @@ type runOptions struct { filter func(string) string checkIR bool checkOutput bool + checkMeta bool } // RunOption customizes directory-based test behavior. @@ -88,7 +89,7 @@ func WithOutputFilter(filter func(string) string) RunOption { } } -// WithOutputCheck enables or disables runtime output golden checks in RunAndTestFromDir. +// WithOutputCheck enables or disables runtime output golden checks. func WithOutputCheck(enabled bool) RunOption { return func(opts *runOptions) { opts.checkOutput = enabled @@ -102,6 +103,13 @@ func WithIRCheck(enabled bool) RunOption { } } +// WithMetaCheck enables or disables package metadata golden checks. +func WithMetaCheck(enabled bool) RunOption { + return func(opts *runOptions) { + opts.checkMeta = enabled + } +} + // FilterEmulatorOutput strips emulator boot logs by returning output after "entry 0x...". func FilterEmulatorOutput(output string) string { output = strings.ReplaceAll(output, "\r\n", "\n") @@ -281,12 +289,26 @@ func testRunAndTestFrom(t *testing.T, pkgDir, relPkg, sel string, opts runOption if opts.checkIR { testFrom(t, pkgDir, sel) } + if opts.checkMeta { + metaDirs, err := findMetaCheckDirs(pkgDir) + if err != nil { + t.Fatal("Find meta check dirs failed:", err) + } + conf, capturedMetas := withMetaCaptures(opts.conf, metaDirs) + output, err := runWithConf(relPkg, pkgDir, conf) + if err != nil { + t.Logf("raw output:\n%s", string(output)) + t.Fatalf("run failed: %v\noutput: %s", err, string(output)) + } + assertExpectedMetas(t, pkgDir, relPkg, capturedMetas) + } return } var irSpec littest.Spec conf := opts.conf var capturedIR *string + var capturedMeta *string var checkIR bool if opts.checkIR { irSpec, checkIR, err = readIRSpec(pkgDir) @@ -294,9 +316,12 @@ func testRunAndTestFrom(t *testing.T, pkgDir, relPkg, sel string, opts runOption t.Fatal("LoadSpec failed:", err) } if checkIR { - conf, capturedIR = withModuleCapture(opts.conf, pkgDir) + conf, capturedIR, capturedMeta = withModuleCapture(opts.conf, pkgDir) } } + if opts.checkMeta && capturedMeta == nil { + conf, capturedIR, capturedMeta = withModuleCapture(conf, pkgDir) + } var output []byte if checkIR { @@ -312,6 +337,9 @@ func testRunAndTestFrom(t *testing.T, pkgDir, relPkg, sel string, opts runOption } assertExpectedOutput(t, pkgDir, expectedOutput, output, opts) + if opts.checkMeta { + assertExpectedMeta(t, pkgDir, relPkg, capturedMeta) + } if !checkIR { return } @@ -324,6 +352,46 @@ func testRunAndTestFrom(t *testing.T, pkgDir, relPkg, sel string, opts runOption } } +func assertExpectedMeta(t *testing.T, pkgDir, relPkg string, capturedMeta *string) { + t.Helper() + expectedMeta, hasMeta, err := readGolden(filepath.Join(pkgDir, "meta-expect.txt")) + if err != nil { + t.Fatal("ReadFile failed:", err) + } + if !hasMeta { + t.Fatal("missing meta-expect.txt") + } + if capturedMeta == nil { + t.Fatalf("metadata snapshot missing for %s", relPkg) + } + if test.Diff(t, filepath.Join(pkgDir, "meta-expect.txt.new"), []byte(*capturedMeta), expectedMeta) { + t.Fatal("metadata: unexpected result") + } +} + +func assertExpectedMetas(t *testing.T, rootDir, relRoot string, capturedMetas map[string]*string) { + t.Helper() + if len(capturedMetas) == 0 { + t.Fatal("missing meta-expect.txt") + } + dirs := make([]string, 0, len(capturedMetas)) + for dir := range capturedMetas { + dirs = append(dirs, dir) + } + slices.Sort(dirs) + for _, dir := range dirs { + relPkg := relRoot + if dir != rootDir { + rel, err := filepath.Rel(rootDir, dir) + if err != nil { + t.Fatal("Rel failed:", err) + } + relPkg = strings.TrimSuffix(relRoot, "/") + "/" + filepath.ToSlash(rel) + } + assertExpectedMeta(t, dir, relPkg, capturedMetas[dir]) + } +} + // sharedGoCacheDir returns a per-process go-build cache reused by every // captured run. Isolation from the developer cache is preserved (fresh // temp dir per test process); sharing across tests avoids re-typechecking @@ -344,17 +412,28 @@ func RunAndCapture(relPkg, pkgDir string) ([]byte, error) { return RunAndCaptureWithConf(relPkg, pkgDir, conf) } +// CaptureMeta builds relPkg and returns the package metadata captured for pkgDir. +func CaptureMeta(relPkg, pkgDir string) (string, error) { + conf, _, capturedMeta := withModuleCapture(build.NewDefaultConf(build.ModeRun), pkgDir) + output, err := runWithConf(relPkg, pkgDir, conf) + if err != nil { + return "", fmt.Errorf("%w\noutput: %s", err, string(output)) + } + return *capturedMeta, nil +} + // RunAndCaptureWithConf runs llgo with a custom build config and captures output. func RunAndCaptureWithConf(relPkg, pkgDir string, conf *build.Config) ([]byte, error) { return runWithConf(relPkg, pkgDir, conf) } -func withModuleCapture(conf *build.Config, pkgDir string) (*build.Config, *string) { +func withModuleCapture(conf *build.Config, pkgDir string) (*build.Config, *string, *string) { if conf == nil { conf = build.NewDefaultConf(build.ModeRun) } localConf := *conf var module string + var meta string prevHook := localConf.ModuleHook localConf.ModuleHook = func(pkg build.Package) { if prevHook != nil { @@ -364,9 +443,60 @@ func withModuleCapture(conf *build.Config, pkgDir string) (*build.Config, *strin return filepath.Dir(file) == pkgDir }) { module = pkg.LPkg.String() + meta = pkg.Meta.String() + } + } + return &localConf, &module, &meta +} + +func findMetaCheckDirs(pkgDir string) ([]string, error) { + var dirs []string + err := filepath.WalkDir(pkgDir, func(path string, d os.DirEntry, err error) error { + if err != nil { + return err + } + if !d.IsDir() { + return nil + } + if _, err := os.Stat(filepath.Join(path, "meta-expect.txt")); err != nil { + if errors.Is(err, os.ErrNotExist) { + return nil + } + return err + } + dirs = append(dirs, filepath.Clean(path)) + return nil + }) + if err != nil { + return nil, err + } + slices.Sort(dirs) + return dirs, nil +} + +func withMetaCaptures(conf *build.Config, pkgDirs []string) (*build.Config, map[string]*string) { + if conf == nil { + conf = build.NewDefaultConf(build.ModeRun) + } + localConf := *conf + localConf.ForceRebuild = true + metas := make(map[string]*string, len(pkgDirs)) + for _, pkgDir := range pkgDirs { + metas[filepath.Clean(pkgDir)] = new(string) + } + prevHook := localConf.ModuleHook + localConf.ModuleHook = func(pkg build.Package) { + if prevHook != nil { + prevHook(pkg) + } + for _, file := range pkg.Package.GoFiles { + if meta := metas[filepath.Clean(filepath.Dir(file))]; meta != nil { + *meta = pkg.Meta.String() + return + } } } - return &localConf, &module + return &localConf, metas } func testBuildAndCheckSymbolsFrom(t *testing.T, pkgDir, relPkg, sel, symbolSpec string, opts runOptions) { diff --git a/cl/compile.go b/cl/compile.go index c38929ba69..cc6b42bbdd 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -1905,7 +1905,7 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret ll // The rewrites map uses short variable names (without package qualifier) and // only affects string-typed globals defined in the current package. func NewPackageEx(prog llssa.Program, patches Patches, rewrites map[string]string, pkg *ssa.Package, files []*ast.File) (ret llssa.Package, externs []string, err error) { - return newPackageEx(prog, nil, patches, rewrites, pkg, files, nil) + return newPackageEx(prog, nil, patches, rewrites, pkg, files, nil, false) } // NewPackageExWithEmbed compiles a package using pre-loaded go:embed metadata. @@ -1916,10 +1916,14 @@ func NewPackageEx(prog llssa.Program, patches Patches, rewrites map[string]strin // of one compilation (like patches). nil means one-shot: a fresh // instance is created for this call. func NewPackageExWithEmbed(prog llssa.Program, ct *CallerTracking, patches Patches, rewrites map[string]string, pkg *ssa.Package, files []*ast.File, embedMap goembed.VarMap) (ret llssa.Package, externs []string, err error) { - return newPackageEx(prog, ct, patches, rewrites, pkg, files, &embedMap) + return newPackageEx(prog, ct, patches, rewrites, pkg, files, &embedMap, false) } -func newPackageEx(prog llssa.Program, ct *CallerTracking, patches Patches, rewrites map[string]string, pkg *ssa.Package, files []*ast.File, embedMap *goembed.VarMap) (ret llssa.Package, externs []string, err error) { +func NewPackageExWithEmbedMeta(prog llssa.Program, ct *CallerTracking, patches Patches, rewrites map[string]string, pkg *ssa.Package, files []*ast.File, embedMap goembed.VarMap, metaCollect bool) (ret llssa.Package, externs []string, err error) { + return newPackageEx(prog, ct, patches, rewrites, pkg, files, &embedMap, metaCollect) +} + +func newPackageEx(prog llssa.Program, ct *CallerTracking, patches Patches, rewrites map[string]string, pkg *ssa.Package, files []*ast.File, embedMap *goembed.VarMap, metaCollect bool) (ret llssa.Package, externs []string, err error) { pkgProg := pkg.Prog pkgTypes := pkg.Pkg oldTypes := pkgTypes @@ -1933,7 +1937,7 @@ func newPackageEx(prog llssa.Program, ct *CallerTracking, patches Patches, rewri if pkgPath == llssa.PkgRuntime { prog.SetRuntime(pkgTypes) } - ret = prog.NewPackage(pkgName, pkgPath) + ret = prog.NewPackageEx(pkgName, pkgPath, metaCollect) if enableDbg { ret.InitDebug(pkgName, pkgPath, pkgProg.Fset) } @@ -2004,6 +2008,11 @@ func newPackageEx(prog llssa.Program, ct *CallerTracking, patches Patches, rewri fn() } ret.MaterializePreserveSyms() + if metaCollect { + if err := ret.FinishMetaCollection(); err != nil { + return nil, nil, fmt.Errorf("build meta for %s: %w", pkgPath, err) + } + } externs = ctx.cgoSymbols return } diff --git a/cl/compile_test.go b/cl/compile_test.go index 2ef92e96ec..3dd61c2ee3 100644 --- a/cl/compile_test.go +++ b/cl/compile_test.go @@ -175,6 +175,14 @@ func TestRunAndTestFromTestgo(t *testing.T) { cltest.RunAndTestFromDir(t, "", "./_testgo", nil) } +func TestRunAndTestFromTestmeta(t *testing.T) { + cltest.RunAndTestFromDir(t, "", "./_testmeta", nil, + cltest.WithOutputCheck(false), + cltest.WithIRCheck(false), + cltest.WithMetaCheck(true), + ) +} + func TestRunAndTestFromTestlto(t *testing.T) { conf := build.NewDefaultConf(build.ModeRun) conf.LTO = lto.Full diff --git a/cl/instr.go b/cl/instr.go index 60e83145e7..af03dad660 100644 --- a/cl/instr.go +++ b/cl/instr.go @@ -2127,21 +2127,23 @@ func (p *context) reflectTypeMethodCheck(call *ssa.CallCommon, method *types.Fun return } if index, ok := constInt(call.Args[0]); ok { - p.pkg.RecordReflectMethodByIndex(index) + p.pkg.RecordReflectMethodByIndex(p.fn.Name(), index) check.Kind = llssa.ReflectTypeMethodByIndex break } + p.pkg.MarkReflectMethod(p.fn.Name()) check.Kind = llssa.ReflectTypeMethodDynamic case "MethodByName": if len(call.Args) != 1 { return } if name, ok := constStr(call.Args[0]); ok { - p.pkg.RecordReflectMethodByName(name) + p.pkg.RecordReflectMethodByName(p.fn.Name(), name) check.Kind = llssa.ReflectTypeMethodByName check.Name = name break } + p.pkg.MarkReflectMethod(p.fn.Name()) check.Kind = llssa.ReflectTypeMethodDynamic | llssa.ReflectTypeMethodByName } p.pkg.NeedAbiInit |= check.Kind diff --git a/cl/instr_reflect_test.go b/cl/instr_reflect_test.go new file mode 100644 index 0000000000..fa696873fe --- /dev/null +++ b/cl/instr_reflect_test.go @@ -0,0 +1,103 @@ +package cl + +import ( + "go/constant" + "go/token" + "go/types" + "testing" + + llssa "github.com/goplus/llgo/ssa" + "golang.org/x/tools/go/ssa" +) + +func TestReflectTypeMethodCheckRecordsDemands(t *testing.T) { + prog := llssa.NewProgram(nil) + defer prog.Dispose() + pkg := prog.NewPackageEx("pkg", "pkg", true) + fn := pkg.NewFunc("pkg.caller", types.NewSignatureType(nil, nil, nil, nil, nil, false), llssa.InGo) + ctx := &context{pkg: pkg, fn: fn} + + reflectPkg := types.NewPackage("reflect", "reflect") + iface := types.NewInterfaceType(nil, nil) + iface.Complete() + reflectType := types.NewNamed(types.NewTypeName(token.NoPos, reflectPkg, "Type", nil), iface, nil) + recv := ssa.NewConst(nil, reflectType) + method := func(name string) *types.Func { + return types.NewFunc(token.NoPos, reflectPkg, name, + types.NewSignatureType(nil, nil, nil, nil, nil, false)) + } + + check := ctx.reflectTypeMethodCheck(&ssa.CallCommon{ + Value: ssa.NewConst(constant.MakeInt64(0), types.Typ[types.Int]), + }, method("Method")) + if check != (llssa.ReflectMethodCheck{}) { + t.Fatalf("non-reflect receiver check = %+v", check) + } + + otherPkg := types.NewPackage("other", "other") + otherMethod := types.NewFunc(token.NoPos, otherPkg, "Method", + types.NewSignatureType(nil, nil, nil, nil, nil, false)) + check = ctx.reflectTypeMethodCheck(&ssa.CallCommon{Value: recv}, otherMethod) + if check != (llssa.ReflectMethodCheck{}) { + t.Fatalf("non-reflect method check = %+v", check) + } + + check = ctx.reflectTypeMethodCheck(&ssa.CallCommon{Value: recv}, method("Method")) + if check != (llssa.ReflectMethodCheck{}) { + t.Fatalf("Method without index check = %+v", check) + } + check = ctx.reflectTypeMethodCheck(&ssa.CallCommon{Value: recv}, method("MethodByName")) + if check != (llssa.ReflectMethodCheck{}) { + t.Fatalf("MethodByName without name check = %+v", check) + } + + check = ctx.reflectTypeMethodCheck(&ssa.CallCommon{ + Value: recv, + Args: []ssa.Value{ssa.NewConst(constant.MakeInt64(0), types.Typ[types.Int])}, + }, method("Method")) + if check.Kind != llssa.ReflectTypeMethodByIndex { + t.Fatalf("constant Method check = %+v", check) + } + + check = ctx.reflectTypeMethodCheck(&ssa.CallCommon{ + Value: recv, + Args: []ssa.Value{&ssa.Parameter{}}, + }, method("Method")) + if check.Kind != llssa.ReflectTypeMethodDynamic { + t.Fatalf("dynamic Method check = %+v", check) + } + + check = ctx.reflectTypeMethodCheck(&ssa.CallCommon{ + Value: recv, + Args: []ssa.Value{ssa.NewConst(constant.MakeString("Keep"), types.Typ[types.String])}, + }, method("MethodByName")) + if check.Kind != llssa.ReflectTypeMethodByName || check.Name != "Keep" { + t.Fatalf("constant MethodByName check = %+v", check) + } + + check = ctx.reflectTypeMethodCheck(&ssa.CallCommon{ + Value: recv, + Args: []ssa.Value{&ssa.Parameter{}}, + }, method("MethodByName")) + if check.Kind != llssa.ReflectTypeMethodDynamic|llssa.ReflectTypeMethodByName || check.Name != "" { + t.Fatalf("dynamic MethodByName check = %+v", check) + } + + if err := pkg.FinishMetaCollection(); err != nil { + t.Fatal(err) + } + pm := pkg.Meta + defer pm.Close() + + const want = `[UseNamedMethod] +pkg.caller: + Keep + +[Reflect] + pkg.caller + +` + if got := pm.String(); got != want { + t.Fatalf("metadata mismatch\ngot:\n%s\nwant:\n%s", got, want) + } +} diff --git a/cmd/internal/flags/flags.go b/cmd/internal/flags/flags.go index 316b96d908..e20ce644eb 100644 --- a/cmd/internal/flags/flags.go +++ b/cmd/internal/flags/flags.go @@ -47,6 +47,7 @@ var SizeFormat string var SizeLevel string var ForceRebuild bool var PrintCommands bool +var NoDeadcodeDrop bool var PthreadStackSize byteSizeFlag var OptLevel optlevel.Level @@ -202,9 +203,11 @@ func AddOptLevelFlags(fs *flag.FlagSet) { } func AddBuildFlags(fs *flag.FlagSet) { + NoDeadcodeDrop = false PthreadStackSize = 0 fs.BoolVar(&ForceRebuild, "a", false, "Force rebuilding of packages that are already up-to-date") fs.BoolVar(&PrintCommands, "x", false, "Print the commands") + fs.BoolVar(&NoDeadcodeDrop, "nodeadcodedrop", false, "Disable Go dead code drop") AddOptLevelFlags(fs) AddLTOFlag(fs) AddGlobalDCEFlag(fs) @@ -334,6 +337,7 @@ func UpdateConfig(conf *build.Config) error { conf.Tags = Tags conf.Verbose = Verbose conf.PrintCommands = PrintCommands + conf.DeadcodeDrop = !NoDeadcodeDrop conf.OptLevel = OptLevel conf.Target = Target conf.Port = Port diff --git a/cmd/internal/flags/flags_test.go b/cmd/internal/flags/flags_test.go index fdac828448..f006df9530 100644 --- a/cmd/internal/flags/flags_test.go +++ b/cmd/internal/flags/flags_test.go @@ -129,6 +129,35 @@ func TestBuildLTOFlagInvalid(t *testing.T) { } } +func TestDeadcodeDropBuildFlag(t *testing.T) { + tests := []struct { + name string + args []string + want bool + }{ + {name: "default enabled", args: nil, want: true}, + {name: "disabled", args: []string{"-nodeadcodedrop"}, want: false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fs := flag.NewFlagSet(tt.name, flag.ContinueOnError) + fs.SetOutput(new(bytes.Buffer)) + AddBuildFlags(fs) + if err := fs.Parse(tt.args); err != nil { + t.Fatalf("Parse(%v) unexpected error: %v", tt.args, err) + } + conf := &build.Config{} + if err := UpdateConfig(conf); err != nil { + t.Fatalf("UpdateConfig error: %v", err) + } + if conf.DeadcodeDrop != tt.want { + t.Fatalf("conf.DeadcodeDrop = %v, want %v", conf.DeadcodeDrop, tt.want) + } + }) + } +} + func TestBuildPthreadStackSizeFlag(t *testing.T) { tests := []struct { arg string diff --git a/internal/build/build.go b/internal/build/build.go index e561ee1c2e..96f5efe710 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -48,6 +48,7 @@ import ( "github.com/goplus/llgo/internal/goembed" "github.com/goplus/llgo/internal/header" "github.com/goplus/llgo/internal/lto" + "github.com/goplus/llgo/internal/meta" "github.com/goplus/llgo/internal/mockable" "github.com/goplus/llgo/internal/monitor" "github.com/goplus/llgo/internal/optlevel" @@ -147,6 +148,7 @@ type Config struct { Verbose bool PrintCommands bool GenLL bool // generate pkg .ll files + DeadcodeDrop bool // enable Go dead code drop CheckLLFiles bool // check .ll files valid CheckLinkArgs bool // check linkargs valid ForceEspClang bool // force to use esp-clang @@ -196,12 +198,13 @@ func NewDefaultConf(mode Mode) *Config { goarch = runtime.GOARCH } conf := &Config{ - Goos: goos, - Goarch: goarch, - BinPath: bin, - Mode: mode, - BuildMode: BuildModeExe, - AbiMode: cabi.ModeAllFunc, + Goos: goos, + Goarch: goarch, + BinPath: bin, + Mode: mode, + BuildMode: BuildModeExe, + AbiMode: cabi.ModeAllFunc, + DeadcodeDrop: true, } return conf } @@ -235,6 +238,10 @@ func (c *Config) goGlobalDCEEnabled() bool { return buildenv.Dev && c.ltoMode() == lto.Full && !c.DisableGoGlobalDCE } +func (c *Config) deadcodeDropEnabled() bool { + return c.DeadcodeDrop && !c.goGlobalDCEEnabled() +} + // ----------------------------------------------------------------------------- const ( @@ -257,6 +264,9 @@ func Do(args []string, conf *Config) ([]Package, error) { if conf.BuildMode == "" { conf.BuildMode = BuildModeExe } + if conf.BuildMode != BuildModeExe { + conf.DeadcodeDrop = false + } if conf.SizeReport && conf.SizeFormat == "" { conf.SizeFormat = "text" } @@ -332,6 +342,7 @@ func Do(args []string, conf *Config) ([]Package, error) { defer prog.Dispose() } prog.EnableGoGlobalDCE(conf.goGlobalDCEEnabled()) + prog.EnableDeadcodeDrop(conf.deadcodeDropEnabled()) if conf.PthreadStackSize > 0 { prog.SetPthreadStackSize(uint64(conf.PthreadStackSize)) } @@ -1381,10 +1392,14 @@ func buildPkg(ctx *context, aPkg *aPackage, verbose bool) error { return fmt.Errorf("load go:embed directives for %s failed: %w", pkgPath, err) } - ret, externs, err := cl.NewPackageExWithEmbed(ctx.prog, ctx.callerTracking, ctx.patches, aPkg.rewriteVars, aPkg.SSA, syntax, embedMap) + needMeta := !aPkg.CacheHit && ctx.prog.DeadcodeDropEnabled() + ret, externs, err := cl.NewPackageExWithEmbedMeta(ctx.prog, ctx.callerTracking, ctx.patches, aPkg.rewriteVars, aPkg.SSA, syntax, embedMap, needMeta) check(err) aPkg.LPkg = ret + if !aPkg.CacheHit { + aPkg.Meta = ret.Meta + } if hook := ctx.buildConf.ModuleHook; hook != nil { hook(aPkg) } @@ -1692,6 +1707,7 @@ type aPackage struct { LinkArgs []string ObjFiles []string // object files: .o or .ll (output of compiler, input to archiver) ArchiveFile string // archive file: .a (output of archiver, used for linking) + Meta *meta.PackageMeta rewriteVars map[string]string // Cache related fields diff --git a/internal/build/build_test.go b/internal/build/build_test.go index c8ce08e9e6..2eb84258e8 100644 --- a/internal/build/build_test.go +++ b/internal/build/build_test.go @@ -504,3 +504,24 @@ func TestDevLTOGlobalDCEDefaultsToFullLTO(t *testing.T) { }) } } + +func TestDeadcodeDropDisabledWhenGoGlobalDCEEnabled(t *testing.T) { + tests := []struct { + name string + conf *Config + want bool + }{ + {name: "default without full lto", conf: &Config{DeadcodeDrop: true, LTO: lto.Off}, want: true}, + {name: "disabled by flag", conf: &Config{DeadcodeDrop: false, LTO: lto.Off}, want: false}, + {name: "disabled by go global dce", conf: &Config{DeadcodeDrop: true, LTO: lto.Full}, want: !buildenv.Dev}, + {name: "enabled when go global dce disabled", conf: &Config{DeadcodeDrop: true, LTO: lto.Full, DisableGoGlobalDCE: true}, want: true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.conf.deadcodeDropEnabled(); got != tt.want { + t.Fatalf("deadcodeDropEnabled() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/build/cache.go b/internal/build/cache.go index f8870f6d92..78b622929e 100644 --- a/internal/build/cache.go +++ b/internal/build/cache.go @@ -25,12 +25,14 @@ import ( "strings" "github.com/goplus/llgo/internal/env" + "github.com/goplus/llgo/internal/meta" ) const ( cacheBuildDirName = "build" cacheArchiveExt = ".a" cacheManifestExt = ".manifest" + cacheMetaExt = ".meta" ) // cacheRootFunc can be overridden for testing @@ -56,6 +58,7 @@ type cachePaths struct { Dir string // Directory containing cache files Archive string // Path to .a file Manifest string // Path to .manifest file + Meta string // Path to .meta file } // PackagePaths returns the cache paths for a package @@ -66,6 +69,7 @@ func (cm *cacheManager) PackagePaths(targetTriple, pkgPath, fingerprint string) Dir: dir, Archive: filepath.Join(dir, fingerprint+cacheArchiveExt), Manifest: filepath.Join(dir, fingerprint+cacheManifestExt), + Meta: filepath.Join(dir, fingerprint+cacheMetaExt), } } @@ -174,16 +178,59 @@ func readManifest(path string) (string, error) { return string(content), nil } -// cacheExists checks if a valid cache entry exists -func (cm *cacheManager) cacheExists(paths cachePaths) bool { - // Both archive and manifest must exist +// writeMeta writes package summary metadata to a file atomically. +func writeMeta(path string, pm *meta.PackageMeta) error { + if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { + return fmt.Errorf("create meta dir: %w", err) + } + tmp, err := os.CreateTemp(filepath.Dir(path), filepath.Base(path)+".tmp-*") + if err != nil { + return fmt.Errorf("create temp meta: %w", err) + } + tmpName := tmp.Name() + cleanup := true + defer func() { + if cleanup { + tmp.Close() + os.Remove(tmpName) + } + }() + + if _, err := pm.WriteTo(tmp); err != nil { + return fmt.Errorf("write meta: %w", err) + } + if err := tmp.Close(); err != nil { + return fmt.Errorf("close meta: %w", err) + } + if err := os.Rename(tmpName, path); err != nil { + return fmt.Errorf("publish meta: %w", err) + } + + cleanup = false + return nil +} + +// readMeta reads package summary metadata from a file. +func readMeta(path string) (*meta.PackageMeta, error) { + return meta.Open(path) +} + +// cacheExists checks if a valid cache entry exists. +func (cm *cacheManager) cacheExists(paths cachePaths, needMeta bool) bool { if _, err := os.Stat(paths.Archive); err != nil { return false } if _, err := os.Stat(paths.Manifest); err != nil { return false } - return true + if !needMeta { + return true + } + pm, err := readMeta(paths.Meta) + if err != nil { + return false + } + return pm.Close() == nil } // cleanPackageCache removes all cache entries for a package diff --git a/internal/build/cache_test.go b/internal/build/cache_test.go index 6c6d4a0eb2..7fcc2aca55 100644 --- a/internal/build/cache_test.go +++ b/internal/build/cache_test.go @@ -23,6 +23,8 @@ import ( "path/filepath" "strings" "testing" + + "github.com/goplus/llgo/internal/meta" ) func TestSanitizePkgPath(t *testing.T) { @@ -69,6 +71,11 @@ func TestCacheManager_PackagePaths(t *testing.T) { if paths.Manifest != expectedManifest { t.Errorf("Manifest = %q, want %q", paths.Manifest, expectedManifest) } + + expectedMeta := filepath.Join(expectedDir, "abc123.meta") + if paths.Meta != expectedMeta { + t.Errorf("Meta = %q, want %q", paths.Meta, expectedMeta) + } } func TestCacheManager_EnsureDir(t *testing.T) { @@ -159,7 +166,7 @@ func TestCacheManager_CacheExists(t *testing.T) { paths := cm.PackagePaths("arm64-darwin", "test/pkg", "fp123") // Initially should not exist - if cm.cacheExists(paths) { + if cm.cacheExists(paths, false) { t.Error("cache should not exist initially") } @@ -170,17 +177,95 @@ func TestCacheManager_CacheExists(t *testing.T) { os.WriteFile(paths.Archive, []byte("archive"), 0644) // Still should not exist (manifest missing) - if cm.cacheExists(paths) { + if cm.cacheExists(paths, false) { t.Error("cache should not exist without manifest") } // Create manifest os.WriteFile(paths.Manifest, []byte("manifest"), 0644) + if !cm.cacheExists(paths, false) { + t.Error("cache should exist without meta when meta is not required") + } + if cm.cacheExists(paths, true) { + t.Error("cache should not exist without meta") + } + + // Create invalid meta + os.WriteFile(paths.Meta, []byte("bad meta"), 0644) + if !cm.cacheExists(paths, false) { + t.Error("cache should ignore invalid meta when meta is not required") + } + if cm.cacheExists(paths, true) { + t.Error("cache should not exist with invalid meta") + } + + // Create valid meta + writeTestMetaFile(t, paths.Meta) + // Now should exist - if !cm.cacheExists(paths) { - t.Error("cache should exist with both files") + if !cm.cacheExists(paths, true) { + t.Error("cache should exist with archive, manifest, and valid meta") + } +} + +func writeTestMetaFile(t *testing.T, path string) { + t.Helper() + pm, err := meta.NewBuilder().Build() + if err != nil { + t.Fatalf("Build: %v", err) } + if err := writeMeta(path, pm); err != nil { + t.Fatalf("writeMeta %s: %v", path, err) + } +} + +func TestWriteMetaErrors(t *testing.T) { + pm, err := meta.NewBuilder().Build() + if err != nil { + t.Fatal(err) + } + + t.Run("create directory", func(t *testing.T) { + blocker := filepath.Join(t.TempDir(), "file") + if err := os.WriteFile(blocker, nil, 0o644); err != nil { + t.Fatal(err) + } + err := writeMeta(filepath.Join(blocker, "test.meta"), pm) + if err == nil || !strings.Contains(err.Error(), "create meta dir") { + t.Fatalf("writeMeta error = %v, want create meta dir error", err) + } + }) + + t.Run("create temporary file", func(t *testing.T) { + path := filepath.Join(t.TempDir(), strings.Repeat("x", 256)) + err := writeMeta(path, pm) + if err == nil || !strings.Contains(err.Error(), "create temp meta") { + t.Fatalf("writeMeta error = %v, want create temp meta error", err) + } + }) + + t.Run("publish", func(t *testing.T) { + dir := t.TempDir() + path := filepath.Join(dir, "test.meta") + if err := os.Mkdir(path, 0o755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(path, "keep"), nil, 0o644); err != nil { + t.Fatal(err) + } + err := writeMeta(path, pm) + if err == nil || !strings.Contains(err.Error(), "publish meta") { + t.Fatalf("writeMeta error = %v, want publish meta error", err) + } + matches, err := filepath.Glob(path + ".tmp-*") + if err != nil { + t.Fatal(err) + } + if len(matches) != 0 { + t.Fatalf("temporary metadata files were not removed: %v", matches) + } + }) } func TestTargetTriple(t *testing.T) { @@ -224,7 +309,7 @@ func TestCacheManager_CleanPackageCache(t *testing.T) { } // Should not exist - if cm.cacheExists(paths) { + if cm.cacheExists(paths, false) { t.Error("cache should be cleaned") } } diff --git a/internal/build/collect.go b/internal/build/collect.go index dd30daf72b..c127ba7055 100644 --- a/internal/build/collect.go +++ b/internal/build/collect.go @@ -27,6 +27,7 @@ import ( "strings" "github.com/goplus/llgo/internal/env" + "github.com/goplus/llgo/internal/meta" "github.com/goplus/llgo/internal/packages" gopackages "golang.org/x/tools/go/packages" ) @@ -348,6 +349,13 @@ func (c *context) tryLoadFromCache(pkg *aPackage) bool { if err != nil { return false } + var pkgMeta *meta.PackageMeta + if c.buildConf.deadcodeDropEnabled() { + pkgMeta, err = readMeta(paths.Meta) + if err != nil { + return false + } + } // Parse metadata from manifest [Package] section (INI format) meta, err := parseManifestMetadata(content) @@ -360,6 +368,7 @@ func (c *context) tryLoadFromCache(pkg *aPackage) bool { pkg.LinkArgs = meta.LinkArgs pkg.NeedRt = meta.NeedRt pkg.NeedPyInit = meta.NeedPyInit + pkg.Meta = pkgMeta pkg.CacheHit = true return true @@ -467,6 +476,12 @@ func (c *context) saveToCache(pkg *aPackage) error { return nil } + if c.buildConf.deadcodeDropEnabled() { + if err := writeMeta(paths.Meta, pkg.Meta); err != nil { + return err + } + } + // Append metadata to existing manifest (pkg.Manifest was built in collectFingerprint). manifestContent := pkg.Manifest if manifestContent == "" { diff --git a/internal/build/collect_test.go b/internal/build/collect_test.go index bfa52ead8c..84a589d3a6 100644 --- a/internal/build/collect_test.go +++ b/internal/build/collect_test.go @@ -29,6 +29,7 @@ import ( "github.com/goplus/llgo/internal/buildenv" "github.com/goplus/llgo/internal/crosscompile" "github.com/goplus/llgo/internal/lto" + "github.com/goplus/llgo/internal/meta" "github.com/goplus/llgo/internal/packages" gopackages "golang.org/x/tools/go/packages" ) @@ -467,6 +468,7 @@ func TestTryLoadFromCache_ForceRebuild(t *testing.T) { m.pkg.PkgPath = "example.com/cached" return m.Build() }(), + Meta: func() *meta.PackageMeta { pm, _ := meta.NewBuilder().Build(); return pm }(), } // Create a temporary .o file @@ -522,8 +524,9 @@ func TestSaveToCache_MainPackage(t *testing.T) { ctx := &context{ conf: &packages.Config{}, buildConf: &Config{ - Goos: "darwin", - Goarch: "arm64", + Goos: "darwin", + Goarch: "arm64", + DeadcodeDrop: true, }, crossCompile: crosscompile.Export{ LLVMTarget: "arm64-apple-darwin", @@ -561,8 +564,9 @@ func TestTryLoadFromCache_MainPackage(t *testing.T) { ctx := &context{ conf: &packages.Config{}, buildConf: &Config{ - Goos: "darwin", - Goarch: "arm64", + Goos: "darwin", + Goarch: "arm64", + DeadcodeDrop: true, }, crossCompile: crosscompile.Export{ LLVMTarget: "arm64-apple-darwin", @@ -609,8 +613,9 @@ func TestSaveToCache_Success(t *testing.T) { ctx := &context{ conf: &packages.Config{}, buildConf: &Config{ - Goos: "darwin", - Goarch: "arm64", + Goos: "darwin", + Goarch: "arm64", + DeadcodeDrop: true, }, crossCompile: crosscompile.Export{ LLVMTarget: "arm64-apple-darwin", @@ -639,6 +644,7 @@ func TestSaveToCache_Success(t *testing.T) { return m.Build() }(), ObjFiles: []string{objFile.Name()}, + Meta: func() *meta.PackageMeta { pm, _ := meta.NewBuilder().Build(); return pm }(), } if err := ctx.saveToCache(pkg); err != nil { @@ -669,6 +675,187 @@ func TestSaveToCache_Success(t *testing.T) { if _, err := os.Stat(paths.Archive); err != nil { t.Errorf("archive should exist: %v", err) } + + pm, err := meta.Open(paths.Meta) + if err != nil { + t.Errorf("meta should exist: %v", err) + } else { + defer pm.Close() + } +} + +func TestTryLoadFromCache_LoadsPackageMeta(t *testing.T) { + td := t.TempDir() + oldFunc := cacheRootFunc + cacheRootFunc = func() string { return td } + defer func() { cacheRootFunc = oldFunc }() + + ctx := &context{ + conf: &packages.Config{}, + buildConf: &Config{ + Goos: "darwin", + Goarch: "arm64", + DeadcodeDrop: true, + }, + crossCompile: crosscompile.Export{ + LLVMTarget: "arm64-apple-darwin", + }, + } + + objFile, err := os.CreateTemp(td, "test-*.o") + if err != nil { + t.Fatalf("CreateTemp: %v", err) + } + objFile.WriteString("fake object file") + objFile.Close() + + builder := meta.NewBuilder() + main := builder.Sym("pkg.main") + helper := builder.Sym("pkg.helper") + builder.AddOrdinaryEdge(main, helper) + + pkg := &aPackage{ + Package: &packages.Package{ + PkgPath: "example.com/loadmeta", + Name: "loadmeta", + }, + Fingerprint: "loadmeta123", + Manifest: func() string { + m := newManifestBuilder() + m.env.Goos = "darwin" + m.pkg.PkgPath = "example.com/loadmeta" + return m.Build() + }(), + ObjFiles: []string{objFile.Name()}, + } + pkg.Meta, _ = builder.Build() + + if err := ctx.saveToCache(pkg); err != nil { + t.Fatalf("saveToCache: %v", err) + } + + pkg.ObjFiles = nil + pkg.ArchiveFile = "" + pkg.CacheHit = false + pkg.Meta = nil + + if !ctx.tryLoadFromCache(pkg) { + t.Fatal("tryLoadFromCache = false, want true") + } + if pkg.Meta == nil { + t.Fatal("Meta was not loaded from cache") + } + summary, err := meta.NewGlobalSummary([]*meta.PackageMeta{pkg.Meta}) + if err != nil { + t.Fatalf("NewGlobalSummary: %v", err) + } + mainSym, ok := summary.LookupSymbol("pkg.main") + if !ok { + t.Fatal("pkg.main not found in cached metadata") + } + edges := summary.OrdinaryEdges(mainSym) + if len(edges) != 1 || summary.SymbolName(edges[0]) != "pkg.helper" { + t.Fatalf("cached metadata edge mismatch: %#v", edges) + } +} + +func TestTryLoadFromCacheRejectsBadMeta(t *testing.T) { + td := t.TempDir() + oldFunc := cacheRootFunc + cacheRootFunc = func() string { return td } + defer func() { cacheRootFunc = oldFunc }() + + ctx := &context{ + conf: &packages.Config{}, + buildConf: &Config{ + Goos: "darwin", + Goarch: "arm64", + DeadcodeDrop: true, + }, + crossCompile: crosscompile.Export{ + LLVMTarget: "arm64-apple-darwin", + }, + } + + pkg := &aPackage{ + Package: &packages.Package{ + PkgPath: "example.com/badmeta", + Name: "badmeta", + }, + Fingerprint: "badmeta123", + } + cm := ctx.ensureCacheManager() + paths := cm.PackagePaths("arm64-apple-darwin", "example.com/badmeta", "badmeta123") + if err := cm.EnsureDir(paths); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(paths.Archive, []byte("archive"), 0o644); err != nil { + t.Fatal(err) + } + m := newManifestBuilder() + m.env.Goos = "darwin" + m.pkg.PkgPath = "example.com/badmeta" + if err := writeManifest(paths.Manifest, m.Build()); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(paths.Meta, []byte("bad meta"), 0o644); err != nil { + t.Fatal(err) + } + + if ctx.tryLoadFromCache(pkg) { + t.Fatal("tryLoadFromCache accepted invalid meta") + } +} + +func TestTryLoadFromCacheIgnoresMetaWhenDeadcodeDropDisabled(t *testing.T) { + td := t.TempDir() + oldFunc := cacheRootFunc + cacheRootFunc = func() string { return td } + defer func() { cacheRootFunc = oldFunc }() + + ctx := &context{ + conf: &packages.Config{}, + buildConf: &Config{ + Goos: "darwin", + Goarch: "arm64", + DeadcodeDrop: false, + }, + crossCompile: crosscompile.Export{ + LLVMTarget: "arm64-apple-darwin", + }, + } + + pkg := &aPackage{ + Package: &packages.Package{ + PkgPath: "example.com/nometa", + Name: "nometa", + }, + Fingerprint: "nometa123", + } + cm := ctx.ensureCacheManager() + paths := cm.PackagePaths("arm64-apple-darwin", "example.com/nometa", "nometa123") + if err := cm.EnsureDir(paths); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(paths.Archive, []byte("archive"), 0o644); err != nil { + t.Fatal(err) + } + m := newManifestBuilder() + m.env.Goos = "darwin" + m.pkg.PkgPath = "example.com/nometa" + if err := writeManifest(paths.Manifest, m.Build()); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(paths.Meta, []byte("bad meta"), 0o644); err != nil { + t.Fatal(err) + } + + if !ctx.tryLoadFromCache(pkg) { + t.Fatal("tryLoadFromCache rejected cache while deadcode drop is disabled") + } + if pkg.Meta != nil { + t.Fatal("Meta should not be loaded while deadcode drop is disabled") + } } func TestGetLLVMVersion(t *testing.T) { diff --git a/internal/meta/builder.go b/internal/meta/builder.go new file mode 100644 index 0000000000..6333fa3ec2 --- /dev/null +++ b/internal/meta/builder.go @@ -0,0 +1,447 @@ +package meta + +import "encoding/binary" + +// Builder accumulates per-package metadata facts and serializes them into +// the binary wire format understood by PackageMeta. +// +// Typical usage: +// +// b := NewBuilder() +// fn := b.Sym("main.main") +// callee := b.Sym("fmt.Println") +// b.AddOrdinaryEdge(fn, callee) +// pm, err := b.Build() +type Builder struct { + // string interning + strData []byte // raw byte stream, all strings concatenated + strMap map[string]uint32 // string → offset in strData + + // symbol table + symNames []symEntry // indexed by Symbol + symMap map[string]Symbol // name → Symbol + + // per-symbol ordinary edge lists (source Symbol → target package-local Symbols) + ordinaryEdges [][]Symbol + + // per-symbol function demand lists (source Symbol → demand facts) + funcDemands [][]bFuncDemand + + // per-symbol TypeChildren lists + typeChildren [][]Symbol + typeChildrenSet map[[2]Symbol]struct{} // dedup (parent, child) pairs + + // per-symbol MethodInfo (only concrete types) + methodInfo [][]bMethodSlot + + // per-symbol InterfaceInfo (only interface types) + ifaceInfo [][]bMethodSig +} + +type symEntry struct { + nameOff uint32 + nameLen uint32 +} + +type bFuncDemand struct { + kind DemandKind + target uint32 // Symbol or stringTable offset (DemandNamedMethod) + extra uint32 +} + +type bMethodSlot struct { + name nameRef // method short name + mtype uint32 // Symbol + ifn uint32 // Symbol + tfn uint32 // Symbol +} + +type bMethodSig struct { + name nameRef // method short name + mtype uint32 // Symbol +} + +// NewBuilder creates an empty Builder. +func NewBuilder() *Builder { + return &Builder{ + strMap: make(map[string]uint32), + symMap: make(map[string]Symbol), + typeChildrenSet: make(map[[2]Symbol]struct{}), + } +} + +// internStr adds s to the string byte stream (idempotent) and returns its offset. +func (b *Builder) internStr(s string) uint32 { + if off, ok := b.strMap[s]; ok { + return off + } + off := uint32(len(b.strData)) + b.strData = append(b.strData, s...) + b.strMap[s] = off + return off +} + +// internName registers a name string and returns a nameRef. +func (b *Builder) internName(s string) nameRef { + return nameRef{Off: b.internStr(s), Len: uint32(len(s))} +} + +// Sym registers a symbol by name and returns its Symbol. +// Calling Sym with the same name twice returns the same Symbol. +// Whether the symbol is defined in this package or referenced from another +// makes no difference to the metadata format. +func (b *Builder) Sym(name string) Symbol { + return b.sym(name) +} + +func (b *Builder) sym(name string) Symbol { + if id, ok := b.symMap[name]; ok { + return id + } + id := Symbol(len(b.symNames)) + off := b.internStr(name) + b.symNames = append(b.symNames, symEntry{nameOff: off, nameLen: uint32(len(name))}) + b.symMap[name] = id + // grow all per-symbol structures in sync with the symbol table + b.ordinaryEdges = append(b.ordinaryEdges, nil) + b.funcDemands = append(b.funcDemands, nil) + b.typeChildren = append(b.typeChildren, nil) + b.methodInfo = append(b.methodInfo, nil) + b.ifaceInfo = append(b.ifaceInfo, nil) + return id +} + +// AddOrdinaryEdge records a plain symbol-to-symbol reference from src to dst +// (call, type use, global var reference, etc.). +func (b *Builder) AddOrdinaryEdge(src, dst Symbol) { + b.ordinaryEdges[src] = append(b.ordinaryEdges[src], dst) +} + +// AddIfaceUse records that src converts a value of type typ to an interface. +func (b *Builder) AddIfaceUse(src, typ Symbol) { + b.funcDemands[src] = append(b.funcDemands[src], bFuncDemand{ + kind: DemandUseIface, + target: uint32(typ), + }) +} + +// AddIfaceMethodUse records that src calls the methodIndex-th method (in +// declaration order) of interface iface. +func (b *Builder) AddIfaceMethodUse(src, iface Symbol, methodIndex uint32) { + b.funcDemands[src] = append(b.funcDemands[src], bFuncDemand{ + kind: DemandIfaceMethod, + target: uint32(iface), + extra: methodIndex, + }) +} + +// AddNamedMethodUse records that src does a constant MethodByName(methodName) +// call. The method name is stored as a string-table reference. +func (b *Builder) AddNamedMethodUse(src Symbol, methodName string) { + ref := b.internName(methodName) + b.funcDemands[src] = append(b.funcDemands[src], bFuncDemand{ + kind: DemandNamedMethod, + target: ref.Off, + extra: ref.Len, + }) +} + +// AddTypeChild records that parent type structurally contains child type. +// Idempotent: duplicate (parent, child) pairs are silently ignored. +func (b *Builder) AddTypeChild(parent, child Symbol) { + key := [2]Symbol{parent, child} + if _, ok := b.typeChildrenSet[key]; ok { + return + } + b.typeChildrenSet[key] = struct{}{} + b.typeChildren[parent] = append(b.typeChildren[parent], child) +} + +// AddMethodSlot records one ABI method slot for a concrete type. +// Slots must be appended in abi.Method table order. +func (b *Builder) AddMethodSlot(typ Symbol, methodName string, mtype, ifn, tfn Symbol) { + b.methodInfo[typ] = append(b.methodInfo[typ], bMethodSlot{ + name: b.internName(methodName), + mtype: uint32(mtype), + ifn: uint32(ifn), + tfn: uint32(tfn), + }) +} + +// AddIfaceMethod records one method in an interface's method set. +// Idempotent: if the same (name, mtype) pair is already registered for iface, +// this call is a no-op — the Builder deduplicates internally. +func (b *Builder) AddIfaceMethod(iface Symbol, methodName string, mtype Symbol) { + ref := b.internName(methodName) + mt := uint32(mtype) + for _, s := range b.ifaceInfo[iface] { + if s.name == ref && s.mtype == mt { + return + } + } + b.ifaceInfo[iface] = append(b.ifaceInfo[iface], bMethodSig{ + name: ref, + mtype: mt, + }) +} + +// MarkReflect marks sym as triggering conservative reflection handling. +func (b *Builder) MarkReflect(sym Symbol) { + b.funcDemands[sym] = append(b.funcDemands[sym], bFuncDemand{ + kind: DemandReflectMethod, + }) +} + +// Build serializes all accumulated facts into a PackageMeta. +// +// The process is: +// 1. Calculate the byte size of every section. +// 2. Derive each section's start offset. +// 3. Allocate one []byte for the whole file. +// 4. Write header + every section directly into the buffer — no intermediate +// allocations, no copies. +func (b *Builder) Build() (*PackageMeta, error) { + nsyms := uint32(len(b.symNames)) + + // ── 1. calculate section sizes ──────────────────────────────────────────── + + // stringTable is padded to a 4-byte boundary so every following section + // starts 4-byte aligned, enabling zero-copy unsafe access (e.g. TypeChildren). + strSize := align4(uint32(len(b.strData))) + + symSize := 4 + nsyms*12 // nsyms u32 + N×SymbolRecord(12) + + totalOrdinary := uint32(0) + for _, es := range b.ordinaryEdges { + totalOrdinary += uint32(len(es)) + } + ordinarySize := 4 + (nsyms+1)*4 + totalOrdinary*4 // nsyms + offsets[N+1] + N×Symbol(4) + + totalDemands := uint32(0) + for _, ds := range b.funcDemands { + totalDemands += uint32(len(ds)) + } + demandSize := 4 + (nsyms+1)*4 + totalDemands*12 // nsyms + offsets[N+1] + N×FuncDemand(12) + + totalChildren := uint32(0) + for _, cs := range b.typeChildren { + totalChildren += uint32(len(cs)) + } + childSize := 4 + (nsyms+1)*4 + totalChildren*4 + + totalSlots := uint32(0) + for _, ms := range b.methodInfo { + totalSlots += uint32(len(ms)) + } + methodSize := 4 + (nsyms+1)*4 + totalSlots*20 // N×localMethodSlot(20: nameRef(8)+mtype+ifn+tfn) + + totalSigs := uint32(0) + for _, ss := range b.ifaceInfo { + totalSigs += uint32(len(ss)) + } + ifaceSize := 4 + (nsyms+1)*4 + totalSigs*12 // N×localMethodSig(12: nameRef(8)+mtype) + + // ── 2. calculate section offsets ───────────────────────────────────────── + + var offsets [numSections]uint32 + cur := uint32(headerSize) + offsets[secStringTable] = cur + cur += strSize + offsets[secSymbols] = cur + cur += symSize + offsets[secOrdinaryEdges] = cur + cur += ordinarySize + offsets[secFuncDemand] = cur + cur += demandSize + offsets[secTypeChildren] = cur + cur += childSize + offsets[secMethodInfo] = cur + cur += methodSize + offsets[secIfaceInfo] = cur + cur += ifaceSize + + // ── 3. allocate one buffer ──────────────────────────────────────────────── + + raw := make([]byte, cur) + + // ── 4. write header ─────────────────────────────────────────────────────── + + copy(raw[0:4], magic) + binary.LittleEndian.PutUint32(raw[4:8], version) + for i, off := range offsets { + binary.LittleEndian.PutUint32(raw[8+i*4:], off) + } + + // ── 5. write each section directly into raw ─────────────────────────────── + + writeStringTable(raw[offsets[secStringTable]:], b) + writeSymbols(raw[offsets[secSymbols]:], b, nsyms) + writeOrdinaryEdges(raw[offsets[secOrdinaryEdges]:], b, nsyms) + writeFuncDemand(raw[offsets[secFuncDemand]:], b, nsyms) + writeTypeChildren(raw[offsets[secTypeChildren]:], b, nsyms) + writeMethodInfo(raw[offsets[secMethodInfo]:], b, nsyms) + writeIfaceInfo(raw[offsets[secIfaceInfo]:], b, nsyms) + + return newPackageMeta(raw) +} + +// ── section writers ─────────────────────────────────────────────────────────── +// Each writer receives a slice starting exactly at its section's offset. +// It writes directly into that slice — no allocation, no copy. + +func writeStringTable(dst []byte, b *Builder) { + // dst may be longer than strData (padding); padding bytes stay zero. + copy(dst, b.strData) +} + +// align4 rounds n up to the next multiple of 4. +func align4(n uint32) uint32 { + return (n + 3) &^ 3 +} + +// writeSymbols writes: +// +// nsyms u32 +// [nsyms] { nameOff u32, nameLen u32, _ [4]byte } (12 bytes each) +func writeSymbols(dst []byte, b *Builder, nsyms uint32) { + binary.LittleEndian.PutUint32(dst, nsyms) + const rec = 12 + for i, e := range b.symNames { + base := 4 + i*rec + binary.LittleEndian.PutUint32(dst[base:], e.nameOff) + binary.LittleEndian.PutUint32(dst[base+4:], e.nameLen) + // dst[base+8 : base+12] reserved, already zero + } +} + +// writeCSRHeader writes: +// +// nsyms u32 +// offsets [nsyms+1] u32 +// +// and returns the slice starting at the data area (after the offsets array). +// cur accumulates the running data index as each symbol's entries are counted. +func writeCSROffsets(dst []byte, nsyms uint32, counts []int) []byte { + binary.LittleEndian.PutUint32(dst, nsyms) + offsetBase := dst[4:] + cur := uint32(0) + for i, c := range counts { + binary.LittleEndian.PutUint32(offsetBase[i*4:], cur) + cur += uint32(c) + } + // sentinel + binary.LittleEndian.PutUint32(offsetBase[len(counts)*4:], cur) + // return slice starting at data area + return dst[4+(nsyms+1)*4:] +} + +// writeOrdinaryEdges writes the OrdinaryEdges section. +// +// nsyms u32 +// offsets [nsyms+1] u32 +// data [] u32 (Symbol) +func writeOrdinaryEdges(dst []byte, b *Builder, nsyms uint32) { + counts := make([]int, nsyms) + for i := range b.ordinaryEdges { + counts[i] = len(b.ordinaryEdges[i]) + } + data := writeCSROffsets(dst, nsyms, counts) + pos := 0 + for _, es := range b.ordinaryEdges { + for _, target := range es { + binary.LittleEndian.PutUint32(data[pos:], uint32(target)) + pos += 4 + } + } +} + +// writeFuncDemand writes the FuncDemand section. +// +// nsyms u32 +// offsets [nsyms+1] u32 +// data [] { kind u32, target u32, extra u32 } (12 bytes each) +func writeFuncDemand(dst []byte, b *Builder, nsyms uint32) { + counts := make([]int, nsyms) + for i := range b.funcDemands { + counts[i] = len(b.funcDemands[i]) + } + data := writeCSROffsets(dst, nsyms, counts) + const rec = 12 + pos := 0 + for _, ds := range b.funcDemands { + for _, d := range ds { + binary.LittleEndian.PutUint32(data[pos:], uint32(d.kind)) + binary.LittleEndian.PutUint32(data[pos+4:], d.target) + binary.LittleEndian.PutUint32(data[pos+8:], d.extra) + pos += rec + } + } +} + +// writeTypeChildren writes the TypeChildren section. +// +// nsyms u32 +// offsets [nsyms+1] u32 +// data [] u32 (Symbol) +func writeTypeChildren(dst []byte, b *Builder, nsyms uint32) { + counts := make([]int, nsyms) + for i := range b.typeChildren { + counts[i] = len(b.typeChildren[i]) + } + data := writeCSROffsets(dst, nsyms, counts) + pos := 0 + for _, cs := range b.typeChildren { + for _, child := range cs { + binary.LittleEndian.PutUint32(data[pos:], uint32(child)) + pos += 4 + } + } +} + +// writeMethodInfo writes the MethodInfo section. +// +// nsyms u32 +// offsets [nsyms+1] u32 +// data [] { nameOff u32, nameLen u32, mtype u32, ifn u32, tfn u32 } (20 bytes each) +func writeMethodInfo(dst []byte, b *Builder, nsyms uint32) { + counts := make([]int, nsyms) + for i := range b.methodInfo { + counts[i] = len(b.methodInfo[i]) + } + data := writeCSROffsets(dst, nsyms, counts) + const rec = 20 + pos := 0 + for _, slots := range b.methodInfo { + for _, slot := range slots { + binary.LittleEndian.PutUint32(data[pos:], slot.name.Off) + binary.LittleEndian.PutUint32(data[pos+4:], slot.name.Len) + binary.LittleEndian.PutUint32(data[pos+8:], slot.mtype) + binary.LittleEndian.PutUint32(data[pos+12:], slot.ifn) + binary.LittleEndian.PutUint32(data[pos+16:], slot.tfn) + pos += rec + } + } +} + +// writeIfaceInfo writes the InterfaceInfo section. +// +// nsyms u32 +// offsets [nsyms+1] u32 +// data [] { nameOff u32, nameLen u32, mtype u32 } (12 bytes each) +func writeIfaceInfo(dst []byte, b *Builder, nsyms uint32) { + counts := make([]int, nsyms) + for i := range b.ifaceInfo { + counts[i] = len(b.ifaceInfo[i]) + } + data := writeCSROffsets(dst, nsyms, counts) + const rec = 12 + pos := 0 + for _, sigs := range b.ifaceInfo { + for _, sig := range sigs { + binary.LittleEndian.PutUint32(data[pos:], sig.name.Off) + binary.LittleEndian.PutUint32(data[pos+4:], sig.name.Len) + binary.LittleEndian.PutUint32(data[pos+8:], sig.mtype) + pos += rec + } + } +} diff --git a/internal/meta/format.go b/internal/meta/format.go new file mode 100644 index 0000000000..a19d64e89b --- /dev/null +++ b/internal/meta/format.go @@ -0,0 +1,176 @@ +package meta + +import ( + "fmt" + "sort" + "strings" +) + +// String returns a human-readable representation for testing and debugging. +func (pm *PackageMeta) String() string { + if pm == nil { + return "" + } + var sb strings.Builder + formatMeta(&sb, pm) + return sb.String() +} + +// formatMeta writes a human-readable representation of pm to w, +// grouped by section (TypeChildren, OrdinaryEdges, UseIface, etc.) +// to match the original metadata format used by golden file tests. +func formatMeta(w *strings.Builder, pm *PackageMeta) { + n := pm.nsyms + symName := func(sym Symbol) string { return pm.symbolName(sym) } + + // collect per-sym edge lists by kind + type kindMap = map[string][]string // src → []dst (sorted) + ordinary := make(map[string][]string) + useIface := make(map[string][]string) + useIfaceMethod := make(map[string][]string) // src → ["iface[idx]", ...] + useNamed := make(map[string][]string) + + for i := Symbol(0); i < Symbol(n); i++ { + src := symName(i) + for _, dst := range pm.ordinaryEdges(i) { + ordinary[src] = append(ordinary[src], symName(dst)) + } + for _, d := range pm.funcDemands(i) { + switch d.Kind { + case DemandUseIface: + useIface[src] = append(useIface[src], symName(Symbol(d.Target))) + case DemandIfaceMethod: + ifaceSym := Symbol(d.Target) + iface := symName(ifaceSym) + sigs := pm.ifaceMethods(ifaceSym) + s := sigs[d.Extra] + useIfaceMethod[src] = append(useIfaceMethod[src], + fmt.Sprintf("%s %s %s", iface, pm.nameString(s.Name), symName(s.MType))) + case DemandNamedMethod: + name := pm.nameString(nameRef{Off: d.Target, Len: d.Extra}) + useNamed[src] = append(useNamed[src], name) + } + } + } + + // collect TypeChildren + typeChildren := make(map[string][]string) + for i := Symbol(0); i < Symbol(n); i++ { + parent := symName(i) + for _, c := range pm.typeChildren(i) { + typeChildren[parent] = append(typeChildren[parent], symName(c)) + } + } + + // collect MethodInfo + type slotInfo struct{ name, mtype, ifn, tfn string } + methodInfo := make(map[string][]slotInfo) + for i := Symbol(0); i < Symbol(n); i++ { + typ := symName(i) + for _, s := range pm.methodSlots(i) { + methodInfo[typ] = append(methodInfo[typ], slotInfo{ + name: pm.nameString(s.Name), + mtype: symName(s.MType), + ifn: symName(s.IFn), + tfn: symName(s.TFn), + }) + } + } + + // collect InterfaceInfo + type sigInfo struct{ name, mtype string } + ifaceInfo := make(map[string][]sigInfo) + for i := Symbol(0); i < Symbol(n); i++ { + iface := symName(i) + for _, s := range pm.ifaceMethods(i) { + ifaceInfo[iface] = append(ifaceInfo[iface], sigInfo{ + name: pm.nameString(s.Name), + mtype: symName(s.MType), + }) + } + } + + // collect Reflect + var reflectSyms []string + for i := Symbol(0); i < Symbol(n); i++ { + for _, d := range pm.funcDemands(i) { + if d.Kind == DemandReflectMethod { + reflectSyms = append(reflectSyms, symName(i)) + break + } + } + } + + printSection := func(title string, m map[string][]string) { + if len(m) == 0 { + return + } + fmt.Fprintf(w, "[%s]\n", title) + keys := sortedKeys(m) + for _, k := range keys { + vals := m[k] + sort.Strings(vals) + fmt.Fprintf(w, "%s:\n", k) + for _, v := range vals { + fmt.Fprintf(w, " %s\n", v) + } + } + fmt.Fprintln(w) + } + + printSection("TypeChildren", typeChildren) + printSection("OrdinaryEdges", ordinary) + printSection("UseIface", useIface) + printSection("UseIfaceMethod", useIfaceMethod) + printSection("UseNamedMethod", useNamed) + + if len(methodInfo) > 0 { + fmt.Fprintln(w, "[MethodInfo]") + keys := make([]string, 0, len(methodInfo)) + for k := range methodInfo { + keys = append(keys, k) + } + sort.Strings(keys) + for _, typ := range keys { + fmt.Fprintf(w, "%s:\n", typ) + for idx, s := range methodInfo[typ] { + fmt.Fprintf(w, " %d %s %s %s %s\n", idx, s.name, s.mtype, s.ifn, s.tfn) + } + } + fmt.Fprintln(w) + } + + if len(ifaceInfo) > 0 { + fmt.Fprintln(w, "[InterfaceInfo]") + keys := make([]string, 0, len(ifaceInfo)) + for k := range ifaceInfo { + keys = append(keys, k) + } + sort.Strings(keys) + for _, iface := range keys { + fmt.Fprintf(w, "%s:\n", iface) + for _, s := range ifaceInfo[iface] { + fmt.Fprintf(w, " %s %s\n", s.name, s.mtype) + } + } + fmt.Fprintln(w) + } + + if len(reflectSyms) > 0 { + sort.Strings(reflectSyms) + fmt.Fprintln(w, "[Reflect]") + for _, r := range reflectSyms { + fmt.Fprintf(w, " %s\n", r) + } + fmt.Fprintln(w) + } +} + +func sortedKeys(m map[string][]string) []string { + keys := make([]string, 0, len(m)) + for k := range m { + keys = append(keys, k) + } + sort.Strings(keys) + return keys +} diff --git a/internal/meta/format_test.go b/internal/meta/format_test.go new file mode 100644 index 0000000000..00f1b0a071 --- /dev/null +++ b/internal/meta/format_test.go @@ -0,0 +1,65 @@ +package meta + +import "testing" + +func TestPackageMetaString(t *testing.T) { + b := NewBuilder() + main := b.Sym("pkg.main") + helper := b.Sym("pkg.helper") + typ := b.Sym("_llgo_pkg.T") + child := b.Sym("_llgo_pkg.Child") + iface := b.Sym("_llgo_iface$Reader") + mtype := b.Sym("_llgo_func$Read") + ifn := b.Sym("pkg.(*T).Read") + tfn := b.Sym("pkg.T.Read") + b.AddOrdinaryEdge(main, helper) + b.AddIfaceUse(main, typ) + b.AddIfaceMethodUse(main, iface, 0) + b.AddNamedMethodUse(helper, "Keep") + b.AddTypeChild(typ, child) + b.AddMethodSlot(typ, "Read", mtype, ifn, tfn) + b.AddIfaceMethod(iface, "Read", mtype) + b.MarkReflect(helper) + + pm, err := b.Build() + if err != nil { + t.Fatal(err) + } + defer pm.Close() + + const want = `[TypeChildren] +_llgo_pkg.T: + _llgo_pkg.Child + +[OrdinaryEdges] +pkg.main: + pkg.helper + +[UseIface] +pkg.main: + _llgo_pkg.T + +[UseIfaceMethod] +pkg.main: + _llgo_iface$Reader Read _llgo_func$Read + +[UseNamedMethod] +pkg.helper: + Keep + +[MethodInfo] +_llgo_pkg.T: + 0 Read _llgo_func$Read pkg.(*T).Read pkg.T.Read + +[InterfaceInfo] +_llgo_iface$Reader: + Read _llgo_func$Read + +[Reflect] + pkg.helper + +` + if got := pm.String(); got != want { + t.Fatalf("metadata mismatch\ngot:\n%s\nwant:\n%s", got, want) + } +} diff --git a/internal/meta/global.go b/internal/meta/global.go new file mode 100644 index 0000000000..f27ca29f1d --- /dev/null +++ b/internal/meta/global.go @@ -0,0 +1,286 @@ +package meta + +// GlobalSummary is a whole-program metadata view over multiple PackageMetas, +// in one unified symbol/name space. +// +// Merge strategy: +// - Symbols are interned into a global Symbol space; each package's local +// symbols are mapped via locToGlb. Edges, FuncDemands, TypeChildren, +// MethodSlots and IfaceMethods are NOT rewritten at merge time — they +// are translated lazily on query. Only the strings are interned up front. +// - Duplicate symbols (e.g. linkonce type descriptors emitted by several +// packages) are assigned one owner by fact strength. Function-demand, +// MethodInfo, InterfaceInfo and TypeChildren facts outrank ordinary edges, +// so descriptor references do not hide the package that carries semantic +// method/interface facts. +// +// GlobalSummary borrows its input PackageMetas. They must remain open and +// unchanged for the lifetime of the summary. +type GlobalSummary struct { + pkgs []*PackageMeta + + // symbol space + symIntern map[string]Symbol + symStrings []string // Symbol → text + locToGlb [][]Symbol // [pkgIdx][localSym] → global Symbol + owner []symLoc // global Symbol → owning (pkg, local); pkg<0 if none + + // method-name space (distinct from symbols) + nameIntern map[string]Name + nameStrings []string // Name → text + + interfaces []Symbol +} + +// symLoc identifies a (package, local symbol) pair. pkg < 0 means "no owner". +type symLoc struct { + pkg int32 + local Symbol +} + +type ownerKind uint8 + +const ( + ownerNone ownerKind = iota + ownerOrdinary + ownerType + ownerInterface + ownerFunction +) + +// ownerState exists only while NewGlobalSummary selects owners. +type ownerState struct { + kind ownerKind +} + +// NewGlobalSummary merges package-local metadata into a whole-program view. +// +// Phase 1 interns all symbol names and builds the locToGlb mapping, owner +// indices, and type-kind flags. No per-symbol data is translated — only +// string interning and CSR range checks happen here. +// +// MethodSlots / IfaceMethods / FuncDemands are translated lazily on each +// query. This avoids translating metadata that DCE never reaches. +func NewGlobalSummary(pkgs []*PackageMeta) (*GlobalSummary, error) { + g := &GlobalSummary{ + pkgs: pkgs, + symIntern: make(map[string]Symbol), + nameIntern: make(map[string]Name), + locToGlb: make([][]Symbol, len(pkgs)), + } + var ownerStates []ownerState + var interfaceSeen []bool + + // Phase 1: intern symbols, build locToGlb and owner, mark type kinds. + // Touches no edges, translates no slot/sig data. + for pi, pm := range pkgs { + n := pm.nsyms + tab := make([]Symbol, n) + for li := Symbol(0); li < Symbol(n); li++ { + gs, added := g.internSymbol(pm.symbolName(li)) + if added { + ownerStates = append(ownerStates, ownerState{}) + interfaceSeen = append(interfaceSeen, false) + } + tab[li] = gs + kind := g.considerOwner(gs, symLoc{pkg: int32(pi), local: li}, pm, li, &ownerStates[gs]) + + // Collect each interface with method information once. + if kind == ownerInterface && !interfaceSeen[gs] { + interfaceSeen[gs] = true + g.interfaces = append(g.interfaces, gs) + } + } + g.locToGlb[pi] = tab + } + return g, nil +} + +func packageOwnerKind(pm *PackageMeta, local Symbol) ownerKind { + switch { + case pm.hasFuncDemand(local): + return ownerFunction + case pm.nifaceMethod(local) > 0: + return ownerInterface + case pm.nmethodSlot(local) > 0 || pm.ntypeChild(local) > 0: + return ownerType + case pm.hasOrdinaryEdges(local): + return ownerOrdinary + default: + return ownerNone + } +} + +// considerOwner merges one package-local owner candidate into the global view +// and returns the selected owner's semantic kind. +func (g *GlobalSummary) considerOwner(global Symbol, candidate symLoc, pm *PackageMeta, local Symbol, state *ownerState) ownerKind { + if state.kind == ownerFunction { + return state.kind + } + + candidateKind := packageOwnerKind(pm, local) + switch candidateKind { + case ownerFunction: + g.owner[global] = candidate + state.kind = ownerFunction + case ownerType, ownerInterface: + if state.kind == ownerNone || state.kind == ownerOrdinary { + g.owner[global] = candidate + state.kind = candidateKind + } + case ownerOrdinary: + if state.kind == ownerNone { + g.owner[global] = candidate + state.kind = ownerOrdinary + } + } + return state.kind +} + +func (g *GlobalSummary) internSymbol(s string) (Symbol, bool) { + if id, ok := g.symIntern[s]; ok { + return id, false + } + id := Symbol(len(g.symStrings)) + g.symIntern[s] = id + g.symStrings = append(g.symStrings, s) + g.owner = append(g.owner, symLoc{pkg: -1}) + return id, true +} + +func (g *GlobalSummary) internName(s string) Name { + if id, ok := g.nameIntern[s]; ok { + return id + } + id := Name(len(g.nameStrings)) + g.nameIntern[s] = id + g.nameStrings = append(g.nameStrings, s) + return id +} + +// ownerData returns the owning package and locToGlb table for sym. +func (g *GlobalSummary) ownerData(sym Symbol) (*PackageMeta, []Symbol, Symbol) { + loc := g.owner[sym] + return g.pkgs[loc.pkg], g.locToGlb[loc.pkg], loc.local +} + +func (g *GlobalSummary) translateSlots(tab []Symbol, pm *PackageMeta, li Symbol) []MethodSlot { + local := pm.methodSlots(li) + out := make([]MethodSlot, len(local)) + for i, s := range local { + out[i] = MethodSlot{ + Name: g.internName(pm.nameString(s.Name)), + MType: tab[s.MType], + IFn: tab[s.IFn], + TFn: tab[s.TFn], + } + } + return out +} + +func (g *GlobalSummary) translateSigs(tab []Symbol, pm *PackageMeta, li Symbol) []MethodSig { + local := pm.ifaceMethods(li) + out := make([]MethodSig, len(local)) + for i, s := range local { + out[i] = MethodSig{ + Name: g.internName(pm.nameString(s.Name)), + MType: tab[s.MType], + } + } + return out +} + +func (g *GlobalSummary) translateFuncDemands(tab []Symbol, pm *PackageMeta, li Symbol) []FuncDemand { + local := pm.funcDemands(li) + var out []FuncDemand + for _, d := range local { + switch d.Kind { + case DemandUseIface: + out = append(out, FuncDemand{Kind: d.Kind, Target: tab[d.Target]}) + case DemandIfaceMethod: + iface := tab[d.Target] + sigs := g.IfaceMethods(iface) + if int(d.Extra) < len(sigs) { + out = append(out, FuncDemand{Kind: d.Kind, Target: iface, Sig: sigs[d.Extra]}) + } + case DemandNamedMethod: + name := pm.nameString(nameRef{Off: d.Target, Len: d.Extra}) + out = append(out, FuncDemand{Kind: d.Kind, MethodName: g.internName(name)}) + case DemandReflectMethod: + out = append(out, FuncDemand{Kind: d.Kind}) + } + } + return out +} + +// ── symbol / name identity ──────────────────────────────────────────────────── + +// LookupSymbol returns the global Symbol for a module-level symbol name. +func (g *GlobalSummary) LookupSymbol(name string) (Symbol, bool) { + id, ok := g.symIntern[name] + return id, ok +} + +// SymbolName returns the text of a global Symbol. +func (g *GlobalSummary) SymbolName(sym Symbol) string { + return g.symStrings[sym] +} + +// Name returns the text of a global Name. +func (g *GlobalSummary) Name(n Name) string { + return g.nameStrings[n] +} + +// ── enumeration ─────────────────────────────────────────────────────────────── + +// Ifaces returns all interface type symbols with method information. +// The returned slice is owned by GlobalSummary and must not be modified. +func (g *GlobalSummary) Ifaces() []Symbol { return g.interfaces } + +// ── lazy per-type queries ───────────────────────────────────────────────────── + +// MethodSlots returns the ABI method slots for concrete type typ. +func (g *GlobalSummary) MethodSlots(typ Symbol) []MethodSlot { + pm, tab, li := g.ownerData(typ) + return g.translateSlots(tab, pm, li) +} + +// IfaceMethods returns the method set for interface iface. +func (g *GlobalSummary) IfaceMethods(iface Symbol) []MethodSig { + pm, tab, li := g.ownerData(iface) + return g.translateSigs(tab, pm, li) +} + +// ── lazy edge queries ───────────────────────────────────────────────────────── + +// OrdinaryEdges returns plain reachability targets from sym (global Symbols). +func (g *GlobalSummary) OrdinaryEdges(sym Symbol) []Symbol { + pm, tab, li := g.ownerData(sym) + edges := pm.ordinaryEdges(li) + var out []Symbol + for _, dst := range edges { + out = append(out, tab[dst]) + } + return out +} + +// FuncDemands returns the method/interface/reflection demands emitted by sym. +// Records are translated to the global symbol and name spaces on demand. +func (g *GlobalSummary) FuncDemands(sym Symbol) []FuncDemand { + pm, tab, li := g.ownerData(sym) + return g.translateFuncDemands(tab, pm, li) +} + +// TypeChildren returns child type symbols for typ (global Symbols). +func (g *GlobalSummary) TypeChildren(typ Symbol) []Symbol { + pm, tab, li := g.ownerData(typ) + local := pm.typeChildren(li) + if len(local) == 0 { + return nil + } + out := make([]Symbol, len(local)) + for i, c := range local { + out[i] = tab[c] + } + return out +} diff --git a/internal/meta/global_test.go b/internal/meta/global_test.go new file mode 100644 index 0000000000..fd47613b29 --- /dev/null +++ b/internal/meta/global_test.go @@ -0,0 +1,285 @@ +package meta_test + +import ( + "testing" + + "github.com/goplus/llgo/internal/meta" +) + +// buildPkgMain builds a "main" package that references a symbol from "runtime" +// and converts a type to an interface defined locally. +func buildPkgMain(t *testing.T) *meta.PackageMeta { + t.Helper() + b := meta.NewBuilder() + + main := b.Sym("main.main") + allocZ := b.Sym("runtime.AllocZ") // defined in runtime, referenced here + myType := b.Sym("*main.Stringer") // defined here + reader := b.Sym("main.Reader") // interface defined here + readT := b.Sym("_llgo_func$Read") + + // main calls runtime.AllocZ, converts *Stringer to Reader, calls Reader.Read + b.AddOrdinaryEdge(main, allocZ) + b.AddIfaceUse(main, myType) + b.AddIfaceMethodUse(main, reader, 0) // Reader.Read = index 0 + b.AddNamedMethodUse(main, "Close") + b.MarkReflect(main) + + // Reader interface: { Read } + b.AddIfaceMethod(reader, "Read", readT) + + // *Stringer concrete type: slot 0 = Read + rifn := b.Sym("(*Stringer).Read$ifn") + rtfn := b.Sym("(*Stringer).Read$tfn") + b.AddMethodSlot(myType, "Read", readT, rifn, rtfn) + + pm, err := b.Build() + if err != nil { + t.Fatalf("build main: %v", err) + } + return pm +} + +// buildPkgRuntime builds a "runtime" package that defines AllocZ. +func buildPkgRuntime(t *testing.T) *meta.PackageMeta { + t.Helper() + b := meta.NewBuilder() + + allocZ := b.Sym("runtime.AllocZ") // defined here, with a body edge + mallocgc := b.Sym("runtime.mallocgc") + b.AddOrdinaryEdge(allocZ, mallocgc) + + pm, err := b.Build() + if err != nil { + t.Fatalf("build runtime: %v", err) + } + return pm +} + +func TestGlobalSummaryMerge(t *testing.T) { + mainPkg := buildPkgMain(t) + rtPkg := buildPkgRuntime(t) + defer mainPkg.Close() + defer rtPkg.Close() + + g, err := meta.NewGlobalSummary([]*meta.PackageMeta{mainPkg, rtPkg}) + if err != nil { + t.Fatalf("NewGlobalSummary: %v", err) + } + + sym := func(name string) meta.Symbol { + s, ok := g.LookupSymbol(name) + if !ok { + t.Fatalf("LookupSymbol(%q) not found", name) + } + return s + } + + main := sym("main.main") + allocZ := sym("runtime.AllocZ") + mallocgc := sym("runtime.mallocgc") + myType := sym("*main.Stringer") + reader := sym("main.Reader") + if got := g.SymbolName(main); got != "main.main" { + t.Fatalf("SymbolName(main) = %q, want main.main", got) + } + + // ── lazy OrdinaryEdges: main → runtime.AllocZ (cross-package) ────────────── + mainEdges := g.OrdinaryEdges(main) + if len(mainEdges) != 1 || mainEdges[0] != allocZ { + t.Errorf("OrdinaryEdges(main) = %v, want [runtime.AllocZ=%d]", mainEdges, allocZ) + } + + // ── allocZ's edges come from the runtime package (owner) ─────────────────── + azEdges := g.OrdinaryEdges(allocZ) + if len(azEdges) != 1 || azEdges[0] != mallocgc { + t.Errorf("OrdinaryEdges(allocZ) = %v, want [runtime.mallocgc=%d]", azEdges, mallocgc) + } + + // ── FuncDemands: all function-level facts share one globalized query ──────── + demands := g.FuncDemands(main) + if len(demands) != 4 { + t.Fatalf("FuncDemands(main): got %d, want 4", len(demands)) + } + var ifaceMethod meta.FuncDemand + seenUseIface, seenIfaceMethod, seenNamed, seenReflect := false, false, false, false + for _, demand := range demands { + switch demand.Kind { + case meta.DemandUseIface: + if demand.Target != myType { + t.Errorf("UseIface target = %d, want *Stringer=%d", demand.Target, myType) + } + seenUseIface = true + case meta.DemandIfaceMethod: + if demand.Target != reader { + t.Errorf("IfaceMethod target = %d, want reader=%d", demand.Target, reader) + } + if g.Name(demand.Sig.Name) != "Read" { + t.Errorf("IfaceMethod signature name = %q, want Read", g.Name(demand.Sig.Name)) + } + ifaceMethod = demand + seenIfaceMethod = true + case meta.DemandNamedMethod: + if got := g.Name(demand.MethodName); got != "Close" { + t.Errorf("NamedMethod name = %q, want Close", got) + } + seenNamed = true + case meta.DemandReflectMethod: + seenReflect = true + default: + t.Errorf("unexpected demand kind %d", demand.Kind) + } + } + if !seenUseIface || !seenIfaceMethod || !seenNamed || !seenReflect { + t.Errorf("FuncDemands missing kind: useIface=%t ifaceMethod=%t named=%t reflect=%t", seenUseIface, seenIfaceMethod, seenNamed, seenReflect) + } + + // ── MethodSlots: *Stringer has Read, name interned globally ──────────────── + slots := g.MethodSlots(myType) + if len(slots) != 1 { + t.Fatalf("MethodSlots(myType): got %d, want 1", len(slots)) + } + if g.Name(slots[0].Name) != "Read" { + t.Errorf("slot name = %q, want \"Read\"", g.Name(slots[0].Name)) + } + // the method name "Read" must intern to the SAME global Name in both the + // interface sig and the concrete slot, so DCE can match them. + if slots[0].Name != ifaceMethod.Sig.Name { + t.Errorf("method name not unified: slot=%d demand=%d", slots[0].Name, ifaceMethod.Sig.Name) + } + + // ── enumeration ──────────────────────────────────────────────────────────── + if len(g.Ifaces()) != 1 || g.Ifaces()[0] != reader { + t.Errorf("Ifaces() = %v, want [reader=%d]", g.Ifaces(), reader) + } + if len(g.MethodSlots(myType)) == 0 { + t.Errorf("MethodSlots(myType) = empty, want non-empty") + } +} + +// TestGlobalSummaryLinkonce verifies first-wins for a symbol defined (with +// facts) in two packages — a linkonce type descriptor. +func TestGlobalSummaryLinkonce(t *testing.T) { + build := func() *meta.PackageMeta { + b := meta.NewBuilder() + typ := b.Sym("*shared.Foo") + child := b.Sym("shared.Bar") + b.AddTypeChild(typ, child) + mt := b.Sym("_llgo_func$M") + b.AddMethodSlot(typ, "M", mt, b.Sym("ifn"), b.Sym("tfn")) + pm, err := b.Build() + if err != nil { + t.Fatal(err) + } + return pm + } + a, bp := build(), build() + defer a.Close() + defer bp.Close() + + g, err := meta.NewGlobalSummary([]*meta.PackageMeta{a, bp}) + if err != nil { + t.Fatalf("NewGlobalSummary: %v", err) + } + + foo, _ := g.LookupSymbol("*shared.Foo") + + // only one MethodInfo entry survives (first-wins), no duplicate concrete type + if got := len(g.MethodSlots(foo)); got != 1 { + t.Errorf("MethodSlots(foo) len = %d, want 1 (first-wins)", got) + } + if got := len(g.MethodSlots(foo)); got != 1 { + t.Errorf("MethodSlots(foo) len = %d, want 1", got) + } + // TypeChildren resolves through the owner + if got := len(g.TypeChildren(foo)); got != 1 { + t.Errorf("TypeChildren(foo) len = %d, want 1", got) + } +} + +func TestGlobalSummaryOwnerPrefersInterfaceInfoOverOrdinaryEdges(t *testing.T) { + descriptorPkg := func() *meta.PackageMeta { + b := meta.NewBuilder() + iface := b.Sym("_llgo_io.Reader") + dep := b.Sym("runtime.descriptor") + b.AddOrdinaryEdge(iface, dep) + pm, err := b.Build() + if err != nil { + t.Fatal(err) + } + return pm + }() + defPkg := func() *meta.PackageMeta { + b := meta.NewBuilder() + iface := b.Sym("_llgo_io.Reader") + readT := b.Sym("_llgo_func$Read") + b.AddIfaceMethod(iface, "Read", readT) + pm, err := b.Build() + if err != nil { + t.Fatal(err) + } + return pm + }() + defer descriptorPkg.Close() + defer defPkg.Close() + + g, err := meta.NewGlobalSummary([]*meta.PackageMeta{descriptorPkg, defPkg}) + if err != nil { + t.Fatalf("NewGlobalSummary: %v", err) + } + + iface, ok := g.LookupSymbol("_llgo_io.Reader") + if !ok { + t.Fatal("LookupSymbol(_llgo_io.Reader) not found") + } + methods := g.IfaceMethods(iface) + if len(methods) != 1 { + t.Fatalf("IfaceMethods(_llgo_io.Reader) len = %d, want 1", len(methods)) + } + if got := g.Name(methods[0].Name); got != "Read" { + t.Fatalf("IfaceMethods(_llgo_io.Reader)[0].Name = %q, want Read", got) + } +} + +func TestGlobalSummaryOwnerPrefersFuncDemandOverOrdinaryEdges(t *testing.T) { + refPkg := func() *meta.PackageMeta { + b := meta.NewBuilder() + fn := b.Sym("pkg.use") + b.AddOrdinaryEdge(fn, b.Sym("pkg.stale")) + pm, err := b.Build() + if err != nil { + t.Fatal(err) + } + return pm + }() + defPkg := func() *meta.PackageMeta { + b := meta.NewBuilder() + fn := b.Sym("pkg.use") + b.AddOrdinaryEdge(fn, b.Sym("pkg.live")) + b.AddIfaceUse(fn, b.Sym("pkg.T")) + pm, err := b.Build() + if err != nil { + t.Fatal(err) + } + return pm + }() + defer refPkg.Close() + defer defPkg.Close() + + g, err := meta.NewGlobalSummary([]*meta.PackageMeta{refPkg, defPkg}) + if err != nil { + t.Fatal(err) + } + fn, _ := g.LookupSymbol("pkg.use") + live, _ := g.LookupSymbol("pkg.live") + typ, _ := g.LookupSymbol("pkg.T") + + edges := g.OrdinaryEdges(fn) + if len(edges) != 1 || edges[0] != live { + t.Fatalf("OrdinaryEdges(pkg.use) = %v, want [%d]", edges, live) + } + demands := g.FuncDemands(fn) + if len(demands) != 1 || demands[0].Kind != meta.DemandUseIface || demands[0].Target != typ { + t.Fatalf("FuncDemands(pkg.use) = %+v, want UseIface(%d)", demands, typ) + } +} diff --git a/internal/meta/meta.go b/internal/meta/meta.go new file mode 100644 index 0000000000..13970e5b74 --- /dev/null +++ b/internal/meta/meta.go @@ -0,0 +1,345 @@ +package meta + +import ( + "encoding/binary" + "fmt" + "io" + "os" + "syscall" + "unsafe" +) + +// nameRef identifies a byte range in the package-local string table. +type nameRef struct { + Off uint32 + Len uint32 +} + +// version is the compatibility boundary for the on-disk layout. +const ( + magic = "LLPM" + version = 1 +) + +// Section IDs are also indexes into the header's section-offset array. +// Reordering or changing their wire representation is a format change. +const ( + secStringTable = iota + secSymbols + secOrdinaryEdges + secFuncDemand + secTypeChildren + secMethodInfo + secIfaceInfo + numSections +) + +// headerSize = magic(4) + version(4) + sectionOffsets(numSections*4) +const headerSize = 4 + 4 + numSections*4 + +// PackageMeta is a read-only, zero-copy view of one package's metadata. +// Its backing bytes are either owned Go memory from Builder.Build or a +// read-only mapping created by Open. Package-local lookup helpers return +// strings and slices that alias those bytes. +// +// The version-1 wire format is below. All integers are little-endian uint32 +// values. Header offsets are absolute byte offsets from the beginning of the +// file. Symbol values are indexes in this package's Symbols section. +// +// Header (36 bytes) +// [0:4] magic: "LLPM" +// [4:8] version: 1 +// [8:36] section offsets, in this exact order: +// StringTable, Symbols, OrdinaryEdges, FuncDemand, +// TypeChildren, MethodInfo, InterfaceInfo +// +// StringTable +// starts at byte 36 +// concatenated string bytes, followed by zero padding to a 4-byte boundary +// +// Symbols +// nsyms +// records[nsyms]: {nameOff, nameLen, reserved} // 12 bytes +// +// OrdinaryEdges: CSR +// nsyms +// offsets[nsyms+1] +// data[]: Symbol // 4 bytes +// +// FuncDemand: CSR +// nsyms +// offsets[nsyms+1] +// data[]: {kind, target, extra} // 12 bytes +// +// TypeChildren: CSR +// nsyms +// offsets[nsyms+1] +// data[]: Symbol // 4 bytes +// +// MethodInfo: CSR +// nsyms +// offsets[nsyms+1] +// data[]: {nameOff, nameLen, mtype, ifn, tfn} // 20 bytes +// +// InterfaceInfo: CSR +// nsyms +// offsets[nsyms+1] +// data[]: {nameOff, nameLen, mtype} // 12 bytes +// +// Every CSR offsets entry is a record index into that section's data array, +// not a byte offset. Each CSR nsyms must equal Symbols.nsyms. Every nameOff is +// relative to the start of StringTable; nameLen excludes alignment padding. +// The Symbols reserved field is written as zero and ignored when reading. +// Section sizes are derived from adjacent header offsets, and InterfaceInfo +// extends to the end of the file. Every section starts on a 4-byte boundary. +type PackageMeta struct { + raw []byte + mmap bool // whether Close must unmap raw + + nsyms uint32 + + // cached section start offsets (parsed once from header) + strOff uint32 + symOff uint32 + ordinaryOff uint32 + demandOff uint32 + childOff uint32 + methodOff uint32 + ifaceOff uint32 +} + +// localFuncDemand is the package-local wire representation of a function +// demand. Its layout (Kind@0, Target@4, Extra@8, size 12) must match the file +// format so funcDemands can return a zero-copy view of the backing bytes. +type localFuncDemand struct { + Kind DemandKind + // Target is a Symbol for DemandUseIface and DemandIfaceMethod, a string-table + // offset for DemandNamedMethod, and zero for DemandReflectMethod. + Target uint32 + // Extra is an interface-method index for DemandIfaceMethod, a string length + // for DemandNamedMethod, and zero for the other kinds. + Extra uint32 +} + +// Compile-time assertion: localFuncDemand must be exactly 12 bytes. If either const +// goes negative the build fails, pinning the wire/struct layout match. +const ( + _ = uint(unsafe.Sizeof(localFuncDemand{}) - 12) + _ = uint(12 - unsafe.Sizeof(localFuncDemand{})) +) + +// localMethodSlot is the package-local wire representation of an ABI method +// slot. Its layout (nameRef@0..8, MType@8, IFn@12, TFn@16, size 20) must match +// the file format for zero-copy reads. +type localMethodSlot struct { + Name nameRef // bare if exported; package-qualified if unexported + MType Symbol + IFn Symbol + TFn Symbol +} + +// localMethodSig is the package-local wire representation of an interface +// method signature. Its layout (nameRef@0..8, MType@8, size 12) must match the +// file format for zero-copy reads. +type localMethodSig struct { + Name nameRef // bare if exported; package-qualified if unexported + MType Symbol +} + +// Compile-time assertions pinning the wire/struct layout for zero-copy reads. +// If a struct's size drifts, one of these uint consts goes negative and the +// build fails. +const ( + _ = uint(unsafe.Sizeof(localMethodSlot{}) - 20) + _ = uint(20 - unsafe.Sizeof(localMethodSlot{})) + _ = uint(unsafe.Sizeof(localMethodSig{}) - 12) + _ = uint(12 - unsafe.Sizeof(localMethodSig{})) +) + +// Open maps path read-only and returns a PackageMeta backed by that mapping. +// The caller must call Close. Values returned by package-local lookup helpers +// must not be used after Close. +func Open(path string) (*PackageMeta, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() + + fi, err := f.Stat() + if err != nil { + return nil, err + } + size := int(fi.Size()) + + raw, err := syscall.Mmap(int(f.Fd()), 0, size, syscall.PROT_READ, syscall.MAP_SHARED) + if err != nil { + return nil, fmt.Errorf("meta: mmap %s: %w", path, err) + } + + pm, err := newPackageMeta(raw) + if err != nil { + _ = syscall.Munmap(raw) + return nil, err + } + pm.mmap = true + return pm, nil +} + +// WriteTo writes the complete metadata file in its binary wire format. +func (pm *PackageMeta) WriteTo(w io.Writer) (int64, error) { + n, err := w.Write(pm.raw) + if err == nil && n != len(pm.raw) { + err = io.ErrShortWrite + } + return int64(n), err +} + +// Close releases the mapping owned by a PackageMeta returned from Open. +// It is a no-op for PackageMeta values returned from Builder.Build. +func (pm *PackageMeta) Close() error { + if pm.mmap && pm.raw != nil { + err := syscall.Munmap(pm.raw) + pm.raw = nil + return err + } + return nil +} + +// symbolName returns the name of package-local sym as a string that aliases the +// backing bytes. For a PackageMeta returned from Open, the string must not be +// used after Close. +func (pm *PackageMeta) symbolName(sym Symbol) string { + const recSize = 12 + base := pm.symOff + 4 + uint32(sym)*recSize + nameOff := binary.LittleEndian.Uint32(pm.raw[base+0:]) + nameLen := binary.LittleEndian.Uint32(pm.raw[base+4:]) + return unsafe.String(&pm.raw[pm.strOff+nameOff], int(nameLen)) +} + +// nameString returns the string referenced by a valid package-local ref. The +// string aliases the backing bytes and, for a PackageMeta returned from Open, +// must not be used after Close. +func (pm *PackageMeta) nameString(ref nameRef) string { + return unsafe.String(&pm.raw[pm.strOff+ref.Off], int(ref.Len)) +} + +// nordinaryEdge returns the number of ordinary reachability edges from sym. +func (pm *PackageMeta) nordinaryEdge(sym Symbol) uint32 { + s, e := pm.csrRange(pm.ordinaryOff, sym) + return e - s +} + +// ordinaryEdges returns the package-local ordinary-edge targets from sym. The +// returned slice aliases the backing bytes. +func (pm *PackageMeta) ordinaryEdges(sym Symbol) []Symbol { + return csrSlice[Symbol](pm, pm.ordinaryOff, sym, 4) +} + +// nfuncDemand returns the number of function demands owned by sym. +func (pm *PackageMeta) nfuncDemand(sym Symbol) uint32 { + s, e := pm.csrRange(pm.demandOff, sym) + return e - s +} + +// funcDemands returns the package-local function-demand records owned by sym. +// The returned slice aliases the backing bytes. +func (pm *PackageMeta) funcDemands(sym Symbol) []localFuncDemand { + return csrSlice[localFuncDemand](pm, pm.demandOff, sym, 12) +} + +// ntypeChild returns the number of type children recorded for sym. +func (pm *PackageMeta) ntypeChild(sym Symbol) uint32 { + s, e := pm.csrRange(pm.childOff, sym) + return e - s +} + +// typeChildren returns the package-local child type Symbols recorded for sym. +// The returned slice aliases the backing bytes. +func (pm *PackageMeta) typeChildren(sym Symbol) []Symbol { + return csrSlice[Symbol](pm, pm.childOff, sym, 4) +} + +// nmethodSlot returns the number of ABI method slots recorded for sym. +func (pm *PackageMeta) nmethodSlot(sym Symbol) uint32 { + s, e := pm.csrRange(pm.methodOff, sym) + return e - s +} + +// methodSlots returns the package-local ABI method slots recorded for sym. The +// returned slice aliases the backing bytes. +func (pm *PackageMeta) methodSlots(sym Symbol) []localMethodSlot { + return csrSlice[localMethodSlot](pm, pm.methodOff, sym, 20) +} + +// nifaceMethod returns the number of interface method signatures recorded for +// sym. +func (pm *PackageMeta) nifaceMethod(sym Symbol) uint32 { + s, e := pm.csrRange(pm.ifaceOff, sym) + return e - s +} + +// ifaceMethods returns the package-local interface method signatures recorded +// for sym. The returned slice aliases the backing bytes. +func (pm *PackageMeta) ifaceMethods(sym Symbol) []localMethodSig { + return csrSlice[localMethodSig](pm, pm.ifaceOff, sym, 12) +} + +// hasOrdinaryEdges reports whether sym owns any ordinary reachability edges. +func (pm *PackageMeta) hasOrdinaryEdges(sym Symbol) bool { + return pm.nordinaryEdge(sym) > 0 +} + +// hasFuncDemand reports whether sym owns any function demand. +func (pm *PackageMeta) hasFuncDemand(sym Symbol) bool { + return pm.nfuncDemand(sym) > 0 +} + +// ── internal helpers ────────────────────────────────────────────────────────── + +// newPackageMeta checks the magic and version, then decodes the section offsets +// from the fixed header. +func newPackageMeta(raw []byte) (*PackageMeta, error) { + if string(raw[0:4]) != magic { + return nil, fmt.Errorf("meta: bad magic %q", raw[0:4]) + } + ver := binary.LittleEndian.Uint32(raw[4:8]) + if ver != version { + return nil, fmt.Errorf("meta: unsupported version %d", ver) + } + + pm := &PackageMeta{raw: raw} + pm.strOff = binary.LittleEndian.Uint32(raw[8+secStringTable*4:]) + pm.symOff = binary.LittleEndian.Uint32(raw[8+secSymbols*4:]) + pm.ordinaryOff = binary.LittleEndian.Uint32(raw[8+secOrdinaryEdges*4:]) + pm.demandOff = binary.LittleEndian.Uint32(raw[8+secFuncDemand*4:]) + pm.childOff = binary.LittleEndian.Uint32(raw[8+secTypeChildren*4:]) + pm.methodOff = binary.LittleEndian.Uint32(raw[8+secMethodInfo*4:]) + pm.ifaceOff = binary.LittleEndian.Uint32(raw[8+secIfaceInfo*4:]) + + // read nsyms from Symbols section header + pm.nsyms = binary.LittleEndian.Uint32(raw[pm.symOff:]) + return pm, nil +} + +// csrSlice returns the records for package-local sym from a CSR section, or nil +// if it has no records. The returned slice aliases pm.raw. recSize must match +// unsafe.Sizeof(T). +func csrSlice[T any](pm *PackageMeta, sectionOff uint32, sym Symbol, recSize uintptr) []T { + start, end := pm.csrRange(sectionOff, sym) + if start == end { + return nil + } + dataBase := sectionOff + 4 + (pm.nsyms+1)*4 + p := (*T)(unsafe.Pointer(&pm.raw[dataBase+uint32(uintptr(start)*recSize)])) + return unsafe.Slice(p, end-start) +} + +// csrRange returns the half-open data-record range for package-local sym. +// Callers must pass an in-range sym and a section offset decoded from pm. +func (pm *PackageMeta) csrRange(sectionOff uint32, sym Symbol) (start, end uint32) { + offsetsBase := sectionOff + 4 // skip nsyms u32 + start = binary.LittleEndian.Uint32(pm.raw[offsetsBase+uint32(sym)*4:]) + end = binary.LittleEndian.Uint32(pm.raw[offsetsBase+(uint32(sym)+1)*4:]) + return +} diff --git a/internal/meta/meta_test.go b/internal/meta/meta_test.go new file mode 100644 index 0000000000..90438f5cb1 --- /dev/null +++ b/internal/meta/meta_test.go @@ -0,0 +1,327 @@ +package meta + +import ( + "encoding/binary" + "os" + "path/filepath" + "strings" + "testing" + "unsafe" +) + +// TestWireLayout verifies the zero-copy structs match their on-disk byte layout: +// correct total size and field offsets. If these drift, unsafe reinterpretation +// of mmap bytes would silently corrupt — so we assert them explicitly. +func TestWireLayout(t *testing.T) { + if got := unsafe.Sizeof(localFuncDemand{}); got != 12 { + t.Errorf("sizeof(localFuncDemand) = %d, want 12", got) + } + if got := unsafe.Offsetof(localFuncDemand{}.Kind); got != 0 { + t.Errorf("localFuncDemand.Kind offset = %d, want 0", got) + } + if got := unsafe.Offsetof(localFuncDemand{}.Target); got != 4 { + t.Errorf("localFuncDemand.Target offset = %d, want 4", got) + } + if got := unsafe.Offsetof(localFuncDemand{}.Extra); got != 8 { + t.Errorf("localFuncDemand.Extra offset = %d, want 8", got) + } + + if got := unsafe.Sizeof(localMethodSlot{}); got != 20 { + t.Errorf("sizeof(localMethodSlot) = %d, want 20", got) + } + if got := unsafe.Offsetof(localMethodSlot{}.MType); got != 8 { + t.Errorf("localMethodSlot.MType offset = %d, want 8", got) + } + if got := unsafe.Offsetof(localMethodSlot{}.TFn); got != 16 { + t.Errorf("localMethodSlot.TFn offset = %d, want 16", got) + } + + if got := unsafe.Sizeof(localMethodSig{}); got != 12 { + t.Errorf("sizeof(localMethodSig) = %d, want 12", got) + } + if got := unsafe.Offsetof(localMethodSig{}.MType); got != 8 { + t.Errorf("localMethodSig.MType offset = %d, want 8", got) + } +} + +// TestTypeChildrenAlignment uses symbol names of irregular total length so the +// string table is unlikely to land on a 4-byte boundary on its own, verifying +// that stringTable padding keeps the zero-copy TypeChildren view correctly aligned. +func TestTypeChildrenAlignment(t *testing.T) { + for _, pad := range []string{"a", "ab", "abc", "abcd", "abcde"} { + b := NewBuilder() + // a symbol whose name length varies, to shift the string table size + b.Sym("x." + pad) + parent := b.Sym("*pkg.Parent") + c0 := b.Sym("pkg.C0") + c1 := b.Sym("pkg.C1") + c2 := b.Sym("pkg.C2") + b.AddTypeChild(parent, c0) + b.AddTypeChild(parent, c1) + b.AddTypeChild(parent, c2) + + pm, err := b.Build() + if err != nil { + t.Fatalf("pad=%q build: %v", pad, err) + } + got := pm.typeChildren(parent) + want := []Symbol{c0, c1, c2} + if len(got) != len(want) { + t.Fatalf("pad=%q TypeChildren len = %d, want %d", pad, len(got), len(want)) + } + for i := range want { + if got[i] != want[i] { + t.Errorf("pad=%q child[%d] = %d, want %d", pad, i, got[i], want[i]) + } + } + } +} + +// TestRoundTrip builds a small package summary, serializes it, then reads it +// back and verifies every query returns the expected values. +func TestRoundTrip(t *testing.T) { + b := NewBuilder() + + // symbols + main := b.Sym("main.main") + helper := b.Sym("main.helper") + allocZ := b.Sym("runtime.AllocZ") + myType := b.Sym("*_llgo_main.MyStruct") + myField := b.Sym("_llgo_main.Inner") + myIface := b.Sym("_llgo_iface$Reader") + mtype := b.Sym("_llgo_func$Read") + ifn := b.Sym("(*MyStruct).Read$ifn") + tfn := b.Sym("(*MyStruct).Read$tfn") + + // ordinary edges + b.AddOrdinaryEdge(main, helper) + b.AddOrdinaryEdge(main, allocZ) + + // interface conversion + b.AddIfaceUse(main, myType) + + // interface method call: Reader.Read is method index 0 + b.AddIfaceMethodUse(main, myIface, 0) + + // named method call + b.AddNamedMethodUse(helper, "ServeHTTP") + + // TypeChildren: *MyStruct contains Inner + b.AddTypeChild(myType, myField) + + // MethodInfo for *MyStruct: slot 0 = Read + b.AddMethodSlot(myType, "Read", mtype, ifn, tfn) + + // InterfaceInfo for Reader: method 0 = Read + b.AddIfaceMethod(myIface, "Read", mtype) + + // reflect + b.MarkReflect(helper) + + // build + pm, err := b.Build() + if err != nil { + t.Fatalf("Build: %v", err) + } + + // ── verify Symbols ──────────────────────────────────────────────────────── + + checkName := func(sym Symbol, want string) { + t.Helper() + if got := pm.symbolName(sym); got != want { + t.Errorf("SymbolName(%d) = %q, want %q", sym, got, want) + } + } + checkName(main, "main.main") + checkName(helper, "main.helper") + checkName(allocZ, "runtime.AllocZ") + checkName(myType, "*_llgo_main.MyStruct") + + // ── verify OrdinaryEdges / FuncDemand ───────────────────────────────────── + + mainEdges := pm.ordinaryEdges(main) + if len(mainEdges) != 2 { + t.Fatalf("OrdinaryEdges(main): got %d edges, want 2", len(mainEdges)) + } + if mainEdges[0] != helper { + t.Errorf("ordinary[0] = %d, want helper=%d", mainEdges[0], helper) + } + if mainEdges[1] != allocZ { + t.Errorf("ordinary[1] = %d, want allocZ=%d", mainEdges[1], allocZ) + } + + mainDemands := pm.funcDemands(main) + if len(mainDemands) != 2 { + t.Fatalf("FuncDemand(main): got %d demands, want 2", len(mainDemands)) + } + if d := mainDemands[0]; d.Kind != DemandUseIface || Symbol(d.Target) != myType { + t.Errorf("demand[0] = %+v, want {Kind:UseIface Target:%d}", d, myType) + } + if d := mainDemands[1]; d.Kind != DemandIfaceMethod || Symbol(d.Target) != myIface || d.Extra != 0 { + t.Errorf("demand[1] = %+v, want {Kind:IfaceMethod Target:%d Extra:0}", d, myIface) + } + + helperDemands := pm.funcDemands(helper) + if len(helperDemands) != 2 { + t.Fatalf("FuncDemand(helper): got %d, want 2", len(helperDemands)) + } + if d := helperDemands[0]; d.Kind != DemandNamedMethod { + t.Errorf("helper demand[0].Kind = %d, want NamedMethod", d.Kind) + } + // For UseNamedMethod, target=nameRef.Off and extra=nameRef.Len. + gotName := pm.nameString(nameRef{Off: helperDemands[0].Target, Len: helperDemands[0].Extra}) + if gotName != "ServeHTTP" { + t.Errorf("UseNamedMethod target name = %q, want \"ServeHTTP\"", gotName) + } + if d := helperDemands[1]; d.Kind != DemandReflectMethod { + t.Errorf("helper demand[1].Kind = %d, want ReflectMethod", d.Kind) + } + if got := pm.ordinaryEdges(allocZ); len(got) != 0 { + t.Errorf("OrdinaryEdges(allocZ): got %d, want 0", len(got)) + } + + // ── verify TypeChildren ─────────────────────────────────────────────────── + + children := pm.typeChildren(myType) + if len(children) != 1 || children[0] != myField { + t.Errorf("TypeChildren(myType) = %v, want [%d]", children, myField) + } + if pm.typeChildren(main) != nil { + t.Errorf("TypeChildren(main) should be nil") + } + if pm.ntypeChild(myType) == 0 { + t.Errorf("NTypeChild(myType) = 0, want >0") + } + if pm.ntypeChild(main) > 0 { + t.Errorf("NTypeChild(main) > 0, want 0") + } + + // ── verify MethodSlots ──────────────────────────────────────────────────── + + slots := pm.methodSlots(myType) + if len(slots) != 1 { + t.Fatalf("MethodSlots(myType): got %d, want 1", len(slots)) + } + slot := slots[0] + if pm.nameString(slot.Name) != "Read" { + t.Errorf("slot.Name = %q, want \"Read\"", pm.nameString(slot.Name)) + } + if slot.MType != mtype || slot.IFn != ifn || slot.TFn != tfn { + t.Errorf("slot = %+v, unexpected symbols", slot) + } + if len(pm.methodSlots(myType)) == 0 { + t.Errorf("MethodSlots(myType) = empty, want non-empty") + } + + // ── verify IfaceMethods ─────────────────────────────────────────────────── + + sigs := pm.ifaceMethods(myIface) + if len(sigs) != 1 { + t.Fatalf("IfaceMethods(myIface): got %d, want 1", len(sigs)) + } + if pm.nameString(sigs[0].Name) != "Read" { + t.Errorf("iface method name = %q, want \"Read\"", pm.nameString(sigs[0].Name)) + } + if pm.nifaceMethod(myIface) == 0 { + t.Errorf("NIfaceMethod(myIface) = 0, want >0") + } + if pm.nifaceMethod(main) > 0 { + t.Errorf("NIfaceMethod(main) > 0, want 0") + } + +} + +// TestRoundTripFile writes the meta to disk and reads it back via Open. +func TestRoundTripFile(t *testing.T) { + b := NewBuilder() + fn := b.Sym("pkg.Fn") + dep := b.Sym("runtime.X") + b.AddOrdinaryEdge(fn, dep) + + pm, err := b.Build() + if err != nil { + t.Fatalf("Build: %v", err) + } + + path := t.TempDir() + "/test.meta" + f, err := os.Create(path) + if err != nil { + t.Fatalf("create: %v", err) + } + if _, err := pm.WriteTo(f); err != nil { + f.Close() + t.Fatalf("write: %v", err) + } + if err := f.Close(); err != nil { + t.Fatalf("close: %v", err) + } + + pm2, err := Open(path) + if err != nil { + t.Fatalf("Open: %v", err) + } + defer pm2.Close() + + if got := pm2.symbolName(fn); got != "pkg.Fn" { + t.Errorf("SymbolName after file round-trip = %q, want \"pkg.Fn\"", got) + } + edges := pm2.ordinaryEdges(fn) + if len(edges) != 1 || edges[0] != dep { + t.Errorf("OrdinaryEdges after file round-trip = %v", edges) + } +} + +func TestOpenErrors(t *testing.T) { + t.Run("open", func(t *testing.T) { + if _, err := Open(filepath.Join(t.TempDir(), "missing.meta")); err == nil { + t.Fatal("Open succeeded for a missing file") + } + }) + + t.Run("mmap", func(t *testing.T) { + path := filepath.Join(t.TempDir(), "empty.meta") + if err := os.WriteFile(path, nil, 0o644); err != nil { + t.Fatal(err) + } + if _, err := Open(path); err == nil || !strings.Contains(err.Error(), "meta: mmap") { + t.Fatalf("Open error = %v, want mmap error", err) + } + }) + + tests := []struct { + name string + raw func() []byte + want string + }{ + { + name: "magic", + raw: func() []byte { + raw := make([]byte, headerSize) + copy(raw, "NOPE") + return raw + }, + want: "meta: bad magic", + }, + { + name: "version", + raw: func() []byte { + raw := make([]byte, headerSize) + copy(raw, magic) + binary.LittleEndian.PutUint32(raw[4:8], version+1) + return raw + }, + want: "meta: unsupported version 2", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + path := filepath.Join(t.TempDir(), tt.name+".meta") + if err := os.WriteFile(path, tt.raw(), 0o644); err != nil { + t.Fatal(err) + } + if _, err := Open(path); err == nil || !strings.Contains(err.Error(), tt.want) { + t.Fatalf("Open error = %v, want %q", err, tt.want) + } + }) + } +} diff --git a/internal/meta/types.go b/internal/meta/types.go new file mode 100644 index 0000000000..62287af2ad --- /dev/null +++ b/internal/meta/types.go @@ -0,0 +1,51 @@ +// Package meta defines the binary format and in-memory view for LLGo package +// summary cache files (.meta). The format is designed for zero-copy access via +// mmap: the file layout is the memory layout. +package meta + +// Symbol is a symbol ID whose namespace is determined by the Builder, +// PackageMeta, or GlobalSummary that owns it. Builder and PackageMeta symbols +// are package-local; GlobalSummary symbols belong to its unified namespace. +type Symbol uint32 + +// Name is a whole-program method-name ID, in a namespace distinct from Symbol. +type Name uint32 + +// DemandKind identifies a function-level method/interface/reflection demand. +type DemandKind uint32 + +// FuncDemand kinds used in the FuncDemand section and global analysis API. +const ( + DemandUseIface DemandKind = 1 + DemandIfaceMethod DemandKind = 2 + DemandNamedMethod DemandKind = 3 + DemandReflectMethod DemandKind = 4 +) + +// MethodSlot is a method slot in the global namespace. +type MethodSlot struct { + Name Name + MType Symbol + IFn Symbol + TFn Symbol +} + +// MethodSig is an interface method signature in the global namespace. +type MethodSig struct { + Name Name + MType Symbol +} + +// FuncDemand is a function-level method/interface/reflection demand in the +// global namespace. Valid fields depend on Kind: +// +// - DemandUseIface: Target is the concrete type converted to an interface. +// - DemandIfaceMethod: Target is the interface and Sig is the demanded method. +// - DemandNamedMethod: MethodName is the constant MethodByName argument. +// - DemandReflectMethod: no additional fields are set. +type FuncDemand struct { + Kind DemandKind + Target Symbol + Sig MethodSig + MethodName Name +} diff --git a/ssa/abitype.go b/ssa/abitype.go index 0588a532d5..0c6c3594c5 100644 --- a/ssa/abitype.go +++ b/ssa/abitype.go @@ -341,6 +341,62 @@ func (b Builder) abiExtendedFields(t types.Type, name string, global llvm.Value) return } +func (b Builder) recordTypeChildren(parentName string, t types.Type) { + mb := b.Pkg.metaBuilder + if mb == nil { + return + } + parent := mb.Sym(parentName) + for _, child := range b.directTypeChildren(t) { + childName, _ := b.Prog.abi.TypeName(child) + mb.AddTypeChild(parent, mb.Sym(childName)) + } +} + +func (b Builder) directTypeChildren(t types.Type) []types.Type { + switch t := types.Unalias(t).(type) { + case *types.Basic: + return nil + case *types.Pointer: + return []types.Type{abi.PublicType(t.Elem())} + case *types.Chan: + return []types.Type{abi.PublicType(t.Elem())} + case *types.Slice: + return []types.Type{abi.PublicType(t.Elem())} + case *types.Array: + return []types.Type{abi.PublicType(t.Elem())} + case *types.Map: + return []types.Type{ + abi.PublicType(t.Key()), + abi.PublicType(t.Elem()), + } + case *types.Signature: + var children []types.Type + children = appendTupleTypeChildren(children, t.Params()) + children = appendTupleTypeChildren(children, t.Results()) + return children + case *types.Struct: + children := make([]types.Type, 0, t.NumFields()) + for i := 0; i < t.NumFields(); i++ { + children = append(children, abi.PublicType(t.Field(i).Type())) + } + return children + case *types.Named: + return b.directTypeChildren(t.Underlying()) + } + return nil +} + +func appendTupleTypeChildren(children []types.Type, tuple *types.Tuple) []types.Type { + if tuple == nil { + return children + } + for i := 0; i < tuple.Len(); i++ { + children = append(children, abi.PublicType(tuple.At(i).Type())) + } + return children +} + func (b Builder) abiUncommonPkg(t types.Type) (*types.Package, string) { retry: switch typ := types.Unalias(t).(type) { @@ -432,6 +488,7 @@ func (b Builder) abiUncommonMethods(t types.Type, mset *types.MethodSet) llvm.Va ft := prog.rtType("Method") n := mset.Len() fields := make([]llvm.Value, n) + typeName, _ := prog.abi.TypeName(t) pkg, _ := b.abiUncommonPkg(t) anonymous := pkg == nil if anonymous { @@ -441,11 +498,8 @@ func (b Builder) abiUncommonMethods(t types.Type, mset *types.MethodSet) llvm.Va m := mset.At(i) obj := m.Obj() mName := obj.Name() - abiName := mName - if !token.IsExported(mName) { - abiName = abi.FullName(obj.Pkg(), mName) - } - name := b.Str(abiName).impl + fullName := abiMethodName(obj) + name := b.Str(fullName).impl mSig := m.Type().(*types.Signature) var tfn, ifn llvm.Value tfnFn := b.abiMethodFunc(anonymous, pkg, mName, mSig) @@ -464,6 +518,10 @@ func (b Builder) abiUncommonMethods(t types.Type, mset *types.MethodSet) llvm.Va values = append(values, ifn) values = append(values, tfn) fields[i] = llvm.ConstNamedStruct(ft.ll, values) + if mb := b.Pkg.metaBuilder; mb != nil { + mtypeName, _ := prog.abi.TypeName(ftyp) + mb.AddMethodSlot(mb.Sym(typeName), fullName, mb.Sym(mtypeName), mb.Sym(ifn.Name()), mb.Sym(tfn.Name())) + } } return llvm.ConstArray(ft.ll, fields) } @@ -474,6 +532,14 @@ func funcType(prog Program, typ types.Type) types.Type { return ftyp.raw.Type.(*types.Struct).Field(0).Type() } +func abiMethodName(obj types.Object) string { + name := obj.Name() + if token.IsExported(name) { + return name + } + return abi.FullName(obj.Pkg(), name) +} + func methodExprSignature(sig *types.Signature) *types.Signature { recv := sig.Recv() if recv == nil { @@ -523,6 +589,7 @@ func (b Builder) abiType(t types.Type) Expr { if prog.patchType != nil { t = prog.patchType(t) } + b.recordTypeChildren(name, t) mset, hasUncommon := b.abiUncommonMethodSet(t) methodCount := 0 if mset != nil { @@ -555,6 +622,9 @@ func (b Builder) abiType(t types.Type) Expr { b.abiUncommonType(t, mset), b.abiUncommonMethods(t, mset), } + if pkg.metaBuilder != nil { + pkg.abiTypeWithUncommon[g.impl] = struct{}{} + } } g.impl.SetInitializer(llvm.ConstNamedStruct(g.impl.GlobalValueType(), fields)) g.impl.SetGlobalConstant(true) diff --git a/ssa/edges.go b/ssa/edges.go new file mode 100644 index 0000000000..b8eeab4666 --- /dev/null +++ b/ssa/edges.go @@ -0,0 +1,94 @@ +package ssa + +import ( + "github.com/goplus/llgo/internal/meta" + "github.com/xgo-dev/llvm" +) + +const abiTypeMethodTableOperand = 2 + +func extractOrdinaryEdges(builder *meta.Builder, mod llvm.Module, abiTypeWithUncommon map[llvm.Value]struct{}) { + for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) { + src := fn.Name() + if fn.IsDeclaration() { + continue + } + collector := ordinaryEdgeCollector{builder: builder, src: src} + for bb := fn.FirstBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) { + for instr := bb.FirstInstruction(); !instr.IsNil(); instr = llvm.NextInstruction(instr) { + collector.scanOperands(instr) + } + } + } + for global := mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) { + src := global.Name() + if global.IsDeclaration() { + continue + } + init := global.Initializer() + collector := ordinaryEdgeCollector{builder: builder, src: src} + if _, ok := abiTypeWithUncommon[global]; ok { + for i, n := 0, init.OperandsCount(); i < n; i++ { + if i != abiTypeMethodTableOperand { + collector.scan(init.Operand(i)) + } + } + } else { + collector.scan(init) + } + } +} + +type ordinaryEdgeCollector struct { + builder *meta.Builder + src string + seen map[llvm.Value]struct{} + addedDst map[string]struct{} // dedup (src, dst) pairs +} + +func (c *ordinaryEdgeCollector) scanOperands(v llvm.Value) { + for i, n := 0, v.OperandsCount(); i < n; i++ { + c.scan(v.Operand(i)) + } +} + +func (c *ordinaryEdgeCollector) scan(v llvm.Value) { + if name := namedModuleSymbol(v); name != "" { + c.add(name) + return + } + if v.IsAConstant().IsNil() { + return + } + if c.seen == nil { + c.seen = make(map[llvm.Value]struct{}) + } + if _, ok := c.seen[v]; ok { + return + } + c.seen[v] = struct{}{} + for i, n := 0, v.OperandsCount(); i < n; i++ { + c.scan(v.Operand(i)) + } +} + +func (c *ordinaryEdgeCollector) add(dst string) { + if dst == c.src { + return + } + if c.addedDst == nil { + c.addedDst = make(map[string]struct{}) + } + if _, ok := c.addedDst[dst]; ok { + return + } + c.addedDst[dst] = struct{}{} + c.builder.AddOrdinaryEdge(c.builder.Sym(c.src), c.builder.Sym(dst)) +} + +func namedModuleSymbol(v llvm.Value) string { + if !v.IsAFunction().IsNil() || !v.IsAGlobalVariable().IsNil() { + return v.Name() + } + return "" +} diff --git a/ssa/edges_test.go b/ssa/edges_test.go new file mode 100644 index 0000000000..9b9415c7da --- /dev/null +++ b/ssa/edges_test.go @@ -0,0 +1,158 @@ +package ssa + +import ( + "testing" + + "github.com/goplus/llgo/internal/meta" + "github.com/xgo-dev/llvm" +) + +func TestExtractOrdinaryEdgesFromFunctionAndGlobal(t *testing.T) { + ctx := llvm.NewContext() + defer ctx.Dispose() + mod := ctx.NewModule("ordinary") + defer mod.Dispose() + + voidTy := ctx.VoidType() + fnTy := llvm.FunctionType(voidTy, nil, false) + mainFn := llvm.AddFunction(mod, "pkg.main", fnTy) + helperFn := llvm.AddFunction(mod, "pkg.helper", fnTy) + + b := ctx.NewBuilder() + defer b.Dispose() + entry := ctx.AddBasicBlock(mainFn, "entry") + b.SetInsertPointAtEnd(entry) + b.CreateCall(fnTy, helperFn, nil, "") + b.CreateRetVoid() + + global := llvm.AddGlobal(mod, llvm.PointerType(fnTy, 0), "pkg.global") + global.SetInitializer(helperFn) + llvm.AddGlobal(mod, llvm.PointerType(fnTy, 0), "pkg.external") + + mb := meta.NewBuilder() + extractOrdinaryEdges(mb, mod, nil) + pm, _ := mb.Build() + + if !hasOrdinaryEdge(pm, "pkg.main", "pkg.helper") { + t.Fatalf("missing ordinary edge pkg.main -> pkg.helper") + } + if !hasOrdinaryEdge(pm, "pkg.global", "pkg.helper") { + t.Fatalf("missing ordinary edge pkg.global -> pkg.helper") + } + summary, err := meta.NewGlobalSummary([]*meta.PackageMeta{pm}) + if err != nil { + t.Fatal(err) + } + if _, ok := summary.LookupSymbol("pkg.external"); ok { + t.Fatal("external global declaration was recorded in metadata") + } +} + +func TestExtractOrdinaryEdgesUsesABITypeMarkers(t *testing.T) { + ctx := llvm.NewContext() + defer ctx.Dispose() + mod := ctx.NewModule("ordinary") + defer mod.Dispose() + + voidTy := ctx.VoidType() + fnTy := llvm.FunctionType(voidTy, nil, false) + ifn := llvm.AddFunction(mod, "pkg.(*T).M", fnTy) + tfn := llvm.AddFunction(mod, "pkg.T.M", fnTy) + + i8ptrTy := llvm.PointerType(ctx.Int8Type(), 0) + methodTy := ctx.StructCreateNamed("runtime/internal/runtime.Method") + methodTy.StructSetBody([]llvm.Type{i8ptrTy, i8ptrTy, llvm.PointerType(fnTy, 0), llvm.PointerType(fnTy, 0)}, false) + methods := llvm.ConstArray(methodTy, []llvm.Value{ + llvm.ConstNamedStruct(methodTy, []llvm.Value{ + llvm.ConstNull(i8ptrTy), + llvm.ConstNull(i8ptrTy), + ifn, + tfn, + }), + }) + + typeTy := ctx.StructCreateNamed("pkg.T.type") + typeTy.StructSetBody([]llvm.Type{i8ptrTy, i8ptrTy, methods.Type()}, false) + typeDesc := llvm.AddGlobal(mod, typeTy, "_llgo_pkg.T") + typeDesc.SetInitializer(llvm.ConstNamedStruct(typeTy, []llvm.Value{ + llvm.ConstNull(i8ptrTy), + llvm.ConstNull(i8ptrTy), + methods, + })) + unmarkedTypeDesc := llvm.AddGlobal(mod, typeTy, "_llgo_pkg.Unmarked") + unmarkedTypeDesc.SetInitializer(typeDesc.Initializer()) + + mb := meta.NewBuilder() + extractOrdinaryEdges(mb, mod, map[llvm.Value]struct{}{typeDesc: {}}) + pm, _ := mb.Build() + + if hasOrdinaryEdge(pm, "_llgo_pkg.T", "pkg.(*T).M") { + t.Fatalf("method table IFn was recorded as an ordinary edge") + } + if hasOrdinaryEdge(pm, "_llgo_pkg.T", "pkg.T.M") { + t.Fatalf("method table TFn was recorded as an ordinary edge") + } + if !hasOrdinaryEdge(pm, "_llgo_pkg.Unmarked", "pkg.(*T).M") { + t.Fatalf("unmarked global method-shaped IFn edge was skipped") + } + if !hasOrdinaryEdge(pm, "_llgo_pkg.Unmarked", "pkg.T.M") { + t.Fatalf("unmarked global method-shaped TFn edge was skipped") + } +} + +func TestFinishMetaCollectionDeduplicatesFunctionEdges(t *testing.T) { + prog := NewProgram(nil) + defer prog.Dispose() + pkg := prog.NewPackageEx("pkg", "pkg", true) + + callee := pkg.NewFunc("pkg.callee", NoArgsNoRet, InGo) + callee.MakeBody(1).Return() + caller := pkg.NewFunc("pkg.caller", NoArgsNoRet, InGo) + b := caller.MakeBody(1) + b.Call(caller.Expr) + b.Call(callee.Expr) + b.Call(callee.Expr) + b.Return() + + if err := pkg.FinishMetaCollection(); err != nil { + t.Fatal(err) + } + defer pkg.Meta.Close() + summary, err := meta.NewGlobalSummary([]*meta.PackageMeta{pkg.Meta}) + if err != nil { + t.Fatal(err) + } + callerSym, ok := summary.LookupSymbol("pkg.caller") + if !ok { + t.Fatal("pkg.caller is missing from metadata") + } + calleeSym, ok := summary.LookupSymbol("pkg.callee") + if !ok { + t.Fatal("pkg.callee is missing from metadata") + } + edges := summary.OrdinaryEdges(callerSym) + if len(edges) != 1 || edges[0] != calleeSym { + t.Fatalf("OrdinaryEdges(pkg.caller) = %v, want [%d]", edges, calleeSym) + } +} + +func hasOrdinaryEdge(pm *meta.PackageMeta, srcName, dstName string) bool { + summary, err := meta.NewGlobalSummary([]*meta.PackageMeta{pm}) + if err != nil { + return false + } + src, ok := summary.LookupSymbol(srcName) + if !ok { + return false + } + dst, ok := summary.LookupSymbol(dstName) + if !ok { + return false + } + for _, e := range summary.OrdinaryEdges(src) { + if e == dst { + return true + } + } + return false +} diff --git a/ssa/expr.go b/ssa/expr.go index caee097c06..bfe859baa0 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -1315,21 +1315,23 @@ func (b Builder) checkReflect(fn Expr, args []Expr) (check ReflectMethodCheck) { case "reflect.Value.Method": if len(args) == 2 { if v, ok := extractConstInt(args[1].impl); ok { - pkg.RecordReflectMethodByIndex(v) + pkg.RecordReflectMethodByIndex(b.Func.Name(), v) reflectKind = ReflectMethodByIndex break } reflectKind = ReflectMethodDynamic + pkg.MarkReflectMethod(b.Func.Name()) } case "reflect.Value.MethodByName": if len(args) == 2 { if v, ok := extractConstString(args[1].impl); ok { - pkg.RecordReflectMethodByName(v) + pkg.RecordReflectMethodByName(b.Func.Name(), v) reflectKind = ReflectMethodByName check.Name = v break } reflectKind = ReflectMethodDynamic | ReflectMethodByName + pkg.MarkReflectMethod(b.Func.Name()) } } pkg.NeedAbiInit |= reflectKind @@ -1337,18 +1339,28 @@ func (b Builder) checkReflect(fn Expr, args []Expr) (check ReflectMethodCheck) { return } -func (p Package) RecordReflectMethodByIndex(index int) { +func (p Package) RecordReflectMethodByIndex(funcName string, index int) { if p.MethodByIndex == nil { p.MethodByIndex = make(map[int]none) } p.MethodByIndex[index] = none{} + p.MarkReflectMethod(funcName) } -func (p Package) RecordReflectMethodByName(name string) { +func (p Package) RecordReflectMethodByName(funcName, name string) { if p.MethodByName == nil { p.MethodByName = make(map[string]none) } p.MethodByName[name] = none{} + if mb := p.metaBuilder; mb != nil { + mb.AddNamedMethodUse(mb.Sym(funcName), name) + } +} + +func (p Package) MarkReflectMethod(funcName string) { + if mb := p.metaBuilder; mb != nil { + mb.MarkReflect(mb.Sym(funcName)) + } } func extractConstInt(v llvm.Value) (r int, ok bool) { diff --git a/ssa/interface.go b/ssa/interface.go index 2539c3187e..253df5c64b 100644 --- a/ssa/interface.go +++ b/ssa/interface.go @@ -65,7 +65,9 @@ func iMethodOf(rawIntf *types.Interface, name string) int { // Imethod returns closure of an interface method. func (b Builder) Imethod(intf Expr, method *types.Func) Expr { prog := b.Prog - rawIntf := intf.raw.Type.Underlying().(*types.Interface) + intfType := types.Unalias(intf.raw.Type) + patchedIntfType := prog.patch(intfType) + rawIntf := patchedIntfType.Underlying().(*types.Interface) sig := method.Type().(*types.Signature) if sig.Recv() == nil && sig.Params().Len() > 0 { pt := types.Unalias(sig.Params().At(0).Type()) @@ -79,9 +81,10 @@ func (b Builder) Imethod(intf Expr, method *types.Func) Expr { } } tclosure := prog.Type(sig, InGo) + i := iMethodOf(rawIntf, method.Name()) + b.recordUseIfaceMethod(rawIntf, i) data := b.InlineCall(b.Pkg.rtFunc("IfacePtrData"), intf) var fn Expr - i := iMethodOf(rawIntf, method.Name()) impl := intf.impl itab := Expr{b.faceItab(impl), prog.VoidPtrPtr()} pfn := b.Advance(itab, prog.IntVal(uint64(i+3), prog.Int())) @@ -123,6 +126,7 @@ func (b Builder) MakeInterface(tinter Type, x Expr) (ret Expr) { } prog := b.Prog typ := x.Type + b.recordUseIface(typ) tabi := b.abiType(typ.raw.Type) if !directIfaceType(typ.raw.Type) { vptr := b.AllocU(typ) @@ -172,6 +176,7 @@ func (b Builder) MakeInterfaceFromPtr(tinter Type, ptr Expr) (ret Expr) { return b.MakeInterface(tinter, b.Load(ptr)) } + b.recordUseIface(typ) vptr := b.AllocU(typ) dst := b.Convert(prog.VoidPtr(), vptr) src := b.Convert(prog.VoidPtr(), ptr) @@ -179,6 +184,38 @@ func (b Builder) MakeInterfaceFromPtr(tinter Type, ptr Expr) (ret Expr) { return Expr{b.unsafeInterface(rawIntf, tabi, vptr.impl), tinter} } +func (b Builder) recordUseIface(typ Type) { + if mb := b.Pkg.metaBuilder; mb != nil { + if _, ok := types.Unalias(typ.raw.Type).Underlying().(*types.Interface); !ok { + typeName, _ := b.Prog.abi.TypeName(typ.raw.Type) + mb.AddIfaceUse(mb.Sym(b.Func.Name()), mb.Sym(typeName)) + } + } +} + +func (b Builder) recordUseIfaceMethod(rawIntf *types.Interface, methodIndex int) { + if mb := b.Pkg.metaBuilder; mb != nil { + intfSymName, _ := b.Prog.abi.TypeName(rawIntf) + intfSym := mb.Sym(intfSymName) + b.recordInterfaceInfo(rawIntf, intfSymName) + mb.AddIfaceMethodUse(mb.Sym(b.Func.Name()), intfSym, uint32(methodIndex)) + } +} + +func (b Builder) recordInterfaceInfo(t *types.Interface, typeName string) { + mb := b.Pkg.metaBuilder + if mb == nil { + return + } + prog := b.Prog + intfSym := mb.Sym(typeName) + for i := 0; i < t.NumMethods(); i++ { + f := t.Method(i) + ftypName, _ := prog.abi.TypeName(funcType(prog, f.Type())) + mb.AddIfaceMethod(intfSym, abiMethodName(f), mb.Sym(ftypName)) + } +} + func (b Builder) valFromData(typ Type, data llvm.Value) Expr { prog := b.Prog if !directIfaceType(typ.raw.Type) { diff --git a/ssa/package.go b/ssa/package.go index 5b0e9dd411..5a62486c2e 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -26,6 +26,7 @@ import ( "unsafe" "github.com/goplus/llgo/internal/env" + "github.com/goplus/llgo/internal/meta" "github.com/goplus/llgo/ssa/abi" "github.com/xgo-dev/llvm" "golang.org/x/tools/go/types/typeutil" @@ -234,6 +235,7 @@ type aProgram struct { disposed bool enableGoGlobalDCE bool + enableDeadcodeDrop bool pthreadStackSize uint64 enableLTOPluginMarker bool @@ -351,6 +353,14 @@ func (p Program) EnableGoGlobalDCE(enable bool) { p.enableGoGlobalDCE = enable } +func (p Program) EnableDeadcodeDrop(enable bool) { + p.enableDeadcodeDrop = enable +} + +func (p Program) DeadcodeDropEnabled() bool { + return p.enableDeadcodeDrop +} + func (p Program) SetPthreadStackSize(size uint64) { p.pthreadStackSize = size } @@ -476,6 +486,10 @@ func (p Program) tyComplex128() llvm.Type { // NewPackage creates a new package. func (p Program) NewPackage(name, pkgPath string) Package { + return p.NewPackageEx(name, pkgPath, false) +} + +func (p Program) NewPackageEx(name, pkgPath string, metaCollect bool) Package { mod := p.ctx.NewModule(pkgPath) mod.SetDataLayout(p.DataLayout()) mod.SetTarget(p.Target().Spec().Triple) @@ -509,6 +523,10 @@ func (p Program) NewPackage(name, pkgPath string) Package { abiTypeFakeUseCache: make(map[llvm.Value][]llvm.Value), } + if metaCollect { + ret.metaBuilder = meta.NewBuilder() + ret.abiTypeWithUncommon = make(map[llvm.Value]struct{}) + } if p.enableGoGlobalDCE { p.addVirtualFunctionElimModuleFlag(mod) } @@ -773,11 +791,14 @@ type aPackage struct { iRoutine int - NeedRuntime bool - NeedPyInit bool - NeedAbiInit int // bitmask of Reflect* flags indicating which reflect type-construction operations are used - MethodByIndex map[int]none - MethodByName map[string]none + NeedRuntime bool + NeedPyInit bool + NeedAbiInit int // bitmask of Reflect* flags indicating which reflect type-construction operations are used + MethodByIndex map[int]none + MethodByName map[string]none + Meta *meta.PackageMeta + metaBuilder *meta.Builder + abiTypeWithUncommon map[llvm.Value]struct{} export map[string]string // pkgPath.nameInPkg => exportname preserveSyms map[string]struct{} // set of exported symbol names @@ -794,6 +815,17 @@ func (p Package) Module() llvm.Module { return p.mod } +func (p Package) FinishMetaCollection() error { + extractOrdinaryEdges(p.metaBuilder, p.mod, p.abiTypeWithUncommon) + pm, err := p.metaBuilder.Build() + if err != nil { + return err + } + p.Meta = pm + p.metaBuilder = nil + return nil +} + func (p Package) SetExport(name, export string) { p.export[name] = export p.preserveSyms[export] = struct{}{} diff --git a/ssa/ssa_test.go b/ssa/ssa_test.go index 6f1365b057..dd18c4c2ac 100644 --- a/ssa/ssa_test.go +++ b/ssa/ssa_test.go @@ -839,6 +839,202 @@ func TestDevLTOGlobalDCEAbiTypeFakeUseFieldIndexes(t *testing.T) { checkFieldIndex(reflect.TypeOf(reflect.Method{}), reflectMethodFuncFieldIndex, "Func") } +func TestRecordTypeChildren(t *testing.T) { + prog := NewProgram(nil) + defer prog.Dispose() + prog.TypeSizes(types.SizesFor("gc", runtime.GOARCH)) + disabledPkg := prog.NewPackage("pkg", "pkg/disabled") + (&aBuilder{Prog: prog, Pkg: disabledPkg}).recordTypeChildren("pkg.disabled", types.Typ[types.Int]) + if disabledPkg.metaBuilder != nil { + t.Fatal("metadata builder should remain disabled") + } + + params := types.NewTuple( + types.NewVar(token.NoPos, nil, "n", types.Typ[types.Int]), + types.NewVar(token.NoPos, nil, "s", types.Typ[types.String]), + ) + results := types.NewTuple( + types.NewVar(token.NoPos, nil, "ok", types.Typ[types.Bool]), + types.NewVar(token.NoPos, nil, "n", types.Typ[types.Int]), + ) + sig := types.NewSignatureType(nil, nil, nil, params, results, false) + + mapType := types.NewMap(types.Typ[types.String], types.Typ[types.Int]) + arrayElem := types.Typ[types.Byte] + pkgTypes := types.NewPackage("example.com/pkg", "pkg") + namedType := types.NewNamed( + types.NewTypeName(token.NoPos, pkgTypes, "Named", nil), + types.NewStruct([]*types.Var{ + types.NewVar(token.NoPos, pkgTypes, "Field", types.Typ[types.Bool]), + }, nil), + nil, + ) + ifaceMethod := types.NewFunc(token.NoPos, pkgTypes, "M", types.NewSignatureType( + nil, nil, nil, + types.NewTuple(types.NewVar(token.NoPos, nil, "n", types.Typ[types.Int])), + nil, + false, + )) + interfaceType := types.NewInterfaceType([]*types.Func{ifaceMethod}, nil) + interfaceType.Complete() + + pkg := prog.NewPackageEx("pkg", "pkg", true) + b := &aBuilder{Prog: prog, Pkg: pkg} + b.recordTypeChildren("pkg.basic", types.Typ[types.Int]) + b.recordTypeChildren("pkg.pointer", types.NewPointer(types.Typ[types.Int])) + b.recordTypeChildren("pkg.channel", types.NewChan(types.SendRecv, types.Typ[types.String])) + b.recordTypeChildren("pkg.slice", types.NewSlice(types.Typ[types.Bool])) + b.recordTypeChildren("pkg.array", types.NewArray(arrayElem, 4)) + b.recordTypeChildren("pkg.map", mapType) + b.recordTypeChildren("pkg.signature", sig) + b.recordTypeChildren("pkg.emptySignature", types.NewSignatureType(nil, nil, nil, nil, nil, false)) + b.recordTypeChildren("pkg.struct", types.NewStruct([]*types.Var{ + types.NewVar(token.NoPos, pkgTypes, "N", types.Typ[types.Int]), + types.NewVar(token.NoPos, pkgTypes, "S", types.Typ[types.String]), + }, nil)) + b.recordTypeChildren("pkg.named", namedType) + b.recordTypeChildren("pkg.interface", interfaceType) + + pm, err := pkg.metaBuilder.Build() + if err != nil { + t.Fatal(err) + } + defer pm.Close() + + const want = `[TypeChildren] +pkg.array: + _llgo_uint8 +pkg.channel: + _llgo_string +pkg.map: + _llgo_int + _llgo_string +pkg.named: + _llgo_bool +pkg.pointer: + _llgo_int +pkg.signature: + _llgo_bool + _llgo_int + _llgo_string +pkg.slice: + _llgo_bool +pkg.struct: + _llgo_int + _llgo_string + +` + if got := pm.String(); got != want { + t.Fatalf("metadata mismatch\ngot:\n%s\nwant:\n%s", got, want) + } +} + +func TestRecordMethodSlots(t *testing.T) { + prog := NewProgram(nil) + defer prog.Dispose() + prog.TypeSizes(types.SizesFor("gc", runtime.GOARCH)) + prog.SetRuntime(func() *types.Package { + pkg, err := importer.For("source", nil).Import(PkgRuntime) + if err != nil { + t.Fatal(err) + } + return pkg + }) + + goPkg := types.NewPackage("example.com/pkg", "pkg") + named := types.NewNamed(types.NewTypeName(token.NoPos, goPkg, "T", nil), types.NewStruct(nil, nil), nil) + recv := types.NewVar(token.NoPos, goPkg, "", named) + named.AddMethod(types.NewFunc(token.NoPos, goPkg, "M", types.NewSignatureType(recv, nil, nil, nil, nil, false))) + + pkg := prog.NewPackageEx("pkg", goPkg.Path(), true) + fn := pkg.NewFunc("use", types.NewSignatureType(nil, nil, nil, nil, nil, false), InGo) + b := fn.MakeBody(1) + b.abiType(named) + b.Return() + + pm, err := pkg.metaBuilder.Build() + if err != nil { + t.Fatal(err) + } + defer pm.Close() + + const want = `[TypeChildren] +*_llgo_example.com/pkg.T: + _llgo_example.com/pkg.T +*_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + +[MethodInfo] +*_llgo_example.com/pkg.T: + 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac example.com/pkg.(*T).M __llgo_stub.example.com/pkg.(*T).M +_llgo_example.com/pkg.T: + 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac example.com/pkg.(*T).M __llgo_stub.example.com/pkg.T.M + +` + if got := pm.String(); got != want { + t.Fatalf("metadata mismatch\ngot:\n%s\nwant:\n%s", got, want) + } +} + +func TestRecordReflectMethodDemands(t *testing.T) { + prog := NewProgram(nil) + defer prog.Dispose() + pkg := prog.NewPackageEx("pkg", "pkg", true) + pkg.RecordReflectMethodByName("pkg.named", "Keep") + pkg.MarkReflectMethod("pkg.dynamic") + + pm, err := pkg.metaBuilder.Build() + if err != nil { + t.Fatal(err) + } + defer pm.Close() + + const want = `[UseNamedMethod] +pkg.named: + Keep + +[Reflect] + pkg.dynamic + +` + if got := pm.String(); got != want { + t.Fatalf("metadata mismatch\ngot:\n%s\nwant:\n%s", got, want) + } +} + +func TestRecordUseIface(t *testing.T) { + prog := NewProgram(nil) + defer prog.Dispose() + prog.SetRuntime(func() *types.Package { + pkg, err := importer.For("source", nil).Import(PkgRuntime) + if err != nil { + t.Fatal(err) + } + return pkg + }) + pkg := prog.NewPackageEx("pkg", "pkg", true) + fn := pkg.NewFunc("caller", types.NewSignatureType(nil, nil, nil, nil, nil, false), InGo) + b := fn.MakeBody(1) + b.recordUseIface(prog.Int()) + b.recordUseIface(prog.Any()) + b.Return() + + pm, err := pkg.metaBuilder.Build() + if err != nil { + t.Fatal(err) + } + defer pm.Close() + + const want = `[UseIface] +caller: + _llgo_int + +` + if got := pm.String(); got != want { + t.Fatalf("metadata mismatch\ngot:\n%s\nwant:\n%s", got, want) + } +} + func TestDevLTOGlobalDCERecordAbiTypeFakeUsesUsesCache(t *testing.T) { requireGoGlobalDCE(t) @@ -1375,7 +1571,7 @@ func TestIfaceMethodClosureCallIR(t *testing.T) { types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int])), true) recvMeth := types.NewFunc(0, pkgTypes, "Printf", recvSig) - pkg := prog.NewPackage("bar", "foo/bar") + pkg := prog.NewPackageEx("bar", "foo/bar", true) callerSig := types.NewSignatureType(nil, nil, nil, types.NewTuple(types.NewVar(0, pkgTypes, "i", namedIface)), types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int])), false) @@ -1385,6 +1581,28 @@ func TestIfaceMethodClosureCallIR(t *testing.T) { ret := b.Call(closure, prog.Val(100), prog.Val(200)) b.Return(ret) + if err := pkg.FinishMetaCollection(); err != nil { + t.Fatal(err) + } + pm := pkg.Meta + defer pm.Close() + const wantMeta = `[OrdinaryEdges] +caller: + github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData + +[UseIfaceMethod] +caller: + _llgo_iface$Yoe3OCWqNu8XXGUO_vekWtum96Bix1ffdbPGjVhQ1pI Printf _llgo_func$_RYiBYcSxJjuvzYmA4xYm18hT18pH0_ng6z76aK77Bk + +[InterfaceInfo] +_llgo_iface$Yoe3OCWqNu8XXGUO_vekWtum96Bix1ffdbPGjVhQ1pI: + Printf _llgo_func$_RYiBYcSxJjuvzYmA4xYm18hT18pH0_ng6z76aK77Bk + +` + if got := pm.String(); got != wantMeta { + t.Fatalf("metadata mismatch\ngot:\n%s\nwant:\n%s", got, wantMeta) + } + assertPkg(t, pkg, `; ModuleID = 'foo/bar' source_filename = "foo/bar"