(SearchBar.SELECTORS.breadcrumb) ?? null;
+
+ if (!this.DOM.inputSearch) {
+ console.warn("SearchBar: Input element for search not found.");
+ }
+
+ this.bindEvents();
+ }
+
+ private bindEvents(): void {
+ this.DOM.el?.addEventListener("submit", (e) => {
+ e.preventDefault();
+ this.searchUrl();
+ });
+ }
+
+ public searchUrl(): void {
+ const input = this.DOM.inputSearch?.value.trim();
+
+ if (input) {
+ let url = input;
+
+ // Check if the URL has a proper scheme
+ if (!/^https?:\/\//i.test(url)) {
+ url = `${this.baseUrl}${url.startsWith("/") ? "" : "/"}${url}`;
+ }
+
+ try {
+ window.location.href = new URL(url).href;
+ } catch (error) {
+ console.error("SearchBar: Invalid URL. Please enter a valid URL starting with http:// or https://.");
+ }
+ } else {
+ console.error("SearchBar: Please enter a URL to search.");
+ }
+ }
+}
+
+export default () => new SearchBar();
diff --git a/gno.land/pkg/gnoweb/static/img/favicon.ico b/gno.land/pkg/gnoweb/frontend/static/favicon.ico
similarity index 100%
rename from gno.land/pkg/gnoweb/static/img/favicon.ico
rename to gno.land/pkg/gnoweb/frontend/static/favicon.ico
diff --git a/gno.land/pkg/gnoweb/frontend/static/fonts/intervar/Intervar.woff2 b/gno.land/pkg/gnoweb/frontend/static/fonts/intervar/Intervar.woff2
new file mode 100644
index 00000000000..891fc5cc567
Binary files /dev/null and b/gno.land/pkg/gnoweb/frontend/static/fonts/intervar/Intervar.woff2 differ
diff --git a/gno.land/pkg/gnoweb/frontend/static/fonts/roboto/roboto-mono-normal.woff b/gno.land/pkg/gnoweb/frontend/static/fonts/roboto/roboto-mono-normal.woff
new file mode 100644
index 00000000000..2c58fe2d6d7
Binary files /dev/null and b/gno.land/pkg/gnoweb/frontend/static/fonts/roboto/roboto-mono-normal.woff differ
diff --git a/gno.land/pkg/gnoweb/frontend/static/fonts/roboto/roboto-mono-normal.woff2 b/gno.land/pkg/gnoweb/frontend/static/fonts/roboto/roboto-mono-normal.woff2
new file mode 100644
index 00000000000..53d081f3a53
Binary files /dev/null and b/gno.land/pkg/gnoweb/frontend/static/fonts/roboto/roboto-mono-normal.woff2 differ
diff --git a/gno.land/pkg/gnoweb/frontend/static/imgs/gnoland.svg b/gno.land/pkg/gnoweb/frontend/static/imgs/gnoland.svg
new file mode 100644
index 00000000000..30d2f3ef56a
--- /dev/null
+++ b/gno.land/pkg/gnoweb/frontend/static/imgs/gnoland.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/gno.land/pkg/gnoweb/gnoweb.go b/gno.land/pkg/gnoweb/gnoweb.go
deleted file mode 100644
index 40d027d84b9..00000000000
--- a/gno.land/pkg/gnoweb/gnoweb.go
+++ /dev/null
@@ -1,608 +0,0 @@
-package gnoweb
-
-import (
- "bytes"
- "embed"
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "io/fs"
- "log/slog"
- "net/http"
- "net/url"
- "os"
- "path/filepath"
- "regexp"
- "runtime"
- "strings"
- "time"
-
- "github.com/gnolang/gno/gnovm"
- "github.com/gnolang/gno/tm2/pkg/amino"
- abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types"
- "github.com/gnolang/gno/tm2/pkg/bft/rpc/client"
- "github.com/gorilla/mux"
- "github.com/gotuna/gotuna"
-
- // for static files
- "github.com/gnolang/gno/gno.land/pkg/gnoweb/static"
- "github.com/gnolang/gno/gno.land/pkg/sdk/vm" // for error types
- // "github.com/gnolang/gno/tm2/pkg/sdk" // for baseapp (info, status)
-)
-
-const (
- qFileStr = "vm/qfile"
- gnowebArgsSeparator = "$"
- urlQuerySeparator = "?"
-)
-
-//go:embed views/*
-var defaultViewsFiles embed.FS
-
-type Config struct {
- RemoteAddr string
- CaptchaSite string
- FaucetURL string
- ViewsDir string
- HelpChainID string
- HelpRemote string
- WithAnalytics bool
- WithHTML bool
-}
-
-func NewDefaultConfig() Config {
- return Config{
- RemoteAddr: "127.0.0.1:26657",
- CaptchaSite: "",
- FaucetURL: "http://localhost:5050",
- ViewsDir: "",
- HelpChainID: "dev",
- HelpRemote: "127.0.0.1:26657",
- WithAnalytics: false,
- WithHTML: false,
- }
-}
-
-func MakeApp(logger *slog.Logger, cfg Config) gotuna.App {
- var viewFiles fs.FS
-
- // Get specific views directory if specified
- if cfg.ViewsDir != "" {
- viewFiles = os.DirFS(cfg.ViewsDir)
- } else {
- // Get embed views
- var err error
- viewFiles, err = fs.Sub(defaultViewsFiles, "views")
- if err != nil {
- panic("unable to get views directory from embed fs: " + err.Error())
- }
- }
-
- app := gotuna.App{
- ViewFiles: viewFiles,
- Router: gotuna.NewMuxRouter(),
- Static: static.EmbeddedStatic,
- }
-
- for from, to := range Aliases {
- app.Router.Handle(from, handlerRealmAlias(logger, app, &cfg, to))
- }
-
- for from, to := range Redirects {
- app.Router.Handle(from, handlerRedirect(logger, app, &cfg, to))
- }
- // realm routes
- // NOTE: see rePathPart.
- app.Router.Handle("/r/{rlmname:[a-z][a-z0-9_]*(?:/[a-z][a-z0-9_]*)+}/{filename:(?:(?:.*\\.(?:gno|md|txt|mod)$)|(?:LICENSE$))?}", handlerRealmFile(logger, app, &cfg))
- app.Router.Handle("/r/{rlmname:[a-z][a-z0-9_]*(?:/[a-z][a-z0-9_]*)+}{args:(?:\\$.*)?}", handlerRealmMain(logger, app, &cfg))
- app.Router.Handle("/r/{rlmname:[a-z][a-z0-9_]*(?:/[a-z][a-z0-9_]*)+}:{querystr:[^$]*}{args:(?:\\$.*)?}", handlerRealmRender(logger, app, &cfg))
- app.Router.Handle("/p/{filepath:.*}", handlerPackageFile(logger, app, &cfg))
-
- // other
- app.Router.Handle("/faucet", handlerFaucet(logger, app, &cfg))
- app.Router.Handle("/static/{path:.+}", handlerStaticFile(logger, app, &cfg))
- app.Router.Handle("/favicon.ico", handlerFavicon(logger, app, &cfg))
-
- // api
- app.Router.Handle("/status.json", handlerStatusJSON(logger, app, &cfg))
-
- app.Router.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- path := r.RequestURI
- handleNotFound(logger, app, &cfg, path, w, r)
- })
- return app
-}
-
-var (
- inlineCodePattern = regexp.MustCompile("`[^`]*`")
- htmlTagPattern = regexp.MustCompile(`<\/?\w+[^>]*?>`)
-)
-
-func sanitizeContent(cfg *Config, content string) string {
- if cfg.WithHTML {
- return content
- }
-
- placeholders := map[string]string{}
- contentWithPlaceholders := inlineCodePattern.ReplaceAllStringFunc(content, func(match string) string {
- placeholder := fmt.Sprintf("__GNOMDCODE_%d__", len(placeholders))
- placeholders[placeholder] = match
- return placeholder
- })
-
- sanitizedContent := htmlTagPattern.ReplaceAllString(contentWithPlaceholders, "")
-
- if len(placeholders) > 0 {
- for placeholder, code := range placeholders {
- sanitizedContent = strings.ReplaceAll(sanitizedContent, placeholder, code)
- }
- }
-
- return sanitizedContent
-}
-
-// handlerRealmAlias is used to render official pages from realms.
-// url is intended to be shorter.
-// UX is intended to be more minimalistic.
-// A link to the realm realm is added.
-func handlerRealmAlias(logger *slog.Logger, app gotuna.App, cfg *Config, rlmpath string) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- rlmfullpath := "gno.land" + rlmpath
- querystr := "" // XXX: "?gnoweb-alias=1"
- parts := strings.Split(rlmpath, ":")
- switch len(parts) {
- case 1: // continue
- case 2: // r/realm:querystr
- rlmfullpath = "gno.land" + parts[0]
- querystr = parts[1] + querystr
- default:
- panic("should not happen")
- }
- rlmname := strings.TrimPrefix(rlmfullpath, "gno.land/r/")
- qpath := "vm/qrender"
- data := []byte(fmt.Sprintf("%s:%s", rlmfullpath, querystr))
- res, err := makeRequest(logger, cfg, qpath, data)
- if err != nil {
- writeError(logger, w, fmt.Errorf("gnoweb failed to query gnoland: %w", err))
- return
- }
-
- queryParts := strings.Split(querystr, "/")
- pathLinks := []pathLink{}
- for i, part := range queryParts {
- pathLinks = append(pathLinks, pathLink{
- URL: "/r/" + rlmname + ":" + strings.Join(queryParts[:i+1], "/"),
- Text: part,
- })
- }
-
- tmpl := app.NewTemplatingEngine()
- // XXX: extract title from realm's output
- // XXX: extract description from realm's output
- tmpl.Set("RealmName", rlmname)
- tmpl.Set("RealmPath", rlmpath)
- tmpl.Set("Query", querystr)
- tmpl.Set("PathLinks", pathLinks)
- tmpl.Set("Contents", sanitizeContent(cfg, string(res.Data)))
- tmpl.Set("Config", cfg)
- tmpl.Set("IsAlias", true)
- tmpl.Render(w, r, "realm_render.html", "funcs.html")
- })
-}
-
-func handlerFaucet(logger *slog.Logger, app gotuna.App, cfg *Config) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- app.NewTemplatingEngine().
- Set("Config", cfg).
- Render(w, r, "faucet.html", "funcs.html")
- })
-}
-
-func handlerStatusJSON(logger *slog.Logger, app gotuna.App, cfg *Config) http.Handler {
- startedAt := time.Now()
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- var ret struct {
- Gnoland struct {
- Connected bool `json:"connected"`
- Error *string `json:"error,omitempty"`
- Height *int64 `json:"height,omitempty"`
- // processed txs
- // active connections
-
- Version *string `json:"version,omitempty"`
- // Uptime *float64 `json:"uptime-seconds,omitempty"`
- // Goarch *string `json:"goarch,omitempty"`
- // Goos *string `json:"goos,omitempty"`
- // GoVersion *string `json:"go-version,omitempty"`
- // NumCPU *int `json:"num_cpu,omitempty"`
- } `json:"gnoland"`
- Website struct {
- // Version string `json:"version"`
- Uptime float64 `json:"uptime-seconds"`
- Goarch string `json:"goarch"`
- Goos string `json:"goos"`
- GoVersion string `json:"go-version"`
- NumCPU int `json:"num_cpu"`
- } `json:"website"`
- }
- ret.Website.Uptime = time.Since(startedAt).Seconds()
- ret.Website.Goarch = runtime.GOARCH
- ret.Website.Goos = runtime.GOOS
- ret.Website.NumCPU = runtime.NumCPU()
- ret.Website.GoVersion = runtime.Version()
-
- ret.Gnoland.Connected = true
- res, err := makeRequest(logger, cfg, ".app/version", []byte{})
- if err != nil {
- ret.Gnoland.Connected = false
- errmsg := err.Error()
- ret.Gnoland.Error = &errmsg
- } else {
- version := string(res.Value)
- ret.Gnoland.Version = &version
- ret.Gnoland.Height = &res.Height
- }
-
- out, _ := json.MarshalIndent(ret, "", " ")
- w.Header().Set("Content-Type", "application/json")
- w.Write(out)
- })
-}
-
-func handlerRedirect(logger *slog.Logger, app gotuna.App, cfg *Config, to string) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- http.Redirect(w, r, to, http.StatusFound)
- tmpl := app.NewTemplatingEngine()
- tmpl.Set("To", to)
- tmpl.Set("Config", cfg)
- tmpl.Render(w, r, "redirect.html", "funcs.html")
- })
-}
-
-func handlerRealmMain(logger *slog.Logger, app gotuna.App, cfg *Config) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- args, err := parseGnowebArgs(r.RequestURI)
- if err != nil {
- writeError(logger, w, err)
- return
- }
-
- vars := mux.Vars(r)
- rlmname := vars["rlmname"]
- rlmpath := "gno.land/r/" + rlmname
-
- logger.Info("handling", "name", rlmname, "path", rlmpath)
- if args.Has("help") {
- // Render function helper.
- funcName := args.Get("func")
- qpath := "vm/qfuncs"
- data := []byte(rlmpath)
- res, err := makeRequest(logger, cfg, qpath, data)
- if err != nil {
- writeError(logger, w, fmt.Errorf("request failed: %w", err))
- return
- }
- var fsigs vm.FunctionSignatures
- amino.MustUnmarshalJSON(res.Data, &fsigs)
- // Fill fsigs with query parameters.
- for i := range fsigs {
- fsig := &(fsigs[i])
- for j := range fsig.Params {
- param := &(fsig.Params[j])
- value := args.Get(param.Name)
- param.Value = value
- }
- }
- // Render template.
- tmpl := app.NewTemplatingEngine()
- tmpl.Set("FuncName", funcName)
- tmpl.Set("RealmPath", rlmpath)
- tmpl.Set("DirPath", pathOf(rlmpath))
- tmpl.Set("FunctionSignatures", fsigs)
- tmpl.Set("Config", cfg)
- tmpl.Render(w, r, "realm_help.html", "funcs.html")
- } else {
- // Ensure realm exists. TODO optimize.
- qpath := qFileStr
- data := []byte(rlmpath)
- _, err := makeRequest(logger, cfg, qpath, data)
- if err != nil {
- writeError(logger, w, errors.New("error querying realm package"))
- return
- }
- // Render blank query path, /r/REALM:.
- handleRealmRender(logger, app, cfg, w, r)
- }
- })
-}
-
-type pathLink struct {
- URL string
- Text string
-}
-
-func handlerRealmRender(logger *slog.Logger, app gotuna.App, cfg *Config) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- handleRealmRender(logger, app, cfg, w, r)
- })
-}
-
-func handleRealmRender(logger *slog.Logger, app gotuna.App, cfg *Config, w http.ResponseWriter, r *http.Request) {
- gnowebArgs, err := parseGnowebArgs(r.RequestURI)
- if err != nil {
- writeError(logger, w, err)
- return
- }
-
- queryArgs, err := parseQueryArgs(r.RequestURI)
- if err != nil {
- writeError(logger, w, err)
- return
- }
-
- var urlQuery, gnowebQuery string
- if len(queryArgs) > 0 {
- urlQuery = urlQuerySeparator + queryArgs.Encode()
- }
- if len(gnowebArgs) > 0 {
- gnowebQuery = gnowebArgsSeparator + gnowebArgs.Encode()
- }
-
- vars := mux.Vars(r)
- rlmname := vars["rlmname"]
- rlmpath := "gno.land/r/" + rlmname
- querystr := vars["querystr"]
- if r.URL.Path == "/r/"+rlmname+":" {
- // Redirect to /r/REALM if querypath is empty.
- http.Redirect(w, r, "/r/"+rlmname+urlQuery+gnowebQuery, http.StatusFound)
- return
- }
-
- qpath := "vm/qrender"
- data := []byte(fmt.Sprintf("%s:%s", rlmpath, querystr+urlQuery))
- res, err := makeRequest(logger, cfg, qpath, data)
- if err != nil {
- // XXX hack
- if strings.Contains(err.Error(), "Render not declared") {
- res = &abci.ResponseQuery{}
- res.Data = []byte("realm package has no Render() function")
- } else {
- writeError(logger, w, err)
- return
- }
- }
-
- dirdata := []byte(rlmpath)
- dirres, err := makeRequest(logger, cfg, qFileStr, dirdata)
- if err != nil {
- writeError(logger, w, err)
- return
- }
- hasReadme := bytes.Contains(append(dirres.Data, '\n'), []byte("README.md\n"))
-
- // linkify querystr.
- queryParts := strings.Split(querystr, "/")
- pathLinks := []pathLink{}
- for i, part := range queryParts {
- rlmpath := strings.Join(queryParts[:i+1], "/")
-
- // Add URL query arguments to the last breadcrumb item's URL
- if i+1 == len(queryParts) {
- rlmpath = rlmpath + urlQuery + gnowebQuery
- }
-
- pathLinks = append(pathLinks, pathLink{
- URL: "/r/" + rlmname + ":" + rlmpath,
- Text: part,
- })
- }
-
- // Render template.
- tmpl := app.NewTemplatingEngine()
- // XXX: extract title from realm's output
- // XXX: extract description from realm's output
- tmpl.Set("RealmName", rlmname)
- tmpl.Set("RealmPath", rlmpath)
- tmpl.Set("Query", querystr)
- tmpl.Set("PathLinks", pathLinks)
- tmpl.Set("Contents", sanitizeContent(cfg, string(res.Data)))
- tmpl.Set("Config", cfg)
- tmpl.Set("HasReadme", hasReadme)
- tmpl.Render(w, r, "realm_render.html", "funcs.html")
-}
-
-func handlerRealmFile(logger *slog.Logger, app gotuna.App, cfg *Config) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- vars := mux.Vars(r)
- diruri := "gno.land/r/" + vars["rlmname"]
- filename := vars["filename"]
- renderPackageFile(logger, app, cfg, w, r, diruri, filename)
- })
-}
-
-func handlerPackageFile(logger *slog.Logger, app gotuna.App, cfg *Config) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- vars := mux.Vars(r)
- pkgpath := "gno.land/p/" + vars["filepath"]
- diruri, filename := gnovm.SplitFilepath(pkgpath)
- if filename == "" && diruri == pkgpath {
- // redirect to diruri + "/"
- http.Redirect(w, r, "/p/"+vars["filepath"]+"/", http.StatusFound)
- return
- }
- renderPackageFile(logger, app, cfg, w, r, diruri, filename)
- })
-}
-
-func renderPackageFile(logger *slog.Logger, app gotuna.App, cfg *Config, w http.ResponseWriter, r *http.Request, diruri string, filename string) {
- if filename == "" {
- // Request is for a folder.
- qpath := qFileStr
- data := []byte(diruri)
- res, err := makeRequest(logger, cfg, qpath, data)
- if err != nil {
- writeError(logger, w, err)
- return
- }
- files := strings.Split(string(res.Data), "\n")
- // Render template.
- tmpl := app.NewTemplatingEngine()
- tmpl.Set("DirURI", diruri)
- tmpl.Set("DirPath", pathOf(diruri))
- tmpl.Set("Files", files)
- tmpl.Set("Config", cfg)
- tmpl.Render(w, r, "package_dir.html", "funcs.html")
- } else {
- // Request is for a file.
- filepath := diruri + "/" + filename
- qpath := qFileStr
- data := []byte(filepath)
- res, err := makeRequest(logger, cfg, qpath, data)
- if err != nil {
- writeError(logger, w, err)
- return
- }
- // Render template.
- tmpl := app.NewTemplatingEngine()
- tmpl.Set("DirURI", diruri)
- tmpl.Set("DirPath", pathOf(diruri))
- tmpl.Set("FileName", filename)
- tmpl.Set("FileContents", string(res.Data))
- tmpl.Set("Config", cfg)
- tmpl.Render(w, r, "package_file.html", "funcs.html")
- }
-}
-
-func makeRequest(log *slog.Logger, cfg *Config, qpath string, data []byte) (res *abci.ResponseQuery, err error) {
- opts2 := client.ABCIQueryOptions{
- // Height: height, XXX
- // Prove: false, XXX
- }
- remote := cfg.RemoteAddr
- cli, err := client.NewHTTPClient(remote)
- if err != nil {
- return nil, fmt.Errorf("unable to create HTTP client, %w", err)
- }
-
- qres, err := cli.ABCIQueryWithOptions(
- qpath, data, opts2)
- if err != nil {
- log.Error("request error", "path", qpath, "error", err)
- return nil, fmt.Errorf("unable to query path %q: %w", qpath, err)
- }
- if qres.Response.Error != nil {
- log.Error("response error", "path", qpath, "log", qres.Response.Log)
- return nil, qres.Response.Error
- }
- return &qres.Response, nil
-}
-
-func handlerStaticFile(logger *slog.Logger, app gotuna.App, cfg *Config) http.Handler {
- fs := http.FS(app.Static)
- fileapp := http.StripPrefix("/static", http.FileServer(fs))
-
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- vars := mux.Vars(r)
- fpath := filepath.Clean(vars["path"])
- f, err := fs.Open(fpath)
- if os.IsNotExist(err) {
- handleNotFound(logger, app, cfg, fpath, w, r)
- return
- }
- stat, err := f.Stat()
- if err != nil || stat.IsDir() {
- handleNotFound(logger, app, cfg, fpath, w, r)
- return
- }
-
- // TODO: ModTime doesn't work for embed?
- // w.Header().Set("ETag", fmt.Sprintf("%x", stat.ModTime().UnixNano()))
- // w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%s", "31536000"))
- fileapp.ServeHTTP(w, r)
- })
-}
-
-func handlerFavicon(logger *slog.Logger, app gotuna.App, cfg *Config) http.Handler {
- fs := http.FS(app.Static)
-
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- fpath := "img/favicon.ico"
- f, err := fs.Open(fpath)
- if os.IsNotExist(err) {
- handleNotFound(logger, app, cfg, fpath, w, r)
- return
- }
- w.Header().Set("Content-Type", "image/x-icon")
- w.Header().Set("Cache-Control", "public, max-age=604800") // 7d
- io.Copy(w, f)
- })
-}
-
-func handleNotFound(logger *slog.Logger, app gotuna.App, cfg *Config, path string, w http.ResponseWriter, r *http.Request) {
- // decode path for non-ascii characters
- decodedPath, err := url.PathUnescape(path)
- if err != nil {
- logger.Error("failed to decode path", "error", err)
- decodedPath = path
- }
- w.WriteHeader(http.StatusNotFound)
- app.NewTemplatingEngine().
- Set("title", "Not found").
- Set("path", decodedPath).
- Set("Config", cfg).
- Render(w, r, "404.html", "funcs.html")
-}
-
-func writeError(logger *slog.Logger, w http.ResponseWriter, err error) {
- if details := errors.Unwrap(err); details != nil {
- logger.Error("handler", "error", err, "details", details)
- } else {
- logger.Error("handler", "error", err)
- }
-
- // XXX: writeError should return an error page template.
- w.WriteHeader(500)
- w.Write([]byte(err.Error()))
-}
-
-func pathOf(diruri string) string {
- parts := strings.Split(diruri, "/")
- if parts[0] == "gno.land" {
- return "/" + strings.Join(parts[1:], "/")
- }
-
- panic(fmt.Sprintf("invalid dir-URI %q", diruri))
-}
-
-// parseQueryArgs parses URL query arguments that are not specific to gnoweb.
-// These are the standard arguments that comes after the "?" symbol and before
-// the special "$" symbol. The "$" symbol can be used within public query
-// arguments by using its encoded representation "%24".
-func parseQueryArgs(rawURL string) (url.Values, error) {
- if i := strings.Index(rawURL, gnowebArgsSeparator); i != -1 {
- rawURL = rawURL[:i]
- }
-
- u, err := url.Parse(rawURL)
- if err != nil {
- return url.Values{}, fmt.Errorf("invalid query arguments: %w", err)
- }
- return u.Query(), nil
-}
-
-// parseGnowebArgs parses URL query arguments that are specific to gnoweb.
-// These arguments are indicated by using the "$" symbol followed by a query
-// string with the arguments.
-func parseGnowebArgs(rawURL string) (url.Values, error) {
- i := strings.Index(rawURL, gnowebArgsSeparator)
- if i == -1 {
- return url.Values{}, nil
- }
-
- values, err := url.ParseQuery(rawURL[i+1:])
- if err != nil {
- return url.Values{}, fmt.Errorf("invalid gnoweb arguments: %w", err)
- }
- return values, nil
-}
diff --git a/gno.land/pkg/gnoweb/handler.go b/gno.land/pkg/gnoweb/handler.go
new file mode 100644
index 00000000000..b3a9fcd143c
--- /dev/null
+++ b/gno.land/pkg/gnoweb/handler.go
@@ -0,0 +1,381 @@
+package gnoweb
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "html/template"
+ "io"
+ "log/slog"
+ "net/http"
+ "path/filepath"
+ "slices"
+ "strings"
+ "time"
+
+ "github.com/alecthomas/chroma/v2"
+ "github.com/alecthomas/chroma/v2/lexers"
+ "github.com/gnolang/gno/gno.land/pkg/gnoweb/components"
+ "github.com/gnolang/gno/gno.land/pkg/sdk/vm" // for error types
+)
+
+const DefaultChainDomain = "gno.land"
+
+type StaticMetadata struct {
+ AssetsPath string
+ ChromaPath string
+ RemoteHelp string
+ ChainId string
+ Analytics bool
+}
+
+type WebHandlerConfig struct {
+ Meta StaticMetadata
+ RenderClient *WebClient
+ Formatter Formatter
+}
+
+type WebHandler struct {
+ formatter Formatter
+
+ logger *slog.Logger
+ static StaticMetadata
+ webcli *WebClient
+}
+
+func NewWebHandler(logger *slog.Logger, cfg WebHandlerConfig) *WebHandler {
+ if cfg.RenderClient == nil {
+ logger.Error("no renderer has been defined")
+ }
+
+ return &WebHandler{
+ formatter: cfg.Formatter,
+ webcli: cfg.RenderClient,
+ logger: logger,
+ static: cfg.Meta,
+ }
+}
+
+func (h *WebHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ h.logger.Debug("receiving request", "method", r.Method, "path", r.URL.Path)
+
+ if r.Method != http.MethodGet {
+ http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
+ return
+ }
+
+ h.Get(w, r)
+}
+
+func (h *WebHandler) Get(w http.ResponseWriter, r *http.Request) {
+ var body bytes.Buffer
+
+ start := time.Now()
+ defer func() {
+ h.logger.Debug("request completed",
+ "url", r.URL.String(),
+ "elapsed", time.Since(start).String())
+ }()
+
+ var indexData components.IndexData
+ indexData.HeadData.AssetsPath = h.static.AssetsPath
+ indexData.HeadData.ChromaPath = h.static.ChromaPath
+ indexData.FooterData.Analytics = h.static.Analytics
+ indexData.FooterData.AssetsPath = h.static.AssetsPath
+
+ // Render the page body into the buffer
+ var status int
+ gnourl, err := ParseGnoURL(r.URL)
+ if err != nil {
+ h.logger.Warn("page not found", "path", r.URL.Path, "err", err)
+ status, err = http.StatusNotFound, components.RenderStatusComponent(&body, "page not found")
+ } else {
+ // TODO: real data (title & description)
+ indexData.HeadData.Title = "gno.land - " + gnourl.Path
+
+ // Header
+ indexData.HeaderData.RealmPath = gnourl.Path
+ indexData.HeaderData.Breadcrumb.Parts = generateBreadcrumbPaths(gnourl.Path)
+ indexData.HeaderData.WebQuery = gnourl.WebQuery
+
+ // Render
+ switch gnourl.Kind() {
+ case KindRealm, KindPure:
+ status, err = h.renderPackage(&body, gnourl)
+ default:
+ h.logger.Debug("invalid page kind", "kind", gnourl.Kind)
+ status, err = http.StatusNotFound, components.RenderStatusComponent(&body, "page not found")
+ }
+ }
+
+ if err != nil {
+ http.Error(w, "internal server error", http.StatusInternalServerError)
+ return
+ }
+
+ w.WriteHeader(status)
+
+ // NOTE: HTML escaping should have already been done by markdown rendering package
+ indexData.Body = template.HTML(body.String()) //nolint:gosec
+
+ // Render the final page with the rendered body
+ if err = components.RenderIndexComponent(w, indexData); err != nil {
+ h.logger.Error("failed to render index component", "err", err)
+ }
+
+ return
+}
+
+func (h *WebHandler) renderPackage(w io.Writer, gnourl *GnoURL) (status int, err error) {
+ h.logger.Info("component render", "path", gnourl.Path, "args", gnourl.Args)
+
+ kind := gnourl.Kind()
+
+ // Display realm help page?
+ if kind == KindRealm && gnourl.WebQuery.Has("help") {
+ return h.renderRealmHelp(w, gnourl)
+ }
+
+ // Display package source page?
+ switch {
+ case gnourl.WebQuery.Has("source"):
+ return h.renderRealmSource(w, gnourl)
+ case kind == KindPure,
+ strings.HasSuffix(gnourl.Path, "/"),
+ isFile(gnourl.Path):
+ i := strings.LastIndexByte(gnourl.Path, '/')
+ if i < 0 {
+ return http.StatusInternalServerError, fmt.Errorf("unable to get ending slash for %q", gnourl.Path)
+ }
+
+ // Fill webquery with file infos
+ gnourl.WebQuery.Set("source", "") // set source
+
+ file := gnourl.Path[i+1:]
+ if file == "" {
+ return h.renderRealmDirectory(w, gnourl)
+ }
+
+ gnourl.WebQuery.Set("file", file)
+ gnourl.Path = gnourl.Path[:i]
+
+ return h.renderRealmSource(w, gnourl)
+ }
+
+ // Render content into the content buffer
+ var content bytes.Buffer
+ meta, err := h.webcli.Render(&content, gnourl.Path, gnourl.EncodeArgs())
+ if err != nil {
+ if errors.Is(err, vm.InvalidPkgPathError{}) {
+ return http.StatusNotFound, components.RenderStatusComponent(w, "not found")
+ }
+
+ h.logger.Error("unable to render markdown", "err", err)
+ return http.StatusInternalServerError, components.RenderStatusComponent(w, "internal error")
+ }
+
+ err = components.RenderRealmComponent(w, components.RealmData{
+ TocItems: &components.RealmTOCData{
+ Items: meta.Items,
+ },
+ // NOTE: `content` should have already been escaped by
+ Content: template.HTML(content.String()), //nolint:gosec
+ })
+ if err != nil {
+ h.logger.Error("unable to render template", "err", err)
+ return http.StatusInternalServerError, components.RenderStatusComponent(w, "internal error")
+ }
+
+ // Write the rendered content to the response writer
+ return http.StatusOK, nil
+}
+
+func (h *WebHandler) renderRealmHelp(w io.Writer, gnourl *GnoURL) (status int, err error) {
+ fsigs, err := h.webcli.Functions(gnourl.Path)
+ if err != nil {
+ h.logger.Error("unable to fetch path functions", "err", err)
+ return http.StatusInternalServerError, components.RenderStatusComponent(w, "internal error")
+ }
+
+ var selArgs map[string]string
+ var selFn string
+ if selFn = gnourl.WebQuery.Get("func"); selFn != "" {
+ for _, fn := range fsigs {
+ if selFn != fn.FuncName {
+ continue
+ }
+
+ selArgs = make(map[string]string)
+ for _, param := range fn.Params {
+ selArgs[param.Name] = gnourl.WebQuery.Get(param.Name)
+ }
+
+ fsigs = []vm.FunctionSignature{fn}
+ break
+ }
+ }
+
+ // Catch last name of the path
+ // XXX: we should probably add a helper within the template
+ realmName := filepath.Base(gnourl.Path)
+ err = components.RenderHelpComponent(w, components.HelpData{
+ SelectedFunc: selFn,
+ SelectedArgs: selArgs,
+ RealmName: realmName,
+ ChainId: h.static.ChainId,
+ // TODO: get chain domain and use that.
+ PkgPath: filepath.Join(DefaultChainDomain, gnourl.Path),
+ Remote: h.static.RemoteHelp,
+ Functions: fsigs,
+ })
+ if err != nil {
+ h.logger.Error("unable to render helper", "err", err)
+ return http.StatusInternalServerError, components.RenderStatusComponent(w, "internal error")
+ }
+
+ return http.StatusOK, nil
+}
+
+func (h *WebHandler) renderRealmSource(w io.Writer, gnourl *GnoURL) (status int, err error) {
+ pkgPath := gnourl.Path
+
+ files, err := h.webcli.Sources(pkgPath)
+ if err != nil {
+ h.logger.Error("unable to list sources file", "path", gnourl.Path, "err", err)
+ return http.StatusInternalServerError, components.RenderStatusComponent(w, "internal error")
+ }
+
+ if len(files) == 0 {
+ h.logger.Debug("no files available", "path", gnourl.Path)
+ return http.StatusOK, components.RenderStatusComponent(w, "no files available")
+ }
+
+ var fileName string
+ file := gnourl.WebQuery.Get("file")
+ if file == "" {
+ fileName = files[0]
+ } else if slices.Contains(files, file) {
+ fileName = file
+ } else {
+ h.logger.Error("unable to render source", "file", file, "err", "file does not exist")
+ return http.StatusInternalServerError, components.RenderStatusComponent(w, "internal error")
+ }
+
+ source, err := h.webcli.SourceFile(pkgPath, fileName)
+ if err != nil {
+ h.logger.Error("unable to get source file", "file", fileName, "err", err)
+ return http.StatusInternalServerError, components.RenderStatusComponent(w, "internal error")
+ }
+
+ // XXX: we should either do this on the front or in the markdown parsing side
+ fileLines := strings.Count(string(source), "\n")
+ fileSizeKb := float64(len(source)) / 1024.0
+ fileSizeStr := fmt.Sprintf("%.2f Kb", fileSizeKb)
+
+ // Highlight code source
+ hsource, err := h.highlightSource(fileName, source)
+ if err != nil {
+ h.logger.Error("unable to highlight source file", "file", fileName, "err", err)
+ return http.StatusInternalServerError, components.RenderStatusComponent(w, "internal error")
+ }
+
+ err = components.RenderSourceComponent(w, components.SourceData{
+ PkgPath: gnourl.Path,
+ Files: files,
+ FileName: fileName,
+ FileCounter: len(files),
+ FileLines: fileLines,
+ FileSize: fileSizeStr,
+ FileSource: template.HTML(hsource), //nolint:gosec
+ })
+ if err != nil {
+ h.logger.Error("unable to render helper", "err", err)
+ return http.StatusInternalServerError, components.RenderStatusComponent(w, "internal error")
+ }
+
+ return http.StatusOK, nil
+}
+
+func (h *WebHandler) renderRealmDirectory(w io.Writer, gnourl *GnoURL) (status int, err error) {
+ pkgPath := gnourl.Path
+
+ files, err := h.webcli.Sources(pkgPath)
+ if err != nil {
+ h.logger.Error("unable to list sources file", "path", gnourl.Path, "err", err)
+ return http.StatusInternalServerError, components.RenderStatusComponent(w, "internal error")
+ }
+
+ if len(files) == 0 {
+ h.logger.Debug("no files available", "path", gnourl.Path)
+ return http.StatusOK, components.RenderStatusComponent(w, "no files available")
+ }
+
+ err = components.RenderDirectoryComponent(w, components.DirData{
+ PkgPath: gnourl.Path,
+ Files: files,
+ FileCounter: len(files),
+ })
+ if err != nil {
+ h.logger.Error("unable to render directory", "err", err)
+ return http.StatusInternalServerError, components.RenderStatusComponent(w, "internal error")
+ }
+
+ return http.StatusOK, nil
+}
+
+func (h *WebHandler) highlightSource(fileName string, src []byte) ([]byte, error) {
+ var lexer chroma.Lexer
+
+ switch strings.ToLower(filepath.Ext(fileName)) {
+ case ".gno":
+ lexer = lexers.Get("go")
+ case ".md":
+ lexer = lexers.Get("markdown")
+ case ".mod":
+ lexer = lexers.Get("gomod")
+ default:
+ lexer = lexers.Get("txt") // file kind not supported, fallback on `.txt`
+ }
+
+ if lexer == nil {
+ return nil, fmt.Errorf("unsupported lexer for file %q", fileName)
+ }
+
+ iterator, err := lexer.Tokenise(nil, string(src))
+ if err != nil {
+ h.logger.Error("unable to ", "fileName", fileName, "err", err)
+ }
+
+ var buff bytes.Buffer
+ if err := h.formatter.Format(&buff, iterator); err != nil {
+ return nil, fmt.Errorf("unable to format source file %q: %w", fileName, err)
+ }
+
+ return buff.Bytes(), nil
+}
+
+func generateBreadcrumbPaths(path string) []components.BreadcrumbPart {
+ split := strings.Split(path, "/")
+ parts := []components.BreadcrumbPart{}
+
+ var name string
+ for i := range split {
+ if name = split[i]; name == "" {
+ continue
+ }
+
+ parts = append(parts, components.BreadcrumbPart{
+ Name: name,
+ Path: strings.Join(split[:i+1], "/"),
+ })
+ }
+
+ return parts
+}
+
+// IsFile checks if the last element of the path is a file (has an extension)
+func isFile(path string) bool {
+ base := filepath.Base(path)
+ ext := filepath.Ext(base)
+ return ext != ""
+}
diff --git a/gno.land/pkg/gnoweb/markdown/highlighting.go b/gno.land/pkg/gnoweb/markdown/highlighting.go
new file mode 100644
index 00000000000..51c66674df1
--- /dev/null
+++ b/gno.land/pkg/gnoweb/markdown/highlighting.go
@@ -0,0 +1,588 @@
+// This file was copied from https://github.com/yuin/goldmark-highlighting
+//
+// package highlighting is an extension for the goldmark(http://github.com/yuin/goldmark).
+//
+// This extension adds syntax-highlighting to the fenced code blocks using
+// chroma(https://github.com/alecthomas/chroma).
+package markdown
+
+import (
+ "bytes"
+ "io"
+ "strconv"
+ "strings"
+
+ "github.com/yuin/goldmark"
+ "github.com/yuin/goldmark/ast"
+ "github.com/yuin/goldmark/parser"
+ "github.com/yuin/goldmark/renderer"
+ "github.com/yuin/goldmark/renderer/html"
+ "github.com/yuin/goldmark/text"
+ "github.com/yuin/goldmark/util"
+
+ "github.com/alecthomas/chroma/v2"
+ chromahtml "github.com/alecthomas/chroma/v2/formatters/html"
+ "github.com/alecthomas/chroma/v2/lexers"
+ "github.com/alecthomas/chroma/v2/styles"
+)
+
+// ImmutableAttributes is a read-only interface for ast.Attributes.
+type ImmutableAttributes interface {
+ // Get returns (value, true) if an attribute associated with given
+ // name exists, otherwise (nil, false)
+ Get(name []byte) (interface{}, bool)
+
+ // GetString returns (value, true) if an attribute associated with given
+ // name exists, otherwise (nil, false)
+ GetString(name string) (interface{}, bool)
+
+ // All returns all attributes.
+ All() []ast.Attribute
+}
+
+type immutableAttributes struct {
+ n ast.Node
+}
+
+func (a *immutableAttributes) Get(name []byte) (interface{}, bool) {
+ return a.n.Attribute(name)
+}
+
+func (a *immutableAttributes) GetString(name string) (interface{}, bool) {
+ return a.n.AttributeString(name)
+}
+
+func (a *immutableAttributes) All() []ast.Attribute {
+ if a.n.Attributes() == nil {
+ return []ast.Attribute{}
+ }
+ return a.n.Attributes()
+}
+
+// CodeBlockContext holds contextual information of code highlighting.
+type CodeBlockContext interface {
+ // Language returns (language, true) if specified, otherwise (nil, false).
+ Language() ([]byte, bool)
+
+ // Highlighted returns true if this code block can be highlighted, otherwise false.
+ Highlighted() bool
+
+ // Attributes return attributes of the code block.
+ Attributes() ImmutableAttributes
+}
+
+type codeBlockContext struct {
+ language []byte
+ highlighted bool
+ attributes ImmutableAttributes
+}
+
+func newCodeBlockContext(language []byte, highlighted bool, attrs ImmutableAttributes) CodeBlockContext {
+ return &codeBlockContext{
+ language: language,
+ highlighted: highlighted,
+ attributes: attrs,
+ }
+}
+
+func (c *codeBlockContext) Language() ([]byte, bool) {
+ if c.language != nil {
+ return c.language, true
+ }
+ return nil, false
+}
+
+func (c *codeBlockContext) Highlighted() bool {
+ return c.highlighted
+}
+
+func (c *codeBlockContext) Attributes() ImmutableAttributes {
+ return c.attributes
+}
+
+// WrapperRenderer renders wrapper elements like div, pre, etc.
+type WrapperRenderer func(w util.BufWriter, context CodeBlockContext, entering bool)
+
+// CodeBlockOptions creates Chroma options per code block.
+type CodeBlockOptions func(ctx CodeBlockContext) []chromahtml.Option
+
+// Config struct holds options for the extension.
+type Config struct {
+ html.Config
+
+ // Style is a highlighting style.
+ // Supported styles are defined under https://github.com/alecthomas/chroma/tree/master/formatters.
+ Style string
+
+ // Pass in a custom Chroma style. If this is not nil, the Style string will be ignored
+ CustomStyle *chroma.Style
+
+ // If set, will try to guess language if none provided.
+ // If the guessing fails, we will fall back to a text lexer.
+ // Note that while Chroma's API supports language guessing, the implementation
+ // is not there yet, so you will currently always get the basic text lexer.
+ GuessLanguage bool
+
+ // FormatOptions is a option related to output formats.
+ // See https://github.com/alecthomas/chroma#the-html-formatter for details.
+ FormatOptions []chromahtml.Option
+
+ // CSSWriter is an io.Writer that will be used as CSS data output buffer.
+ // If WithClasses() is enabled, you can get CSS data corresponds to the style.
+ CSSWriter io.Writer
+
+ // CodeBlockOptions allows set Chroma options per code block.
+ CodeBlockOptions CodeBlockOptions
+
+ // WrapperRenderer allows you to change wrapper elements.
+ WrapperRenderer WrapperRenderer
+}
+
+// NewConfig returns a new Config with defaults.
+func NewConfig() Config {
+ return Config{
+ Config: html.NewConfig(),
+ Style: "github",
+ FormatOptions: []chromahtml.Option{},
+ CSSWriter: nil,
+ WrapperRenderer: nil,
+ CodeBlockOptions: nil,
+ }
+}
+
+// SetOption implements renderer.SetOptioner.
+func (c *Config) SetOption(name renderer.OptionName, value interface{}) {
+ switch name {
+ case optStyle:
+ c.Style = value.(string)
+ case optCustomStyle:
+ c.CustomStyle = value.(*chroma.Style)
+ case optFormatOptions:
+ if value != nil {
+ c.FormatOptions = value.([]chromahtml.Option)
+ }
+ case optCSSWriter:
+ c.CSSWriter = value.(io.Writer)
+ case optWrapperRenderer:
+ c.WrapperRenderer = value.(WrapperRenderer)
+ case optCodeBlockOptions:
+ c.CodeBlockOptions = value.(CodeBlockOptions)
+ case optGuessLanguage:
+ c.GuessLanguage = value.(bool)
+ default:
+ c.Config.SetOption(name, value)
+ }
+}
+
+// Option interface is a functional option interface for the extension.
+type Option interface {
+ renderer.Option
+ // SetHighlightingOption sets given option to the extension.
+ SetHighlightingOption(*Config)
+}
+
+type withHTMLOptions struct {
+ value []html.Option
+}
+
+func (o *withHTMLOptions) SetConfig(c *renderer.Config) {
+ if o.value != nil {
+ for _, v := range o.value {
+ v.(renderer.Option).SetConfig(c)
+ }
+ }
+}
+
+func (o *withHTMLOptions) SetHighlightingOption(c *Config) {
+ if o.value != nil {
+ for _, v := range o.value {
+ v.SetHTMLOption(&c.Config)
+ }
+ }
+}
+
+// WithHTMLOptions is functional option that wraps goldmark HTMLRenderer options.
+func WithHTMLOptions(opts ...html.Option) Option {
+ return &withHTMLOptions{opts}
+}
+
+const (
+ optStyle renderer.OptionName = "HighlightingStyle"
+ optCustomStyle renderer.OptionName = "HighlightingCustomStyle"
+)
+
+var highlightLinesAttrName = []byte("hl_lines")
+
+var (
+ styleAttrName = []byte("hl_style")
+ nohlAttrName = []byte("nohl")
+ linenosAttrName = []byte("linenos")
+ linenosTableAttrValue = []byte("table")
+ linenosInlineAttrValue = []byte("inline")
+ linenostartAttrName = []byte("linenostart")
+)
+
+type withStyle struct {
+ value string
+}
+
+func (o *withStyle) SetConfig(c *renderer.Config) {
+ c.Options[optStyle] = o.value
+}
+
+func (o *withStyle) SetHighlightingOption(c *Config) {
+ c.Style = o.value
+}
+
+// WithStyle is a functional option that changes highlighting style.
+func WithStyle(style string) Option {
+ return &withStyle{style}
+}
+
+type withCustomStyle struct {
+ value *chroma.Style
+}
+
+func (o *withCustomStyle) SetConfig(c *renderer.Config) {
+ c.Options[optCustomStyle] = o.value
+}
+
+func (o *withCustomStyle) SetHighlightingOption(c *Config) {
+ c.CustomStyle = o.value
+}
+
+// WithStyle is a functional option that changes highlighting style.
+func WithCustomStyle(style *chroma.Style) Option {
+ return &withCustomStyle{style}
+}
+
+const optCSSWriter renderer.OptionName = "HighlightingCSSWriter"
+
+type withCSSWriter struct {
+ value io.Writer
+}
+
+func (o *withCSSWriter) SetConfig(c *renderer.Config) {
+ c.Options[optCSSWriter] = o.value
+}
+
+func (o *withCSSWriter) SetHighlightingOption(c *Config) {
+ c.CSSWriter = o.value
+}
+
+// WithCSSWriter is a functional option that sets io.Writer for CSS data.
+func WithCSSWriter(w io.Writer) Option {
+ return &withCSSWriter{w}
+}
+
+const optGuessLanguage renderer.OptionName = "HighlightingGuessLanguage"
+
+type withGuessLanguage struct {
+ value bool
+}
+
+func (o *withGuessLanguage) SetConfig(c *renderer.Config) {
+ c.Options[optGuessLanguage] = o.value
+}
+
+func (o *withGuessLanguage) SetHighlightingOption(c *Config) {
+ c.GuessLanguage = o.value
+}
+
+// WithGuessLanguage is a functional option that toggles language guessing
+// if none provided.
+func WithGuessLanguage(b bool) Option {
+ return &withGuessLanguage{value: b}
+}
+
+const optWrapperRenderer renderer.OptionName = "HighlightingWrapperRenderer"
+
+type withWrapperRenderer struct {
+ value WrapperRenderer
+}
+
+func (o *withWrapperRenderer) SetConfig(c *renderer.Config) {
+ c.Options[optWrapperRenderer] = o.value
+}
+
+func (o *withWrapperRenderer) SetHighlightingOption(c *Config) {
+ c.WrapperRenderer = o.value
+}
+
+// WithWrapperRenderer is a functional option that sets WrapperRenderer that
+// renders wrapper elements like div, pre, etc.
+func WithWrapperRenderer(w WrapperRenderer) Option {
+ return &withWrapperRenderer{w}
+}
+
+const optCodeBlockOptions renderer.OptionName = "HighlightingCodeBlockOptions"
+
+type withCodeBlockOptions struct {
+ value CodeBlockOptions
+}
+
+func (o *withCodeBlockOptions) SetConfig(c *renderer.Config) {
+ c.Options[optCodeBlockOptions] = o.value
+}
+
+func (o *withCodeBlockOptions) SetHighlightingOption(c *Config) {
+ c.CodeBlockOptions = o.value
+}
+
+// WithCodeBlockOptions is a functional option that sets CodeBlockOptions that
+// allows setting Chroma options per code block.
+func WithCodeBlockOptions(c CodeBlockOptions) Option {
+ return &withCodeBlockOptions{value: c}
+}
+
+const optFormatOptions renderer.OptionName = "HighlightingFormatOptions"
+
+type withFormatOptions struct {
+ value []chromahtml.Option
+}
+
+func (o *withFormatOptions) SetConfig(c *renderer.Config) {
+ if _, ok := c.Options[optFormatOptions]; !ok {
+ c.Options[optFormatOptions] = []chromahtml.Option{}
+ }
+ c.Options[optFormatOptions] = append(c.Options[optFormatOptions].([]chromahtml.Option), o.value...)
+}
+
+func (o *withFormatOptions) SetHighlightingOption(c *Config) {
+ c.FormatOptions = append(c.FormatOptions, o.value...)
+}
+
+// WithFormatOptions is a functional option that wraps chroma HTML formatter options.
+func WithFormatOptions(opts ...chromahtml.Option) Option {
+ return &withFormatOptions{opts}
+}
+
+// HTMLRenderer struct is a renderer.NodeRenderer implementation for the extension.
+type HTMLRenderer struct {
+ Config
+}
+
+// NewHTMLRenderer builds a new HTMLRenderer with given options and returns it.
+func NewHTMLRenderer(opts ...Option) renderer.NodeRenderer {
+ r := &HTMLRenderer{
+ Config: NewConfig(),
+ }
+ for _, opt := range opts {
+ opt.SetHighlightingOption(&r.Config)
+ }
+ return r
+}
+
+// RegisterFuncs implements NodeRenderer.RegisterFuncs.
+func (r *HTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
+ reg.Register(ast.KindFencedCodeBlock, r.renderFencedCodeBlock)
+}
+
+func getAttributes(node *ast.FencedCodeBlock, infostr []byte) ImmutableAttributes {
+ if node.Attributes() != nil {
+ return &immutableAttributes{node}
+ }
+ if infostr != nil {
+ attrStartIdx := -1
+
+ for idx, char := range infostr {
+ if char == '{' {
+ attrStartIdx = idx
+ break
+ }
+ }
+ if attrStartIdx > 0 {
+ n := ast.NewTextBlock() // dummy node for storing attributes
+ attrStr := infostr[attrStartIdx:]
+ if attrs, hasAttr := parser.ParseAttributes(text.NewReader(attrStr)); hasAttr {
+ for _, attr := range attrs {
+ n.SetAttribute(attr.Name, attr.Value)
+ }
+ return &immutableAttributes{n}
+ }
+ }
+ }
+ return nil
+}
+
+func (r *HTMLRenderer) renderFencedCodeBlock(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
+ n := node.(*ast.FencedCodeBlock)
+ if !entering {
+ return ast.WalkContinue, nil
+ }
+ language := n.Language(source)
+
+ chromaFormatterOptions := make([]chromahtml.Option, 0, len(r.FormatOptions))
+ for _, opt := range r.FormatOptions {
+ chromaFormatterOptions = append(chromaFormatterOptions, opt)
+ }
+
+ style := r.CustomStyle
+ if style == nil {
+ style = styles.Get(r.Style)
+ }
+ nohl := false
+
+ var info []byte
+ if n.Info != nil {
+ info = n.Info.Segment.Value(source)
+ }
+ attrs := getAttributes(n, info)
+ if attrs != nil {
+ baseLineNumber := 1
+ if linenostartAttr, ok := attrs.Get(linenostartAttrName); ok {
+ if linenostart, ok := linenostartAttr.(float64); ok {
+ baseLineNumber = int(linenostart)
+ chromaFormatterOptions = append(
+ chromaFormatterOptions, chromahtml.BaseLineNumber(baseLineNumber),
+ )
+ }
+ }
+ if linesAttr, hasLinesAttr := attrs.Get(highlightLinesAttrName); hasLinesAttr {
+ if lines, ok := linesAttr.([]interface{}); ok {
+ var hlRanges [][2]int
+ for _, l := range lines {
+ if ln, ok := l.(float64); ok {
+ hlRanges = append(hlRanges, [2]int{int(ln) + baseLineNumber - 1, int(ln) + baseLineNumber - 1})
+ }
+ if rng, ok := l.([]uint8); ok {
+ slices := strings.Split(string(rng), "-")
+ lhs, err := strconv.Atoi(slices[0])
+ if err != nil {
+ continue
+ }
+ rhs := lhs
+ if len(slices) > 1 {
+ rhs, err = strconv.Atoi(slices[1])
+ if err != nil {
+ continue
+ }
+ }
+ hlRanges = append(hlRanges, [2]int{lhs + baseLineNumber - 1, rhs + baseLineNumber - 1})
+ }
+ }
+ chromaFormatterOptions = append(chromaFormatterOptions, chromahtml.HighlightLines(hlRanges))
+ }
+ }
+ if styleAttr, hasStyleAttr := attrs.Get(styleAttrName); hasStyleAttr {
+ if st, ok := styleAttr.([]uint8); ok {
+ styleStr := string(st)
+ style = styles.Get(styleStr)
+ }
+ }
+ if _, hasNohlAttr := attrs.Get(nohlAttrName); hasNohlAttr {
+ nohl = true
+ }
+
+ if linenosAttr, ok := attrs.Get(linenosAttrName); ok {
+ switch v := linenosAttr.(type) {
+ case bool:
+ chromaFormatterOptions = append(chromaFormatterOptions, chromahtml.WithLineNumbers(v))
+ case []uint8:
+ if v != nil {
+ chromaFormatterOptions = append(chromaFormatterOptions, chromahtml.WithLineNumbers(true))
+ }
+ if bytes.Equal(v, linenosTableAttrValue) {
+ chromaFormatterOptions = append(chromaFormatterOptions, chromahtml.LineNumbersInTable(true))
+ } else if bytes.Equal(v, linenosInlineAttrValue) {
+ chromaFormatterOptions = append(chromaFormatterOptions, chromahtml.LineNumbersInTable(false))
+ }
+ }
+ }
+ }
+
+ var lexer chroma.Lexer
+ if language != nil {
+ lexer = lexers.Get(string(language))
+ }
+ if !nohl && (lexer != nil || r.GuessLanguage) {
+ if style == nil {
+ style = styles.Fallback
+ }
+ var buffer bytes.Buffer
+ l := n.Lines().Len()
+ for i := 0; i < l; i++ {
+ line := n.Lines().At(i)
+ buffer.Write(line.Value(source))
+ }
+
+ if lexer == nil {
+ lexer = lexers.Analyse(buffer.String())
+ if lexer == nil {
+ lexer = lexers.Fallback
+ }
+ language = []byte(strings.ToLower(lexer.Config().Name))
+ }
+ lexer = chroma.Coalesce(lexer)
+
+ iterator, err := lexer.Tokenise(nil, buffer.String())
+ if err == nil {
+ c := newCodeBlockContext(language, true, attrs)
+
+ if r.CodeBlockOptions != nil {
+ chromaFormatterOptions = append(chromaFormatterOptions, r.CodeBlockOptions(c)...)
+ }
+ formatter := chromahtml.New(chromaFormatterOptions...)
+ if r.WrapperRenderer != nil {
+ r.WrapperRenderer(w, c, true)
+ }
+ _ = formatter.Format(w, style, iterator) == nil
+ if r.WrapperRenderer != nil {
+ r.WrapperRenderer(w, c, false)
+ }
+ if r.CSSWriter != nil {
+ _ = formatter.WriteCSS(r.CSSWriter, style)
+ }
+ return ast.WalkContinue, nil
+ }
+ }
+
+ var c CodeBlockContext
+ if r.WrapperRenderer != nil {
+ c = newCodeBlockContext(language, false, attrs)
+ r.WrapperRenderer(w, c, true)
+ } else {
+ _, _ = w.WriteString("')
+ }
+ l := n.Lines().Len()
+ for i := 0; i < l; i++ {
+ line := n.Lines().At(i)
+ r.Writer.RawWrite(w, line.Value(source))
+ }
+ if r.WrapperRenderer != nil {
+ r.WrapperRenderer(w, c, false)
+ } else {
+ _, _ = w.WriteString("
\n")
+ }
+ return ast.WalkContinue, nil
+}
+
+type highlighting struct {
+ options []Option
+}
+
+// Highlighting is a goldmark.Extender implementation.
+var Highlighting = &highlighting{
+ options: []Option{},
+}
+
+// NewHighlighting returns a new extension with given options.
+func NewHighlighting(opts ...Option) goldmark.Extender {
+ return &highlighting{
+ options: opts,
+ }
+}
+
+// Extend implements goldmark.Extender.
+func (e *highlighting) Extend(m goldmark.Markdown) {
+ m.Renderer().AddOptions(renderer.WithNodeRenderers(
+ util.Prioritized(NewHTMLRenderer(e.options...), 200),
+ ))
+}
diff --git a/gno.land/pkg/gnoweb/markdown/highlighting_test.go b/gno.land/pkg/gnoweb/markdown/highlighting_test.go
new file mode 100644
index 00000000000..25bc4fedd61
--- /dev/null
+++ b/gno.land/pkg/gnoweb/markdown/highlighting_test.go
@@ -0,0 +1,568 @@
+// This file was copied from https://github.com/yuin/goldmark-highlighting
+
+package markdown
+
+import (
+ "bytes"
+ "fmt"
+ "strings"
+ "testing"
+
+ "github.com/alecthomas/chroma/v2"
+ chromahtml "github.com/alecthomas/chroma/v2/formatters/html"
+ "github.com/yuin/goldmark"
+ "github.com/yuin/goldmark/testutil"
+ "github.com/yuin/goldmark/util"
+)
+
+func TestHighlighting(t *testing.T) {
+ var css bytes.Buffer
+ markdown := goldmark.New(
+ goldmark.WithExtensions(
+ NewHighlighting(
+ WithStyle("monokai"),
+ WithCSSWriter(&css),
+ WithFormatOptions(
+ chromahtml.WithClasses(true),
+ chromahtml.WithLineNumbers(false),
+ ),
+ WithWrapperRenderer(func(w util.BufWriter, c CodeBlockContext, entering bool) {
+ _, ok := c.Language()
+ if entering {
+ if !ok {
+ w.WriteString("")
+ return
+ }
+ w.WriteString(``)
+ } else {
+ if !ok {
+ w.WriteString("")
+ return
+ }
+ w.WriteString(`
`)
+ }
+ }),
+ WithCodeBlockOptions(func(c CodeBlockContext) []chromahtml.Option {
+ if language, ok := c.Language(); ok {
+ // Turn on line numbers for Go only.
+ if string(language) == "go" {
+ return []chromahtml.Option{
+ chromahtml.WithLineNumbers(true),
+ }
+ }
+ }
+ return nil
+ }),
+ ),
+ ),
+ )
+ var buffer bytes.Buffer
+ if err := markdown.Convert([]byte(`
+Title
+=======
+`+"``` go\n"+`func main() {
+ fmt.Println("ok")
+}
+`+"```"+`
+`), &buffer); err != nil {
+ t.Fatal(err)
+ }
+
+ if strings.TrimSpace(buffer.String()) != strings.TrimSpace(`
+Title
+1 func main () {
+2 fmt . Println ( "ok" )
+ 3 }
+
+`) {
+ t.Errorf("failed to render HTML\n%s", buffer.String())
+ }
+
+ expected := strings.TrimSpace(`/* Background */ .bg { color: #f8f8f2; background-color: #272822; }
+/* PreWrapper */ .chroma { color: #f8f8f2; background-color: #272822; }
+/* LineNumbers targeted by URL anchor */ .chroma .ln:target { color: #f8f8f2; background-color: #3c3d38 }
+/* LineNumbersTable targeted by URL anchor */ .chroma .lnt:target { color: #f8f8f2; background-color: #3c3d38 }
+/* Error */ .chroma .err { color: #960050; background-color: #1e0010 }
+/* LineLink */ .chroma .lnlinks { outline: none; text-decoration: none; color: inherit }
+/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; }
+/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; }
+/* LineHighlight */ .chroma .hl { background-color: #3c3d38 }
+/* LineNumbersTable */ .chroma .lnt { white-space: pre; -webkit-user-select: none; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
+/* LineNumbers */ .chroma .ln { white-space: pre; -webkit-user-select: none; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
+/* Line */ .chroma .line { display: flex; }
+/* Keyword */ .chroma .k { color: #66d9ef }
+/* KeywordConstant */ .chroma .kc { color: #66d9ef }
+/* KeywordDeclaration */ .chroma .kd { color: #66d9ef }
+/* KeywordNamespace */ .chroma .kn { color: #f92672 }
+/* KeywordPseudo */ .chroma .kp { color: #66d9ef }
+/* KeywordReserved */ .chroma .kr { color: #66d9ef }
+/* KeywordType */ .chroma .kt { color: #66d9ef }
+/* NameAttribute */ .chroma .na { color: #a6e22e }
+/* NameClass */ .chroma .nc { color: #a6e22e }
+/* NameConstant */ .chroma .no { color: #66d9ef }
+/* NameDecorator */ .chroma .nd { color: #a6e22e }
+/* NameException */ .chroma .ne { color: #a6e22e }
+/* NameFunction */ .chroma .nf { color: #a6e22e }
+/* NameOther */ .chroma .nx { color: #a6e22e }
+/* NameTag */ .chroma .nt { color: #f92672 }
+/* Literal */ .chroma .l { color: #ae81ff }
+/* LiteralDate */ .chroma .ld { color: #e6db74 }
+/* LiteralString */ .chroma .s { color: #e6db74 }
+/* LiteralStringAffix */ .chroma .sa { color: #e6db74 }
+/* LiteralStringBacktick */ .chroma .sb { color: #e6db74 }
+/* LiteralStringChar */ .chroma .sc { color: #e6db74 }
+/* LiteralStringDelimiter */ .chroma .dl { color: #e6db74 }
+/* LiteralStringDoc */ .chroma .sd { color: #e6db74 }
+/* LiteralStringDouble */ .chroma .s2 { color: #e6db74 }
+/* LiteralStringEscape */ .chroma .se { color: #ae81ff }
+/* LiteralStringHeredoc */ .chroma .sh { color: #e6db74 }
+/* LiteralStringInterpol */ .chroma .si { color: #e6db74 }
+/* LiteralStringOther */ .chroma .sx { color: #e6db74 }
+/* LiteralStringRegex */ .chroma .sr { color: #e6db74 }
+/* LiteralStringSingle */ .chroma .s1 { color: #e6db74 }
+/* LiteralStringSymbol */ .chroma .ss { color: #e6db74 }
+/* LiteralNumber */ .chroma .m { color: #ae81ff }
+/* LiteralNumberBin */ .chroma .mb { color: #ae81ff }
+/* LiteralNumberFloat */ .chroma .mf { color: #ae81ff }
+/* LiteralNumberHex */ .chroma .mh { color: #ae81ff }
+/* LiteralNumberInteger */ .chroma .mi { color: #ae81ff }
+/* LiteralNumberIntegerLong */ .chroma .il { color: #ae81ff }
+/* LiteralNumberOct */ .chroma .mo { color: #ae81ff }
+/* Operator */ .chroma .o { color: #f92672 }
+/* OperatorWord */ .chroma .ow { color: #f92672 }
+/* Comment */ .chroma .c { color: #75715e }
+/* CommentHashbang */ .chroma .ch { color: #75715e }
+/* CommentMultiline */ .chroma .cm { color: #75715e }
+/* CommentSingle */ .chroma .c1 { color: #75715e }
+/* CommentSpecial */ .chroma .cs { color: #75715e }
+/* CommentPreproc */ .chroma .cp { color: #75715e }
+/* CommentPreprocFile */ .chroma .cpf { color: #75715e }
+/* GenericDeleted */ .chroma .gd { color: #f92672 }
+/* GenericEmph */ .chroma .ge { font-style: italic }
+/* GenericInserted */ .chroma .gi { color: #a6e22e }
+/* GenericStrong */ .chroma .gs { font-weight: bold }
+/* GenericSubheading */ .chroma .gu { color: #75715e }`)
+
+ gotten := strings.TrimSpace(css.String())
+
+ if expected != gotten {
+ diff := testutil.DiffPretty([]byte(expected), []byte(gotten))
+ t.Errorf("incorrect CSS.\n%s", string(diff))
+ }
+}
+
+func TestHighlighting2(t *testing.T) {
+ markdown := goldmark.New(
+ goldmark.WithExtensions(
+ Highlighting,
+ ),
+ )
+ var buffer bytes.Buffer
+ if err := markdown.Convert([]byte(`
+Title
+=======
+`+"```"+`
+func main() {
+ fmt.Println("ok")
+}
+`+"```"+`
+`), &buffer); err != nil {
+ t.Fatal(err)
+ }
+
+ if strings.TrimSpace(buffer.String()) != strings.TrimSpace(`
+Title
+func main() {
+ fmt.Println("ok")
+}
+
+`) {
+ t.Error("failed to render HTML")
+ }
+}
+
+func TestHighlighting3(t *testing.T) {
+ markdown := goldmark.New(
+ goldmark.WithExtensions(
+ Highlighting,
+ ),
+ )
+ var buffer bytes.Buffer
+ if err := markdown.Convert([]byte(`
+Title
+=======
+
+`+"```"+`cpp {hl_lines=[1,2]}
+#include
+int main() {
+ std::cout<< "hello" << std::endl;
+}
+`+"```"+`
+`), &buffer); err != nil {
+ t.Fatal(err)
+ }
+ if strings.TrimSpace(buffer.String()) != strings.TrimSpace(`
+Title
+#include <iostream>
+ int main () {
+ std:: cout<< "hello" << std:: endl;
+ }
+
+`) {
+ t.Errorf("failed to render HTML:\n%s", buffer.String())
+ }
+}
+
+func TestHighlightingCustom(t *testing.T) {
+ custom := chroma.MustNewStyle("custom", chroma.StyleEntries{
+ chroma.Background: "#cccccc bg:#1d1d1d",
+ chroma.Comment: "#999999",
+ chroma.CommentSpecial: "#cd0000",
+ chroma.Keyword: "#cc99cd",
+ chroma.KeywordDeclaration: "#cc99cd",
+ chroma.KeywordNamespace: "#cc99cd",
+ chroma.KeywordType: "#cc99cd",
+ chroma.Operator: "#67cdcc",
+ chroma.OperatorWord: "#cdcd00",
+ chroma.NameClass: "#f08d49",
+ chroma.NameBuiltin: "#f08d49",
+ chroma.NameFunction: "#f08d49",
+ chroma.NameException: "bold #666699",
+ chroma.NameVariable: "#00cdcd",
+ chroma.LiteralString: "#7ec699",
+ chroma.LiteralNumber: "#f08d49",
+ chroma.LiteralStringBoolean: "#f08d49",
+ chroma.GenericHeading: "bold #000080",
+ chroma.GenericSubheading: "bold #800080",
+ chroma.GenericDeleted: "#e2777a",
+ chroma.GenericInserted: "#cc99cd",
+ chroma.GenericError: "#e2777a",
+ chroma.GenericEmph: "italic",
+ chroma.GenericStrong: "bold",
+ chroma.GenericPrompt: "bold #000080",
+ chroma.GenericOutput: "#888",
+ chroma.GenericTraceback: "#04D",
+ chroma.GenericUnderline: "underline",
+ chroma.Error: "border:#e2777a",
+ })
+
+ var css bytes.Buffer
+ markdown := goldmark.New(
+ goldmark.WithExtensions(
+ NewHighlighting(
+ WithStyle("monokai"), // to make sure it is overrided even if present
+ WithCustomStyle(custom),
+ WithCSSWriter(&css),
+ WithFormatOptions(
+ chromahtml.WithClasses(true),
+ chromahtml.WithLineNumbers(false),
+ ),
+ WithWrapperRenderer(func(w util.BufWriter, c CodeBlockContext, entering bool) {
+ _, ok := c.Language()
+ if entering {
+ if !ok {
+ w.WriteString("")
+ return
+ }
+ w.WriteString(``)
+ } else {
+ if !ok {
+ w.WriteString("")
+ return
+ }
+ w.WriteString(`
`)
+ }
+ }),
+ WithCodeBlockOptions(func(c CodeBlockContext) []chromahtml.Option {
+ if language, ok := c.Language(); ok {
+ // Turn on line numbers for Go only.
+ if string(language) == "go" {
+ return []chromahtml.Option{
+ chromahtml.WithLineNumbers(true),
+ }
+ }
+ }
+ return nil
+ }),
+ ),
+ ),
+ )
+ var buffer bytes.Buffer
+ if err := markdown.Convert([]byte(`
+Title
+=======
+`+"``` go\n"+`func main() {
+ fmt.Println("ok")
+}
+`+"```"+`
+`), &buffer); err != nil {
+ t.Fatal(err)
+ }
+
+ if strings.TrimSpace(buffer.String()) != strings.TrimSpace(`
+Title
+1 func main () {
+2 fmt . Println ( "ok" )
+ 3 }
+
+`) {
+ t.Error("failed to render HTML", buffer.String())
+ }
+
+ expected := strings.TrimSpace(`/* Background */ .bg { color: #cccccc; background-color: #1d1d1d; }
+/* PreWrapper */ .chroma { color: #cccccc; background-color: #1d1d1d; }
+/* LineNumbers targeted by URL anchor */ .chroma .ln:target { color: #cccccc; background-color: #333333 }
+/* LineNumbersTable targeted by URL anchor */ .chroma .lnt:target { color: #cccccc; background-color: #333333 }
+/* Error */ .chroma .err { }
+/* LineLink */ .chroma .lnlinks { outline: none; text-decoration: none; color: inherit }
+/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; }
+/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; }
+/* LineHighlight */ .chroma .hl { background-color: #333333 }
+/* LineNumbersTable */ .chroma .lnt { white-space: pre; -webkit-user-select: none; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #666666 }
+/* LineNumbers */ .chroma .ln { white-space: pre; -webkit-user-select: none; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #666666 }
+/* Line */ .chroma .line { display: flex; }
+/* Keyword */ .chroma .k { color: #cc99cd }
+/* KeywordConstant */ .chroma .kc { color: #cc99cd }
+/* KeywordDeclaration */ .chroma .kd { color: #cc99cd }
+/* KeywordNamespace */ .chroma .kn { color: #cc99cd }
+/* KeywordPseudo */ .chroma .kp { color: #cc99cd }
+/* KeywordReserved */ .chroma .kr { color: #cc99cd }
+/* KeywordType */ .chroma .kt { color: #cc99cd }
+/* NameBuiltin */ .chroma .nb { color: #f08d49 }
+/* NameClass */ .chroma .nc { color: #f08d49 }
+/* NameException */ .chroma .ne { color: #666699; font-weight: bold }
+/* NameFunction */ .chroma .nf { color: #f08d49 }
+/* NameVariable */ .chroma .nv { color: #00cdcd }
+/* LiteralString */ .chroma .s { color: #7ec699 }
+/* LiteralStringAffix */ .chroma .sa { color: #7ec699 }
+/* LiteralStringBacktick */ .chroma .sb { color: #7ec699 }
+/* LiteralStringChar */ .chroma .sc { color: #7ec699 }
+/* LiteralStringDelimiter */ .chroma .dl { color: #7ec699 }
+/* LiteralStringDoc */ .chroma .sd { color: #7ec699 }
+/* LiteralStringDouble */ .chroma .s2 { color: #7ec699 }
+/* LiteralStringEscape */ .chroma .se { color: #7ec699 }
+/* LiteralStringHeredoc */ .chroma .sh { color: #7ec699 }
+/* LiteralStringInterpol */ .chroma .si { color: #7ec699 }
+/* LiteralStringOther */ .chroma .sx { color: #7ec699 }
+/* LiteralStringRegex */ .chroma .sr { color: #7ec699 }
+/* LiteralStringSingle */ .chroma .s1 { color: #7ec699 }
+/* LiteralStringSymbol */ .chroma .ss { color: #7ec699 }
+/* LiteralNumber */ .chroma .m { color: #f08d49 }
+/* LiteralNumberBin */ .chroma .mb { color: #f08d49 }
+/* LiteralNumberFloat */ .chroma .mf { color: #f08d49 }
+/* LiteralNumberHex */ .chroma .mh { color: #f08d49 }
+/* LiteralNumberInteger */ .chroma .mi { color: #f08d49 }
+/* LiteralNumberIntegerLong */ .chroma .il { color: #f08d49 }
+/* LiteralNumberOct */ .chroma .mo { color: #f08d49 }
+/* Operator */ .chroma .o { color: #67cdcc }
+/* OperatorWord */ .chroma .ow { color: #cdcd00 }
+/* Comment */ .chroma .c { color: #999999 }
+/* CommentHashbang */ .chroma .ch { color: #999999 }
+/* CommentMultiline */ .chroma .cm { color: #999999 }
+/* CommentSingle */ .chroma .c1 { color: #999999 }
+/* CommentSpecial */ .chroma .cs { color: #cd0000 }
+/* CommentPreproc */ .chroma .cp { color: #999999 }
+/* CommentPreprocFile */ .chroma .cpf { color: #999999 }
+/* GenericDeleted */ .chroma .gd { color: #e2777a }
+/* GenericEmph */ .chroma .ge { font-style: italic }
+/* GenericError */ .chroma .gr { color: #e2777a }
+/* GenericHeading */ .chroma .gh { color: #000080; font-weight: bold }
+/* GenericInserted */ .chroma .gi { color: #cc99cd }
+/* GenericOutput */ .chroma .go { color: #888888 }
+/* GenericPrompt */ .chroma .gp { color: #000080; font-weight: bold }
+/* GenericStrong */ .chroma .gs { font-weight: bold }
+/* GenericSubheading */ .chroma .gu { color: #800080; font-weight: bold }
+/* GenericTraceback */ .chroma .gt { color: #0044dd }
+/* GenericUnderline */ .chroma .gl { text-decoration: underline }`)
+
+ gotten := strings.TrimSpace(css.String())
+
+ if expected != gotten {
+ diff := testutil.DiffPretty([]byte(expected), []byte(gotten))
+ t.Errorf("incorrect CSS.\n%s", string(diff))
+ }
+}
+
+func TestHighlightingHlLines(t *testing.T) {
+ markdown := goldmark.New(
+ goldmark.WithExtensions(
+ NewHighlighting(
+ WithFormatOptions(
+ chromahtml.WithClasses(true),
+ ),
+ ),
+ ),
+ )
+
+ for i, test := range []struct {
+ attributes string
+ expect []int
+ }{
+ {`hl_lines=["2"]`, []int{2}},
+ {`hl_lines=["2-3",5],linenostart=5`, []int{2, 3, 5}},
+ {`hl_lines=["2-3"]`, []int{2, 3}},
+ {`hl_lines=["2-3",5],linenostart="5"`, []int{2, 3}}, // linenostart must be a number. string values are ignored
+ } {
+ t.Run(fmt.Sprint(i), func(t *testing.T) {
+ var buffer bytes.Buffer
+ codeBlock := fmt.Sprintf(`bash {%s}
+LINE1
+LINE2
+LINE3
+LINE4
+LINE5
+LINE6
+LINE7
+LINE8
+`, test.attributes)
+
+ if err := markdown.Convert([]byte(`
+`+"```"+codeBlock+"```"+`
+`), &buffer); err != nil {
+ t.Fatal(err)
+ }
+
+ for _, line := range test.expect {
+ expectStr := fmt.Sprintf("LINE%d\n ", line)
+ if !strings.Contains(buffer.String(), expectStr) {
+ t.Fatal("got\n", buffer.String(), "\nexpected\n", expectStr)
+ }
+ }
+ })
+ }
+}
+
+type nopPreWrapper struct{}
+
+// Start is called to write a start element.
+func (nopPreWrapper) Start(code bool, styleAttr string) string { return "" }
+
+// End is called to write the end element.
+func (nopPreWrapper) End(code bool) string { return "" }
+
+func TestHighlightingLinenos(t *testing.T) {
+ outputLineNumbersInTable := ``
+
+ for i, test := range []struct {
+ attributes string
+ lineNumbers bool
+ lineNumbersInTable bool
+ expect string
+ }{
+ {`linenos=true`, false, false, `1 LINE1
+ `},
+ {`linenos=false`, false, false, `LINE1
+ `},
+ {``, true, false, `1 LINE1
+ `},
+ {``, true, true, outputLineNumbersInTable},
+ {`linenos=inline`, true, true, `1 LINE1
+ `},
+ {`linenos=foo`, false, false, `1 LINE1
+ `},
+ {`linenos=table`, false, false, outputLineNumbersInTable},
+ } {
+ t.Run(fmt.Sprint(i), func(t *testing.T) {
+ markdown := goldmark.New(
+ goldmark.WithExtensions(
+ NewHighlighting(
+ WithFormatOptions(
+ chromahtml.WithLineNumbers(test.lineNumbers),
+ chromahtml.LineNumbersInTable(test.lineNumbersInTable),
+ chromahtml.WithPreWrapper(nopPreWrapper{}),
+ chromahtml.WithClasses(true),
+ ),
+ ),
+ ),
+ )
+
+ var buffer bytes.Buffer
+ codeBlock := fmt.Sprintf(`bash {%s}
+LINE1
+`, test.attributes)
+
+ content := "```" + codeBlock + "```"
+
+ if err := markdown.Convert([]byte(content), &buffer); err != nil {
+ t.Fatal(err)
+ }
+
+ s := strings.TrimSpace(buffer.String())
+
+ if s != test.expect {
+ t.Fatal("got\n", s, "\nexpected\n", test.expect)
+ }
+ })
+ }
+}
+
+func TestHighlightingGuessLanguage(t *testing.T) {
+ markdown := goldmark.New(
+ goldmark.WithExtensions(
+ NewHighlighting(
+ WithGuessLanguage(true),
+ WithFormatOptions(
+ chromahtml.WithClasses(true),
+ chromahtml.WithLineNumbers(true),
+ ),
+ ),
+ ),
+ )
+ var buffer bytes.Buffer
+ if err := markdown.Convert([]byte("```"+`
+LINE
+`+"```"), &buffer); err != nil {
+ t.Fatal(err)
+ }
+ if strings.TrimSpace(buffer.String()) != strings.TrimSpace(`
+1 LINE
+
+`) {
+ t.Errorf("render mismatch, got\n%s", buffer.String())
+ }
+}
+
+func TestCoalesceNeeded(t *testing.T) {
+ markdown := goldmark.New(
+ goldmark.WithExtensions(
+ NewHighlighting(
+ // WithGuessLanguage(true),
+ WithFormatOptions(
+ chromahtml.WithClasses(true),
+ chromahtml.WithLineNumbers(true),
+ ),
+ ),
+ ),
+ )
+ var buffer bytes.Buffer
+ if err := markdown.Convert([]byte("```http"+`
+GET /foo HTTP/1.1
+Content-Type: application/json
+User-Agent: foo
+
+{
+ "hello": "world"
+}
+`+"```"), &buffer); err != nil {
+ t.Fatal(err)
+ }
+ if strings.TrimSpace(buffer.String()) != strings.TrimSpace(`
+1 GET /foo HTTP / 1.1
+2 Content-Type : application/json
+3 User-Agent : foo
+4
+ 5 {
+6 "hello" : "world"
+ 7 }
+
+`) {
+ t.Errorf("render mismatch, got\n%s", buffer.String())
+ }
+}
diff --git a/gno.land/pkg/gnoweb/markdown/toc.go b/gno.land/pkg/gnoweb/markdown/toc.go
new file mode 100644
index 00000000000..59d4941fabf
--- /dev/null
+++ b/gno.land/pkg/gnoweb/markdown/toc.go
@@ -0,0 +1,137 @@
+// This file is a minimal version of https://github.com/abhinav/goldmark-toc
+
+package markdown
+
+import (
+ "github.com/yuin/goldmark/ast"
+ "github.com/yuin/goldmark/util"
+)
+
+const MaxDepth = 6
+
+type Toc struct {
+ Items []*TocItem
+}
+
+type TocItem struct {
+ // Title of this item in the table of contents.
+ //
+ // This may be blank for items that don't refer to a heading, and only
+ // have sub-items.
+ Title []byte
+
+ // ID is the identifier for the heading that this item refers to. This
+ // is the fragment portion of the link without the "#".
+ //
+ // This may be blank if the item doesn't have an id assigned to it, or
+ // if it doesn't have a title.
+ //
+ // Enable AutoHeadingID in your parser if you expected these to be set
+ // but they weren't.
+ ID []byte
+
+ // Items references children of this item.
+ //
+ // For a heading at level 3, Items, contains the headings at level 4
+ // under that section.
+ Items []*TocItem
+}
+
+func (i TocItem) Anchor() string {
+ return "#" + string(i.ID)
+}
+
+type TocOptions struct {
+ MinDepth, MaxDepth int
+}
+
+func TocInspect(n ast.Node, src []byte, opts TocOptions) (*Toc, error) {
+ // Appends an empty subitem to the given node
+ // and returns a reference to it.
+ appendChild := func(n *TocItem) *TocItem {
+ child := new(TocItem)
+ n.Items = append(n.Items, child)
+ return child
+ }
+
+ // Returns the last subitem of the given node,
+ // creating it if necessary.
+ lastChild := func(n *TocItem) *TocItem {
+ if len(n.Items) > 0 {
+ return n.Items[len(n.Items)-1]
+ }
+ return appendChild(n)
+ }
+
+ var root TocItem
+
+ stack := []*TocItem{&root} // inv: len(stack) >= 1
+ err := ast.Walk(n, func(n ast.Node, entering bool) (ast.WalkStatus, error) {
+ if !entering {
+ return ast.WalkContinue, nil
+ }
+
+ // Skip non-heading node
+ heading, ok := n.(*ast.Heading)
+ if !ok {
+ return ast.WalkContinue, nil
+ }
+
+ if opts.MinDepth > 0 && heading.Level < opts.MinDepth {
+ return ast.WalkSkipChildren, nil
+ }
+
+ if opts.MaxDepth > 0 && heading.Level > opts.MaxDepth {
+ return ast.WalkSkipChildren, nil
+ }
+
+ // The heading is deeper than the current depth.
+ // Append empty items to match the heading's level.
+ for len(stack) < heading.Level {
+ parent := stack[len(stack)-1]
+ stack = append(stack, lastChild(parent))
+ }
+
+ // The heading is shallower than the current depth.
+ // Move back up the stack until we reach the heading's level.
+ if len(stack) > heading.Level {
+ stack = stack[:heading.Level]
+ }
+
+ parent := stack[len(stack)-1]
+ target := lastChild(parent)
+ if len(target.Title) > 0 || len(target.Items) > 0 {
+ target = appendChild(parent)
+ }
+
+ target.Title = util.UnescapePunctuations(heading.Text(src))
+ if id, ok := n.AttributeString("id"); ok {
+ target.ID, _ = id.([]byte)
+ }
+
+ return ast.WalkSkipChildren, nil
+ })
+
+ root.Items = compactItems(root.Items)
+
+ return &Toc{Items: root.Items}, err
+}
+
+// compactItems removes items with no titles
+// from the given list of items.
+//
+// Children of removed items will be promoted to the parent item.
+func compactItems(items []*TocItem) []*TocItem {
+ result := make([]*TocItem, 0)
+ for _, item := range items {
+ if len(item.Title) == 0 {
+ result = append(result, compactItems(item.Items)...)
+ continue
+ }
+
+ item.Items = compactItems(item.Items)
+ result = append(result, item)
+ }
+
+ return result
+}
diff --git a/gno.land/pkg/gnoweb/public/favicon.ico b/gno.land/pkg/gnoweb/public/favicon.ico
new file mode 100644
index 00000000000..528c362c44a
Binary files /dev/null and b/gno.land/pkg/gnoweb/public/favicon.ico differ
diff --git a/gno.land/pkg/gnoweb/public/fonts/intervar/Intervar.woff2 b/gno.land/pkg/gnoweb/public/fonts/intervar/Intervar.woff2
new file mode 100644
index 00000000000..891fc5cc567
Binary files /dev/null and b/gno.land/pkg/gnoweb/public/fonts/intervar/Intervar.woff2 differ
diff --git a/gno.land/pkg/gnoweb/public/fonts/roboto/roboto-mono-normal.woff b/gno.land/pkg/gnoweb/public/fonts/roboto/roboto-mono-normal.woff
new file mode 100644
index 00000000000..2c58fe2d6d7
Binary files /dev/null and b/gno.land/pkg/gnoweb/public/fonts/roboto/roboto-mono-normal.woff differ
diff --git a/gno.land/pkg/gnoweb/public/fonts/roboto/roboto-mono-normal.woff2 b/gno.land/pkg/gnoweb/public/fonts/roboto/roboto-mono-normal.woff2
new file mode 100644
index 00000000000..53d081f3a53
Binary files /dev/null and b/gno.land/pkg/gnoweb/public/fonts/roboto/roboto-mono-normal.woff2 differ
diff --git a/gno.land/pkg/gnoweb/public/imgs/gnoland.svg b/gno.land/pkg/gnoweb/public/imgs/gnoland.svg
new file mode 100644
index 00000000000..30d2f3ef56a
--- /dev/null
+++ b/gno.land/pkg/gnoweb/public/imgs/gnoland.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/gno.land/pkg/gnoweb/public/js/copy.js b/gno.land/pkg/gnoweb/public/js/copy.js
new file mode 100644
index 00000000000..918a30b1ca3
--- /dev/null
+++ b/gno.land/pkg/gnoweb/public/js/copy.js
@@ -0,0 +1 @@
+var s=class o{DOM;static FEEDBACK_DELAY=750;btnClicked=null;btnClickedIcons=[];isAnimationRunning=!1;static SELECTORS={button:"[data-copy-btn]",icon:"[data-copy-icon] > use",content:t=>`[data-copy-content="${t}"]`};constructor(){this.DOM={el:document.querySelector("main")},this.DOM.el?this.init():console.warn("Copy: Main container not found.")}init(){this.bindEvents()}bindEvents(){this.DOM.el?.addEventListener("click",this.handleClick.bind(this))}handleClick(t){let e=t.target.closest(o.SELECTORS.button);if(!e)return;this.btnClicked=e,this.btnClickedIcons=Array.from(e.querySelectorAll(o.SELECTORS.icon));let i=e.getAttribute("data-copy-btn");if(!i){console.warn("Copy: No content ID found on the button.");return}let r=this.DOM.el?.querySelector(o.SELECTORS.content(i));r?this.copyToClipboard(r,this.btnClickedIcons):console.warn(`Copy: No content found for ID "${i}".`)}sanitizeContent(t){let n=t.innerHTML.replace(/]*class="chroma-ln"[^>]*>[\s\S]*?<\/span>/g,""),e=document.createElement("div");return e.innerHTML=n,e.textContent?.trim()||""}toggleIcons(t){t.forEach(n=>{n.classList.toggle("hidden")})}showFeedback(t){!this.btnClicked||this.isAnimationRunning===!0||(this.isAnimationRunning=!0,this.toggleIcons(t),window.setTimeout(()=>{this.toggleIcons(t),this.isAnimationRunning=!1},o.FEEDBACK_DELAY))}async copyToClipboard(t,n){let e=this.sanitizeContent(t);if(!navigator.clipboard){console.error("Copy: Clipboard API is not supported in this browser."),this.showFeedback(n);return}try{await navigator.clipboard.writeText(e),this.showFeedback(n)}catch(i){console.error("Copy: Error while copying text.",i),this.showFeedback(n)}}},a=()=>new s;export{a as default};
diff --git a/gno.land/pkg/gnoweb/public/js/index.js b/gno.land/pkg/gnoweb/public/js/index.js
new file mode 100644
index 00000000000..e990dd91f5f
--- /dev/null
+++ b/gno.land/pkg/gnoweb/public/js/index.js
@@ -0,0 +1 @@
+(()=>{let s={copy:{selector:"[data-copy-btn]",path:"/public/js/copy.js"},help:{selector:"#help",path:"/public/js/realmhelp.js"},searchBar:{selector:"#header-searchbar",path:"/public/js/searchbar.js"}},r=async({selector:e,path:o})=>{if(document.querySelector(e))try{(await import(o)).default()}catch(t){console.error(`Error while loading script ${o}:`,t)}else console.warn(`Module not loaded: no element matches selector "${e}"`)},l=async()=>{let e=Object.values(s).map(o=>r(o));await Promise.all(e)};document.addEventListener("DOMContentLoaded",l)})();
diff --git a/gno.land/pkg/gnoweb/public/js/realmhelp.js b/gno.land/pkg/gnoweb/public/js/realmhelp.js
new file mode 100644
index 00000000000..9b045061a00
--- /dev/null
+++ b/gno.land/pkg/gnoweb/public/js/realmhelp.js
@@ -0,0 +1 @@
+var s=class a{DOM;funcList;static SELECTORS={container:"#help",func:"[data-func]",addressInput:"[data-role='help-input-addr']",cmdModeSelect:"[data-role='help-select-mode']"};constructor(){this.DOM={el:document.querySelector(a.SELECTORS.container),funcs:[],addressInput:null,cmdModeSelect:null},this.funcList=[],this.DOM.el?this.init():console.warn("Help: Main container not found.")}init(){let{el:e}=this.DOM;e&&(this.DOM.funcs=Array.from(e.querySelectorAll(a.SELECTORS.func)),this.DOM.addressInput=e.querySelector(a.SELECTORS.addressInput),this.DOM.cmdModeSelect=e.querySelector(a.SELECTORS.cmdModeSelect),console.log(this.DOM),this.funcList=this.DOM.funcs.map(t=>new r(t)),this.bindEvents())}bindEvents(){let{addressInput:e,cmdModeSelect:t}=this.DOM;e?.addEventListener("input",()=>{this.funcList.forEach(n=>n.updateAddr(e.value))}),t?.addEventListener("change",n=>{let d=n.target;this.funcList.forEach(l=>l.updateMode(d.value))})}},r=class a{DOM;funcName;static SELECTORS={address:"[data-role='help-code-address']",args:"[data-role='help-code-args']",mode:"[data-code-mode]",paramInput:"[data-role='help-param-input']"};constructor(e){this.DOM={el:e,addrs:Array.from(e.querySelectorAll(a.SELECTORS.address)),args:Array.from(e.querySelectorAll(a.SELECTORS.args)),modes:Array.from(e.querySelectorAll(a.SELECTORS.mode))},this.funcName=e.dataset.func||null,this.bindEvents()}bindEvents(){this.DOM.el.addEventListener("input",e=>{let t=e.target;t.dataset.role==="help-param-input"&&this.updateArg(t.dataset.param||"",t.value)})}updateArg(e,t){this.DOM.args.filter(n=>n.dataset.arg===e).forEach(n=>{n.textContent=t.trim()||""})}updateAddr(e){this.DOM.addrs.forEach(t=>{t.textContent=e.trim()||"ADDRESS"})}updateMode(e){this.DOM.modes.forEach(t=>{let n=t.dataset.codeMode===e;t.className=n?"inline":"hidden",t.dataset.copyContent=n?`help-cmd-${this.funcName}`:""})}},i=()=>new s;export{i as default};
diff --git a/gno.land/pkg/gnoweb/public/js/searchbar.js b/gno.land/pkg/gnoweb/public/js/searchbar.js
new file mode 100644
index 00000000000..e8012b9b6d9
--- /dev/null
+++ b/gno.land/pkg/gnoweb/public/js/searchbar.js
@@ -0,0 +1 @@
+var n=class r{DOM;baseUrl;static SELECTORS={container:"#header-searchbar",inputSearch:"[data-role='header-input-search']",breadcrumb:"[data-role='header-breadcrumb-search']"};constructor(){this.DOM={el:document.querySelector(r.SELECTORS.container),inputSearch:null,breadcrumb:null},this.baseUrl=window.location.origin,this.DOM.el?this.init():console.warn("SearchBar: Main container not found.")}init(){let{el:e}=this.DOM;this.DOM.inputSearch=e?.querySelector(r.SELECTORS.inputSearch)??null,this.DOM.breadcrumb=e?.querySelector(r.SELECTORS.breadcrumb)??null,this.DOM.inputSearch||console.warn("SearchBar: Input element for search not found."),this.bindEvents()}bindEvents(){this.DOM.el?.addEventListener("submit",e=>{e.preventDefault(),this.searchUrl()})}searchUrl(){let e=this.DOM.inputSearch?.value.trim();if(e){let t=e;/^https?:\/\//i.test(t)||(t=`${this.baseUrl}${t.startsWith("/")?"":"/"}${t}`);try{window.location.href=new URL(t).href}catch{console.error("SearchBar: Invalid URL. Please enter a valid URL starting with http:// or https://.")}}else console.error("SearchBar: Please enter a URL to search.")}},i=()=>new n;export{i as default};
diff --git a/gno.land/pkg/gnoweb/public/styles.css b/gno.land/pkg/gnoweb/public/styles.css
new file mode 100644
index 00000000000..9d79989f1f8
--- /dev/null
+++ b/gno.land/pkg/gnoweb/public/styles.css
@@ -0,0 +1,3 @@
+@font-face{font-family:Roboto;font-style:normal;font-weight:900;font-display:swap;src:url(fonts/roboto/roboto-mono-normal.woff2) format("woff2"),url(fonts/roboto/roboto-mono-normal.woff) format("woff")}@font-face{font-family:Inter var;font-weight:100 900;font-display:block;font-style:oblique 0deg 10deg;src:url(fonts/intervar/Intervar.woff2) format("woff2")}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }
+
+/*! tailwindcss v3.4.14 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #bdbdbd}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:Roboto,Menlo,Consolas,Ubuntu Mono,Roboto Mono,DejaVu Sans Mono,monospace;;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#7c7c7c}input::placeholder,textarea::placeholder{opacity:1;color:#7c7c7c}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}html{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity));font-family:Inter var,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji,sans-serif;font-size:1rem;--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity));font-feature-settings:"kern" on,"liga" on,"calt" on,"zero" on;-webkit-font-feature-settings:"kern" on,"liga" on,"calt" on,"zero" on;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;text-size-adjust:100%;-moz-osx-font-smoothing:grayscale;font-smoothing:antialiased;font-variant-ligatures:contextual common-ligatures;font-kerning:normal;text-rendering:optimizeLegibility}svg{max-height:100%;max-width:100%}form{margin-top:0;margin-bottom:0}.realm-content{overflow-wrap:break-word;padding-top:2.5rem;font-size:1rem}.realm-content>:first-child{margin-top:0!important}.realm-content a{font-weight:500;--tw-text-opacity:1;color:rgb(34 108 87/var(--tw-text-opacity))}.realm-content a:hover{text-decoration-line:underline}.realm-content h1,.realm-content h2,.realm-content h3,.realm-content h4{margin-top:3rem;line-height:1.25;--tw-text-opacity:1;color:rgb(8 8 9/var(--tw-text-opacity))}.realm-content h2,.realm-content h2 *{font-weight:700}.realm-content h3,.realm-content h3 *,.realm-content h4,.realm-content h4 *{font-weight:600}.realm-content h1+h2,.realm-content h2+h3,.realm-content h3+h4{margin-top:1rem}.realm-content h1{font-size:2.375rem;font-weight:700}.realm-content h2{font-size:1.5rem}.realm-content h3{margin-top:2.5rem;font-size:1.25rem}.realm-content h3,.realm-content h4{--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity))}.realm-content h4{margin-top:1.5rem;margin-bottom:1.5rem;font-size:1.125rem;font-weight:500}.realm-content p{margin-top:1.25rem;margin-bottom:1.25rem}.realm-content strong{font-weight:700;--tw-text-opacity:1;color:rgb(8 8 9/var(--tw-text-opacity))}.realm-content strong *{font-weight:700}.realm-content em{font-style:oblique 10deg}.realm-content blockquote{margin-top:1rem;margin-bottom:1rem;border-left-width:4px;--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity));padding-left:1rem;--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity));font-style:oblique 10deg}.realm-content ol,.realm-content ul{margin-top:1.5rem;margin-bottom:1.5rem;padding-left:1rem}.realm-content ol li,.realm-content ul li{margin-bottom:.5rem}.realm-content img{margin-top:2rem;margin-bottom:2rem;max-width:100%}.realm-content figure{margin-top:1.5rem;margin-bottom:1.5rem;text-align:center}.realm-content figcaption{font-size:.875rem;--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity))}.realm-content :not(pre)>code{border-radius:.25rem;background-color:rgb(226 226 226/var(--tw-bg-opacity));padding:.125rem .25rem;font-size:.875rem}.realm-content :not(pre)>code,.realm-content pre{--tw-bg-opacity:1;font-family:Roboto,Menlo,Consolas,Ubuntu Mono,Roboto Mono,DejaVu Sans Mono,monospace;}.realm-content pre{overflow-x:auto;border-radius:.375rem;background-color:rgb(240 240 240/var(--tw-bg-opacity));padding:1rem}.realm-content hr{margin-top:2.5rem;margin-bottom:2.5rem;border-top-width:1px;--tw-border-opacity:1;border-color:rgb(226 226 226/var(--tw-border-opacity))}.realm-content table{margin-top:2rem;margin-bottom:2rem;width:100%;border-collapse:collapse}.realm-content td,.realm-content th{border-width:1px;--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity));padding:.5rem 1rem}.realm-content th{--tw-bg-opacity:1;background-color:rgb(226 226 226/var(--tw-bg-opacity));font-weight:700}.realm-content caption{margin-top:.5rem;text-align:left;font-size:.875rem;--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity))}.realm-content q{margin-top:1.5rem;margin-bottom:1.5rem;border-left-width:4px;--tw-border-opacity:1;border-left-color:rgb(204 204 204/var(--tw-border-opacity));padding-left:1rem;--tw-text-opacity:1;color:rgb(85 85 85/var(--tw-text-opacity));font-style:oblique 10deg;quotes:"“" "”" "‘" "’"}.realm-content q:after,.realm-content q:before{margin-right:.25rem;font-size:1.5rem;--tw-text-opacity:1;color:rgb(153 153 153/var(--tw-text-opacity));content:open-quote;vertical-align:-.4rem}.realm-content q:after{content:close-quote}.realm-content q:before{content:open-quote}.realm-content q:after{content:close-quote}.realm-content ol ol,.realm-content ol ul,.realm-content ul ol,.realm-content ul ul{margin-top:.75rem;margin-bottom:.5rem;padding-left:1rem}.realm-content ul{list-style-type:disc}.realm-content ol{list-style-type:decimal}.realm-content table th:first-child,.realm-content td:first-child{padding-left:0}.realm-content table th:last-child,.realm-content td:last-child{padding-right:0}.realm-content abbr[title]{cursor:help;border-bottom-width:1px;border-style:dotted}.realm-content details{margin-top:1.25rem;margin-bottom:1.25rem}.realm-content summary{cursor:pointer;font-weight:700}.realm-content a code{color:inherit}.realm-content video{margin-top:2rem;margin-bottom:2rem;max-width:100%}.realm-content math{font-family:Roboto,Menlo,Consolas,Ubuntu Mono,Roboto Mono,DejaVu Sans Mono,monospace;}.realm-content small{font-size:.875rem}.realm-content del{text-decoration-line:line-through}.realm-content sub{vertical-align:sub;font-size:.75rem}.realm-content sup{vertical-align:super;font-size:.75rem}.realm-content button,.realm-content input{border-width:1px;--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity));padding:.5rem 1rem}main :is(h1,h2,h3,h4){scroll-margin-top:6rem}::-moz-selection{--tw-bg-opacity:1;background-color:rgb(34 108 87/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}::selection{--tw-bg-opacity:1;background-color:rgb(34 108 87/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.sidemenu .peer:checked+label>svg{--tw-text-opacity:1;color:rgb(34 108 87/var(--tw-text-opacity))}.toc-expend-btn:has(#toc-expend:checked)+nav{display:block}.toc-expend-btn:has(#toc-expend:checked) .toc-expend-btn_ico{--tw-rotate:180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.main-header:has(#sidemenu-docs:checked)+main #sidebar #sidebar-docs,.main-header:has(#sidemenu-meta:checked)+main #sidebar #sidebar-meta,.main-header:has(#sidemenu-source:checked)+main #sidebar #sidebar-source,.main-header:has(#sidemenu-summary:checked)+main #sidebar #sidebar-summary{display:block}@media (min-width:40rem){:is(.main-header:has(#sidemenu-source:checked),.main-header:has(#sidemenu-docs:checked),.main-header:has(#sidemenu-meta:checked)) .main-navigation,:is(.main-header:has(#sidemenu-source:checked),.main-header:has(#sidemenu-docs:checked),.main-header:has(#sidemenu-meta:checked))+main .realm-content{grid-column:span 6/span 6}:is(.main-header:has(#sidemenu-source:checked),.main-header:has(#sidemenu-docs:checked),.main-header:has(#sidemenu-meta:checked)) .sidemenu,:is(.main-header:has(#sidemenu-source:checked),.main-header:has(#sidemenu-docs:checked),.main-header:has(#sidemenu-meta:checked))+main #sidebar{grid-column:span 4/span 4}}:is(.main-header:has(#sidemenu-source:checked),.main-header:has(#sidemenu-docs:checked),.main-header:has(#sidemenu-meta:checked))+main #sidebar:before{position:absolute;top:0;left:-1.75rem;z-index:-1;display:block;height:100%;width:50vw;--tw-bg-opacity:1;background-color:rgb(226 226 226/var(--tw-bg-opacity));--tw-content:"";content:var(--tw-content)}main :is(.source-code)>pre{overflow:scroll;border-radius:.375rem;--tw-bg-opacity:1!important;background-color:rgb(255 255 255/var(--tw-bg-opacity))!important;padding:1rem .25rem;font-family:Roboto,Menlo,Consolas,Ubuntu Mono,Roboto Mono,DejaVu Sans Mono,monospace;;font-size:.875rem}@media (min-width:40rem){main :is(.source-code)>pre{padding:2rem .75rem;font-size:1rem}}main .realm-content>pre a:hover{text-decoration-line:none}main :is(.realm-content,.source-code)>pre .chroma-ln:target{background-color:transparent!important}main :is(.realm-content,.source-code)>pre .chroma-line:has(.chroma-ln:target),main :is(.realm-content,.source-code)>pre .chroma-line:has(.chroma-ln:target) .chroma-cl,main :is(.realm-content,.source-code)>pre .chroma-line:has(.chroma-lnlinks:hover),main :is(.realm-content,.source-code)>pre .chroma-line:has(.chroma-lnlinks:hover) .chroma-cl{border-radius:.375rem;--tw-bg-opacity:1!important;background-color:rgb(226 226 226/var(--tw-bg-opacity))!important}main :is(.realm-content,.source-code)>pre .chroma-ln{scroll-margin-top:6rem}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.bottom-1{bottom:.25rem}.left-0{left:0}.right-2{right:.5rem}.right-3{right:.75rem}.top-0{top:0}.top-1\/2{top:50%}.top-14{top:3.5rem}.top-2{top:.5rem}.z-max{z-index:9999}.col-span-1{grid-column:span 1/span 1}.col-span-10{grid-column:span 10/span 10}.col-span-3{grid-column:span 3/span 3}.col-span-7{grid-column:span 7/span 7}.row-span-1{grid-row:span 1/span 1}.row-start-1{grid-row-start:1}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-8{margin-bottom:2rem}.mr-10{margin-right:2.5rem}.mt-1{margin-top:.25rem}.mt-10{margin-top:2.5rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.grid{display:grid}.hidden{display:none}.h-10{height:2.5rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-full{height:100%}.max-h-screen{max-height:100vh}.min-h-full{min-height:100%}.min-h-screen{min-height:100vh}.w-10{width:2.5rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-full{width:100%}.min-w-2{min-width:.5rem}.min-w-48{min-width:12rem}.max-w-screen-max{max-width:98.75rem}.shrink-0{flex-shrink:0}.grow-\[2\]{flex-grow:2}.-translate-y-1\/2{--tw-translate-y:-50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-pointer{cursor:pointer}.list-none{list-style-type:none}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-flow-dense{grid-auto-flow:dense}.auto-rows-min{grid-auto-rows:min-content}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-10{grid-template-columns:repeat(10,minmax(0,1fr))}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.items-stretch{align-items:stretch}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-8{gap:2rem}.gap-x-20{-moz-column-gap:5rem;column-gap:5rem}.gap-x-3{-moz-column-gap:.75rem;column-gap:.75rem}.gap-y-2{row-gap:.5rem}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-scroll{overflow:scroll}.whitespace-pre-wrap{white-space:pre-wrap}.rounded{border-radius:.375rem}.rounded-sm{border-radius:.25rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-t{border-top-width:1px}.border-gray-100{--tw-border-opacity:1;border-color:rgb(226 226 226/var(--tw-border-opacity))}.border-gray-300{--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity))}.border-gray-400{--tw-border-opacity:1;border-color:rgb(124 124 124/var(--tw-border-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(226 226 226/var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(240 240 240/var(--tw-bg-opacity))}.bg-light{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-transparent{background-color:transparent}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-4{padding:1rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-10{padding-left:2.5rem;padding-right:2.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-px{padding-top:1px;padding-bottom:1px}.pb-24{padding-bottom:6rem}.pb-3{padding-bottom:.75rem}.pb-4{padding-bottom:1rem}.pb-6{padding-bottom:1.5rem}.pb-8{padding-bottom:2rem}.pl-4{padding-left:1rem}.pr-10{padding-right:2.5rem}.pt-0\.5{padding-top:.125rem}.pt-2{padding-top:.5rem}.font-mono{font-family:Roboto,Menlo,Consolas,Ubuntu Mono,Roboto Mono,DejaVu Sans Mono,monospace;}.text-100{font-size:.875rem}.text-200{font-size:1rem}.text-50{font-size:.75rem}.text-600{font-size:1.5rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.capitalize{text-transform:capitalize}.leading-tight{line-height:1.25}.text-gray-300{--tw-text-opacity:1;color:rgb(153 153 153/var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity:1;color:rgb(124 124 124/var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity:1;color:rgb(19 19 19/var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgb(8 8 9/var(--tw-text-opacity))}.text-green-600{--tw-text-opacity:1;color:rgb(34 108 87/var(--tw-text-opacity))}.outline-none{outline:2px solid transparent;outline-offset:2px}.text-stroke{-webkit-text-stroke:currentColor;-webkit-text-stroke-width:.6px}.no-scrollbar::-webkit-scrollbar{display:none}.no-scrollbar{-ms-overflow-style:none;scrollbar-width:none}.\*\:pl-0>*{padding-left:0}.before\:px-\[0\.18rem\]:before{content:var(--tw-content);padding-left:.18rem;padding-right:.18rem}.before\:text-gray-300:before{content:var(--tw-content);--tw-text-opacity:1;color:rgb(153 153 153/var(--tw-text-opacity))}.before\:content-\[\'\/\'\]:before{--tw-content:"/";content:var(--tw-content)}.before\:content-\[\'open\'\]:before{--tw-content:"open";content:var(--tw-content)}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:bottom-0:after{content:var(--tw-content);bottom:0}.after\:left-0:after{content:var(--tw-content);left:0}.after\:block:after{content:var(--tw-content);display:block}.after\:h-1:after{content:var(--tw-content);height:.25rem}.after\:w-full:after{content:var(--tw-content);width:100%}.after\:rounded-t-sm:after{content:var(--tw-content);border-top-left-radius:.25rem;border-top-right-radius:.25rem}.after\:bg-green-600:after{content:var(--tw-content);--tw-bg-opacity:1;background-color:rgb(34 108 87/var(--tw-bg-opacity))}.first\:border-t:first-child{border-top-width:1px}.hover\:border-gray-300:hover{--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity))}.hover\:bg-gray-100:hover{--tw-bg-opacity:1;background-color:rgb(226 226 226/var(--tw-bg-opacity))}.hover\:bg-gray-50:hover{--tw-bg-opacity:1;background-color:rgb(240 240 240/var(--tw-bg-opacity))}.hover\:bg-green-600:hover{--tw-bg-opacity:1;background-color:rgb(34 108 87/var(--tw-bg-opacity))}.hover\:text-gray-600:hover{--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity))}.hover\:text-green-600:hover{--tw-text-opacity:1;color:rgb(34 108 87/var(--tw-text-opacity))}.hover\:text-light:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.hover\:underline:hover{text-decoration-line:underline}.focus\:border-gray-300:focus{--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity))}.focus\:border-l-gray-300:focus{--tw-border-opacity:1;border-left-color:rgb(153 153 153/var(--tw-border-opacity))}.group:hover .group-hover\:border-gray-300{--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity))}.group:hover .group-hover\:border-l-gray-300{--tw-border-opacity:1;border-left-color:rgb(153 153 153/var(--tw-border-opacity))}.group.is-active .group-\[\.is-active\]\:text-green-600{--tw-text-opacity:1;color:rgb(34 108 87/var(--tw-text-opacity))}.peer:checked~.peer-checked\:before\:content-\[\'close\'\]:before{--tw-content:"close";content:var(--tw-content)}.peer:focus-within~.peer-focus-within\:hidden{display:none}.has-\[ul\:empty\]\:hidden:has(ul:empty){display:none}.has-\[\:focus-within\]\:border-gray-300:has(:focus-within){--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity))}.has-\[\:focus\]\:border-gray-300:has(:focus){--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity))}@media (min-width:30rem){.sm\:gap-6{gap:1.5rem}}@media (min-width:40rem){.md\:col-span-3{grid-column:span 3/span 3}.md\:mb-0{margin-bottom:0}.md\:h-4{height:1rem}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:gap-x-8{-moz-column-gap:2rem;column-gap:2rem}.md\:px-10{padding-left:2.5rem;padding-right:2.5rem}.md\:pb-0{padding-bottom:0}}@media (min-width:51.25rem){.lg\:order-2{order:2}.lg\:col-span-3{grid-column:span 3/span 3}.lg\:col-span-7{grid-column:span 7/span 7}.lg\:row-span-2{grid-row:span 2/span 2}.lg\:row-start-1{grid-row-start:1}.lg\:row-start-2{grid-row-start:2}.lg\:mb-4{margin-bottom:1rem}.lg\:mt-0{margin-top:0}.lg\:mt-10{margin-top:2.5rem}.lg\:block{display:block}.lg\:hidden{display:none}.lg\:grid-cols-10{grid-template-columns:repeat(10,minmax(0,1fr))}.lg\:flex-row{flex-direction:row}.lg\:justify-start{justify-content:flex-start}.lg\:justify-between{justify-content:space-between}.lg\:gap-x-20{-moz-column-gap:5rem;column-gap:5rem}.lg\:border-none{border-style:none}.lg\:bg-transparent{background-color:transparent}.lg\:p-0{padding:0}.lg\:px-0{padding-left:0;padding-right:0}.lg\:px-2{padding-left:.5rem;padding-right:.5rem}.lg\:py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.lg\:pb-28{padding-bottom:7rem}.lg\:pt-2{padding-top:.5rem}.lg\:text-200{font-size:1rem}.lg\:font-semibold{font-weight:600}.lg\:hover\:bg-transparent:hover{background-color:transparent}}@media (min-width:63.75rem){.xl\:inline{display:inline}.xl\:hidden{display:none}.xl\:grid-cols-10{grid-template-columns:repeat(10,minmax(0,1fr))}.xl\:flex-row{flex-direction:row}.xl\:items-center{align-items:center}.xl\:gap-20{gap:5rem}.xl\:gap-6{gap:1.5rem}.xl\:pt-0{padding-top:0}}@media (min-width:85.375rem){.xxl\:inline-block{display:inline-block}.xxl\:h-4{height:1rem}.xxl\:w-4{width:1rem}.xxl\:gap-20{gap:5rem}.xxl\:gap-x-32{-moz-column-gap:8rem;column-gap:8rem}.xxl\:pr-1{padding-right:.25rem}}
\ No newline at end of file
diff --git a/gno.land/pkg/gnoweb/static.go b/gno.land/pkg/gnoweb/static.go
new file mode 100644
index 00000000000..7900dcd7891
--- /dev/null
+++ b/gno.land/pkg/gnoweb/static.go
@@ -0,0 +1,28 @@
+package gnoweb
+
+import (
+ "embed"
+ "net/http"
+)
+
+//go:embed public/*
+var assets embed.FS
+
+func disableCache(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Cache-Control", "no-store")
+ next.ServeHTTP(w, r)
+ })
+}
+
+// AssetHandler returns the handler to serve static assets. If cache is true,
+// these will be served using the static files embedded in the binary; otherwise
+// they will served from the filesystem.
+func AssetHandler() http.Handler {
+ return http.FileServer(http.FS(assets))
+}
+
+func DevAssetHandler(path, dir string) http.Handler {
+ handler := http.StripPrefix(path, http.FileServer(http.Dir(dir)))
+ return disableCache(handler)
+}
diff --git a/gno.land/pkg/gnoweb/static/css/app.css b/gno.land/pkg/gnoweb/static/css/app.css
deleted file mode 100644
index c10fc8ec0e0..00000000000
--- a/gno.land/pkg/gnoweb/static/css/app.css
+++ /dev/null
@@ -1,862 +0,0 @@
-/**** ROBOTO ****/
-
-@font-face {
- font-family: "Roboto Mono";
- font-style: normal;
- font-weight: normal;
- font-display: swap;
- src: local("Roboto Mono Regular"), url("/static/font/roboto/RobotoMono-Regular.woff") format("woff");
- }
-
- @font-face {
- font-family: "Roboto Mono";
- font-style: italic;
- font-weight: normal;
- font-display: swap;
- src: local("Roboto Mono Italic"), url("/static/font/roboto/RobotoMono-Italic.woff") format("woff");
- }
-
- @font-face {
- font-family: "Roboto Mono Bold";
- font-style: normal;
- font-weight: 700;
- font-display: swap;
- src: local("Roboto Mono Bold"), url("/static/font/roboto/RobotoMono-Bold.woff") format("woff");
- }
-
- @font-face {
- font-family: "Roboto Mono";
- font-style: italic;
- font-weight: 700;
- font-display: swap;
- src: local("Roboto Mono Bold Italic"), url("/static/font/roboto/RobotoMono-BoldItalic.woff") format("woff");
- }
-
-
-/*** DARK/LIGHT THEME COLORS ***/
-
-html:not([data-theme="dark"]),
-html[data-theme="light"] {
- --background-color: #eee;
- --input-background-color: #eee;
- --text-color: #000;
- --link-color: #25172a;
- --muted-color: #757575;
- --border-color: #d7d9db;
- --icon-color: #000;
-
- --quote-background: #ddd;
- --quote-2-background: #aaa4;
- --code-background: #d7d9db;
- --header-background: #373737;
- --header-forground: #ffffff;
- --logo-hat: #ffffff;
- --logo-beard: #808080;
-
- --realm-help-background-color: #d7d9db9e;
- --realm-help-odd-background-color: #d7d9db45;
- --realm-help-code-color: #5d5d5d;
-
- --highlight-color: #2f3337;
- --highlight-bg: #f6f6f6;
- --highlight-color: #2f3337;
- --highlight-comment: #656e77;
- --highlight-keyword: #015692;
- --highlight-attribute: #015692;
- --highlight-symbol: #803378;
- --highlight-namespace: #b75501;
- --highlight-keyword: #015692;
- --highlight-variable: #54790d;
- --highlight-keyword: #015692;
- --highlight-literal: #b75501;
- --highlight-punctuation: #535a60;
- --highlight-variable: #54790d;
- --highlight-deletion: #c02d2e;
- --highlight-addition: #2f6f44;
-}
-
-html[data-theme="dark"] {
- --background-color: #1e1e1e;
- --input-background-color: #393939;
- --text-color: #c7c7c7;
- --link-color: #c7c7c7;
- --muted-color: #737373;
- --border-color: #606060;
- --icon-color: #dddddd;
-
- --quote-background: #404040;
- --quote-2-background: #555555;
- --code-background: #606060;
- --header-background: #373737;
- --header-forground: #ffffff;
- --logo-hat: #ffffff;
- --logo-beard: #808080;
-
- --realm-help-background-color: #45454545;
- --realm-help-odd-background-color: #4545459e;
- --realm-help-code-color: #b6b6b6;
-
- --highlight-color: #ffffff;
- --highlight-bg: #1c1b1b;
- --highlight-color: #ffffff;
- --highlight-comment: #999999;
- --highlight-keyword: #88aece;
- --highlight-attribute: #88aece;
- --highlight-symbol: #c59bc1;
- --highlight-namespace: #f08d49;
- --highlight-keyword: #88aece;
- --highlight-variable: #b5bd68;
- --highlight-keyword: #88aece;
- --highlight-literal: #f08d49;
- --highlight-punctuation: #cccccc;
- --highlight-variable: #b5bd68;
- --highlight-deletion: #de7176;
- --highlight-addition: #76c490;
-}
-
-.logo-wording path {fill: var(--header-forground, #ffffff); }
-.logo-beard { fill: var(--logo-beard, #808080); }
-.logo-hat {fill: var(--logo-hat, #ffffff); }
-
-#theme-toggle {
- cursor: pointer;
- display: inline-block;
- padding: 0;
- color: var(--header-forground, #ffffff);
-}
-
-html[data-theme="dark"] #theme-toggle-moon,
-html[data-theme="light"] #theme-toggle-sun {
- display: none;
-}
-
-/*** BASE HTML ELEMENTS ***/
-
-* {
- box-sizing: border-box;
-}
-
-html {
- font-feature-settings: "kern" on, "liga" on, "calt" on, "zero" on;
- -webkit-font-feature-settings: "kern" on, "liga" on, "calt" on, "zero" on;
- text-size-adjust: 100%;
- -moz-osx-font-smoothing: grayscale;
- font-smoothing: antialiased;
- font-variant-ligatures: contextual common-ligatures;
- font-kerning: normal;
- text-rendering: optimizeLegibility;
- -moz-text-size-adjust: none;
- -webkit-text-size-adjust: none;
- text-size-adjust: none;
-}
-
-html,
-body {
- padding: 0;
- margin: 0;
- font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
- "Segoe UI Symbol", "Noto Color Emoji"; background-color: var(--background-color, #eee);
- color: var(--text-color, #000);
- font-size: 15px;
- transition: 0.25s all ease;
-}
-
-h1,
-h2,
-h3,
-h4,
-nav {
-
- font-weight: 600;
- letter-spacing: 0.08rem;
-}
-
-:is(h1, h2, h3, h4) a {
- text-decoration: none;
-}
-
-h1 {
- text-align: center;
- font-size: 2rem;
- margin-block: 4.2rem 2rem;
-}
-
-h2 {
- font-size: 1.625rem;
- margin-block: 3.4rem 1.2rem;
- line-height: 1.4;
-}
-
-h3 {
- font-size: 1.467rem;
- margin-block: 2.6rem 1rem;
-}
-
-p {
- font-size: 1rem;
- margin-block: 1.2rem;
- line-height: 1.4;
-}
-
-p:last-child:has(a:only-child) {
- margin-block-start: 0.8rem;
-}
-.stack > p:last-child:has(a:only-child) {
- margin-block-start: 0;
-}
-
-hr {
- border: none;
- height: 1px;
- background: var(--border-color, #d7d9db);
- width: 100%;
- margin-block: 1.5rem 2rem;
-}
-
-nav {
- font-weight: 400;
-}
-
-button {
- color: var(--text-color, #000);
-}
-
-body {
- height: 100%;
- width: 100%;
-}
-
-input {
- -webkit-appearance: none;
- -moz-appearance: none;
- appearance: none;
-}
-
-a {
- color: var(--link-color, #25172a);
-}
-
-a[href="#"] {
- color: var(--muted-color, #757575);
-}
-
-.gno-tmpl-section ul {
- padding: 0;
-}
-
-.gno-tmpl-section li ,
-#header li ,
-.footer li {
- list-style: none;
-}
-
-.gno-tmpl-section blockquote {
- margin-inline: 0;
-}
-
-li {
- margin-bottom: 0.4rem;
-}
-
-li > * {
- vertical-align: middle;
-}
-
-input {
- background-color: var(--input-background-color, #eee);
- border: 1px solid var(--border-color);
- color: var(--text-color, #000);
- width: 25em;
- padding: 0.4rem 0.5rem;
- max-width: 100%;x
-}
-
-blockquote {
- background-color: var(--quote-background, #ddd);
-}
-
-blockquote blockquote {
- margin: 0;
- background-color: var(--quote-2-background, #aaa4);
-}
-
-pre, code {
- font-family: "Roboto Mono", "Courier New", "sans-serif";
-}
-pre {
- background-color: var(--code-background, #d7d9db);
- margin: 0;
- padding: 0.5rem;
-}
-
-label {
- margin-block-end: 0.8rem;
- display: block;
-}
-
-label > img {
- margin-inline-end: 0.8rem;
-}
-
-code {
- white-space: pre-wrap;
- overflow-wrap: anywhere;
-}
-/*** COMPOSITION ***/
-.container {
- width: 100%;
- max-width: 63.75rem;
- margin: auto;
- padding: 1.25rem;
-}
-
-.container p > img:only-child {
- max-width: 100%;
-}
-.gno-tmpl-page p img:only-child {
- margin-inline: auto;
- display: block;
- max-width: 100%;
-}
-
-.inline-list {
- padding: 1rem;
- display: flex;
- justify-content: space-between;
-}
-
-
-
-.stack,
-.stack > p {
- display: flex;
- flex-direction: column;
-}
-
-.stack > p {
- margin: 0;
-}
-
-.stack > a,
-.stack > p > a{
- margin-block-end: 0.4rem;
-}
-
-.column > h1,
-.column > h2,
-.column > h3,
-.column > h4,
-.column > h5,
-.column > h6 {
- margin-block-start: 0;
-}
-
-.columns-2,
-.columns-3 {
- display: grid;
- grid-template-columns: repeat(1, 1fr);
- grid-gap: 3.75rem;
- margin: 3.75rem auto;
-}
-
-.footer {
- text-align: center;
- margin-block-start: 2rem;
- background-color: var(--header-background, #d7d9db);
- border-top: 1px solid var(--border-color);
-}
-
-.footer > .logo {
- display: inline-block;
- margin: 1rem;
- height: 1.2rem;
-}
-
-/** 51.2rem **/
-@media screen and (min-width: 68.75rem) {
- .stack,
- .stack > p {
- flex-direction: row;
- }
- .stack *:not(:first-child) {
- margin-left: 3.75rem;
- }
- .stack > a,
- .stack > p > a{
- margin-block-end: 0;
- }
- .columns-2 {
- grid-template-columns: repeat(2, 1fr);
- }
- .columns-3 {
- grid-template-columns: repeat(3, 1fr);
- }
-}
-
-/*** UTILITIES ***/
-
-.is-hidden {
- display: none;
-}
-
-.is-muted {
- color: var(--muted-color, #757575);
-}
-
-.is-finished {
- text-decoration: line-through;
-}
-
-.is-underline {
- text-decoration: underline;
-}
-
-/*** BLOCKS ***/
-.tabs button {
- border: none;
- cursor: pointer;
- text-decoration: underline;
- padding: 0;
- background: none;
- color: var(--text-color, #000);
-}
-
-.tabs button[aria-selected="true"] {
- font-weight: 700;
-}
-
-.tabs + .jumbotron {
- margin-top: 2.5rem;
-}
-.tabs > .columns-2,
-.tabs > .columns-3 {
- margin-bottom: 2.5rem;
-}
-
-.accordion-trigger {
- display: block;
- border: none;
- cursor: pointer;
- padding: 0.4rem 0;
- font-size: 1.125rem;
- font-weight: 700;
- text-align: left;
- background: none;
-}
-
-.accordion-trigger ~ div {
- padding: 0.875rem 0 2.2rem;
-}
-
-.accordion > p {
- margin-block: 0;
-}
-/** 51.2rem **/
-@media screen and (min-width: 68.75rem) {
- .accordion .accordion-trigger ~ div {
- padding: 0.875rem 0 2.2rem 2rem;
- }
-}
-
-.gor-accordion button::first-letter {
- font-size: 1.5em;
- color: var(--text-color, #000);
-}
-
-.jumbotron {
- border: 1px solid var(--border-color, #d7d9db);
- padding: 1.4rem;
- margin: 3.75rem auto;
-}
-
-.jumbotron h1 {
- text-align: left;
-}
-
-.jumbotron > *:first-child,
-.jumbotron > * > *:first-child {
- margin-block-start: 0;
-}
-
-.jumbotron > *:last-child,
-.jumbotron > * > *:last-child {
- margin-block-end: 0;
-}
-
-/** 68.75rem**/
-@media screen and (min-width: 68.75rem) {
- .jumbotron {
- margin: 3.75rem -3.5rem;
- padding: 3.5rem;
- }
-}
-
-#root {
- display: flex;
- flex-direction: column;
- border: 1px solid var(--header-background, #d7d9db);
- margin: 20px;
- overflow: hidden;
- /* height: calc(100vh - 40px); */
-}
-
-#header {
- position: relative;
- background-color: var(--header-background, #d7d9db);
- padding: 1.333rem;
- display: flex;
- align-items: center;
- justify-content: space-between;
-}
-
-#header > nav {
- flex-grow: 2;
-}
-
-#header .logo {
- display: flex;
- align-items: center;
- color: var(--link-color, #25172a);
- position: absolute;
- height: 2.4rem;
- z-index: 2;
-}
-
-.logo > svg {
- height: 100%;
-}
-
-#logo_path a {
- text-decoration: none;
-}
-
-#logo_path {
- padding-right: 0.8rem;
-}
-
-#logo_path a:hover {
- text-decoration: underline;
-}
-
-#realm_links a {
- font-size: 0.8rem;
-}
-
-#header_buttons {
- position: relative;
- width: 100%;
- height: 3rem;
-}
-
-#header_buttons nav {
- height: 100%;
- display: flex;
- justify-content: flex-end;
- align-items: center;
-}
-
-/* enabled conditionally with */
-#source {
- opacity: 0;
-}
-
-/*** REALM HELP ***/
-#realm_render,
-#realm_help {
- padding: 0 1.467rem;
-}
-
-#realm_help .func_specs {
- box-sizing: border-box;
- width: calc(100% + 1.467rem);
- margin-left: -1.467rem;
-}
-
-#realm_help .func_spec {
- padding: 1.467rem;
- background: var(--realm-help-background-color, #d7d9db9e);
- margin-top: 1.467rem;
-}
-#realm_help .func_spec:nth-child(odd) {
- background: var(--realm-help-odd-background-color, #d7d9db45);
-}
-
-#realm_help .func_spec > table > tbody > tr > th {
- width: 3.333rem;
- vertical-align: top;
- text-align: right;
- color: var(--text-color, #000);
-}
-
-#realm_help .func_spec > table th,
-#realm_help .func_spec > table td {
- padding-bottom: 16px;
-}
-
-#realm_help .func_spec > table th + td {
- padding-left: 1rem;
-}
-
-#realm_help .func_spec > table th + td table td {
- padding-left: 0.8rem;
-}
-
-#realm_help .func_spec .shell_command {
- color: var(--realm-help-code-color, #5d5d5d);
-}
-
-#realm_help .func_name td {
- font-weight: bold;
-}
-
-/** menu **/
-#menu-toggle {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- position: relative;
- z-index: 1;
- -webkit-user-select: none;
- user-select: none;
- height: 100%;
-}
-
-#menu-toggle input {
- display: block;
- width: 2.667rem;
- height: 2.133rem;
- position: absolute;
- cursor: pointer;
- opacity: 0;
- z-index: 2;
- -webkit-touch-callout: none;
-}
-
-#menu-toggle span {
- display: block;
- width: 2.2rem;
- height: 0.133rem;
- margin-bottom: 0.333rem;
- position: relative;
- background: var(--header-forground, #ffffff);
- z-index: 1;
- transform-origin: 50% 50%;
- /* transition: transform 0.5s cubic-bezier(0.77, 0.2, 0.05, 1), background 0.5s cubic-bezier(0.77, 0.2, 0.05, 1), opacity 0.55s ease; */
-}
-
-#menu-toggle input:checked ~ span {
- opacity: 1;
- transform: rotate(45deg) translate(0.467rem, 0.467rem);
-}
-
-#menu-toggle input:checked ~ span:nth-last-child(3) {
- opacity: 0;
- transform: rotate(0deg) scale(0.2, 0.2);
-}
-
-#menu-toggle input:checked ~ span:nth-last-child(2) {
- transform: rotate(-45deg) translate(0.2rem, -0.267rem);
-}
-
-#menu-toggle > .navigation {
- position: absolute;
- width: calc(100vw - 2.6rem);
- right: -1.467rem;
- top: -1.467rem;
- padding: 7.333rem 1.467rem 4.333rem;
- background: var(--header-background, #d7d9db);
- list-style-type: none;
- -webkit-font-smoothing: antialiased;
- transform-origin: 0% 0%;
- transform: translate(0, -100%);
- /* transition: transform 0.5s cubic-bezier(0.77, 0.2, 0.05, 1); */
-}
-
-#menu-toggle .buttons {
- position: absolute;
- left: 1.467rem;
- display: flex;
- align-items: center;
- margin-block-start: .7em;
- color: var(--icon-color, #000);
-}
-
-#menu-toggle .buttons > *:not(:last-child) {
- margin-right: 0.6rem;
-}
-
-#menu-toggle .buttons button {
- background-color: transparent;
- border: none;
-}
-
-#menu-toggle .buttons a {
- text-decoration: none;
-}
-
-#menu-toggle > .navigation > ul {
- display: flex;
- flex-direction: column;
- margin-bottom: 2rem;
- margin-top: .8rem;
-}
-
-#menu-toggle > .navigation a {
- color: var(--header-forground, #ffffff);
-}
-
-#menu-toggle input:checked ~ .navigation {
- transform: none;
-}
-
-/** 51.2rem **/
-@media screen and (min-width: 51.2rem) {
- #menu-toggle {
- display: block;
- width: 50%;
- float: right;
- }
- #menu-toggle span,
- #menu-toggle input {
- display: none;
- }
- #menu-toggle > .navigation {
- position: relative;
- display: flex;
- align-items: center;
- justify-content: space-between;
- width: auto;
- padding: 0;
- right: initial;
- top: initial;
- background: transparent;
- transform: translate(0, 0);
- }
- #menu-toggle .buttons {
- right: 0;
- left: initial;
- }
- #menu-toggle > .navigation > ul {
- transform: translateX(-50%);
- flex-direction: row;
- margin-bottom: 0;
- }
- #menu-toggle > .navigation li:not(:first-child) {
- margin-left: min(1vw, 1.5rem);
- }
-}
-
-/*** EXCEPTIONS ***/
-.stack--center {
- justify-content: center;
-}
-
-.stack--thin *:not(:first-child) {
- margin-left: 1.5rem;
-}
-
-/*** HLJS ***/
-
-/* Copyright (c) 2006, Ivan Sagalaev.
- * https://github.com/highlightjs/highlight.js/blob/86dcb210227ef130a00b5ece50605ea1ec887be8/src/styles/default.css */
-pre code.hljs {
- display: block;
- overflow-x: auto;
- padding: 1em;
-}
-
-code.hljs {
- padding: 3px 5px;
-}
-
-/* Copyright 2017-2020 Stack Exchange Inc.
- * https://github.com/highlightjs/highlight.js/blob/86dcb210227ef130a00b5ece50605ea1ec887be8/src/styles/stackoverflow-light.css
- * https://github.com/highlightjs/highlight.js/blob/86dcb210227ef130a00b5ece50605ea1ec887be8/src/styles/stackoverflow-dark.css */
-.hljs {
- color: var(--highlight-color, #2f3337);
- background: var(--highlight-bg, #f6f6f6);
-}
-
-.hljs-subst {
- color: var(--highlight-color, #2f3337);
-}
-
-.hljs-comment {
- color: var(--highlight-comment, #656e77);
-}
-
-.hljs-keyword,
-.hljs-selector-tag,
-.hljs-meta .hljs-keyword,
-.hljs-doctag,
-.hljs-section {
- color: var(--highlight-keyword, #015692);
-}
-
-.hljs-attr {
- color: var(--highlight-attribute, #015692);
-}
-
-.hljs-attribute {
- color: var(--highlight-symbol, #803378);
-}
-
-.hljs-name,
-.hljs-type,
-.hljs-number,
-.hljs-selector-id,
-.hljs-quote,
-.hljs-template-tag {
- color: var(--highlight-namespace, #b75501);
-}
-
-.hljs-selector-class {
- color: var(--highlight-keyword, #015692);
-}
-
-.hljs-string,
-.hljs-regexp,
-.hljs-symbol,
-.hljs-variable,
-.hljs-template-variable,
-.hljs-link,
-.hljs-selector-attr {
- color: var(--highlight-variable, #54790d);
-}
-
-.hljs-meta,
-.hljs-selector-pseudo {
- color: var(--highlight-keyword, #015692);
-}
-
-.hljs-built_in,
-.hljs-title,
-.hljs-literal {
- color: var(--highlight-literal, #b75501);
-}
-
-.hljs-bullet,
-.hljs-code {
- color: var(--highlight-punctuation, #535a60);
-}
-
-.hljs-meta .hljs-string {
- color: var(--highlight-variable, #54790d);
-}
-
-.hljs-deletion {
- color: var(--highlight-deletion, #c02d2e);
-}
-
-.hljs-addition {
- color: var(--highlight-addition, #2f6f44);
-}
-
-.hljs-emphasis {
- font-style: italic;
-}
-
-.hljs-strong {
- font-weight: bold;
-}
diff --git a/gno.land/pkg/gnoweb/static/css/normalize.css b/gno.land/pkg/gnoweb/static/css/normalize.css
deleted file mode 100644
index 195264e4366..00000000000
--- a/gno.land/pkg/gnoweb/static/css/normalize.css
+++ /dev/null
@@ -1,379 +0,0 @@
-/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
-
-/* Document
- ========================================================================== */
-
-/**
- * 1. Correct the line height in all browsers.
- * 2. Prevent adjustments of font size after orientation changes in iOS.
- */
-
-html {
- line-height: 1.15;
- /* 1 */
- -webkit-text-size-adjust: 100%;
- /* 2 */
-}
-
-/* Sections
- ========================================================================== */
-
-/**
- * Remove the margin in all browsers.
- */
-
-body {
- margin: 0;
-}
-
-/**
- * Render the `main` element consistently in IE.
- */
-
-main {
- display: block;
-}
-
-/**
- * Correct the font size and margin on `h1` elements within `section` and
- * `article` contexts in Chrome, Firefox, and Safari.
- */
-
-h1 {
- font-size: 2em;
- margin: 0.67em 0;
-}
-
-/* Grouping content
- ========================================================================== */
-
-/**
- * 1. Add the correct box sizing in Firefox.
- * 2. Show the overflow in Edge and IE.
- */
-
-hr {
- box-sizing: content-box;
- /* 1 */
- height: 0;
- /* 1 */
- overflow: visible;
- /* 2 */
-}
-
-/**
- * 1. Correct the inheritance and scaling of font size in all browsers.
- * 2. Correct the odd `em` font sizing in all browsers.
- */
-
-pre {
- font-family: monospace, monospace;
- /* 1 */
- font-size: 1em;
- /* 2 */
-}
-
-/* Text-level semantics
- ========================================================================== */
-
-/**
- * Remove the gray background on active links in IE 10.
- */
-
-a {
- background-color: transparent;
-}
-
-/**
- * 1. Remove the bottom border in Chrome 57-
- * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
- */
-
-abbr[title] {
- border-bottom: none;
- /* 1 */
- text-decoration: underline;
- /* 2 */
- text-decoration: underline dotted;
- /* 2 */
-}
-
-/**
- * Add the correct font weight in Chrome, Edge, and Safari.
- */
-
-b,
-strong {
- font-weight: bolder;
-}
-
-/**
- * 1. Correct the inheritance and scaling of font size in all browsers.
- * 2. Correct the odd `em` font sizing in all browsers.
- */
-
-code,
-kbd,
-samp {
- font-family: monospace, monospace;
- /* 1 */
- font-size: 1em;
- /* 2 */
-}
-
-/**
- * Add the correct font size in all browsers.
- */
-
-small {
- font-size: 80%;
-}
-
-/**
- * Prevent `sub` and `sup` elements from affecting the line height in
- * all browsers.
- */
-
-sub,
-sup {
- font-size: 75%;
- line-height: 0;
- position: relative;
- vertical-align: baseline;
-}
-
-sub {
- bottom: -0.25em;
-}
-
-sup {
- top: -0.5em;
-}
-
-/* Embedded content
- ========================================================================== */
-
-/**
- * Remove the border on images inside links in IE 10.
- */
-
-img {
- border-style: none;
-}
-
-/* Forms
- ========================================================================== */
-
-/**
- * 1. Change the font styles in all browsers.
- * 2. Remove the margin in Firefox and Safari.
- */
-
-button,
-input,
-optgroup,
-select,
-textarea {
- font-family: inherit;
- /* 1 */
- font-size: 100%;
- /* 1 */
- line-height: 1.15;
- /* 1 */
- margin: 0;
- /* 2 */
-}
-
-/**
- * Show the overflow in IE.
- * 1. Show the overflow in Edge.
- */
-
-button,
-input {
- /* 1 */
- overflow: visible;
-}
-
-/**
- * Remove the inheritance of text transform in Edge, Firefox, and IE.
- * 1. Remove the inheritance of text transform in Firefox.
- */
-
-button,
-select {
- /* 1 */
- text-transform: none;
-}
-
-/**
- * Correct the inability to style clickable types in iOS and Safari.
- */
-
-button,
-[type="button"],
-[type="reset"],
-[type="submit"] {
- -webkit-appearance: button;
-}
-
-/**
- * Remove the inner border and padding in Firefox.
- */
-
-button::-moz-focus-inner,
-[type="button"]::-moz-focus-inner,
-[type="reset"]::-moz-focus-inner,
-[type="submit"]::-moz-focus-inner {
- border-style: none;
- padding: 0;
-}
-
-/**
- * Restore the focus styles unset by the previous rule.
- */
-
-button:-moz-focusring,
-[type="button"]:-moz-focusring,
-[type="reset"]:-moz-focusring,
-[type="submit"]:-moz-focusring {
- outline: 1px dotted ButtonText;
-}
-
-/**
- * Correct the padding in Firefox.
- */
-
-fieldset {
- padding: 0.35em 0.75em 0.625em;
-}
-
-/**
- * 1. Correct the text wrapping in Edge and IE.
- * 2. Correct the color inheritance from `fieldset` elements in IE.
- * 3. Remove the padding so developers are not caught out when they zero out
- * `fieldset` elements in all browsers.
- */
-
-legend {
- box-sizing: border-box;
- /* 1 */
- color: inherit;
- /* 2 */
- display: table;
- /* 1 */
- max-width: 100%;
- /* 1 */
- padding: 0;
- /* 3 */
- white-space: normal;
- /* 1 */
-}
-
-/**
- * Add the correct vertical alignment in Chrome, Firefox, and Opera.
- */
-
-progress {
- vertical-align: baseline;
-}
-
-/**
- * Remove the default vertical scrollbar in IE 10+.
- */
-
-textarea {
- overflow: auto;
-}
-
-/**
- * 1. Add the correct box sizing in IE 10.
- * 2. Remove the padding in IE 10.
- */
-
-[type="checkbox"],
-[type="radio"] {
- box-sizing: border-box;
- /* 1 */
- padding: 0;
- /* 2 */
-}
-
-/**
- * Correct the cursor style of increment and decrement buttons in Chrome.
- */
-
-[type="number"]::-webkit-inner-spin-button,
-[type="number"]::-webkit-outer-spin-button {
- height: auto;
-}
-
-/**
- * 1. Correct the odd appearance in Chrome and Safari.
- * 2. Correct the outline style in Safari.
- */
-
-[type="search"] {
- -webkit-appearance: textfield;
- /* 1 */
- outline-offset: -2px;
- /* 2 */
-}
-
-/**
- * Remove the inner padding in Chrome and Safari on macOS.
- */
-
-[type="search"]::-webkit-search-decoration {
- -webkit-appearance: none;
-}
-
-/**
- * 1. Correct the inability to style clickable types in iOS and Safari.
- * 2. Change font properties to `inherit` in Safari.
- */
-
-::-webkit-file-upload-button {
- -webkit-appearance: button;
- /* 1 */
- font: inherit;
- /* 2 */
-}
-
-/* Interactive
- ========================================================================== */
-
-/*
- * Add the correct display in Edge, IE 10+, and Firefox.
- */
-
-details {
- display: block;
-}
-
-/*
- * Add the correct display in all browsers.
- */
-
-summary {
- display: list-item;
-}
-
-/* Misc
- ========================================================================== */
-
-/**
- * Add the correct display in IE 10+.
- */
-
-template {
- display: none;
-}
-
-/**
- * Add the correct display in IE 10.
- */
-
-[hidden] {
- display: none;
-}
diff --git a/gno.land/pkg/gnoweb/static/font/README.md b/gno.land/pkg/gnoweb/static/font/README.md
deleted file mode 100644
index a077b97d304..00000000000
--- a/gno.land/pkg/gnoweb/static/font/README.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# FONTS
-
-This is the source repository for gno.land fonts: Roboto. Both are Open source and available from Google Fonts.
-
-- RobotoMono: Apache 2.0 — [https://fonts.google.com/specimen/Roboto+Mono/about](https://fonts.google.com/specimen/Roboto+Mono/about)
diff --git a/gno.land/pkg/gnoweb/static/font/roboto/LICENSE.txt b/gno.land/pkg/gnoweb/static/font/roboto/LICENSE.txt
deleted file mode 100644
index c61b66391a3..00000000000
--- a/gno.land/pkg/gnoweb/static/font/roboto/LICENSE.txt
+++ /dev/null
@@ -1,201 +0,0 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
-TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
-2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
-3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
-4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
-5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
-6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
-7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
-8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
-9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
-END OF TERMS AND CONDITIONS
-
-APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
-Copyright [yyyy] [name of copyright owner]
-
-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.
diff --git a/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-Bold.woff b/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-Bold.woff
deleted file mode 100644
index c500c3857d6..00000000000
Binary files a/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-Bold.woff and /dev/null differ
diff --git a/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-BoldItalic.woff b/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-BoldItalic.woff
deleted file mode 100644
index 9ed406ab932..00000000000
Binary files a/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-BoldItalic.woff and /dev/null differ
diff --git a/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-Italic.woff b/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-Italic.woff
deleted file mode 100644
index 5beafc7c57e..00000000000
Binary files a/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-Italic.woff and /dev/null differ
diff --git a/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-Light.woff b/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-Light.woff
deleted file mode 100644
index 68e61b1298d..00000000000
Binary files a/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-Light.woff and /dev/null differ
diff --git a/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-LightItalic.woff b/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-LightItalic.woff
deleted file mode 100644
index 2a50c6d2fd4..00000000000
Binary files a/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-LightItalic.woff and /dev/null differ
diff --git a/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-Medium.woff b/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-Medium.woff
deleted file mode 100644
index e65d5310e45..00000000000
Binary files a/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-Medium.woff and /dev/null differ
diff --git a/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-MediumItalic.woff b/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-MediumItalic.woff
deleted file mode 100644
index a556d2fb729..00000000000
Binary files a/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-MediumItalic.woff and /dev/null differ
diff --git a/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-Regular.woff b/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-Regular.woff
deleted file mode 100644
index 3de661ee0d9..00000000000
Binary files a/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-Regular.woff and /dev/null differ
diff --git a/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-Thin.woff b/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-Thin.woff
deleted file mode 100644
index 6ca15f36f5c..00000000000
Binary files a/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-Thin.woff and /dev/null differ
diff --git a/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-ThinItalic.woff b/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-ThinItalic.woff
deleted file mode 100644
index e8653ff4791..00000000000
Binary files a/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-ThinItalic.woff and /dev/null differ
diff --git a/gno.land/pkg/gnoweb/static/img/apple-touch-icon.png b/gno.land/pkg/gnoweb/static/img/apple-touch-icon.png
deleted file mode 100644
index dcc70338eaa..00000000000
Binary files a/gno.land/pkg/gnoweb/static/img/apple-touch-icon.png and /dev/null differ
diff --git a/gno.land/pkg/gnoweb/static/img/favicon-16x16.png b/gno.land/pkg/gnoweb/static/img/favicon-16x16.png
deleted file mode 100644
index ee407d9a7ea..00000000000
Binary files a/gno.land/pkg/gnoweb/static/img/favicon-16x16.png and /dev/null differ
diff --git a/gno.land/pkg/gnoweb/static/img/favicon-32x32.png b/gno.land/pkg/gnoweb/static/img/favicon-32x32.png
deleted file mode 100644
index 86dcfed925c..00000000000
Binary files a/gno.land/pkg/gnoweb/static/img/favicon-32x32.png and /dev/null differ
diff --git a/gno.land/pkg/gnoweb/static/img/github-mark-32px.png b/gno.land/pkg/gnoweb/static/img/github-mark-32px.png
deleted file mode 100644
index 8b25551a979..00000000000
Binary files a/gno.land/pkg/gnoweb/static/img/github-mark-32px.png and /dev/null differ
diff --git a/gno.land/pkg/gnoweb/static/img/github-mark-64px.png b/gno.land/pkg/gnoweb/static/img/github-mark-64px.png
deleted file mode 100644
index 182a1a3f734..00000000000
Binary files a/gno.land/pkg/gnoweb/static/img/github-mark-64px.png and /dev/null differ
diff --git a/gno.land/pkg/gnoweb/static/img/ico-discord.svg b/gno.land/pkg/gnoweb/static/img/ico-discord.svg
deleted file mode 100644
index 2f73278fbe9..00000000000
--- a/gno.land/pkg/gnoweb/static/img/ico-discord.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/gno.land/pkg/gnoweb/static/img/ico-email.svg b/gno.land/pkg/gnoweb/static/img/ico-email.svg
deleted file mode 100644
index ff397fe664d..00000000000
--- a/gno.land/pkg/gnoweb/static/img/ico-email.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/gno.land/pkg/gnoweb/static/img/ico-telegram.svg b/gno.land/pkg/gnoweb/static/img/ico-telegram.svg
deleted file mode 100644
index 32932830dc3..00000000000
--- a/gno.land/pkg/gnoweb/static/img/ico-telegram.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/gno.land/pkg/gnoweb/static/img/ico-twitter.svg b/gno.land/pkg/gnoweb/static/img/ico-twitter.svg
deleted file mode 100644
index cf666e3842d..00000000000
--- a/gno.land/pkg/gnoweb/static/img/ico-twitter.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/gno.land/pkg/gnoweb/static/img/ico-youtube.svg b/gno.land/pkg/gnoweb/static/img/ico-youtube.svg
deleted file mode 100644
index 36efdd185f0..00000000000
--- a/gno.land/pkg/gnoweb/static/img/ico-youtube.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/gno.land/pkg/gnoweb/static/img/list-alt.png b/gno.land/pkg/gnoweb/static/img/list-alt.png
deleted file mode 100644
index 14296a4d28f..00000000000
Binary files a/gno.land/pkg/gnoweb/static/img/list-alt.png and /dev/null differ
diff --git a/gno.land/pkg/gnoweb/static/img/list.png b/gno.land/pkg/gnoweb/static/img/list.png
deleted file mode 100644
index efe3934567f..00000000000
Binary files a/gno.land/pkg/gnoweb/static/img/list.png and /dev/null differ
diff --git a/gno.land/pkg/gnoweb/static/img/logo-square.png b/gno.land/pkg/gnoweb/static/img/logo-square.png
deleted file mode 100644
index ce8c93d0c8a..00000000000
Binary files a/gno.land/pkg/gnoweb/static/img/logo-square.png and /dev/null differ
diff --git a/gno.land/pkg/gnoweb/static/img/logo-square.svg b/gno.land/pkg/gnoweb/static/img/logo-square.svg
deleted file mode 100644
index e6ab42f5ad4..00000000000
--- a/gno.land/pkg/gnoweb/static/img/logo-square.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
diff --git a/gno.land/pkg/gnoweb/static/img/logo-v1.png b/gno.land/pkg/gnoweb/static/img/logo-v1.png
deleted file mode 100644
index 702fce47a52..00000000000
Binary files a/gno.land/pkg/gnoweb/static/img/logo-v1.png and /dev/null differ
diff --git a/gno.land/pkg/gnoweb/static/img/og-gnoland.png b/gno.land/pkg/gnoweb/static/img/og-gnoland.png
deleted file mode 100644
index 9872fea17da..00000000000
Binary files a/gno.land/pkg/gnoweb/static/img/og-gnoland.png and /dev/null differ
diff --git a/gno.land/pkg/gnoweb/static/img/safari-pinned-tab.svg b/gno.land/pkg/gnoweb/static/img/safari-pinned-tab.svg
deleted file mode 100644
index 0005420c58d..00000000000
--- a/gno.land/pkg/gnoweb/static/img/safari-pinned-tab.svg
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
-Created by potrace 1.14, written by Peter Selinger 2001-2017
-
-
-
-
-
diff --git a/gno.land/pkg/gnoweb/static/invites.txt b/gno.land/pkg/gnoweb/static/invites.txt
deleted file mode 100644
index 7bef15f954f..00000000000
--- a/gno.land/pkg/gnoweb/static/invites.txt
+++ /dev/null
@@ -1,48 +0,0 @@
-g1589c8cekvmjfmy0qrd4f3z52r7fn7rgk02667s:1
-g13sm84nuqed3fuank8huh7x9mupgw22uft3lcl8:1
-g1m6732pkrngu9vrt0g7056lvr9kcqc4mv83xl5q:1
-g1wg88rhzlwxjd2z4j5de5v5xq30dcf6rjq3dhsj:1
-g18pmaskasz7mxj6rmgrl3al58xu45a7w0l5nmc0:1
-g19wwhkmqlns70604ksp6rkuuu42qhtvyh05lffz:1
-g187982000zsc493znqt828s90cmp6hcp2erhu6m:1
-g1ndpsnrspdnauckytvkfv8s823t3gmpqmtky8pl:1
-g16ja66d65emkr0zxd2tu7xjvm7utthyhpej0037:1
-g1ds24jj9kqjcskd0gzu24r9e4n62ggye230zuv5:1
-g1trkzq75ntamsnw9xnrav2v7gy2lt5g6p29yhdr:1
-g1rrf8s5mrmu00sx04fzfsvc399fklpeg2x0a7mz:1
-g19p5ntfvpt4lwq4jqsmnxsnelhf3tff9scy3w8w:1
-g1tue8l73d6rq4vhqdsp2sr3zhuzpure3k2rnwpz:1
-g14hhsss4ngx5kq77je5g0tl4vftg8qp45ceadk3:1
-g1768hvkh7anhd40ch4h7jdh6j3mpcs7hrat4gl0:1
-g15fa8kyjhu88t9dr8zzua8fwdvkngv5n8yqsm0n:1
-g1xhccdjcscuhgmt3quww6qdy3j3czqt3urc2eac:1
-g1z629z04f85k4t5gnkk5egpxw9tqxeec435esap:1
-g1pfldkplz9puq0v82lu9vqcve9nwrxuq9qe5ttv:1
-g152pn0g5qfgxr7yx8zlwjq48hytkafd8x7egsfv:1
-g1cf2ye686ke38vjyqakreprljum4xu6rwf5jskq:1
-g1c5shztyaj4gjrc5zlwmh9xhex5w7l4asffs2w6:1
-g1lhpx2ktk0ha3qw42raxq4m24a4c4xqxyrgv54q:1
-g1026p54q0j902059sm2zsv37krf0ghcl7gmhyv7:1
-g1n4yvwnv77frq2ccuw27dmtjkd7u4p4jg0pgm7k:1
-g13m7f2e6r3lh3ykxupacdt9sem2tlvmaamwjhll:1
-g19uxluuecjlsqvwmwu8sp6pxaaqfhk972q975xd:1
-g1j80fpcsumfkxypvydvtwtz3j4sdwr8c2u0lr64:1
-g1tjdpptuk9eysq6z38nscqyycr998xjyx3w8jvw:1
-g19t3n89slfemgd3mwuat4lajwcp0yxrkadgeg7a:1
-g1yqndt8xx92l9h494jfruz2w79swzjes3n4wqjc:1
-g13278z0a5ufeg80ffqxpda9dlp599t7ekregcy6:1
-g1ht236wjd83x96uqwh9rh3fq6pylyn78mtwq9v6:1
-g1fj9jccm3zjnqspq7lp2g7lj4czyfq0s35600g9:1
-g1wwppuzdns5u6c6jqpkzua24zh6ppsus6399cea:1
-g1k8pjnguyu36pkc8hy0ufzgpzfmj2jl78la7ek3:1
-g1e8umkzumtxgs8399lw0us4rclea3xl5gxy9spp:1
-g14qekdkj2nmmwea4ufg9n002a3pud23y8k7ugs5:1
-g19w2488ntfgpduzqq3sk4j5x387zynwknqdvjqf:1
-g1495y3z7zrej4rendysnw5kaeu4g3d7x7w0734g:1
-g1hygx8ga9qakhkczyrzs9drm8j8tu4qds9y5e3r:1
-g1f977l6wxdh3qu60kzl75vx2wmzswu68l03r8su:1
-g1644qje5rx6jsdqfkzmgnfcegx4dxkjh6rwqd69:1
-g1mzjajymvmtksdwh3wkrndwj6zls2awl9q83dh6:1
-g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq:10
-g14da4n9hcynyzz83q607uu8keuh9hwlv42ra6fa:10
-g14vhcdsyf83ngsrrqc92kmw8q9xakqjm0v8448t:5
diff --git a/gno.land/pkg/gnoweb/static/js/highlight.min.js b/gno.land/pkg/gnoweb/static/js/highlight.min.js
deleted file mode 100644
index 5135b77ab5b..00000000000
--- a/gno.land/pkg/gnoweb/static/js/highlight.min.js
+++ /dev/null
@@ -1,331 +0,0 @@
-/*!
- Highlight.js v11.9.0 (git: b7ec4bfafc)
- (c) 2006-2024 undefined and other contributors
- License: BSD-3-Clause
- */
- var hljs=function(){"use strict";function e(t){
- return t instanceof Map?t.clear=t.delete=t.set=()=>{
- throw Error("map is read-only")}:t instanceof Set&&(t.add=t.clear=t.delete=()=>{
- throw Error("set is read-only")
- }),Object.freeze(t),Object.getOwnPropertyNames(t).forEach((n=>{
- const i=t[n],s=typeof i;"object"!==s&&"function"!==s||Object.isFrozen(i)||e(i)
- })),t}class t{constructor(e){
- void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1}
- ignoreMatch(){this.isMatchIgnored=!0}}function n(e){
- return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")
- }function i(e,...t){const n=Object.create(null);for(const t in e)n[t]=e[t]
- ;return t.forEach((e=>{for(const t in e)n[t]=e[t]})),n}const s=e=>!!e.scope
- ;class o{constructor(e,t){
- this.buffer="",this.classPrefix=t.classPrefix,e.walk(this)}addText(e){
- this.buffer+=n(e)}openNode(e){if(!s(e))return;const t=((e,{prefix:t})=>{
- if(e.startsWith("language:"))return e.replace("language:","language-")
- ;if(e.includes(".")){const n=e.split(".")
- ;return[`${t}${n.shift()}`,...n.map(((e,t)=>`${e}${"_".repeat(t+1)}`))].join(" ")
- }return`${t}${e}`})(e.scope,{prefix:this.classPrefix});this.span(t)}
- closeNode(e){s(e)&&(this.buffer+=" ")}value(){return this.buffer}span(e){
- this.buffer+=``}}const r=(e={})=>{const t={children:[]}
- ;return Object.assign(t,e),t};class a{constructor(){
- this.rootNode=r(),this.stack=[this.rootNode]}get top(){
- return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){
- this.top.children.push(e)}openNode(e){const t=r({scope:e})
- ;this.add(t),this.stack.push(t)}closeNode(){
- if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){
- for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}
- walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,t){
- return"string"==typeof t?e.addText(t):t.children&&(e.openNode(t),
- t.children.forEach((t=>this._walk(e,t))),e.closeNode(t)),e}static _collapse(e){
- "string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{
- a._collapse(e)})))}}class c extends a{constructor(e){super(),this.options=e}
- addText(e){""!==e&&this.add(e)}startScope(e){this.openNode(e)}endScope(){
- this.closeNode()}__addSublanguage(e,t){const n=e.root
- ;t&&(n.scope="language:"+t),this.add(n)}toHTML(){
- return new o(this,this.options).value()}finalize(){
- return this.closeAllNodes(),!0}}function l(e){
- return e?"string"==typeof e?e:e.source:null}function g(e){return h("(?=",e,")")}
- function u(e){return h("(?:",e,")*")}function d(e){return h("(?:",e,")?")}
- function h(...e){return e.map((e=>l(e))).join("")}function f(...e){const t=(e=>{
- const t=e[e.length-1]
- ;return"object"==typeof t&&t.constructor===Object?(e.splice(e.length-1,1),t):{}
- })(e);return"("+(t.capture?"":"?:")+e.map((e=>l(e))).join("|")+")"}
- function p(e){return RegExp(e.toString()+"|").exec("").length-1}
- const b=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./
- ;function m(e,{joinWith:t}){let n=0;return e.map((e=>{n+=1;const t=n
- ;let i=l(e),s="";for(;i.length>0;){const e=b.exec(i);if(!e){s+=i;break}
- s+=i.substring(0,e.index),
- i=i.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?s+="\\"+(Number(e[1])+t):(s+=e[0],
- "("===e[0]&&n++)}return s})).map((e=>`(${e})`)).join(t)}
- const E="[a-zA-Z]\\w*",x="[a-zA-Z_]\\w*",w="\\b\\d+(\\.\\d+)?",y="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",_="\\b(0b[01]+)",O={
- begin:"\\\\[\\s\\S]",relevance:0},v={scope:"string",begin:"'",end:"'",
- illegal:"\\n",contains:[O]},k={scope:"string",begin:'"',end:'"',illegal:"\\n",
- contains:[O]},N=(e,t,n={})=>{const s=i({scope:"comment",begin:e,end:t,
- contains:[]},n);s.contains.push({scope:"doctag",
- begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)",
- end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0})
- ;const o=f("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/)
- ;return s.contains.push({begin:h(/[ ]+/,"(",o,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),s
- },S=N("//","$"),M=N("/\\*","\\*/"),R=N("#","$");var j=Object.freeze({
- __proto__:null,APOS_STRING_MODE:v,BACKSLASH_ESCAPE:O,BINARY_NUMBER_MODE:{
- scope:"number",begin:_,relevance:0},BINARY_NUMBER_RE:_,COMMENT:N,
- C_BLOCK_COMMENT_MODE:M,C_LINE_COMMENT_MODE:S,C_NUMBER_MODE:{scope:"number",
- begin:y,relevance:0},C_NUMBER_RE:y,END_SAME_AS_BEGIN:e=>Object.assign(e,{
- "on:begin":(e,t)=>{t.data._beginMatch=e[1]},"on:end":(e,t)=>{
- t.data._beginMatch!==e[1]&&t.ignoreMatch()}}),HASH_COMMENT_MODE:R,IDENT_RE:E,
- MATCH_NOTHING_RE:/\b\B/,METHOD_GUARD:{begin:"\\.\\s*"+x,relevance:0},
- NUMBER_MODE:{scope:"number",begin:w,relevance:0},NUMBER_RE:w,
- PHRASAL_WORDS_MODE:{
- begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/
- },QUOTE_STRING_MODE:k,REGEXP_MODE:{scope:"regexp",begin:/\/(?=[^/\n]*\/)/,
- end:/\/[gimuy]*/,contains:[O,{begin:/\[/,end:/\]/,relevance:0,contains:[O]}]},
- RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",
- SHEBANG:(e={})=>{const t=/^#![ ]*\//
- ;return e.binary&&(e.begin=h(t,/.*\b/,e.binary,/\b.*/)),i({scope:"meta",begin:t,
- end:/$/,relevance:0,"on:begin":(e,t)=>{0!==e.index&&t.ignoreMatch()}},e)},
- TITLE_MODE:{scope:"title",begin:E,relevance:0},UNDERSCORE_IDENT_RE:x,
- UNDERSCORE_TITLE_MODE:{scope:"title",begin:x,relevance:0}});function A(e,t){
- "."===e.input[e.index-1]&&t.ignoreMatch()}function I(e,t){
- void 0!==e.className&&(e.scope=e.className,delete e.className)}function T(e,t){
- t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",
- e.__beforeBegin=A,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords,
- void 0===e.relevance&&(e.relevance=0))}function L(e,t){
- Array.isArray(e.illegal)&&(e.illegal=f(...e.illegal))}function B(e,t){
- if(e.match){
- if(e.begin||e.end)throw Error("begin & end are not supported with match")
- ;e.begin=e.match,delete e.match}}function P(e,t){
- void 0===e.relevance&&(e.relevance=1)}const D=(e,t)=>{if(!e.beforeMatch)return
- ;if(e.starts)throw Error("beforeMatch cannot be used with starts")
- ;const n=Object.assign({},e);Object.keys(e).forEach((t=>{delete e[t]
- })),e.keywords=n.keywords,e.begin=h(n.beforeMatch,g(n.begin)),e.starts={
- relevance:0,contains:[Object.assign(n,{endsParent:!0})]
- },e.relevance=0,delete n.beforeMatch
- },H=["of","and","for","in","not","or","if","then","parent","list","value"],C="keyword"
- ;function $(e,t,n=C){const i=Object.create(null)
- ;return"string"==typeof e?s(n,e.split(" ")):Array.isArray(e)?s(n,e):Object.keys(e).forEach((n=>{
- Object.assign(i,$(e[n],t,n))})),i;function s(e,n){
- t&&(n=n.map((e=>e.toLowerCase()))),n.forEach((t=>{const n=t.split("|")
- ;i[n[0]]=[e,U(n[0],n[1])]}))}}function U(e,t){
- return t?Number(t):(e=>H.includes(e.toLowerCase()))(e)?0:1}const z={},W=e=>{
- console.error(e)},X=(e,...t)=>{console.log("WARN: "+e,...t)},G=(e,t)=>{
- z[`${e}/${t}`]||(console.log(`Deprecated as of ${e}. ${t}`),z[`${e}/${t}`]=!0)
- },K=Error();function F(e,t,{key:n}){let i=0;const s=e[n],o={},r={}
- ;for(let e=1;e<=t.length;e++)r[e+i]=s[e],o[e+i]=!0,i+=p(t[e-1])
- ;e[n]=r,e[n]._emit=o,e[n]._multi=!0}function Z(e){(e=>{
- e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope,
- delete e.scope)})(e),"string"==typeof e.beginScope&&(e.beginScope={
- _wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope
- }),(e=>{if(Array.isArray(e.begin)){
- if(e.skip||e.excludeBegin||e.returnBegin)throw W("skip, excludeBegin, returnBegin not compatible with beginScope: {}"),
- K
- ;if("object"!=typeof e.beginScope||null===e.beginScope)throw W("beginScope must be object"),
- K;F(e,e.begin,{key:"beginScope"}),e.begin=m(e.begin,{joinWith:""})}})(e),(e=>{
- if(Array.isArray(e.end)){
- if(e.skip||e.excludeEnd||e.returnEnd)throw W("skip, excludeEnd, returnEnd not compatible with endScope: {}"),
- K
- ;if("object"!=typeof e.endScope||null===e.endScope)throw W("endScope must be object"),
- K;F(e,e.end,{key:"endScope"}),e.end=m(e.end,{joinWith:""})}})(e)}function V(e){
- function t(t,n){
- return RegExp(l(t),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(n?"g":""))
- }class n{constructor(){
- this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}
- addRule(e,t){
- t.position=this.position++,this.matchIndexes[this.matchAt]=t,this.regexes.push([t,e]),
- this.matchAt+=p(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null)
- ;const e=this.regexes.map((e=>e[1]));this.matcherRe=t(m(e,{joinWith:"|"
- }),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex
- ;const t=this.matcherRe.exec(e);if(!t)return null
- ;const n=t.findIndex(((e,t)=>t>0&&void 0!==e)),i=this.matchIndexes[n]
- ;return t.splice(0,n),Object.assign(t,i)}}class s{constructor(){
- this.rules=[],this.multiRegexes=[],
- this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){
- if(this.multiRegexes[e])return this.multiRegexes[e];const t=new n
- ;return this.rules.slice(e).forEach((([e,n])=>t.addRule(e,n))),
- t.compile(),this.multiRegexes[e]=t,t}resumingScanAtSamePosition(){
- return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,t){
- this.rules.push([e,t]),"begin"===t.type&&this.count++}exec(e){
- const t=this.getMatcher(this.regexIndex);t.lastIndex=this.lastIndex
- ;let n=t.exec(e)
- ;if(this.resumingScanAtSamePosition())if(n&&n.index===this.lastIndex);else{
- const t=this.getMatcher(0);t.lastIndex=this.lastIndex+1,n=t.exec(e)}
- return n&&(this.regexIndex+=n.position+1,
- this.regexIndex===this.count&&this.considerAll()),n}}
- if(e.compilerExtensions||(e.compilerExtensions=[]),
- e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.")
- ;return e.classNameAliases=i(e.classNameAliases||{}),function n(o,r){const a=o
- ;if(o.isCompiled)return a
- ;[I,B,Z,D].forEach((e=>e(o,r))),e.compilerExtensions.forEach((e=>e(o,r))),
- o.__beforeBegin=null,[T,L,P].forEach((e=>e(o,r))),o.isCompiled=!0;let c=null
- ;return"object"==typeof o.keywords&&o.keywords.$pattern&&(o.keywords=Object.assign({},o.keywords),
- c=o.keywords.$pattern,
- delete o.keywords.$pattern),c=c||/\w+/,o.keywords&&(o.keywords=$(o.keywords,e.case_insensitive)),
- a.keywordPatternRe=t(c,!0),
- r&&(o.begin||(o.begin=/\B|\b/),a.beginRe=t(a.begin),o.end||o.endsWithParent||(o.end=/\B|\b/),
- o.end&&(a.endRe=t(a.end)),
- a.terminatorEnd=l(a.end)||"",o.endsWithParent&&r.terminatorEnd&&(a.terminatorEnd+=(o.end?"|":"")+r.terminatorEnd)),
- o.illegal&&(a.illegalRe=t(o.illegal)),
- o.contains||(o.contains=[]),o.contains=[].concat(...o.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((t=>i(e,{
- variants:null},t)))),e.cachedVariants?e.cachedVariants:q(e)?i(e,{
- starts:e.starts?i(e.starts):null
- }):Object.isFrozen(e)?i(e):e))("self"===e?o:e)))),o.contains.forEach((e=>{n(e,a)
- })),o.starts&&n(o.starts,r),a.matcher=(e=>{const t=new s
- ;return e.contains.forEach((e=>t.addRule(e.begin,{rule:e,type:"begin"
- }))),e.terminatorEnd&&t.addRule(e.terminatorEnd,{type:"end"
- }),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t})(a),a}(e)}function q(e){
- return!!e&&(e.endsWithParent||q(e.starts))}class J extends Error{
- constructor(e,t){super(e),this.name="HTMLInjectionError",this.html=t}}
- const Y=n,Q=i,ee=Symbol("nomatch"),te=n=>{
- const i=Object.create(null),s=Object.create(null),o=[];let r=!0
- ;const a="Could not find the language '{}', did you forget to load/include a language module?",l={
- disableAutodetect:!0,name:"Plain text",contains:[]};let p={
- ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i,
- languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",
- cssSelector:"pre code",languages:null,__emitter:c};function b(e){
- return p.noHighlightRe.test(e)}function m(e,t,n){let i="",s=""
- ;"object"==typeof t?(i=e,
- n=t.ignoreIllegals,s=t.language):(G("10.7.0","highlight(lang, code, ...args) has been deprecated."),
- G("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"),
- s=e,i=t),void 0===n&&(n=!0);const o={code:i,language:s};N("before:highlight",o)
- ;const r=o.result?o.result:E(o.language,o.code,n)
- ;return r.code=o.code,N("after:highlight",r),r}function E(e,n,s,o){
- const c=Object.create(null);function l(){if(!N.keywords)return void M.addText(R)
- ;let e=0;N.keywordPatternRe.lastIndex=0;let t=N.keywordPatternRe.exec(R),n=""
- ;for(;t;){n+=R.substring(e,t.index)
- ;const s=_.case_insensitive?t[0].toLowerCase():t[0],o=(i=s,N.keywords[i]);if(o){
- const[e,i]=o
- ;if(M.addText(n),n="",c[s]=(c[s]||0)+1,c[s]<=7&&(j+=i),e.startsWith("_"))n+=t[0];else{
- const n=_.classNameAliases[e]||e;u(t[0],n)}}else n+=t[0]
- ;e=N.keywordPatternRe.lastIndex,t=N.keywordPatternRe.exec(R)}var i
- ;n+=R.substring(e),M.addText(n)}function g(){null!=N.subLanguage?(()=>{
- if(""===R)return;let e=null;if("string"==typeof N.subLanguage){
- if(!i[N.subLanguage])return void M.addText(R)
- ;e=E(N.subLanguage,R,!0,S[N.subLanguage]),S[N.subLanguage]=e._top
- }else e=x(R,N.subLanguage.length?N.subLanguage:null)
- ;N.relevance>0&&(j+=e.relevance),M.__addSublanguage(e._emitter,e.language)
- })():l(),R=""}function u(e,t){
- ""!==e&&(M.startScope(t),M.addText(e),M.endScope())}function d(e,t){let n=1
- ;const i=t.length-1;for(;n<=i;){if(!e._emit[n]){n++;continue}
- const i=_.classNameAliases[e[n]]||e[n],s=t[n];i?u(s,i):(R=s,l(),R=""),n++}}
- function h(e,t){
- return e.scope&&"string"==typeof e.scope&&M.openNode(_.classNameAliases[e.scope]||e.scope),
- e.beginScope&&(e.beginScope._wrap?(u(R,_.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap),
- R=""):e.beginScope._multi&&(d(e.beginScope,t),R="")),N=Object.create(e,{parent:{
- value:N}}),N}function f(e,n,i){let s=((e,t)=>{const n=e&&e.exec(t)
- ;return n&&0===n.index})(e.endRe,i);if(s){if(e["on:end"]){const i=new t(e)
- ;e["on:end"](n,i),i.isMatchIgnored&&(s=!1)}if(s){
- for(;e.endsParent&&e.parent;)e=e.parent;return e}}
- if(e.endsWithParent)return f(e.parent,n,i)}function b(e){
- return 0===N.matcher.regexIndex?(R+=e[0],1):(T=!0,0)}function m(e){
- const t=e[0],i=n.substring(e.index),s=f(N,e,i);if(!s)return ee;const o=N
- ;N.endScope&&N.endScope._wrap?(g(),
- u(t,N.endScope._wrap)):N.endScope&&N.endScope._multi?(g(),
- d(N.endScope,e)):o.skip?R+=t:(o.returnEnd||o.excludeEnd||(R+=t),
- g(),o.excludeEnd&&(R=t));do{
- N.scope&&M.closeNode(),N.skip||N.subLanguage||(j+=N.relevance),N=N.parent
- }while(N!==s.parent);return s.starts&&h(s.starts,e),o.returnEnd?0:t.length}
- let w={};function y(i,o){const a=o&&o[0];if(R+=i,null==a)return g(),0
- ;if("begin"===w.type&&"end"===o.type&&w.index===o.index&&""===a){
- if(R+=n.slice(o.index,o.index+1),!r){const t=Error(`0 width match regex (${e})`)
- ;throw t.languageName=e,t.badRule=w.rule,t}return 1}
- if(w=o,"begin"===o.type)return(e=>{
- const n=e[0],i=e.rule,s=new t(i),o=[i.__beforeBegin,i["on:begin"]]
- ;for(const t of o)if(t&&(t(e,s),s.isMatchIgnored))return b(n)
- ;return i.skip?R+=n:(i.excludeBegin&&(R+=n),
- g(),i.returnBegin||i.excludeBegin||(R=n)),h(i,e),i.returnBegin?0:n.length})(o)
- ;if("illegal"===o.type&&!s){
- const e=Error('Illegal lexeme "'+a+'" for mode "'+(N.scope||"")+'"')
- ;throw e.mode=N,e}if("end"===o.type){const e=m(o);if(e!==ee)return e}
- if("illegal"===o.type&&""===a)return 1
- ;if(I>1e5&&I>3*o.index)throw Error("potential infinite loop, way more iterations than matches")
- ;return R+=a,a.length}const _=O(e)
- ;if(!_)throw W(a.replace("{}",e)),Error('Unknown language: "'+e+'"')
- ;const v=V(_);let k="",N=o||v;const S={},M=new p.__emitter(p);(()=>{const e=[]
- ;for(let t=N;t!==_;t=t.parent)t.scope&&e.unshift(t.scope)
- ;e.forEach((e=>M.openNode(e)))})();let R="",j=0,A=0,I=0,T=!1;try{
- if(_.__emitTokens)_.__emitTokens(n,M);else{for(N.matcher.considerAll();;){
- I++,T?T=!1:N.matcher.considerAll(),N.matcher.lastIndex=A
- ;const e=N.matcher.exec(n);if(!e)break;const t=y(n.substring(A,e.index),e)
- ;A=e.index+t}y(n.substring(A))}return M.finalize(),k=M.toHTML(),{language:e,
- value:k,relevance:j,illegal:!1,_emitter:M,_top:N}}catch(t){
- if(t.message&&t.message.includes("Illegal"))return{language:e,value:Y(n),
- illegal:!0,relevance:0,_illegalBy:{message:t.message,index:A,
- context:n.slice(A-100,A+100),mode:t.mode,resultSoFar:k},_emitter:M};if(r)return{
- language:e,value:Y(n),illegal:!1,relevance:0,errorRaised:t,_emitter:M,_top:N}
- ;throw t}}function x(e,t){t=t||p.languages||Object.keys(i);const n=(e=>{
- const t={value:Y(e),illegal:!1,relevance:0,_top:l,_emitter:new p.__emitter(p)}
- ;return t._emitter.addText(e),t})(e),s=t.filter(O).filter(k).map((t=>E(t,e,!1)))
- ;s.unshift(n);const o=s.sort(((e,t)=>{
- if(e.relevance!==t.relevance)return t.relevance-e.relevance
- ;if(e.language&&t.language){if(O(e.language).supersetOf===t.language)return 1
- ;if(O(t.language).supersetOf===e.language)return-1}return 0})),[r,a]=o,c=r
- ;return c.secondBest=a,c}function w(e){let t=null;const n=(e=>{
- let t=e.className+" ";t+=e.parentNode?e.parentNode.className:""
- ;const n=p.languageDetectRe.exec(t);if(n){const t=O(n[1])
- ;return t||(X(a.replace("{}",n[1])),
- X("Falling back to no-highlight mode for this block.",e)),t?n[1]:"no-highlight"}
- return t.split(/\s+/).find((e=>b(e)||O(e)))})(e);if(b(n))return
- ;if(N("before:highlightElement",{el:e,language:n
- }),e.dataset.highlighted)return void console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",e)
- ;if(e.children.length>0&&(p.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."),
- console.warn("https://github.com/highlightjs/highlight.js/wiki/security"),
- console.warn("The element with unescaped HTML:"),
- console.warn(e)),p.throwUnescapedHTML))throw new J("One of your code blocks includes unescaped HTML.",e.innerHTML)
- ;t=e;const i=t.textContent,o=n?m(i,{language:n,ignoreIllegals:!0}):x(i)
- ;e.innerHTML=o.value,e.dataset.highlighted="yes",((e,t,n)=>{const i=t&&s[t]||n
- ;e.classList.add("hljs"),e.classList.add("language-"+i)
- })(e,n,o.language),e.result={language:o.language,re:o.relevance,
- relevance:o.relevance},o.secondBest&&(e.secondBest={
- language:o.secondBest.language,relevance:o.secondBest.relevance
- }),N("after:highlightElement",{el:e,result:o,text:i})}let y=!1;function _(){
- "loading"!==document.readyState?document.querySelectorAll(p.cssSelector).forEach(w):y=!0
- }function O(e){return e=(e||"").toLowerCase(),i[e]||i[s[e]]}
- function v(e,{languageName:t}){"string"==typeof e&&(e=[e]),e.forEach((e=>{
- s[e.toLowerCase()]=t}))}function k(e){const t=O(e)
- ;return t&&!t.disableAutodetect}function N(e,t){const n=e;o.forEach((e=>{
- e[n]&&e[n](t)}))}
- "undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(()=>{
- y&&_()}),!1),Object.assign(n,{highlight:m,highlightAuto:x,highlightAll:_,
- highlightElement:w,
- highlightBlock:e=>(G("10.7.0","highlightBlock will be removed entirely in v12.0"),
- G("10.7.0","Please use highlightElement now."),w(e)),configure:e=>{p=Q(p,e)},
- initHighlighting:()=>{
- _(),G("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")},
- initHighlightingOnLoad:()=>{
- _(),G("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.")
- },registerLanguage:(e,t)=>{let s=null;try{s=t(n)}catch(t){
- if(W("Language definition for '{}' could not be registered.".replace("{}",e)),
- !r)throw t;W(t),s=l}
- s.name||(s.name=e),i[e]=s,s.rawDefinition=t.bind(null,n),s.aliases&&v(s.aliases,{
- languageName:e})},unregisterLanguage:e=>{delete i[e]
- ;for(const t of Object.keys(s))s[t]===e&&delete s[t]},
- listLanguages:()=>Object.keys(i),getLanguage:O,registerAliases:v,
- autoDetection:k,inherit:Q,addPlugin:e=>{(e=>{
- e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=t=>{
- e["before:highlightBlock"](Object.assign({block:t.el},t))
- }),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=t=>{
- e["after:highlightBlock"](Object.assign({block:t.el},t))})})(e),o.push(e)},
- removePlugin:e=>{const t=o.indexOf(e);-1!==t&&o.splice(t,1)}}),n.debugMode=()=>{
- r=!1},n.safeMode=()=>{r=!0},n.versionString="11.9.0",n.regex={concat:h,
- lookahead:g,either:f,optional:d,anyNumberOfTimes:u}
- ;for(const t in j)"object"==typeof j[t]&&e(j[t]);return Object.assign(n,j),n
- },ne=te({});return ne.newInstance=()=>te({}),ne}()
- ;"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs);/*! `go` grammar compiled for Highlight.js 11.9.0 */
- (()=>{var e=(()=>{"use strict";return e=>{const n={
- keyword:["break","case","chan","const","continue","default","defer","else","fallthrough","for","func","go","goto","if","import","interface","map","package","range","return","select","struct","switch","type","var"],
- type:["bool","byte","complex64","complex128","error","float32","float64","int8","int16","int32","int64","string","uint8","uint16","uint32","uint64","int","uint","uintptr","rune"],
- literal:["true","false","iota","nil"],
- built_in:["append","cap","close","complex","copy","imag","len","make","new","panic","print","println","real","recover","delete"]
- };return{name:"Go",aliases:["golang"],keywords:n,illegal:"",
- contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"string",
- variants:[e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,{begin:"`",end:"`"}]},{
- className:"number",variants:[{begin:e.C_NUMBER_RE+"[i]",relevance:1
- },e.C_NUMBER_MODE]},{begin:/:=/},{className:"function",beginKeywords:"func",
- end:"\\s*(\\{|$)",excludeEnd:!0,contains:[e.TITLE_MODE,{className:"params",
- begin:/\(/,end:/\)/,endsParent:!0,keywords:n,illegal:/["']/}]}]}}})()
- ;hljs.registerLanguage("go",e)})();/*! `json` grammar compiled for Highlight.js 11.9.0 */
- (()=>{var e=(()=>{"use strict";return e=>{const a=["true","false","null"],n={
- scope:"literal",beginKeywords:a.join(" ")};return{name:"JSON",keywords:{
- literal:a},contains:[{className:"attr",begin:/"(\\.|[^\\"\r\n])*"(?=\s*:)/,
- relevance:1.01},{match:/[{}[\],:]/,className:"punctuation",relevance:0
- },e.QUOTE_STRING_MODE,n,e.C_NUMBER_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE],
- illegal:"\\S"}}})();hljs.registerLanguage("json",e)})();/*! `plaintext` grammar compiled for Highlight.js 11.9.0 */
- (()=>{var t=(()=>{"use strict";return t=>({name:"Plain text",
- aliases:["text","txt"],disableAutodetect:!0})})()
- ;hljs.registerLanguage("plaintext",t)})();
\ No newline at end of file
diff --git a/gno.land/pkg/gnoweb/static/js/marked.min.js b/gno.land/pkg/gnoweb/static/js/marked.min.js
deleted file mode 100644
index 3cc149db48e..00000000000
--- a/gno.land/pkg/gnoweb/static/js/marked.min.js
+++ /dev/null
@@ -1,14 +0,0 @@
-/**
- * marked v12.0.2 - a markdown parser
- * Copyright (c) 2011-2024, Christopher Jeffrey. (MIT Licensed)
- * https://github.com/markedjs/marked
- */
-!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).marked={})}(this,(function(e){"use strict";function t(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}function n(t){e.defaults=t}e.defaults={async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null};const s=/[&<>"']/,r=new RegExp(s.source,"g"),i=/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,l=new RegExp(i.source,"g"),o={"&":"&","<":"<",">":">",'"':""","'":"'"},a=e=>o[e];function c(e,t){if(t){if(s.test(e))return e.replace(r,a)}else if(i.test(e))return e.replace(l,a);return e}const h=/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/gi;function p(e){return e.replace(h,((e,t)=>"colon"===(t=t.toLowerCase())?":":"#"===t.charAt(0)?"x"===t.charAt(1)?String.fromCharCode(parseInt(t.substring(2),16)):String.fromCharCode(+t.substring(1)):""))}const u=/(^|[^\[])\^/g;function k(e,t){let n="string"==typeof e?e:e.source;t=t||"";const s={replace:(e,t)=>{let r="string"==typeof t?t:t.source;return r=r.replace(u,"$1"),n=n.replace(e,r),s},getRegex:()=>new RegExp(n,t)};return s}function g(e){try{e=encodeURI(e).replace(/%25/g,"%")}catch(e){return null}return e}const f={exec:()=>null};function d(e,t){const n=e.replace(/\|/g,((e,t,n)=>{let s=!1,r=t;for(;--r>=0&&"\\"===n[r];)s=!s;return s?"|":" |"})).split(/ \|/);let s=0;if(n[0].trim()||n.shift(),n.length>0&&!n[n.length-1].trim()&&n.pop(),t)if(n.length>t)n.splice(t);else for(;n.length0)return{type:"space",raw:t[0]}}code(e){const t=this.rules.block.code.exec(e);if(t){const e=t[0].replace(/^ {1,4}/gm,"");return{type:"code",raw:t[0],codeBlockStyle:"indented",text:this.options.pedantic?e:x(e,"\n")}}}fences(e){const t=this.rules.block.fences.exec(e);if(t){const e=t[0],n=function(e,t){const n=e.match(/^(\s+)(?:```)/);if(null===n)return t;const s=n[1];return t.split("\n").map((e=>{const t=e.match(/^\s+/);if(null===t)return e;const[n]=t;return n.length>=s.length?e.slice(s.length):e})).join("\n")}(e,t[3]||"");return{type:"code",raw:e,lang:t[2]?t[2].trim().replace(this.rules.inline.anyPunctuation,"$1"):t[2],text:n}}}heading(e){const t=this.rules.block.heading.exec(e);if(t){let e=t[2].trim();if(/#$/.test(e)){const t=x(e,"#");this.options.pedantic?e=t.trim():t&&!/ $/.test(t)||(e=t.trim())}return{type:"heading",raw:t[0],depth:t[1].length,text:e,tokens:this.lexer.inline(e)}}}hr(e){const t=this.rules.block.hr.exec(e);if(t)return{type:"hr",raw:t[0]}}blockquote(e){const t=this.rules.block.blockquote.exec(e);if(t){let e=t[0].replace(/\n {0,3}((?:=+|-+) *)(?=\n|$)/g,"\n $1");e=x(e.replace(/^ *>[ \t]?/gm,""),"\n");const n=this.lexer.state.top;this.lexer.state.top=!0;const s=this.lexer.blockTokens(e);return this.lexer.state.top=n,{type:"blockquote",raw:t[0],tokens:s,text:e}}}list(e){let t=this.rules.block.list.exec(e);if(t){let n=t[1].trim();const s=n.length>1,r={type:"list",raw:"",ordered:s,start:s?+n.slice(0,-1):"",loose:!1,items:[]};n=s?`\\d{1,9}\\${n.slice(-1)}`:`\\${n}`,this.options.pedantic&&(n=s?n:"[*+-]");const i=new RegExp(`^( {0,3}${n})((?:[\t ][^\\n]*)?(?:\\n|$))`);let l="",o="",a=!1;for(;e;){let n=!1;if(!(t=i.exec(e)))break;if(this.rules.block.hr.test(e))break;l=t[0],e=e.substring(l.length);let s=t[2].split("\n",1)[0].replace(/^\t+/,(e=>" ".repeat(3*e.length))),c=e.split("\n",1)[0],h=0;this.options.pedantic?(h=2,o=s.trimStart()):(h=t[2].search(/[^ ]/),h=h>4?1:h,o=s.slice(h),h+=t[1].length);let p=!1;if(!s&&/^ *$/.test(c)&&(l+=c+"\n",e=e.substring(c.length+1),n=!0),!n){const t=new RegExp(`^ {0,${Math.min(3,h-1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ \t][^\\n]*)?(?:\\n|$))`),n=new RegExp(`^ {0,${Math.min(3,h-1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`),r=new RegExp(`^ {0,${Math.min(3,h-1)}}(?:\`\`\`|~~~)`),i=new RegExp(`^ {0,${Math.min(3,h-1)}}#`);for(;e;){const a=e.split("\n",1)[0];if(c=a,this.options.pedantic&&(c=c.replace(/^ {1,4}(?=( {4})*[^ ])/g," ")),r.test(c))break;if(i.test(c))break;if(t.test(c))break;if(n.test(e))break;if(c.search(/[^ ]/)>=h||!c.trim())o+="\n"+c.slice(h);else{if(p)break;if(s.search(/[^ ]/)>=4)break;if(r.test(s))break;if(i.test(s))break;if(n.test(s))break;o+="\n"+c}p||c.trim()||(p=!0),l+=a+"\n",e=e.substring(a.length+1),s=c.slice(h)}}r.loose||(a?r.loose=!0:/\n *\n *$/.test(l)&&(a=!0));let u,k=null;this.options.gfm&&(k=/^\[[ xX]\] /.exec(o),k&&(u="[ ] "!==k[0],o=o.replace(/^\[[ xX]\] +/,""))),r.items.push({type:"list_item",raw:l,task:!!k,checked:u,loose:!1,text:o,tokens:[]}),r.raw+=l}r.items[r.items.length-1].raw=l.trimEnd(),r.items[r.items.length-1].text=o.trimEnd(),r.raw=r.raw.trimEnd();for(let e=0;e"space"===e.type)),n=t.length>0&&t.some((e=>/\n.*\n/.test(e.raw)));r.loose=n}if(r.loose)for(let e=0;e$/,"$1").replace(this.rules.inline.anyPunctuation,"$1"):"",s=t[3]?t[3].substring(1,t[3].length-1).replace(this.rules.inline.anyPunctuation,"$1"):t[3];return{type:"def",tag:e,raw:t[0],href:n,title:s}}}table(e){const t=this.rules.block.table.exec(e);if(!t)return;if(!/[:|]/.test(t[2]))return;const n=d(t[1]),s=t[2].replace(/^\||\| *$/g,"").split("|"),r=t[3]&&t[3].trim()?t[3].replace(/\n[ \t]*$/,"").split("\n"):[],i={type:"table",raw:t[0],header:[],align:[],rows:[]};if(n.length===s.length){for(const e of s)/^ *-+: *$/.test(e)?i.align.push("right"):/^ *:-+: *$/.test(e)?i.align.push("center"):/^ *:-+ *$/.test(e)?i.align.push("left"):i.align.push(null);for(const e of n)i.header.push({text:e,tokens:this.lexer.inline(e)});for(const e of r)i.rows.push(d(e,i.header.length).map((e=>({text:e,tokens:this.lexer.inline(e)}))));return i}}lheading(e){const t=this.rules.block.lheading.exec(e);if(t)return{type:"heading",raw:t[0],depth:"="===t[2].charAt(0)?1:2,text:t[1],tokens:this.lexer.inline(t[1])}}paragraph(e){const t=this.rules.block.paragraph.exec(e);if(t){const e="\n"===t[1].charAt(t[1].length-1)?t[1].slice(0,-1):t[1];return{type:"paragraph",raw:t[0],text:e,tokens:this.lexer.inline(e)}}}text(e){const t=this.rules.block.text.exec(e);if(t)return{type:"text",raw:t[0],text:t[0],tokens:this.lexer.inline(t[0])}}escape(e){const t=this.rules.inline.escape.exec(e);if(t)return{type:"escape",raw:t[0],text:c(t[1])}}tag(e){const t=this.rules.inline.tag.exec(e);if(t)return!this.lexer.state.inLink&&/^/i.test(t[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&/^<(pre|code|kbd|script)(\s|>)/i.test(t[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&/^<\/(pre|code|kbd|script)(\s|>)/i.test(t[0])&&(this.lexer.state.inRawBlock=!1),{type:"html",raw:t[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:t[0]}}link(e){const t=this.rules.inline.link.exec(e);if(t){const e=t[2].trim();if(!this.options.pedantic&&/^$/.test(e))return;const t=x(e.slice(0,-1),"\\");if((e.length-t.length)%2==0)return}else{const e=function(e,t){if(-1===e.indexOf(t[1]))return-1;let n=0;for(let s=0;s-1){const n=(0===t[0].indexOf("!")?5:4)+t[1].length+e;t[2]=t[2].substring(0,e),t[0]=t[0].substring(0,n).trim(),t[3]=""}}let n=t[2],s="";if(this.options.pedantic){const e=/^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(n);e&&(n=e[1],s=e[3])}else s=t[3]?t[3].slice(1,-1):"";return n=n.trim(),/^$/.test(e)?n.slice(1):n.slice(1,-1)),b(t,{href:n?n.replace(this.rules.inline.anyPunctuation,"$1"):n,title:s?s.replace(this.rules.inline.anyPunctuation,"$1"):s},t[0],this.lexer)}}reflink(e,t){let n;if((n=this.rules.inline.reflink.exec(e))||(n=this.rules.inline.nolink.exec(e))){const e=t[(n[2]||n[1]).replace(/\s+/g," ").toLowerCase()];if(!e){const e=n[0].charAt(0);return{type:"text",raw:e,text:e}}return b(n,e,n[0],this.lexer)}}emStrong(e,t,n=""){let s=this.rules.inline.emStrongLDelim.exec(e);if(!s)return;if(s[3]&&n.match(/[\p{L}\p{N}]/u))return;if(!(s[1]||s[2]||"")||!n||this.rules.inline.punctuation.exec(n)){const n=[...s[0]].length-1;let r,i,l=n,o=0;const a="*"===s[0][0]?this.rules.inline.emStrongRDelimAst:this.rules.inline.emStrongRDelimUnd;for(a.lastIndex=0,t=t.slice(-1*e.length+n);null!=(s=a.exec(t));){if(r=s[1]||s[2]||s[3]||s[4]||s[5]||s[6],!r)continue;if(i=[...r].length,s[3]||s[4]){l+=i;continue}if((s[5]||s[6])&&n%3&&!((n+i)%3)){o+=i;continue}if(l-=i,l>0)continue;i=Math.min(i,i+l+o);const t=[...s[0]][0].length,a=e.slice(0,n+s.index+t+i);if(Math.min(n,i)%2){const e=a.slice(1,-1);return{type:"em",raw:a,text:e,tokens:this.lexer.inlineTokens(e)}}const c=a.slice(2,-2);return{type:"strong",raw:a,text:c,tokens:this.lexer.inlineTokens(c)}}}}codespan(e){const t=this.rules.inline.code.exec(e);if(t){let e=t[2].replace(/\n/g," ");const n=/[^ ]/.test(e),s=/^ /.test(e)&&/ $/.test(e);return n&&s&&(e=e.substring(1,e.length-1)),e=c(e,!0),{type:"codespan",raw:t[0],text:e}}}br(e){const t=this.rules.inline.br.exec(e);if(t)return{type:"br",raw:t[0]}}del(e){const t=this.rules.inline.del.exec(e);if(t)return{type:"del",raw:t[0],text:t[2],tokens:this.lexer.inlineTokens(t[2])}}autolink(e){const t=this.rules.inline.autolink.exec(e);if(t){let e,n;return"@"===t[2]?(e=c(t[1]),n="mailto:"+e):(e=c(t[1]),n=e),{type:"link",raw:t[0],text:e,href:n,tokens:[{type:"text",raw:e,text:e}]}}}url(e){let t;if(t=this.rules.inline.url.exec(e)){let e,n;if("@"===t[2])e=c(t[0]),n="mailto:"+e;else{let s;do{s=t[0],t[0]=this.rules.inline._backpedal.exec(t[0])?.[0]??""}while(s!==t[0]);e=c(t[0]),n="www."===t[1]?"http://"+t[0]:t[0]}return{type:"link",raw:t[0],text:e,href:n,tokens:[{type:"text",raw:e,text:e}]}}}inlineText(e){const t=this.rules.inline.text.exec(e);if(t){let e;return e=this.lexer.state.inRawBlock?t[0]:c(t[0]),{type:"text",raw:t[0],text:e}}}}const m=/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,y=/(?:[*+-]|\d{1,9}[.)])/,$=k(/^(?!bull |blockCode|fences|blockquote|heading|html)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html))+?)\n {0,3}(=+|-+) *(?:\n+|$)/).replace(/bull/g,y).replace(/blockCode/g,/ {4}/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).getRegex(),z=/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/,T=/(?!\s*\])(?:\\.|[^\[\]\\])+/,R=k(/^ {0,3}\[(label)\]: *(?:\n *)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n *)?| *\n *)(title))? *(?:\n+|$)/).replace("label",T).replace("title",/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex(),_=k(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/).replace(/bull/g,y).getRegex(),A="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",S=/|$))/,I=k("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:\\1>[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:(?:\\n *)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)|(?!script|pre|style|textarea)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$))","i").replace("comment",S).replace("tag",A).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),E=k(z).replace("hr",m).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("|table","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",A).getRegex(),q={blockquote:k(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph",E).getRegex(),code:/^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/,def:R,fences:/^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,heading:/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,hr:m,html:I,lheading:$,list:_,newline:/^(?: *(?:\n|$))+/,paragraph:E,table:f,text:/^[^\n]+/},Z=k("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr",m).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("blockquote"," {0,3}>").replace("code"," {4}[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",A).getRegex(),L={...q,table:Z,paragraph:k(z).replace("hr",m).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("table",Z).replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",A).getRegex()},P={...q,html:k("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+?\\1> *(?:\\n{2,}|\\s*$)| \\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",S).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:f,lheading:/^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,paragraph:k(z).replace("hr",m).replace("heading"," *#{1,6} *[^\n]").replace("lheading",$).replace("|table","").replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").replace("|tag","").getRegex()},Q=/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,v=/^( {2,}|\\)\n(?!\s*$)/,B="\\p{P}\\p{S}",C=k(/^((?![*_])[\spunctuation])/,"u").replace(/punctuation/g,B).getRegex(),M=k(/^(?:\*+(?:((?!\*)[punct])|[^\s*]))|^_+(?:((?!_)[punct])|([^\s_]))/,"u").replace(/punct/g,B).getRegex(),O=k("^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)[punct](\\*+)(?=[\\s]|$)|[^punct\\s](\\*+)(?!\\*)(?=[punct\\s]|$)|(?!\\*)[punct\\s](\\*+)(?=[^punct\\s])|[\\s](\\*+)(?!\\*)(?=[punct])|(?!\\*)[punct](\\*+)(?!\\*)(?=[punct])|[^punct\\s](\\*+)(?=[^punct\\s])","gu").replace(/punct/g,B).getRegex(),D=k("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)[punct](_+)(?=[\\s]|$)|[^punct\\s](_+)(?!_)(?=[punct\\s]|$)|(?!_)[punct\\s](_+)(?=[^punct\\s])|[\\s](_+)(?!_)(?=[punct])|(?!_)[punct](_+)(?!_)(?=[punct])","gu").replace(/punct/g,B).getRegex(),j=k(/\\([punct])/,"gu").replace(/punct/g,B).getRegex(),H=k(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme",/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("email",/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(),U=k(S).replace("(?:--\x3e|$)","--\x3e").getRegex(),X=k("^comment|^[a-zA-Z][\\w:-]*\\s*>|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^").replace("comment",U).replace("attribute",/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex(),F=/(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,N=k(/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/).replace("label",F).replace("href",/<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/).replace("title",/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex(),G=k(/^!?\[(label)\]\[(ref)\]/).replace("label",F).replace("ref",T).getRegex(),J=k(/^!?\[(ref)\](?:\[\])?/).replace("ref",T).getRegex(),K={_backpedal:f,anyPunctuation:j,autolink:H,blockSkip:/\[[^[\]]*?\]\([^\(\)]*?\)|`[^`]*?`|<[^<>]*?>/g,br:v,code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,del:f,emStrongLDelim:M,emStrongRDelimAst:O,emStrongRDelimUnd:D,escape:Q,link:N,nolink:J,punctuation:C,reflink:G,reflinkSearch:k("reflink|nolink(?!\\()","g").replace("reflink",G).replace("nolink",J).getRegex(),tag:X,text:/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\t+" ".repeat(n.length)));e;)if(!(this.options.extensions&&this.options.extensions.block&&this.options.extensions.block.some((s=>!!(n=s.call({lexer:this},e,t))&&(e=e.substring(n.raw.length),t.push(n),!0)))))if(n=this.tokenizer.space(e))e=e.substring(n.raw.length),1===n.raw.length&&t.length>0?t[t.length-1].raw+="\n":t.push(n);else if(n=this.tokenizer.code(e))e=e.substring(n.raw.length),s=t[t.length-1],!s||"paragraph"!==s.type&&"text"!==s.type?t.push(n):(s.raw+="\n"+n.raw,s.text+="\n"+n.text,this.inlineQueue[this.inlineQueue.length-1].src=s.text);else if(n=this.tokenizer.fences(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.heading(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.hr(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.blockquote(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.list(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.html(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.def(e))e=e.substring(n.raw.length),s=t[t.length-1],!s||"paragraph"!==s.type&&"text"!==s.type?this.tokens.links[n.tag]||(this.tokens.links[n.tag]={href:n.href,title:n.title}):(s.raw+="\n"+n.raw,s.text+="\n"+n.raw,this.inlineQueue[this.inlineQueue.length-1].src=s.text);else if(n=this.tokenizer.table(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.lheading(e))e=e.substring(n.raw.length),t.push(n);else{if(r=e,this.options.extensions&&this.options.extensions.startBlock){let t=1/0;const n=e.slice(1);let s;this.options.extensions.startBlock.forEach((e=>{s=e.call({lexer:this},n),"number"==typeof s&&s>=0&&(t=Math.min(t,s))})),t<1/0&&t>=0&&(r=e.substring(0,t+1))}if(this.state.top&&(n=this.tokenizer.paragraph(r)))s=t[t.length-1],i&&"paragraph"===s.type?(s.raw+="\n"+n.raw,s.text+="\n"+n.text,this.inlineQueue.pop(),this.inlineQueue[this.inlineQueue.length-1].src=s.text):t.push(n),i=r.length!==e.length,e=e.substring(n.raw.length);else if(n=this.tokenizer.text(e))e=e.substring(n.raw.length),s=t[t.length-1],s&&"text"===s.type?(s.raw+="\n"+n.raw,s.text+="\n"+n.text,this.inlineQueue.pop(),this.inlineQueue[this.inlineQueue.length-1].src=s.text):t.push(n);else if(e){const t="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(t);break}throw new Error(t)}}return this.state.top=!0,t}inline(e,t=[]){return this.inlineQueue.push({src:e,tokens:t}),t}inlineTokens(e,t=[]){let n,s,r,i,l,o,a=e;if(this.tokens.links){const e=Object.keys(this.tokens.links);if(e.length>0)for(;null!=(i=this.tokenizer.rules.inline.reflinkSearch.exec(a));)e.includes(i[0].slice(i[0].lastIndexOf("[")+1,-1))&&(a=a.slice(0,i.index)+"["+"a".repeat(i[0].length-2)+"]"+a.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;null!=(i=this.tokenizer.rules.inline.blockSkip.exec(a));)a=a.slice(0,i.index)+"["+"a".repeat(i[0].length-2)+"]"+a.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);for(;null!=(i=this.tokenizer.rules.inline.anyPunctuation.exec(a));)a=a.slice(0,i.index)+"++"+a.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);for(;e;)if(l||(o=""),l=!1,!(this.options.extensions&&this.options.extensions.inline&&this.options.extensions.inline.some((s=>!!(n=s.call({lexer:this},e,t))&&(e=e.substring(n.raw.length),t.push(n),!0)))))if(n=this.tokenizer.escape(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.tag(e))e=e.substring(n.raw.length),s=t[t.length-1],s&&"text"===n.type&&"text"===s.type?(s.raw+=n.raw,s.text+=n.text):t.push(n);else if(n=this.tokenizer.link(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.reflink(e,this.tokens.links))e=e.substring(n.raw.length),s=t[t.length-1],s&&"text"===n.type&&"text"===s.type?(s.raw+=n.raw,s.text+=n.text):t.push(n);else if(n=this.tokenizer.emStrong(e,a,o))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.codespan(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.br(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.del(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.autolink(e))e=e.substring(n.raw.length),t.push(n);else if(this.state.inLink||!(n=this.tokenizer.url(e))){if(r=e,this.options.extensions&&this.options.extensions.startInline){let t=1/0;const n=e.slice(1);let s;this.options.extensions.startInline.forEach((e=>{s=e.call({lexer:this},n),"number"==typeof s&&s>=0&&(t=Math.min(t,s))})),t<1/0&&t>=0&&(r=e.substring(0,t+1))}if(n=this.tokenizer.inlineText(r))e=e.substring(n.raw.length),"_"!==n.raw.slice(-1)&&(o=n.raw.slice(-1)),l=!0,s=t[t.length-1],s&&"text"===s.type?(s.raw+=n.raw,s.text+=n.text):t.push(n);else if(e){const t="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(t);break}throw new Error(t)}}else e=e.substring(n.raw.length),t.push(n);return t}}class se{options;constructor(t){this.options=t||e.defaults}code(e,t,n){const s=(t||"").match(/^\S*/)?.[0];return e=e.replace(/\n$/,"")+"\n",s?''+(n?e:c(e,!0))+"
\n":""+(n?e:c(e,!0))+"
\n"}blockquote(e){return`\n${e} \n`}html(e,t){return e}heading(e,t,n){return`${e} \n`}hr(){return" \n"}list(e,t,n){const s=t?"ol":"ul";return"<"+s+(t&&1!==n?' start="'+n+'"':"")+">\n"+e+""+s+">\n"}listitem(e,t,n){return`${e} \n`}checkbox(e){return" '}paragraph(e){return`${e}
\n`}table(e,t){return t&&(t=`${t} `),"\n"}tablerow(e){return`\n${e} \n`}tablecell(e,t){const n=t.header?"th":"td";return(t.align?`<${n} align="${t.align}">`:`<${n}>`)+e+`${n}>\n`}strong(e){return`${e} `}em(e){return`${e} `}codespan(e){return`${e}
`}br(){return" "}del(e){return`${e}`}link(e,t,n){const s=g(e);if(null===s)return n;let r='"+n+" ",r}image(e,t,n){const s=g(e);if(null===s)return n;let r=` ",r}text(e){return e}}class re{strong(e){return e}em(e){return e}codespan(e){return e}del(e){return e}html(e){return e}text(e){return e}link(e,t,n){return""+n}image(e,t,n){return""+n}br(){return""}}class ie{options;renderer;textRenderer;constructor(t){this.options=t||e.defaults,this.options.renderer=this.options.renderer||new se,this.renderer=this.options.renderer,this.renderer.options=this.options,this.textRenderer=new re}static parse(e,t){return new ie(t).parse(e)}static parseInline(e,t){return new ie(t).parseInline(e)}parse(e,t=!0){let n="";for(let s=0;s0&&"paragraph"===n.tokens[0].type?(n.tokens[0].text=e+" "+n.tokens[0].text,n.tokens[0].tokens&&n.tokens[0].tokens.length>0&&"text"===n.tokens[0].tokens[0].type&&(n.tokens[0].tokens[0].text=e+" "+n.tokens[0].tokens[0].text)):n.tokens.unshift({type:"text",text:e+" "}):o+=e+" "}o+=this.parse(n.tokens,i),l+=this.renderer.listitem(o,r,!!s)}n+=this.renderer.list(l,t,s);continue}case"html":{const e=r;n+=this.renderer.html(e.text,e.block);continue}case"paragraph":{const e=r;n+=this.renderer.paragraph(this.parseInline(e.tokens));continue}case"text":{let i=r,l=i.tokens?this.parseInline(i.tokens):i.text;for(;s+1{const r=e[s].flat(1/0);n=n.concat(this.walkTokens(r,t))})):e.tokens&&(n=n.concat(this.walkTokens(e.tokens,t)))}}return n}use(...e){const t=this.defaults.extensions||{renderers:{},childTokens:{}};return e.forEach((e=>{const n={...e};if(n.async=this.defaults.async||n.async||!1,e.extensions&&(e.extensions.forEach((e=>{if(!e.name)throw new Error("extension name required");if("renderer"in e){const n=t.renderers[e.name];t.renderers[e.name]=n?function(...t){let s=e.renderer.apply(this,t);return!1===s&&(s=n.apply(this,t)),s}:e.renderer}if("tokenizer"in e){if(!e.level||"block"!==e.level&&"inline"!==e.level)throw new Error("extension level must be 'block' or 'inline'");const n=t[e.level];n?n.unshift(e.tokenizer):t[e.level]=[e.tokenizer],e.start&&("block"===e.level?t.startBlock?t.startBlock.push(e.start):t.startBlock=[e.start]:"inline"===e.level&&(t.startInline?t.startInline.push(e.start):t.startInline=[e.start]))}"childTokens"in e&&e.childTokens&&(t.childTokens[e.name]=e.childTokens)})),n.extensions=t),e.renderer){const t=this.defaults.renderer||new se(this.defaults);for(const n in e.renderer){if(!(n in t))throw new Error(`renderer '${n}' does not exist`);if("options"===n)continue;const s=n,r=e.renderer[s],i=t[s];t[s]=(...e)=>{let n=r.apply(t,e);return!1===n&&(n=i.apply(t,e)),n||""}}n.renderer=t}if(e.tokenizer){const t=this.defaults.tokenizer||new w(this.defaults);for(const n in e.tokenizer){if(!(n in t))throw new Error(`tokenizer '${n}' does not exist`);if(["options","rules","lexer"].includes(n))continue;const s=n,r=e.tokenizer[s],i=t[s];t[s]=(...e)=>{let n=r.apply(t,e);return!1===n&&(n=i.apply(t,e)),n}}n.tokenizer=t}if(e.hooks){const t=this.defaults.hooks||new le;for(const n in e.hooks){if(!(n in t))throw new Error(`hook '${n}' does not exist`);if("options"===n)continue;const s=n,r=e.hooks[s],i=t[s];le.passThroughHooks.has(n)?t[s]=e=>{if(this.defaults.async)return Promise.resolve(r.call(t,e)).then((e=>i.call(t,e)));const n=r.call(t,e);return i.call(t,n)}:t[s]=(...e)=>{let n=r.apply(t,e);return!1===n&&(n=i.apply(t,e)),n}}n.hooks=t}if(e.walkTokens){const t=this.defaults.walkTokens,s=e.walkTokens;n.walkTokens=function(e){let n=[];return n.push(s.call(this,e)),t&&(n=n.concat(t.call(this,e))),n}}this.defaults={...this.defaults,...n}})),this}setOptions(e){return this.defaults={...this.defaults,...e},this}lexer(e,t){return ne.lex(e,t??this.defaults)}parser(e,t){return ie.parse(e,t??this.defaults)}#e(e,t){return(n,s)=>{const r={...s},i={...this.defaults,...r};!0===this.defaults.async&&!1===r.async&&(i.silent||console.warn("marked(): The async option was set to true by an extension. The async: false option sent to parse will be ignored."),i.async=!0);const l=this.#t(!!i.silent,!!i.async);if(null==n)return l(new Error("marked(): input parameter is undefined or null"));if("string"!=typeof n)return l(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(n)+", string expected"));if(i.hooks&&(i.hooks.options=i),i.async)return Promise.resolve(i.hooks?i.hooks.preprocess(n):n).then((t=>e(t,i))).then((e=>i.hooks?i.hooks.processAllTokens(e):e)).then((e=>i.walkTokens?Promise.all(this.walkTokens(e,i.walkTokens)).then((()=>e)):e)).then((e=>t(e,i))).then((e=>i.hooks?i.hooks.postprocess(e):e)).catch(l);try{i.hooks&&(n=i.hooks.preprocess(n));let s=e(n,i);i.hooks&&(s=i.hooks.processAllTokens(s)),i.walkTokens&&this.walkTokens(s,i.walkTokens);let r=t(s,i);return i.hooks&&(r=i.hooks.postprocess(r)),r}catch(e){return l(e)}}}#t(e,t){return n=>{if(n.message+="\nPlease report this to https://github.com/markedjs/marked.",e){const e="An error occurred:
"+c(n.message+"",!0)+" ";return t?Promise.resolve(e):e}if(t)return Promise.reject(n);throw n}}}const ae=new oe;function ce(e,t){return ae.parse(e,t)}ce.options=ce.setOptions=function(e){return ae.setOptions(e),ce.defaults=ae.defaults,n(ce.defaults),ce},ce.getDefaults=t,ce.defaults=e.defaults,ce.use=function(...e){return ae.use(...e),ce.defaults=ae.defaults,n(ce.defaults),ce},ce.walkTokens=function(e,t){return ae.walkTokens(e,t)},ce.parseInline=ae.parseInline,ce.Parser=ie,ce.parser=ie.parse,ce.Renderer=se,ce.TextRenderer=re,ce.Lexer=ne,ce.lexer=ne.lex,ce.Tokenizer=w,ce.Hooks=le,ce.parse=ce;const he=ce.options,pe=ce.setOptions,ue=ce.use,ke=ce.walkTokens,ge=ce.parseInline,fe=ce,de=ie.parse,xe=ne.lex;e.Hooks=le,e.Lexer=ne,e.Marked=oe,e.Parser=ie,e.Renderer=se,e.TextRenderer=re,e.Tokenizer=w,e.getDefaults=t,e.lexer=xe,e.marked=ce,e.options=he,e.parse=fe,e.parseInline=ge,e.parser=de,e.setOptions=pe,e.use=ue,e.walkTokens=ke}));
-/**
- * Minified by jsDelivr using Terser v5.19.2.
- * Original file: /npm/marked-highlight@2.1.1/lib/index.umd.js
- *
- * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
- */
-!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).markedHighlight={})}(this,(function(e){"use strict";function t(e){return(e||"").match(/\S*/)[0]}function n(e){return t=>{"string"==typeof t&&t!==e.text&&(e.escaped=!0,e.text=t)}}const i=/[&<>"']/,o=new RegExp(i.source,"g"),r=/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,g=new RegExp(r.source,"g"),h={"&":"&","<":"<",">":">",'"':""","'":"'"},s=e=>h[e];function c(e,t){if(t){if(i.test(e))return e.replace(o,s)}else if(r.test(e))return e.replace(g,s);return e}e.markedHighlight=function(e){if("function"==typeof e&&(e={highlight:e}),!e||"function"!=typeof e.highlight)throw new Error("Must provide highlight function");return"string"!=typeof e.langPrefix&&(e.langPrefix="language-"),{async:!!e.async,walkTokens(i){if("code"!==i.type)return;const o=t(i.lang);if(e.async)return Promise.resolve(e.highlight(i.text,o,i.lang||"")).then(n(i));const r=e.highlight(i.text,o,i.lang||"");if(r instanceof Promise)throw new Error("markedHighlight is not set to async but the highlight function is async. Set the async option to true on markedHighlight to await the async highlight function.");n(i)(r)},renderer:{code(n,i,o){const r=t(i),g=r?` class="${e.langPrefix}${c(r)}"`:"";return n=n.replace(/\n$/,""),`${o?n:c(n,!0)}\n
`}}}}}));
-//# sourceMappingURL=/sm/3bfb625a4ed441ddc1f215743851a4b727156eef53b458bd31c51a627ce891c9.map
\ No newline at end of file
diff --git a/gno.land/pkg/gnoweb/static/js/purify.min.js b/gno.land/pkg/gnoweb/static/js/purify.min.js
deleted file mode 100644
index ed613fcc36f..00000000000
--- a/gno.land/pkg/gnoweb/static/js/purify.min.js
+++ /dev/null
@@ -1,3 +0,0 @@
-/*! @license DOMPurify 2.3.6 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.3.6/LICENSE */
-!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).DOMPurify=t()}(this,(function(){"use strict";var e=Object.hasOwnProperty,t=Object.setPrototypeOf,n=Object.isFrozen,r=Object.getPrototypeOf,o=Object.getOwnPropertyDescriptor,i=Object.freeze,a=Object.seal,l=Object.create,c="undefined"!=typeof Reflect&&Reflect,s=c.apply,u=c.construct;s||(s=function(e,t,n){return e.apply(t,n)}),i||(i=function(e){return e}),a||(a=function(e){return e}),u||(u=function(e,t){return new(Function.prototype.bind.apply(e,[null].concat(function(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t1?n-1:0),o=1;o/gm),z=a(/^data-[\-\w.\u00B7-\uFFFF]/),B=a(/^aria-[\-\w]+$/),P=a(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),j=a(/^(?:\w+script|data):/i),G=a(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),W=a(/^html$/i),q="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};function Y(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t0&&void 0!==arguments[0]?arguments[0]:K(),n=function(t){return e(t)};if(n.version="2.3.6",n.removed=[],!t||!t.document||9!==t.document.nodeType)return n.isSupported=!1,n;var r=t.document,o=t.document,a=t.DocumentFragment,l=t.HTMLTemplateElement,c=t.Node,s=t.Element,u=t.NodeFilter,m=t.NamedNodeMap,A=void 0===m?t.NamedNodeMap||t.MozNamedAttrMap:m,$=t.HTMLFormElement,X=t.DOMParser,Z=t.trustedTypes,J=s.prototype,Q=w(J,"cloneNode"),ee=w(J,"nextSibling"),te=w(J,"childNodes"),ne=w(J,"parentNode");if("function"==typeof l){var re=o.createElement("template");re.content&&re.content.ownerDocument&&(o=re.content.ownerDocument)}var oe=V(Z,r),ie=oe?oe.createHTML(""):"",ae=o,le=ae.implementation,ce=ae.createNodeIterator,se=ae.createDocumentFragment,ue=ae.getElementsByTagName,me=r.importNode,fe={};try{fe=x(o).documentMode?o.documentMode:{}}catch(e){}var de={};n.isSupported="function"==typeof ne&&le&&void 0!==le.createHTMLDocument&&9!==fe;var pe=H,he=U,ge=z,ye=B,ve=j,be=G,Te=P,Ne=null,Ae=E({},[].concat(Y(k),Y(S),Y(_),Y(O),Y(M))),Ee=null,xe=E({},[].concat(Y(L),Y(R),Y(I),Y(F))),we=Object.seal(Object.create(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),ke=null,Se=null,_e=!0,De=!0,Oe=!1,Ce=!1,Me=!1,Le=!1,Re=!1,Ie=!1,Fe=!1,He=!1,Ue=!0,ze=!0,Be=!1,Pe={},je=null,Ge=E({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]),We=null,qe=E({},["audio","video","img","source","image","track"]),Ye=null,Ke=E({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),Ve="http://www.w3.org/1998/Math/MathML",$e="http://www.w3.org/2000/svg",Xe="http://www.w3.org/1999/xhtml",Ze=Xe,Je=!1,Qe=void 0,et=["application/xhtml+xml","text/html"],tt="text/html",nt=void 0,rt=null,ot=o.createElement("form"),it=function(e){return e instanceof RegExp||e instanceof Function},at=function(e){rt&&rt===e||(e&&"object"===(void 0===e?"undefined":q(e))||(e={}),e=x(e),Ne="ALLOWED_TAGS"in e?E({},e.ALLOWED_TAGS):Ae,Ee="ALLOWED_ATTR"in e?E({},e.ALLOWED_ATTR):xe,Ye="ADD_URI_SAFE_ATTR"in e?E(x(Ke),e.ADD_URI_SAFE_ATTR):Ke,We="ADD_DATA_URI_TAGS"in e?E(x(qe),e.ADD_DATA_URI_TAGS):qe,je="FORBID_CONTENTS"in e?E({},e.FORBID_CONTENTS):Ge,ke="FORBID_TAGS"in e?E({},e.FORBID_TAGS):{},Se="FORBID_ATTR"in e?E({},e.FORBID_ATTR):{},Pe="USE_PROFILES"in e&&e.USE_PROFILES,_e=!1!==e.ALLOW_ARIA_ATTR,De=!1!==e.ALLOW_DATA_ATTR,Oe=e.ALLOW_UNKNOWN_PROTOCOLS||!1,Ce=e.SAFE_FOR_TEMPLATES||!1,Me=e.WHOLE_DOCUMENT||!1,Ie=e.RETURN_DOM||!1,Fe=e.RETURN_DOM_FRAGMENT||!1,He=e.RETURN_TRUSTED_TYPE||!1,Re=e.FORCE_BODY||!1,Ue=!1!==e.SANITIZE_DOM,ze=!1!==e.KEEP_CONTENT,Be=e.IN_PLACE||!1,Te=e.ALLOWED_URI_REGEXP||Te,Ze=e.NAMESPACE||Xe,e.CUSTOM_ELEMENT_HANDLING&&it(e.CUSTOM_ELEMENT_HANDLING.tagNameCheck)&&(we.tagNameCheck=e.CUSTOM_ELEMENT_HANDLING.tagNameCheck),e.CUSTOM_ELEMENT_HANDLING&&it(e.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)&&(we.attributeNameCheck=e.CUSTOM_ELEMENT_HANDLING.attributeNameCheck),e.CUSTOM_ELEMENT_HANDLING&&"boolean"==typeof e.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements&&(we.allowCustomizedBuiltInElements=e.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements),Qe=Qe=-1===et.indexOf(e.PARSER_MEDIA_TYPE)?tt:e.PARSER_MEDIA_TYPE,nt="application/xhtml+xml"===Qe?function(e){return e}:h,Ce&&(De=!1),Fe&&(Ie=!0),Pe&&(Ne=E({},[].concat(Y(M))),Ee=[],!0===Pe.html&&(E(Ne,k),E(Ee,L)),!0===Pe.svg&&(E(Ne,S),E(Ee,R),E(Ee,F)),!0===Pe.svgFilters&&(E(Ne,_),E(Ee,R),E(Ee,F)),!0===Pe.mathMl&&(E(Ne,O),E(Ee,I),E(Ee,F))),e.ADD_TAGS&&(Ne===Ae&&(Ne=x(Ne)),E(Ne,e.ADD_TAGS)),e.ADD_ATTR&&(Ee===xe&&(Ee=x(Ee)),E(Ee,e.ADD_ATTR)),e.ADD_URI_SAFE_ATTR&&E(Ye,e.ADD_URI_SAFE_ATTR),e.FORBID_CONTENTS&&(je===Ge&&(je=x(je)),E(je,e.FORBID_CONTENTS)),ze&&(Ne["#text"]=!0),Me&&E(Ne,["html","head","body"]),Ne.table&&(E(Ne,["tbody"]),delete ke.tbody),i&&i(e),rt=e)},lt=E({},["mi","mo","mn","ms","mtext"]),ct=E({},["foreignobject","desc","title","annotation-xml"]),st=E({},S);E(st,_),E(st,D);var ut=E({},O);E(ut,C);var mt=function(e){var t=ne(e);t&&t.tagName||(t={namespaceURI:Xe,tagName:"template"});var n=h(e.tagName),r=h(t.tagName);if(e.namespaceURI===$e)return t.namespaceURI===Xe?"svg"===n:t.namespaceURI===Ve?"svg"===n&&("annotation-xml"===r||lt[r]):Boolean(st[n]);if(e.namespaceURI===Ve)return t.namespaceURI===Xe?"math"===n:t.namespaceURI===$e?"math"===n&&ct[r]:Boolean(ut[n]);if(e.namespaceURI===Xe){if(t.namespaceURI===$e&&!ct[r])return!1;if(t.namespaceURI===Ve&&!lt[r])return!1;var o=E({},["title","style","font","a","script"]);return!ut[n]&&(o[n]||!st[n])}return!1},ft=function(e){p(n.removed,{element:e});try{e.parentNode.removeChild(e)}catch(t){try{e.outerHTML=ie}catch(t){e.remove()}}},dt=function(e,t){try{p(n.removed,{attribute:t.getAttributeNode(e),from:t})}catch(e){p(n.removed,{attribute:null,from:t})}if(t.removeAttribute(e),"is"===e&&!Ee[e])if(Ie||Fe)try{ft(t)}catch(e){}else try{t.setAttribute(e,"")}catch(e){}},pt=function(e){var t=void 0,n=void 0;if(Re)e=" "+e;else{var r=g(e,/^[\r\n\t ]+/);n=r&&r[0]}"application/xhtml+xml"===Qe&&(e=''+e+"");var i=oe?oe.createHTML(e):e;if(Ze===Xe)try{t=(new X).parseFromString(i,Qe)}catch(e){}if(!t||!t.documentElement){t=le.createDocument(Ze,"template",null);try{t.documentElement.innerHTML=Je?"":i}catch(e){}}var a=t.body||t.documentElement;return e&&n&&a.insertBefore(o.createTextNode(n),a.childNodes[0]||null),Ze===Xe?ue.call(t,Me?"html":"body")[0]:Me?t.documentElement:a},ht=function(e){return ce.call(e.ownerDocument||e,e,u.SHOW_ELEMENT|u.SHOW_COMMENT|u.SHOW_TEXT,null,!1)},gt=function(e){return e instanceof $&&("string"!=typeof e.nodeName||"string"!=typeof e.textContent||"function"!=typeof e.removeChild||!(e.attributes instanceof A)||"function"!=typeof e.removeAttribute||"function"!=typeof e.setAttribute||"string"!=typeof e.namespaceURI||"function"!=typeof e.insertBefore)},yt=function(e){return"object"===(void 0===c?"undefined":q(c))?e instanceof c:e&&"object"===(void 0===e?"undefined":q(e))&&"number"==typeof e.nodeType&&"string"==typeof e.nodeName},vt=function(e,t,r){de[e]&&f(de[e],(function(e){e.call(n,t,r,rt)}))},bt=function(e){var t=void 0;if(vt("beforeSanitizeElements",e,null),gt(e))return ft(e),!0;if(g(e.nodeName,/[\u0080-\uFFFF]/))return ft(e),!0;var r=nt(e.nodeName);if(vt("uponSanitizeElement",e,{tagName:r,allowedTags:Ne}),!yt(e.firstElementChild)&&(!yt(e.content)||!yt(e.content.firstElementChild))&&T(/<[/\w]/g,e.innerHTML)&&T(/<[/\w]/g,e.textContent))return ft(e),!0;if("select"===r&&T(/=0;--a)o.insertBefore(Q(i[a],!0),ee(e))}return ft(e),!0}return e instanceof s&&!mt(e)?(ft(e),!0):"noscript"!==r&&"noembed"!==r||!T(/<\/no(script|embed)/i,e.innerHTML)?(Ce&&3===e.nodeType&&(t=e.textContent,t=y(t,pe," "),t=y(t,he," "),e.textContent!==t&&(p(n.removed,{element:e.cloneNode()}),e.textContent=t)),vt("afterSanitizeElements",e,null),!1):(ft(e),!0)},Tt=function(e,t,n){if(Ue&&("id"===t||"name"===t)&&(n in o||n in ot))return!1;if(De&&!Se[t]&&T(ge,t));else if(_e&&T(ye,t));else if(!Ee[t]||Se[t]){if(!(Nt(e)&&(we.tagNameCheck instanceof RegExp&&T(we.tagNameCheck,e)||we.tagNameCheck instanceof Function&&we.tagNameCheck(e))&&(we.attributeNameCheck instanceof RegExp&&T(we.attributeNameCheck,t)||we.attributeNameCheck instanceof Function&&we.attributeNameCheck(t))||"is"===t&&we.allowCustomizedBuiltInElements&&(we.tagNameCheck instanceof RegExp&&T(we.tagNameCheck,n)||we.tagNameCheck instanceof Function&&we.tagNameCheck(n))))return!1}else if(Ye[t]);else if(T(Te,y(n,be,"")));else if("src"!==t&&"xlink:href"!==t&&"href"!==t||"script"===e||0!==v(n,"data:")||!We[e]){if(Oe&&!T(ve,y(n,be,"")));else if(n)return!1}else;return!0},Nt=function(e){return e.indexOf("-")>0},At=function(e){var t=void 0,r=void 0,o=void 0,i=void 0;vt("beforeSanitizeAttributes",e,null);var a=e.attributes;if(a){var l={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:Ee};for(i=a.length;i--;){var c=t=a[i],s=c.name,u=c.namespaceURI;if(r=b(t.value),o=nt(s),l.attrName=o,l.attrValue=r,l.keepAttr=!0,l.forceKeepAttr=void 0,vt("uponSanitizeAttribute",e,l),r=l.attrValue,!l.forceKeepAttr&&(dt(s,e),l.keepAttr))if(T(/\/>/i,r))dt(s,e);else{Ce&&(r=y(r,pe," "),r=y(r,he," "));var m=nt(e.nodeName);if(Tt(m,o,r))try{u?e.setAttributeNS(u,s,r):e.setAttribute(s,r),d(n.removed)}catch(e){}}}vt("afterSanitizeAttributes",e,null)}},Et=function e(t){var n=void 0,r=ht(t);for(vt("beforeSanitizeShadowDOM",t,null);n=r.nextNode();)vt("uponSanitizeShadowNode",n,null),bt(n)||(n.content instanceof a&&e(n.content),At(n));vt("afterSanitizeShadowDOM",t,null)};return n.sanitize=function(e,o){var i=void 0,l=void 0,s=void 0,u=void 0,m=void 0;if((Je=!e)&&(e="\x3c!--\x3e"),"string"!=typeof e&&!yt(e)){if("function"!=typeof e.toString)throw N("toString is not a function");if("string"!=typeof(e=e.toString()))throw N("dirty is not a string, aborting")}if(!n.isSupported){if("object"===q(t.toStaticHTML)||"function"==typeof t.toStaticHTML){if("string"==typeof e)return t.toStaticHTML(e);if(yt(e))return t.toStaticHTML(e.outerHTML)}return e}if(Le||at(o),n.removed=[],"string"==typeof e&&(Be=!1),Be){if(e.nodeName){var f=nt(e.nodeName);if(!Ne[f]||ke[f])throw N("root node is forbidden and cannot be sanitized in-place")}}else if(e instanceof c)1===(l=(i=pt("\x3c!----\x3e")).ownerDocument.importNode(e,!0)).nodeType&&"BODY"===l.nodeName||"HTML"===l.nodeName?i=l:i.appendChild(l);else{if(!Ie&&!Ce&&!Me&&-1===e.indexOf("<"))return oe&&He?oe.createHTML(e):e;if(!(i=pt(e)))return Ie?null:He?ie:""}i&&Re&&ft(i.firstChild);for(var d=ht(Be?e:i);s=d.nextNode();)3===s.nodeType&&s===u||bt(s)||(s.content instanceof a&&Et(s.content),At(s),u=s);if(u=null,Be)return e;if(Ie){if(Fe)for(m=se.call(i.ownerDocument);i.firstChild;)m.appendChild(i.firstChild);else m=i;return Ee.shadowroot&&(m=me.call(r,m,!0)),m}var p=Me?i.outerHTML:i.innerHTML;return Me&&Ne["!doctype"]&&i.ownerDocument&&i.ownerDocument.doctype&&i.ownerDocument.doctype.name&&T(W,i.ownerDocument.doctype.name)&&(p="\n"+p),Ce&&(p=y(p,pe," "),p=y(p,he," ")),oe&&He?oe.createHTML(p):p},n.setConfig=function(e){at(e),Le=!0},n.clearConfig=function(){rt=null,Le=!1},n.isValidAttribute=function(e,t,n){rt||at({});var r=nt(e),o=nt(t);return Tt(r,o,n)},n.addHook=function(e,t){"function"==typeof t&&(de[e]=de[e]||[],p(de[e],t))},n.removeHook=function(e){de[e]&&d(de[e])},n.removeHooks=function(e){de[e]&&(de[e]=[])},n.removeAllHooks=function(){de={}},n}()}));
-//# sourceMappingURL=purify.min.js.map
diff --git a/gno.land/pkg/gnoweb/static/js/realm_help.js b/gno.land/pkg/gnoweb/static/js/realm_help.js
deleted file mode 100644
index 30cfacd5f59..00000000000
--- a/gno.land/pkg/gnoweb/static/js/realm_help.js
+++ /dev/null
@@ -1,111 +0,0 @@
-function main() {
- // init
- var myAddr = getMyAddress()
- u("#my_address").first().value = myAddr;
- setMyAddress(myAddr);
- // main renders
- u("div.func_spec").each(function(x) {
- updateCommand(u(x));
- });
- // main hooks
- u("div.func_spec input").on("input", function(e) {
- var x = u(e.currentTarget).closest("div.func_spec");
- updateCommand(x);
- });
- // special case: when address changes.
- u("#my_address").on("input", function(e) {
- var value = u("#my_address").first().value;
- setMyAddress(value)
- u("div.func_spec").each(function(node, i) {
- updateCommand(u(node));
- });
- });
-};
-
-function setMyAddress(addr) {
- localStorage.setItem("my_address", addr);
-}
-
-function getMyAddress() {
- var myAddr = localStorage.getItem("my_address");
- if (!myAddr) {
- return "";
- }
- return myAddr;
-}
-
-// x: the u("div.func_spec") element.
-function updateCommand(x) {
- var realmPath = u("#data").data("realm-path");
- var remote = u("#data").data("remote");
- var chainid = u("#data").data("chainid");
- var funcName = x.data("func-name");
- var ins = x.find("table>tbody>tr.func_params input");
- var vals = [];
- ins.each(function(input) {
- vals.push(input.value);
- });
- var myAddr = getMyAddress() || "ADDRESS";
- var shell = x.find(".shell_command");
- shell.empty();
-
- // command Z: all in one.
- shell.append(u("").text("### INSECURE BUT QUICK ###")).append(u(" "));
- var args = ["gnokey", "maketx", "call",
- "-pkgpath", shq(realmPath), "-func", shq(funcName),
- "-gas-fee", "1000000ugnot", "-gas-wanted", "2000000",
- "-send", shq(""),
- "-broadcast", "-chainid", shq(chainid)];
- vals.forEach(function(arg) {
- args.push("-args");
- args.push(shq(arg));
- });
- args.push("-remote", shq(remote));
- args.push(myAddr);
- var command = args.join(" ");
- shell.append(u("").text(command)).append(u(" ")).append(u(" "));
-
- // or...
- shell.append(u("").text("### FULL SECURITY WITH AIRGAP ###")).append(u(" "));
-
- // command 0: query account info.
- var args = ["gnokey", "query", "-remote", shq(remote), "auth/accounts/" + myAddr];
- var command = args.join(" ");
- shell.append(u("").text(command)).append(u(" "));
-
- // command 1: construct tx.
- var args = ["gnokey", "maketx", "call",
- "-pkgpath", shq(realmPath), "-func", shq(funcName),
- "-gas-fee", "1000000ugnot", "-gas-wanted", "2000000",
- "-send", shq("")];
- vals.forEach(function(arg) {
- args.push("-args");
- args.push(shq(arg));
- });
- args.push(myAddr)
- var command = args.join(" ");
- command = command + " > call.tx";
- shell.append(u("").text(command)).append(u(" "));
-
- // command 2: sign tx.
- var args = ["gnokey", "sign",
- "-tx-path", "call.tx", "-chainid", shq(chainid),
- "-account-number", "ACCOUNTNUMBER",
- "-account-sequence", "SEQUENCENUMBER", myAddr];
- var command = args.join(" ");
- shell.append(u("").text(command)).append(u(" "));
-
- // command 3: broadcast tx.
- var args = ["gnokey", "broadcast", "-remote", shq(remote), "call.tx"];
- var command = args.join(" ");
- command = command;
- shell.append(u("").text(command)).append(u(" "));
-}
-
-// Jae: why isn't this a library somewhere?
-function shq(s) {
- var s2 = String(s).replace(/\t/g, '\\t');
- var s2 = String(s2).replace(/\n/g, '\\n');
- var s2 = String(s2).replace(/([$'"`\\!])/g, '\\$1');
- return '"' + s2 + '"';
-};
diff --git a/gno.land/pkg/gnoweb/static/js/renderer.js b/gno.land/pkg/gnoweb/static/js/renderer.js
deleted file mode 100644
index 0aa6400633d..00000000000
--- a/gno.land/pkg/gnoweb/static/js/renderer.js
+++ /dev/null
@@ -1,225 +0,0 @@
-/**
- * Replaces @username by [@username](/r/demo/users:username)
- * @param string rawData text to render usernames in
- * @returns string rendered text
- */
-function renderUsernames(raw) {
- return raw.replace(/( |\n)@([_a-z0-9]{5,16})/, "$1[@$2](/r/demo/users:$2)");
-}
-
-function parseContent(source, isCode) {
- if (isCode) {
- const highlightedCode = hljs.highlightAuto(source).value;
- const codeElement = document.createElement("code");
- codeElement.classList.add("hljs");
- codeElement.innerHTML = highlightedCode;
-
- const preElement = document.createElement("pre");
- preElement.appendChild(codeElement);
-
- return preElement;
- } else {
- const { markedHighlight } = globalThis.markedHighlight;
- const { Marked } = globalThis.marked;
- const markedInstance = new Marked(
- markedHighlight({
- langPrefix: "language-",
- highlight(code, lang, info) {
- if (lang === "json") {
- try {
- code = JSON.stringify(JSON.parse(code), null, 2);
- } catch {
- console.error('Error: The provided JSON code is invalid.');
- }
- }
- const language = hljs.getLanguage(lang) ? lang : "plaintext";
- return hljs.highlight(code, { language }).value;
- },
- })
- );
- markedInstance.setOptions({ gfm: true });
- const doc = new DOMParser().parseFromString(source, "text/html");
- const contents = doc.documentElement.textContent;
-
- return markedInstance.parse(contents);
- }
-}
-
-/*
- * ### ACCORDIONS ###
- *
- * This content is licensed according to the W3C Software License at
- * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
- *
- * Desc: Simple accordion pattern example
- */
-
-class Accordion {
- constructor(domNode) {
- this.buttonEl = domNode;
- this.contentEl = this.buttonEl.nextElementSibling ?? this.buttonEl.parentElement.nextElementSibling;
- this.open = this.buttonEl.getAttribute("aria-expanded") === "true";
-
- this.buttonEl.addEventListener("click", this.onButtonClick.bind(this));
- }
-
- onButtonClick() {
- this.toggle(!this.open);
- }
-
- toggle(open) {
- if (open === this.open) {
- return;
- }
-
- this.open = open;
-
- this.buttonEl.setAttribute("aria-expanded", `${open}`);
- if (open) {
- this.contentEl.classList.remove("is-hidden");
- } else {
- this.contentEl.classList.add("is-hidden");
- }
- }
-}
-
-/*
- * ### TABS ###
- *
- * This content is licensed according to the W3C Software License at
- * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
- *
- * Desc: Tablist widget that implements ARIA Authoring Practices
- */
-
-class Tabs {
- constructor(groupNode) {
- this.tablistNode = groupNode;
-
- this.tabs = [];
-
- this.firstTab = null;
- this.lastTab = null;
-
- this.tabs = Array.from(this.tablistNode.querySelectorAll("[role=tab]"));
- this.tabpanels = [];
-
- for (let tab of this.tabs) {
- const tabpanel = document.getElementById(tab.getAttribute("aria-controls"));
-
- tab.tabIndex = -1;
- tab.setAttribute("aria-selected", "false");
- this.tabpanels.push(tabpanel);
-
- tab.addEventListener("keydown", this.onKeydown.bind(this));
- tab.addEventListener("click", this.onClick.bind(this));
-
- if (!this.firstTab) {
- this.firstTab = tab;
- }
- this.lastTab = tab;
- }
-
- this.setSelectedTab(this.firstTab, false);
- }
-
- setSelectedTab(currentTab, setFocus) {
- if (typeof setFocus !== "boolean") {
- setFocus = true;
- }
- for (let i = 0; i < this.tabs.length; i += 1) {
- var tab = this.tabs[i];
- if (currentTab === tab) {
- tab.setAttribute("aria-selected", "true");
- tab.removeAttribute("tabindex");
- this.tabpanels[i].classList.remove("is-hidden");
- if (setFocus) {
- tab.focus();
- }
- } else {
- tab.setAttribute("aria-selected", "false");
- tab.tabIndex = -1;
- this.tabpanels[i].classList.add("is-hidden");
- }
- }
- }
-
- setSelectedToPreviousTab(currentTab) {
- let index;
-
- if (currentTab === this.firstTab) {
- this.setSelectedTab(this.lastTab);
- } else {
- index = this.tabs.indexOf(currentTab);
- this.setSelectedTab(this.tabs[index - 1]);
- }
- }
-
- setSelectedToNextTab(currentTab) {
- var index;
-
- if (currentTab === this.lastTab) {
- this.setSelectedTab(this.firstTab);
- } else {
- index = this.tabs.indexOf(currentTab);
- this.setSelectedTab(this.tabs[index + 1]);
- }
- }
-
- /* EVENT HANDLERS */
-
- onKeydown(event) {
- const tgt = event.currentTarget,
- flag = false;
-
- switch (event.key) {
- case "ArrowLeft":
- this.setSelectedToPreviousTab(tgt);
- flag = true;
- break;
-
- case "ArrowRight":
- this.setSelectedToNextTab(tgt);
- flag = true;
- break;
-
- case "Home":
- this.setSelectedTab(this.firstTab);
- flag = true;
- break;
-
- case "End":
- this.setSelectedTab(this.lastTab);
- flag = true;
- break;
-
- default:
- break;
- }
-
- if (flag) {
- event.stopPropagation();
- event.preventDefault();
- }
- }
-
- onClick(event) {
- this.setSelectedTab(event.currentTarget);
- }
-}
-
-/*
- * ### INIT COMPONENTS ###
- */
-
-window.addEventListener("load", function () {
- const accordions = Array.from(document.querySelectorAll(".accordion-trigger"));
- for (let accordion of accordions) {
- new Accordion(accordion);
- }
-
- const tablists = Array.from(document.querySelectorAll("[role=tablist].tabs"));
- for (let tab of tablists) {
- new Tabs(tab);
- }
-});
diff --git a/gno.land/pkg/gnoweb/static/js/umbrella.js b/gno.land/pkg/gnoweb/static/js/umbrella.js
deleted file mode 100644
index 9735d031265..00000000000
--- a/gno.land/pkg/gnoweb/static/js/umbrella.js
+++ /dev/null
@@ -1,807 +0,0 @@
-// Umbrella JS http://umbrellajs.com/
-// -----------
-// Small, lightweight jQuery alternative
-// @author Francisco Presencia Fandos https://francisco.io/
-// @inspiration http://youmightnotneedjquery.com/
-
-// Initialize the library
-var u = function (parameter, context) {
- // Make it an instance of u() to avoid needing 'new' as in 'new u()' and just
- // use 'u().bla();'.
- // @reference http://stackoverflow.com/q/24019863
- // @reference http://stackoverflow.com/q/8875878
- if (!(this instanceof u)) {
- return new u(parameter, context);
- }
-
- // No need to further processing it if it's already an instance
- if (parameter instanceof u) {
- return parameter;
- }
-
- // Parse it as a CSS selector if it's a string
- if (typeof parameter === 'string') {
- parameter = this.select(parameter, context);
- }
-
- // If we're referring a specific node as in on('click', function(){ u(this) })
- // or the select() function returned a single node such as in '#id'
- if (parameter && parameter.nodeName) {
- parameter = [parameter];
- }
-
- // Convert to an array, since there are many 'array-like' stuff in js-land
- this.nodes = this.slice(parameter);
-};
-
-// Map u(...).length to u(...).nodes.length
-u.prototype = {
- get length () {
- return this.nodes.length;
- }
-};
-
-// This made the code faster, read "Initializing instance variables" in
-// https://developers.google.com/speed/articles/optimizing-javascript
-u.prototype.nodes = [];
-
-// Add class(es) to the matched nodes
-u.prototype.addClass = function () {
- return this.eacharg(arguments, function (el, name) {
- el.classList.add(name);
- });
-};
-
-
-// [INTERNAL USE ONLY]
-// Add text in the specified position. It is used by other functions
-u.prototype.adjacent = function (html, data, callback) {
- if (typeof data === 'number') {
- if (data === 0) {
- data = [];
- } else {
- data = new Array(data).join().split(',').map(Number.call, Number);
- }
- }
-
- // Loop through all the nodes. It cannot reuse the eacharg() since the data
- // we want to do it once even if there's no "data" and we accept a selector
- return this.each(function (node, j) {
- var fragment = document.createDocumentFragment();
-
- // Allow for data to be falsy and still loop once
- u(data || {}).map(function (el, i) {
- // Allow for callbacks that accept some data
- var part = (typeof html === 'function') ? html.call(this, el, i, node, j) : html;
-
- if (typeof part === 'string') {
- return this.generate(part);
- }
-
- return u(part);
- }).each(function (n) {
- this.isInPage(n)
- ? fragment.appendChild(u(n).clone().first())
- : fragment.appendChild(n);
- });
-
- callback.call(this, node, fragment);
- });
-};
-
-// Add some html as a sibling after each of the matched elements.
-u.prototype.after = function (html, data) {
- return this.adjacent(html, data, function (node, fragment) {
- node.parentNode.insertBefore(fragment, node.nextSibling);
- });
-};
-
-
-// Add some html as a child at the end of each of the matched elements.
-u.prototype.append = function (html, data) {
- return this.adjacent(html, data, function (node, fragment) {
- node.appendChild(fragment);
- });
-};
-
-
-// [INTERNAL USE ONLY]
-
-// Normalize the arguments to an array of strings
-// Allow for several class names like "a b, c" and several parameters
-u.prototype.args = function (args, node, i) {
- if (typeof args === 'function') {
- args = args(node, i);
- }
-
- // First flatten it all to a string http://stackoverflow.com/q/22920305
- // If we try to slice a string bad things happen: ['n', 'a', 'm', 'e']
- if (typeof args !== 'string') {
- args = this.slice(args).map(this.str(node, i));
- }
-
- // Then convert that string to an array of not-null strings
- return args.toString().split(/[\s,]+/).filter(function (e) {
- return e.length;
- });
-};
-
-
-// Merge all of the nodes that the callback return into a simple array
-u.prototype.array = function (callback) {
- callback = callback;
- var self = this;
- return this.nodes.reduce(function (list, node, i) {
- var val;
- if (callback) {
- val = callback.call(self, node, i);
- if (!val) val = false;
- if (typeof val === 'string') val = u(val);
- if (val instanceof u) val = val.nodes;
- } else {
- val = node.innerHTML;
- }
- return list.concat(val !== false ? val : []);
- }, []);
-};
-
-
-// [INTERNAL USE ONLY]
-
-// Handle attributes for the matched elements
-u.prototype.attr = function (name, value, data) {
- data = data ? 'data-' : '';
-
- // This will handle those elements that can accept a pair with these footprints:
- // .attr('a'), .attr('a', 'b'), .attr({ a: 'b' })
- return this.pairs(name, value, function (node, name) {
- return node.getAttribute(data + name);
- }, function (node, name, value) {
- if (value) {
- node.setAttribute(data + name, value);
- } else {
- node.removeAttribute(data + name);
- }
- });
-};
-
-
-// Add some html before each of the matched elements.
-u.prototype.before = function (html, data) {
- return this.adjacent(html, data, function (node, fragment) {
- node.parentNode.insertBefore(fragment, node);
- });
-};
-
-
-// Get the direct children of all of the nodes with an optional filter
-u.prototype.children = function (selector) {
- return this.map(function (node) {
- return this.slice(node.children);
- }).filter(selector);
-};
-
-
-/**
- * Deep clone a DOM node and its descendants.
- * @return {[Object]} Returns an Umbrella.js instance.
- */
-u.prototype.clone = function () {
- return this.map(function (node, i) {
- var clone = node.cloneNode(true);
- var dest = this.getAll(clone);
-
- this.getAll(node).each(function (src, i) {
- for (var key in this.mirror) {
- if (this.mirror[key]) {
- this.mirror[key](src, dest.nodes[i]);
- }
- }
- });
-
- return clone;
- });
-};
-
-/**
- * Return an array of DOM nodes of a source node and its children.
- * @param {[Object]} context DOM node.
- * @param {[String]} tag DOM node tagName.
- * @return {[Array]} Array containing queried DOM nodes.
- */
-u.prototype.getAll = function getAll (context) {
- return u([context].concat(u('*', context).nodes));
-};
-
-// Store all of the operations to perform when cloning elements
-u.prototype.mirror = {};
-
-/**
- * Copy all JavaScript events of source node to destination node.
- * @param {[Object]} source DOM node
- * @param {[Object]} destination DOM node
- * @return {[undefined]]}
- */
-u.prototype.mirror.events = function (src, dest) {
- if (!src._e) return;
-
- for (var type in src._e) {
- src._e[type].forEach(function (ref) {
- u(dest).on(type, ref.callback);
- });
- }
-};
-
-/**
- * Copy select input value to its clone.
- * @param {[Object]} src DOM node
- * @param {[Object]} dest DOM node
- * @return {[undefined]}
- */
-u.prototype.mirror.select = function (src, dest) {
- if (u(src).is('select')) {
- dest.value = src.value;
- }
-};
-
-/**
- * Copy textarea input value to its clone
- * @param {[Object]} src DOM node
- * @param {[Object]} dest DOM node
- * @return {[undefined]}
- */
-u.prototype.mirror.textarea = function (src, dest) {
- if (u(src).is('textarea')) {
- dest.value = src.value;
- }
-};
-
-
-// Find the first ancestor that matches the selector for each node
-u.prototype.closest = function (selector) {
- return this.map(function (node) {
- // Keep going up and up on the tree. First element is also checked
- do {
- if (u(node).is(selector)) {
- return node;
- }
- } while ((node = node.parentNode) && node !== document);
- });
-};
-
-
-// Handle data-* attributes for the matched elements
-u.prototype.data = function (name, value) {
- return this.attr(name, value, true);
-};
-
-
-// Loops through every node from the current call
-u.prototype.each = function (callback) {
- // By doing callback.call we allow "this" to be the context for
- // the callback (see http://stackoverflow.com/q/4065353 precisely)
- this.nodes.forEach(callback.bind(this));
-
- return this;
-};
-
-
-// [INTERNAL USE ONLY]
-// Loop through the combination of every node and every argument passed
-u.prototype.eacharg = function (args, callback) {
- return this.each(function (node, i) {
- this.args(args, node, i).forEach(function (arg) {
- // Perform the callback for this node
- // By doing callback.call we allow "this" to be the context for
- // the callback (see http://stackoverflow.com/q/4065353 precisely)
- callback.call(this, node, arg);
- }, this);
- });
-};
-
-
-// Remove all children of the matched nodes from the DOM.
-u.prototype.empty = function () {
- return this.each(function (node) {
- while (node.firstChild) {
- node.removeChild(node.firstChild);
- }
- });
-};
-
-
-// .filter(selector)
-// Delete all of the nodes that don't pass the selector
-u.prototype.filter = function (selector) {
- // The default function if it's a CSS selector
- // Cannot change name to 'selector' since it'd mess with it inside this fn
- var callback = function (node) {
- // Make it compatible with some other browsers
- node.matches = node.matches || node.msMatchesSelector || node.webkitMatchesSelector;
-
- // Check if it's the same element (or any element if no selector was passed)
- return node.matches(selector || '*');
- };
-
- // filter() receives a function as in .filter(e => u(e).children().length)
- if (typeof selector === 'function') callback = selector;
-
- // filter() receives an instance of Umbrella as in .filter(u('a'))
- if (selector instanceof u) {
- callback = function (node) {
- return (selector.nodes).indexOf(node) !== -1;
- };
- }
-
- // Just a native filtering function for ultra-speed
- return u(this.nodes.filter(callback));
-};
-
-
-// Find all the nodes children of the current ones matched by a selector
-u.prototype.find = function (selector) {
- return this.map(function (node) {
- return u(selector || '*', node);
- });
-};
-
-
-// Get the first of the nodes
-u.prototype.first = function () {
- return this.nodes[0] || false;
-};
-
-
-// [INTERNAL USE ONLY]
-// Generate a fragment of HTML. This irons out the inconsistences
-u.prototype.generate = function (html) {
- // Table elements need to be child of for some f***ed up reason
- if (/^\s* ]/.test(html)) {
- return u(document.createElement('table')).html(html).children().children().nodes;
- } else if (/^\s* ]/.test(html)) {
- return u(document.createElement('table')).html(html).children().children().children().nodes;
- } else if (/^\s* 0;
-};
-
-
-/**
- * Internal use only. This function checks to see if an element is in the page's body. As contains is inclusive and determining if the body contains itself isn't the intention of isInPage this case explicitly returns false.
-https://developer.mozilla.org/en-US/docs/Web/API/Node/contains
- * @param {[Object]} node DOM node
- * @return {Boolean} The Node.contains() method returns a Boolean value indicating whether a node is a descendant of a given node or not.
- */
-u.prototype.isInPage = function isInPage (node) {
- return (node === document.body) ? false : document.body.contains(node);
-};
-
- // Get the last of the nodes
-u.prototype.last = function () {
- return this.nodes[this.length - 1] || false;
-};
-
-
-// Merge all of the nodes that the callback returns
-u.prototype.map = function (callback) {
- return callback ? u(this.array(callback)).unique() : this;
-};
-
-
-// Delete all of the nodes that equals the filter
-u.prototype.not = function (filter) {
- return this.filter(function (node) {
- return !u(node).is(filter || true);
- });
-};
-
-
-// Removes the callback to the event listener for each node
-u.prototype.off = function (events, cb, cb2) {
- var cb_filter_off = (cb == null && cb2 == null);
- var sel = null;
- var cb_to_be_removed = cb;
- if (typeof cb === 'string') {
- sel = cb;
- cb_to_be_removed = cb2;
- }
-
- return this.eacharg(events, function (node, event) {
- u(node._e ? node._e[event] : []).each(function (ref) {
- if (cb_filter_off || (ref.orig_callback === cb_to_be_removed && ref.selector === sel)) {
- node.removeEventListener(event, ref.callback);
- }
- });
- });
-};
-
-
-// Attach a callback to the specified events
-u.prototype.on = function (events, cb, cb2) {
- function overWriteCurrent (e, value) {
- try {
- Object.defineProperty(e, 'currentTarget', {
- value: value,
- configurable: true
- });
- } catch (err) {}
- }
-
- var selector = null;
- var orig_callback = cb;
- if (typeof cb === 'string') {
- selector = cb;
- orig_callback = cb2;
- cb = function (e) {
- var args = arguments;
- u(e.currentTarget)
- .find(selector)
- .each(function (target) {
- // The event is triggered either in the correct node, or a child
- // of the node that we are interested in
- // Note: .contains() will also check itself (besides children)
- if (!target.contains(e.target)) return;
-
- // If e.g. a child of a link was clicked, but we are listening
- // to the link, this will make the currentTarget the link itself,
- // so it's the "delegated" element instead of the root target. It
- // makes u('.render a').on('click') and u('.render').on('click', 'a')
- // to have the same currentTarget (the 'a')
- var curr = e.currentTarget;
- overWriteCurrent(e, target);
- cb2.apply(target, args);
- // Need to undo it afterwards, in case this event is reused in another
- // callback since otherwise u(e.currentTarget) above would break
- overWriteCurrent(e, curr);
- });
- };
- }
-
- var callback = function (e) {
- return cb.apply(this, [e].concat(e.detail || []));
- };
-
- return this.eacharg(events, function (node, event) {
- node.addEventListener(event, callback);
-
- // Store it so we can dereference it with `.off()` later on
- node._e = node._e || {};
- node._e[event] = node._e[event] || [];
- node._e[event].push({
- callback: callback,
- orig_callback: orig_callback,
- selector: selector
- });
- });
-};
-
-
-// [INTERNAL USE ONLY]
-
-// Take the arguments and a couple of callback to handle the getter/setter pairs
-// such as: .css('a'), .css('a', 'b'), .css({ a: 'b' })
-u.prototype.pairs = function (name, value, get, set) {
- // Convert it into a plain object if it is not
- if (typeof value !== 'undefined') {
- var nm = name;
- name = {};
- name[nm] = value;
- }
-
- if (typeof name === 'object') {
- // Set the value of each one, for each of the { prop: value } pairs
- return this.each(function (node, i) {
- for (var key in name) {
- if (typeof name[key] === 'function') {
- set(node, key, name[key](node, i));
- } else {
- set(node, key, name[key]);
- }
- }
- });
- }
-
- // Return the style of the first one
- return this.length ? get(this.first(), name) : '';
-};
-
-// [INTERNAL USE ONLY]
-
-// Parametize an object: { a: 'b', c: 'd' } => 'a=b&c=d'
-u.prototype.param = function (obj) {
- return Object.keys(obj).map(function (key) {
- return this.uri(key) + '=' + this.uri(obj[key]);
- }.bind(this)).join('&');
-};
-
-// Travel the matched elements one node up
-u.prototype.parent = function (selector) {
- return this.map(function (node) {
- return node.parentNode;
- }).filter(selector);
-};
-
-
-// Add nodes at the beginning of each node
-u.prototype.prepend = function (html, data) {
- return this.adjacent(html, data, function (node, fragment) {
- node.insertBefore(fragment, node.firstChild);
- });
-};
-
-
-// Delete the matched nodes from the DOM
-u.prototype.remove = function () {
- // Loop through all the nodes
- return this.each(function (node) {
- // Perform the removal only if the node has a parent
- if (node.parentNode) {
- node.parentNode.removeChild(node);
- }
- });
-};
-
-
-// Removes a class from all of the matched nodes
-u.prototype.removeClass = function () {
- // Loop the combination of each node with each argument
- return this.eacharg(arguments, function (el, name) {
- // Remove the class using the native method
- el.classList.remove(name);
- });
-};
-
-
-// Replace the matched elements with the passed argument.
-u.prototype.replace = function (html, data) {
- var nodes = [];
- this.adjacent(html, data, function (node, fragment) {
- nodes = nodes.concat(this.slice(fragment.children));
- node.parentNode.replaceChild(fragment, node);
- });
- return u(nodes);
-};
-
-
-// Scroll to the first matched element
-u.prototype.scroll = function () {
- this.first().scrollIntoView({ behavior: 'smooth' });
- return this;
-};
-
-
-// [INTERNAL USE ONLY]
-// Select the adequate part from the context
-u.prototype.select = function (parameter, context) {
- // Allow for spaces before or after
- parameter = parameter.replace(/^\s*/, '').replace(/\s*$/, '');
-
- if (/^'),
- // 2) clone the currently matched node
- // 3) append cloned dom node to constructed node based on selector
- return this.map(function (node) {
- return u(selector).each(function (n) {
- findDeepestNode(n)
- .append(node.cloneNode(true));
-
- node
- .parentNode
- .replaceChild(n, node);
- });
- });
-};
-
-// Export it for webpack
-if (typeof module === 'object' && module.exports) {
- // Avoid breaking it for `import { u } from ...`. Add `import u from ...`
- module.exports = u;
- module.exports.u = u;
-}
-
diff --git a/gno.land/pkg/gnoweb/static/js/umbrella.min.js b/gno.land/pkg/gnoweb/static/js/umbrella.min.js
deleted file mode 100644
index 70ecb99cc32..00000000000
--- a/gno.land/pkg/gnoweb/static/js/umbrella.min.js
+++ /dev/null
@@ -1,3 +0,0 @@
-/* Umbrella JS 3.3.0 umbrellajs.com */
-
-var u=function(t,e){return this instanceof u?t instanceof u?t:((t="string"==typeof t?this.select(t,e):t)&&t.nodeName&&(t=[t]),void(this.nodes=this.slice(t))):new u(t,e)};u.prototype={get length(){return this.nodes.length}},u.prototype.nodes=[],u.prototype.addClass=function(){return this.eacharg(arguments,function(t,e){t.classList.add(e)})},u.prototype.adjacent=function(o,t,i){return"number"==typeof t&&(t=0===t?[]:new Array(t).join().split(",").map(Number.call,Number)),this.each(function(n,r){var e=document.createDocumentFragment();u(t||{}).map(function(t,e){e="function"==typeof o?o.call(this,t,e,n,r):o;return"string"==typeof e?this.generate(e):u(e)}).each(function(t){this.isInPage(t)?e.appendChild(u(t).clone().first()):e.appendChild(t)}),i.call(this,n,e)})},u.prototype.after=function(t,e){return this.adjacent(t,e,function(t,e){t.parentNode.insertBefore(e,t.nextSibling)})},u.prototype.append=function(t,e){return this.adjacent(t,e,function(t,e){t.appendChild(e)})},u.prototype.args=function(t,e,n){return(t="string"!=typeof(t="function"==typeof t?t(e,n):t)?this.slice(t).map(this.str(e,n)):t).toString().split(/[\s,]+/).filter(function(t){return t.length})},u.prototype.array=function(o){var i=this;return this.nodes.reduce(function(t,e,n){var r;return o?(r="string"==typeof(r=(r=o.call(i,e,n))||!1)?u(r):r)instanceof u&&(r=r.nodes):r=e.innerHTML,t.concat(!1!==r?r:[])},[])},u.prototype.attr=function(t,e,r){return r=r?"data-":"",this.pairs(t,e,function(t,e){return t.getAttribute(r+e)},function(t,e,n){n?t.setAttribute(r+e,n):t.removeAttribute(r+e)})},u.prototype.before=function(t,e){return this.adjacent(t,e,function(t,e){t.parentNode.insertBefore(e,t)})},u.prototype.children=function(t){return this.map(function(t){return this.slice(t.children)}).filter(t)},u.prototype.clone=function(){return this.map(function(t,e){var n=t.cloneNode(!0),r=this.getAll(n);return this.getAll(t).each(function(t,e){for(var n in this.mirror)this.mirror[n]&&this.mirror[n](t,r.nodes[e])}),n})},u.prototype.getAll=function(t){return u([t].concat(u("*",t).nodes))},u.prototype.mirror={},u.prototype.mirror.events=function(t,e){if(t._e)for(var n in t._e)t._e[n].forEach(function(t){u(e).on(n,t.callback)})},u.prototype.mirror.select=function(t,e){u(t).is("select")&&(e.value=t.value)},u.prototype.mirror.textarea=function(t,e){u(t).is("textarea")&&(e.value=t.value)},u.prototype.closest=function(e){return this.map(function(t){do{if(u(t).is(e))return t}while((t=t.parentNode)&&t!==document)})},u.prototype.data=function(t,e){return this.attr(t,e,!0)},u.prototype.each=function(t){return this.nodes.forEach(t.bind(this)),this},u.prototype.eacharg=function(n,r){return this.each(function(e,t){this.args(n,e,t).forEach(function(t){r.call(this,e,t)},this)})},u.prototype.empty=function(){return this.each(function(t){for(;t.firstChild;)t.removeChild(t.firstChild)})},u.prototype.filter=function(e){var t=e instanceof u?function(t){return-1!==e.nodes.indexOf(t)}:"function"==typeof e?e:function(t){return t.matches=t.matches||t.msMatchesSelector||t.webkitMatchesSelector,t.matches(e||"*")};return u(this.nodes.filter(t))},u.prototype.find=function(e){return this.map(function(t){return u(e||"*",t)})},u.prototype.first=function(){return this.nodes[0]||!1},u.prototype.generate=function(t){return/^\s* ]/.test(t)?u(document.createElement("table")).html(t).children().children().nodes:/^\s* ]/.test(t)?u(document.createElement("table")).html(t).children().children().children().nodes:/^\s*= 360 || s < 0 || s > 1 || l < 0 || l > 1 {
+ return 0, 0, 0
+ }
+
+ C := (1 - math.Abs((2*l)-1)) * s
+ X := C * (1 - math.Abs(math.Mod(h/60, 2)-1))
+ m := l - (C / 2)
+
+ var rNot, gNot, bNot float64
+ switch {
+ case 0 <= h && h < 60:
+ rNot, gNot, bNot = C, X, 0
+ case 60 <= h && h < 120:
+ rNot, gNot, bNot = X, C, 0
+ case 120 <= h && h < 180:
+ rNot, gNot, bNot = 0, C, X
+ case 180 <= h && h < 240:
+ rNot, gNot, bNot = 0, X, C
+ case 240 <= h && h < 300:
+ rNot, gNot, bNot = X, 0, C
+ case 300 <= h && h < 360:
+ rNot, gNot, bNot = C, 0, X
+ }
+
+ r = uint8(math.Round((rNot + m) * 255))
+ g = uint8(math.Round((gNot + m) * 255))
+ b = uint8(math.Round((bNot + m) * 255))
+ return r, g, b
+}
+
+func rgbToHex(r, g, b uint8) string {
+ return fmt.Sprintf("#%02X%02X%02X", r, g, b)
+}
diff --git a/gno.land/pkg/gnoweb/tools/cmd/logname/main.go b/gno.land/pkg/gnoweb/tools/cmd/logname/main.go
new file mode 100644
index 00000000000..50b42b79c0a
--- /dev/null
+++ b/gno.land/pkg/gnoweb/tools/cmd/logname/main.go
@@ -0,0 +1,41 @@
+package main
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+
+ "github.com/charmbracelet/lipgloss"
+)
+
+func main() {
+ if len(os.Args) < 1 {
+ fmt.Fprintln(os.Stderr, "invalid name")
+ os.Exit(1)
+ }
+
+ name := os.Args[1]
+
+ const width = 12
+ if len(name) >= width {
+ name = name[:width-3] + "..."
+ }
+
+ colorLeft := colorFromString(name, 0.5, 0.6, 90)
+ colorRight := colorFromString(name, 1.0, 0.92, 90)
+ borderStyle := lipgloss.NewStyle().Foreground(colorLeft).
+ Border(lipgloss.ThickBorder(), false, true, false, false).
+ BorderForeground(colorLeft).
+ Bold(true).
+ Width(width)
+ lineStyle := lipgloss.NewStyle().Foreground(colorRight)
+
+ w, r := os.Stdout, os.Stdin
+
+ scanner := bufio.NewScanner(r)
+ for scanner.Scan() {
+ line := scanner.Text()
+ fmt.Fprint(w, borderStyle.Render(name)+" ")
+ fmt.Fprintln(w, lineStyle.Render(line))
+ }
+}
diff --git a/gno.land/pkg/gnoweb/tools/go.mod b/gno.land/pkg/gnoweb/tools/go.mod
new file mode 100644
index 00000000000..1b50c9d52dd
--- /dev/null
+++ b/gno.land/pkg/gnoweb/tools/go.mod
@@ -0,0 +1,25 @@
+module tools
+
+go 1.22.3
+
+require (
+ github.com/cespare/reflex v0.3.1
+ github.com/charmbracelet/lipgloss v0.11.0
+)
+
+require (
+ github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
+ github.com/charmbracelet/x/ansi v0.1.1 // indirect
+ github.com/creack/pty v1.1.21 // indirect
+ github.com/fsnotify/fsnotify v1.7.0 // indirect
+ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
+ github.com/kr/pretty v0.3.1 // indirect
+ github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
+ github.com/mattn/go-isatty v0.0.20 // indirect
+ github.com/mattn/go-runewidth v0.0.15 // indirect
+ github.com/muesli/termenv v0.15.2 // indirect
+ github.com/ogier/pflag v0.0.1 // indirect
+ github.com/rivo/uniseg v0.4.7 // indirect
+ github.com/rogpeppe/go-internal v1.12.0 // indirect
+ golang.org/x/sys v0.21.0 // indirect
+)
diff --git a/gno.land/pkg/gnoweb/tools/go.sum b/gno.land/pkg/gnoweb/tools/go.sum
new file mode 100644
index 00000000000..7eec65cb8ec
--- /dev/null
+++ b/gno.land/pkg/gnoweb/tools/go.sum
@@ -0,0 +1,45 @@
+github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
+github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
+github.com/cespare/reflex v0.3.1 h1:N4Y/UmRrjwOkNT0oQQnYsdr6YBxvHqtSfPB4mqOyAKk=
+github.com/cespare/reflex v0.3.1/go.mod h1:I+0Pnu2W693i7Hv6ZZG76qHTY0mgUa7uCIfCtikXojE=
+github.com/charmbracelet/lipgloss v0.11.0 h1:UoAcbQ6Qml8hDwSWs0Y1cB5TEQuZkDPH/ZqwWWYTG4g=
+github.com/charmbracelet/lipgloss v0.11.0/go.mod h1:1UdRTH9gYgpcdNN5oBtjbu/IzNKtzVtb7sqN1t9LNn8=
+github.com/charmbracelet/x/ansi v0.1.1 h1:CGAduulr6egay/YVbGc8Hsu8deMg1xZ/bkaXTPi1JDk=
+github.com/charmbracelet/x/ansi v0.1.1/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
+github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
+github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
+github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
+github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
+github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
+github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
+github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
+github.com/ogier/pflag v0.0.1 h1:RW6JSWSu/RkSatfcLtogGfFgpim5p7ARQ10ECk5O750=
+github.com/ogier/pflag v0.0.1/go.mod h1:zkFki7tvTa0tafRvTBIZTvzYyAu6kQhPZFnshFFPE+g=
+github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
+github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
+github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
+github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
+github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
+github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
+golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
diff --git a/gno.land/pkg/gnoweb/tools/tools.go b/gno.land/pkg/gnoweb/tools/tools.go
new file mode 100644
index 00000000000..a444aecbcea
--- /dev/null
+++ b/gno.land/pkg/gnoweb/tools/tools.go
@@ -0,0 +1,5 @@
+package tools
+
+import (
+ _ "github.com/cespare/reflex"
+)
diff --git a/gno.land/pkg/gnoweb/url.go b/gno.land/pkg/gnoweb/url.go
new file mode 100644
index 00000000000..bc03f2182d9
--- /dev/null
+++ b/gno.land/pkg/gnoweb/url.go
@@ -0,0 +1,148 @@
+package gnoweb
+
+import (
+ "errors"
+ "fmt"
+ "net/url"
+ "regexp"
+ "strings"
+)
+
+type PathKind byte
+
+const (
+ KindInvalid PathKind = 0
+ KindRealm PathKind = 'r'
+ KindPure PathKind = 'p'
+)
+
+// GnoURL decomposes the parts of an URL to query a realm.
+type GnoURL struct {
+ // Example full path:
+ // gno.land/r/demo/users:jae$help&a=b?c=d
+
+ Domain string // gno.land
+ Path string // /r/demo/users
+ Args string // jae
+ WebQuery url.Values // help&a=b
+ Query url.Values // c=d
+}
+
+func (url GnoURL) EncodeArgs() string {
+ var urlstr strings.Builder
+ if url.Args != "" {
+ urlstr.WriteString(url.Args)
+ }
+
+ if len(url.Query) > 0 {
+ urlstr.WriteString("?" + url.Query.Encode())
+ }
+
+ return urlstr.String()
+}
+
+func (url GnoURL) EncodePath() string {
+ var urlstr strings.Builder
+ urlstr.WriteString(url.Path)
+ if url.Args != "" {
+ urlstr.WriteString(":" + url.Args)
+ }
+
+ if len(url.Query) > 0 {
+ urlstr.WriteString("?" + url.Query.Encode())
+ }
+
+ return urlstr.String()
+}
+
+func (url GnoURL) EncodeWebPath() string {
+ var urlstr strings.Builder
+ urlstr.WriteString(url.Path)
+ if url.Args != "" {
+ pathEscape := escapeDollarSign(url.Args)
+ urlstr.WriteString(":" + pathEscape)
+ }
+
+ if len(url.WebQuery) > 0 {
+ urlstr.WriteString("$" + url.WebQuery.Encode())
+ }
+
+ if len(url.Query) > 0 {
+ urlstr.WriteString("?" + url.Query.Encode())
+ }
+
+ return urlstr.String()
+}
+
+func (url GnoURL) Kind() PathKind {
+ if len(url.Path) < 2 {
+ return KindInvalid
+ }
+ pk := PathKind(url.Path[1])
+ switch pk {
+ case KindPure, KindRealm:
+ return pk
+ }
+ return KindInvalid
+}
+
+var (
+ ErrURLMalformedPath = errors.New("malformed URL path")
+ ErrURLInvalidPathKind = errors.New("invalid path kind")
+)
+
+// reRealName match a realm path
+// - matches[1]: path
+// - matches[2]: path args
+var reRealmPath = regexp.MustCompile(`^` +
+ `(/(?:[a-zA-Z0-9_-]+)/` + // path kind
+ `[a-zA-Z][a-zA-Z0-9_-]*` + // First path segment
+ `(?:/[a-zA-Z][.a-zA-Z0-9_-]*)*/?)` + // Additional path segments
+ `([:$](?:.*))?$`, // Remaining portions args, separate by `$` or `:`
+)
+
+func ParseGnoURL(u *url.URL) (*GnoURL, error) {
+ matches := reRealmPath.FindStringSubmatch(u.EscapedPath())
+ if len(matches) != 3 {
+ return nil, fmt.Errorf("%w: %s", ErrURLMalformedPath, u.Path)
+ }
+
+ path := matches[1]
+ args := matches[2]
+
+ if len(args) > 0 {
+ switch args[0] {
+ case ':':
+ args = args[1:]
+ case '$':
+ default:
+ return nil, fmt.Errorf("%w: %s", ErrURLMalformedPath, u.Path)
+ }
+ }
+
+ var err error
+ webquery := url.Values{}
+ args, webargs, found := strings.Cut(args, "$")
+ if found {
+ if webquery, err = url.ParseQuery(webargs); err != nil {
+ return nil, fmt.Errorf("unable to parse webquery %q: %w ", webquery, err)
+ }
+ }
+
+ uargs, err := url.PathUnescape(args)
+ if err != nil {
+ return nil, fmt.Errorf("unable to unescape path %q: %w", args, err)
+ }
+
+ return &GnoURL{
+ Path: path,
+ Args: uargs,
+ WebQuery: webquery,
+ Query: u.Query(),
+ Domain: u.Hostname(),
+ }, nil
+}
+
+func escapeDollarSign(s string) string {
+ return strings.ReplaceAll(s, "$", "%24")
+}
diff --git a/gno.land/pkg/gnoweb/url_test.go b/gno.land/pkg/gnoweb/url_test.go
new file mode 100644
index 00000000000..73cfdda69bd
--- /dev/null
+++ b/gno.land/pkg/gnoweb/url_test.go
@@ -0,0 +1,135 @@
+package gnoweb
+
+import (
+ "net/url"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestParseGnoURL(t *testing.T) {
+ testCases := []struct {
+ Name string
+ Input string
+ Expected *GnoURL
+ Err error
+ }{
+ {
+ Name: "malformed url",
+ Input: "https://gno.land/r/dem)o:$?",
+ Expected: nil,
+ Err: ErrURLMalformedPath,
+ },
+ {
+ Name: "simple",
+ Input: "https://gno.land/r/simple/test",
+ Expected: &GnoURL{
+ Domain: "gno.land",
+ Path: "/r/simple/test",
+ WebQuery: url.Values{},
+ Query: url.Values{},
+ },
+ Err: nil,
+ },
+ {
+ Name: "webquery + query",
+ Input: "https://gno.land/r/demo/foo$help&func=Bar&name=Baz",
+ Expected: &GnoURL{
+ Path: "/r/demo/foo",
+ Args: "",
+ WebQuery: url.Values{
+ "help": []string{""},
+ "func": []string{"Bar"},
+ "name": []string{"Baz"},
+ },
+ Query: url.Values{},
+ Domain: "gno.land",
+ },
+ Err: nil,
+ },
+
+ {
+ Name: "path args + webquery",
+ Input: "https://gno.land/r/demo/foo:example$tz=Europe/Paris",
+ Expected: &GnoURL{
+ Path: "/r/demo/foo",
+ Args: "example",
+ WebQuery: url.Values{
+ "tz": []string{"Europe/Paris"},
+ },
+ Query: url.Values{},
+ Domain: "gno.land",
+ },
+ Err: nil,
+ },
+
+ {
+ Name: "path args + webquery + query",
+ Input: "https://gno.land/r/demo/foo:example$tz=Europe/Paris?hello=42",
+ Expected: &GnoURL{
+ Path: "/r/demo/foo",
+ Args: "example",
+ WebQuery: url.Values{
+ "tz": []string{"Europe/Paris"},
+ },
+ Query: url.Values{
+ "hello": []string{"42"},
+ },
+ Domain: "gno.land",
+ },
+ Err: nil,
+ },
+
+ {
+ Name: "webquery inside query",
+ Input: "https://gno.land/r/demo/foo:example?value=42$tz=Europe/Paris",
+ Expected: &GnoURL{
+ Path: "/r/demo/foo",
+ Args: "example",
+ WebQuery: url.Values{},
+ Query: url.Values{
+ "value": []string{"42$tz=Europe/Paris"},
+ },
+ Domain: "gno.land",
+ },
+ Err: nil,
+ },
+
+ {
+ Name: "webquery escaped $",
+ Input: "https://gno.land/r/demo/foo:example%24hello=43$hello=42",
+ Expected: &GnoURL{
+ Path: "/r/demo/foo",
+ Args: "example$hello=43",
+ WebQuery: url.Values{
+ "hello": []string{"42"},
+ },
+ Query: url.Values{},
+ Domain: "gno.land",
+ },
+ Err: nil,
+ },
+
+ // XXX: more tests
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.Name, func(t *testing.T) {
+ u, err := url.Parse(tc.Input)
+ require.NoError(t, err)
+
+ result, err := ParseGnoURL(u)
+ if tc.Err == nil {
+ require.NoError(t, err)
+ t.Logf("parsed: %s", result.EncodePath())
+ t.Logf("parsed web: %s", result.EncodeWebPath())
+ } else {
+ require.Error(t, err)
+ require.ErrorIs(t, err, tc.Err)
+ }
+
+ assert.Equal(t, tc.Expected, result)
+ })
+ }
+}
diff --git a/gno.land/pkg/gnoweb/views/404.html b/gno.land/pkg/gnoweb/views/404.html
deleted file mode 100644
index fee4fff8689..00000000000
--- a/gno.land/pkg/gnoweb/views/404.html
+++ /dev/null
@@ -1,18 +0,0 @@
-{{- define "app" -}}
-
-
-
- {{ template "html_head" . }}
- 404 - Not Found
-
-
-
-
-
{{.Data.title}}
-
{{.Data.path}}
-
-
- {{ template "analytics" .}}
-
-
-{{- end -}}
diff --git a/gno.land/pkg/gnoweb/views/faucet.html b/gno.land/pkg/gnoweb/views/faucet.html
deleted file mode 100644
index 938b8993e0e..00000000000
--- a/gno.land/pkg/gnoweb/views/faucet.html
+++ /dev/null
@@ -1,139 +0,0 @@
-{{- define "app" -}}
-
-
-
- {{ template "html_head" . }}
- gno.land
-
-
-
-
-
- This is the gno.land (test) {{ if .Data.Config.CaptchaSite }}
-
- {{ end }}
-
-
-
-
-
-
-
- Faucet Response:
-
-
- {{ template "footer" }}
-
- {{ template "js" }}
-
-
-{{- end -}}
diff --git a/gno.land/pkg/gnoweb/views/funcs.html b/gno.land/pkg/gnoweb/views/funcs.html
deleted file mode 100644
index 5a3a086e155..00000000000
--- a/gno.land/pkg/gnoweb/views/funcs.html
+++ /dev/null
@@ -1,337 +0,0 @@
-{{- define "header_buttons" -}}
-
-{{- end -}} {{- define "html_head" -}}
-
-{{if .Data.Description}} {{end}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-{{- end -}} {{- define "logo" -}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-{{- end -}} {{- define "footer" -}}
-
-{{- end -}} {{- define "js" -}}
-
-
-
-
-
-
-{{ template "analytics" .}} {{- end -}} {{- define "analytics" -}} {{- if
-.Data.Config.WithAnalytics -}}
-
-
-
-{{- end -}} {{- end -}} {{- define "subscribe" -}}
-
-
-
-{{- end -}}
diff --git a/gno.land/pkg/gnoweb/views/generic.html b/gno.land/pkg/gnoweb/views/generic.html
deleted file mode 100644
index a917ad34c03..00000000000
--- a/gno.land/pkg/gnoweb/views/generic.html
+++ /dev/null
@@ -1,24 +0,0 @@
-{{- define "app" -}}
-
-
-
- gno.land - {{ .Data.Title }}
- {{ template "html_head" . }}
-
-
-
-
-
-
- {{- .Data.MainContent -}}
-
-
- {{ template "footer" }}
-
- {{ template "js" .}}
-
-
-{{- end -}}
diff --git a/gno.land/pkg/gnoweb/views/package_dir.html b/gno.land/pkg/gnoweb/views/package_dir.html
deleted file mode 100644
index ed7cd9a8347..00000000000
--- a/gno.land/pkg/gnoweb/views/package_dir.html
+++ /dev/null
@@ -1,37 +0,0 @@
-{{- define "app" -}}
-
-
-
- {{ template "html_head" . }}
- gno.land - {{.Data.DirPath}}
-
-
-
-
-
-
- {{ template "dir_contents" . }}
-
- {{ template "footer" }}
-
- {{ template "js" . }}
-
-
-{{- end -}} {{- define "dir_contents" -}}
-
- {{ $dirPath := .Data.DirPath }}
-
- {{ range .Data.Files }}
-
- {{ . }}
-
- {{ end }}
-
-
-{{- end -}}
diff --git a/gno.land/pkg/gnoweb/views/package_file.html b/gno.land/pkg/gnoweb/views/package_file.html
deleted file mode 100644
index 32d8af9e174..00000000000
--- a/gno.land/pkg/gnoweb/views/package_file.html
+++ /dev/null
@@ -1,28 +0,0 @@
-{{- define "app" -}}
-
-
-
- {{ template "html_head" . }}
- gno.land - {{.Data.DirPath}}/{{.Data.FileName}}
-
-
-
-
-
-
- {{ .Data.FileContents }}
-
-
- {{ template "footer" }}
-
- {{ template "js" .}}
-
-
-{{- end -}}
diff --git a/gno.land/pkg/gnoweb/views/realm_help.html b/gno.land/pkg/gnoweb/views/realm_help.html
deleted file mode 100644
index f33bb50e9e6..00000000000
--- a/gno.land/pkg/gnoweb/views/realm_help.html
+++ /dev/null
@@ -1,100 +0,0 @@
-{{- define "app" -}}
-
-
-
- {{ template "html_head" . }}
- gno.land - {{.Data.DirPath}}
-
-
-
-
-
-
-
-
-
- These are the realm's exposed functions ("public smart
- contracts").
-
- My address:
-
(see
-
`gnokey list` )
-
-
- {{ template "func_specs" . }}
-
-
- {{ template "footer" }}
-
- {{ template "js" . }}
-
-
-
-{{- end -}} {{- define "func_specs" -}}
-
- {{ $funcName := .Data.FuncName }} {{ $found := false }} {{ if eq $funcName
- "" }} {{ range .Data.FunctionSignatures }} {{ template "func_spec" . }} {{
- end }} {{ else }} {{ range .Data.FunctionSignatures }} {{ if eq .FuncName
- $funcName }} {{ $found = true }} {{ template "func_spec" . }} {{ end }} {{
- end }} {{ if not $found }} {{ $funcName }} not found. {{ end }} {{ end }}
-
-{{- end -}} {{- define "func_spec" -}}
-
-
-
- contract
- {{ .FuncName }}(...)
-
-
- params
-
-
- {{ range .Params }}{{ template "func_param" . }}{{ end }}
-
-
-
-
- results
-
-
- {{ range .Results }}{{ template "func_result" . }}{{ end }}
-
-
-
-
- command
-
-
-
-
-
-
-{{- end -}} {{- define "func_param" -}}
-
- {{ .Name }}
-
-
-
- {{ .Type }}
-
-{{- end -}} {{- define "func_result" -}}
-
- {{ .Name }}
- {{ .Type }}
-
-{{ end }}
diff --git a/gno.land/pkg/gnoweb/views/realm_render.html b/gno.land/pkg/gnoweb/views/realm_render.html
deleted file mode 100644
index 978b6afed46..00000000000
--- a/gno.land/pkg/gnoweb/views/realm_render.html
+++ /dev/null
@@ -1,40 +0,0 @@
-{{- define "app" -}}
-
-
-
- {{ template "html_head" . }}
- gno.land - {{.Data.RealmName}}
-
-
-
-
-
-
-
- {{ template "footer" }}
-
- {{ template "js" .}}
-
-
-{{- end -}}
diff --git a/gno.land/pkg/gnoweb/views/redirect.html b/gno.land/pkg/gnoweb/views/redirect.html
deleted file mode 100644
index 6fe43a7138b..00000000000
--- a/gno.land/pkg/gnoweb/views/redirect.html
+++ /dev/null
@@ -1,16 +0,0 @@
-{{- define "app" -}}
-
-
-
-
-
-
-
- Redirecting to {{.Data.To}}
-
-
- {{.Data.To}}
- {{ template "analytics" .}}
-
-
-{{- end -}}
diff --git a/gno.land/pkg/gnoweb/webclient.go b/gno.land/pkg/gnoweb/webclient.go
new file mode 100644
index 00000000000..a1005baa0a5
--- /dev/null
+++ b/gno.land/pkg/gnoweb/webclient.go
@@ -0,0 +1,127 @@
+package gnoweb
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "log/slog"
+ "path/filepath"
+ "strings"
+
+ md "github.com/gnolang/gno/gno.land/pkg/gnoweb/markdown"
+ "github.com/gnolang/gno/gno.land/pkg/sdk/vm" // for error types
+ "github.com/gnolang/gno/tm2/pkg/amino"
+ "github.com/gnolang/gno/tm2/pkg/bft/rpc/client"
+ "github.com/yuin/goldmark"
+ "github.com/yuin/goldmark/parser"
+ "github.com/yuin/goldmark/text"
+)
+
+type WebClient struct {
+ logger *slog.Logger
+ client *client.RPCClient
+ md goldmark.Markdown
+}
+
+func NewWebClient(log *slog.Logger, cl *client.RPCClient, m goldmark.Markdown) *WebClient {
+ m.Parser().AddOptions(parser.WithAutoHeadingID())
+ return &WebClient{
+ logger: log,
+ client: cl,
+ md: m,
+ }
+}
+
+func (s *WebClient) Functions(pkgPath string) ([]vm.FunctionSignature, error) {
+ const qpath = "vm/qfuncs"
+
+ args := fmt.Sprintf("gno.land/%s", strings.Trim(pkgPath, "/"))
+ res, err := s.query(qpath, []byte(args))
+ if err != nil {
+ return nil, fmt.Errorf("unable query funcs list: %w", err)
+ }
+
+ var fsigs vm.FunctionSignatures
+ if err := amino.UnmarshalJSON(res, &fsigs); err != nil {
+ s.logger.Warn("unable to unmarshal fsigs, client is probably outdated ?")
+ return nil, fmt.Errorf("unable to unamarshal fsigs: %w", err)
+ }
+
+ return fsigs, nil
+}
+
+func (s *WebClient) SourceFile(path, fileName string) ([]byte, error) {
+ const qpath = "vm/qfile"
+
+ fileName = strings.TrimSpace(fileName) // sanitize filename
+ if fileName == "" {
+ return nil, errors.New("empty filename given") // XXX -> ErrXXX
+ }
+
+ // XXX: move this into gnoclient ?
+ path = fmt.Sprintf("gno.land/%s", strings.Trim(path, "/"))
+ path = filepath.Join(path, fileName)
+ return s.query(qpath, []byte(path))
+}
+
+func (s *WebClient) Sources(path string) ([]string, error) {
+ const qpath = "vm/qfile"
+
+ // XXX: move this into gnoclient
+ path = fmt.Sprintf("gno.land/%s", strings.Trim(path, "/"))
+ res, err := s.query(qpath, []byte(path))
+ if err != nil {
+ return nil, err
+ }
+
+ files := strings.Split(string(res), "\n")
+ return files, nil
+}
+
+type Metadata struct {
+ *md.Toc
+}
+
+func (s *WebClient) Render(w io.Writer, pkgPath string, args string) (*Metadata, error) {
+ const qpath = "vm/qrender"
+
+ data := []byte(gnoPath(pkgPath, args))
+ rawres, err := s.query(qpath, data)
+ if err != nil {
+ return nil, err
+ }
+
+ doc := s.md.Parser().Parse(text.NewReader(rawres))
+ if err := s.md.Renderer().Render(w, rawres, doc); err != nil {
+ return nil, fmt.Errorf("unable render real %q: %w", data, err)
+ }
+
+ var meta Metadata
+ meta.Toc, err = md.TocInspect(doc, rawres, md.TocOptions{MaxDepth: 6, MinDepth: 2})
+ if err != nil {
+ s.logger.Warn("unable to inspect for toc elements", "err", err)
+ }
+
+ return &meta, nil
+}
+
+func (s *WebClient) query(qpath string, data []byte) ([]byte, error) {
+ s.logger.Info("query", "qpath", qpath, "data", string(data))
+
+ qres, err := s.client.ABCIQuery(qpath, data)
+ if err != nil {
+ s.logger.Error("request error", "path", qpath, "data", string(data), "error", err)
+ return nil, fmt.Errorf("unable to query path %q: %w", qpath, err)
+ }
+ if qres.Response.Error != nil {
+ s.logger.Error("response error", "path", qpath, "log", qres.Response.Log)
+ return nil, qres.Response.Error
+ }
+
+ return qres.Response.Data, nil
+}
+
+func gnoPath(pkgPath, args string) string {
+ pkgPath = strings.Trim(pkgPath, "/")
+ return fmt.Sprintf("gno.land/%s:%s", pkgPath, args)
+}
diff --git a/gno.land/pkg/integration/testdata/loadpkg_example.txtar b/gno.land/pkg/integration/testdata/loadpkg_example.txtar
index 9dccd72c8a6..c05bedfef65 100644
--- a/gno.land/pkg/integration/testdata/loadpkg_example.txtar
+++ b/gno.land/pkg/integration/testdata/loadpkg_example.txtar
@@ -4,11 +4,11 @@ loadpkg gno.land/p/demo/ufmt
## start a new node
gnoland start
-gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/importtest -gas-fee 1000000ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1
+gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/importtest -gas-fee 1000000ugnot -gas-wanted 9000000 -broadcast -chainid=tendermint_test test1
stdout OK!
## execute Render
-gnokey maketx call -pkgpath gno.land/r/importtest -func Render -gas-fee 1000000ugnot -gas-wanted 4000000 -args '' -broadcast -chainid=tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/importtest -func Render -gas-fee 1000000ugnot -gas-wanted 9000000 -args '' -broadcast -chainid=tendermint_test test1
stdout '("92054" string)'
stdout OK!
diff --git a/gno.land/pkg/integration/testdata/restart.txtar b/gno.land/pkg/integration/testdata/restart.txtar
index 8d50dd15814..8a63713a214 100644
--- a/gno.land/pkg/integration/testdata/restart.txtar
+++ b/gno.land/pkg/integration/testdata/restart.txtar
@@ -4,12 +4,12 @@
loadpkg gno.land/r/demo/counter $WORK
gnoland start
-gnokey maketx call -pkgpath gno.land/r/demo/counter -func Incr -gas-fee 1000000ugnot -gas-wanted 100000 -broadcast -chainid tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/demo/counter -func Incr -gas-fee 1000000ugnot -gas-wanted 150000 -broadcast -chainid tendermint_test test1
stdout '\(1 int\)'
gnoland restart
-gnokey maketx call -pkgpath gno.land/r/demo/counter -func Incr -gas-fee 1000000ugnot -gas-wanted 100000 -broadcast -chainid tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/demo/counter -func Incr -gas-fee 1000000ugnot -gas-wanted 150000 -broadcast -chainid tendermint_test test1
stdout '\(2 int\)'
-- counter.gno --
@@ -21,4 +21,3 @@ func Incr() int {
counter++
return counter
}
-
diff --git a/gno.land/pkg/integration/testing_integration.go b/gno.land/pkg/integration/testing_integration.go
index 235b9581ae0..ce1413134e3 100644
--- a/gno.land/pkg/integration/testing_integration.go
+++ b/gno.land/pkg/integration/testing_integration.go
@@ -19,7 +19,9 @@ import (
"github.com/gnolang/gno/gno.land/pkg/log"
"github.com/gnolang/gno/gno.land/pkg/sdk/vm"
"github.com/gnolang/gno/gnovm/pkg/gnoenv"
+ "github.com/gnolang/gno/gnovm/pkg/gnolang"
"github.com/gnolang/gno/gnovm/pkg/gnomod"
+ "github.com/gnolang/gno/gnovm/pkg/packages"
"github.com/gnolang/gno/tm2/pkg/bft/node"
bft "github.com/gnolang/gno/tm2/pkg/bft/types"
"github.com/gnolang/gno/tm2/pkg/commands"
@@ -132,11 +134,12 @@ func setupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params {
// Track new user balances added via the `adduser`
// command and packages added with the `loadpkg` command.
// This genesis will be use when node is started.
- genesis := &gnoland.GnoGenesisState{
- Balances: LoadDefaultGenesisBalanceFile(t, gnoRootDir),
- Params: LoadDefaultGenesisParamFile(t, gnoRootDir),
- Txs: []gnoland.TxWithMetadata{},
- }
+
+ genesis := gnoland.DefaultGenState()
+ genesis.Balances = LoadDefaultGenesisBalanceFile(t, gnoRootDir)
+ genesis.Params = LoadDefaultGenesisParamFile(t, gnoRootDir)
+ genesis.Auth.Params.InitialGasPrice = std.GasPrice{Gas: 0, Price: std.Coin{Amount: 0, Denom: "ugnot"}}
+ genesis.Txs = []gnoland.TxWithMetadata{}
// test1 must be created outside of the loop below because it is already included in genesis so
// attempting to recreate results in it getting overwritten and breaking existing tests that
@@ -145,7 +148,7 @@ func setupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params {
env.Setenv("USER_SEED_"+DefaultAccount_Name, DefaultAccount_Seed)
env.Setenv("USER_ADDR_"+DefaultAccount_Name, DefaultAccount_Address)
- env.Values[envKeyGenesis] = genesis
+ env.Values[envKeyGenesis] = &genesis
env.Values[envKeyPkgsLoader] = newPkgsLoader()
env.Setenv("GNOROOT", gnoRootDir)
@@ -185,8 +188,10 @@ func setupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params {
pkgs := ts.Value(envKeyPkgsLoader).(*pkgsLoader) // grab logger
creator := crypto.MustAddressFromString(DefaultAccount_Address) // test1
defaultFee := std.NewFee(50000, std.MustParseCoin(ugnot.ValueString(1000000)))
- pkgsTxs, err := pkgs.LoadPackages(creator, defaultFee, nil)
- if err != nil {
+ // we need to define a new err1 otherwise the out err would be shadowed in the case "start":
+ pkgsTxs, loadErr := pkgs.LoadPackages(creator, defaultFee, nil)
+
+ if loadErr != nil {
ts.Fatalf("unable to load packages txs: %s", err)
}
@@ -743,8 +748,20 @@ func (pl *pkgsLoader) LoadPackage(modroot string, path, name string) error {
// Override package info with mod infos
currentPkg.Name = gm.Module.Mod.Path
currentPkg.Draft = gm.Draft
- for _, req := range gm.Require {
- currentPkg.Requires = append(currentPkg.Requires, req.Mod.Path)
+
+ pkg, err := gnolang.ReadMemPackage(currentPkg.Dir, currentPkg.Name)
+ if err != nil {
+ return fmt.Errorf("unable to read package at %q: %w", currentPkg.Dir, err)
+ }
+ imports, err := packages.Imports(pkg)
+ if err != nil {
+ return fmt.Errorf("unable to load package imports in %q: %w", currentPkg.Dir, err)
+ }
+ for _, imp := range imports {
+ if imp == currentPkg.Name || gnolang.IsStdlib(imp) {
+ continue
+ }
+ currentPkg.Imports = append(currentPkg.Imports, imp)
}
}
@@ -758,7 +775,7 @@ func (pl *pkgsLoader) LoadPackage(modroot string, path, name string) error {
pl.add(currentPkg)
// Add requirements to the queue
- for _, pkgPath := range currentPkg.Requires {
+ for _, pkgPath := range currentPkg.Imports {
fullPath := filepath.Join(modroot, pkgPath)
queue = append(queue, gnomod.Pkg{Dir: fullPath})
}
diff --git a/gno.land/pkg/integration/testing_node.go b/gno.land/pkg/integration/testing_node.go
index 7e34049d352..7eaf3457b03 100644
--- a/gno.land/pkg/integration/testing_node.go
+++ b/gno.land/pkg/integration/testing_node.go
@@ -65,11 +65,11 @@ func TestingNodeConfig(t TestingTS, gnoroot string, additionalTxs ...gnoland.TxW
txs = append(txs, LoadDefaultPackages(t, creator, gnoroot)...)
txs = append(txs, additionalTxs...)
- cfg.Genesis.AppState = gnoland.GnoGenesisState{
- Balances: balances,
- Txs: txs,
- Params: params,
- }
+ ggs := cfg.Genesis.AppState.(gnoland.GnoGenesisState)
+ ggs.Balances = balances
+ ggs.Txs = txs
+ ggs.Params = params
+ cfg.Genesis.AppState = ggs
return cfg, creator
}
@@ -97,6 +97,15 @@ func TestingMinimalNodeConfig(t TestingTS, gnoroot string) *gnoland.InMemoryNode
}
func DefaultTestingGenesisConfig(t TestingTS, gnoroot string, self crypto.PubKey, tmconfig *tmcfg.Config) *bft.GenesisDoc {
+ genState := gnoland.DefaultGenState()
+ genState.Balances = []gnoland.Balance{
+ {
+ Address: crypto.MustAddressFromString(DefaultAccount_Address),
+ Amount: std.MustParseCoins(ugnot.ValueString(10000000000000)),
+ },
+ }
+ genState.Txs = []gnoland.TxWithMetadata{}
+ genState.Params = []gnoland.Param{}
return &bft.GenesisDoc{
GenesisTime: time.Now(),
ChainID: tmconfig.ChainID(),
@@ -116,16 +125,7 @@ func DefaultTestingGenesisConfig(t TestingTS, gnoroot string, self crypto.PubKey
Name: "self",
},
},
- AppState: gnoland.GnoGenesisState{
- Balances: []gnoland.Balance{
- {
- Address: crypto.MustAddressFromString(DefaultAccount_Address),
- Amount: std.MustParseCoins(ugnot.ValueString(10_000_000_000_000)),
- },
- },
- Txs: []gnoland.TxWithMetadata{},
- Params: []gnoland.Param{},
- },
+ AppState: genState,
}
}
diff --git a/gno.land/pkg/keyscli/addpkg.go b/gno.land/pkg/keyscli/addpkg.go
index 37463d13b5c..5308d9d2ac4 100644
--- a/gno.land/pkg/keyscli/addpkg.go
+++ b/gno.land/pkg/keyscli/addpkg.go
@@ -71,6 +71,12 @@ func execMakeAddPkg(cfg *MakeAddPkgCfg, args []string, io commands.IO) error {
if cfg.PkgDir == "" {
return errors.New("pkgdir not specified")
}
+ if cfg.RootCfg.GasWanted == 0 {
+ return errors.New("gas-wanted not specified")
+ }
+ if cfg.RootCfg.GasFee == "" {
+ return errors.New("gas-fee not specified")
+ }
if len(args) != 1 {
return flag.ErrHelp
@@ -96,7 +102,7 @@ func execMakeAddPkg(cfg *MakeAddPkgCfg, args []string, io commands.IO) error {
}
// open files in directory as MemPackage.
- memPkg := gno.ReadMemPackage(cfg.PkgDir, cfg.PkgPath)
+ memPkg := gno.MustReadMemPackage(cfg.PkgDir, cfg.PkgPath)
if memPkg.IsEmpty() {
panic(fmt.Sprintf("found an empty package %q", cfg.PkgPath))
}
diff --git a/gno.land/pkg/keyscli/run.go b/gno.land/pkg/keyscli/run.go
index b0e05fe5a84..00b2be585c6 100644
--- a/gno.land/pkg/keyscli/run.go
+++ b/gno.land/pkg/keyscli/run.go
@@ -92,7 +92,7 @@ func execMakeRun(cfg *MakeRunCfg, args []string, cmdio commands.IO) error {
return fmt.Errorf("could not read source path: %q, %w", sourcePath, err)
}
if info.IsDir() {
- memPkg = gno.ReadMemPackage(sourcePath, "")
+ memPkg = gno.MustReadMemPackage(sourcePath, "")
} else { // is file
b, err := os.ReadFile(sourcePath)
if err != nil {
diff --git a/gno.land/pkg/sdk/vm/common_test.go b/gno.land/pkg/sdk/vm/common_test.go
index 8b1b7d909c1..10402f31f64 100644
--- a/gno.land/pkg/sdk/vm/common_test.go
+++ b/gno.land/pkg/sdk/vm/common_test.go
@@ -47,9 +47,10 @@ func _setupTestEnv(cacheStdlibs bool) testEnv {
ms.LoadLatestVersion()
ctx := sdk.NewContext(sdk.RunTxModeDeliver, ms, &bft.Header{ChainID: "test-chain-id"}, log.NewNoopLogger())
- acck := authm.NewAccountKeeper(iavlCapKey, std.ProtoBaseAccount)
- bank := bankm.NewBankKeeper(acck)
prmk := paramsm.NewParamsKeeper(iavlCapKey, "params")
+ acck := authm.NewAccountKeeper(iavlCapKey, prmk, std.ProtoBaseAccount)
+ bank := bankm.NewBankKeeper(acck)
+
vmk := NewVMKeeper(baseCapKey, iavlCapKey, acck, bank, prmk)
mcw := ms.MultiCacheWrap()
diff --git a/gno.land/pkg/sdk/vm/gas_test.go b/gno.land/pkg/sdk/vm/gas_test.go
index 3a11d97c235..0001e3acf7c 100644
--- a/gno.land/pkg/sdk/vm/gas_test.go
+++ b/gno.land/pkg/sdk/vm/gas_test.go
@@ -21,8 +21,8 @@ import (
// Insufficient gas for a successful message.
func TestAddPkgDeliverTxInsuffGas(t *testing.T) {
- success := true
- ctx, tx, vmHandler := setupAddPkg(success)
+ isValidTx := true
+ ctx, tx, vmHandler := setupAddPkg(isValidTx)
ctx = ctx.WithMode(sdk.RunTxModeDeliver)
simulate := false
@@ -47,7 +47,7 @@ func TestAddPkgDeliverTxInsuffGas(t *testing.T) {
assert.True(t, abort)
assert.False(t, res.IsOK())
gasCheck := gctx.GasMeter().GasConsumed()
- assert.Equal(t, int64(3231), gasCheck)
+ assert.Equal(t, int64(3462), gasCheck)
} else {
t.Errorf("should panic")
}
@@ -58,8 +58,8 @@ func TestAddPkgDeliverTxInsuffGas(t *testing.T) {
// Enough gas for a successful message.
func TestAddPkgDeliverTx(t *testing.T) {
- success := true
- ctx, tx, vmHandler := setupAddPkg(success)
+ isValidTx := true
+ ctx, tx, vmHandler := setupAddPkg(isValidTx)
var simulate bool
@@ -75,13 +75,13 @@ func TestAddPkgDeliverTx(t *testing.T) {
assert.True(t, res.IsOK())
// NOTE: let's try to keep this bellow 100_000 :)
- assert.Equal(t, int64(92825), gasDeliver)
+ assert.Equal(t, int64(135365), gasDeliver)
}
// Enough gas for a failed transaction.
func TestAddPkgDeliverTxFailed(t *testing.T) {
- success := false
- ctx, tx, vmHandler := setupAddPkg(success)
+ isValidTx := false
+ ctx, tx, vmHandler := setupAddPkg(isValidTx)
var simulate bool
@@ -95,19 +95,19 @@ func TestAddPkgDeliverTxFailed(t *testing.T) {
gasDeliver := gctx.GasMeter().GasConsumed()
assert.False(t, res.IsOK())
- assert.Equal(t, int64(2231), gasDeliver)
+ assert.Equal(t, int64(1231), gasDeliver)
}
// Not enough gas for a failed transaction.
func TestAddPkgDeliverTxFailedNoGas(t *testing.T) {
- success := false
- ctx, tx, vmHandler := setupAddPkg(success)
+ isValidTx := false
+ ctx, tx, vmHandler := setupAddPkg(isValidTx)
var simulate bool
ctx = ctx.WithMode(sdk.RunTxModeDeliver)
simulate = false
- tx.Fee.GasWanted = 2230
+ tx.Fee.GasWanted = 1230
gctx := auth.SetGasMeter(simulate, ctx, tx.Fee.GasWanted)
gctx = vmHandler.vm.MakeGnoTransactionStore(gctx)
@@ -126,7 +126,7 @@ func TestAddPkgDeliverTxFailedNoGas(t *testing.T) {
assert.True(t, abort)
assert.False(t, res.IsOK())
gasCheck := gctx.GasMeter().GasConsumed()
- assert.Equal(t, int64(2231), gasCheck)
+ assert.Equal(t, int64(1231), gasCheck)
} else {
t.Errorf("should panic")
}
diff --git a/gno.land/pkg/sdk/vm/keeper.go b/gno.land/pkg/sdk/vm/keeper.go
index 5fa2075b8f7..f158524b3df 100644
--- a/gno.land/pkg/sdk/vm/keeper.go
+++ b/gno.land/pkg/sdk/vm/keeper.go
@@ -85,6 +85,7 @@ func NewVMKeeper(
bank: bank,
prmk: prmk,
}
+
return vmk
}
@@ -100,7 +101,7 @@ func (vm *VMKeeper) Initialize(
alloc := gno.NewAllocator(maxAllocTx)
vm.gnoStore = gno.NewStore(alloc, baseStore, iavlStore)
- vm.gnoStore.SetNativeStore(stdlibs.NativeStore)
+ vm.gnoStore.SetNativeResolver(stdlibs.NativeResolver)
if vm.gnoStore.NumMemPackages() > 0 {
// for now, all mem packages must be re-run after reboot.
@@ -146,7 +147,7 @@ func (vm *VMKeeper) LoadStdlibCached(ctx sdk.Context, stdlibDir string) {
}
gs := gno.NewStore(nil, cachedStdlib.base, cachedStdlib.iavl)
- gs.SetNativeStore(stdlibs.NativeStore)
+ gs.SetNativeResolver(stdlibs.NativeResolver)
loadStdlib(gs, stdlibDir)
cachedStdlib.gno = gs
})
@@ -185,13 +186,14 @@ func loadStdlibPackage(pkgPath, stdlibDir string, store gno.Store) {
// does not exist.
panic(fmt.Sprintf("failed loading stdlib %q: does not exist", pkgPath))
}
- memPkg := gno.ReadMemPackage(stdlibPath, pkgPath)
+ memPkg := gno.MustReadMemPackage(stdlibPath, pkgPath)
if memPkg.IsEmpty() {
// no gno files are present
panic(fmt.Sprintf("failed loading stdlib %q: not a valid MemPackage", pkgPath))
}
m := gno.NewMachineWithOptions(gno.MachineOptions{
+ // XXX: gno.land, vm.domain, other?
PkgPath: "gno.land/r/stdlibs/" + pkgPath,
// PkgPath: pkgPath, XXX why?
Store: store,
@@ -207,8 +209,9 @@ var gnoStoreContextKey gnoStoreContextKeyType
func (vm *VMKeeper) newGnoTransactionStore(ctx sdk.Context) gno.TransactionStore {
base := ctx.Store(vm.baseKey)
iavl := ctx.Store(vm.iavlKey)
+ gasMeter := ctx.GasMeter()
- return vm.gnoStore.BeginTransaction(base, iavl)
+ return vm.gnoStore.BeginTransaction(base, iavl, gasMeter)
}
func (vm *VMKeeper) MakeGnoTransactionStore(ctx sdk.Context) sdk.Context {
@@ -226,20 +229,22 @@ func (vm *VMKeeper) getGnoTransactionStore(ctx sdk.Context) gno.TransactionStore
}
// Namespace can be either a user or crypto address.
-var reNamespace = regexp.MustCompile(`^gno.land/(?:r|p)/([\.~_a-zA-Z0-9]+)`)
-
-const sysUsersPkgParamPath = "gno.land/r/sys/params.sys.users_pkgpath.string"
+var reNamespace = regexp.MustCompile(`^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/(?:r|p)/([\.~_a-zA-Z0-9]+)`)
// checkNamespacePermission check if the user as given has correct permssion to on the given pkg path
func (vm *VMKeeper) checkNamespacePermission(ctx sdk.Context, creator crypto.Address, pkgPath string) error {
- var sysUsersPkg string
- vm.prmk.GetString(ctx, sysUsersPkgParamPath, &sysUsersPkg)
+ sysUsersPkg := vm.getSysUsersPkgParam(ctx)
if sysUsersPkg == "" {
return nil
}
+ chainDomain := vm.getChainDomainParam(ctx)
store := vm.getGnoTransactionStore(ctx)
+ if !strings.HasPrefix(pkgPath, chainDomain+"/") {
+ return ErrInvalidPkgPath(pkgPath) // no match
+ }
+
match := reNamespace.FindStringSubmatch(pkgPath)
switch len(match) {
case 0:
@@ -248,9 +253,6 @@ func (vm *VMKeeper) checkNamespacePermission(ctx sdk.Context, creator crypto.Add
default:
panic("invalid pattern while matching pkgpath")
}
- if len(match) != 2 {
- return ErrInvalidPkgPath(pkgPath)
- }
username := match[1]
// if `sysUsersPkg` does not exist -> skip validation.
@@ -263,6 +265,7 @@ func (vm *VMKeeper) checkNamespacePermission(ctx sdk.Context, creator crypto.Add
pkgAddr := gno.DerivePkgAddr(pkgPath)
msgCtx := stdlibs.ExecContext{
ChainID: ctx.ChainID(),
+ ChainDomain: chainDomain,
Height: ctx.BlockHeight(),
Timestamp: ctx.BlockTime().Unix(),
OrigCaller: creator.Bech32(),
@@ -320,6 +323,7 @@ func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) (err error) {
memPkg := msg.Package
deposit := msg.Deposit
gnostore := vm.getGnoTransactionStore(ctx)
+ chainDomain := vm.getChainDomainParam(ctx)
// Validate arguments.
if creator.IsZero() {
@@ -332,6 +336,9 @@ func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) (err error) {
if err := msg.Package.Validate(); err != nil {
return ErrInvalidPkgPath(err.Error())
}
+ if !strings.HasPrefix(pkgPath, chainDomain+"/") {
+ return ErrInvalidPkgPath("invalid domain: " + pkgPath)
+ }
if pv := gnostore.GetPackage(pkgPath, false); pv != nil {
return ErrPkgAlreadyExists("package already exists: " + pkgPath)
}
@@ -363,9 +370,9 @@ func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) (err error) {
// Parse and run the files, construct *PV.
msgCtx := stdlibs.ExecContext{
ChainID: ctx.ChainID(),
+ ChainDomain: chainDomain,
Height: ctx.BlockHeight(),
Timestamp: ctx.BlockTime().Unix(),
- Msg: msg,
OrigCaller: creator.Bech32(),
OrigSend: deposit,
OrigSendSpent: new(std.Coins),
@@ -391,7 +398,7 @@ func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) (err error) {
case store.OutOfGasException: // panic in consumeGas()
panic(r)
default:
- err = errors.Wrap(fmt.Errorf("%v", r), "VM addpkg panic: %v\n%s\n",
+ err = errors.Wrapf(fmt.Errorf("%v", r), "VM addpkg panic: %v\n%s\n",
r, m2.String())
return
}
@@ -462,11 +469,12 @@ func (vm *VMKeeper) Call(ctx sdk.Context, msg MsgCall) (res string, err error) {
// Make context.
// NOTE: if this is too expensive,
// could it be safely partially memoized?
+ chainDomain := vm.getChainDomainParam(ctx)
msgCtx := stdlibs.ExecContext{
ChainID: ctx.ChainID(),
+ ChainDomain: chainDomain,
Height: ctx.BlockHeight(),
Timestamp: ctx.BlockTime().Unix(),
- Msg: msg,
OrigCaller: caller.Bech32(),
OrigSend: send,
OrigSendSpent: new(std.Coins),
@@ -493,10 +501,10 @@ func (vm *VMKeeper) Call(ctx sdk.Context, msg MsgCall) (res string, err error) {
case store.OutOfGasException: // panic in consumeGas()
panic(r)
case gno.UnhandledPanicError:
- err = errors.Wrap(fmt.Errorf("%v", r.Error()), "VM call panic: %s\nStacktrace: %s\n",
+ err = errors.Wrapf(fmt.Errorf("%v", r.Error()), "VM call panic: %s\nStacktrace: %s\n",
r.Error(), m.ExceptionsStacktrace())
default:
- err = errors.Wrap(fmt.Errorf("%v", r), "VM call panic: %v\nMachine State:%s\nStacktrace: %s\n",
+ err = errors.Wrapf(fmt.Errorf("%v", r), "VM call panic: %v\nMachine State:%s\nStacktrace: %s\n",
r, m.String(), m.Stacktrace().String())
return
}
@@ -533,11 +541,12 @@ func (vm *VMKeeper) Run(ctx sdk.Context, msg MsgRun) (res string, err error) {
gnostore := vm.getGnoTransactionStore(ctx)
send := msg.Send
memPkg := msg.Package
+ chainDomain := vm.getChainDomainParam(ctx)
// coerce path to right one.
// the path in the message must be "" or the following path.
// this is already checked in MsgRun.ValidateBasic
- memPkg.Path = "gno.land/r/" + msg.Caller.String() + "/run"
+ memPkg.Path = chainDomain + "/r/" + msg.Caller.String() + "/run"
// Validate arguments.
callerAcc := vm.acck.GetAccount(ctx, caller)
@@ -563,9 +572,9 @@ func (vm *VMKeeper) Run(ctx sdk.Context, msg MsgRun) (res string, err error) {
// Parse and run the files, construct *PV.
msgCtx := stdlibs.ExecContext{
ChainID: ctx.ChainID(),
+ ChainDomain: chainDomain,
Height: ctx.BlockHeight(),
Timestamp: ctx.BlockTime().Unix(),
- Msg: msg,
OrigCaller: caller.Bech32(),
OrigSend: send,
OrigSendSpent: new(std.Coins),
@@ -597,7 +606,7 @@ func (vm *VMKeeper) Run(ctx sdk.Context, msg MsgRun) (res string, err error) {
case store.OutOfGasException: // panic in consumeGas()
panic(r)
default:
- err = errors.Wrap(fmt.Errorf("%v", r), "VM run main addpkg panic: %v\n%s\n",
+ err = errors.Wrapf(fmt.Errorf("%v", r), "VM run main addpkg panic: %v\n%s\n",
r, m.String())
return
}
@@ -623,7 +632,7 @@ func (vm *VMKeeper) Run(ctx sdk.Context, msg MsgRun) (res string, err error) {
case store.OutOfGasException: // panic in consumeGas()
panic(r)
default:
- err = errors.Wrap(fmt.Errorf("%v", r), "VM run main call panic: %v\n%s\n",
+ err = errors.Wrapf(fmt.Errorf("%v", r), "VM run main call panic: %v\n%s\n",
r, m2.String())
return
}
@@ -725,11 +734,12 @@ func (vm *VMKeeper) QueryEval(ctx sdk.Context, pkgPath string, expr string) (res
return "", err
}
// Construct new machine.
+ chainDomain := vm.getChainDomainParam(ctx)
msgCtx := stdlibs.ExecContext{
- ChainID: ctx.ChainID(),
- Height: ctx.BlockHeight(),
- Timestamp: ctx.BlockTime().Unix(),
- // Msg: msg,
+ ChainID: ctx.ChainID(),
+ ChainDomain: chainDomain,
+ Height: ctx.BlockHeight(),
+ Timestamp: ctx.BlockTime().Unix(),
// OrigCaller: caller,
// OrigSend: send,
// OrigSendSpent: nil,
@@ -754,7 +764,7 @@ func (vm *VMKeeper) QueryEval(ctx sdk.Context, pkgPath string, expr string) (res
case store.OutOfGasException: // panic in consumeGas()
panic(r)
default:
- err = errors.Wrap(fmt.Errorf("%v", r), "VM query eval panic: %v\n%s\n",
+ err = errors.Wrapf(fmt.Errorf("%v", r), "VM query eval panic: %v\n%s\n",
r, m.String())
return
}
@@ -792,11 +802,12 @@ func (vm *VMKeeper) QueryEvalString(ctx sdk.Context, pkgPath string, expr string
return "", err
}
// Construct new machine.
+ chainDomain := vm.getChainDomainParam(ctx)
msgCtx := stdlibs.ExecContext{
- ChainID: ctx.ChainID(),
- Height: ctx.BlockHeight(),
- Timestamp: ctx.BlockTime().Unix(),
- // Msg: msg,
+ ChainID: ctx.ChainID(),
+ ChainDomain: chainDomain,
+ Height: ctx.BlockHeight(),
+ Timestamp: ctx.BlockTime().Unix(),
// OrigCaller: caller,
// OrigSend: jsend,
// OrigSendSpent: nil,
@@ -821,7 +832,7 @@ func (vm *VMKeeper) QueryEvalString(ctx sdk.Context, pkgPath string, expr string
case store.OutOfGasException: // panic in consumeGas()
panic(r)
default:
- err = errors.Wrap(fmt.Errorf("%v", r), "VM query eval string panic: %v\n%s\n",
+ err = errors.Wrapf(fmt.Errorf("%v", r), "VM query eval string panic: %v\n%s\n",
r, m.String())
return
}
diff --git a/gno.land/pkg/sdk/vm/keeper_test.go b/gno.land/pkg/sdk/vm/keeper_test.go
index 9afbb3de551..f8144988c44 100644
--- a/gno.land/pkg/sdk/vm/keeper_test.go
+++ b/gno.land/pkg/sdk/vm/keeper_test.go
@@ -22,7 +22,7 @@ import (
"github.com/gnolang/gno/tm2/pkg/store/types"
)
-var coinsString = ugnot.ValueString(10000000)
+var coinsString = ugnot.ValueString(10_000_000)
func TestVMKeeperAddPackage(t *testing.T) {
env := setupTestEnv()
@@ -68,6 +68,43 @@ func Echo() string { return "hello world" }
assert.Equal(t, expected, memFile.Body)
}
+func TestVMKeeperAddPackage_InvalidDomain(t *testing.T) {
+ env := setupTestEnv()
+ ctx := env.vmk.MakeGnoTransactionStore(env.ctx)
+
+ // Give "addr1" some gnots.
+ addr := crypto.AddressFromPreimage([]byte("addr1"))
+ acc := env.acck.NewAccountWithAddress(ctx, addr)
+ env.acck.SetAccount(ctx, acc)
+ env.bank.SetCoins(ctx, addr, std.MustParseCoins(coinsString))
+ assert.True(t, env.bank.GetCoins(ctx, addr).IsEqual(std.MustParseCoins(coinsString)))
+
+ // Create test package.
+ files := []*gnovm.MemFile{
+ {
+ Name: "test.gno",
+ Body: `package test
+func Echo() string {return "hello world"}`,
+ },
+ }
+ pkgPath := "anotherdomain.land/r/test"
+ msg1 := NewMsgAddPackage(addr, pkgPath, files)
+ assert.Nil(t, env.vmk.getGnoTransactionStore(ctx).GetPackage(pkgPath, false))
+
+ err := env.vmk.AddPackage(ctx, msg1)
+
+ assert.Error(t, err, ErrInvalidPkgPath("invalid domain: anotherdomain.land/r/test"))
+ assert.Nil(t, env.vmk.getGnoTransactionStore(ctx).GetPackage(pkgPath, false))
+
+ err = env.vmk.AddPackage(ctx, msg1)
+ assert.Error(t, err, ErrInvalidPkgPath("invalid domain: anotherdomain.land/r/test"))
+
+ // added package is formatted
+ store := env.vmk.getGnoTransactionStore(ctx)
+ memFile := store.GetMemFile("gno.land/r/test", "test.gno")
+ assert.Nil(t, memFile)
+}
+
// Sending total send amount succeeds.
func TestVMKeeperOrigSend1(t *testing.T) {
env := setupTestEnv()
diff --git a/gno.land/pkg/sdk/vm/msgs.go b/gno.land/pkg/sdk/vm/msgs.go
index d5b82067a98..38f35ab7110 100644
--- a/gno.land/pkg/sdk/vm/msgs.go
+++ b/gno.land/pkg/sdk/vm/msgs.go
@@ -29,7 +29,7 @@ func NewMsgAddPackage(creator crypto.Address, pkgPath string, files []*gnovm.Mem
var pkgName string
for _, file := range files {
if strings.HasSuffix(file.Name, ".gno") {
- pkgName = string(gno.PackageNameFromFileBody(file.Name, file.Body))
+ pkgName = string(gno.MustPackageNameFromFileBody(file.Name, file.Body))
break
}
}
@@ -156,7 +156,7 @@ var _ std.Msg = MsgRun{}
func NewMsgRun(caller crypto.Address, send std.Coins, files []*gnovm.MemFile) MsgRun {
for _, file := range files {
if strings.HasSuffix(file.Name, ".gno") {
- pkgName := string(gno.PackageNameFromFileBody(file.Name, file.Body))
+ pkgName := string(gno.MustPackageNameFromFileBody(file.Name, file.Body))
if pkgName != "main" {
panic("package name should be 'main'")
}
@@ -186,8 +186,8 @@ func (msg MsgRun) ValidateBasic() error {
}
// Force memPkg path to the reserved run path.
- wantPath := "gno.land/r/" + msg.Caller.String() + "/run"
- if path := msg.Package.Path; path != "" && path != wantPath {
+ wantSuffix := "/r/" + msg.Caller.String() + "/run"
+ if path := msg.Package.Path; path != "" && !strings.HasSuffix(path, wantSuffix) {
return ErrInvalidPkgPath(fmt.Sprintf("invalid pkgpath for MsgRun: %q", path))
}
diff --git a/gno.land/pkg/sdk/vm/params.go b/gno.land/pkg/sdk/vm/params.go
new file mode 100644
index 00000000000..248fb8a81fb
--- /dev/null
+++ b/gno.land/pkg/sdk/vm/params.go
@@ -0,0 +1,20 @@
+package vm
+
+import "github.com/gnolang/gno/tm2/pkg/sdk"
+
+const (
+ sysUsersPkgParamPath = "gno.land/r/sys/params.sys.users_pkgpath.string"
+ chainDomainParamPath = "gno.land/r/sys/params.chain_domain.string"
+)
+
+func (vm *VMKeeper) getChainDomainParam(ctx sdk.Context) string {
+ chainDomain := "gno.land" // default
+ vm.prmk.GetString(ctx, chainDomainParamPath, &chainDomain)
+ return chainDomain
+}
+
+func (vm *VMKeeper) getSysUsersPkgParam(ctx sdk.Context) string {
+ var sysUsersPkg string
+ vm.prmk.GetString(ctx, sysUsersPkgParamPath, &sysUsersPkg)
+ return sysUsersPkg
+}
diff --git a/gnovm/Makefile b/gnovm/Makefile
index d27395d9cd1..3cf2f74276b 100644
--- a/gnovm/Makefile
+++ b/gnovm/Makefile
@@ -64,7 +64,7 @@ imports:
########################################
# Test suite
.PHONY: test
-test: _test.cmd _test.pkg _test.gnolang
+test: _test.cmd _test.pkg _test.stdlibs
.PHONY: _test.cmd
_test.cmd:
@@ -92,20 +92,13 @@ test.cmd.coverage_view: test.cmd.coverage
_test.pkg:
go test ./pkg/... $(GOTEST_FLAGS)
-.PHONY: _test.gnolang
-_test.gnolang: _test.gnolang.native _test.gnolang.stdlibs _test.gnolang.realm _test.gnolang.pkg0 _test.gnolang.pkg1 _test.gnolang.pkg2 _test.gnolang.other
-_test.gnolang.other:; go test tests/*.go -run "(TestFileStr|TestSelectors)" $(GOTEST_FLAGS)
-_test.gnolang.realm:; go test tests/*.go -run "TestFiles/^zrealm" $(GOTEST_FLAGS)
-_test.gnolang.pkg0:; go test tests/*.go -run "TestStdlibs/(bufio|crypto|encoding|errors|internal|io|math|sort|std|strconv|strings|testing|unicode)" $(GOTEST_FLAGS)
-_test.gnolang.pkg1:; go test tests/*.go -run "TestStdlibs/regexp" $(GOTEST_FLAGS)
-_test.gnolang.pkg2:; go test tests/*.go -run "TestStdlibs/bytes" $(GOTEST_FLAGS)
-_test.gnolang.native:; go test tests/*.go -test.short -run "TestFilesNative/" $(GOTEST_FLAGS)
-_test.gnolang.stdlibs:; go test tests/*.go -test.short -run 'TestFiles$$/' $(GOTEST_FLAGS)
-_test.gnolang.native.sync:; go test tests/*.go -test.short -run "TestFilesNative/" --update-golden-tests $(GOTEST_FLAGS)
-_test.gnolang.stdlibs.sync:; go test tests/*.go -test.short -run 'TestFiles$$/' --update-golden-tests $(GOTEST_FLAGS)
-# NOTE: challenges are current GnoVM bugs which are supposed to fail.
-# If any of these tests pass, it should be moved to a normal test.
-_test.gnolang.challenges:; go test tests/*.go -test.short -run 'TestChallenges$$/' $(GOTEST_FLAGS)
+.PHONY: _test.stdlibs
+_test.stdlibs:
+ go run ./cmd/gno test -v ./stdlibs/...
+
+
+_test.filetest:;
+ go test pkg/gnolang/files_test.go -test.short -run 'TestFiles$$/' $(GOTEST_FLAGS)
########################################
# Code gen
diff --git a/gnovm/cmd/gno/download_deps.go b/gnovm/cmd/gno/download_deps.go
new file mode 100644
index 00000000000..d19de9dd338
--- /dev/null
+++ b/gnovm/cmd/gno/download_deps.go
@@ -0,0 +1,86 @@
+package main
+
+import (
+ "errors"
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/gnolang/gno/gnovm/cmd/gno/internal/pkgdownload"
+ "github.com/gnolang/gno/gnovm/pkg/gnolang"
+ "github.com/gnolang/gno/gnovm/pkg/gnomod"
+ "github.com/gnolang/gno/gnovm/pkg/packages"
+ "github.com/gnolang/gno/tm2/pkg/commands"
+ "golang.org/x/mod/module"
+)
+
+// downloadDeps recursively fetches the imports of a local package while following a given gno.mod replace directives
+func downloadDeps(io commands.IO, pkgDir string, gnoMod *gnomod.File, fetcher pkgdownload.PackageFetcher) error {
+ if fetcher == nil {
+ return errors.New("fetcher is nil")
+ }
+
+ pkg, err := gnolang.ReadMemPackage(pkgDir, gnoMod.Module.Mod.Path)
+ if err != nil {
+ return fmt.Errorf("read package at %q: %w", pkgDir, err)
+ }
+ imports, err := packages.Imports(pkg)
+ if err != nil {
+ return fmt.Errorf("read imports at %q: %w", pkgDir, err)
+ }
+
+ for _, pkgPath := range imports {
+ resolved := gnoMod.Resolve(module.Version{Path: pkgPath})
+ resolvedPkgPath := resolved.Path
+
+ if !isRemotePkgPath(resolvedPkgPath) {
+ continue
+ }
+
+ depDir := gnomod.PackageDir("", module.Version{Path: resolvedPkgPath})
+
+ if err := downloadPackage(io, resolvedPkgPath, depDir, fetcher); err != nil {
+ return fmt.Errorf("download import %q of %q: %w", resolvedPkgPath, pkgDir, err)
+ }
+
+ if err := downloadDeps(io, depDir, gnoMod, fetcher); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// downloadPackage downloads a remote gno package by pkg path and store it at dst
+func downloadPackage(io commands.IO, pkgPath string, dst string, fetcher pkgdownload.PackageFetcher) error {
+ modFilePath := filepath.Join(dst, "gno.mod")
+
+ if _, err := os.Stat(modFilePath); err == nil {
+ // modfile exists in modcache, do nothing
+ return nil
+ } else if !os.IsNotExist(err) {
+ return fmt.Errorf("stat downloaded module %q at %q: %w", pkgPath, dst, err)
+ }
+
+ io.ErrPrintfln("gno: downloading %s", pkgPath)
+
+ if err := pkgdownload.Download(pkgPath, dst, fetcher); err != nil {
+ return err
+ }
+
+ // We need to write a marker file for each downloaded package.
+ // For example: if you first download gno.land/r/foo/bar then download gno.land/r/foo,
+ // we need to know that gno.land/r/foo is not downloaded yet.
+ // We do this by checking for the presence of gno.land/r/foo/gno.mod
+ if err := os.WriteFile(modFilePath, []byte("module "+pkgPath+"\n"), 0o644); err != nil {
+ return fmt.Errorf("write modfile at %q: %w", modFilePath, err)
+ }
+
+ return nil
+}
+
+// isRemotePkgPath determines whether s is a remote pkg path, i.e.: not a filepath nor a standard library
+func isRemotePkgPath(s string) bool {
+ return !strings.HasPrefix(s, ".") && !filepath.IsAbs(s) && !gnolang.IsStdlib(s)
+}
diff --git a/gnovm/cmd/gno/download_deps_test.go b/gnovm/cmd/gno/download_deps_test.go
new file mode 100644
index 00000000000..0828e9b2245
--- /dev/null
+++ b/gnovm/cmd/gno/download_deps_test.go
@@ -0,0 +1,152 @@
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/gnolang/gno/gnovm/cmd/gno/internal/pkgdownload/examplespkgfetcher"
+ "github.com/gnolang/gno/gnovm/pkg/gnomod"
+ "github.com/gnolang/gno/tm2/pkg/commands"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "golang.org/x/mod/modfile"
+ "golang.org/x/mod/module"
+)
+
+func TestDownloadDeps(t *testing.T) {
+ for _, tc := range []struct {
+ desc string
+ pkgPath string
+ modFile gnomod.File
+ errorShouldContain string
+ requirements []string
+ ioErrContains []string
+ }{
+ {
+ desc: "not_exists",
+ pkgPath: "gno.land/p/demo/does_not_exists",
+ modFile: gnomod.File{
+ Module: &modfile.Module{
+ Mod: module.Version{
+ Path: "testFetchDeps",
+ },
+ },
+ },
+ errorShouldContain: "query files list for pkg \"gno.land/p/demo/does_not_exists\": package \"gno.land/p/demo/does_not_exists\" is not available",
+ }, {
+ desc: "fetch_gno.land/p/demo/avl",
+ pkgPath: "gno.land/p/demo/avl",
+ modFile: gnomod.File{
+ Module: &modfile.Module{
+ Mod: module.Version{
+ Path: "testFetchDeps",
+ },
+ },
+ },
+ requirements: []string{"avl"},
+ ioErrContains: []string{
+ "gno: downloading gno.land/p/demo/avl",
+ },
+ }, {
+ desc: "fetch_gno.land/p/demo/blog6",
+ pkgPath: "gno.land/p/demo/blog",
+ modFile: gnomod.File{
+ Module: &modfile.Module{
+ Mod: module.Version{
+ Path: "testFetchDeps",
+ },
+ },
+ },
+ requirements: []string{"avl", "blog", "diff", "uassert", "ufmt", "mux"},
+ ioErrContains: []string{
+ "gno: downloading gno.land/p/demo/blog",
+ "gno: downloading gno.land/p/demo/avl",
+ "gno: downloading gno.land/p/demo/ufmt",
+ },
+ }, {
+ desc: "fetch_replace_gno.land/p/demo/avl",
+ pkgPath: "gno.land/p/demo/replaced_avl",
+ modFile: gnomod.File{
+ Module: &modfile.Module{
+ Mod: module.Version{
+ Path: "testFetchDeps",
+ },
+ },
+ Replace: []*modfile.Replace{{
+ Old: module.Version{Path: "gno.land/p/demo/replaced_avl"},
+ New: module.Version{Path: "gno.land/p/demo/avl"},
+ }},
+ },
+ requirements: []string{"avl"},
+ ioErrContains: []string{
+ "gno: downloading gno.land/p/demo/avl",
+ },
+ }, {
+ desc: "fetch_replace_local",
+ pkgPath: "gno.land/p/demo/foo",
+ modFile: gnomod.File{
+ Module: &modfile.Module{
+ Mod: module.Version{
+ Path: "testFetchDeps",
+ },
+ },
+ Replace: []*modfile.Replace{{
+ Old: module.Version{Path: "gno.land/p/demo/foo"},
+ New: module.Version{Path: "../local_foo"},
+ }},
+ },
+ },
+ } {
+ t.Run(tc.desc, func(t *testing.T) {
+ mockErr := bytes.NewBufferString("")
+ io := commands.NewTestIO()
+ io.SetErr(commands.WriteNopCloser(mockErr))
+
+ dirPath := t.TempDir()
+
+ err := os.WriteFile(filepath.Join(dirPath, "main.gno"), []byte(fmt.Sprintf("package main\n\n import %q\n", tc.pkgPath)), 0o644)
+ require.NoError(t, err)
+
+ tmpGnoHome := t.TempDir()
+ t.Setenv("GNOHOME", tmpGnoHome)
+
+ fetcher := examplespkgfetcher.New()
+
+ // gno: downloading dependencies
+ err = downloadDeps(io, dirPath, &tc.modFile, fetcher)
+ if tc.errorShouldContain != "" {
+ require.ErrorContains(t, err, tc.errorShouldContain)
+ } else {
+ require.Nil(t, err)
+
+ // Read dir
+ entries, err := os.ReadDir(filepath.Join(tmpGnoHome, "pkg", "mod", "gno.land", "p", "demo"))
+ if !os.IsNotExist(err) {
+ require.Nil(t, err)
+ }
+
+ // Check dir entries
+ assert.Equal(t, len(tc.requirements), len(entries))
+ for _, e := range entries {
+ assert.Contains(t, tc.requirements, e.Name())
+ }
+
+ // Check logs
+ for _, c := range tc.ioErrContains {
+ assert.Contains(t, mockErr.String(), c)
+ }
+
+ mockErr.Reset()
+
+ // Try fetching again. Should be cached
+ downloadDeps(io, dirPath, &tc.modFile, fetcher)
+ for _, c := range tc.ioErrContains {
+ assert.NotContains(t, mockErr.String(), c)
+ }
+ }
+ })
+ }
+}
diff --git a/gnovm/cmd/gno/internal/pkgdownload/examplespkgfetcher/examplespkgfetcher.go b/gnovm/cmd/gno/internal/pkgdownload/examplespkgfetcher/examplespkgfetcher.go
new file mode 100644
index 00000000000..1642c62d21e
--- /dev/null
+++ b/gnovm/cmd/gno/internal/pkgdownload/examplespkgfetcher/examplespkgfetcher.go
@@ -0,0 +1,52 @@
+// Package examplespkgfetcher provides an implementation of [pkgdownload.PackageFetcher]
+// to fetch packages from the examples folder at GNOROOT
+package examplespkgfetcher
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+
+ "github.com/gnolang/gno/gnovm"
+ "github.com/gnolang/gno/gnovm/cmd/gno/internal/pkgdownload"
+ "github.com/gnolang/gno/gnovm/pkg/gnoenv"
+)
+
+type ExamplesPackageFetcher struct{}
+
+var _ pkgdownload.PackageFetcher = (*ExamplesPackageFetcher)(nil)
+
+func New() pkgdownload.PackageFetcher {
+ return &ExamplesPackageFetcher{}
+}
+
+// FetchPackage implements [pkgdownload.PackageFetcher].
+func (e *ExamplesPackageFetcher) FetchPackage(pkgPath string) ([]*gnovm.MemFile, error) {
+ pkgDir := filepath.Join(gnoenv.RootDir(), "examples", filepath.FromSlash(pkgPath))
+
+ entries, err := os.ReadDir(pkgDir)
+ if os.IsNotExist(err) {
+ return nil, fmt.Errorf("query files list for pkg %q: package %q is not available", pkgPath, pkgPath)
+ } else if err != nil {
+ return nil, err
+ }
+
+ res := []*gnovm.MemFile{}
+ for _, entry := range entries {
+ if entry.IsDir() {
+ continue
+ }
+
+ name := entry.Name()
+ filePath := filepath.Join(pkgDir, name)
+
+ body, err := os.ReadFile(filePath)
+ if err != nil {
+ return nil, fmt.Errorf("read file at %q: %w", filePath, err)
+ }
+
+ res = append(res, &gnovm.MemFile{Name: name, Body: string(body)})
+ }
+
+ return res, nil
+}
diff --git a/gnovm/cmd/gno/internal/pkgdownload/pkgdownload.go b/gnovm/cmd/gno/internal/pkgdownload/pkgdownload.go
new file mode 100644
index 00000000000..722cab01555
--- /dev/null
+++ b/gnovm/cmd/gno/internal/pkgdownload/pkgdownload.go
@@ -0,0 +1,30 @@
+// Package pkgdownload provides interfaces and utility functions to download gno packages files.
+package pkgdownload
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+)
+
+// Download downloads the package identified by `pkgPath` in the directory at `dst` using the provided [PackageFetcher].
+// The directory at `dst` is created if it does not exists.
+func Download(pkgPath string, dst string, fetcher PackageFetcher) error {
+ files, err := fetcher.FetchPackage(pkgPath)
+ if err != nil {
+ return err
+ }
+
+ if err := os.MkdirAll(dst, 0o744); err != nil {
+ return err
+ }
+
+ for _, file := range files {
+ fileDst := filepath.Join(dst, file.Name)
+ if err := os.WriteFile(fileDst, []byte(file.Body), 0o644); err != nil {
+ return fmt.Errorf("write file at %q: %w", fileDst, err)
+ }
+ }
+
+ return nil
+}
diff --git a/gnovm/cmd/gno/internal/pkgdownload/pkgfetcher.go b/gnovm/cmd/gno/internal/pkgdownload/pkgfetcher.go
new file mode 100644
index 00000000000..79a7a6a54e2
--- /dev/null
+++ b/gnovm/cmd/gno/internal/pkgdownload/pkgfetcher.go
@@ -0,0 +1,7 @@
+package pkgdownload
+
+import "github.com/gnolang/gno/gnovm"
+
+type PackageFetcher interface {
+ FetchPackage(pkgPath string) ([]*gnovm.MemFile, error)
+}
diff --git a/gnovm/cmd/gno/internal/pkgdownload/rpcpkgfetcher/rpcpkgfetcher.go b/gnovm/cmd/gno/internal/pkgdownload/rpcpkgfetcher/rpcpkgfetcher.go
new file mode 100644
index 00000000000..a71c1d43719
--- /dev/null
+++ b/gnovm/cmd/gno/internal/pkgdownload/rpcpkgfetcher/rpcpkgfetcher.go
@@ -0,0 +1,89 @@
+// Package rpcpkgfetcher provides an implementation of [pkgdownload.PackageFetcher]
+// to fetch packages from gno.land rpc endpoints
+package rpcpkgfetcher
+
+import (
+ "fmt"
+ "path"
+ "strings"
+
+ "github.com/gnolang/gno/gnovm"
+ "github.com/gnolang/gno/gnovm/cmd/gno/internal/pkgdownload"
+ "github.com/gnolang/gno/tm2/pkg/bft/rpc/client"
+)
+
+type gnoPackageFetcher struct {
+ remoteOverrides map[string]string
+}
+
+var _ pkgdownload.PackageFetcher = (*gnoPackageFetcher)(nil)
+
+func New(remoteOverrides map[string]string) pkgdownload.PackageFetcher {
+ return &gnoPackageFetcher{
+ remoteOverrides: remoteOverrides,
+ }
+}
+
+// FetchPackage implements [pkgdownload.PackageFetcher].
+func (gpf *gnoPackageFetcher) FetchPackage(pkgPath string) ([]*gnovm.MemFile, error) {
+ rpcURL, err := rpcURLFromPkgPath(pkgPath, gpf.remoteOverrides)
+ if err != nil {
+ return nil, fmt.Errorf("get rpc url for pkg path %q: %w", pkgPath, err)
+ }
+
+ client, err := client.NewHTTPClient(rpcURL)
+ if err != nil {
+ return nil, fmt.Errorf("failed to instantiate tm2 client with remote %q: %w", rpcURL, err)
+ }
+ defer client.Close()
+
+ data, err := qfile(client, pkgPath)
+ if err != nil {
+ return nil, fmt.Errorf("query files list for pkg %q: %w", pkgPath, err)
+ }
+
+ files := strings.Split(string(data), "\n")
+ res := make([]*gnovm.MemFile, len(files))
+ for i, file := range files {
+ filePath := path.Join(pkgPath, file)
+ data, err := qfile(client, filePath)
+ if err != nil {
+ return nil, fmt.Errorf("query package file %q: %w", filePath, err)
+ }
+
+ res[i] = &gnovm.MemFile{Name: file, Body: string(data)}
+ }
+ return res, nil
+}
+
+func rpcURLFromPkgPath(pkgPath string, remoteOverrides map[string]string) (string, error) {
+ parts := strings.Split(pkgPath, "/")
+ if len(parts) < 2 {
+ return "", fmt.Errorf("bad pkg path %q", pkgPath)
+ }
+ domain := parts[0]
+
+ if override, ok := remoteOverrides[domain]; ok {
+ return override, nil
+ }
+
+ // XXX: retrieve host/port from r/sys/zones.
+ rpcURL := fmt.Sprintf("https://rpc.%s:443", domain)
+
+ return rpcURL, nil
+}
+
+func qfile(c client.Client, pkgPath string) ([]byte, error) {
+ path := "vm/qfile"
+ data := []byte(pkgPath)
+
+ qres, err := c.ABCIQuery(path, data)
+ if err != nil {
+ return nil, fmt.Errorf("query qfile: %w", err)
+ }
+ if qres.Response.Error != nil {
+ return nil, fmt.Errorf("qfile failed: %w\n%s", qres.Response.Error, qres.Response.Log)
+ }
+
+ return qres.Response.Data, nil
+}
diff --git a/gnovm/cmd/gno/internal/pkgdownload/rpcpkgfetcher/rpcpkgfetcher_test.go b/gnovm/cmd/gno/internal/pkgdownload/rpcpkgfetcher/rpcpkgfetcher_test.go
new file mode 100644
index 00000000000..56db5b796de
--- /dev/null
+++ b/gnovm/cmd/gno/internal/pkgdownload/rpcpkgfetcher/rpcpkgfetcher_test.go
@@ -0,0 +1,53 @@
+package rpcpkgfetcher
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestRpcURLFromPkgPath(t *testing.T) {
+ cases := []struct {
+ name string
+ pkgPath string
+ overrides map[string]string
+ result string
+ errorContains string
+ }{
+ {
+ name: "happy path simple",
+ pkgPath: "gno.land/p/demo/avl",
+ result: "https://rpc.gno.land:443",
+ },
+ {
+ name: "happy path override",
+ pkgPath: "gno.land/p/demo/avl",
+ overrides: map[string]string{"gno.land": "https://example.com/rpc:42"},
+ result: "https://example.com/rpc:42",
+ },
+ {
+ name: "happy path override no effect",
+ pkgPath: "gno.land/p/demo/avl",
+ overrides: map[string]string{"some.chain": "https://example.com/rpc:42"},
+ result: "https://rpc.gno.land:443",
+ },
+ {
+ name: "error bad pkg path",
+ pkgPath: "std",
+ result: "",
+ errorContains: `bad pkg path "std"`,
+ },
+ }
+
+ for _, c := range cases {
+ t.Run(c.name, func(t *testing.T) {
+ res, err := rpcURLFromPkgPath(c.pkgPath, c.overrides)
+ if len(c.errorContains) == 0 {
+ require.NoError(t, err)
+ } else {
+ require.ErrorContains(t, err, c.errorContains)
+ }
+ require.Equal(t, c.result, res)
+ })
+ }
+}
diff --git a/gnovm/cmd/gno/lint.go b/gnovm/cmd/gno/lint.go
index c6008117f13..a3e7f5310e1 100644
--- a/gnovm/cmd/gno/lint.go
+++ b/gnovm/cmd/gno/lint.go
@@ -6,17 +6,19 @@ import (
"flag"
"fmt"
"go/scanner"
+ "go/types"
"io"
"os"
"path/filepath"
"regexp"
"strings"
+ "github.com/gnolang/gno/gnovm"
"github.com/gnolang/gno/gnovm/pkg/gnoenv"
gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
- "github.com/gnolang/gno/gnovm/tests"
+ "github.com/gnolang/gno/gnovm/pkg/gnomod"
+ "github.com/gnolang/gno/gnovm/pkg/test"
"github.com/gnolang/gno/tm2/pkg/commands"
- osm "github.com/gnolang/gno/tm2/pkg/os"
"go.uber.org/multierr"
)
@@ -50,6 +52,31 @@ func (c *lintCfg) RegisterFlags(fs *flag.FlagSet) {
fs.StringVar(&c.rootDir, "root-dir", rootdir, "clone location of github.com/gnolang/gno (gno tries to guess it)")
}
+type lintCode int
+
+const (
+ lintUnknown lintCode = iota
+ lintGnoMod
+ lintGnoError
+ lintParserError
+ lintTypeCheckError
+
+ // TODO: add new linter codes here.
+)
+
+type lintIssue struct {
+ Code lintCode
+ Msg string
+ Confidence float64 // 1 is 100%
+ Location string // file:line, or equivalent
+ // TODO: consider writing fix suggestions
+}
+
+func (i lintIssue) String() string {
+ // TODO: consider crafting a doc URL based on Code.
+ return fmt.Sprintf("%s: %s (code=%d)", i.Location, i.Msg, i.Code)
+}
+
func execLint(cfg *lintCfg, args []string, io commands.IO) error {
if len(args) < 1 {
return flag.ErrHelp
@@ -72,66 +99,69 @@ func execLint(cfg *lintCfg, args []string, io commands.IO) error {
for _, pkgPath := range pkgPaths {
if verbose {
- fmt.Fprintf(io.Err(), "Linting %q...\n", pkgPath)
+ io.ErrPrintln(pkgPath)
+ }
+
+ info, err := os.Stat(pkgPath)
+ if err == nil && !info.IsDir() {
+ pkgPath = filepath.Dir(pkgPath)
}
// Check if 'gno.mod' exists
- gnoModPath := filepath.Join(pkgPath, "gno.mod")
- if !osm.FileExists(gnoModPath) {
- hasError = true
+ gmFile, err := gnomod.ParseAt(pkgPath)
+ if err != nil {
issue := lintIssue{
- Code: lintNoGnoMod,
+ Code: lintGnoMod,
Confidence: 1,
Location: pkgPath,
- Msg: "missing 'gno.mod' file",
+ Msg: err.Error(),
}
- fmt.Fprint(io.Err(), issue.String()+"\n")
+ io.ErrPrintln(issue)
+ hasError = true
}
- // Handle runtime errors
- hasError = catchRuntimeError(pkgPath, io.Err(), func() {
- stdout, stdin, stderr := io.Out(), io.In(), io.Err()
- testStore := tests.TestStore(
- rootDir, "",
- stdin, stdout, stderr,
- tests.ImportModeStdlibsOnly,
- )
-
- targetPath := pkgPath
- info, err := os.Stat(pkgPath)
- if err == nil && !info.IsDir() {
- targetPath = filepath.Dir(pkgPath)
+ stdout, stdin, stderr := io.Out(), io.In(), io.Err()
+ _, testStore := test.Store(
+ rootDir, false,
+ stdin, stdout, stderr,
+ )
+
+ memPkg, err := gno.ReadMemPackage(pkgPath, pkgPath)
+ if err != nil {
+ io.ErrPrintln(issueFromError(pkgPath, err).String())
+ hasError = true
+ continue
+ }
+
+ // Run type checking
+ if gmFile == nil || !gmFile.Draft {
+ foundErr, err := lintTypeCheck(io, memPkg, testStore)
+ if err != nil {
+ io.ErrPrintln(err)
+ hasError = true
+ } else if foundErr {
+ hasError = true
}
+ } else if verbose {
+ io.ErrPrintfln("%s: module is draft, skipping type check", pkgPath)
+ }
- memPkg := gno.ReadMemPackage(targetPath, targetPath)
- tm := tests.TestMachine(testStore, stdout, memPkg.Name)
+ // Handle runtime errors
+ hasRuntimeErr := catchRuntimeError(pkgPath, io.Err(), func() {
+ tm := test.Machine(testStore, stdout, memPkg.Path)
+ defer tm.Release()
// Check package
tm.RunMemPackage(memPkg, true)
// Check test files
- testfiles := &gno.FileSet{}
- for _, mfile := range memPkg.Files {
- if !strings.HasSuffix(mfile.Name, ".gno") {
- continue // Skip non-GNO files
- }
+ testFiles := lintTestFiles(memPkg)
- n, _ := gno.ParseFile(mfile.Name, mfile.Body)
- if n == nil {
- continue // Skip empty files
- }
-
- // XXX: package ending with `_test` is not supported yet
- if strings.HasSuffix(mfile.Name, "_test.gno") && !strings.HasSuffix(string(n.PkgName), "_test") {
- // Keep only test files
- testfiles.AddFiles(n)
- }
- }
-
- tm.RunFiles(testfiles.Files...)
- }) || hasError
-
- // TODO: Add more checkers
+ tm.RunFiles(testFiles.Files...)
+ })
+ if hasRuntimeErr {
+ hasError = true
+ }
}
if hasError {
@@ -141,6 +171,66 @@ func execLint(cfg *lintCfg, args []string, io commands.IO) error {
return nil
}
+func lintTypeCheck(io commands.IO, memPkg *gnovm.MemPackage, testStore gno.Store) (errorsFound bool, err error) {
+ tcErr := gno.TypeCheckMemPackageTest(memPkg, testStore)
+ if tcErr == nil {
+ return false, nil
+ }
+
+ errs := multierr.Errors(tcErr)
+ for _, err := range errs {
+ switch err := err.(type) {
+ case types.Error:
+ io.ErrPrintln(lintIssue{
+ Code: lintTypeCheckError,
+ Msg: err.Msg,
+ Confidence: 1,
+ Location: err.Fset.Position(err.Pos).String(),
+ })
+ case scanner.ErrorList:
+ for _, scErr := range err {
+ io.ErrPrintln(lintIssue{
+ Code: lintParserError,
+ Msg: scErr.Msg,
+ Confidence: 1,
+ Location: scErr.Pos.String(),
+ })
+ }
+ case scanner.Error:
+ io.ErrPrintln(lintIssue{
+ Code: lintParserError,
+ Msg: err.Msg,
+ Confidence: 1,
+ Location: err.Pos.String(),
+ })
+ default:
+ return false, fmt.Errorf("unexpected error type: %T", err)
+ }
+ }
+ return true, nil
+}
+
+func lintTestFiles(memPkg *gnovm.MemPackage) *gno.FileSet {
+ testfiles := &gno.FileSet{}
+ for _, mfile := range memPkg.Files {
+ if !strings.HasSuffix(mfile.Name, ".gno") {
+ continue // Skip non-GNO files
+ }
+
+ n, _ := gno.ParseFile(mfile.Name, mfile.Body)
+ if n == nil {
+ continue // Skip empty files
+ }
+
+ // XXX: package ending with `_test` is not supported yet
+ if strings.HasSuffix(mfile.Name, "_test.gno") && !strings.HasSuffix(string(n.PkgName), "_test") {
+ // Keep only test files
+ testfiles.AddFiles(n)
+ }
+ }
+ return testfiles
+}
+
func guessSourcePath(pkg, source string) string {
if info, err := os.Stat(pkg); !os.IsNotExist(err) && !info.IsDir() {
pkg = filepath.Dir(pkg)
@@ -161,7 +251,7 @@ func guessSourcePath(pkg, source string) string {
// reParseRecover is a regex designed to parse error details from a string.
// It extracts the file location, line number, and error message from a formatted error string.
// XXX: Ideally, error handling should encapsulate location details within a dedicated error type.
-var reParseRecover = regexp.MustCompile(`^([^:]+):(\d+)(?::\d+)?:? *(.*)$`)
+var reParseRecover = regexp.MustCompile(`^([^:]+)((?::(?:\d+)){1,2}):? *(.*)$`)
func catchRuntimeError(pkgPath string, stderr io.WriteCloser, action func()) (hasError bool) {
defer func() {
@@ -174,21 +264,21 @@ func catchRuntimeError(pkgPath string, stderr io.WriteCloser, action func()) (ha
switch verr := r.(type) {
case *gno.PreprocessError:
err := verr.Unwrap()
- fmt.Fprint(stderr, issueFromError(pkgPath, err).String()+"\n")
+ fmt.Fprintln(stderr, issueFromError(pkgPath, err).String())
case error:
errors := multierr.Errors(verr)
for _, err := range errors {
errList, ok := err.(scanner.ErrorList)
if ok {
for _, errorInList := range errList {
- fmt.Fprint(stderr, issueFromError(pkgPath, errorInList).String()+"\n")
+ fmt.Fprintln(stderr, issueFromError(pkgPath, errorInList).String())
}
} else {
- fmt.Fprint(stderr, issueFromError(pkgPath, err).String()+"\n")
+ fmt.Fprintln(stderr, issueFromError(pkgPath, err).String())
}
}
case string:
- fmt.Fprint(stderr, issueFromError(pkgPath, errors.New(verr)).String()+"\n")
+ fmt.Fprintln(stderr, issueFromError(pkgPath, errors.New(verr)).String())
default:
panic(r)
}
@@ -198,29 +288,6 @@ func catchRuntimeError(pkgPath string, stderr io.WriteCloser, action func()) (ha
return
}
-type lintCode int
-
-const (
- lintUnknown lintCode = 0
- lintNoGnoMod lintCode = iota
- lintGnoError
-
- // TODO: add new linter codes here.
-)
-
-type lintIssue struct {
- Code lintCode
- Msg string
- Confidence float64 // 1 is 100%
- Location string // file:line, or equivalent
- // TODO: consider writing fix suggestions
-}
-
-func (i lintIssue) String() string {
- // TODO: consider crafting a doc URL based on Code.
- return fmt.Sprintf("%s: %s (code=%d).", i.Location, i.Msg, i.Code)
-}
-
func issueFromError(pkgPath string, err error) lintIssue {
var issue lintIssue
issue.Confidence = 1
@@ -230,9 +297,9 @@ func issueFromError(pkgPath string, err error) lintIssue {
parsedError = strings.TrimPrefix(parsedError, pkgPath+"/")
matches := reParseRecover.FindStringSubmatch(parsedError)
- if len(matches) == 4 {
+ if len(matches) > 0 {
sourcepath := guessSourcePath(pkgPath, matches[1])
- issue.Location = fmt.Sprintf("%s:%s", sourcepath, matches[2])
+ issue.Location = sourcepath + matches[2]
issue.Msg = strings.TrimSpace(matches[3])
} else {
issue.Location = fmt.Sprintf("%s:0", filepath.Clean(pkgPath))
diff --git a/gnovm/cmd/gno/lint_test.go b/gnovm/cmd/gno/lint_test.go
index 20d21c05d05..4589fc55f92 100644
--- a/gnovm/cmd/gno/lint_test.go
+++ b/gnovm/cmd/gno/lint_test.go
@@ -1,6 +1,9 @@
package main
-import "testing"
+import (
+ "strings"
+ "testing"
+)
func TestLintApp(t *testing.T) {
tc := []testMainCase{
@@ -9,41 +12,51 @@ func TestLintApp(t *testing.T) {
errShouldBe: "flag: help requested",
}, {
args: []string{"lint", "../../tests/integ/run_main/"},
- stderrShouldContain: "./../../tests/integ/run_main: missing 'gno.mod' file (code=1).",
+ stderrShouldContain: "./../../tests/integ/run_main: gno.mod file not found in current or any parent directory (code=1)",
errShouldBe: "exit code: 1",
}, {
args: []string{"lint", "../../tests/integ/undefined_variable_test/undefined_variables_test.gno"},
- stderrShouldContain: "undefined_variables_test.gno:6: name toto not declared (code=2)",
+ stderrShouldContain: "undefined_variables_test.gno:6:28: name toto not declared (code=2)",
errShouldBe: "exit code: 1",
}, {
args: []string{"lint", "../../tests/integ/package_not_declared/main.gno"},
- stderrShouldContain: "main.gno:4: name fmt not declared (code=2).",
+ stderrShouldContain: "main.gno:4:2: name fmt not declared (code=2)",
errShouldBe: "exit code: 1",
}, {
args: []string{"lint", "../../tests/integ/several-lint-errors/main.gno"},
- stderrShouldContain: "../../tests/integ/several-lint-errors/main.gno:5: expected ';', found example (code=2).\n../../tests/integ/several-lint-errors/main.gno:6",
+ stderrShouldContain: "../../tests/integ/several-lint-errors/main.gno:5:5: expected ';', found example (code=2)\n../../tests/integ/several-lint-errors/main.gno:6",
errShouldBe: "exit code: 1",
}, {
- args: []string{"lint", "../../tests/integ/several-files-multiple-errors/main.gno"},
- stderrShouldContain: "../../tests/integ/several-files-multiple-errors/file2.gno:3: expected 'IDENT', found '{' (code=2).\n../../tests/integ/several-files-multiple-errors/file2.gno:5: expected type, found '}' (code=2).\n../../tests/integ/several-files-multiple-errors/main.gno:5: expected ';', found example (code=2).\n../../tests/integ/several-files-multiple-errors/main.gno:6: expected '}', found 'EOF' (code=2).\n",
- errShouldBe: "exit code: 1",
- }, {
- args: []string{"lint", "../../tests/integ/run_main/"},
- stderrShouldContain: "./../../tests/integ/run_main: missing 'gno.mod' file (code=1).",
- errShouldBe: "exit code: 1",
+ args: []string{"lint", "../../tests/integ/several-files-multiple-errors/main.gno"},
+ stderrShouldContain: func() string {
+ lines := []string{
+ "../../tests/integ/several-files-multiple-errors/file2.gno:3:5: expected 'IDENT', found '{' (code=2)",
+ "../../tests/integ/several-files-multiple-errors/file2.gno:5:1: expected type, found '}' (code=2)",
+ "../../tests/integ/several-files-multiple-errors/main.gno:5:5: expected ';', found example (code=2)",
+ "../../tests/integ/several-files-multiple-errors/main.gno:6:2: expected '}', found 'EOF' (code=2)",
+ }
+ return strings.Join(lines, "\n") + "\n"
+ }(),
+ errShouldBe: "exit code: 1",
}, {
args: []string{"lint", "../../tests/integ/minimalist_gnomod/"},
// TODO: raise an error because there is a gno.mod, but no .gno files
}, {
args: []string{"lint", "../../tests/integ/invalid_module_name/"},
// TODO: raise an error because gno.mod is invalid
+ }, {
+ args: []string{"lint", "../../tests/integ/invalid_gno_file/"},
+ stderrShouldContain: "../../tests/integ/invalid_gno_file/invalid.gno:1:1: expected 'package', found packag (code=2)",
+ errShouldBe: "exit code: 1",
+ }, {
+ args: []string{"lint", "../../tests/integ/typecheck_missing_return/"},
+ stderrShouldContain: "../../tests/integ/typecheck_missing_return/main.gno:5:1: missing return (code=4)",
+ errShouldBe: "exit code: 1",
},
// TODO: 'gno mod' is valid?
- // TODO: is gno source valid?
// TODO: are dependencies valid?
// TODO: is gno source using unsafe/discouraged features?
- // TODO: consider making `gno transpile; go lint *gen.go`
// TODO: check for imports of native libs from non _test.gno files
}
testMainCaseRun(t, tc)
diff --git a/gnovm/cmd/gno/main_test.go b/gnovm/cmd/gno/main_test.go
index 76c67f6807b..2ea3e31f977 100644
--- a/gnovm/cmd/gno/main_test.go
+++ b/gnovm/cmd/gno/main_test.go
@@ -9,9 +9,9 @@ import (
"strings"
"testing"
- "github.com/stretchr/testify/require"
-
+ "github.com/gnolang/gno/gnovm/cmd/gno/internal/pkgdownload/examplespkgfetcher"
"github.com/gnolang/gno/tm2/pkg/commands"
+ "github.com/stretchr/testify/require"
)
func TestMain_Gno(t *testing.T) {
@@ -60,10 +60,7 @@ func testMainCaseRun(t *testing.T, tc []testMainCase) {
mockErr := bytes.NewBufferString("")
if !test.noTmpGnohome {
- tmpGnoHome, err := os.MkdirTemp(os.TempDir(), "gnotesthome_")
- require.NoError(t, err)
- t.Cleanup(func() { os.RemoveAll(tmpGnoHome) })
- t.Setenv("GNOHOME", tmpGnoHome)
+ t.Setenv("GNOHOME", t.TempDir())
}
checkOutputs := func(t *testing.T) {
@@ -131,6 +128,8 @@ func testMainCaseRun(t *testing.T, tc []testMainCase) {
io.SetOut(commands.WriteNopCloser(mockOut))
io.SetErr(commands.WriteNopCloser(mockErr))
+ testPackageFetcher = examplespkgfetcher.New()
+
err := newGnocliCmd(io).ParseAndRun(context.Background(), test.args)
if errShouldBeEmpty {
diff --git a/gnovm/cmd/gno/mod.go b/gnovm/cmd/gno/mod.go
index 67af5631c71..f762b070fe4 100644
--- a/gnovm/cmd/gno/mod.go
+++ b/gnovm/cmd/gno/mod.go
@@ -4,19 +4,22 @@ import (
"context"
"flag"
"fmt"
- "go/parser"
- "go/token"
"os"
"path/filepath"
- "sort"
"strings"
+ "github.com/gnolang/gno/gnovm/cmd/gno/internal/pkgdownload"
+ "github.com/gnolang/gno/gnovm/cmd/gno/internal/pkgdownload/rpcpkgfetcher"
"github.com/gnolang/gno/gnovm/pkg/gnomod"
+ "github.com/gnolang/gno/gnovm/pkg/packages"
"github.com/gnolang/gno/tm2/pkg/commands"
"github.com/gnolang/gno/tm2/pkg/errors"
"go.uber.org/multierr"
)
+// testPackageFetcher allows to override the package fetcher during tests.
+var testPackageFetcher pkgdownload.PackageFetcher
+
func newModCmd(io commands.IO) *commands.Command {
cmd := commands.NewCommand(
commands.Metadata{
@@ -123,23 +126,17 @@ For example:
}
type modDownloadCfg struct {
- remote string
- verbose bool
+ remoteOverrides string
}
+const remoteOverridesArgName = "remote-overrides"
+
func (c *modDownloadCfg) RegisterFlags(fs *flag.FlagSet) {
fs.StringVar(
- &c.remote,
- "remote",
- "gno.land:26657",
- "remote for fetching gno modules",
- )
-
- fs.BoolVar(
- &c.verbose,
- "v",
- false,
- "verbose output when running",
+ &c.remoteOverrides,
+ remoteOverridesArgName,
+ "",
+ "chain-domain=rpc-url comma-separated list",
)
}
@@ -148,6 +145,17 @@ func execModDownload(cfg *modDownloadCfg, args []string, io commands.IO) error {
return flag.ErrHelp
}
+ fetcher := testPackageFetcher
+ if fetcher == nil {
+ remoteOverrides, err := parseRemoteOverrides(cfg.remoteOverrides)
+ if err != nil {
+ return fmt.Errorf("invalid %s flag: %w", remoteOverridesArgName, err)
+ }
+ fetcher = rpcpkgfetcher.New(remoteOverrides)
+ } else if len(cfg.remoteOverrides) != 0 {
+ return fmt.Errorf("can't use %s flag with a custom package fetcher", remoteOverridesArgName)
+ }
+
path, err := os.Getwd()
if err != nil {
return err
@@ -176,23 +184,26 @@ func execModDownload(cfg *modDownloadCfg, args []string, io commands.IO) error {
return fmt.Errorf("validate: %w", err)
}
- // fetch dependencies
- if err := gnoMod.FetchDeps(gnomod.ModCachePath(), cfg.remote, cfg.verbose); err != nil {
- return fmt.Errorf("fetch: %w", err)
+ if err := downloadDeps(io, path, gnoMod, fetcher); err != nil {
+ return err
}
- gomod, err := gnomod.GnoToGoMod(*gnoMod)
- if err != nil {
- return fmt.Errorf("sanitize: %w", err)
- }
+ return nil
+}
- // write go.mod file
- err = gomod.Write(filepath.Join(path, "go.mod"))
- if err != nil {
- return fmt.Errorf("write go.mod file: %w", err)
+func parseRemoteOverrides(arg string) (map[string]string, error) {
+ pairs := strings.Split(arg, ",")
+ res := make(map[string]string, len(pairs))
+ for _, pair := range pairs {
+ parts := strings.Split(pair, "=")
+ if len(parts) != 2 {
+ return nil, fmt.Errorf("expected 2 parts in chain-domain=rpc-url pair %q", arg)
+ }
+ domain := strings.TrimSpace(parts[0])
+ rpcURL := strings.TrimSpace(parts[1])
+ res[domain] = rpcURL
}
-
- return nil
+ return res, nil
}
func execModInit(args []string) error {
@@ -276,26 +287,6 @@ func modTidyOnce(cfg *modTidyCfg, wd, pkgdir string, io commands.IO) error {
return err
}
- // Drop all existing requires
- for _, r := range gm.Require {
- gm.DropRequire(r.Mod.Path)
- }
-
- imports, err := getGnoPackageImports(pkgdir)
- if err != nil {
- return err
- }
- for _, im := range imports {
- // skip if importpath is modulepath
- if im == gm.Module.Mod.Path {
- continue
- }
- gm.AddRequire(im, "v0.0.0-latest")
- if cfg.verbose {
- io.ErrPrintfln(" %s", im)
- }
- }
-
gm.Write(fname)
return nil
}
@@ -366,79 +357,22 @@ func getImportToFilesMap(pkgPath string) (map[string][]string, error) {
if strings.HasSuffix(filename, "_filetest.gno") {
continue
}
- imports, err := getGnoFileImports(filepath.Join(pkgPath, filename))
+
+ data, err := os.ReadFile(filepath.Join(pkgPath, filename))
if err != nil {
return nil, err
}
-
- for _, imp := range imports {
- m[imp] = append(m[imp], filename)
- }
- }
- return m, nil
-}
-
-// getGnoPackageImports returns the list of gno imports from a given path.
-// Note: It ignores subdirs. Since right now we are still deciding on
-// how to handle subdirs.
-// See:
-// - https://github.com/gnolang/gno/issues/1024
-// - https://github.com/gnolang/gno/issues/852
-//
-// TODO: move this to better location.
-func getGnoPackageImports(path string) ([]string, error) {
- entries, err := os.ReadDir(path)
- if err != nil {
- return nil, err
- }
-
- allImports := make([]string, 0)
- seen := make(map[string]struct{})
- for _, e := range entries {
- filename := e.Name()
- if ext := filepath.Ext(filename); ext != ".gno" {
- continue
- }
- if strings.HasSuffix(filename, "_filetest.gno") {
- continue
- }
- imports, err := getGnoFileImports(filepath.Join(path, filename))
+ imports, _, err := packages.FileImports(filename, string(data))
if err != nil {
return nil, err
}
- for _, im := range imports {
- if !strings.HasPrefix(im, "gno.land/") {
- continue
- }
- if _, ok := seen[im]; ok {
- continue
+
+ for _, imp := range imports {
+ if imp.Error != nil {
+ return nil, err
}
- allImports = append(allImports, im)
- seen[im] = struct{}{}
+ m[imp.PkgPath] = append(m[imp.PkgPath], filename)
}
}
- sort.Strings(allImports)
-
- return allImports, nil
-}
-
-func getGnoFileImports(fname string) ([]string, error) {
- if !strings.HasSuffix(fname, ".gno") {
- return nil, fmt.Errorf("not a gno file: %q", fname)
- }
- data, err := os.ReadFile(fname)
- if err != nil {
- return nil, err
- }
- fs := token.NewFileSet()
- f, err := parser.ParseFile(fs, fname, data, parser.ImportsOnly)
- if err != nil {
- return nil, err
- }
- res := make([]string, 0)
- for _, im := range f.Imports {
- importPath := strings.TrimPrefix(strings.TrimSuffix(im.Path.Value, `"`), `"`)
- res = append(res, importPath)
- }
- return res, nil
+ return m, nil
}
diff --git a/gnovm/cmd/gno/mod_test.go b/gnovm/cmd/gno/mod_test.go
index d35ab311b6c..afce25597cd 100644
--- a/gnovm/cmd/gno/mod_test.go
+++ b/gnovm/cmd/gno/mod_test.go
@@ -1,12 +1,7 @@
package main
import (
- "os"
- "path/filepath"
"testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
)
func TestModApp(t *testing.T) {
@@ -44,24 +39,19 @@ func TestModApp(t *testing.T) {
args: []string{"mod", "download"},
testDir: "../../tests/integ/require_remote_module",
simulateExternalRepo: true,
+ stderrShouldContain: "gno: downloading gno.land/p/demo/avl",
},
{
args: []string{"mod", "download"},
testDir: "../../tests/integ/require_invalid_module",
simulateExternalRepo: true,
- errShouldContain: "fetch: writepackage: querychain",
+ stderrShouldContain: "gno: downloading gno.land/p/demo/notexists",
+ errShouldContain: "query files list for pkg \"gno.land/p/demo/notexists\": package \"gno.land/p/demo/notexists\" is not available",
},
{
args: []string{"mod", "download"},
- testDir: "../../tests/integ/invalid_module_version1",
+ testDir: "../../tests/integ/require_std_lib",
simulateExternalRepo: true,
- errShouldContain: "usage: require module/path v1.2.3",
- },
- {
- args: []string{"mod", "download"},
- testDir: "../../tests/integ/invalid_module_version2",
- simulateExternalRepo: true,
- errShouldContain: "invalid: must be of the form v1.2.3",
},
{
args: []string{"mod", "download"},
@@ -72,12 +62,14 @@ func TestModApp(t *testing.T) {
args: []string{"mod", "download"},
testDir: "../../tests/integ/replace_with_module",
simulateExternalRepo: true,
+ stderrShouldContain: "gno: downloading gno.land/p/demo/users",
},
{
args: []string{"mod", "download"},
testDir: "../../tests/integ/replace_with_invalid_module",
simulateExternalRepo: true,
- errShouldContain: "fetch: writepackage: querychain",
+ stderrShouldContain: "gno: downloading gno.land/p/demo/notexists",
+ errShouldContain: "query files list for pkg \"gno.land/p/demo/notexists\": package \"gno.land/p/demo/notexists\" is not available",
},
// test `gno mod init` with no module name
@@ -158,12 +150,6 @@ func TestModApp(t *testing.T) {
simulateExternalRepo: true,
errShouldContain: "could not read gno.mod file",
},
- {
- args: []string{"mod", "tidy"},
- testDir: "../../tests/integ/invalid_module_version1",
- simulateExternalRepo: true,
- errShouldContain: "error parsing gno.mod file at",
- },
{
args: []string{"mod", "tidy"},
testDir: "../../tests/integ/minimalist_gnomod",
@@ -179,12 +165,6 @@ func TestModApp(t *testing.T) {
testDir: "../../tests/integ/valid2",
simulateExternalRepo: true,
},
- {
- args: []string{"mod", "tidy"},
- testDir: "../../tests/integ/invalid_gno_file",
- simulateExternalRepo: true,
- errShouldContain: "expected 'package', found packag",
- },
// test `gno mod why`
{
@@ -199,12 +179,6 @@ func TestModApp(t *testing.T) {
simulateExternalRepo: true,
errShouldContain: "could not read gno.mod file",
},
- {
- args: []string{"mod", "why", "std"},
- testDir: "../../tests/integ/invalid_module_version1",
- simulateExternalRepo: true,
- errShouldContain: "error parsing gno.mod file at",
- },
{
args: []string{"mod", "why", "std"},
testDir: "../../tests/integ/invalid_gno_file",
@@ -239,122 +213,6 @@ valid.gno
`,
},
}
- testMainCaseRun(t, tc)
-}
-
-func TestGetGnoImports(t *testing.T) {
- workingDir, err := os.Getwd()
- require.NoError(t, err)
-
- // create external dir
- tmpDir, cleanUpFn := createTmpDir(t)
- defer cleanUpFn()
-
- // cd to tmp directory
- os.Chdir(tmpDir)
- defer os.Chdir(workingDir)
-
- files := []struct {
- name, data string
- }{
- {
- name: "file1.gno",
- data: `
- package tmp
-
- import (
- "std"
-
- "gno.land/p/demo/pkg1"
- )
- `,
- },
- {
- name: "file2.gno",
- data: `
- package tmp
-
- import (
- "gno.land/p/demo/pkg1"
- "gno.land/p/demo/pkg2"
- )
- `,
- },
- {
- name: "file1_test.gno",
- data: `
- package tmp
-
- import (
- "testing"
-
- "gno.land/p/demo/testpkg"
- )
- `,
- },
- {
- name: "z_0_filetest.gno",
- data: `
- package main
-
- import (
- "gno.land/p/demo/filetestpkg"
- )
- `,
- },
-
- // subpkg files
- {
- name: filepath.Join("subtmp", "file1.gno"),
- data: `
- package subtmp
-
- import (
- "std"
-
- "gno.land/p/demo/subpkg1"
- )
- `,
- },
- {
- name: filepath.Join("subtmp", "file2.gno"),
- data: `
- package subtmp
-
- import (
- "gno.land/p/demo/subpkg1"
- "gno.land/p/demo/subpkg2"
- )
- `,
- },
- }
-
- // Expected list of imports
- // - ignore subdirs
- // - ignore duplicate
- // - ignore *_filetest.gno
- // - should be sorted
- expected := []string{
- "gno.land/p/demo/pkg1",
- "gno.land/p/demo/pkg2",
- "gno.land/p/demo/testpkg",
- }
-
- // Create subpkg dir
- err = os.Mkdir("subtmp", 0o700)
- require.NoError(t, err)
-
- // Create files
- for _, f := range files {
- err = os.WriteFile(f.name, []byte(f.data), 0o644)
- require.NoError(t, err)
- }
- imports, err := getGnoPackageImports(tmpDir)
- require.NoError(t, err)
-
- require.Equal(t, len(expected), len(imports))
- for i := range imports {
- assert.Equal(t, expected[i], imports[i])
- }
+ testMainCaseRun(t, tc)
}
diff --git a/gnovm/cmd/gno/run.go b/gnovm/cmd/gno/run.go
index f174c2b4cc5..9a9beac5cd1 100644
--- a/gnovm/cmd/gno/run.go
+++ b/gnovm/cmd/gno/run.go
@@ -12,7 +12,7 @@ import (
"github.com/gnolang/gno/gnovm/pkg/gnoenv"
gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
- "github.com/gnolang/gno/gnovm/tests"
+ "github.com/gnolang/gno/gnovm/pkg/test"
"github.com/gnolang/gno/tm2/pkg/commands"
"github.com/gnolang/gno/tm2/pkg/std"
)
@@ -92,9 +92,9 @@ func execRun(cfg *runCfg, args []string, io commands.IO) error {
stderr := io.Err()
// init store and machine
- testStore := tests.TestStore(cfg.rootDir,
- "", stdin, stdout, stderr,
- tests.ImportModeStdlibsPreferred)
+ _, testStore := test.Store(
+ cfg.rootDir, false,
+ stdin, stdout, stderr)
if cfg.verbose {
testStore.SetLogStoreOps(true)
}
@@ -115,7 +115,7 @@ func execRun(cfg *runCfg, args []string, io commands.IO) error {
var send std.Coins
pkgPath := string(files[0].PkgName)
- ctx := tests.TestContext(pkgPath, send)
+ ctx := test.Context(pkgPath, send)
m := gno.NewMachineWithOptions(gno.MachineOptions{
PkgPath: pkgPath,
Output: stdout,
diff --git a/gnovm/cmd/gno/run_test.go b/gnovm/cmd/gno/run_test.go
index e5aa1bd6279..aa7780c149e 100644
--- a/gnovm/cmd/gno/run_test.go
+++ b/gnovm/cmd/gno/run_test.go
@@ -1,6 +1,9 @@
package main
-import "testing"
+import (
+ "strings"
+ "testing"
+)
func TestRunApp(t *testing.T) {
tc := []testMainCase{
@@ -84,9 +87,17 @@ func TestRunApp(t *testing.T) {
stdoutShouldContain: "Context worked",
},
{
- args: []string{"run", "../../tests/integ/several-files-multiple-errors/"},
- stderrShouldContain: "../../tests/integ/several-files-multiple-errors/file2.gno:3: expected 'IDENT', found '{' (code=2).\n../../tests/integ/several-files-multiple-errors/file2.gno:5: expected type, found '}' (code=2).\n../../tests/integ/several-files-multiple-errors/main.gno:5: expected ';', found example (code=2).\n../../tests/integ/several-files-multiple-errors/main.gno:6: expected '}', found 'EOF' (code=2).",
- errShouldBe: "exit code: 1",
+ args: []string{"run", "../../tests/integ/several-files-multiple-errors/"},
+ stderrShouldContain: func() string {
+ lines := []string{
+ "../../tests/integ/several-files-multiple-errors/file2.gno:3:5: expected 'IDENT', found '{' (code=2)",
+ "../../tests/integ/several-files-multiple-errors/file2.gno:5:1: expected type, found '}' (code=2)",
+ "../../tests/integ/several-files-multiple-errors/main.gno:5:5: expected ';', found example (code=2)",
+ "../../tests/integ/several-files-multiple-errors/main.gno:6:2: expected '}', found 'EOF' (code=2)",
+ }
+ return strings.Join(lines, "\n") + "\n"
+ }(),
+ errShouldBe: "exit code: 1",
},
// TODO: a test file
// TODO: args
diff --git a/gnovm/cmd/gno/test.go b/gnovm/cmd/gno/test.go
index d54b12f6a4f..fec0de7c221 100644
--- a/gnovm/cmd/gno/test.go
+++ b/gnovm/cmd/gno/test.go
@@ -1,33 +1,21 @@
package main
import (
- "bytes"
"context"
- "encoding/json"
"flag"
"fmt"
+ goio "io"
"log"
- "math"
- "os"
"path/filepath"
- "runtime/debug"
- "sort"
"strings"
- "text/template"
"time"
- "go.uber.org/multierr"
-
- "github.com/gnolang/gno/gnovm"
"github.com/gnolang/gno/gnovm/pkg/gnoenv"
gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
"github.com/gnolang/gno/gnovm/pkg/gnomod"
- "github.com/gnolang/gno/gnovm/tests"
- teststd "github.com/gnolang/gno/gnovm/tests/stdlibs/std"
+ "github.com/gnolang/gno/gnovm/pkg/test"
"github.com/gnolang/gno/tm2/pkg/commands"
- "github.com/gnolang/gno/tm2/pkg/errors"
"github.com/gnolang/gno/tm2/pkg/random"
- "github.com/gnolang/gno/tm2/pkg/testutils"
)
type testCfg struct {
@@ -38,7 +26,6 @@ type testCfg struct {
updateGoldenTests bool
printRuntimeMetrics bool
printEvents bool
- withNativeFallback bool
}
func newTestCmd(io commands.IO) *commands.Command {
@@ -66,34 +53,38 @@ module name found in 'gno.mod', or else it is randomly generated like
- "*_filetest.gno" files on the other hand are kind of unique. They exist to
provide a way to interact and assert a gno contract, thanks to a set of
-specific instructions that can be added using code comments.
+specific directives that can be added using code comments.
"*_filetest.gno" must be declared in the 'main' package and so must have a
'main' function, that will be executed to test the target contract.
-List of available instructions that can be used in "*_filetest.gno" files:
- - "PKGPATH:" is a single line instruction that can be used to define the
+These single-line directives can set "input parameters" for the machine used
+to perform the test:
+ - "PKGPATH:" is a single line directive that can be used to define the
package used to interact with the tested package. If not specified, "main" is
used.
- - "MAXALLOC:" is a signle line instruction that can be used to define a limit
+ - "MAXALLOC:" is a single line directive that can be used to define a limit
to the VM allocator. If this limit is exceeded, the VM will panic. Default to
0, no limit.
- - "SEND:" is a single line instruction that can be used to send an amount of
+ - "SEND:" is a single line directive that can be used to send an amount of
token along with the transaction. The format is for example "1000000ugnot".
Default is empty.
- - "Output:\n" (*) is a multiple lines instruction that can be used to assert
- the output of the "*_filetest.gno" file. Any prints executed inside the
- 'main' function must match the lines that follows the "Output:\n"
- instruction, or else the test fails.
- - "Error:\n" works similarly to "Output:\n", except that it asserts the
- stderr of the program, which in that case, comes from the VM because of a
- panic, rather than the 'main' function.
- - "Realm:\n" (*) is a multiple lines instruction that can be used to assert
- what has been recorded in the store following the execution of the 'main'
- function.
-
-(*) The 'update-golden-tests' flag can be set to fill out the content of the
-instruction with the actual content of the test instead of failing.
+
+These directives, instead, match the comment that follows with the result
+of the GnoVM, acting as a "golden test":
+ - "Output:" tests the following comment with the standard output of the
+ filetest.
+ - "Error:" tests the following comment with any panic, or other kind of
+ error that the filetest generates (like a parsing or preprocessing error).
+ - "Realm:" tests the following comment against the store log, which can show
+ what realm information is stored.
+ - "Stacktrace:" can be used to verify the following lines against the
+ stacktrace of the error.
+ - "Events:" can be used to verify the emitted events against a JSON.
+
+To speed up execution, imports of pure packages are processed separately from
+the execution of the tests. This makes testing faster, but means that the
+initialization of imported pure packages cannot be checked in filetests.
`,
},
cfg,
@@ -115,7 +106,7 @@ func (c *testCfg) RegisterFlags(fs *flag.FlagSet) {
&c.updateGoldenTests,
"update-golden-tests",
false,
- `writes actual as wanted for "Output:" and "Realm:" instructions`,
+ `writes actual as wanted for "golden" directives in filetests`,
)
fs.StringVar(
@@ -139,13 +130,6 @@ func (c *testCfg) RegisterFlags(fs *flag.FlagSet) {
"max execution time",
)
- fs.BoolVar(
- &c.withNativeFallback,
- "with-native-fallback",
- false,
- "use stdlibs/* if present, otherwise use supported native Go packages",
- )
-
fs.BoolVar(
&c.printRuntimeMetrics,
"print-runtime-metrics",
@@ -192,6 +176,18 @@ func execTest(cfg *testCfg, args []string, io commands.IO) error {
return fmt.Errorf("list sub packages: %w", err)
}
+ // Set up options to run tests.
+ stdout := goio.Discard
+ if cfg.verbose {
+ stdout = io.Out()
+ }
+ opts := test.NewTestOptions(cfg.rootDir, io.In(), stdout, io.Err())
+ opts.RunFlag = cfg.run
+ opts.Sync = cfg.updateGoldenTests
+ opts.Verbose = cfg.verbose
+ opts.Metrics = cfg.printRuntimeMetrics
+ opts.Events = cfg.printEvents
+
buildErrCount := 0
testErrCount := 0
for _, pkg := range subPkgs {
@@ -199,200 +195,48 @@ func execTest(cfg *testCfg, args []string, io commands.IO) error {
io.ErrPrintfln("? %s \t[no test files]", pkg.Dir)
continue
}
-
- sort.Strings(pkg.TestGnoFiles)
- sort.Strings(pkg.FiletestGnoFiles)
-
- startedAt := time.Now()
- err = gnoTestPkg(pkg.Dir, pkg.TestGnoFiles, pkg.FiletestGnoFiles, cfg, io)
- duration := time.Since(startedAt)
- dstr := fmtDuration(duration)
-
- if err != nil {
- io.ErrPrintfln("%s: test pkg: %v", pkg.Dir, err)
- io.ErrPrintfln("FAIL")
- io.ErrPrintfln("FAIL %s \t%s", pkg.Dir, dstr)
- io.ErrPrintfln("FAIL")
- testErrCount++
- } else {
- io.ErrPrintfln("ok %s \t%s", pkg.Dir, dstr)
- }
- }
- if testErrCount > 0 || buildErrCount > 0 {
- io.ErrPrintfln("FAIL")
- return fmt.Errorf("FAIL: %d build errors, %d test errors", buildErrCount, testErrCount)
- }
-
- return nil
-}
-
-func gnoTestPkg(
- pkgPath string,
- unittestFiles,
- filetestFiles []string,
- cfg *testCfg,
- io commands.IO,
-) error {
- var (
- verbose = cfg.verbose
- rootDir = cfg.rootDir
- runFlag = cfg.run
- printRuntimeMetrics = cfg.printRuntimeMetrics
- printEvents = cfg.printEvents
-
- stdin = io.In()
- stdout = io.Out()
- stderr = io.Err()
- errs error
- )
-
- mode := tests.ImportModeStdlibsOnly
- if cfg.withNativeFallback {
- // XXX: display a warn?
- mode = tests.ImportModeStdlibsPreferred
- }
- if !verbose {
- // TODO: speedup by ignoring if filter is file/*?
- mockOut := bytes.NewBufferString("")
- stdout = commands.WriteNopCloser(mockOut)
- }
-
- // testing with *_test.gno
- if len(unittestFiles) > 0 {
// Determine gnoPkgPath by reading gno.mod
var gnoPkgPath string
- modfile, err := gnomod.ParseAt(pkgPath)
+ modfile, err := gnomod.ParseAt(pkg.Dir)
if err == nil {
gnoPkgPath = modfile.Module.Mod.Path
} else {
- gnoPkgPath = pkgPathFromRootDir(pkgPath, rootDir)
+ gnoPkgPath = pkgPathFromRootDir(pkg.Dir, cfg.rootDir)
if gnoPkgPath == "" {
// unable to read pkgPath from gno.mod, generate a random realm path
io.ErrPrintfln("--- WARNING: unable to read package path from gno.mod or gno root directory; try creating a gno.mod file")
- gnoPkgPath = gno.RealmPathPrefix + random.RandStr(8)
+ gnoPkgPath = "gno.land/r/" + strings.ToLower(random.RandStr(8)) // XXX: gno.land hardcoded for convenience.
}
}
- memPkg := gno.ReadMemPackage(pkgPath, gnoPkgPath)
- // tfiles, ifiles := gno.ParseMemPackageTests(memPkg)
- var tfiles, ifiles *gno.FileSet
+ memPkg := gno.MustReadMemPackage(pkg.Dir, gnoPkgPath)
- hasError := catchRuntimeError(gnoPkgPath, stderr, func() {
- tfiles, ifiles = parseMemPackageTests(memPkg)
+ startedAt := time.Now()
+ hasError := catchRuntimeError(gnoPkgPath, io.Err(), func() {
+ err = test.Test(memPkg, pkg.Dir, opts)
})
- if hasError {
- return commands.ExitCodeError(1)
- }
- testPkgName := getPkgNameFromFileset(ifiles)
-
- // run test files in pkg
- if len(tfiles.Files) > 0 {
- testStore := tests.TestStore(
- rootDir, "",
- stdin, stdout, stderr,
- mode,
- )
- if verbose {
- testStore.SetLogStoreOps(true)
- }
-
- m := tests.TestMachine(testStore, stdout, gnoPkgPath)
- if printRuntimeMetrics {
- // from tm2/pkg/sdk/vm/keeper.go
- // XXX: make maxAllocTx configurable.
- maxAllocTx := int64(math.MaxInt64)
-
- m.Alloc = gno.NewAllocator(maxAllocTx)
- }
- m.RunMemPackage(memPkg, true)
- err := runTestFiles(m, tfiles, memPkg.Name, verbose, printRuntimeMetrics, printEvents, runFlag, io)
- if err != nil {
- errs = multierr.Append(errs, err)
- }
- }
-
- // test xxx_test pkg
- if len(ifiles.Files) > 0 {
- testStore := tests.TestStore(
- rootDir, "",
- stdin, stdout, stderr,
- mode,
- )
- if verbose {
- testStore.SetLogStoreOps(true)
- }
-
- m := tests.TestMachine(testStore, stdout, testPkgName)
-
- memFiles := make([]*gnovm.MemFile, 0, len(ifiles.FileNames())+1)
- for _, f := range memPkg.Files {
- for _, ifileName := range ifiles.FileNames() {
- if f.Name == "gno.mod" || f.Name == ifileName {
- memFiles = append(memFiles, f)
- break
- }
- }
- }
-
- memPkg.Files = memFiles
- memPkg.Name = testPkgName
- memPkg.Path = memPkg.Path + "_test"
- m.RunMemPackage(memPkg, true)
+ duration := time.Since(startedAt)
+ dstr := fmtDuration(duration)
- err := runTestFiles(m, ifiles, testPkgName, verbose, printRuntimeMetrics, printEvents, runFlag, io)
+ if hasError || err != nil {
if err != nil {
- errs = multierr.Append(errs, err)
+ io.ErrPrintfln("%s: test pkg: %v", pkg.Dir, err)
}
+ io.ErrPrintfln("FAIL")
+ io.ErrPrintfln("FAIL %s \t%s", pkg.Dir, dstr)
+ io.ErrPrintfln("FAIL")
+ testErrCount++
+ } else {
+ io.ErrPrintfln("ok %s \t%s", pkg.Dir, dstr)
}
}
-
- // testing with *_filetest.gno
- {
- filter := splitRegexp(runFlag)
- for _, testFile := range filetestFiles {
- testFileName := filepath.Base(testFile)
- testName := "file/" + testFileName
- if !shouldRun(filter, testName) {
- continue
- }
-
- startedAt := time.Now()
- if verbose {
- io.ErrPrintfln("=== RUN %s", testName)
- }
-
- var closer func() (string, error)
- if !verbose {
- closer = testutils.CaptureStdoutAndStderr()
- }
-
- testFilePath := filepath.Join(pkgPath, testFileName)
- err := tests.RunFileTest(rootDir, testFilePath, tests.WithSyncWanted(cfg.updateGoldenTests))
- duration := time.Since(startedAt)
- dstr := fmtDuration(duration)
-
- if err != nil {
- errs = multierr.Append(errs, err)
- io.ErrPrintfln("--- FAIL: %s (%s)", testName, dstr)
- if verbose {
- stdouterr, err := closer()
- if err != nil {
- panic(err)
- }
- fmt.Fprintln(os.Stderr, stdouterr)
- }
- continue
- }
-
- if verbose {
- io.ErrPrintfln("--- PASS: %s (%s)", testName, dstr)
- }
- // XXX: add per-test metrics
- }
+ if testErrCount > 0 || buildErrCount > 0 {
+ io.ErrPrintfln("FAIL")
+ return fmt.Errorf("FAIL: %d build errors, %d test errors", buildErrCount, testErrCount)
}
- return errs
+ return nil
}
// attempts to determine the full gno pkg path by analyzing the directory.
@@ -423,228 +267,3 @@ func pkgPathFromRootDir(pkgPath, rootDir string) string {
}
return ""
}
-
-func runTestFiles(
- m *gno.Machine,
- files *gno.FileSet,
- pkgName string,
- verbose bool,
- printRuntimeMetrics bool,
- printEvents bool,
- runFlag string,
- io commands.IO,
-) (errs error) {
- defer func() {
- if r := recover(); r != nil {
- errs = multierr.Append(fmt.Errorf("panic: %v\nstack:\n%v\ngno machine: %v", r, string(debug.Stack()), m.String()), errs)
- }
- }()
-
- testFuncs := &testFuncs{
- PackageName: pkgName,
- Verbose: verbose,
- RunFlag: runFlag,
- }
- loadTestFuncs(pkgName, testFuncs, files)
-
- // before/after statistics
- numPackagesBefore := m.Store.NumMemPackages()
-
- testmain, err := formatTestmain(testFuncs)
- if err != nil {
- log.Fatal(err)
- }
-
- m.RunFiles(files.Files...)
- n := gno.MustParseFile("main_test.gno", testmain)
- m.RunFiles(n)
-
- for _, test := range testFuncs.Tests {
- // cleanup machine between tests
- tests.CleanupMachine(m)
-
- testFuncStr := fmt.Sprintf("%q", test.Name)
-
- eval := m.Eval(gno.Call("runtest", testFuncStr))
-
- if printEvents {
- events := m.Context.(*teststd.TestExecContext).EventLogger.Events()
- if events != nil {
- res, err := json.Marshal(events)
- if err != nil {
- panic(err)
- }
- io.ErrPrintfln("EVENTS: %s", string(res))
- }
- }
-
- ret := eval[0].GetString()
- if ret == "" {
- err := errors.New("failed to execute unit test: %q", test.Name)
- errs = multierr.Append(errs, err)
- io.ErrPrintfln("--- FAIL: %s [internal gno testing error]", test.Name)
- continue
- }
-
- // TODO: replace with amino or send native type?
- var rep report
- err = json.Unmarshal([]byte(ret), &rep)
- if err != nil {
- errs = multierr.Append(errs, err)
- io.ErrPrintfln("--- FAIL: %s [internal gno testing error]", test.Name)
- continue
- }
-
- if rep.Failed {
- err := errors.New("failed: %q", test.Name)
- errs = multierr.Append(errs, err)
- }
-
- if printRuntimeMetrics {
- imports := m.Store.NumMemPackages() - numPackagesBefore - 1
- // XXX: store changes
- // XXX: max mem consumption
- allocsVal := "n/a"
- if m.Alloc != nil {
- maxAllocs, allocs := m.Alloc.Status()
- allocsVal = fmt.Sprintf("%s(%.2f%%)",
- prettySize(allocs),
- float64(allocs)/float64(maxAllocs)*100,
- )
- }
- io.ErrPrintfln("--- runtime: cycle=%s imports=%d allocs=%s",
- prettySize(m.Cycles),
- imports,
- allocsVal,
- )
- }
- }
-
- return errs
-}
-
-// mirror of stdlibs/testing.Report
-type report struct {
- Failed bool
- Skipped bool
-}
-
-var testmainTmpl = template.Must(template.New("testmain").Parse(`
-package {{ .PackageName }}
-
-import (
- "testing"
-)
-
-var tests = []testing.InternalTest{
-{{range .Tests}}
- {"{{.Name}}", {{.Name}}},
-{{end}}
-}
-
-func runtest(name string) (report string) {
- for _, test := range tests {
- if test.Name == name {
- return testing.RunTest({{printf "%q" .RunFlag}}, {{.Verbose}}, test)
- }
- }
- panic("no such test: " + name)
- return ""
-}
-`))
-
-type testFuncs struct {
- Tests []testFunc
- PackageName string
- Verbose bool
- RunFlag string
-}
-
-type testFunc struct {
- Package string
- Name string
-}
-
-func getPkgNameFromFileset(files *gno.FileSet) string {
- if len(files.Files) <= 0 {
- return ""
- }
- return string(files.Files[0].PkgName)
-}
-
-func formatTestmain(t *testFuncs) (string, error) {
- var buf bytes.Buffer
- if err := testmainTmpl.Execute(&buf, t); err != nil {
- return "", err
- }
- return buf.String(), nil
-}
-
-func loadTestFuncs(pkgName string, t *testFuncs, tfiles *gno.FileSet) *testFuncs {
- for _, tf := range tfiles.Files {
- for _, d := range tf.Decls {
- if fd, ok := d.(*gno.FuncDecl); ok {
- fname := string(fd.Name)
- if strings.HasPrefix(fname, "Test") {
- tf := testFunc{
- Package: pkgName,
- Name: fname,
- }
- t.Tests = append(t.Tests, tf)
- }
- }
- }
- }
- return t
-}
-
-// parseMemPackageTests is copied from gno.ParseMemPackageTests
-// for except to _filetest.gno
-func parseMemPackageTests(memPkg *gnovm.MemPackage) (tset, itset *gno.FileSet) {
- tset = &gno.FileSet{}
- itset = &gno.FileSet{}
- var errs error
- for _, mfile := range memPkg.Files {
- if !strings.HasSuffix(mfile.Name, ".gno") {
- continue // skip this file.
- }
- if strings.HasSuffix(mfile.Name, "_filetest.gno") {
- continue
- }
- n, err := gno.ParseFile(mfile.Name, mfile.Body)
- if err != nil {
- errs = multierr.Append(errs, err)
- continue
- }
- if n == nil {
- panic("should not happen")
- }
- if strings.HasSuffix(mfile.Name, "_test.gno") {
- // add test file.
- if memPkg.Name+"_test" == string(n.PkgName) {
- itset.AddFiles(n)
- } else {
- tset.AddFiles(n)
- }
- } else if memPkg.Name == string(n.PkgName) {
- // skip package file.
- } else {
- panic(fmt.Sprintf(
- "expected package name [%s] or [%s_test] but got [%s] file [%s]",
- memPkg.Name, memPkg.Name, n.PkgName, mfile))
- }
- }
- if errs != nil {
- panic(errs)
- }
- return tset, itset
-}
-
-func shouldRun(filter filterMatch, path string) bool {
- if filter == nil {
- return true
- }
- elem := strings.Split(path, "/")
- ok, _ := filter.matches(elem, matchString)
- return ok
-}
diff --git a/gnovm/cmd/gno/testdata/gno_fmt/empty.txtar b/gnovm/cmd/gno/testdata/fmt/empty.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_fmt/empty.txtar
rename to gnovm/cmd/gno/testdata/fmt/empty.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_fmt/import_cleaning.txtar b/gnovm/cmd/gno/testdata/fmt/import_cleaning.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_fmt/import_cleaning.txtar
rename to gnovm/cmd/gno/testdata/fmt/import_cleaning.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_fmt/include.txtar b/gnovm/cmd/gno/testdata/fmt/include.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_fmt/include.txtar
rename to gnovm/cmd/gno/testdata/fmt/include.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_fmt/multi_import.txtar b/gnovm/cmd/gno/testdata/fmt/multi_import.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_fmt/multi_import.txtar
rename to gnovm/cmd/gno/testdata/fmt/multi_import.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_fmt/noimport_format.txtar b/gnovm/cmd/gno/testdata/fmt/noimport_format.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_fmt/noimport_format.txtar
rename to gnovm/cmd/gno/testdata/fmt/noimport_format.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_fmt/parse_error.txtar b/gnovm/cmd/gno/testdata/fmt/parse_error.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_fmt/parse_error.txtar
rename to gnovm/cmd/gno/testdata/fmt/parse_error.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_fmt/shadow_import.txtar b/gnovm/cmd/gno/testdata/fmt/shadow_import.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_fmt/shadow_import.txtar
rename to gnovm/cmd/gno/testdata/fmt/shadow_import.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_lint/file_error_txtar b/gnovm/cmd/gno/testdata/gno_lint/file_error_txtar
deleted file mode 100644
index 9482eeb1f4f..00000000000
--- a/gnovm/cmd/gno/testdata/gno_lint/file_error_txtar
+++ /dev/null
@@ -1,20 +0,0 @@
-# gno lint: test file error
-
-! gno lint ./i_have_error_test.gno
-
-cmp stdout stdout.golden
-cmp stderr stderr.golden
-
--- i_have_error_test.gno --
-package main
-
-import "fmt"
-
-func TestIHaveSomeError() {
- i := undefined_variable
- fmt.Println("Hello", 42)
-}
-
--- stdout.golden --
--- stderr.golden --
-i_have_error_test.gno:6: name undefined_variable not declared (code=2).
diff --git a/gnovm/cmd/gno/testdata/gno_test/test_with-native-fallback.txtar b/gnovm/cmd/gno/testdata/gno_test/test_with-native-fallback.txtar
deleted file mode 100644
index 6099788a9a1..00000000000
--- a/gnovm/cmd/gno/testdata/gno_test/test_with-native-fallback.txtar
+++ /dev/null
@@ -1,32 +0,0 @@
-# Test native lib
-
-! gno test -v .
-
-! stdout .+
-stderr 'panic: unknown import path net \[recovered\]'
-stderr ' panic: gno.land/r/\w{8}/contract.gno:3:8: unknown import path net'
-
-gno test -v --with-native-fallback .
-
-! stdout .+
-stderr '=== RUN TestFoo'
-stderr '--- PASS: TestFoo'
-
--- contract.gno --
-package contract
-
-import "net"
-
-func Foo() {
- _ = net.IPv4
-}
-
--- contract_test.gno --
-package contract
-
-import "testing"
-
-func TestFoo(t *testing.T) {
- Foo()
-}
-
diff --git a/gnovm/cmd/gno/testdata/gno_test/unknow_lib.txtar b/gnovm/cmd/gno/testdata/gno_test/unknow_lib.txtar
deleted file mode 100644
index 37ef68f3d91..00000000000
--- a/gnovm/cmd/gno/testdata/gno_test/unknow_lib.txtar
+++ /dev/null
@@ -1,32 +0,0 @@
-# Test unknow lib
-
-! gno test -v .
-
-! stdout .+
-stderr 'panic: unknown import path foobarbaz \[recovered\]'
-stderr ' panic: gno.land/r/\w{8}/contract.gno:3:8: unknown import path foobarbaz'
-
-! gno test -v --with-native-fallback .
-
-! stdout .+
-stderr 'panic: unknown import path foobarbaz \[recovered\]'
-stderr ' panic: gno.land/r/\w{8}/contract.gno:3:8: unknown import path foobarbaz'
-
--- contract.gno --
-package contract
-
-import "foobarbaz"
-
-func Foo() {
- _ = foobarbaz.Gnognogno
-}
-
--- contract_test.gno --
-package contract
-
-import "testing"
-
-func TestFoo(t *testing.T) {
- Foo()
-}
-
diff --git a/gnovm/cmd/gno/testdata/gno_lint/bad_import.txtar b/gnovm/cmd/gno/testdata/lint/bad_import.txtar
similarity index 54%
rename from gnovm/cmd/gno/testdata/gno_lint/bad_import.txtar
rename to gnovm/cmd/gno/testdata/lint/bad_import.txtar
index fc4039d38c6..b5edbdd0223 100644
--- a/gnovm/cmd/gno/testdata/gno_lint/bad_import.txtar
+++ b/gnovm/cmd/gno/testdata/lint/bad_import.txtar
@@ -11,9 +11,13 @@ package main
import "python"
func main() {
- fmt.Println("Hello", 42)
+ println("Hello", 42)
}
+-- gno.mod --
+module gno.land/p/test
+
-- stdout.golden --
-- stderr.golden --
-bad_file.gno:3: unknown import path python (code=2).
+bad_file.gno:3:8: could not import python (import not found: python) (code=4)
+bad_file.gno:3:8: unknown import path python (code=2)
diff --git a/gnovm/cmd/gno/testdata/gno_lint/file_error.txtar b/gnovm/cmd/gno/testdata/lint/file_error.txtar
similarity index 72%
rename from gnovm/cmd/gno/testdata/gno_lint/file_error.txtar
rename to gnovm/cmd/gno/testdata/lint/file_error.txtar
index 9482eeb1f4f..4fa50c6da81 100644
--- a/gnovm/cmd/gno/testdata/gno_lint/file_error.txtar
+++ b/gnovm/cmd/gno/testdata/lint/file_error.txtar
@@ -15,6 +15,9 @@ func TestIHaveSomeError() {
fmt.Println("Hello", 42)
}
+-- gno.mod --
+module gno.land/p/test
+
-- stdout.golden --
-- stderr.golden --
-i_have_error_test.gno:6: name undefined_variable not declared (code=2).
+i_have_error_test.gno:6:7: name undefined_variable not declared (code=2)
diff --git a/gnovm/cmd/gno/testdata/gno_lint/no_error.txtar b/gnovm/cmd/gno/testdata/lint/no_error.txtar
similarity index 68%
rename from gnovm/cmd/gno/testdata/gno_lint/no_error.txtar
rename to gnovm/cmd/gno/testdata/lint/no_error.txtar
index 95356b1ba2b..5dd3b164952 100644
--- a/gnovm/cmd/gno/testdata/gno_lint/no_error.txtar
+++ b/gnovm/cmd/gno/testdata/lint/no_error.txtar
@@ -1,6 +1,6 @@
# testing simple gno lint command with any error
-gno lint ./good_file.gno
+gno lint ./good_file.gno
cmp stdout stdout.golden
cmp stdout stderr.golden
@@ -8,11 +8,12 @@ cmp stdout stderr.golden
-- good_file.gno --
package main
-import "fmt"
-
func main() {
- fmt.Println("Hello", 42)
+ println("Hello", 42)
}
+-- gno.mod --
+module gno.land/p/demo/test
+
-- stdout.golden --
-- stderr.golden --
diff --git a/gnovm/cmd/gno/testdata/gno_lint/no_gnomod.txtar b/gnovm/cmd/gno/testdata/lint/no_gnomod.txtar
similarity index 60%
rename from gnovm/cmd/gno/testdata/gno_lint/no_gnomod.txtar
rename to gnovm/cmd/gno/testdata/lint/no_gnomod.txtar
index 52daa6f0e9b..b5a046a7095 100644
--- a/gnovm/cmd/gno/testdata/gno_lint/no_gnomod.txtar
+++ b/gnovm/cmd/gno/testdata/lint/no_gnomod.txtar
@@ -8,12 +8,10 @@ cmp stderr stderr.golden
-- good_file.gno --
package main
-import "fmt"
-
func main() {
- fmt.Println("Hello", 42)
+ println("Hello", 42)
}
-- stdout.golden --
-- stderr.golden --
-./.: missing 'gno.mod' file (code=1).
+./.: parsing gno.mod at ./.: gno.mod file not found in current or any parent directory (code=1)
diff --git a/gnovm/cmd/gno/testdata/gno_lint/not_declared.txtar b/gnovm/cmd/gno/testdata/lint/not_declared.txtar
similarity index 55%
rename from gnovm/cmd/gno/testdata/gno_lint/not_declared.txtar
rename to gnovm/cmd/gno/testdata/lint/not_declared.txtar
index 7bd74a34855..ac56b27e0df 100644
--- a/gnovm/cmd/gno/testdata/gno_lint/not_declared.txtar
+++ b/gnovm/cmd/gno/testdata/lint/not_declared.txtar
@@ -8,13 +8,15 @@ cmp stderr stderr.golden
-- bad_file.gno --
package main
-import "fmt"
-
func main() {
- hello.Foo()
- fmt.Println("Hello", 42)
+ hello.Foo()
+ println("Hello", 42)
}
+-- gno.mod --
+module gno.land/p/demo/hello
+
-- stdout.golden --
-- stderr.golden --
-bad_file.gno:6: name hello not declared (code=2).
+bad_file.gno:4:2: undefined: hello (code=4)
+bad_file.gno:4:2: name hello not declared (code=2)
diff --git a/gnovm/cmd/gno/testdata/gno_test/dir_not_exist.txtar b/gnovm/cmd/gno/testdata/test/dir_not_exist.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/dir_not_exist.txtar
rename to gnovm/cmd/gno/testdata/test/dir_not_exist.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/empty_dir.txtar b/gnovm/cmd/gno/testdata/test/empty_dir.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/empty_dir.txtar
rename to gnovm/cmd/gno/testdata/test/empty_dir.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/empty_gno1.txtar b/gnovm/cmd/gno/testdata/test/empty_gno1.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/empty_gno1.txtar
rename to gnovm/cmd/gno/testdata/test/empty_gno1.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/empty_gno2.txtar b/gnovm/cmd/gno/testdata/test/empty_gno2.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/empty_gno2.txtar
rename to gnovm/cmd/gno/testdata/test/empty_gno2.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/empty_gno3.txtar b/gnovm/cmd/gno/testdata/test/empty_gno3.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/empty_gno3.txtar
rename to gnovm/cmd/gno/testdata/test/empty_gno3.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/error_correct.txtar b/gnovm/cmd/gno/testdata/test/error_correct.txtar
similarity index 86%
rename from gnovm/cmd/gno/testdata/gno_test/error_correct.txtar
rename to gnovm/cmd/gno/testdata/test/error_correct.txtar
index 20a399881be..f9ce4dd9028 100644
--- a/gnovm/cmd/gno/testdata/gno_test/error_correct.txtar
+++ b/gnovm/cmd/gno/testdata/test/error_correct.txtar
@@ -2,7 +2,6 @@
gno test -v .
-stdout 'Machine\.RunMain\(\) panic: oups'
stderr '=== RUN file/x_filetest.gno'
stderr '--- PASS: file/x_filetest.gno \(\d\.\d\ds\)'
stderr 'ok \. \d\.\d\ds'
diff --git a/gnovm/cmd/gno/testdata/gno_test/error_incorrect.txtar b/gnovm/cmd/gno/testdata/test/error_incorrect.txtar
similarity index 62%
rename from gnovm/cmd/gno/testdata/gno_test/error_incorrect.txtar
rename to gnovm/cmd/gno/testdata/test/error_incorrect.txtar
index 00737d8dd67..621397d8d1f 100644
--- a/gnovm/cmd/gno/testdata/gno_test/error_incorrect.txtar
+++ b/gnovm/cmd/gno/testdata/test/error_incorrect.txtar
@@ -2,9 +2,10 @@
! gno test -v .
-stdout 'Machine\.RunMain\(\) panic: oups'
stderr '=== RUN file/x_filetest.gno'
-stderr 'panic: fail on x_filetest.gno: got "oups", want: "xxx"'
+stderr 'Error diff:'
+stderr '-xxx'
+stderr '\+oups'
-- x_filetest.gno --
package main
diff --git a/gnovm/cmd/gno/testdata/gno_test/error_sync.txtar b/gnovm/cmd/gno/testdata/test/error_sync.txtar
similarity index 72%
rename from gnovm/cmd/gno/testdata/gno_test/error_sync.txtar
rename to gnovm/cmd/gno/testdata/test/error_sync.txtar
index e2b67cb3333..067489c41f2 100644
--- a/gnovm/cmd/gno/testdata/gno_test/error_sync.txtar
+++ b/gnovm/cmd/gno/testdata/test/error_sync.txtar
@@ -3,9 +3,9 @@
# by the '-update-golden-tests' flag. The Error is only updated when it is
# empty.
-! gno test -v .
+gno test -update-golden-tests -v .
-stdout 'Machine\.RunMain\(\) panic: oups'
+! stdout .+
stderr '=== RUN file/x_filetest.gno'
cmp x_filetest.gno x_filetest.gno.golden
@@ -18,7 +18,6 @@ func main() {
}
// Error:
-
-- x_filetest.gno.golden --
package main
@@ -28,5 +27,3 @@ func main() {
// Error:
// oups
-// *** CHECK THE ERR MESSAGES ABOVE, MAKE SURE IT'S WHAT YOU EXPECTED, DELETE THIS LINE AND RUN TEST AGAIN ***
-
diff --git a/gnovm/cmd/gno/testdata/gno_test/failing_filetest.txtar b/gnovm/cmd/gno/testdata/test/failing_filetest.txtar
similarity index 65%
rename from gnovm/cmd/gno/testdata/gno_test/failing_filetest.txtar
rename to gnovm/cmd/gno/testdata/test/failing_filetest.txtar
index 91431e4f7bb..7b57729ee91 100644
--- a/gnovm/cmd/gno/testdata/gno_test/failing_filetest.txtar
+++ b/gnovm/cmd/gno/testdata/test/failing_filetest.txtar
@@ -2,9 +2,8 @@
! gno test -v .
-stdout 'Machine.RunMain\(\) panic: beep boop'
stderr '=== RUN file/failing_filetest.gno'
-stderr 'panic: fail on failing_filetest.gno: got unexpected error: beep boop'
+stderr 'unexpected panic: beep boop'
-- failing.gno --
package failing
diff --git a/gnovm/cmd/gno/testdata/gno_test/failing_test.txtar b/gnovm/cmd/gno/testdata/test/failing_test.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/failing_test.txtar
rename to gnovm/cmd/gno/testdata/test/failing_test.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/filetest_events.txtar b/gnovm/cmd/gno/testdata/test/filetest_events.txtar
similarity index 98%
rename from gnovm/cmd/gno/testdata/gno_test/filetest_events.txtar
rename to gnovm/cmd/gno/testdata/test/filetest_events.txtar
index 0236872e78a..34da5fe2ff0 100644
--- a/gnovm/cmd/gno/testdata/gno_test/filetest_events.txtar
+++ b/gnovm/cmd/gno/testdata/test/filetest_events.txtar
@@ -7,7 +7,7 @@ stderr 'ok \. \d\.\d\ds'
gno test -print-events -v .
-! stdout .+
+stdout 'test'
stderr '=== RUN file/valid_filetest.gno'
stderr '--- PASS: file/valid_filetest.gno \(\d\.\d\ds\)'
stderr 'ok \. \d\.\d\ds'
diff --git a/gnovm/cmd/gno/testdata/gno_test/flag_print-runtime-metrics.txtar b/gnovm/cmd/gno/testdata/test/flag_print-runtime-metrics.txtar
similarity index 75%
rename from gnovm/cmd/gno/testdata/gno_test/flag_print-runtime-metrics.txtar
rename to gnovm/cmd/gno/testdata/test/flag_print-runtime-metrics.txtar
index e065d00d55a..99747a0a241 100644
--- a/gnovm/cmd/gno/testdata/gno_test/flag_print-runtime-metrics.txtar
+++ b/gnovm/cmd/gno/testdata/test/flag_print-runtime-metrics.txtar
@@ -3,7 +3,7 @@
gno test --print-runtime-metrics .
! stdout .+
-stderr '--- runtime: cycle=[\d\.kM]+ imports=\d+ allocs=[\d\.kM]+\(\d\.\d\d%\)'
+stderr '--- runtime: cycle=[\d\.kM]+ allocs=[\d\.kM]+\(\d\.\d\d%\)'
-- metrics.gno --
package metrics
@@ -20,4 +20,3 @@ func TestTimeout(t *testing.T) {
println("plop")
}
}
-
diff --git a/gnovm/cmd/gno/testdata/gno_test/flag_run.txtar b/gnovm/cmd/gno/testdata/test/flag_run.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/flag_run.txtar
rename to gnovm/cmd/gno/testdata/test/flag_run.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/flag_timeout.txtar b/gnovm/cmd/gno/testdata/test/flag_timeout.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/flag_timeout.txtar
rename to gnovm/cmd/gno/testdata/test/flag_timeout.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/fmt_write_import.txtar b/gnovm/cmd/gno/testdata/test/fmt_write_import.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/fmt_write_import.txtar
rename to gnovm/cmd/gno/testdata/test/fmt_write_import.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/minim1.txtar b/gnovm/cmd/gno/testdata/test/minim1.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/minim1.txtar
rename to gnovm/cmd/gno/testdata/test/minim1.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/minim2.txtar b/gnovm/cmd/gno/testdata/test/minim2.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/minim2.txtar
rename to gnovm/cmd/gno/testdata/test/minim2.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/minim3.txtar b/gnovm/cmd/gno/testdata/test/minim3.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/minim3.txtar
rename to gnovm/cmd/gno/testdata/test/minim3.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/multitest_events.txtar b/gnovm/cmd/gno/testdata/test/multitest_events.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/multitest_events.txtar
rename to gnovm/cmd/gno/testdata/test/multitest_events.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/no_args.txtar b/gnovm/cmd/gno/testdata/test/no_args.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/no_args.txtar
rename to gnovm/cmd/gno/testdata/test/no_args.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/output_correct.txtar b/gnovm/cmd/gno/testdata/test/output_correct.txtar
similarity index 88%
rename from gnovm/cmd/gno/testdata/gno_test/output_correct.txtar
rename to gnovm/cmd/gno/testdata/test/output_correct.txtar
index e734dad7934..a8aa878e0a4 100644
--- a/gnovm/cmd/gno/testdata/gno_test/output_correct.txtar
+++ b/gnovm/cmd/gno/testdata/test/output_correct.txtar
@@ -2,7 +2,8 @@
gno test -v .
-! stdout .+ # stdout should be empty
+stdout 'hey'
+stdout 'hru?'
stderr '=== RUN file/x_filetest.gno'
stderr '--- PASS: file/x_filetest.gno \(\d\.\d\ds\)'
stderr 'ok \. \d\.\d\ds'
diff --git a/gnovm/cmd/gno/testdata/gno_test/output_incorrect.txtar b/gnovm/cmd/gno/testdata/test/output_incorrect.txtar
similarity index 70%
rename from gnovm/cmd/gno/testdata/gno_test/output_incorrect.txtar
rename to gnovm/cmd/gno/testdata/test/output_incorrect.txtar
index 009d09623a0..60a38933d47 100644
--- a/gnovm/cmd/gno/testdata/gno_test/output_incorrect.txtar
+++ b/gnovm/cmd/gno/testdata/test/output_incorrect.txtar
@@ -1,13 +1,14 @@
# Test Output instruction incorrect
+# with -v, stdout should contain output (unmodified).
! gno test -v .
-! stdout .+ # stdout should be empty
+stdout 'hey'
+
stderr '=== RUN file/x_filetest.gno'
-stderr 'panic: fail on x_filetest.gno: diff:'
stderr '--- Expected'
stderr '\+\+\+ Actual'
-stderr '@@ -1,2 \+1 @@'
+stderr '@@ -1,3 \+1,2 @@'
stderr 'hey'
stderr '-hru?'
diff --git a/gnovm/cmd/gno/testdata/gno_test/output_sync.txtar b/gnovm/cmd/gno/testdata/test/output_sync.txtar
similarity index 92%
rename from gnovm/cmd/gno/testdata/gno_test/output_sync.txtar
rename to gnovm/cmd/gno/testdata/test/output_sync.txtar
index 45e6e5c79be..45385a7eef9 100644
--- a/gnovm/cmd/gno/testdata/gno_test/output_sync.txtar
+++ b/gnovm/cmd/gno/testdata/test/output_sync.txtar
@@ -2,7 +2,9 @@
gno test -v . -update-golden-tests
-! stdout .+ # stdout should be empty
+stdout 'hey'
+stdout '^hru\?'
+
stderr '=== RUN file/x_filetest.gno'
stderr '--- PASS: file/x_filetest.gno \(\d\.\d\ds\)'
stderr 'ok \. \d\.\d\ds'
@@ -19,7 +21,6 @@ func main() {
// Output:
// hey
-
-- x_filetest.gno.golden --
package main
@@ -31,4 +32,3 @@ func main() {
// Output:
// hey
// hru?
-
diff --git a/gnovm/cmd/gno/testdata/gno_test/panic.txtar b/gnovm/cmd/gno/testdata/test/panic.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/panic.txtar
rename to gnovm/cmd/gno/testdata/test/panic.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/pkg_underscore_test.txtar b/gnovm/cmd/gno/testdata/test/pkg_underscore_test.txtar
similarity index 97%
rename from gnovm/cmd/gno/testdata/gno_test/pkg_underscore_test.txtar
rename to gnovm/cmd/gno/testdata/test/pkg_underscore_test.txtar
index b38683adf81..7d204bdb98d 100644
--- a/gnovm/cmd/gno/testdata/gno_test/pkg_underscore_test.txtar
+++ b/gnovm/cmd/gno/testdata/test/pkg_underscore_test.txtar
@@ -66,4 +66,5 @@ func main() {
println("filetest " + hello.Name)
}
-// Output: filetest foo
+// Output:
+// filetest foo
diff --git a/gnovm/cmd/gno/testdata/gno_test/realm_boundmethod.txtar b/gnovm/cmd/gno/testdata/test/realm_boundmethod.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/realm_boundmethod.txtar
rename to gnovm/cmd/gno/testdata/test/realm_boundmethod.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/realm_correct.txtar b/gnovm/cmd/gno/testdata/test/realm_correct.txtar
similarity index 79%
rename from gnovm/cmd/gno/testdata/gno_test/realm_correct.txtar
rename to gnovm/cmd/gno/testdata/test/realm_correct.txtar
index 99e6fccd42d..ced183bec67 100644
--- a/gnovm/cmd/gno/testdata/gno_test/realm_correct.txtar
+++ b/gnovm/cmd/gno/testdata/test/realm_correct.txtar
@@ -8,8 +8,8 @@ stderr '--- PASS: file/x_filetest.gno \(\d\.\d\ds\)'
stderr 'ok \. \d\.\d\ds'
-- x_filetest.gno --
-// PKGPATH: gno.land/r/x
-package x
+// PKGPATH: gno.land/r/xx
+package xx
var x int
@@ -18,11 +18,11 @@ func main() {
}
// Realm:
-// switchrealm["gno.land/r/x"]
-// u[58cde29876a8d185e30c727361981efb068f4726:2]={
+// switchrealm["gno.land/r/xx"]
+// u[aea84df38908f9569d0f552575606e6e6e7e22dd:2]={
// "Blank": {},
// "ObjectInfo": {
-// "ID": "58cde29876a8d185e30c727361981efb068f4726:2",
+// "ID": "aea84df38908f9569d0f552575606e6e6e7e22dd:2",
// "IsEscaped": true,
// "ModTime": "3",
// "RefCount": "2"
@@ -35,7 +35,7 @@ func main() {
// "Column": "0",
// "File": "",
// "Line": "0",
-// "PkgPath": "gno.land/r/x"
+// "PkgPath": "gno.land/r/xx"
// }
// },
// "Values": [
@@ -57,22 +57,22 @@ func main() {
// "Closure": {
// "@type": "/gno.RefValue",
// "Escaped": true,
-// "ObjectID": "58cde29876a8d185e30c727361981efb068f4726:3"
+// "ObjectID": "aea84df38908f9569d0f552575606e6e6e7e22dd:3"
// },
-// "FileName": "main.gno",
+// "FileName": "x.gno",
// "IsMethod": false,
// "Name": "main",
// "NativeName": "",
// "NativePkg": "",
-// "PkgPath": "gno.land/r/x",
+// "PkgPath": "gno.land/r/xx",
// "Source": {
// "@type": "/gno.RefNode",
// "BlockNode": null,
// "Location": {
// "Column": "1",
-// "File": "main.gno",
+// "File": "x.gno",
// "Line": "6",
-// "PkgPath": "gno.land/r/x"
+// "PkgPath": "gno.land/r/xx"
// }
// },
// "Type": {
@@ -84,4 +84,3 @@ func main() {
// }
// ]
// }
-
diff --git a/gnovm/cmd/gno/testdata/gno_test/realm_incorrect.txtar b/gnovm/cmd/gno/testdata/test/realm_incorrect.txtar
similarity index 62%
rename from gnovm/cmd/gno/testdata/gno_test/realm_incorrect.txtar
rename to gnovm/cmd/gno/testdata/test/realm_incorrect.txtar
index 6dfd6d70bb9..234d0f81e77 100644
--- a/gnovm/cmd/gno/testdata/gno_test/realm_incorrect.txtar
+++ b/gnovm/cmd/gno/testdata/test/realm_incorrect.txtar
@@ -4,16 +4,17 @@
! stdout .+ # stdout should be empty
stderr '=== RUN file/x_filetest.gno'
-stderr 'panic: fail on x_filetest.gno: diff:'
+stderr 'Realm diff:'
stderr '--- Expected'
stderr '\+\+\+ Actual'
-stderr '@@ -1 \+1,66 @@'
+stderr '@@ -1,2 \+1,67 @@'
stderr '-xxx'
-stderr '\+switchrealm\["gno.land/r/x"\]'
+stderr '\+switchrealm\["gno.land/r/xx"\]'
+stderr 'x_filetest.gno failed'
-- x_filetest.gno --
-// PKGPATH: gno.land/r/x
-package x
+// PKGPATH: gno.land/r/xx
+package xx
var x int
@@ -23,4 +24,3 @@ func main() {
// Realm:
// xxxx
-
diff --git a/gnovm/cmd/gno/testdata/gno_test/realm_sync.txtar b/gnovm/cmd/gno/testdata/test/realm_sync.txtar
similarity index 79%
rename from gnovm/cmd/gno/testdata/gno_test/realm_sync.txtar
rename to gnovm/cmd/gno/testdata/test/realm_sync.txtar
index 3d27ab4fde0..c93e6d86e8f 100644
--- a/gnovm/cmd/gno/testdata/gno_test/realm_sync.txtar
+++ b/gnovm/cmd/gno/testdata/test/realm_sync.txtar
@@ -10,8 +10,8 @@ stderr 'ok \. \d\.\d\ds'
cmp x_filetest.gno x_filetest.gno.golden
-- x_filetest.gno --
-// PKGPATH: gno.land/r/x
-package x
+// PKGPATH: gno.land/r/xx
+package xx
var x int
@@ -21,10 +21,9 @@ func main() {
// Realm:
// xxx
-
-- x_filetest.gno.golden --
-// PKGPATH: gno.land/r/x
-package x
+// PKGPATH: gno.land/r/xx
+package xx
var x int
@@ -33,11 +32,11 @@ func main() {
}
// Realm:
-// switchrealm["gno.land/r/x"]
-// u[58cde29876a8d185e30c727361981efb068f4726:2]={
+// switchrealm["gno.land/r/xx"]
+// u[aea84df38908f9569d0f552575606e6e6e7e22dd:2]={
// "Blank": {},
// "ObjectInfo": {
-// "ID": "58cde29876a8d185e30c727361981efb068f4726:2",
+// "ID": "aea84df38908f9569d0f552575606e6e6e7e22dd:2",
// "IsEscaped": true,
// "ModTime": "3",
// "RefCount": "2"
@@ -50,7 +49,7 @@ func main() {
// "Column": "0",
// "File": "",
// "Line": "0",
-// "PkgPath": "gno.land/r/x"
+// "PkgPath": "gno.land/r/xx"
// }
// },
// "Values": [
@@ -72,22 +71,22 @@ func main() {
// "Closure": {
// "@type": "/gno.RefValue",
// "Escaped": true,
-// "ObjectID": "58cde29876a8d185e30c727361981efb068f4726:3"
+// "ObjectID": "aea84df38908f9569d0f552575606e6e6e7e22dd:3"
// },
-// "FileName": "main.gno",
+// "FileName": "x.gno",
// "IsMethod": false,
// "Name": "main",
// "NativeName": "",
// "NativePkg": "",
-// "PkgPath": "gno.land/r/x",
+// "PkgPath": "gno.land/r/xx",
// "Source": {
// "@type": "/gno.RefNode",
// "BlockNode": null,
// "Location": {
// "Column": "1",
-// "File": "main.gno",
+// "File": "x.gno",
// "Line": "6",
-// "PkgPath": "gno.land/r/x"
+// "PkgPath": "gno.land/r/xx"
// }
// },
// "Type": {
@@ -99,4 +98,3 @@ func main() {
// }
// ]
// }
-
diff --git a/gnovm/cmd/gno/testdata/gno_test/recover.txtar b/gnovm/cmd/gno/testdata/test/recover.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/recover.txtar
rename to gnovm/cmd/gno/testdata/test/recover.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_test/skip.txtar b/gnovm/cmd/gno/testdata/test/skip.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/skip.txtar
rename to gnovm/cmd/gno/testdata/test/skip.txtar
diff --git a/gnovm/cmd/gno/testdata/test/unknown_package.txtar b/gnovm/cmd/gno/testdata/test/unknown_package.txtar
new file mode 100644
index 00000000000..0611d3440a4
--- /dev/null
+++ b/gnovm/cmd/gno/testdata/test/unknown_package.txtar
@@ -0,0 +1,24 @@
+# Test for loading an unknown package
+
+! gno test -v .
+
+! stdout .+
+stderr 'contract.gno:3:8: unknown import path foobarbaz'
+
+-- contract.gno --
+package contract
+
+import "foobarbaz"
+
+func Foo() {
+ _ = foobarbaz.Gnognogno
+}
+
+-- contract_test.gno --
+package contract
+
+import "testing"
+
+func TestFoo(t *testing.T) {
+ Foo()
+}
diff --git a/gnovm/cmd/gno/testdata/gno_test/valid_filetest.txtar b/gnovm/cmd/gno/testdata/test/valid_filetest.txtar
similarity index 96%
rename from gnovm/cmd/gno/testdata/gno_test/valid_filetest.txtar
rename to gnovm/cmd/gno/testdata/test/valid_filetest.txtar
index 02ae3f72304..4e24ad9ab08 100644
--- a/gnovm/cmd/gno/testdata/gno_test/valid_filetest.txtar
+++ b/gnovm/cmd/gno/testdata/test/valid_filetest.txtar
@@ -7,7 +7,7 @@ stderr 'ok \. \d\.\d\ds'
gno test -v .
-! stdout .+
+stdout 'test'
stderr '=== RUN file/valid_filetest.gno'
stderr '--- PASS: file/valid_filetest.gno \(\d\.\d\ds\)'
stderr 'ok \. \d\.\d\ds'
diff --git a/gnovm/cmd/gno/testdata/gno_test/valid_test.txtar b/gnovm/cmd/gno/testdata/test/valid_test.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_test/valid_test.txtar
rename to gnovm/cmd/gno/testdata/test/valid_test.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_transpile/gobuild_flag_build_error.txtar b/gnovm/cmd/gno/testdata/transpile/gobuild_flag_build_error.txtar
similarity index 78%
rename from gnovm/cmd/gno/testdata/gno_transpile/gobuild_flag_build_error.txtar
rename to gnovm/cmd/gno/testdata/transpile/gobuild_flag_build_error.txtar
index d21390f9472..145fe796c09 100644
--- a/gnovm/cmd/gno/testdata/gno_transpile/gobuild_flag_build_error.txtar
+++ b/gnovm/cmd/gno/testdata/transpile/gobuild_flag_build_error.txtar
@@ -1,10 +1,11 @@
# Run gno transpile with -gobuild flag
+# The error messages changed sometime in go1.23, so this avoids errors
! gno transpile -gobuild .
! stdout .+
-stderr '^main.gno:4:6: x declared and not used$'
-stderr '^main.gno:5:6: y declared and not used$'
+stderr '^main.gno:4:6: .*declared and not used'
+stderr '^main.gno:5:6: .*declared and not used'
stderr '^2 transpile error\(s\)$'
cmp main.gno.gen.go main.gno.gen.go.golden
diff --git a/gnovm/cmd/gno/testdata/gno_transpile/gobuild_flag_parse_error.txtar b/gnovm/cmd/gno/testdata/transpile/gobuild_flag_parse_error.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_transpile/gobuild_flag_parse_error.txtar
rename to gnovm/cmd/gno/testdata/transpile/gobuild_flag_parse_error.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_transpile/invalid_import.txtar b/gnovm/cmd/gno/testdata/transpile/invalid_import.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_transpile/invalid_import.txtar
rename to gnovm/cmd/gno/testdata/transpile/invalid_import.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_transpile/no_args.txtar b/gnovm/cmd/gno/testdata/transpile/no_args.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_transpile/no_args.txtar
rename to gnovm/cmd/gno/testdata/transpile/no_args.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_transpile/parse_error.txtar b/gnovm/cmd/gno/testdata/transpile/parse_error.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_transpile/parse_error.txtar
rename to gnovm/cmd/gno/testdata/transpile/parse_error.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_transpile/valid_empty_dir.txtar b/gnovm/cmd/gno/testdata/transpile/valid_empty_dir.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_transpile/valid_empty_dir.txtar
rename to gnovm/cmd/gno/testdata/transpile/valid_empty_dir.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_transpile/valid_gobuild_file.txtar b/gnovm/cmd/gno/testdata/transpile/valid_gobuild_file.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_transpile/valid_gobuild_file.txtar
rename to gnovm/cmd/gno/testdata/transpile/valid_gobuild_file.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_transpile/valid_gobuild_flag.txtar b/gnovm/cmd/gno/testdata/transpile/valid_gobuild_flag.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_transpile/valid_gobuild_flag.txtar
rename to gnovm/cmd/gno/testdata/transpile/valid_gobuild_flag.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_transpile/valid_output_flag.txtar b/gnovm/cmd/gno/testdata/transpile/valid_output_flag.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_transpile/valid_output_flag.txtar
rename to gnovm/cmd/gno/testdata/transpile/valid_output_flag.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_transpile/valid_output_gobuild.txtar b/gnovm/cmd/gno/testdata/transpile/valid_output_gobuild.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_transpile/valid_output_gobuild.txtar
rename to gnovm/cmd/gno/testdata/transpile/valid_output_gobuild.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_transpile/valid_transpile_file.txtar b/gnovm/cmd/gno/testdata/transpile/valid_transpile_file.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_transpile/valid_transpile_file.txtar
rename to gnovm/cmd/gno/testdata/transpile/valid_transpile_file.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_transpile/valid_transpile_package.txtar b/gnovm/cmd/gno/testdata/transpile/valid_transpile_package.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_transpile/valid_transpile_package.txtar
rename to gnovm/cmd/gno/testdata/transpile/valid_transpile_package.txtar
diff --git a/gnovm/cmd/gno/testdata/gno_transpile/valid_transpile_tree.txtar b/gnovm/cmd/gno/testdata/transpile/valid_transpile_tree.txtar
similarity index 100%
rename from gnovm/cmd/gno/testdata/gno_transpile/valid_transpile_tree.txtar
rename to gnovm/cmd/gno/testdata/transpile/valid_transpile_tree.txtar
diff --git a/gnovm/cmd/gno/testdata_test.go b/gnovm/cmd/gno/testdata_test.go
index 15bc8d96e26..6b1bbd1d459 100644
--- a/gnovm/cmd/gno/testdata_test.go
+++ b/gnovm/cmd/gno/testdata_test.go
@@ -24,7 +24,6 @@ func Test_Scripts(t *testing.T) {
}
name := dir.Name()
- t.Logf("testing: %s", name)
t.Run(name, func(t *testing.T) {
updateScripts, _ := strconv.ParseBool(os.Getenv("UPDATE_SCRIPTS"))
p := testscript.Params{
diff --git a/gnovm/cmd/gno/transpile_test.go b/gnovm/cmd/gno/transpile_test.go
index 827c09e23f1..5a03ddc7657 100644
--- a/gnovm/cmd/gno/transpile_test.go
+++ b/gnovm/cmd/gno/transpile_test.go
@@ -6,29 +6,9 @@ import (
"strconv"
"testing"
- "github.com/rogpeppe/go-internal/testscript"
"github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-
- "github.com/gnolang/gno/gnovm/pkg/integration"
)
-func Test_ScriptsTranspile(t *testing.T) {
- p := testscript.Params{
- Dir: "testdata/gno_transpile",
- }
-
- if coverdir, ok := integration.ResolveCoverageDir(); ok {
- err := integration.SetupTestscriptsCoverage(&p, coverdir)
- require.NoError(t, err)
- }
-
- err := integration.SetupGno(&p, t.TempDir())
- require.NoError(t, err)
-
- testscript.Run(t, p)
-}
-
func Test_parseGoBuildErrors(t *testing.T) {
t.Parallel()
diff --git a/gnovm/cmd/gno/util.go b/gnovm/cmd/gno/util.go
index 90aedd5d27a..697aa94b3c6 100644
--- a/gnovm/cmd/gno/util.go
+++ b/gnovm/cmd/gno/util.go
@@ -338,17 +338,3 @@ func copyFile(src, dst string) error {
return nil
}
-
-// Adapted from https://yourbasic.org/golang/formatting-byte-size-to-human-readable-format/
-func prettySize(nb int64) string {
- const unit = 1000
- if nb < unit {
- return fmt.Sprintf("%d", nb)
- }
- div, exp := int64(unit), 0
- for n := nb / unit; n >= unit; n /= unit {
- div *= unit
- exp++
- }
- return fmt.Sprintf("%.1f%c", float64(nb)/float64(div), "kMGTPE"[exp])
-}
diff --git a/gnovm/memfile.go b/gnovm/memfile.go
index a37bba6ef3d..6988c893dd7 100644
--- a/gnovm/memfile.go
+++ b/gnovm/memfile.go
@@ -41,7 +41,7 @@ const pathLengthLimit = 256
var (
rePkgName = regexp.MustCompile(`^[a-z][a-z0-9_]*$`)
- rePkgOrRlmPath = regexp.MustCompile(`^gno\.land\/(?:p|r)(?:\/_?[a-z]+[a-z0-9_]*)+$`)
+ rePkgOrRlmPath = regexp.MustCompile(`^([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.[a-zA-Z]{2,}\/(?:p|r)(?:\/_?[a-z]+[a-z0-9_]*)+$`)
reFileName = regexp.MustCompile(`^([a-zA-Z0-9_]*\.[a-z0-9_\.]*|LICENSE|README)$`)
)
diff --git a/gnovm/memfile_test.go b/gnovm/memfile_test.go
index c93c251b0e7..5ef70e9e868 100644
--- a/gnovm/memfile_test.go
+++ b/gnovm/memfile_test.go
@@ -158,13 +158,13 @@ func TestMemPackage_Validate(t *testing.T) {
"invalid package/realm path",
},
{
- "Invalid path",
+ "Custom domain",
&MemPackage{
Name: "hey",
Path: "github.com/p/path/path",
Files: []*MemFile{{Name: "a.gno"}},
},
- "invalid package/realm path",
+ "",
},
{
"Special character",
diff --git a/gnovm/pkg/doc/dirs.go b/gnovm/pkg/doc/dirs.go
index 19d312f6826..eadbec7d464 100644
--- a/gnovm/pkg/doc/dirs.go
+++ b/gnovm/pkg/doc/dirs.go
@@ -9,10 +9,15 @@ import (
"os"
"path"
"path/filepath"
+ "slices"
"sort"
"strings"
+ "github.com/gnolang/gno/gnovm"
+ "github.com/gnolang/gno/gnovm/pkg/gnolang"
"github.com/gnolang/gno/gnovm/pkg/gnomod"
+ "github.com/gnolang/gno/gnovm/pkg/packages"
+ "golang.org/x/mod/module"
)
// A bfsDir describes a directory holding code by specifying
@@ -60,25 +65,27 @@ func newDirs(dirs []string, modDirs []string) *bfsDirs {
dir: mdir,
importPath: gm.Module.Mod.Path,
})
- roots = append(roots, getGnoModDirs(gm)...)
+ roots = append(roots, getGnoModDirs(gm, mdir)...)
}
go d.walk(roots)
return d
}
-func getGnoModDirs(gm *gnomod.File) []bfsDir {
+func getGnoModDirs(gm *gnomod.File, root string) []bfsDir {
// cmd/go makes use of the go list command, we don't have that here.
- dirs := make([]bfsDir, 0, len(gm.Require))
- for _, r := range gm.Require {
- mv := gm.Resolve(r)
+ imports := packageImportsRecursive(root, gm.Module.Mod.Path)
+
+ dirs := make([]bfsDir, 0, len(imports))
+ for _, r := range imports {
+ mv := gm.Resolve(module.Version{Path: r})
path := gnomod.PackageDir("", mv)
if _, err := os.Stat(path); err != nil {
// only give directories which actually exist and don't give
// an error when accessing
if !os.IsNotExist(err) {
- log.Println("open source directories from gno.mod:", err)
+ log.Println("open source directories from import:", err)
}
continue
}
@@ -91,6 +98,45 @@ func getGnoModDirs(gm *gnomod.File) []bfsDir {
return dirs
}
+func packageImportsRecursive(root string, pkgPath string) []string {
+ pkg, err := gnolang.ReadMemPackage(root, pkgPath)
+ if err != nil {
+ // ignore invalid packages
+ pkg = &gnovm.MemPackage{}
+ }
+
+ res, err := packages.Imports(pkg)
+ if err != nil {
+ // ignore packages with invalid imports
+ res = nil
+ }
+
+ entries, err := os.ReadDir(root)
+ if err != nil {
+ // ignore unreadable dirs
+ entries = nil
+ }
+
+ for _, entry := range entries {
+ if !entry.IsDir() {
+ continue
+ }
+
+ dirName := entry.Name()
+ sub := packageImportsRecursive(filepath.Join(root, dirName), path.Join(pkgPath, dirName))
+
+ for _, imp := range sub {
+ if !slices.Contains(res, imp) {
+ res = append(res, imp)
+ }
+ }
+ }
+
+ sort.Strings(res)
+
+ return res
+}
+
// Reset puts the scan back at the beginning.
func (d *bfsDirs) Reset() {
d.offset = 0
diff --git a/gnovm/pkg/doc/dirs_test.go b/gnovm/pkg/doc/dirs_test.go
index 8659f3cbfcb..3139298a7ae 100644
--- a/gnovm/pkg/doc/dirs_test.go
+++ b/gnovm/pkg/doc/dirs_test.go
@@ -63,18 +63,9 @@ func TestNewDirs_invalidModDir(t *testing.T) {
func tNewDirs(t *testing.T) (string, *bfsDirs) {
t.Helper()
- // modify GNO_HOME to testdata/dirsdep -- this allows us to test
+ // modify GNOHOME to testdata/dirsdep -- this allows us to test
// dependency lookup by dirs.
- old, ex := os.LookupEnv("GNO_HOME")
- os.Setenv("GNO_HOME", wdJoin(t, "testdata/dirsdep"))
-
- t.Cleanup(func() {
- if ex {
- os.Setenv("GNO_HOME", old)
- } else {
- os.Unsetenv("GNO_HOME")
- }
- })
+ t.Setenv("GNOHOME", wdJoin(t, "testdata/dirsdep"))
return wdJoin(t, "testdata"),
newDirs([]string{wdJoin(t, "testdata/dirs")}, []string{wdJoin(t, "testdata/dirsmod")})
diff --git a/gnovm/pkg/doc/testdata/dirsmod/a.gno b/gnovm/pkg/doc/testdata/dirsmod/a.gno
new file mode 100644
index 00000000000..ee57c47dff5
--- /dev/null
+++ b/gnovm/pkg/doc/testdata/dirsmod/a.gno
@@ -0,0 +1,9 @@
+package dirsmod
+
+import (
+ "dirs.mod/dep"
+)
+
+func foo() {
+ dep.Bar()
+}
diff --git a/gnovm/pkg/doc/testdata/dirsmod/gno.mod b/gnovm/pkg/doc/testdata/dirsmod/gno.mod
index 6c8008b958c..34d825571cc 100644
--- a/gnovm/pkg/doc/testdata/dirsmod/gno.mod
+++ b/gnovm/pkg/doc/testdata/dirsmod/gno.mod
@@ -1,5 +1 @@
-module dirs.mod/prefix
-
-require (
- dirs.mod/dep v0.0.0
-)
+module dirs.mod/prefix
\ No newline at end of file
diff --git a/gnovm/pkg/gnolang/debugger_test.go b/gnovm/pkg/gnolang/debugger_test.go
index 63a3ee74675..926ff0595e6 100644
--- a/gnovm/pkg/gnolang/debugger_test.go
+++ b/gnovm/pkg/gnolang/debugger_test.go
@@ -12,7 +12,7 @@ import (
"github.com/gnolang/gno/gnovm/pkg/gnoenv"
"github.com/gnolang/gno/gnovm/pkg/gnolang"
- "github.com/gnolang/gno/gnovm/tests"
+ "github.com/gnolang/gno/gnovm/pkg/test"
)
type dtest struct{ in, out string }
@@ -24,17 +24,12 @@ type writeNopCloser struct{ io.Writer }
func (writeNopCloser) Close() error { return nil }
// TODO (Marc): move evalTest to gnovm/tests package and remove code duplicates
-func evalTest(debugAddr, in, file string) (out, err, stacktrace string) {
+func evalTest(debugAddr, in, file string) (out, err string) {
bout := bytes.NewBufferString("")
berr := bytes.NewBufferString("")
stdin := bytes.NewBufferString(in)
stdout := writeNopCloser{bout}
stderr := writeNopCloser{berr}
- debug := in != "" || debugAddr != ""
- mode := tests.ImportModeStdlibsPreferred
- if strings.HasSuffix(file, "_native.gno") {
- mode = tests.ImportModeNativePreferred
- }
defer func() {
if r := recover(); r != nil {
@@ -44,7 +39,7 @@ func evalTest(debugAddr, in, file string) (out, err, stacktrace string) {
err = strings.TrimSpace(strings.ReplaceAll(err, "../../tests/files/", "files/"))
}()
- testStore := tests.TestStore(gnoenv.RootDir(), "../../tests/files", stdin, stdout, stderr, mode)
+ _, testStore := test.Store(gnoenv.RootDir(), false, stdin, stdout, stderr)
f := gnolang.MustReadFile(file)
@@ -53,23 +48,11 @@ func evalTest(debugAddr, in, file string) (out, err, stacktrace string) {
Input: stdin,
Output: stdout,
Store: testStore,
- Context: tests.TestContext(string(f.PkgName), nil),
- Debug: debug,
+ Context: test.Context(string(f.PkgName), nil),
+ Debug: true,
})
defer m.Release()
- defer func() {
- if r := recover(); r != nil {
- switch r.(type) {
- case gnolang.UnhandledPanicError:
- stacktrace = m.ExceptionsStacktrace()
- default:
- stacktrace = m.Stacktrace().String()
- }
- stacktrace = strings.TrimSpace(strings.ReplaceAll(stacktrace, "../../tests/files/", "files/"))
- panic(r)
- }
- }()
if debugAddr != "" {
if e := m.Debugger.Serve(debugAddr); e != nil {
@@ -81,7 +64,7 @@ func evalTest(debugAddr, in, file string) (out, err, stacktrace string) {
m.RunFiles(f)
ex, _ := gnolang.ParseExpr("main()")
m.Eval(ex)
- out, err, stacktrace = bout.String(), berr.String(), m.ExceptionsStacktrace()
+ out, err = bout.String(), berr.String()
return
}
@@ -90,7 +73,7 @@ func runDebugTest(t *testing.T, targetPath string, tests []dtest) {
for _, test := range tests {
t.Run("", func(t *testing.T) {
- out, err, _ := evalTest("", test.in, targetPath)
+ out, err := evalTest("", test.in, targetPath)
t.Log("in:", test.in, "out:", out, "err:", err)
if !strings.Contains(out, test.out) {
t.Errorf("unexpected output\nwant\"%s\"\n got \"%s\"", test.out, out)
@@ -206,7 +189,7 @@ func TestRemoteDebug(t *testing.T) {
}
func TestRemoteError(t *testing.T) {
- _, err, _ := evalTest(":xxx", "", debugTarget)
+ _, err := evalTest(":xxx", "", debugTarget)
t.Log("err:", err)
if !strings.Contains(err, "tcp/xxx: unknown port") &&
!strings.Contains(err, "tcp/xxx: nodename nor servname provided, or not known") {
diff --git a/gnovm/pkg/gnolang/eval_test.go b/gnovm/pkg/gnolang/eval_test.go
deleted file mode 100644
index 9b83d673767..00000000000
--- a/gnovm/pkg/gnolang/eval_test.go
+++ /dev/null
@@ -1,132 +0,0 @@
-package gnolang_test
-
-import (
- "io/fs"
- "os"
- "path/filepath"
- "regexp"
- "sort"
- "strings"
- "testing"
-)
-
-func TestEvalFiles(t *testing.T) {
- dir := "../../tests/files"
- fsys := os.DirFS(dir)
- err := fs.WalkDir(fsys, ".", func(path string, de fs.DirEntry, err error) error {
- switch {
- case err != nil:
- return err
- case path == "extern":
- return fs.SkipDir
- case de.IsDir():
- return nil
- }
-
- fullPath := filepath.Join(dir, path)
- wantOut, wantErr, wantStacktrace, ok := testData(fullPath)
- if !ok {
- return nil
- }
-
- t.Run(path, func(t *testing.T) {
- out, err, stacktrace := evalTest("", "", fullPath)
-
- if wantErr != "" && !strings.Contains(err, wantErr) ||
- wantErr == "" && err != "" {
- t.Fatalf("unexpected error\nWant: %s\n Got: %s", wantErr, err)
- }
-
- if wantStacktrace != "" && !strings.Contains(stacktrace, wantStacktrace) {
- t.Fatalf("unexpected stacktrace\nWant: %s\n Got: %s", wantStacktrace, stacktrace)
- }
- if wantOut != "" && strings.TrimSpace(out) != strings.TrimSpace(wantOut) {
- t.Fatalf("unexpected output\nWant: \"%s\"\n Got: \"%s\"", wantOut, out)
- }
- })
-
- return nil
- })
- if err != nil {
- t.Fatal(err)
- }
-}
-
-// testData returns the expected output and error string, and true if entry is valid.
-func testData(name string) (testOut, testErr, testStacktrace string, ok bool) {
- if !strings.HasSuffix(name, ".gno") || strings.HasSuffix(name, "_long.gno") {
- return
- }
- buf, err := os.ReadFile(name)
- if err != nil {
- return
- }
- str := string(buf)
- if strings.Contains(str, "// PKGPATH:") {
- return
- }
- res := commentFrom(str, []string{
- "// Output:",
- "// Error:",
- "// Stacktrace:",
- })
-
- return res[0], res[1], res[2], true
-}
-
-type directive struct {
- delim string
- res string
- index int
-}
-
-// (?m) makes ^ and $ match start/end of string.
-// Used to substitute from a comment all the //.
-// Using a regex allows us to parse lines only containing "//" as an empty line.
-var reCommentPrefix = regexp.MustCompile("(?m)^//(?: |$)")
-
-// commentFrom returns the comments from s that are between the delimiters.
-// delims is a list of delimiters like "// Output:", which should be on a
-// single line to mark the beginning of a directive.
-// The return value is the content of each directive, matching the indexes
-// of delims, ie. len(result) == len(delims).
-func commentFrom(s string, delims []string) []string {
- directives := make([]directive, len(delims))
- directivesFound := make([]*directive, 0, len(delims))
-
- // Find directives
- for i, delim := range delims {
- // must find delim isolated on one line
- delim = "\n" + delim + "\n"
- index := strings.Index(s, delim)
- directives[i] = directive{delim: delim, index: index}
- if index >= 0 {
- directivesFound = append(directivesFound, &directives[i])
- }
- }
- sort.Slice(directivesFound, func(i, j int) bool {
- return directivesFound[i].index < directivesFound[j].index
- })
-
- for i := range directivesFound {
- next := len(s)
- if i != len(directivesFound)-1 {
- next = directivesFound[i+1].index
- }
-
- // Mark beginning of directive content from the line after the directive.
- contentStart := directivesFound[i].index + len(directivesFound[i].delim)
- content := s[contentStart:next]
-
- // Remove comment prefixes.
- parsed := reCommentPrefix.ReplaceAllLiteralString(content, "")
- directivesFound[i].res = strings.TrimSuffix(parsed, "\n")
- }
-
- res := make([]string, len(directives))
- for i, d := range directives {
- res[i] = d.res
- }
-
- return res
-}
diff --git a/gnovm/pkg/gnolang/files_test.go b/gnovm/pkg/gnolang/files_test.go
new file mode 100644
index 00000000000..2c82f6d8f29
--- /dev/null
+++ b/gnovm/pkg/gnolang/files_test.go
@@ -0,0 +1,182 @@
+package gnolang_test
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "io"
+ "io/fs"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "github.com/gnolang/gno/gnovm/pkg/gnolang"
+ "github.com/gnolang/gno/gnovm/pkg/test"
+ "github.com/stretchr/testify/require"
+)
+
+var withSync = flag.Bool("update-golden-tests", false, "rewrite tests updating Realm: and Output: with new values where changed")
+
+type nopReader struct{}
+
+func (nopReader) Read(p []byte) (int, error) { return 0, io.EOF }
+
+// TestFiles tests all the files in "gnovm/tests/files".
+//
+// Cheatsheet:
+//
+// fail on the first test:
+// go test -run TestFiles -failfast
+// run a specific test:
+// go test -run TestFiles/addr0b
+// fix a specific test:
+// go test -run TestFiles/'^bin1.gno' -short -v -update-golden-tests .
+func TestFiles(t *testing.T) {
+ t.Parallel()
+
+ rootDir, err := filepath.Abs("../../../")
+ require.NoError(t, err)
+
+ newOpts := func() *test.TestOptions {
+ o := &test.TestOptions{
+ RootDir: rootDir,
+ Output: io.Discard,
+ Error: io.Discard,
+ Sync: *withSync,
+ }
+ o.BaseStore, o.TestStore = test.Store(
+ rootDir, true,
+ nopReader{}, o.WriterForStore(), io.Discard,
+ )
+ return o
+ }
+ // sharedOpts is used for all "short" tests.
+ sharedOpts := newOpts()
+
+ dir := "../../tests/"
+ fsys := os.DirFS(dir)
+ err = fs.WalkDir(fsys, "files", func(path string, de fs.DirEntry, err error) error {
+ switch {
+ case err != nil:
+ return err
+ case path == "files/extern":
+ return fs.SkipDir
+ case de.IsDir():
+ return nil
+ }
+ subTestName := path[len("files/"):]
+ isLong := strings.HasSuffix(path, "_long.gno")
+ if isLong && testing.Short() {
+ t.Run(subTestName, func(t *testing.T) {
+ t.Skip("skipping in -short")
+ })
+ return nil
+ }
+
+ content, err := fs.ReadFile(fsys, path)
+ if err != nil {
+ return err
+ }
+
+ var criticalError error
+ t.Run(subTestName, func(t *testing.T) {
+ opts := sharedOpts
+ if isLong {
+ // Long tests are run in parallel, and with their own store.
+ t.Parallel()
+ opts = newOpts()
+ }
+ changed, err := opts.RunFiletest(path, content)
+ if err != nil {
+ t.Fatal(err.Error())
+ }
+ if changed != "" {
+ err = os.WriteFile(filepath.Join(dir, path), []byte(changed), de.Type())
+ if err != nil {
+ criticalError = fmt.Errorf("could not fix golden file: %w", err)
+ }
+ }
+ })
+
+ return criticalError
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+// TestStdlibs tests all the standard library packages.
+func TestStdlibs(t *testing.T) {
+ t.Parallel()
+
+ rootDir, err := filepath.Abs("../../../")
+ require.NoError(t, err)
+
+ newOpts := func() (capture *bytes.Buffer, opts *test.TestOptions) {
+ var out io.Writer
+ if testing.Verbose() {
+ out = os.Stdout
+ } else {
+ capture = new(bytes.Buffer)
+ out = capture
+ }
+ opts = test.NewTestOptions(rootDir, nopReader{}, out, out)
+ opts.Verbose = true
+ return
+ }
+ sharedCapture, sharedOpts := newOpts()
+
+ dir := "../../stdlibs/"
+ fsys := os.DirFS(dir)
+ err = fs.WalkDir(fsys, ".", func(path string, de fs.DirEntry, err error) error {
+ switch {
+ case err != nil:
+ return err
+ case !de.IsDir() || path == ".":
+ return nil
+ }
+
+ fp := filepath.Join(dir, path)
+ memPkg := gnolang.MustReadMemPackage(fp, path)
+ t.Run(strings.ReplaceAll(memPkg.Path, "/", "-"), func(t *testing.T) {
+ capture, opts := sharedCapture, sharedOpts
+ switch memPkg.Path {
+ // Excluded in short
+ case
+ "bufio",
+ "bytes",
+ "strconv":
+ if testing.Short() {
+ t.Skip("Skipped because of -short, and this stdlib is very long currently.")
+ }
+ fallthrough
+ // Run using separate store, as it's faster
+ case
+ "math/rand",
+ "regexp",
+ "regexp/syntax",
+ "sort":
+ t.Parallel()
+ capture, opts = newOpts()
+ }
+
+ if capture != nil {
+ capture.Reset()
+ }
+
+ err := test.Test(memPkg, "", opts)
+ if !testing.Verbose() {
+ t.Log(capture.String())
+ }
+ if err != nil {
+ t.Error(err)
+ }
+ })
+
+ return nil
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+}
diff --git a/gnovm/pkg/gnolang/go2gno.go b/gnovm/pkg/gnolang/go2gno.go
index 99e051f7913..82d5c69b08b 100644
--- a/gnovm/pkg/gnolang/go2gno.go
+++ b/gnovm/pkg/gnolang/go2gno.go
@@ -39,7 +39,9 @@ import (
"go/token"
"go/types"
"os"
+ "path"
"reflect"
+ "slices"
"strconv"
"strings"
@@ -471,6 +473,8 @@ func Go2Gno(fs *token.FileSet, gon ast.Node) (n Node) {
PkgName: pkgName,
Decls: decls,
}
+ case *ast.EmptyStmt:
+ return &EmptyStmt{}
default:
panic(fmt.Sprintf("unknown Go type %v: %s\n",
reflect.TypeOf(gon),
@@ -497,6 +501,18 @@ type MemPackageGetter interface {
// If format is true, the code will be automatically updated with the
// formatted source code.
func TypeCheckMemPackage(mempkg *gnovm.MemPackage, getter MemPackageGetter, format bool) error {
+ return typeCheckMemPackage(mempkg, getter, false, format)
+}
+
+// TypeCheckMemPackageTest performs the same type checks as [TypeCheckMemPackage],
+// but allows re-declarations.
+//
+// Note: like TypeCheckMemPackage, this function ignores tests and filetests.
+func TypeCheckMemPackageTest(mempkg *gnovm.MemPackage, getter MemPackageGetter) error {
+ return typeCheckMemPackage(mempkg, getter, true, false)
+}
+
+func typeCheckMemPackage(mempkg *gnovm.MemPackage, getter MemPackageGetter, testing, format bool) error {
var errs error
imp := &gnoImporter{
getter: getter,
@@ -506,6 +522,7 @@ func TypeCheckMemPackage(mempkg *gnovm.MemPackage, getter MemPackageGetter, form
errs = multierr.Append(errs, err)
},
},
+ allowRedefinitions: testing,
}
imp.cfg.Importer = imp
@@ -527,6 +544,9 @@ type gnoImporter struct {
getter MemPackageGetter
cache map[string]gnoImporterResult
cfg *types.Config
+
+ // allow symbol redefinitions? (test standard libraries)
+ allowRedefinitions bool
}
// Unused, but satisfies the Importer interface.
@@ -557,22 +577,39 @@ func (g *gnoImporter) ImportFrom(path, _ string, _ types.ImportMode) (*types.Pac
}
func (g *gnoImporter) parseCheckMemPackage(mpkg *gnovm.MemPackage, fmt bool) (*types.Package, error) {
+ // This map is used to allow for function re-definitions, which are allowed
+ // in Gno (testing context) but not in Go.
+ // This map links each function identifier with a closure to remove its
+ // associated declaration.
+ var delFunc map[string]func()
+ if g.allowRedefinitions {
+ delFunc = make(map[string]func())
+ }
+
fset := token.NewFileSet()
files := make([]*ast.File, 0, len(mpkg.Files))
var errs error
for _, file := range mpkg.Files {
+ // Ignore non-gno files.
+ // TODO: support filetest type checking. (should probably handle as each its
+ // own separate pkg, which should also be typechecked)
if !strings.HasSuffix(file.Name, ".gno") ||
- endsWith(file.Name, []string{"_test.gno", "_filetest.gno"}) {
- continue // skip spurious file.
+ strings.HasSuffix(file.Name, "_test.gno") ||
+ strings.HasSuffix(file.Name, "_filetest.gno") {
+ continue
}
const parseOpts = parser.ParseComments | parser.DeclarationErrors | parser.SkipObjectResolution
- f, err := parser.ParseFile(fset, file.Name, file.Body, parseOpts)
+ f, err := parser.ParseFile(fset, path.Join(mpkg.Path, file.Name), file.Body, parseOpts)
if err != nil {
errs = multierr.Append(errs, err)
continue
}
+ if delFunc != nil {
+ deleteOldIdents(delFunc, f)
+ }
+
// enforce formatting
if fmt {
var buf bytes.Buffer
@@ -593,6 +630,24 @@ func (g *gnoImporter) parseCheckMemPackage(mpkg *gnovm.MemPackage, fmt bool) (*t
return g.cfg.Check(mpkg.Path, fset, files, nil)
}
+func deleteOldIdents(idents map[string]func(), f *ast.File) {
+ for _, decl := range f.Decls {
+ fd, ok := decl.(*ast.FuncDecl)
+ if !ok || fd.Recv != nil { // ignore methods
+ continue
+ }
+ if del := idents[fd.Name.Name]; del != nil {
+ del()
+ }
+ decl := decl
+ idents[fd.Name.Name] = func() {
+ // NOTE: cannot use the index as a file may contain multiple decls to be removed,
+ // so removing one would make all "later" indexes wrong.
+ f.Decls = slices.DeleteFunc(f.Decls, func(d ast.Decl) bool { return decl == d })
+ }
+ }
+}
+
//----------------------------------------
// utility methods
diff --git a/gnovm/pkg/gnolang/gonative.go b/gnovm/pkg/gnolang/gonative.go
index fe92f5bcd23..5a39c76b5e1 100644
--- a/gnovm/pkg/gnolang/gonative.go
+++ b/gnovm/pkg/gnolang/gonative.go
@@ -83,11 +83,6 @@ func go2GnoBaseType(rt reflect.Type) Type {
}
}
-// Implements Store.
-func (ds *defaultStore) SetStrictGo2GnoMapping(strict bool) {
- ds.go2gnoStrict = strict
-}
-
// Implements Store.
// See go2GnoValue2(). Like go2GnoType() but also converts any
// top-level complex types (or pointers to them). The result gets
@@ -109,54 +104,9 @@ func (ds *defaultStore) Go2GnoType(rt reflect.Type) (t Type) {
// wrap t with declared type.
pkgPath := rt.PkgPath()
if pkgPath != "" {
- // mappings have been removed, so for any non-builtin type in strict mode,
- // this will panic.
- if ds.go2gnoStrict {
- // mapping failed and strict: error.
- gokey := pkgPath + "." + rt.Name()
- panic(fmt.Sprintf("native type does not exist for %s", gokey))
- }
-
- // generate a new gno type for testing.
- mtvs := []TypedValue(nil)
- if t.Kind() == InterfaceKind {
- // methods already set on t.Methods.
- // *DT.Methods not used in Go for interfaces.
- } else {
- prt := rt
- if rt.Kind() != reflect.Ptr {
- // NOTE: go reflect requires ptr kind
- // for methods with ptr receivers,
- // whereas gno methods are all
- // declared on the *DeclaredType.
- prt = reflect.PointerTo(rt)
- }
- nm := prt.NumMethod()
- mtvs = make([]TypedValue, nm)
- for i := 0; i < nm; i++ {
- mthd := prt.Method(i)
- ft := ds.go2GnoFuncType(mthd.Type)
- fv := &FuncValue{
- Type: ft,
- IsMethod: true,
- Source: nil,
- Name: Name(mthd.Name),
- Closure: nil,
- PkgPath: pkgPath,
- body: nil, // XXX
- nativeBody: nil,
- }
- mtvs[i] = TypedValue{T: ft, V: fv}
- }
- }
- dt := &DeclaredType{
- PkgPath: pkgPath,
- Name: Name(rt.Name()),
- Base: t,
- Methods: mtvs,
- }
- dt.Seal()
- t = dt
+ // mappings have been removed, so this should never happen.
+ gokey := pkgPath + "." + rt.Name()
+ panic(fmt.Sprintf("native type does not exist for %s", gokey))
}
// memoize t to cache.
if debug {
@@ -1230,34 +1180,6 @@ func gno2GoValue(tv *TypedValue, rv reflect.Value) (ret reflect.Value) {
// ----------------------------------------
// PackageNode methods
-func (x *PackageNode) DefineGoNativeType(rt reflect.Type) {
- if debug {
- debug.Printf("*PackageNode.DefineGoNativeType(%s)\n", rt.String())
- }
- pkgp := rt.PkgPath()
- if pkgp == "" {
- // DefineGoNativeType can only work with defined exported types.
- // Unexported types should be composed, and primitive types
- // should just use Gno types.
- panic(fmt.Sprintf(
- "reflect.Type %s has no package path",
- rt.String()))
- }
- name := rt.Name()
- if name == "" {
- panic(fmt.Sprintf(
- "reflect.Type %s is not named",
- rt.String()))
- }
- if rt.PkgPath() == "" {
- panic(fmt.Sprintf(
- "reflect.Type %s is not defined/exported",
- rt.String()))
- }
- nt := &NativeType{Type: rt}
- x.Define(Name(name), asValue(nt))
-}
-
func (x *PackageNode) DefineGoNativeValue(name Name, nv interface{}) {
x.defineGoNativeValue(false, name, nv)
}
diff --git a/gnovm/pkg/gnolang/gonative_test.go b/gnovm/pkg/gnolang/gonative_test.go
deleted file mode 100644
index fa5415a8068..00000000000
--- a/gnovm/pkg/gnolang/gonative_test.go
+++ /dev/null
@@ -1,149 +0,0 @@
-package gnolang
-
-import (
- "bytes"
- "fmt"
- "reflect"
- "testing"
-
- "github.com/gnolang/gno/tm2/pkg/crypto"
- "github.com/stretchr/testify/assert"
-)
-
-// args is an even number of elements,
-// the even index items are package nodes,
-// and the odd index items are corresponding package values.
-func gonativeTestStore(args ...interface{}) Store {
- store := NewStore(nil, nil, nil)
- store.SetPackageGetter(func(pkgPath string, _ Store) (*PackageNode, *PackageValue) {
- for i := 0; i < len(args)/2; i++ {
- pn := args[i*2].(*PackageNode)
- pv := args[i*2+1].(*PackageValue)
- if pkgPath == pv.PkgPath {
- return pn, pv
- }
- }
- return nil, nil
- })
- store.SetStrictGo2GnoMapping(false)
- return store
-}
-
-type Foo struct {
- A int
- B int32
- C int64
- D string
-}
-
-func TestGoNativeDefine(t *testing.T) {
- // Create package foo and define Foo.
- pkg := NewPackageNode("foo", "test.foo", nil)
- rt := reflect.TypeOf(Foo{})
- pkg.DefineGoNativeType(rt)
- nt := pkg.GetValueRef(nil, Name("Foo"), true).GetType().(*NativeType)
- assert.Equal(t, rt, nt.Type)
- path := pkg.GetPathForName(nil, Name("Foo"))
- assert.Equal(t, uint8(1), path.Depth)
- assert.Equal(t, uint16(0), path.Index)
- pv := pkg.NewPackage()
- nt = pv.GetBlock(nil).GetPointerTo(nil, path).TV.GetType().(*NativeType)
- assert.Equal(t, rt, nt.Type)
- store := gonativeTestStore(pkg, pv)
-
- // Import above package and evaluate foo.Foo.
- m := NewMachineWithOptions(MachineOptions{
- PkgPath: "test",
- Store: store,
- })
- m.RunDeclaration(ImportD("foo", "test.foo"))
- tvs := m.Eval(Sel(Nx("foo"), "Foo"))
- assert.Equal(t, 1, len(tvs))
- assert.Equal(t, nt, tvs[0].V.(TypeValue).Type)
-}
-
-func TestGoNativeDefine2(t *testing.T) {
- // Create package foo and define Foo.
- pkg := NewPackageNode("foo", "test.foo", nil)
- rt := reflect.TypeOf(Foo{})
- pkg.DefineGoNativeType(rt)
- pv := pkg.NewPackage()
- store := gonativeTestStore(pkg, pv)
-
- // Import above package and run file.
- out := new(bytes.Buffer)
- m := NewMachineWithOptions(MachineOptions{
- PkgPath: "main",
- Output: out,
- Store: store,
- })
-
- c := `package main
-import foo "test.foo"
-func main() {
- f := foo.Foo{A:1}
- println("A:", f.A)
- println("B:", f.B)
- println("C:", f.C)
- println("D:", f.D)
-}`
- n := MustParseFile("main.go", c)
- m.RunFiles(n)
- m.RunMain()
- // weird `+` is used to place a space, without having editors strip it away.
- assert.Equal(t, `A: 1
-B: 0
-C: 0
-D: `+`
-`, string(out.Bytes()))
-}
-
-func TestGoNativeDefine3(t *testing.T) {
- t.Parallel()
-
- // Create package foo and define Foo.
- out := new(bytes.Buffer)
- pkg := NewPackageNode("foo", "test.foo", nil)
- pkg.DefineGoNativeType(reflect.TypeOf(Foo{}))
- pkg.DefineGoNativeValue("PrintFoo", func(f Foo) {
- out.Write([]byte(fmt.Sprintf("A: %v\n", f.A)))
- out.Write([]byte(fmt.Sprintf("B: %v\n", f.B)))
- out.Write([]byte(fmt.Sprintf("C: %v\n", f.C)))
- out.Write([]byte(fmt.Sprintf("D: %v\n", f.D)))
- })
- pv := pkg.NewPackage()
- store := gonativeTestStore(pkg, pv)
-
- // Import above package and run file.
- m := NewMachineWithOptions(MachineOptions{
- PkgPath: "main",
- Output: out,
- Store: store,
- })
-
- c := `package main
-import foo "test.foo"
-func main() {
- f := foo.Foo{A:1}
- foo.PrintFoo(f)
-}`
- n := MustParseFile("main.go", c)
- m.RunFiles(n)
- m.RunMain()
- assert.Equal(t, `A: 1
-B: 0
-C: 0
-D: `+`
-`, out.String())
-}
-
-func TestCrypto(t *testing.T) {
- t.Parallel()
-
- addr := crypto.Address{}
- store := gonativeTestStore()
- tv := Go2GnoValue(nilAllocator, store, reflect.ValueOf(addr))
- assert.Equal(t,
- `(array[0x0000000000000000000000000000000000000000] github.com/gnolang/gno/tm2/pkg/crypto.Address)`,
- tv.String())
-}
diff --git a/gnovm/pkg/gnolang/helpers.go b/gnovm/pkg/gnolang/helpers.go
index c6f7e696ea4..ddc1fd2fa55 100644
--- a/gnovm/pkg/gnolang/helpers.go
+++ b/gnovm/pkg/gnolang/helpers.go
@@ -10,29 +10,39 @@ import (
// ----------------------------------------
// Functions centralizing definitions
-// RealmPathPrefix is the prefix used to identify pkgpaths which are meant to
-// be realms and as such to have their state persisted. This is used by [IsRealmPath].
-const RealmPathPrefix = "gno.land/r/"
+// ReRealmPath and RePackagePath are the regexes used to identify pkgpaths which are meant to
+// be realms with persisted states and pure packages.
+var (
+ ReRealmPath = regexp.MustCompile(`^([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.[a-zA-Z]{2,}/r/[a-z0-9_/]+`)
+ RePackagePath = regexp.MustCompile(`^([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.[a-zA-Z]{2,}/p/[a-z0-9_/]+`)
+)
// ReGnoRunPath is the path used for realms executed in maketx run.
-// These are not considered realms, as an exception to the RealmPathPrefix rule.
-var ReGnoRunPath = regexp.MustCompile(`^gno\.land/r/g[a-z0-9]+/run$`)
+// These are not considered realms, as an exception to the ReRealmPathPrefix rule.
+var ReGnoRunPath = regexp.MustCompile(`^([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.[a-zA-Z]{2,}/r/g[a-z0-9]+/run$`)
// IsRealmPath determines whether the given pkgpath is for a realm, and as such
// should persist the global state.
func IsRealmPath(pkgPath string) bool {
- return strings.HasPrefix(pkgPath, RealmPathPrefix) &&
- // MsgRun pkgPath aren't realms
+ return ReRealmPath.MatchString(pkgPath) &&
!ReGnoRunPath.MatchString(pkgPath)
}
+// IsPurePackagePath determines whether the given pkgpath is for a published Gno package.
+// It only considers "pure" those starting with gno.land/p/, so it returns false for
+// stdlib packages and MsgRun paths.
+func IsPurePackagePath(pkgPath string) bool {
+ return RePackagePath.MatchString(pkgPath)
+}
+
// IsStdlib determines whether s is a pkgpath for a standard library.
func IsStdlib(s string) bool {
- // NOTE(morgan): this is likely to change in the future as we add support for
- // IBC/ICS and we allow import paths to other chains. It might be good to
- // (eventually) follow the same rule as Go, which is: does the first
- // element of the import path contain a dot?
- return !strings.HasPrefix(s, "gno.land/")
+ idx := strings.IndexByte(s, '/')
+ if idx < 0 {
+ // If no '/' is found, consider the whole string
+ return strings.IndexByte(s, '.') < 0
+ }
+ return strings.IndexByte(s[:idx+1], '.') < 0
}
// ----------------------------------------
diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go
index e341ef8e9f1..5ceccffda2c 100644
--- a/gnovm/pkg/gnolang/machine.go
+++ b/gnovm/pkg/gnolang/machine.go
@@ -3,7 +3,6 @@ package gnolang
// XXX rename file to machine.go.
import (
- "encoding/json"
"fmt"
"io"
"reflect"
@@ -11,12 +10,10 @@ import (
"strconv"
"strings"
"sync"
- "testing"
-
- "github.com/gnolang/overflow"
"github.com/gnolang/gno/gnovm"
"github.com/gnolang/gno/tm2/pkg/errors"
+ "github.com/gnolang/gno/tm2/pkg/overflow"
"github.com/gnolang/gno/tm2/pkg/store"
)
@@ -299,7 +296,7 @@ func (m *Machine) runMemPackage(memPkg *gnovm.MemPackage, save, overrides bool)
}
m.SetActivePackage(pv)
// run files.
- updates := m.RunFileDecls(files.Files...)
+ updates := m.runFileDecls(files.Files...)
// save package value and mempackage.
// XXX save condition will be removed once gonative is removed.
var throwaway *Realm
@@ -400,107 +397,10 @@ func destar(x Expr) Expr {
return x
}
-// Tests all test files in a mempackage.
-// Assumes that the importing of packages is handled elsewhere.
-// The resulting package value and node become injected with TestMethods and
-// other declarations, so it is expected that non-test code will not be run
-// afterwards from the same store.
-func (m *Machine) TestMemPackage(t *testing.T, memPkg *gnovm.MemPackage) {
- defer m.injectLocOnPanic()
- DisableDebug()
- fmt.Println("DEBUG DISABLED (FOR TEST DEPENDENCIES INIT)")
- // parse test files.
- tfiles, itfiles := ParseMemPackageTests(memPkg)
- { // first, tfiles which run in the same package.
- pv := m.Store.GetPackage(memPkg.Path, false)
- pvBlock := pv.GetBlock(m.Store)
- pvSize := len(pvBlock.Values)
- m.SetActivePackage(pv)
- // run test files.
- m.RunFiles(tfiles.Files...)
- // run all tests in test files.
- for i := pvSize; i < len(pvBlock.Values); i++ {
- tv := pvBlock.Values[i]
- m.TestFunc(t, tv)
- }
- }
- { // run all (import) tests in test files.
- pn := NewPackageNode(Name(memPkg.Name+"_test"), memPkg.Path+"_test", itfiles)
- pv := pn.NewPackage()
- m.Store.SetBlockNode(pn)
- m.Store.SetCachePackage(pv)
- pvBlock := pv.GetBlock(m.Store)
- m.SetActivePackage(pv)
- m.RunFiles(itfiles.Files...)
- pn.PrepareNewValues(pv)
- EnableDebug()
- fmt.Println("DEBUG ENABLED")
- for i := 0; i < len(pvBlock.Values); i++ {
- tv := pvBlock.Values[i]
- m.TestFunc(t, tv)
- }
- }
-}
-
-// TestFunc calls tv with testing.RunTest, if tv is a function with a name that
-// starts with `Test`.
-func (m *Machine) TestFunc(t *testing.T, tv TypedValue) {
- if !(tv.T.Kind() == FuncKind &&
- strings.HasPrefix(string(tv.V.(*FuncValue).Name), "Test")) {
- return // not a test function.
- }
- // XXX ensure correct func type.
- name := string(tv.V.(*FuncValue).Name)
- // prefetch the testing package.
- testingpv := m.Store.GetPackage("testing", false)
- testingtv := TypedValue{T: gPackageType, V: testingpv}
- testingcx := &ConstExpr{TypedValue: testingtv}
-
- t.Run(name, func(t *testing.T) {
- defer m.injectLocOnPanic()
- x := Call(
- Sel(testingcx, "RunTest"), // Call testing.RunTest
- Str(name), // First param, the name of the test
- X("true"), // Second Param, verbose bool
- &CompositeLitExpr{ // Third param, the testing.InternalTest
- Type: Sel(testingcx, "InternalTest"),
- Elts: KeyValueExprs{
- {Key: X("Name"), Value: Str(name)},
- {Key: X("F"), Value: X(name)},
- },
- },
- )
- res := m.Eval(x)
- ret := res[0].GetString()
- if ret == "" {
- t.Errorf("failed to execute unit test: %q", name)
- return
- }
-
- // mirror of stdlibs/testing.Report
- var report struct {
- Skipped bool
- Failed bool
- }
- err := json.Unmarshal([]byte(ret), &report)
- if err != nil {
- t.Errorf("failed to parse test output %q", name)
- return
- }
-
- switch {
- case report.Skipped:
- t.SkipNow()
- case report.Failed:
- t.Fail()
- }
- })
-}
-
// Stacktrace returns the stack trace of the machine.
// It collects the executions and frames from the machine's frames and statements.
func (m *Machine) Stacktrace() (stacktrace Stacktrace) {
- if len(m.Frames) == 0 {
+ if len(m.Frames) == 0 || len(m.Stmts) == 0 {
return
}
@@ -534,58 +434,6 @@ func (m *Machine) Stacktrace() (stacktrace Stacktrace) {
return
}
-// in case of panic, inject location information to exception.
-func (m *Machine) injectLocOnPanic() {
- if r := recover(); r != nil {
- // Show last location information.
- // First, determine the line number of expression or statement if any.
- lastLine := 0
- lastColumn := 0
- if len(m.Exprs) > 0 {
- for i := len(m.Exprs) - 1; i >= 0; i-- {
- expr := m.Exprs[i]
- if expr.GetLine() > 0 {
- lastLine = expr.GetLine()
- lastColumn = expr.GetColumn()
- break
- }
- }
- }
- if lastLine == 0 && len(m.Stmts) > 0 {
- for i := len(m.Stmts) - 1; i >= 0; i-- {
- stmt := m.Stmts[i]
- if stmt.GetLine() > 0 {
- lastLine = stmt.GetLine()
- lastColumn = stmt.GetColumn()
- break
- }
- }
- }
- // Append line number to block location.
- lastLoc := Location{}
- for i := len(m.Blocks) - 1; i >= 0; i-- {
- block := m.Blocks[i]
- src := block.GetSource(m.Store)
- loc := src.GetLocation()
- if !loc.IsZero() {
- lastLoc = loc
- if lastLine > 0 {
- lastLoc.Line = lastLine
- lastLoc.Column = lastColumn
- }
- break
- }
- }
- // wrap panic with location information.
- if !lastLoc.IsZero() {
- fmt.Printf("%s: %v\n", lastLoc.String(), r)
- panic(errors.Wrap(r, fmt.Sprintf("location: %s", lastLoc.String())))
- } else {
- panic(r)
- }
- }
-}
-
// Convenience for tests.
// Production must not use this, because realm package init
// must happen after persistence and realm finalization,
@@ -602,10 +450,6 @@ func (m *Machine) RunFiles(fns ...*FileNode) {
// Add files to the package's *FileSet and run decls in them.
// This will also run each init function encountered.
// Returns the updated typed values of package.
-func (m *Machine) RunFileDecls(fns ...*FileNode) []TypedValue {
- return m.runFileDecls(fns...)
-}
-
func (m *Machine) runFileDecls(fns ...*FileNode) []TypedValue {
// Files' package names must match the machine's active one.
// if there is one.
@@ -1155,127 +999,130 @@ func (m *Machine) incrCPU(cycles int64) {
}
const (
+ // CPU cycles
/* Control operators */
OpCPUInvalid = 1
OpCPUHalt = 1
OpCPUNoop = 1
- OpCPUExec = 1
- OpCPUPrecall = 1
- OpCPUCall = 1
- OpCPUCallNativeBody = 1
- OpCPUReturn = 1
- OpCPUReturnFromBlock = 1
- OpCPUReturnToBlock = 1
- OpCPUDefer = 1
- OpCPUCallDeferNativeBody = 1
- OpCPUGo = 1
- OpCPUSelect = 1
- OpCPUSwitchClause = 1
- OpCPUSwitchClauseCase = 1
- OpCPUTypeSwitch = 1
- OpCPUIfCond = 1
+ OpCPUExec = 25
+ OpCPUPrecall = 207
+ OpCPUCall = 256
+ OpCPUCallNativeBody = 424
+ OpCPUReturn = 38
+ OpCPUReturnFromBlock = 36
+ OpCPUReturnToBlock = 23
+ OpCPUDefer = 64
+ OpCPUCallDeferNativeBody = 33
+ OpCPUGo = 1 // Not yet implemented
+ OpCPUSelect = 1 // Not yet implemented
+ OpCPUSwitchClause = 38
+ OpCPUSwitchClauseCase = 143
+ OpCPUTypeSwitch = 171
+ OpCPUIfCond = 38
OpCPUPopValue = 1
OpCPUPopResults = 1
- OpCPUPopBlock = 1
- OpCPUPopFrameAndReset = 1
- OpCPUPanic1 = 1
- OpCPUPanic2 = 1
+ OpCPUPopBlock = 3
+ OpCPUPopFrameAndReset = 15
+ OpCPUPanic1 = 121
+ OpCPUPanic2 = 21
/* Unary & binary operators */
- OpCPUUpos = 1
- OpCPUUneg = 1
- OpCPUUnot = 1
- OpCPUUxor = 1
- OpCPUUrecv = 1
- OpCPULor = 1
- OpCPULand = 1
- OpCPUEql = 1
- OpCPUNeq = 1
- OpCPULss = 1
- OpCPULeq = 1
- OpCPUGtr = 1
- OpCPUGeq = 1
- OpCPUAdd = 1
- OpCPUSub = 1
- OpCPUBor = 1
- OpCPUXor = 1
- OpCPUMul = 1
- OpCPUQuo = 1
- OpCPURem = 1
- OpCPUShl = 1
- OpCPUShr = 1
- OpCPUBand = 1
- OpCPUBandn = 1
+ OpCPUUpos = 7
+ OpCPUUneg = 25
+ OpCPUUnot = 6
+ OpCPUUxor = 14
+ OpCPUUrecv = 1 // Not yet implemented
+ OpCPULor = 26
+ OpCPULand = 24
+ OpCPUEql = 160
+ OpCPUNeq = 95
+ OpCPULss = 13
+ OpCPULeq = 19
+ OpCPUGtr = 20
+ OpCPUGeq = 26
+ OpCPUAdd = 18
+ OpCPUSub = 6
+ OpCPUBor = 23
+ OpCPUXor = 13
+ OpCPUMul = 19
+ OpCPUQuo = 16
+ OpCPURem = 18
+ OpCPUShl = 22
+ OpCPUShr = 20
+ OpCPUBand = 9
+ OpCPUBandn = 15
/* Other expression operators */
- OpCPUEval = 1
- OpCPUBinary1 = 1
- OpCPUIndex1 = 1
- OpCPUIndex2 = 1
- OpCPUSelector = 1
- OpCPUSlice = 1
- OpCPUStar = 1
- OpCPURef = 1
- OpCPUTypeAssert1 = 1
- OpCPUTypeAssert2 = 1
- OpCPUStaticTypeOf = 1
- OpCPUCompositeLit = 1
- OpCPUArrayLit = 1
- OpCPUSliceLit = 1
- OpCPUSliceLit2 = 1
- OpCPUMapLit = 1
- OpCPUStructLit = 1
- OpCPUFuncLit = 1
- OpCPUConvert = 1
+ OpCPUEval = 29
+ OpCPUBinary1 = 19
+ OpCPUIndex1 = 77
+ OpCPUIndex2 = 195
+ OpCPUSelector = 32
+ OpCPUSlice = 103
+ OpCPUStar = 40
+ OpCPURef = 125
+ OpCPUTypeAssert1 = 30
+ OpCPUTypeAssert2 = 25
+ // TODO: OpCPUStaticTypeOf is an arbitrary number.
+ // A good way to benchmark this is yet to be determined.
+ OpCPUStaticTypeOf = 100
+ OpCPUCompositeLit = 50
+ OpCPUArrayLit = 137
+ OpCPUSliceLit = 183
+ OpCPUSliceLit2 = 467
+ OpCPUMapLit = 475
+ OpCPUStructLit = 179
+ OpCPUFuncLit = 61
+ OpCPUConvert = 16
/* Native operators */
- OpCPUArrayLitGoNative = 1
- OpCPUSliceLitGoNative = 1
- OpCPUStructLitGoNative = 1
- OpCPUCallGoNative = 1
+ OpCPUArrayLitGoNative = 137
+ OpCPUSliceLitGoNative = 183
+ OpCPUStructLitGoNative = 179
+ OpCPUCallGoNative = 256
/* Type operators */
- OpCPUFieldType = 1
- OpCPUArrayType = 1
- OpCPUSliceType = 1
- OpCPUPointerType = 1
- OpCPUInterfaceType = 1
- OpCPUChanType = 1
- OpCPUFuncType = 1
- OpCPUMapType = 1
- OpCPUStructType = 1
- OpCPUMaybeNativeType = 1
+ OpCPUFieldType = 59
+ OpCPUArrayType = 57
+ OpCPUSliceType = 55
+ OpCPUPointerType = 1 // Not yet implemented
+ OpCPUInterfaceType = 75
+ OpCPUChanType = 57
+ OpCPUFuncType = 81
+ OpCPUMapType = 59
+ OpCPUStructType = 174
+ OpCPUMaybeNativeType = 67
/* Statement operators */
- OpCPUAssign = 1
- OpCPUAddAssign = 1
- OpCPUSubAssign = 1
- OpCPUMulAssign = 1
- OpCPUQuoAssign = 1
- OpCPURemAssign = 1
- OpCPUBandAssign = 1
- OpCPUBandnAssign = 1
- OpCPUBorAssign = 1
- OpCPUXorAssign = 1
- OpCPUShlAssign = 1
- OpCPUShrAssign = 1
- OpCPUDefine = 1
- OpCPUInc = 1
- OpCPUDec = 1
+ OpCPUAssign = 79
+ OpCPUAddAssign = 85
+ OpCPUSubAssign = 57
+ OpCPUMulAssign = 55
+ OpCPUQuoAssign = 50
+ OpCPURemAssign = 46
+ OpCPUBandAssign = 54
+ OpCPUBandnAssign = 44
+ OpCPUBorAssign = 55
+ OpCPUXorAssign = 48
+ OpCPUShlAssign = 68
+ OpCPUShrAssign = 76
+ OpCPUDefine = 111
+ OpCPUInc = 76
+ OpCPUDec = 46
/* Decl operators */
- OpCPUValueDecl = 1
- OpCPUTypeDecl = 1
+ OpCPUValueDecl = 113
+ OpCPUTypeDecl = 100
/* Loop (sticky) operators (>= 0xD0) */
- OpCPUSticky = 1
- OpCPUBody = 1
- OpCPUForLoop = 1
- OpCPURangeIter = 1
- OpCPURangeIterString = 1
- OpCPURangeIterMap = 1
- OpCPURangeIterArrayPtr = 1
- OpCPUReturnCallDefers = 1
+ OpCPUSticky = 1 // Not a real op
+ OpCPUBody = 43
+ OpCPUForLoop = 27
+ OpCPURangeIter = 105
+ OpCPURangeIterString = 55
+ OpCPURangeIterMap = 48
+ OpCPURangeIterArrayPtr = 46
+ OpCPUReturnCallDefers = 78
)
//----------------------------------------
diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go
index 45062f8e14c..0496d37ed72 100644
--- a/gnovm/pkg/gnolang/nodes.go
+++ b/gnovm/pkg/gnolang/nodes.go
@@ -13,7 +13,6 @@ import (
"strings"
"github.com/gnolang/gno/gnovm"
- "github.com/gnolang/gno/tm2/pkg/errors"
"go.uber.org/multierr"
)
@@ -442,7 +441,7 @@ type IndexExpr struct { // X[Index]
Attributes
X Expr // expression
Index Expr // index expression
- HasOK bool // if true, is form: `value, ok := []
+ HasOK bool // if true, is form: `value, ok := []`
}
type SelectorExpr struct { // X.Sel
@@ -1133,14 +1132,23 @@ type FileSet struct {
// PackageNameFromFileBody extracts the package name from the given Gno code body.
// The 'name' parameter is used for better error traces, and 'body' contains the Gno code.
-func PackageNameFromFileBody(name, body string) Name {
+func PackageNameFromFileBody(name, body string) (Name, error) {
fset := token.NewFileSet()
astFile, err := parser.ParseFile(fset, name, body, parser.PackageClauseOnly)
if err != nil {
- panic(err)
+ return "", err
}
- return Name(astFile.Name.Name)
+ return Name(astFile.Name.Name), nil
+}
+
+// MustPackageNameFromFileBody is a wrapper around [PackageNameFromFileBody] that panics on error.
+func MustPackageNameFromFileBody(name, body string) Name {
+ pkgName, err := PackageNameFromFileBody(name, body)
+ if err != nil {
+ panic(err)
+ }
+ return pkgName
}
// ReadMemPackage initializes a new MemPackage by reading the OS directory
@@ -1153,10 +1161,10 @@ func PackageNameFromFileBody(name, body string) Name {
//
// NOTE: panics if package name is invalid (characters must be alphanumeric or _,
// lowercase, and must start with a letter).
-func ReadMemPackage(dir string, pkgPath string) *gnovm.MemPackage {
+func ReadMemPackage(dir string, pkgPath string) (*gnovm.MemPackage, error) {
files, err := os.ReadDir(dir)
if err != nil {
- panic(err)
+ return nil, err
}
allowedFiles := []string{ // make case insensitive?
"LICENSE",
@@ -1187,24 +1195,36 @@ func ReadMemPackage(dir string, pkgPath string) *gnovm.MemPackage {
return ReadMemPackageFromList(list, pkgPath)
}
+// MustReadMemPackage is a wrapper around [ReadMemPackage] that panics on error.
+func MustReadMemPackage(dir string, pkgPath string) *gnovm.MemPackage {
+ pkg, err := ReadMemPackage(dir, pkgPath)
+ if err != nil {
+ panic(err)
+ }
+ return pkg
+}
+
// ReadMemPackageFromList creates a new [gnovm.MemPackage] with the specified pkgPath,
// containing the contents of all the files provided in the list slice.
// No parsing or validation is done on the filenames.
//
-// NOTE: panics if package name is invalid (characters must be alphanumeric or _,
+// NOTE: errors out if package name is invalid (characters must be alphanumeric or _,
// lowercase, and must start with a letter).
-func ReadMemPackageFromList(list []string, pkgPath string) *gnovm.MemPackage {
+func ReadMemPackageFromList(list []string, pkgPath string) (*gnovm.MemPackage, error) {
memPkg := &gnovm.MemPackage{Path: pkgPath}
var pkgName Name
for _, fpath := range list {
fname := filepath.Base(fpath)
bz, err := os.ReadFile(fpath)
if err != nil {
- panic(err)
+ return nil, err
}
// XXX: should check that all pkg names are the same (else package is invalid)
if pkgName == "" && strings.HasSuffix(fname, ".gno") {
- pkgName = PackageNameFromFileBody(fname, string(bz))
+ pkgName, err = PackageNameFromFileBody(fname, string(bz))
+ if err != nil {
+ return nil, err
+ }
if strings.HasSuffix(string(pkgName), "_test") {
pkgName = pkgName[:len(pkgName)-len("_test")]
}
@@ -1218,11 +1238,22 @@ func ReadMemPackageFromList(list []string, pkgPath string) *gnovm.MemPackage {
// If no .gno files are present, package simply does not exist.
if !memPkg.IsEmpty() {
- validatePkgName(string(pkgName))
+ if err := validatePkgName(string(pkgName)); err != nil {
+ return nil, err
+ }
memPkg.Name = string(pkgName)
}
- return memPkg
+ return memPkg, nil
+}
+
+// MustReadMemPackageFromList is a wrapper around [ReadMemPackageFromList] that panics on error.
+func MustReadMemPackageFromList(list []string, pkgPath string) *gnovm.MemPackage {
+ pkg, err := ReadMemPackageFromList(list, pkgPath)
+ if err != nil {
+ panic(err)
+ }
+ return pkg
}
// ParseMemPackage executes [ParseFile] on each file of the memPkg, excluding
@@ -1257,38 +1288,6 @@ func ParseMemPackage(memPkg *gnovm.MemPackage) (fset *FileSet) {
return fset
}
-func ParseMemPackageTests(memPkg *gnovm.MemPackage) (tset, itset *FileSet) {
- tset = &FileSet{}
- itset = &FileSet{}
- for _, mfile := range memPkg.Files {
- if !strings.HasSuffix(mfile.Name, ".gno") {
- continue // skip this file.
- }
- n, err := ParseFile(mfile.Name, mfile.Body)
- if err != nil {
- panic(errors.Wrap(err, "parsing file "+mfile.Name))
- }
- if n == nil {
- panic("should not happen")
- }
- if strings.HasSuffix(mfile.Name, "_test.gno") {
- // add test file.
- if memPkg.Name+"_test" == string(n.PkgName) {
- itset.AddFiles(n)
- } else {
- tset.AddFiles(n)
- }
- } else if memPkg.Name == string(n.PkgName) {
- // skip package file.
- } else {
- panic(fmt.Sprintf(
- "expected package name [%s] or [%s_test] but got [%s] file [%s]",
- memPkg.Name, memPkg.Name, n.PkgName, mfile))
- }
- }
- return tset, itset
-}
-
func (fs *FileSet) AddFiles(fns ...*FileNode) {
fs.Files = append(fs.Files, fns...)
}
@@ -2154,6 +2153,7 @@ type ValuePather interface {
// Utility
func (x *BasicLitExpr) GetString() string {
+ // Matches string literal parsing in go/constant.MakeFromLiteral.
str, err := strconv.Unquote(x.Value)
if err != nil {
panic("error in parsing string literal: " + err.Error())
@@ -2173,10 +2173,11 @@ var rePkgName = regexp.MustCompile(`^[a-z][a-z0-9_]+$`)
// TODO: consider length restrictions.
// If this function is changed, ReadMemPackage's documentation should be updated accordingly.
-func validatePkgName(name string) {
+func validatePkgName(name string) error {
if !rePkgName.MatchString(name) {
- panic(fmt.Sprintf("cannot create package with invalid name %q", name))
+ return fmt.Errorf("cannot create package with invalid name %q", name)
}
+ return nil
}
const hiddenResultVariable = ".res_"
diff --git a/gnovm/pkg/gnolang/op_eval.go b/gnovm/pkg/gnolang/op_eval.go
index 1beba1d6e3f..2aa13b21753 100644
--- a/gnovm/pkg/gnolang/op_eval.go
+++ b/gnovm/pkg/gnolang/op_eval.go
@@ -204,16 +204,14 @@ func (m *Machine) doOpEval() {
// and github.com/golang/go/issues/19921
panic("imaginaries are not supported")
case CHAR:
- cstr, err := strconv.Unquote(x.Value)
+ // Matching character literal parsing in go/constant.MakeFromLiteral.
+ val := x.Value
+ rne, _, _, err := strconv.UnquoteChar(val[1:len(val)-1], '\'')
if err != nil {
panic("error in parsing character literal: " + err.Error())
}
- runes := []rune(cstr)
- if len(runes) != 1 {
- panic(fmt.Sprintf("error in parsing character literal: 1 rune expected, but got %v (%s)", len(runes), cstr))
- }
tv := TypedValue{T: UntypedRuneType}
- tv.SetInt32(runes[0])
+ tv.SetInt32(rne)
m.PushValue(tv)
case STRING:
m.PushValue(TypedValue{
diff --git a/gnovm/pkg/gnolang/op_exec.go b/gnovm/pkg/gnolang/op_exec.go
index 900b5f8e9bb..5f71ffefa0c 100644
--- a/gnovm/pkg/gnolang/op_exec.go
+++ b/gnovm/pkg/gnolang/op_exec.go
@@ -769,6 +769,7 @@ EXEC_SWITCH:
}
m.PushOp(OpBody)
m.PushStmt(b.GetBodyStmt())
+ case *EmptyStmt:
default:
panic(fmt.Sprintf("unexpected statement %#v", s))
}
diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go
index b614e72e945..c0f6225740b 100644
--- a/gnovm/pkg/gnolang/op_expressions.go
+++ b/gnovm/pkg/gnolang/op_expressions.go
@@ -88,20 +88,20 @@ func (m *Machine) doOpSelector() {
func (m *Machine) doOpSlice() {
sx := m.PopExpr().(*SliceExpr)
- var low, high, max int = -1, -1, -1
+ var lowVal, highVal, maxVal int = -1, -1, -1
// max
if sx.Max != nil {
- max = m.PopValue().ConvertGetInt()
+ maxVal = m.PopValue().ConvertGetInt()
}
// high
if sx.High != nil {
- high = m.PopValue().ConvertGetInt()
+ highVal = m.PopValue().ConvertGetInt()
}
// low
if sx.Low != nil {
- low = m.PopValue().ConvertGetInt()
+ lowVal = m.PopValue().ConvertGetInt()
} else {
- low = 0
+ lowVal = 0
}
// slice base x
xv := m.PopValue()
@@ -114,14 +114,14 @@ func (m *Machine) doOpSlice() {
}
// fill default based on xv
if sx.High == nil {
- high = xv.GetLength()
+ highVal = xv.GetLength()
}
// all low:high:max cases
- if max == -1 {
- sv := xv.GetSlice(m.Alloc, low, high)
+ if maxVal == -1 {
+ sv := xv.GetSlice(m.Alloc, lowVal, highVal)
m.PushValue(sv)
} else {
- sv := xv.GetSlice2(m.Alloc, low, high, max)
+ sv := xv.GetSlice2(m.Alloc, lowVal, highVal, maxVal)
m.PushValue(sv)
}
}
@@ -593,16 +593,16 @@ func (m *Machine) doOpSliceLit2() {
// peek slice type.
st := m.PeekValue(1).V.(TypeValue).Type
// calculate maximum index.
- max := 0
+ maxVal := 0
for i := 0; i < el; i++ {
itv := tvs[i*2+0]
idx := itv.ConvertGetInt()
- if idx > max {
- max = idx
+ if idx > maxVal {
+ maxVal = idx
}
}
// construct element buf slice.
- es := make([]TypedValue, max+1)
+ es := make([]TypedValue, maxVal+1)
for i := 0; i < el; i++ {
itv := tvs[i*2+0]
vtv := tvs[i*2+1]
diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go
index 7198d4f6a98..8951c2c04cf 100644
--- a/gnovm/pkg/gnolang/preprocess.go
+++ b/gnovm/pkg/gnolang/preprocess.go
@@ -10,6 +10,7 @@ import (
"sync/atomic"
"github.com/gnolang/gno/tm2/pkg/errors"
+ tmstore "github.com/gnolang/gno/tm2/pkg/store"
)
const (
@@ -174,6 +175,13 @@ func initStaticBlocks(store Store, ctx BlockNode, bn BlockNode) {
case *ImportDecl:
nx := &n.NameExpr
nn := nx.Name
+ loc := last.GetLocation()
+ // NOTE: imports from "pure packages" are actually sometimes
+ // allowed, most notably in MsgRun and filetests; IsPurePackagePath
+ // returns false in these cases.
+ if IsPurePackagePath(loc.PkgPath) && IsRealmPath(n.PkgPath) {
+ panic(fmt.Sprintf("pure package path %q cannot import realm path %q", loc.PkgPath, n.PkgPath))
+ }
if nn == "." {
panic("dot imports not allowed in gno")
}
@@ -358,6 +366,17 @@ func initStaticBlocks(store Store, ctx BlockNode, bn BlockNode) {
func doRecover(stack []BlockNode, n Node) {
if r := recover(); r != nil {
+ // Catch the out-of-gas exception and throw it
+ if exp, ok := r.(tmstore.OutOfGasException); ok {
+ exp.Descriptor = fmt.Sprintf("in preprocess: %v", r)
+ panic(exp)
+ }
+
+ if _, ok := r.(*PreprocessError); ok {
+ // re-panic directly if this is a PreprocessError already.
+ panic(r)
+ }
+
// before re-throwing the error, append location information to message.
last := stack[len(stack)-1]
loc := last.GetLocation()
@@ -573,6 +592,10 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
// define key/value.
n.X = Preprocess(store, last, n.X).(Expr)
xt := evalStaticTypeOf(store, last, n.X)
+ if xt == nil {
+ panic("cannot range over nil")
+ }
+
switch xt.Kind() {
case MapKind:
n.IsMap = true
@@ -731,7 +754,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
for i, cx := range n.Cases {
cx = Preprocess(
store, last, cx).(Expr)
- checkOrConvertType(store, last, &cx, tt, false) // #nosec G601
+ checkOrConvertType(store, last, n, &cx, tt, false) // #nosec G601
n.Cases[i] = cx
}
}
@@ -870,7 +893,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
// Preprocess and convert tag if const.
if n.X != nil {
n.X = Preprocess(store, last, n.X).(Expr)
- convertIfConst(store, last, n.X)
+ convertIfConst(store, last, n, n.X)
}
}
return n, TRANS_CONTINUE
@@ -915,6 +938,14 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
switch n := n.(type) {
// TRANS_LEAVE -----------------------
case *NameExpr:
+ if isBlankIdentifier(n) {
+ switch ftype {
+ case TRANS_ASSIGN_LHS, TRANS_RANGE_KEY, TRANS_RANGE_VALUE, TRANS_VAR_NAME:
+ // can use _ as value or type in these contexts
+ default:
+ panic("cannot use _ as value or type")
+ }
+ }
// Validity: check that name isn't reserved.
if isReservedName(n.Name) {
panic(fmt.Sprintf(
@@ -1082,10 +1113,10 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
// First, convert untyped as necessary.
if !shouldSwapOnSpecificity(lcx.T, rcx.T) {
// convert n.Left to right type.
- checkOrConvertType(store, last, &n.Left, rcx.T, false)
+ checkOrConvertType(store, last, n, &n.Left, rcx.T, false)
} else {
// convert n.Right to left type.
- checkOrConvertType(store, last, &n.Right, lcx.T, false)
+ checkOrConvertType(store, last, n, &n.Right, lcx.T, false)
}
// Then, evaluate the expression.
cx := evalConst(store, last, n)
@@ -1105,7 +1136,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
rnt.String()))
}
// convert n.Left to pt type,
- checkOrConvertType(store, last, &n.Left, pt, false)
+ checkOrConvertType(store, last, n, &n.Left, pt, false)
// if check pass, convert n.Right to (gno) pt type,
rn := Expr(Call(pt.String(), n.Right))
// and convert result back.
@@ -1134,7 +1165,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
}
if !isUntyped(rt) { // right is typed
- checkOrConvertType(store, last, &n.Left, rt, false)
+ checkOrConvertType(store, last, n, &n.Left, rt, false)
} else {
if shouldSwapOnSpecificity(lt, rt) {
checkUntypedShiftExpr(n.Right)
@@ -1145,10 +1176,10 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
}
} else if lcx.T == nil { // LHS is nil.
// convert n.Left to typed-nil type.
- checkOrConvertType(store, last, &n.Left, rt, false)
+ checkOrConvertType(store, last, n, &n.Left, rt, false)
} else {
if isUntyped(rt) {
- checkOrConvertType(store, last, &n.Right, lt, false)
+ checkOrConvertType(store, last, n, &n.Right, lt, false)
}
}
} else if ric { // right is const, left is not
@@ -1166,7 +1197,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
// convert n.Left to (gno) pt type,
ln := Expr(Call(pt.String(), n.Left))
// convert n.Right to pt type,
- checkOrConvertType(store, last, &n.Right, pt, false)
+ checkOrConvertType(store, last, n, &n.Right, pt, false)
// and convert result back.
tx := constType(n, lnt)
// reset/create n2 to preprocess left child.
@@ -1192,7 +1223,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
}
// both untyped, e.g. 1< float64.
// (const) untyped bigint -> int.
if !constConverted {
- convertConst(store, last, arg0, nil)
+ convertConst(store, last, n, arg0, nil)
}
// evaluate the new expression.
cx := evalConst(store, last, n)
@@ -1377,15 +1408,15 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
if isUntyped(at) {
switch arg0.Op {
case EQL, NEQ, LSS, GTR, LEQ, GEQ:
- assertAssignableTo(at, ct, false)
+ assertAssignableTo(n, at, ct, false)
break
default:
- checkOrConvertType(store, last, &n.Args[0], ct, false)
+ checkOrConvertType(store, last, n, &n.Args[0], ct, false)
}
}
case *UnaryExpr:
if isUntyped(at) {
- checkOrConvertType(store, last, &n.Args[0], ct, false)
+ checkOrConvertType(store, last, n, &n.Args[0], ct, false)
}
default:
// do nothing
@@ -1529,7 +1560,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
panic("should not happen")
}
// Specify function param/result generics.
- sft := ft.Specify(store, argTVs, isVarg)
+ sft := ft.Specify(store, n, argTVs, isVarg)
spts := sft.Params
srts := FieldTypeList(sft.Results).Types()
// If generics were specified, override attr
@@ -1555,12 +1586,12 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
for i, tv := range argTVs {
if hasVarg {
if (len(spts) - 1) <= i {
- assertAssignableTo(tv.T, spts[len(spts)-1].Type.Elem(), true)
+ assertAssignableTo(n, tv.T, spts[len(spts)-1].Type.Elem(), true)
} else {
- assertAssignableTo(tv.T, spts[i].Type, true)
+ assertAssignableTo(n, tv.T, spts[i].Type, true)
}
} else {
- assertAssignableTo(tv.T, spts[i].Type, true)
+ assertAssignableTo(n, tv.T, spts[i].Type, true)
}
}
} else {
@@ -1571,16 +1602,16 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
if len(spts) <= i {
panic("expected final vargs slice but got many")
}
- checkOrConvertType(store, last, &n.Args[i], spts[i].Type, true)
+ checkOrConvertType(store, last, n, &n.Args[i], spts[i].Type, true)
} else {
- checkOrConvertType(store, last, &n.Args[i],
+ checkOrConvertType(store, last, n, &n.Args[i],
spts[len(spts)-1].Type.Elem(), true)
}
} else {
- checkOrConvertType(store, last, &n.Args[i], spts[i].Type, true)
+ checkOrConvertType(store, last, n, &n.Args[i], spts[i].Type, true)
}
} else {
- checkOrConvertType(store, last, &n.Args[i], spts[i].Type, true)
+ checkOrConvertType(store, last, n, &n.Args[i], spts[i].Type, true)
}
}
}
@@ -1601,10 +1632,10 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
case StringKind, ArrayKind, SliceKind:
// Replace const index with int *ConstExpr,
// or if not const, assert integer type..
- checkOrConvertIntegerKind(store, last, n.Index)
+ checkOrConvertIntegerKind(store, last, n, n.Index)
case MapKind:
mt := baseOf(gnoTypeOf(store, dt)).(*MapType)
- checkOrConvertType(store, last, &n.Index, mt.Key, false)
+ checkOrConvertType(store, last, n, &n.Index, mt.Key, false)
default:
panic(fmt.Sprintf(
"unexpected index base kind for type %s",
@@ -1615,15 +1646,15 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
case *SliceExpr:
// Replace const L/H/M with int *ConstExpr,
// or if not const, assert integer type..
- checkOrConvertIntegerKind(store, last, n.Low)
- checkOrConvertIntegerKind(store, last, n.High)
- checkOrConvertIntegerKind(store, last, n.Max)
+ checkOrConvertIntegerKind(store, last, n, n.Low)
+ checkOrConvertIntegerKind(store, last, n, n.High)
+ checkOrConvertIntegerKind(store, last, n, n.Max)
// if n.X is untyped, convert to corresponding type
t := evalStaticTypeOf(store, last, n.X)
if isUntyped(t) {
dt := defaultTypeOf(t)
- checkOrConvertType(store, last, &n.X, dt, false)
+ checkOrConvertType(store, last, n, &n.X, dt, false)
}
// TRANS_LEAVE -----------------------
@@ -1633,7 +1664,8 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
}
// Type assertions on the blank identifier are illegal.
- if nx, ok := n.X.(*NameExpr); ok && string(nx.Name) == blankIdentifier {
+
+ if isBlankIdentifier(n.X) {
panic("cannot use _ as value or type")
}
@@ -1701,28 +1733,28 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
key := n.Elts[i].Key.(*NameExpr).Name
path := cclt.GetPathForName(key)
ft := cclt.GetStaticTypeOfAt(path)
- checkOrConvertType(store, last, &n.Elts[i].Value, ft, false)
+ checkOrConvertType(store, last, n, &n.Elts[i].Value, ft, false)
}
} else {
for i := 0; i < len(n.Elts); i++ {
ft := cclt.Fields[i].Type
- checkOrConvertType(store, last, &n.Elts[i].Value, ft, false)
+ checkOrConvertType(store, last, n, &n.Elts[i].Value, ft, false)
}
}
case *ArrayType:
for i := 0; i < len(n.Elts); i++ {
- convertType(store, last, &n.Elts[i].Key, IntType)
- checkOrConvertType(store, last, &n.Elts[i].Value, cclt.Elt, false)
+ convertType(store, last, n, &n.Elts[i].Key, IntType)
+ checkOrConvertType(store, last, n, &n.Elts[i].Value, cclt.Elt, false)
}
case *SliceType:
for i := 0; i < len(n.Elts); i++ {
- convertType(store, last, &n.Elts[i].Key, IntType)
- checkOrConvertType(store, last, &n.Elts[i].Value, cclt.Elt, false)
+ convertType(store, last, n, &n.Elts[i].Key, IntType)
+ checkOrConvertType(store, last, n, &n.Elts[i].Value, cclt.Elt, false)
}
case *MapType:
for i := 0; i < len(n.Elts); i++ {
- checkOrConvertType(store, last, &n.Elts[i].Key, cclt.Key, false)
- checkOrConvertType(store, last, &n.Elts[i].Value, cclt.Value, false)
+ checkOrConvertType(store, last, n, &n.Elts[i].Key, cclt.Key, false)
+ checkOrConvertType(store, last, n, &n.Elts[i].Value, cclt.Value, false)
}
case *NativeType:
clt = cclt.GnoType(store)
@@ -1922,7 +1954,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
// TRANS_LEAVE -----------------------
case *FieldTypeExpr:
// Replace const Tag with default *ConstExpr.
- convertIfConst(store, last, n.Tag)
+ convertIfConst(store, last, n, n.Tag)
// TRANS_LEAVE -----------------------
case *ArrayTypeExpr:
@@ -1931,7 +1963,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
} else {
// Replace const Len with int *ConstExpr.
cx := evalConst(store, last, n.Len)
- convertConst(store, last, cx, IntType)
+ convertConst(store, last, n, cx, IntType)
n.Len = cx
}
// NOTE: For all TypeExprs, the node is not replaced
@@ -1972,7 +2004,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
// Rhs consts become default *ConstExprs.
for _, rx := range n.Rhs {
// NOTE: does nothing if rx is "nil".
- convertIfConst(store, last, rx)
+ convertIfConst(store, last, n, rx)
}
nameExprs := make(NameExprs, len(n.Lhs))
@@ -1980,7 +2012,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
nameExprs[i] = *n.Lhs[i].(*NameExpr)
}
- defineOrDecl(store, last, false, nameExprs, nil, n.Rhs)
+ defineOrDecl(store, last, n, false, nameExprs, nil, n.Rhs)
} else { // ASSIGN, or assignment operation (+=, -=, <<=, etc.)
// NOTE: Keep in sync with DEFINE above.
if len(n.Lhs) > len(n.Rhs) {
@@ -2069,11 +2101,11 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
} else { // len(Lhs) == len(Rhs)
if n.Op == SHL_ASSIGN || n.Op == SHR_ASSIGN {
// Special case if shift assign <<= or >>=.
- convertType(store, last, &n.Rhs[0], UintType)
+ convertType(store, last, n, &n.Rhs[0], UintType)
} else if n.Op == ADD_ASSIGN || n.Op == SUB_ASSIGN || n.Op == MUL_ASSIGN || n.Op == QUO_ASSIGN || n.Op == REM_ASSIGN {
// e.g. a += b, single value for lhs and rhs,
lt := evalStaticTypeOf(store, last, n.Lhs[0])
- checkOrConvertType(store, last, &n.Rhs[0], lt, true)
+ checkOrConvertType(store, last, n, &n.Rhs[0], lt, true)
} else { // all else, like BAND_ASSIGN, etc
// General case: a, b = x, y.
for i, lx := range n.Lhs {
@@ -2083,7 +2115,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
}
// if lt is interface, nothing will happen
- checkOrConvertType(store, last, &n.Rhs[i], lt, true)
+ checkOrConvertType(store, last, n, &n.Rhs[i], lt, true)
}
}
}
@@ -2160,12 +2192,12 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
// TRANS_LEAVE -----------------------
case *ForStmt:
// Cond consts become bool *ConstExprs.
- checkOrConvertBoolKind(store, last, n.Cond)
+ checkOrConvertBoolKind(store, last, n, n.Cond)
// TRANS_LEAVE -----------------------
case *IfStmt:
// Cond consts become bool *ConstExprs.
- checkOrConvertBoolKind(store, last, n.Cond)
+ checkOrConvertBoolKind(store, last, n, n.Cond)
// TRANS_LEAVE -----------------------
case *RangeStmt:
@@ -2221,7 +2253,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
// XXX how to deal?
panic("not yet implemented")
} else {
- checkOrConvertType(store, last, &n.Results[i], rt, false)
+ checkOrConvertType(store, last, n, &n.Results[i], rt, false)
}
}
}
@@ -2229,7 +2261,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
// TRANS_LEAVE -----------------------
case *SendStmt:
// Value consts become default *ConstExprs.
- checkOrConvertType(store, last, &n.Value, nil, false)
+ checkOrConvertType(store, last, n, &n.Value, nil, false)
// TRANS_LEAVE -----------------------
case *SelectCaseStmt:
@@ -2282,7 +2314,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
// runDeclaration(), as this uses OpStaticTypeOf.
}
- defineOrDecl(store, last, n.Const, n.NameExprs, n.Type, n.Values)
+ defineOrDecl(store, last, n, n.Const, n.NameExprs, n.Type, n.Values)
// TODO make note of constance in static block for
// future use, or consider "const paths". set as
@@ -2331,6 +2363,10 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
// }
*dst = *dt2
}
+ case PrimitiveType:
+ dst = tmp.(PrimitiveType)
+ case *PointerType:
+ *dst = *(tmp.(*PointerType))
default:
panic(fmt.Sprintf("unexpected type declaration type %v",
reflect.TypeOf(dst)))
@@ -2358,6 +2394,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node {
func defineOrDecl(
store Store,
bn BlockNode,
+ n Node,
isConst bool,
nameExprs []NameExpr,
typeExpr Expr,
@@ -2374,9 +2411,9 @@ func defineOrDecl(
tvs := make([]TypedValue, numNames)
if numVals == 1 && numNames > 1 {
- parseMultipleAssignFromOneExpr(sts, tvs, store, bn, nameExprs, typeExpr, valueExprs[0])
+ parseMultipleAssignFromOneExpr(store, bn, n, sts, tvs, nameExprs, typeExpr, valueExprs[0])
} else {
- parseAssignFromExprList(sts, tvs, store, bn, isConst, nameExprs, typeExpr, valueExprs)
+ parseAssignFromExprList(store, bn, n, sts, tvs, isConst, nameExprs, typeExpr, valueExprs)
}
node := skipFile(bn)
@@ -2395,10 +2432,11 @@ func defineOrDecl(
// parseAssignFromExprList parses assignment to multiple variables from a list of expressions.
// This function will alter the value of sts, tvs.
func parseAssignFromExprList(
- sts []Type,
- tvs []TypedValue,
store Store,
bn BlockNode,
+ n Node,
+ sts []Type,
+ tvs []TypedValue,
isConst bool,
nameExprs []NameExpr,
typeExpr Expr,
@@ -2425,7 +2463,7 @@ func parseAssignFromExprList(
}
// Convert if const to nt.
for i := range valueExprs {
- checkOrConvertType(store, bn, &valueExprs[i], nt, false)
+ checkOrConvertType(store, bn, n, &valueExprs[i], nt, false)
}
} else if isConst {
// Derive static type from values.
@@ -2437,10 +2475,10 @@ func parseAssignFromExprList(
// Convert n.Value to default type.
for i, vx := range valueExprs {
if cx, ok := vx.(*ConstExpr); ok {
- convertConst(store, bn, cx, nil)
+ convertConst(store, bn, n, cx, nil)
// convertIfConst(store, last, vx)
} else {
- checkOrConvertType(store, bn, &vx, nil, false)
+ checkOrConvertType(store, bn, n, &vx, nil, false)
}
vt := evalStaticTypeOf(store, bn, vx)
sts[i] = vt
@@ -2481,10 +2519,11 @@ func parseAssignFromExprList(
// - a, b := n.(T)
// - a, b := n[i], where n is a map
func parseMultipleAssignFromOneExpr(
- sts []Type,
- tvs []TypedValue,
store Store,
bn BlockNode,
+ n Node,
+ sts []Type,
+ tvs []TypedValue,
nameExprs []NameExpr,
typeExpr Expr,
valueExpr Expr,
@@ -2542,7 +2581,7 @@ func parseMultipleAssignFromOneExpr(
if st != nil {
tt := tuple.Elts[i]
- if checkAssignableTo(tt, st, false) != nil {
+ if checkAssignableTo(n, tt, st, false) != nil {
panic(
fmt.Sprintf(
"cannot use %v (value of type %s) as %s value in assignment",
@@ -3090,7 +3129,7 @@ func evalStaticType(store Store, last BlockNode, x Expr) Type {
// See comment in evalStaticTypeOfRaw.
if store != nil && pn.PkgPath != uversePkgPath {
pv := pn.NewPackage() // temporary
- store = store.BeginTransaction(nil, nil)
+ store = store.BeginTransaction(nil, nil, nil)
store.SetCachePackage(pv)
}
m := NewMachine(pn.PkgPath, store)
@@ -3158,7 +3197,7 @@ func evalStaticTypeOfRaw(store Store, last BlockNode, x Expr) (t Type) {
// yet predefined this time around.
if store != nil && pn.PkgPath != uversePkgPath {
pv := pn.NewPackage() // temporary
- store = store.BeginTransaction(nil, nil)
+ store = store.BeginTransaction(nil, nil, nil)
store.SetCachePackage(pv)
}
m := NewMachine(pn.PkgPath, store)
@@ -3466,14 +3505,14 @@ func isConstType(x Expr) bool {
}
// check before convert type
-func checkOrConvertType(store Store, last BlockNode, x *Expr, t Type, autoNative bool) {
+func checkOrConvertType(store Store, last BlockNode, n Node, x *Expr, t Type, autoNative bool) {
if debug {
debug.Printf("checkOrConvertType, *x: %v:, t:%v \n", *x, t)
}
if cx, ok := (*x).(*ConstExpr); ok {
if _, ok := t.(*NativeType); !ok { // not native type, refer to time4_native.gno.
// e.g. int(1) == int8(1)
- assertAssignableTo(cx.T, t, autoNative)
+ assertAssignableTo(n, cx.T, t, autoNative)
}
} else if bx, ok := (*x).(*BinaryExpr); ok && (bx.Op == SHL || bx.Op == SHR) {
xt := evalStaticTypeOf(store, last, *x)
@@ -3482,22 +3521,22 @@ func checkOrConvertType(store Store, last BlockNode, x *Expr, t Type, autoNative
}
if isUntyped(xt) {
// check assignable first, see: types/shift_b6.gno
- assertAssignableTo(xt, t, autoNative)
+ assertAssignableTo(n, xt, t, autoNative)
if t == nil || t.Kind() == InterfaceKind {
t = defaultTypeOf(xt)
}
bx.assertShiftExprCompatible2(t)
- checkOrConvertType(store, last, &bx.Left, t, autoNative)
+ checkOrConvertType(store, last, n, &bx.Left, t, autoNative)
} else {
- assertAssignableTo(xt, t, autoNative)
+ assertAssignableTo(n, xt, t, autoNative)
}
return
} else if *x != nil {
xt := evalStaticTypeOf(store, last, *x)
if t != nil {
- assertAssignableTo(xt, t, autoNative)
+ assertAssignableTo(n, xt, t, autoNative)
}
if isUntyped(xt) {
// Push type into expr if qualifying binary expr.
@@ -3509,8 +3548,8 @@ func checkOrConvertType(store Store, last BlockNode, x *Expr, t Type, autoNative
rt := evalStaticTypeOf(store, last, bx.Right)
if t != nil {
// push t into bx.Left and bx.Right
- checkOrConvertType(store, last, &bx.Left, t, autoNative)
- checkOrConvertType(store, last, &bx.Right, t, autoNative)
+ checkOrConvertType(store, last, n, &bx.Left, t, autoNative)
+ checkOrConvertType(store, last, n, &bx.Right, t, autoNative)
return
} else {
if shouldSwapOnSpecificity(lt, rt) {
@@ -3521,11 +3560,11 @@ func checkOrConvertType(store Store, last BlockNode, x *Expr, t Type, autoNative
// without a specific context type, '1.0< 0 {
- loc.Line = nline
- loc.Column = d.GetColumn()
- }
- if rerr, ok := r.(error); ok {
- // NOTE: gotuna/gorilla expects error exceptions.
- panic(errors.Wrap(rerr, loc.String()))
- } else {
- // NOTE: gotuna/gorilla expects error exceptions.
- panic(fmt.Errorf("%s: %v", loc.String(), r))
- }
- }
- }()
+ defer doRecover([]BlockNode{last}, d)
stack := &[]Name{}
return predefineNow2(store, last, d, stack)
}
@@ -4099,10 +4124,10 @@ func predefineNow2(store Store, last BlockNode, d Decl, stack *[]Name) (Decl, bo
// check base type of receiver type, should not be pointer type or interface type
assertValidReceiverType := func(t Type) {
if _, ok := t.(*PointerType); ok {
- panic(fmt.Sprintf("invalid receiver type %v (base type is pointer type)\n", rt))
+ panic(fmt.Sprintf("invalid receiver type %v (base type is pointer type)", rt))
}
if _, ok := t.(*InterfaceType); ok {
- panic(fmt.Sprintf("invalid receiver type %v (base type is interface type)\n", rt))
+ panic(fmt.Sprintf("invalid receiver type %v (base type is interface type)", rt))
}
}
@@ -4220,6 +4245,12 @@ func tryPredefine(store Store, last BlockNode, d Decl) (un Name) {
})
d.Path = last.GetPathForName(store, d.Name)
case *ValueDecl:
+ // check for blank identifier in type
+ // e.g., `var x _`
+ if isBlankIdentifier(d.Type) {
+ panic("cannot use _ as value or type")
+ }
+
un = findUndefined(store, last, d.Type)
if un != "" {
return
@@ -4262,6 +4293,17 @@ func tryPredefine(store Store, last BlockNode, d Decl) (un Name) {
case *StarExpr:
t = &PointerType{}
case *NameExpr:
+ // check for blank identifier in type
+ // e.g., `type T _`
+ if isBlankIdentifier(tx) {
+ panic("cannot use _ as value or type")
+ }
+
+ // do not allow nil as type.
+ if tx.Name == "nil" {
+ panic("nil is not a type")
+ }
+
if tv := last.GetValueRef(store, tx.Name, true); tv != nil {
t = tv.GetType()
if dt, ok := t.(*DeclaredType); ok {
@@ -4269,9 +4311,6 @@ func tryPredefine(store Store, last BlockNode, d Decl) (un Name) {
// predefineNow preprocessed dependent types.
panic("should not happen")
}
- } else {
- // all names are declared types.
- panic("should not happen")
}
} else if idx, ok := UverseNode().GetLocalIndex(tx.Name); ok {
// uverse name
diff --git a/gnovm/pkg/gnolang/preprocess_test.go b/gnovm/pkg/gnolang/preprocess_test.go
deleted file mode 100644
index 53ad97dd972..00000000000
--- a/gnovm/pkg/gnolang/preprocess_test.go
+++ /dev/null
@@ -1,62 +0,0 @@
-package gnolang
-
-import (
- "fmt"
- "reflect"
- "testing"
- "time"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestPreprocess_BinaryExpressionOneNative(t *testing.T) {
- pn := NewPackageNode("time", "time", nil)
- pn.DefineGoNativeConstValue("Millisecond", time.Millisecond)
- pn.DefineGoNativeConstValue("Second", time.Second)
- pn.DefineGoNativeType(reflect.TypeOf(time.Duration(0)))
- pv := pn.NewPackage()
- store := gonativeTestStore(pn, pv)
- store.SetBlockNode(pn)
-
- const src = `package main
- import "time"
-func main() {
- var a int64 = 2
- println(time.Second * a)
-
-}`
- n := MustParseFile("main.go", src)
-
- defer func() {
- err := recover()
- assert.Contains(t, fmt.Sprint(err), "incompatible operands in binary expression")
- }()
- initStaticBlocks(store, pn, n)
- Preprocess(store, pn, n)
-}
-
-func TestPreprocess_BinaryExpressionBothNative(t *testing.T) {
- pn := NewPackageNode("time", "time", nil)
- pn.DefineGoNativeConstValue("March", time.March)
- pn.DefineGoNativeConstValue("Wednesday", time.Wednesday)
- pn.DefineGoNativeType(reflect.TypeOf(time.Month(0)))
- pn.DefineGoNativeType(reflect.TypeOf(time.Weekday(0)))
- pv := pn.NewPackage()
- store := gonativeTestStore(pn, pv)
- store.SetBlockNode(pn)
-
- const src = `package main
- import "time"
-func main() {
- println(time.March * time.Wednesday)
-
-}`
- n := MustParseFile("main.go", src)
-
- defer func() {
- err := recover()
- assert.Contains(t, fmt.Sprint(err), "incompatible operands in binary expression")
- }()
- initStaticBlocks(store, pn, n)
- Preprocess(store, pn, n)
-}
diff --git a/gnovm/pkg/gnolang/realm.go b/gnovm/pkg/gnolang/realm.go
index 5913f13a0f7..d25d456edf3 100644
--- a/gnovm/pkg/gnolang/realm.go
+++ b/gnovm/pkg/gnolang/realm.go
@@ -1132,7 +1132,7 @@ func copyValueWithRefs(val Value) Value {
if cv.Closure != nil {
closure = toRefValue(cv.Closure)
}
- // nativeBody funcs which don't come from NativeStore (and thus don't
+ // nativeBody funcs which don't come from NativeResolver (and thus don't
// have NativePkg/Name) can't be persisted, and should not be able
// to get here anyway.
if cv.nativeBody != nil && cv.NativePkg == "" {
diff --git a/gnovm/pkg/gnolang/static_analysis.go b/gnovm/pkg/gnolang/static_analysis.go
index 311a0d42feb..7094ccbb4c8 100644
--- a/gnovm/pkg/gnolang/static_analysis.go
+++ b/gnovm/pkg/gnolang/static_analysis.go
@@ -108,6 +108,8 @@ func (s *staticAnalysis) staticAnalysisExpr(expr Expr) bool {
// indicating whether a statement is terminating or not
func (s *staticAnalysis) staticAnalysisStmt(stmt Stmt) bool {
switch n := stmt.(type) {
+ case *BlockStmt:
+ return s.staticAnalysisBlockStmt(n.Body)
case *BranchStmt:
switch n.Op {
case BREAK:
diff --git a/gnovm/pkg/gnolang/store.go b/gnovm/pkg/gnolang/store.go
index 9410eede29e..683f4c923d4 100644
--- a/gnovm/pkg/gnolang/store.go
+++ b/gnovm/pkg/gnolang/store.go
@@ -11,6 +11,7 @@ import (
"github.com/gnolang/gno/gnovm/pkg/gnolang/internal/txlog"
"github.com/gnolang/gno/tm2/pkg/amino"
"github.com/gnolang/gno/tm2/pkg/colors"
+ "github.com/gnolang/gno/tm2/pkg/overflow"
"github.com/gnolang/gno/tm2/pkg/store"
"github.com/gnolang/gno/tm2/pkg/store/utils"
stringz "github.com/gnolang/gno/tm2/pkg/strings"
@@ -25,15 +26,15 @@ import (
// cause writes to happen to the store, such as MemPackages to iavlstore.
type PackageGetter func(pkgPath string, store Store) (*PackageNode, *PackageValue)
-// NativeStore is a function which can retrieve native bodies of native functions.
-type NativeStore func(pkgName string, name Name) func(m *Machine)
+// NativeResolver is a function which can retrieve native bodies of native functions.
+type NativeResolver func(pkgName string, name Name) func(m *Machine)
// Store is the central interface that specifies the communications between the
// GnoVM and the underlying data store; currently, generally the gno.land
// blockchain, or the file system.
type Store interface {
// STABLE
- BeginTransaction(baseStore, iavlStore store.Store) TransactionStore
+ BeginTransaction(baseStore, iavlStore store.Store, gasMeter store.GasMeter) TransactionStore
SetPackageGetter(PackageGetter)
GetPackage(pkgPath string, isImport bool) *PackageValue
SetCachePackage(*PackageValue)
@@ -50,8 +51,8 @@ type Store interface {
GetBlockNode(Location) BlockNode // to get a PackageNode, use PackageNodeLocation().
GetBlockNodeSafe(Location) BlockNode
SetBlockNode(BlockNode)
+
// UNSTABLE
- SetStrictGo2GnoMapping(bool)
Go2GnoType(rt reflect.Type) Type
GetAllocator() *Allocator
NumMemPackages() int64
@@ -63,12 +64,11 @@ type Store interface {
GetMemFile(path string, name string) *gnovm.MemFile
IterMemPackage() <-chan *gnovm.MemPackage
ClearObjectCache() // run before processing a message
- SetNativeStore(NativeStore) // for "new" natives XXX
+ SetNativeResolver(NativeResolver) // for "new" natives XXX
GetNative(pkgPath string, name Name) func(m *Machine) // for "new" natives XXX
SetLogStoreOps(enabled bool)
SprintStoreOps() string
LogSwitchRealm(rlmpath string) // to mark change of realm boundaries
- ClearCache()
Print()
}
@@ -83,6 +83,47 @@ type TransactionStore interface {
Write()
}
+// Gas consumption descriptors.
+const (
+ GasGetObjectDesc = "GetObjectPerByte"
+ GasSetObjectDesc = "SetObjectPerByte"
+ GasGetTypeDesc = "GetTypePerByte"
+ GasSetTypeDesc = "SetTypePerByte"
+ GasGetPackageRealmDesc = "GetPackageRealmPerByte"
+ GasSetPackageRealmDesc = "SetPackageRealmPerByte"
+ GasAddMemPackageDesc = "AddMemPackagePerByte"
+ GasGetMemPackageDesc = "GetMemPackagePerByte"
+ GasDeleteObjectDesc = "DeleteObjectFlat"
+)
+
+// GasConfig defines gas cost for each operation on KVStores
+type GasConfig struct {
+ GasGetObject int64
+ GasSetObject int64
+ GasGetType int64
+ GasSetType int64
+ GasGetPackageRealm int64
+ GasSetPackageRealm int64
+ GasAddMemPackage int64
+ GasGetMemPackage int64
+ GasDeleteObject int64
+}
+
+// DefaultGasConfig returns a default gas config for KVStores.
+func DefaultGasConfig() GasConfig {
+ return GasConfig{
+ GasGetObject: 16, // per byte cost
+ GasSetObject: 16, // per byte cost
+ GasGetType: 52, // per byte cost
+ GasSetType: 52, // per byte cost
+ GasGetPackageRealm: 524, // per byte cost
+ GasSetPackageRealm: 524, // per byte cost
+ GasAddMemPackage: 8, // per byte cost
+ GasGetMemPackage: 8, // per byte cost
+ GasDeleteObject: 3715, // flat cost
+ }
+}
+
type defaultStore struct {
// underlying stores used to keep data
baseStore store.Store // for objects, types, nodes
@@ -97,12 +138,15 @@ type defaultStore struct {
// store configuration; cannot be modified in a transaction
pkgGetter PackageGetter // non-realm packages
cacheNativeTypes map[reflect.Type]Type // reflect doc: reflect.Type are comparable
- nativeStore NativeStore // for injecting natives
- go2gnoStrict bool // if true, native->gno type conversion must be registered.
+ nativeResolver NativeResolver // for injecting natives
// transient
opslog []StoreOp // for debugging and testing.
current []string // for detecting import cycles.
+
+ // gas
+ gasMeter store.GasMeter
+ gasConfig GasConfig
}
func NewStore(alloc *Allocator, baseStore, iavlStore store.Store) *defaultStore {
@@ -119,15 +163,15 @@ func NewStore(alloc *Allocator, baseStore, iavlStore store.Store) *defaultStore
// store configuration
pkgGetter: nil,
cacheNativeTypes: make(map[reflect.Type]Type),
- nativeStore: nil,
- go2gnoStrict: true,
+ nativeResolver: nil,
+ gasConfig: DefaultGasConfig(),
}
InitStoreCaches(ds)
return ds
}
// If nil baseStore and iavlStore, the baseStores are re-used.
-func (ds *defaultStore) BeginTransaction(baseStore, iavlStore store.Store) TransactionStore {
+func (ds *defaultStore) BeginTransaction(baseStore, iavlStore store.Store, gasMeter store.GasMeter) TransactionStore {
if baseStore == nil {
baseStore = ds.baseStore
}
@@ -148,8 +192,11 @@ func (ds *defaultStore) BeginTransaction(baseStore, iavlStore store.Store) Trans
// store configuration
pkgGetter: ds.pkgGetter,
cacheNativeTypes: ds.cacheNativeTypes,
- nativeStore: ds.nativeStore,
- go2gnoStrict: ds.go2gnoStrict,
+ nativeResolver: ds.nativeResolver,
+
+ // gas meter
+ gasMeter: gasMeter,
+ gasConfig: ds.gasConfig,
// transient
current: nil,
@@ -171,10 +218,6 @@ func (transactionStore) SetPackageGetter(pg PackageGetter) {
panic("SetPackageGetter may not be called in a transaction store")
}
-func (transactionStore) ClearCache() {
- panic("ClearCache may not be called in a transaction store")
-}
-
// XXX: we should block Go2GnoType, because it uses a global cache map;
// but it's called during preprocess and thus breaks some testing code.
// let's wait until we remove Go2Gno entirely.
@@ -183,12 +226,8 @@ func (transactionStore) ClearCache() {
// panic("Go2GnoType may not be called in a transaction store")
// }
-func (transactionStore) SetNativeStore(ns NativeStore) {
- panic("SetNativeStore may not be called in a transaction store")
-}
-
-func (transactionStore) SetStrictGo2GnoMapping(strict bool) {
- panic("SetStrictGo2GnoMapping may not be called in a transaction store")
+func (transactionStore) SetNativeResolver(ns NativeResolver) {
+ panic("SetNativeResolver may not be called in a transaction store")
}
// CopyCachesFromStore allows to copy a store's internal object, type and
@@ -308,6 +347,8 @@ func (ds *defaultStore) GetPackageRealm(pkgPath string) (rlm *Realm) {
if bz == nil {
return nil
}
+ gas := overflow.Mul64p(ds.gasConfig.GasGetPackageRealm, store.Gas(len(bz)))
+ ds.consumeGas(gas, GasGetPackageRealmDesc)
amino.MustUnmarshal(bz, &rlm)
if debug {
if rlm.ID != oid.PkgID {
@@ -323,6 +364,8 @@ func (ds *defaultStore) SetPackageRealm(rlm *Realm) {
oid := ObjectIDFromPkgPath(rlm.Path)
key := backendRealmKey(oid)
bz := amino.MustMarshal(rlm)
+ gas := overflow.Mul64p(ds.gasConfig.GasSetPackageRealm, store.Gas(len(bz)))
+ ds.consumeGas(gas, GasSetPackageRealmDesc)
ds.baseStore.Set([]byte(key), bz)
}
@@ -368,6 +411,8 @@ func (ds *defaultStore) loadObjectSafe(oid ObjectID) Object {
bz := hashbz[HashSize:]
var oo Object
ds.alloc.AllocateAmino(int64(len(bz)))
+ gas := overflow.Mul64p(ds.gasConfig.GasGetObject, store.Gas(len(bz)))
+ ds.consumeGas(gas, GasGetObjectDesc)
amino.MustUnmarshal(bz, &oo)
if debug {
if oo.GetObjectID() != oid {
@@ -391,6 +436,8 @@ func (ds *defaultStore) SetObject(oo Object) {
o2 := copyValueWithRefs(oo)
// marshal to binary.
bz := amino.MustMarshalAny(o2)
+ gas := overflow.Mul64p(ds.gasConfig.GasSetObject, store.Gas(len(bz)))
+ ds.consumeGas(gas, GasSetObjectDesc)
// set hash.
hash := HashBytes(bz) // XXX objectHash(bz)???
if len(hash) != HashSize {
@@ -440,6 +487,7 @@ func (ds *defaultStore) SetObject(oo Object) {
}
func (ds *defaultStore) DelObject(oo Object) {
+ ds.consumeGas(ds.gasConfig.GasDeleteObject, GasDeleteObjectDesc)
oid := oo.GetObjectID()
// delete from cache.
delete(ds.cacheObjects, oid)
@@ -476,6 +524,8 @@ func (ds *defaultStore) GetTypeSafe(tid TypeID) Type {
key := backendTypeKey(tid)
bz := ds.baseStore.Get([]byte(key))
if bz != nil {
+ gas := overflow.Mul64p(ds.gasConfig.GasGetType, store.Gas(len(bz)))
+ ds.consumeGas(gas, GasGetTypeDesc)
var tt Type
amino.MustUnmarshal(bz, &tt)
if debug {
@@ -498,8 +548,7 @@ func (ds *defaultStore) SetCacheType(tt Type) {
tid := tt.TypeID()
if tt2, exists := ds.cacheTypes.Get(tid); exists {
if tt != tt2 {
- // NOTE: not sure why this would happen.
- panic("should not happen")
+ panic(fmt.Sprintf("cannot re-register %q with different type", tid))
} else {
// already set.
}
@@ -523,6 +572,8 @@ func (ds *defaultStore) SetType(tt Type) {
key := backendTypeKey(tid)
tcopy := copyTypeWithRefs(tt)
bz := amino.MustMarshalAny(tcopy)
+ gas := overflow.Mul64p(ds.gasConfig.GasSetType, store.Gas(len(bz)))
+ ds.consumeGas(gas, GasSetTypeDesc)
ds.baseStore.Set([]byte(key), bz)
}
// save type to cache.
@@ -616,6 +667,8 @@ func (ds *defaultStore) AddMemPackage(memPkg *gnovm.MemPackage) {
ctr := ds.incGetPackageIndexCounter()
idxkey := []byte(backendPackageIndexKey(ctr))
bz := amino.MustMarshal(memPkg)
+ gas := overflow.Mul64p(ds.gasConfig.GasAddMemPackage, store.Gas(len(bz)))
+ ds.consumeGas(gas, GasAddMemPackageDesc)
ds.baseStore.Set(idxkey, []byte(memPkg.Path))
pathkey := []byte(backendPackagePathKey(memPkg.Path))
ds.iavlStore.Set(pathkey, bz)
@@ -643,6 +696,8 @@ func (ds *defaultStore) getMemPackage(path string, isRetry bool) *gnovm.MemPacka
}
return nil
}
+ gas := overflow.Mul64p(ds.gasConfig.GasGetMemPackage, store.Gas(len(bz)))
+ ds.consumeGas(gas, GasGetMemPackageDesc)
var memPkg *gnovm.MemPackage
amino.MustUnmarshal(bz, &memPkg)
@@ -689,6 +744,13 @@ func (ds *defaultStore) IterMemPackage() <-chan *gnovm.MemPackage {
}
}
+func (ds *defaultStore) consumeGas(gas int64, descriptor string) {
+ // In the tests, the defaultStore may not set the gas meter.
+ if ds.gasMeter != nil {
+ ds.gasMeter.ConsumeGas(gas, descriptor)
+ }
+}
+
// Unstable.
// This function is used to clear the object cache every transaction.
// It also sets a new allocator.
@@ -699,13 +761,13 @@ func (ds *defaultStore) ClearObjectCache() {
ds.SetCachePackage(Uverse())
}
-func (ds *defaultStore) SetNativeStore(ns NativeStore) {
- ds.nativeStore = ns
+func (ds *defaultStore) SetNativeResolver(ns NativeResolver) {
+ ds.nativeResolver = ns
}
func (ds *defaultStore) GetNative(pkgPath string, name Name) func(m *Machine) {
- if ds.nativeStore != nil {
- return ds.nativeStore(pkgPath, name)
+ if ds.nativeResolver != nil {
+ return ds.nativeResolver(pkgPath, name)
}
return nil
}
@@ -778,15 +840,6 @@ func (ds *defaultStore) LogSwitchRealm(rlmpath string) {
StoreOp{Type: StoreOpSwitchRealm, RlmPath: rlmpath})
}
-func (ds *defaultStore) ClearCache() {
- ds.cacheObjects = make(map[ObjectID]Object)
- ds.cacheTypes = txlog.GoMap[TypeID, Type](map[TypeID]Type{})
- ds.cacheNodes = txlog.GoMap[Location, BlockNode](map[Location]BlockNode{})
- ds.cacheNativeTypes = make(map[reflect.Type]Type)
- // restore builtin types to cache.
- InitStoreCaches(ds)
-}
-
// for debugging
func (ds *defaultStore) Print() {
fmt.Println(colors.Yellow("//----------------------------------------"))
@@ -841,7 +894,7 @@ func backendPackageIndexKey(index uint64) string {
}
func backendPackagePathKey(path string) string {
- return fmt.Sprintf("pkg:" + path)
+ return "pkg:" + path
}
// ----------------------------------------
diff --git a/gnovm/pkg/gnolang/store_test.go b/gnovm/pkg/gnolang/store_test.go
index 40f84b65375..c4fc52a9c7c 100644
--- a/gnovm/pkg/gnolang/store_test.go
+++ b/gnovm/pkg/gnolang/store_test.go
@@ -17,7 +17,7 @@ func TestTransactionStore(t *testing.T) {
st := NewStore(nil, tm2Store, tm2Store)
wrappedTm2Store := tm2Store.CacheWrap()
- txSt := st.BeginTransaction(wrappedTm2Store, wrappedTm2Store)
+ txSt := st.BeginTransaction(wrappedTm2Store, wrappedTm2Store, nil)
m := NewMachineWithOptions(MachineOptions{
PkgPath: "hello",
Store: txSt,
@@ -58,9 +58,7 @@ func TestTransactionStore_blockedMethods(t *testing.T) {
// These methods should panic as they modify store settings, which should
// only be changed in the root store.
assert.Panics(t, func() { transactionStore{}.SetPackageGetter(nil) })
- assert.Panics(t, func() { transactionStore{}.ClearCache() })
- assert.Panics(t, func() { transactionStore{}.SetNativeStore(nil) })
- assert.Panics(t, func() { transactionStore{}.SetStrictGo2GnoMapping(false) })
+ assert.Panics(t, func() { transactionStore{}.SetNativeResolver(nil) })
}
func TestCopyFromCachedStore(t *testing.T) {
@@ -88,7 +86,7 @@ func TestCopyFromCachedStore(t *testing.T) {
d1s := dbadapter.StoreConstructor(d1, storetypes.StoreOptions{})
d2s := dbadapter.StoreConstructor(d2, storetypes.StoreOptions{})
destStore := NewStore(nil, d1s, d2s)
- destStoreTx := destStore.BeginTransaction(nil, nil) // CopyFromCachedStore requires a tx store.
+ destStoreTx := destStore.BeginTransaction(nil, nil, nil) // CopyFromCachedStore requires a tx store.
CopyFromCachedStore(destStoreTx, cachedStore, c1s, c2s)
destStoreTx.Write()
diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go
index e786bed683f..70166116fcc 100644
--- a/gnovm/pkg/gnolang/type_check.go
+++ b/gnovm/pkg/gnolang/type_check.go
@@ -208,8 +208,8 @@ func checkSame(at, bt Type, msg string) error {
return nil
}
-func assertAssignableTo(xt, dt Type, autoNative bool) {
- err := checkAssignableTo(xt, dt, autoNative)
+func assertAssignableTo(n Node, xt, dt Type, autoNative bool) {
+ err := checkAssignableTo(n, xt, dt, autoNative)
if err != nil {
panic(err.Error())
}
@@ -282,14 +282,30 @@ func checkValDefineMismatch(n Node) {
// Assert that xt can be assigned as dt (dest type).
// If autoNative is true, a broad range of xt can match against
// a target native dt type, if and only if dt is a native type.
-func checkAssignableTo(xt, dt Type, autoNative bool) error {
+func checkAssignableTo(n Node, xt, dt Type, autoNative bool) error {
if debug {
debug.Printf("checkAssignableTo, xt: %v dt: %v \n", xt, dt)
}
// case0
if xt == nil { // see test/files/types/eql_0f18
+ if dt == nil || dt.Kind() == InterfaceKind {
+ return nil
+ }
if !maybeNil(dt) {
- panic(fmt.Sprintf("invalid operation, nil can not be compared to %v", dt))
+ switch n := n.(type) {
+ case *ValueDecl:
+ panic(fmt.Sprintf("cannot use nil as %v value in variable declaration", dt))
+ case *AssignStmt:
+ panic(fmt.Sprintf("cannot use nil as %v value in assignment", dt))
+ case *CompositeLitExpr:
+ panic(fmt.Sprintf("cannot use nil as %v value in array, slice literal or map literal", dt))
+ case *CallExpr:
+ panic(fmt.Sprintf("cannot use nil as %v value in argument to %v", dt, n.Func))
+ case *BinaryExpr:
+ panic(fmt.Sprintf("invalid operation: %v (mismatched types %v and untyped nil)", n, dt))
+ default:
+ panic(fmt.Sprintf("cannot use nil as %v value", dt))
+ }
}
return nil
} else if dt == nil { // _ = xxx, assign8.gno, 0f31. else cases?
@@ -501,7 +517,7 @@ func checkAssignableTo(xt, dt Type, autoNative bool) error {
}
case *PointerType: // case 4 from here on
if pt, ok := xt.(*PointerType); ok {
- return checkAssignableTo(pt.Elt, cdt.Elt, false)
+ return checkAssignableTo(n, pt.Elt, cdt.Elt, false)
}
case *ArrayType:
if at, ok := xt.(*ArrayType); ok {
@@ -523,7 +539,7 @@ func checkAssignableTo(xt, dt Type, autoNative bool) error {
case *SliceType:
if st, ok := xt.(*SliceType); ok {
if cdt.Vrd {
- return checkAssignableTo(st.Elt, cdt.Elt, false)
+ return checkAssignableTo(n, st.Elt, cdt.Elt, false)
} else {
err := checkSame(st.Elt, cdt.Elt, "")
if err != nil {
@@ -631,7 +647,7 @@ func (x *BinaryExpr) assertShiftExprCompatible1(store Store, last BlockNode, lt,
if lt == UntypedBigdecType {
// 1.0 << 1
if lic && ric {
- convertConst(store, last, lcx, UntypedBigintType)
+ convertConst(store, last, x, lcx, UntypedBigintType)
return
}
}
@@ -694,11 +710,11 @@ func (x *BinaryExpr) AssertCompatible(lt, rt Type) {
case EQL, NEQ:
assertComparable(xt, dt)
if !isUntyped(xt) && !isUntyped(dt) {
- assertAssignableTo(xt, dt, false)
+ assertAssignableTo(x, xt, dt, false)
}
case LSS, LEQ, GTR, GEQ:
if checker, ok := binaryChecker[x.Op]; ok {
- x.checkCompatibility(xt, dt, checker, x.Op.TokenString())
+ x.checkCompatibility(x, xt, dt, checker, x.Op.TokenString())
} else {
panic(fmt.Sprintf("checker for %s does not exist", x.Op))
}
@@ -707,7 +723,7 @@ func (x *BinaryExpr) AssertCompatible(lt, rt Type) {
}
} else {
if checker, ok := binaryChecker[x.Op]; ok {
- x.checkCompatibility(xt, dt, checker, x.Op.TokenString())
+ x.checkCompatibility(x, xt, dt, checker, x.Op.TokenString())
} else {
panic(fmt.Sprintf("checker for %s does not exist", x.Op))
}
@@ -735,14 +751,14 @@ func (x *BinaryExpr) AssertCompatible(lt, rt Type) {
// The function checkOrConvertType will be invoked after this check.
// NOTE: dt is established based on a specificity check between xt and dt,
// confirming dt as the appropriate destination type for this context.
-func (x *BinaryExpr) checkCompatibility(xt, dt Type, checker func(t Type) bool, OpStr string) {
+func (x *BinaryExpr) checkCompatibility(n Node, xt, dt Type, checker func(t Type) bool, OpStr string) {
if !checker(dt) {
panic(fmt.Sprintf("operator %s not defined on: %v", OpStr, kindString(dt)))
}
// if both typed
if !isUntyped(xt) && !isUntyped(dt) {
- err := checkAssignableTo(xt, dt, false)
+ err := checkAssignableTo(n, xt, dt, false)
if err != nil {
panic(fmt.Sprintf("invalid operation: mismatched types %v and %v", xt, dt))
}
@@ -807,19 +823,19 @@ func (x *RangeStmt) AssertCompatible(store Store, last BlockNode) {
xt := evalStaticTypeOf(store, last, x.X)
switch cxt := xt.(type) {
case *MapType:
- assertAssignableTo(cxt.Key, kt, false)
+ assertAssignableTo(x, cxt.Key, kt, false)
if vt != nil {
- assertAssignableTo(cxt.Value, vt, false)
+ assertAssignableTo(x, cxt.Value, vt, false)
}
case *SliceType:
assertIndexTypeIsInt(kt)
if vt != nil {
- assertAssignableTo(cxt.Elt, vt, false)
+ assertAssignableTo(x, cxt.Elt, vt, false)
}
case *ArrayType:
assertIndexTypeIsInt(kt)
if vt != nil {
- assertAssignableTo(cxt.Elt, vt, false)
+ assertAssignableTo(x, cxt.Elt, vt, false)
}
case PrimitiveType:
if cxt.Kind() == StringKind {
@@ -859,7 +875,7 @@ func (x *AssignStmt) AssertCompatible(store Store, last BlockNode) {
assertValidAssignLhs(store, last, lx)
if !isBlankIdentifier(lx) {
lxt := evalStaticTypeOf(store, last, lx)
- assertAssignableTo(cft.Results[i].Type, lxt, false)
+ assertAssignableTo(x, cft.Results[i].Type, lxt, false)
}
}
}
@@ -874,7 +890,7 @@ func (x *AssignStmt) AssertCompatible(store Store, last BlockNode) {
if !isBlankIdentifier(x.Lhs[0]) { // see composite3.gno
dt := evalStaticTypeOf(store, last, x.Lhs[0])
ift := evalStaticTypeOf(store, last, cx)
- assertAssignableTo(ift, dt, false)
+ assertAssignableTo(x, ift, dt, false)
}
// check second value
assertValidAssignLhs(store, last, x.Lhs[1])
@@ -897,12 +913,12 @@ func (x *AssignStmt) AssertCompatible(store Store, last BlockNode) {
if _, ok := cx.X.(*NameExpr); ok {
rt := evalStaticTypeOf(store, last, cx.X)
if mt, ok := rt.(*MapType); ok {
- assertAssignableTo(mt.Value, lt, false)
+ assertAssignableTo(x, mt.Value, lt, false)
}
} else if _, ok := cx.X.(*CompositeLitExpr); ok {
cpt := evalStaticTypeOf(store, last, cx.X)
if mt, ok := cpt.(*MapType); ok {
- assertAssignableTo(mt.Value, lt, false)
+ assertAssignableTo(x, mt.Value, lt, false)
} else {
panic("should not happen")
}
diff --git a/gnovm/pkg/gnolang/type_check_test.go b/gnovm/pkg/gnolang/type_check_test.go
new file mode 100644
index 00000000000..7fddf2c65d0
--- /dev/null
+++ b/gnovm/pkg/gnolang/type_check_test.go
@@ -0,0 +1,57 @@
+package gnolang
+
+import "testing"
+
+func TestCheckAssignableTo(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ xt Type
+ dt Type
+ autoNative bool
+ wantPanic bool
+ }{
+ {
+ name: "nil to nil",
+ xt: nil,
+ dt: nil,
+ },
+ {
+ name: "nil and interface",
+ xt: nil,
+ dt: &InterfaceType{},
+ },
+ {
+ name: "interface to nil",
+ xt: &InterfaceType{},
+ dt: nil,
+ },
+ {
+ name: "nil to non-nillable",
+ xt: nil,
+ dt: PrimitiveType(StringKind),
+ wantPanic: true,
+ },
+ {
+ name: "interface to interface",
+ xt: &InterfaceType{},
+ dt: &InterfaceType{},
+ },
+ }
+
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+ if tt.wantPanic {
+ defer func() {
+ if r := recover(); r == nil {
+ t.Errorf("checkAssignableTo() did not panic, want panic")
+ }
+ }()
+ }
+ checkAssignableTo(nil, tt.xt, tt.dt, tt.autoNative)
+ })
+ }
+}
diff --git a/gnovm/pkg/gnolang/types.go b/gnovm/pkg/gnolang/types.go
index eedb71ffa73..18774fcc462 100644
--- a/gnovm/pkg/gnolang/types.go
+++ b/gnovm/pkg/gnolang/types.go
@@ -41,7 +41,15 @@ func (tid TypeID) String() string {
return string(tid)
}
-func typeid(f string, args ...interface{}) (tid TypeID) {
+func typeid(s string) (tid TypeID) {
+ x := TypeID(s)
+ if debug {
+ debug.Println("TYPEID", s)
+ }
+ return x
+}
+
+func typeidf(f string, args ...interface{}) (tid TypeID) {
fs := fmt.Sprintf(f, args...)
x := TypeID(fs)
if debug {
@@ -521,7 +529,7 @@ func (at *ArrayType) Kind() Kind {
func (at *ArrayType) TypeID() TypeID {
if at.typeid.IsZero() {
- at.typeid = typeid("[%d]%s", at.Len, at.Elt.TypeID().String())
+ at.typeid = typeidf("[%d]%s", at.Len, at.Elt.TypeID().String())
}
return at.typeid
}
@@ -564,9 +572,9 @@ func (st *SliceType) Kind() Kind {
func (st *SliceType) TypeID() TypeID {
if st.typeid.IsZero() {
if st.Vrd {
- st.typeid = typeid("...%s", st.Elt.TypeID().String())
+ st.typeid = typeidf("...%s", st.Elt.TypeID().String())
} else {
- st.typeid = typeid("[]%s", st.Elt.TypeID().String())
+ st.typeid = typeidf("[]%s", st.Elt.TypeID().String())
}
}
return st.typeid
@@ -607,7 +615,7 @@ func (pt *PointerType) Kind() Kind {
func (pt *PointerType) TypeID() TypeID {
if pt.typeid.IsZero() {
- pt.typeid = typeid("*%s", pt.Elt.TypeID().String())
+ pt.typeid = typeidf("*%s", pt.Elt.TypeID().String())
}
return pt.typeid
}
@@ -748,7 +756,7 @@ func (st *StructType) TypeID() TypeID {
// may have the same TypeID if and only if neither have
// unexported fields. st.PkgPath is only included in field
// names that are not uppercase.
- st.typeid = typeid(
+ st.typeid = typeidf(
"struct{%s}",
FieldTypeList(st.Fields).TypeIDForPackage(st.PkgPath),
)
@@ -1078,11 +1086,11 @@ func (ct *ChanType) TypeID() TypeID {
if ct.typeid.IsZero() {
switch ct.Dir {
case SEND | RECV:
- ct.typeid = typeid("chan{%s}" + ct.Elt.TypeID().String())
+ ct.typeid = typeidf("chan{%s}", ct.Elt.TypeID().String())
case SEND:
- ct.typeid = typeid("<-chan{%s}" + ct.Elt.TypeID().String())
+ ct.typeid = typeidf("<-chan{%s}", ct.Elt.TypeID().String())
case RECV:
- ct.typeid = typeid("chan<-{%s}" + ct.Elt.TypeID().String())
+ ct.typeid = typeidf("chan<-{%s}", ct.Elt.TypeID().String())
default:
panic("should not happen")
}
@@ -1173,7 +1181,7 @@ func (ft *FuncType) UnboundType(rft FieldType) *FuncType {
// NOTE: if ft.HasVarg() and !isVarg, argTVs[len(ft.Params):]
// are ignored (since they are of the same type as
// argTVs[len(ft.Params)-1]).
-func (ft *FuncType) Specify(store Store, argTVs []TypedValue, isVarg bool) *FuncType {
+func (ft *FuncType) Specify(store Store, n Node, argTVs []TypedValue, isVarg bool) *FuncType {
hasGenericParams := false
hasGenericResults := false
for _, pf := range ft.Params {
@@ -1240,9 +1248,9 @@ func (ft *FuncType) Specify(store Store, argTVs []TypedValue, isVarg bool) *Func
for i, pf := range ft.Params {
arg := &argTVs[i]
if arg.T.Kind() == TypeKind {
- specifyType(store, lookup, pf.Type, arg.T, arg.GetType())
+ specifyType(store, n, lookup, pf.Type, arg.T, arg.GetType())
} else {
- specifyType(store, lookup, pf.Type, arg.T, nil)
+ specifyType(store, n, lookup, pf.Type, arg.T, nil)
}
}
// apply specifics to generic params and results.
@@ -1298,7 +1306,7 @@ func (ft *FuncType) TypeID() TypeID {
}
*/
if ft.typeid.IsZero() {
- ft.typeid = typeid(
+ ft.typeid = typeidf(
"func(%s)(%s)",
// pp,
ps.UnnamedTypeID(),
@@ -1361,7 +1369,7 @@ func (mt *MapType) Kind() Kind {
func (mt *MapType) TypeID() TypeID {
if mt.typeid.IsZero() {
- mt.typeid = typeid(
+ mt.typeid = typeidf(
"map[%s]%s",
mt.Key.TypeID().String(),
mt.Value.TypeID().String(),
@@ -1489,7 +1497,7 @@ func (dt *DeclaredType) TypeID() TypeID {
}
func DeclaredTypeID(pkgPath string, name Name) TypeID {
- return typeid("%s.%s", pkgPath, name)
+ return typeidf("%s.%s", pkgPath, name)
}
func (dt *DeclaredType) String() string {
@@ -1787,9 +1795,9 @@ func (nt *NativeType) TypeID() TypeID {
// > (e.g., base64 instead of "encoding/base64") and is not
// > guaranteed to be unique among types. To test for type identity,
// > compare the Types directly.
- nt.typeid = typeid("go:%s.%s", nt.Type.PkgPath(), nt.Type.String())
+ nt.typeid = typeidf("go:%s.%s", nt.Type.PkgPath(), nt.Type.String())
} else {
- nt.typeid = typeid("go:%s.%s", nt.Type.PkgPath(), nt.Type.Name())
+ nt.typeid = typeidf("go:%s.%s", nt.Type.PkgPath(), nt.Type.Name())
}
}
return nt.typeid
@@ -2419,7 +2427,7 @@ func IsImplementedBy(it Type, ot Type) bool {
// specTypeval is Type if spec is TypeKind.
// NOTE: type-checking isn't strictly necessary here, as the resulting lookup
// map gets applied to produce the ultimate param and result types.
-func specifyType(store Store, lookup map[Name]Type, tmpl Type, spec Type, specTypeval Type) {
+func specifyType(store Store, n Node, lookup map[Name]Type, tmpl Type, spec Type, specTypeval Type) {
if isGeneric(spec) {
panic("spec must not be generic")
}
@@ -2433,11 +2441,11 @@ func specifyType(store Store, lookup map[Name]Type, tmpl Type, spec Type, specTy
case *PointerType:
switch pt := baseOf(spec).(type) {
case *PointerType:
- specifyType(store, lookup, ct.Elt, pt.Elt, nil)
+ specifyType(store, n, lookup, ct.Elt, pt.Elt, nil)
case *NativeType:
// NOTE: see note about type-checking.
et := pt.Elem()
- specifyType(store, lookup, ct.Elt, et, nil)
+ specifyType(store, n, lookup, ct.Elt, et, nil)
default:
panic(fmt.Sprintf(
"expected pointer kind but got %s",
@@ -2446,11 +2454,11 @@ func specifyType(store Store, lookup map[Name]Type, tmpl Type, spec Type, specTy
case *ArrayType:
switch at := baseOf(spec).(type) {
case *ArrayType:
- specifyType(store, lookup, ct.Elt, at.Elt, nil)
+ specifyType(store, n, lookup, ct.Elt, at.Elt, nil)
case *NativeType:
// NOTE: see note about type-checking.
et := at.Elem()
- specifyType(store, lookup, ct.Elt, et, nil)
+ specifyType(store, n, lookup, ct.Elt, et, nil)
default:
panic(fmt.Sprintf(
"expected array kind but got %s",
@@ -2461,7 +2469,7 @@ func specifyType(store Store, lookup map[Name]Type, tmpl Type, spec Type, specTy
case PrimitiveType:
if isGeneric(ct.Elt) {
if st.Kind() == StringKind {
- specifyType(store, lookup, ct.Elt, Uint8Type, nil)
+ specifyType(store, n, lookup, ct.Elt, Uint8Type, nil)
} else {
panic(fmt.Sprintf(
"expected slice kind but got %s",
@@ -2477,11 +2485,11 @@ func specifyType(store Store, lookup map[Name]Type, tmpl Type, spec Type, specTy
spec.Kind()))
}
case *SliceType:
- specifyType(store, lookup, ct.Elt, st.Elt, nil)
+ specifyType(store, n, lookup, ct.Elt, st.Elt, nil)
case *NativeType:
// NOTE: see note about type-checking.
et := st.Elem()
- specifyType(store, lookup, ct.Elt, et, nil)
+ specifyType(store, n, lookup, ct.Elt, et, nil)
default:
panic(fmt.Sprintf(
"expected slice kind but got %s",
@@ -2490,14 +2498,14 @@ func specifyType(store Store, lookup map[Name]Type, tmpl Type, spec Type, specTy
case *MapType:
switch mt := baseOf(spec).(type) {
case *MapType:
- specifyType(store, lookup, ct.Key, mt.Key, nil)
- specifyType(store, lookup, ct.Value, mt.Value, nil)
+ specifyType(store, n, lookup, ct.Key, mt.Key, nil)
+ specifyType(store, n, lookup, ct.Value, mt.Value, nil)
case *NativeType:
// NOTE: see note about type-checking.
kt := mt.Key()
vt := mt.Elem()
- specifyType(store, lookup, ct.Key, kt, nil)
- specifyType(store, lookup, ct.Value, vt, nil)
+ specifyType(store, n, lookup, ct.Key, kt, nil)
+ specifyType(store, n, lookup, ct.Value, vt, nil)
default:
panic(fmt.Sprintf(
"expected map kind but got %s",
@@ -2536,7 +2544,7 @@ func specifyType(store Store, lookup map[Name]Type, tmpl Type, spec Type, specTy
generic := ct.Generic[:len(ct.Generic)-len(".Elem()")]
match, ok := lookup[generic]
if ok {
- assertAssignableTo(spec, match.Elem(), false)
+ assertAssignableTo(n, spec, match.Elem(), false)
return // ok
} else {
// Panic here, because we don't know whether T
@@ -2550,7 +2558,7 @@ func specifyType(store Store, lookup map[Name]Type, tmpl Type, spec Type, specTy
} else {
match, ok := lookup[ct.Generic]
if ok {
- assertAssignableTo(spec, match, false)
+ assertAssignableTo(n, spec, match, false)
return // ok
} else {
if isUntyped(spec) {
@@ -2568,9 +2576,9 @@ func specifyType(store Store, lookup map[Name]Type, tmpl Type, spec Type, specTy
switch cbt := baseOf(spec).(type) {
case *NativeType:
gnoType := store.Go2GnoType(cbt.Type)
- specifyType(store, lookup, ct.Type, gnoType, nil)
+ specifyType(store, n, lookup, ct.Type, gnoType, nil)
default:
- specifyType(store, lookup, ct.Type, cbt, nil)
+ specifyType(store, n, lookup, ct.Type, cbt, nil)
}
default:
// ignore, no generics.
diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go
index 8e27bcbcbdb..4c2e2835f95 100644
--- a/gnovm/pkg/gnolang/values.go
+++ b/gnovm/pkg/gnolang/values.go
@@ -548,7 +548,7 @@ type FuncValue struct {
Captures []TypedValue `json:",omitempty"` // HeapItemValues captured from closure.
FileName Name // file name where declared
PkgPath string
- NativePkg string // for native bindings through NativeStore
+ NativePkg string // for native bindings through NativeResolver
NativeName Name // not redundant with Name; this cannot be changed in userspace
body []Stmt // function body
@@ -2248,41 +2248,41 @@ func (tv *TypedValue) GetSlice(alloc *Allocator, low, high int) TypedValue {
}
}
-func (tv *TypedValue) GetSlice2(alloc *Allocator, low, high, max int) TypedValue {
- if low < 0 {
+func (tv *TypedValue) GetSlice2(alloc *Allocator, lowVal, highVal, maxVal int) TypedValue {
+ if lowVal < 0 {
panic(fmt.Sprintf(
"invalid slice index %d (index must be non-negative)",
- low))
+ lowVal))
}
- if high < 0 {
+ if highVal < 0 {
panic(fmt.Sprintf(
"invalid slice index %d (index must be non-negative)",
- high))
+ highVal))
}
- if max < 0 {
+ if maxVal < 0 {
panic(fmt.Sprintf(
"invalid slice index %d (index must be non-negative)",
- max))
+ maxVal))
}
- if low > high {
+ if lowVal > highVal {
panic(fmt.Sprintf(
"invalid slice index %d > %d",
- low, high))
+ lowVal, highVal))
}
- if high > max {
+ if highVal > maxVal {
panic(fmt.Sprintf(
"invalid slice index %d > %d",
- high, max))
+ highVal, maxVal))
}
- if tv.GetCapacity() < high {
+ if tv.GetCapacity() < highVal {
panic(fmt.Sprintf(
"slice bounds out of range [%d:%d:%d] with capacity %d",
- low, high, max, tv.GetCapacity()))
+ lowVal, highVal, maxVal, tv.GetCapacity()))
}
- if tv.GetCapacity() < max {
+ if tv.GetCapacity() < maxVal {
panic(fmt.Sprintf(
"slice bounds out of range [%d:%d:%d] with capacity %d",
- low, high, max, tv.GetCapacity()))
+ lowVal, highVal, maxVal, tv.GetCapacity()))
}
switch bt := baseOf(tv.T).(type) {
case *ArrayType:
@@ -2294,15 +2294,15 @@ func (tv *TypedValue) GetSlice2(alloc *Allocator, low, high, max int) TypedValue
return TypedValue{
T: st,
V: alloc.NewSlice(
- av, // base
- low, // low
- high-low, // length
- max-low, // maxcap
+ av, // base
+ lowVal, // low
+ highVal-lowVal, // length
+ maxVal-lowVal, // maxcap
),
}
case *SliceType:
if tv.V == nil {
- if low != 0 || high != 0 || max != 0 {
+ if lowVal != 0 || highVal != 0 || maxVal != 0 {
panic("nil slice index out of range")
}
return TypedValue{
@@ -2314,10 +2314,10 @@ func (tv *TypedValue) GetSlice2(alloc *Allocator, low, high, max int) TypedValue
return TypedValue{
T: tv.T,
V: alloc.NewSlice(
- sv.Base, // base
- sv.Offset+low, // offset
- high-low, // length
- max-low, // maxcap
+ sv.Base, // base
+ sv.Offset+lowVal, // offset
+ highVal-lowVal, // length
+ maxVal-lowVal, // maxcap
),
}
default:
diff --git a/gnovm/pkg/gnomod/fetch.go b/gnovm/pkg/gnomod/fetch.go
deleted file mode 100644
index 24aaac2f9d4..00000000000
--- a/gnovm/pkg/gnomod/fetch.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package gnomod
-
-import (
- "fmt"
-
- abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types"
- "github.com/gnolang/gno/tm2/pkg/bft/rpc/client"
-)
-
-func queryChain(remote string, qpath string, data []byte) (res *abci.ResponseQuery, err error) {
- opts2 := client.ABCIQueryOptions{
- // Height: height, XXX
- // Prove: false, XXX
- }
- cli, err := client.NewHTTPClient(remote)
- if err != nil {
- return nil, err
- }
-
- qres, err := cli.ABCIQueryWithOptions(qpath, data, opts2)
- if err != nil {
- return nil, err
- }
- if qres.Response.Error != nil {
- fmt.Printf("Log: %s\n", qres.Response.Log)
- return nil, qres.Response.Error
- }
-
- return &qres.Response, nil
-}
diff --git a/gnovm/pkg/gnomod/file.go b/gnovm/pkg/gnomod/file.go
index b6ee95acac8..a1c77b51e45 100644
--- a/gnovm/pkg/gnomod/file.go
+++ b/gnovm/pkg/gnomod/file.go
@@ -12,12 +12,8 @@ package gnomod
import (
"errors"
"fmt"
- "log"
"os"
- "path/filepath"
- "strings"
- gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
"golang.org/x/mod/modfile"
"golang.org/x/mod/module"
)
@@ -27,51 +23,11 @@ type File struct {
Draft bool
Module *modfile.Module
Go *modfile.Go
- Require []*modfile.Require
Replace []*modfile.Replace
Syntax *modfile.FileSyntax
}
-// AddRequire sets the first require line for path to version vers,
-// preserving any existing comments for that line and removing all
-// other lines for path.
-//
-// If no line currently exists for path, AddRequire adds a new line
-// at the end of the last require block.
-func (f *File) AddRequire(path, vers string) error {
- need := true
- for _, r := range f.Require {
- if r.Mod.Path == path {
- if need {
- r.Mod.Version = vers
- updateLine(r.Syntax, "require", modfile.AutoQuote(path), vers)
- need = false
- } else {
- markLineAsRemoved(r.Syntax)
- *r = modfile.Require{}
- }
- }
- }
-
- if need {
- f.AddNewRequire(path, vers, false)
- }
- return nil
-}
-
-// AddNewRequire adds a new require line for path at version vers at the end of
-// the last require block, regardless of any existing require lines for path.
-func (f *File) AddNewRequire(path, vers string, indirect bool) {
- line := addLine(f.Syntax, nil, "require", modfile.AutoQuote(path), vers)
- r := &modfile.Require{
- Mod: module.Version{Path: path, Version: vers},
- Syntax: line,
- }
- setIndirect(r, indirect)
- f.Require = append(f.Require, r)
-}
-
func (f *File) AddModuleStmt(path string) error {
if f.Syntax == nil {
f.Syntax = new(modfile.FileSyntax)
@@ -107,16 +63,6 @@ func (f *File) AddReplace(oldPath, oldVers, newPath, newVers string) error {
return addReplace(f.Syntax, &f.Replace, oldPath, oldVers, newPath, newVers)
}
-func (f *File) DropRequire(path string) error {
- for _, r := range f.Require {
- if r.Mod.Path == path {
- markLineAsRemoved(r.Syntax)
- *r = modfile.Require{}
- }
- }
- return nil
-}
-
func (f *File) DropReplace(oldPath, oldVers string) error {
for _, r := range f.Replace {
if r.Old.Path == oldPath && r.Old.Version == oldVers {
@@ -136,76 +82,17 @@ func (f *File) Validate() error {
return nil
}
-// Resolve takes a Require directive from File and returns any adequate replacement
+// Resolve takes a module version and returns any adequate replacement
// following the Replace directives.
-func (f *File) Resolve(r *modfile.Require) module.Version {
- mod, replaced := isReplaced(r.Mod, f.Replace)
+func (f *File) Resolve(m module.Version) module.Version {
+ if f == nil {
+ return m
+ }
+ mod, replaced := isReplaced(m, f.Replace)
if replaced {
return mod
}
- return r.Mod
-}
-
-// FetchDeps fetches and writes gno.mod packages
-// in GOPATH/pkg/gnomod/
-func (f *File) FetchDeps(path string, remote string, verbose bool) error {
- for _, r := range f.Require {
- mod := f.Resolve(r)
- if r.Mod.Path != mod.Path {
- if modfile.IsDirectoryPath(mod.Path) {
- continue
- }
- }
- indirect := ""
- if r.Indirect {
- indirect = "// indirect"
- }
-
- _, err := os.Stat(PackageDir(path, mod))
- if !os.IsNotExist(err) {
- if verbose {
- log.Println("cached", mod.Path, indirect)
- }
- continue
- }
- if verbose {
- log.Println("fetching", mod.Path, indirect)
- }
- requirements, err := writePackage(remote, path, mod.Path)
- if err != nil {
- return fmt.Errorf("writepackage: %w", err)
- }
-
- modFile := new(File)
- modFile.AddModuleStmt(mod.Path)
- for _, req := range requirements {
- path := req[1 : len(req)-1] // trim leading and trailing `"`
- if strings.HasSuffix(path, modFile.Module.Mod.Path) {
- continue
- }
-
- if !gno.IsStdlib(path) {
- modFile.AddNewRequire(path, "v0.0.0-latest", true)
- }
- }
-
- err = modFile.FetchDeps(path, remote, verbose)
- if err != nil {
- return err
- }
- goMod, err := GnoToGoMod(*modFile)
- if err != nil {
- return err
- }
- pkgPath := PackageDir(path, mod)
- goModFilePath := filepath.Join(pkgPath, "go.mod")
- err = goMod.Write(goModFilePath)
- if err != nil {
- return err
- }
- }
-
- return nil
+ return m
}
// writes file to the given absolute file path
@@ -220,5 +107,5 @@ func (f *File) Write(fname string) error {
}
func (f *File) Sanitize() {
- removeDups(f.Syntax, &f.Require, &f.Replace)
+ removeDups(f.Syntax, &f.Replace)
}
diff --git a/gnovm/pkg/gnomod/file_test.go b/gnovm/pkg/gnomod/file_test.go
deleted file mode 100644
index a64c2794a65..00000000000
--- a/gnovm/pkg/gnomod/file_test.go
+++ /dev/null
@@ -1,142 +0,0 @@
-package gnomod
-
-import (
- "bytes"
- "log"
- "os"
- "path/filepath"
- "testing"
-
- "github.com/gnolang/gno/tm2/pkg/testutils"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- "golang.org/x/mod/modfile"
- "golang.org/x/mod/module"
-)
-
-const testRemote string = "gno.land:26657" // XXX(race condition): test with a local node so that this test is consistent with git and not with a deploy
-
-func TestFetchDeps(t *testing.T) {
- for _, tc := range []struct {
- desc string
- modFile File
- errorShouldContain string
- requirements []string
- stdOutContains []string
- cachedStdOutContains []string
- }{
- {
- desc: "not_exists",
- modFile: File{
- Module: &modfile.Module{
- Mod: module.Version{
- Path: "testFetchDeps",
- },
- },
- Require: []*modfile.Require{
- {
- Mod: module.Version{
- Path: "gno.land/p/demo/does_not_exists",
- Version: "v0.0.0",
- },
- },
- },
- },
- errorShouldContain: "querychain (gno.land/p/demo/does_not_exists)",
- }, {
- desc: "fetch_gno.land/p/demo/avl",
- modFile: File{
- Module: &modfile.Module{
- Mod: module.Version{
- Path: "testFetchDeps",
- },
- },
- Require: []*modfile.Require{
- {
- Mod: module.Version{
- Path: "gno.land/p/demo/avl",
- Version: "v0.0.0",
- },
- },
- },
- },
- requirements: []string{"avl"},
- stdOutContains: []string{
- "fetching gno.land/p/demo/avl",
- },
- cachedStdOutContains: []string{
- "cached gno.land/p/demo/avl",
- },
- }, {
- desc: "fetch_gno.land/p/demo/blog6",
- modFile: File{
- Module: &modfile.Module{
- Mod: module.Version{
- Path: "testFetchDeps",
- },
- },
- Require: []*modfile.Require{
- {
- Mod: module.Version{
- Path: "gno.land/p/demo/blog",
- Version: "v0.0.0",
- },
- },
- },
- },
- requirements: []string{"avl", "blog", "ufmt", "mux"},
- stdOutContains: []string{
- "fetching gno.land/p/demo/blog",
- "fetching gno.land/p/demo/avl // indirect",
- "fetching gno.land/p/demo/ufmt // indirect",
- },
- cachedStdOutContains: []string{
- "cached gno.land/p/demo/blog",
- },
- },
- } {
- t.Run(tc.desc, func(t *testing.T) {
- var buf bytes.Buffer
- log.SetOutput(&buf)
- defer func() {
- log.SetOutput(os.Stderr)
- }()
-
- // Create test dir
- dirPath, cleanUpFn := testutils.NewTestCaseDir(t)
- assert.NotNil(t, dirPath)
- defer cleanUpFn()
-
- // Fetching dependencies
- err := tc.modFile.FetchDeps(dirPath, testRemote, true)
- if tc.errorShouldContain != "" {
- require.ErrorContains(t, err, tc.errorShouldContain)
- } else {
- require.Nil(t, err)
-
- // Read dir
- entries, err := os.ReadDir(filepath.Join(dirPath, "gno.land", "p", "demo"))
- require.Nil(t, err)
-
- // Check dir entries
- assert.Equal(t, len(tc.requirements), len(entries))
- for _, e := range entries {
- assert.Contains(t, tc.requirements, e.Name())
- }
-
- // Check logs
- for _, c := range tc.stdOutContains {
- assert.Contains(t, buf.String(), c)
- }
-
- buf.Reset()
-
- // Try fetching again. Should be cached
- tc.modFile.FetchDeps(dirPath, testRemote, true)
- for _, c := range tc.cachedStdOutContains {
- assert.Contains(t, buf.String(), c)
- }
- }
- })
- }
-}
diff --git a/gnovm/pkg/gnomod/gnomod.go b/gnovm/pkg/gnomod/gnomod.go
index 9384c41c293..a34caa2e48d 100644
--- a/gnovm/pkg/gnomod/gnomod.go
+++ b/gnovm/pkg/gnomod/gnomod.go
@@ -3,22 +3,16 @@ package gnomod
import (
"errors"
"fmt"
- "go/parser"
- gotoken "go/token"
"os"
"path/filepath"
"strings"
- "github.com/gnolang/gno/gnovm"
"github.com/gnolang/gno/gnovm/pkg/gnoenv"
"github.com/gnolang/gno/gnovm/pkg/gnolang"
- "github.com/gnolang/gno/gnovm/pkg/transpiler"
"golang.org/x/mod/modfile"
"golang.org/x/mod/module"
)
-const queryPathFile = "vm/qfile"
-
// ModCachePath returns the path for gno modules
func ModCachePath() string {
return filepath.Join(gnoenv.HomeDir(), "pkg", "mod")
@@ -27,127 +21,10 @@ func ModCachePath() string {
// PackageDir resolves a given module.Version to the path on the filesystem.
// If root is dir, it is defaulted to the value of [ModCachePath].
func PackageDir(root string, v module.Version) string {
- // This is also used internally exactly like filepath.Join; but we'll keep
- // the calls centralized to make sure we can change the path centrally should
- // we start including the module version in the path.
-
if root == "" {
root = ModCachePath()
}
- return filepath.Join(root, v.Path)
-}
-
-func writePackage(remote, basePath, pkgPath string) (requirements []string, err error) {
- res, err := queryChain(remote, queryPathFile, []byte(pkgPath))
- if err != nil {
- return nil, fmt.Errorf("querychain (%s): %w", pkgPath, err)
- }
-
- dirPath, fileName := gnovm.SplitFilepath(pkgPath)
- if fileName == "" {
- // Is Dir
- // Create Dir if not exists
- dirPath := filepath.Join(basePath, dirPath)
- if _, err = os.Stat(dirPath); os.IsNotExist(err) {
- if err = os.MkdirAll(dirPath, 0o755); err != nil {
- return nil, fmt.Errorf("mkdir %q: %w", dirPath, err)
- }
- }
-
- files := strings.Split(string(res.Data), "\n")
- for _, file := range files {
- reqs, err := writePackage(remote, basePath, filepath.Join(pkgPath, file))
- if err != nil {
- return nil, fmt.Errorf("writepackage: %w", err)
- }
- requirements = append(requirements, reqs...)
- }
- } else {
- // Is File
- // Transpile and write generated go file
- file, err := parser.ParseFile(gotoken.NewFileSet(), fileName, res.Data, parser.ImportsOnly)
- if err != nil {
- return nil, fmt.Errorf("parse gno file: %w", err)
- }
- for _, i := range file.Imports {
- requirements = append(requirements, i.Path.Value)
- }
-
- // Write file
- fileNameWithPath := filepath.Join(basePath, dirPath, fileName)
- err = os.WriteFile(fileNameWithPath, res.Data, 0o644)
- if err != nil {
- return nil, fmt.Errorf("writefile %q: %w", fileNameWithPath, err)
- }
- }
-
- return removeDuplicateStr(requirements), nil
-}
-
-// GnoToGoMod make necessary modifications in the gno.mod
-// and return go.mod file.
-func GnoToGoMod(f File) (*File, error) {
- // TODO(morgan): good candidate to move to pkg/transpiler.
-
- gnoModPath := ModCachePath()
-
- if !gnolang.IsStdlib(f.Module.Mod.Path) {
- f.AddModuleStmt(transpiler.TranspileImportPath(f.Module.Mod.Path))
- }
-
- for i := range f.Require {
- mod, replaced := isReplaced(f.Require[i].Mod, f.Replace)
- if replaced {
- if modfile.IsDirectoryPath(mod.Path) {
- continue
- }
- }
- path := f.Require[i].Mod.Path
- if !gnolang.IsStdlib(path) {
- // Add dependency with a modified import path
- f.AddRequire(transpiler.TranspileImportPath(path), f.Require[i].Mod.Version)
- }
- f.AddReplace(path, f.Require[i].Mod.Version, filepath.Join(gnoModPath, path), "")
- // Remove the old require since the new dependency was added above
- f.DropRequire(path)
- }
-
- // Remove replacements that are not replaced by directories.
- //
- // Explanation:
- // By this stage every replacement should be replace by dir.
- // If not replaced by dir, remove it.
- //
- // e.g:
- //
- // ```
- // require (
- // gno.land/p/demo/avl v1.2.3
- // )
- //
- // replace (
- // gno.land/p/demo/avl v1.2.3 => gno.land/p/demo/avl v3.2.1
- // )
- // ```
- //
- // In above case we will fetch `gno.land/p/demo/avl v3.2.1` and
- // replace will look something like:
- //
- // ```
- // replace (
- // gno.land/p/demo/avl v1.2.3 => gno.land/p/demo/avl v3.2.1
- // gno.land/p/demo/avl v3.2.1 => /path/to/avl/version/v3.2.1
- // )
- // ```
- //
- // Remove `gno.land/p/demo/avl v1.2.3 => gno.land/p/demo/avl v3.2.1`.
- for _, r := range f.Replace {
- if !modfile.IsDirectoryPath(r.New.Path) {
- f.DropReplace(r.Old.Path, r.Old.Version)
- }
- }
-
- return &f, nil
+ return filepath.Join(root, filepath.FromSlash(v.Path))
}
func CreateGnoModFile(rootDir, modPath string) error {
@@ -180,7 +57,7 @@ func CreateGnoModFile(rootDir, modPath string) error {
return fmt.Errorf("read file %q: %w", fpath, err)
}
- pn := gnolang.PackageNameFromFileBody(file.Name(), string(bz))
+ pn := gnolang.MustPackageNameFromFileBody(file.Name(), string(bz))
if strings.HasSuffix(string(pkgName), "_test") {
pkgName = pkgName[:len(pkgName)-len("_test")]
}
@@ -217,14 +94,3 @@ func isReplaced(mod module.Version, repl []*modfile.Replace) (module.Version, bo
}
return module.Version{}, false
}
-
-func removeDuplicateStr(str []string) (res []string) {
- m := make(map[string]struct{}, len(str))
- for _, s := range str {
- if _, ok := m[s]; !ok {
- m[s] = struct{}{}
- res = append(res, s)
- }
- }
- return
-}
diff --git a/gnovm/pkg/gnomod/parse.go b/gnovm/pkg/gnomod/parse.go
index a6314d5729f..e3a3fbcaeea 100644
--- a/gnovm/pkg/gnomod/parse.go
+++ b/gnovm/pkg/gnomod/parse.go
@@ -105,7 +105,7 @@ func Parse(file string, data []byte) (*File, error) {
Err: fmt.Errorf("unknown block type: %s", strings.Join(x.Token, " ")),
})
continue
- case "module", "require", "replace":
+ case "module", "replace":
for _, l := range x.Line {
f.add(&errs, x, l, x.Token[0], l.Token)
}
@@ -180,26 +180,6 @@ func (f *File) add(errs *modfile.ErrorList, block *modfile.LineBlock, line *modf
}
f.Module.Mod = module.Version{Path: s}
- case "require":
- if len(args) != 2 {
- errorf("usage: %s module/path v1.2.3", verb)
- return
- }
- s, err := parseString(&args[0])
- if err != nil {
- errorf("invalid quoted string: %v", err)
- return
- }
- v, err := parseVersion(verb, s, &args[1])
- if err != nil {
- wrapError(err)
- return
- }
- f.Require = append(f.Require, &modfile.Require{
- Mod: module.Version{Path: s, Version: v},
- Syntax: line,
- })
-
case "replace":
replace, wrappederr := parseReplace(f.Syntax.Name, line, verb, args)
if wrappederr != nil {
diff --git a/gnovm/pkg/gnomod/parse_test.go b/gnovm/pkg/gnomod/parse_test.go
index 61aaa83482b..ec54c6424fc 100644
--- a/gnovm/pkg/gnomod/parse_test.go
+++ b/gnovm/pkg/gnomod/parse_test.go
@@ -194,16 +194,29 @@ func TestParseGnoMod(t *testing.T) {
modPath: filepath.Join(pkgDir, "gno.mod"),
},
{
- desc: "error parsing gno.mod",
+ desc: "valid gno.mod file with replace",
+ modData: `module foo
+ replace bar => ../bar`,
+ modPath: filepath.Join(pkgDir, "gno.mod"),
+ },
+ {
+ desc: "error bad module directive",
modData: `module foo v0.0.0`,
modPath: filepath.Join(pkgDir, "gno.mod"),
errShouldContain: "error parsing gno.mod file at",
},
{
- desc: "error validating gno.mod",
- modData: `require bar v0.0.0`,
+ desc: "error gno.mod without module",
+ modData: `replace bar => ../bar`,
+ modPath: filepath.Join(pkgDir, "gno.mod"),
+ errShouldContain: "requires module",
+ },
+ {
+ desc: "error gno.mod with require",
+ modData: `module foo
+ require bar v0.0.0`,
modPath: filepath.Join(pkgDir, "gno.mod"),
- errShouldContain: "error validating gno.mod file at",
+ errShouldContain: "unknown directive: require",
},
} {
t.Run(tc.desc, func(t *testing.T) {
diff --git a/gnovm/pkg/gnomod/pkg.go b/gnovm/pkg/gnomod/pkg.go
index f6fe7f60301..35f52e3dded 100644
--- a/gnovm/pkg/gnomod/pkg.go
+++ b/gnovm/pkg/gnomod/pkg.go
@@ -5,14 +5,19 @@ import (
"io/fs"
"os"
"path/filepath"
+ "slices"
"strings"
+
+ "github.com/gnolang/gno/gnovm"
+ "github.com/gnolang/gno/gnovm/pkg/gnolang"
+ "github.com/gnolang/gno/gnovm/pkg/packages"
)
type Pkg struct {
- Dir string // absolute path to package dir
- Name string // package name
- Requires []string // dependencies
- Draft bool // whether the package is a draft
+ Dir string // absolute path to package dir
+ Name string // package name
+ Imports []string // direct imports of this pkg
+ Draft bool // whether the package is a draft
}
type SubPkg struct {
@@ -60,10 +65,10 @@ func visitPackage(pkg Pkg, pkgs []Pkg, visited, onStack map[string]bool, sortedP
onStack[pkg.Name] = true
// Visit package's dependencies
- for _, req := range pkg.Requires {
+ for _, imp := range pkg.Imports {
found := false
for _, p := range pkgs {
- if p.Name != req {
+ if p.Name != imp {
continue
}
if err := visitPackage(p, pkgs, visited, onStack, sortedPkgs); err != nil {
@@ -73,7 +78,7 @@ func visitPackage(pkg Pkg, pkgs []Pkg, visited, onStack map[string]bool, sortedP
break
}
if !found {
- return fmt.Errorf("missing dependency '%s' for package '%s'", req, pkg.Name)
+ return fmt.Errorf("missing dependency '%s' for package '%s'", imp, pkg.Name)
}
}
@@ -111,17 +116,28 @@ func ListPkgs(root string) (PkgList, error) {
return fmt.Errorf("validate: %w", err)
}
+ pkg, err := gnolang.ReadMemPackage(path, gnoMod.Module.Mod.Path)
+ if err != nil {
+ // ignore package files on error
+ pkg = &gnovm.MemPackage{}
+ }
+
+ imports, err := packages.Imports(pkg)
+ if err != nil {
+ // ignore imports on error
+ imports = []string{}
+ }
+
+ // remove self and standard libraries from imports
+ imports = slices.DeleteFunc(imports, func(imp string) bool {
+ return imp == gnoMod.Module.Mod.Path || gnolang.IsStdlib(imp)
+ })
+
pkgs = append(pkgs, Pkg{
- Dir: path,
- Name: gnoMod.Module.Mod.Path,
- Draft: gnoMod.Draft,
- Requires: func() []string {
- var reqs []string
- for _, req := range gnoMod.Require {
- reqs = append(reqs, req.Mod.Path)
- }
- return reqs
- }(),
+ Dir: path,
+ Name: gnoMod.Module.Mod.Path,
+ Draft: gnoMod.Draft,
+ Imports: imports,
})
return nil
})
@@ -144,7 +160,7 @@ func (sp SortedPkgList) GetNonDraftPkgs() SortedPkgList {
continue
}
dependsOnDraft := false
- for _, req := range pkg.Requires {
+ for _, req := range pkg.Imports {
if draft[req] {
dependsOnDraft = true
draft[pkg.Name] = true
diff --git a/gnovm/pkg/gnomod/pkg_test.go b/gnovm/pkg/gnomod/pkg_test.go
index 587a0bb8f81..7c3035a4b7b 100644
--- a/gnovm/pkg/gnomod/pkg_test.go
+++ b/gnovm/pkg/gnomod/pkg_test.go
@@ -47,12 +47,6 @@ func TestListAndNonDraftPkgs(t *testing.T) {
"foo",
`module foo`,
},
- {
- "bar",
- `module bar
-
- require foo v0.0.0`,
- },
{
"baz",
`module baz`,
@@ -64,121 +58,8 @@ func TestListAndNonDraftPkgs(t *testing.T) {
module qux`,
},
},
- outListPkgs: []string{"foo", "bar", "baz", "qux"},
- outNonDraftPkgs: []string{"foo", "bar", "baz"},
- },
- {
- desc: "package directly depends on draft package",
- in: []struct{ name, modfile string }{
- {
- "foo",
- `// Draft
-
- module foo`,
- },
- {
- "bar",
- `module bar
- require foo v0.0.0`,
- },
- {
- "baz",
- `module baz`,
- },
- },
- outListPkgs: []string{"foo", "bar", "baz"},
- outNonDraftPkgs: []string{"baz"},
- },
- {
- desc: "package indirectly depends on draft package",
- in: []struct{ name, modfile string }{
- {
- "foo",
- `// Draft
-
- module foo`,
- },
- {
- "bar",
- `module bar
-
- require foo v0.0.0`,
- },
- {
- "baz",
- `module baz
-
- require bar v0.0.0`,
- },
- {
- "qux",
- `module qux`,
- },
- },
- outListPkgs: []string{"foo", "bar", "baz", "qux"},
- outNonDraftPkgs: []string{"qux"},
- },
- {
- desc: "package indirectly depends on draft package (multiple levels - 1)",
- in: []struct{ name, modfile string }{
- {
- "foo",
- `// Draft
-
- module foo`,
- },
- {
- "bar",
- `module bar
-
- require foo v0.0.0`,
- },
- {
- "baz",
- `module baz
-
- require bar v0.0.0`,
- },
- {
- "qux",
- `module qux
-
- require baz v0.0.0`,
- },
- },
- outListPkgs: []string{"foo", "bar", "baz", "qux"},
- outNonDraftPkgs: []string{},
- },
- {
- desc: "package indirectly depends on draft package (multiple levels - 2)",
- in: []struct{ name, modfile string }{
- {
- "foo",
- `// Draft
-
- module foo`,
- },
- {
- "bar",
- `module bar
-
- require qux v0.0.0`,
- },
- {
- "baz",
- `module baz
-
- require foo v0.0.0`,
- },
- {
- "qux",
- `module qux
-
- require baz v0.0.0`,
- },
- },
- outListPkgs: []string{"foo", "bar", "baz", "qux"},
- outNonDraftPkgs: []string{},
+ outListPkgs: []string{"foo", "baz", "qux"},
+ outNonDraftPkgs: []string{"foo", "baz"},
},
} {
t.Run(tc.desc, func(t *testing.T) {
@@ -224,6 +105,7 @@ func createGnoModPkg(t *testing.T, dirPath, pkgName, modData string) {
// Create gno.mod
err = os.WriteFile(filepath.Join(pkgDirPath, "gno.mod"), []byte(modData), 0o644)
+ require.NoError(t, err)
}
func TestSortPkgs(t *testing.T) {
@@ -240,30 +122,30 @@ func TestSortPkgs(t *testing.T) {
}, {
desc: "no_dependencies",
in: []Pkg{
- {Name: "pkg1", Dir: "/path/to/pkg1", Requires: []string{}},
- {Name: "pkg2", Dir: "/path/to/pkg2", Requires: []string{}},
- {Name: "pkg3", Dir: "/path/to/pkg3", Requires: []string{}},
+ {Name: "pkg1", Dir: "/path/to/pkg1", Imports: []string{}},
+ {Name: "pkg2", Dir: "/path/to/pkg2", Imports: []string{}},
+ {Name: "pkg3", Dir: "/path/to/pkg3", Imports: []string{}},
},
expected: []string{"pkg1", "pkg2", "pkg3"},
}, {
desc: "circular_dependencies",
in: []Pkg{
- {Name: "pkg1", Dir: "/path/to/pkg1", Requires: []string{"pkg2"}},
- {Name: "pkg2", Dir: "/path/to/pkg2", Requires: []string{"pkg1"}},
+ {Name: "pkg1", Dir: "/path/to/pkg1", Imports: []string{"pkg2"}},
+ {Name: "pkg2", Dir: "/path/to/pkg2", Imports: []string{"pkg1"}},
},
shouldErr: true,
}, {
desc: "missing_dependencies",
in: []Pkg{
- {Name: "pkg1", Dir: "/path/to/pkg1", Requires: []string{"pkg2"}},
+ {Name: "pkg1", Dir: "/path/to/pkg1", Imports: []string{"pkg2"}},
},
shouldErr: true,
}, {
desc: "valid_dependencies",
in: []Pkg{
- {Name: "pkg1", Dir: "/path/to/pkg1", Requires: []string{"pkg2"}},
- {Name: "pkg2", Dir: "/path/to/pkg2", Requires: []string{"pkg3"}},
- {Name: "pkg3", Dir: "/path/to/pkg3", Requires: []string{}},
+ {Name: "pkg1", Dir: "/path/to/pkg1", Imports: []string{"pkg2"}},
+ {Name: "pkg2", Dir: "/path/to/pkg2", Imports: []string{"pkg3"}},
+ {Name: "pkg3", Dir: "/path/to/pkg3", Imports: []string{}},
},
expected: []string{"pkg3", "pkg2", "pkg1"},
},
diff --git a/gnovm/pkg/gnomod/preprocess.go b/gnovm/pkg/gnomod/preprocess.go
index ec1faaa5c29..df6910f769b 100644
--- a/gnovm/pkg/gnomod/preprocess.go
+++ b/gnovm/pkg/gnomod/preprocess.go
@@ -3,50 +3,15 @@ package gnomod
import (
"golang.org/x/mod/modfile"
"golang.org/x/mod/module"
- "golang.org/x/mod/semver"
)
-func removeDups(syntax *modfile.FileSyntax, require *[]*modfile.Require, replace *[]*modfile.Replace) {
- if require != nil {
- purged := removeRequireDups(require)
- cleanSyntaxTree(syntax, purged)
- }
+func removeDups(syntax *modfile.FileSyntax, replace *[]*modfile.Replace) {
if replace != nil {
purged := removeReplaceDups(replace)
cleanSyntaxTree(syntax, purged)
}
}
-// removeRequireDups removes duplicate requirements.
-// Requirements with higher version takes priority.
-func removeRequireDups(require *[]*modfile.Require) map[*modfile.Line]bool {
- purge := make(map[*modfile.Line]bool)
-
- keepRequire := make(map[string]string)
- for _, r := range *require {
- if v, ok := keepRequire[r.Mod.Path]; ok {
- if semver.Compare(r.Mod.Version, v) == 1 {
- keepRequire[r.Mod.Path] = r.Mod.Version
- }
- continue
- }
- keepRequire[r.Mod.Path] = r.Mod.Version
- }
- var req []*modfile.Require
- added := make(map[string]bool)
- for _, r := range *require {
- if v, ok := keepRequire[r.Mod.Path]; ok && !added[r.Mod.Path] && v == r.Mod.Version {
- req = append(req, r)
- added[r.Mod.Path] = true
- continue
- }
- purge[r.Syntax] = true
- }
- *require = req
-
- return purge
-}
-
// removeReplaceDups removes duplicate replacements.
// Later replacements take priority over earlier ones.
func removeReplaceDups(replace *[]*modfile.Replace) map[*modfile.Line]bool {
diff --git a/gnovm/pkg/gnomod/preprocess_test.go b/gnovm/pkg/gnomod/preprocess_test.go
index 28f42d740e3..6e0a890763c 100644
--- a/gnovm/pkg/gnomod/preprocess_test.go
+++ b/gnovm/pkg/gnomod/preprocess_test.go
@@ -8,133 +8,6 @@ import (
"golang.org/x/mod/module"
)
-func TestRemoveRequireDups(t *testing.T) {
- for _, tc := range []struct {
- desc string
- in []*modfile.Require
- expected []*modfile.Require
- }{
- {
- desc: "no_duplicate",
- in: []*modfile.Require{
- {
- Mod: module.Version{
- Path: "x.y/w",
- Version: "v1.0.0",
- },
- },
- {
- Mod: module.Version{
- Path: "x.y/z",
- Version: "v1.1.0",
- },
- },
- },
- expected: []*modfile.Require{
- {
- Mod: module.Version{
- Path: "x.y/w",
- Version: "v1.0.0",
- },
- },
- {
- Mod: module.Version{
- Path: "x.y/z",
- Version: "v1.1.0",
- },
- },
- },
- },
- {
- desc: "one_duplicate",
- in: []*modfile.Require{
- {
- Mod: module.Version{
- Path: "x.y/w",
- Version: "v1.0.0",
- },
- },
- {
- Mod: module.Version{
- Path: "x.y/w",
- Version: "v1.0.0",
- },
- },
- {
- Mod: module.Version{
- Path: "x.y/z",
- Version: "v1.1.0",
- },
- },
- },
- expected: []*modfile.Require{
- {
- Mod: module.Version{
- Path: "x.y/w",
- Version: "v1.0.0",
- },
- },
- {
- Mod: module.Version{
- Path: "x.y/z",
- Version: "v1.1.0",
- },
- },
- },
- },
- {
- desc: "multiple_duplicate",
- in: []*modfile.Require{
- {
- Mod: module.Version{
- Path: "x.y/w",
- Version: "v1.0.0",
- },
- },
- {
- Mod: module.Version{
- Path: "x.y/w",
- Version: "v1.0.0",
- },
- },
- {
- Mod: module.Version{
- Path: "x.y/z",
- Version: "v1.1.0",
- },
- },
- {
- Mod: module.Version{
- Path: "x.y/w",
- Version: "v1.2.0",
- },
- },
- },
- expected: []*modfile.Require{
- {
- Mod: module.Version{
- Path: "x.y/z",
- Version: "v1.1.0",
- },
- },
- {
- Mod: module.Version{
- Path: "x.y/w",
- Version: "v1.2.0",
- },
- },
- },
- },
- } {
- t.Run(tc.desc, func(t *testing.T) {
- in := tc.in
- removeRequireDups(&in)
-
- assert.Equal(t, tc.expected, in)
- })
- }
-}
-
func TestRemoveReplaceDups(t *testing.T) {
for _, tc := range []struct {
desc string
diff --git a/gnovm/pkg/gnomod/read.go b/gnovm/pkg/gnomod/read.go
index d6d771429d3..bb03ddf6efd 100644
--- a/gnovm/pkg/gnomod/read.go
+++ b/gnovm/pkg/gnomod/read.go
@@ -770,12 +770,6 @@ func parseReplace(filename string, line *modfile.Line, verb string, args []strin
}
nv := ""
if len(args) == arrow+2 {
- if !modfile.IsDirectoryPath(ns) {
- if strings.Contains(ns, "@") {
- return nil, errorf("replacement module must match format 'path version', not 'path@version'")
- }
- return nil, errorf("replacement module without version must be directory path (rooted or starting with . or ..)")
- }
if filepath.Separator == '/' && strings.Contains(ns, `\`) {
return nil, errorf("replacement directory appears to be Windows path (on a non-windows system)")
}
@@ -862,60 +856,6 @@ func updateLine(line *modfile.Line, tokens ...string) {
line.Token = tokens
}
-// setIndirect sets line to have (or not have) a "// indirect" comment.
-func setIndirect(r *modfile.Require, indirect bool) {
- r.Indirect = indirect
- line := r.Syntax
- if isIndirect(line) == indirect {
- return
- }
- if indirect {
- // Adding comment.
- if len(line.Suffix) == 0 {
- // New comment.
- line.Suffix = []modfile.Comment{{Token: "// indirect", Suffix: true}}
- return
- }
-
- com := &line.Suffix[0]
- text := strings.TrimSpace(strings.TrimPrefix(com.Token, string(slashSlash)))
- if text == "" {
- // Empty comment.
- com.Token = "// indirect"
- return
- }
-
- // Insert at beginning of existing comment.
- com.Token = "// indirect; " + text
- return
- }
-
- // Removing comment.
- f := strings.TrimSpace(strings.TrimPrefix(line.Suffix[0].Token, string(slashSlash)))
- if f == "indirect" {
- // Remove whole comment.
- line.Suffix = nil
- return
- }
-
- // Remove comment prefix.
- com := &line.Suffix[0]
- i := strings.Index(com.Token, "indirect;")
- com.Token = "//" + com.Token[i+len("indirect;"):]
-}
-
-// isIndirect reports whether line has a "// indirect" comment,
-// meaning it is in go.mod only for its effect on indirect dependencies,
-// so that it can be dropped entirely once the effective version of the
-// indirect dependency reaches the given minimum version.
-func isIndirect(line *modfile.Line) bool {
- if len(line.Suffix) == 0 {
- return false
- }
- f := strings.Fields(strings.TrimPrefix(line.Suffix[0].Token, string(slashSlash)))
- return (len(f) == 1 && f[0] == "indirect" || len(f) > 1 && f[0] == "indirect;")
-}
-
// addLine adds a line containing the given tokens to the file.
//
// If the first token of the hint matches the first token of the
diff --git a/gnovm/pkg/gnomod/read_test.go b/gnovm/pkg/gnomod/read_test.go
index cf3b6f59076..d9c35205a51 100644
--- a/gnovm/pkg/gnomod/read_test.go
+++ b/gnovm/pkg/gnomod/read_test.go
@@ -210,85 +210,6 @@ comments before "// e"
}
}
-var addRequireTests = []struct {
- desc string
- in string
- path string
- vers string
- out string
-}{
- {
- `existing`,
- `
- module m
- require x.y/z v1.2.3
- `,
- "x.y/z", "v1.5.6",
- `
- module m
- require x.y/z v1.5.6
- `,
- },
- {
- `existing2`,
- `
- module m
- require (
- x.y/z v1.2.3 // first
- x.z/a v0.1.0 // first-a
- )
- require x.y/z v1.4.5 // second
- require (
- x.y/z v1.6.7 // third
- x.z/a v0.2.0 // third-a
- )
- `,
- "x.y/z", "v1.8.9",
- `
- module m
-
- require (
- x.y/z v1.8.9 // first
- x.z/a v0.1.0 // first-a
- )
-
- require x.z/a v0.2.0 // third-a
- `,
- },
- {
- `new`,
- `
- module m
- require x.y/z v1.2.3
- `,
- "x.y/w", "v1.5.6",
- `
- module m
- require (
- x.y/z v1.2.3
- x.y/w v1.5.6
- )
- `,
- },
- {
- `new2`,
- `
- module m
- require x.y/z v1.2.3
- require x.y/q/v2 v2.3.4
- `,
- "x.y/w", "v1.5.6",
- `
- module m
- require x.y/z v1.2.3
- require (
- x.y/q/v2 v2.3.4
- x.y/w v1.5.6
- )
- `,
- },
-}
-
var addModuleStmtTests = []struct {
desc string
in string
@@ -299,12 +220,10 @@ var addModuleStmtTests = []struct {
`existing`,
`
module m
- require x.y/z v1.2.3
`,
"n",
`
module n
- require x.y/z v1.2.3
`,
},
{
@@ -330,7 +249,6 @@ var addReplaceTests = []struct {
`replace_with_module`,
`
module m
- require x.y/z v1.2.3
`,
"x.y/z",
"v1.5.6",
@@ -338,7 +256,6 @@ var addReplaceTests = []struct {
"v1.5.6",
`
module m
- require x.y/z v1.2.3
replace x.y/z v1.5.6 => a.b/c v1.5.6
`,
},
@@ -346,7 +263,6 @@ var addReplaceTests = []struct {
`replace_with_dir`,
`
module m
- require x.y/z v1.2.3
`,
"x.y/z",
"v1.5.6",
@@ -354,66 +270,11 @@ var addReplaceTests = []struct {
"",
`
module m
- require x.y/z v1.2.3
replace x.y/z v1.5.6 => /path/to/dir
`,
},
}
-var dropRequireTests = []struct {
- desc string
- in string
- path string
- out string
-}{
- {
- `existing`,
- `
- module m
- require x.y/z v1.2.3
- `,
- "x.y/z",
- `
- module m
- `,
- },
- {
- `existing2`,
- `
- module m
- require (
- x.y/z v1.2.3 // first
- x.z/a v0.1.0 // first-a
- )
- require x.y/z v1.4.5 // second
- require (
- x.y/z v1.6.7 // third
- x.z/a v0.2.0 // third-a
- )
- `,
- "x.y/z",
- `
- module m
-
- require x.z/a v0.1.0 // first-a
-
- require x.z/a v0.2.0 // third-a
- `,
- },
- {
- `not_exists`,
- `
- module m
- require x.y/z v1.2.3
- `,
- "a.b/c",
- `
- module m
- require x.y/z v1.2.3
- `,
- },
-}
-
var dropReplaceTests = []struct {
desc string
in string
@@ -425,7 +286,6 @@ var dropReplaceTests = []struct {
`existing`,
`
module m
- require x.y/z v1.2.3
replace x.y/z v1.2.3 => a.b/c v1.5.6
`,
@@ -433,14 +293,12 @@ var dropReplaceTests = []struct {
"v1.2.3",
`
module m
- require x.y/z v1.2.3
`,
},
{
`not_exists`,
`
module m
- require x.y/z v1.2.3
replace x.y/z v1.2.3 => a.b/c v1.5.6
`,
@@ -448,25 +306,12 @@ var dropReplaceTests = []struct {
"v3.2.1",
`
module m
- require x.y/z v1.2.3
replace x.y/z v1.2.3 => a.b/c v1.5.6
`,
},
}
-func TestAddRequire(t *testing.T) {
- for _, tt := range addRequireTests {
- t.Run(tt.desc, func(t *testing.T) {
- testEdit(t, tt.in, tt.out, func(f *File) error {
- err := f.AddRequire(tt.path, tt.vers)
- f.Syntax.Cleanup()
- return err
- })
- })
- }
-}
-
func TestAddModuleStmt(t *testing.T) {
for _, tt := range addModuleStmtTests {
t.Run(tt.desc, func(t *testing.T) {
@@ -491,18 +336,6 @@ func TestAddReplace(t *testing.T) {
}
}
-func TestDropRequire(t *testing.T) {
- for _, tt := range dropRequireTests {
- t.Run(tt.desc, func(t *testing.T) {
- testEdit(t, tt.in, tt.out, func(f *File) error {
- err := f.DropRequire(tt.path)
- f.Syntax.Cleanup()
- return err
- })
- })
- }
-}
-
func TestDropReplace(t *testing.T) {
for _, tt := range dropReplaceTests {
t.Run(tt.desc, func(t *testing.T) {
diff --git a/gnovm/pkg/packages/doc.go b/gnovm/pkg/packages/doc.go
new file mode 100644
index 00000000000..fb63ae3838e
--- /dev/null
+++ b/gnovm/pkg/packages/doc.go
@@ -0,0 +1,2 @@
+// Package packages provides utility functions to statically analyze Gno MemPackages
+package packages
diff --git a/gnovm/pkg/packages/imports.go b/gnovm/pkg/packages/imports.go
new file mode 100644
index 00000000000..e72f37276db
--- /dev/null
+++ b/gnovm/pkg/packages/imports.go
@@ -0,0 +1,72 @@
+package packages
+
+import (
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "go/token"
+ "sort"
+ "strconv"
+ "strings"
+
+ "github.com/gnolang/gno/gnovm"
+)
+
+// Imports returns the list of gno imports from a [gnovm.MemPackage].
+func Imports(pkg *gnovm.MemPackage) ([]string, error) {
+ allImports := make([]string, 0)
+ seen := make(map[string]struct{})
+ for _, file := range pkg.Files {
+ if !strings.HasSuffix(file.Name, ".gno") {
+ continue
+ }
+ if strings.HasSuffix(file.Name, "_filetest.gno") {
+ continue
+ }
+ imports, _, err := FileImports(file.Name, file.Body)
+ if err != nil {
+ return nil, err
+ }
+ for _, im := range imports {
+ if im.Error != nil {
+ return nil, err
+ }
+ if _, ok := seen[im.PkgPath]; ok {
+ continue
+ }
+ allImports = append(allImports, im.PkgPath)
+ seen[im.PkgPath] = struct{}{}
+ }
+ }
+ sort.Strings(allImports)
+
+ return allImports, nil
+}
+
+type FileImport struct {
+ PkgPath string
+ Spec *ast.ImportSpec
+ Error error
+}
+
+// FileImports returns the list of gno imports in the given file src.
+// The given filename is only used when recording position information.
+func FileImports(filename string, src string) ([]*FileImport, *token.FileSet, error) {
+ fs := token.NewFileSet()
+ f, err := parser.ParseFile(fs, filename, src, parser.ImportsOnly)
+ if err != nil {
+ return nil, nil, err
+ }
+ res := make([]*FileImport, len(f.Imports))
+ for i, im := range f.Imports {
+ fi := FileImport{Spec: im}
+ importPath, err := strconv.Unquote(im.Path.Value)
+ if err != nil {
+ fi.Error = fmt.Errorf("%v: unexpected invalid import path: %v", fs.Position(im.Pos()).String(), im.Path.Value)
+ } else {
+ fi.PkgPath = importPath
+ }
+ res[i] = &fi
+ }
+ return res, fs, nil
+}
diff --git a/gnovm/pkg/packages/imports_test.go b/gnovm/pkg/packages/imports_test.go
new file mode 100644
index 00000000000..14808dcbd6f
--- /dev/null
+++ b/gnovm/pkg/packages/imports_test.go
@@ -0,0 +1,127 @@
+package packages
+
+import (
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/gnolang/gno/gnovm/pkg/gnolang"
+ "github.com/stretchr/testify/require"
+)
+
+func TestImports(t *testing.T) {
+ workingDir, err := os.Getwd()
+ require.NoError(t, err)
+
+ // create external dir
+ tmpDir := t.TempDir()
+
+ // cd to tmp directory
+ os.Chdir(tmpDir)
+ defer os.Chdir(workingDir)
+
+ files := []struct {
+ name, data string
+ }{
+ {
+ name: "file1.gno",
+ data: `
+ package tmp
+
+ import (
+ "std"
+
+ "gno.land/p/demo/pkg1"
+ )
+ `,
+ },
+ {
+ name: "file2.gno",
+ data: `
+ package tmp
+
+ import (
+ "gno.land/p/demo/pkg1"
+ "gno.land/p/demo/pkg2"
+ )
+ `,
+ },
+ {
+ name: "file1_test.gno",
+ data: `
+ package tmp
+
+ import (
+ "testing"
+
+ "gno.land/p/demo/testpkg"
+ )
+ `,
+ },
+ {
+ name: "z_0_filetest.gno",
+ data: `
+ package main
+
+ import (
+ "gno.land/p/demo/filetestpkg"
+ )
+ `,
+ },
+
+ // subpkg files
+ {
+ name: filepath.Join("subtmp", "file1.gno"),
+ data: `
+ package subtmp
+
+ import (
+ "std"
+
+ "gno.land/p/demo/subpkg1"
+ )
+ `,
+ },
+ {
+ name: filepath.Join("subtmp", "file2.gno"),
+ data: `
+ package subtmp
+
+ import (
+ "gno.land/p/demo/subpkg1"
+ "gno.land/p/demo/subpkg2"
+ )
+ `,
+ },
+ }
+
+ // Expected list of imports
+ // - ignore subdirs
+ // - ignore duplicate
+ // - ignore *_filetest.gno
+ // - should be sorted
+ expected := []string{
+ "gno.land/p/demo/pkg1",
+ "gno.land/p/demo/pkg2",
+ "gno.land/p/demo/testpkg",
+ "std",
+ "testing",
+ }
+
+ // Create subpkg dir
+ err = os.Mkdir("subtmp", 0o700)
+ require.NoError(t, err)
+
+ // Create files
+ for _, f := range files {
+ err = os.WriteFile(f.name, []byte(f.data), 0o644)
+ require.NoError(t, err)
+ }
+
+ pkg, err := gnolang.ReadMemPackage(tmpDir, "test")
+ require.NoError(t, err)
+ imports, err := Imports(pkg)
+ require.NoError(t, err)
+
+ require.Equal(t, expected, imports)
+}
diff --git a/gnovm/pkg/repl/repl.go b/gnovm/pkg/repl/repl.go
index e7b5ecea96f..b0944d21646 100644
--- a/gnovm/pkg/repl/repl.go
+++ b/gnovm/pkg/repl/repl.go
@@ -13,8 +13,9 @@ import (
"os"
"text/template"
+ "github.com/gnolang/gno/gnovm/pkg/gnoenv"
gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
- "github.com/gnolang/gno/gnovm/tests"
+ "github.com/gnolang/gno/gnovm/pkg/test"
)
const (
@@ -124,7 +125,8 @@ func NewRepl(opts ...ReplOption) *Repl {
r.stderr = &b
r.storeFunc = func() gno.Store {
- return tests.TestStore("teststore", "", r.stdin, r.stdout, r.stderr, tests.ImportModeStdlibsOnly)
+ _, st := test.Store(gnoenv.RootDir(), false, r.stdin, r.stdout, r.stderr)
+ return st
}
for _, o := range opts {
diff --git a/gnovm/pkg/test/filetest.go b/gnovm/pkg/test/filetest.go
new file mode 100644
index 00000000000..da2c7a55797
--- /dev/null
+++ b/gnovm/pkg/test/filetest.go
@@ -0,0 +1,407 @@
+package test
+
+import (
+ "bufio"
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "io"
+ "regexp"
+ "runtime/debug"
+ "strconv"
+ "strings"
+
+ "github.com/gnolang/gno/gnovm"
+ gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
+ teststd "github.com/gnolang/gno/gnovm/tests/stdlibs/std"
+ "github.com/gnolang/gno/tm2/pkg/std"
+ "github.com/pmezard/go-difflib/difflib"
+ "go.uber.org/multierr"
+)
+
+// RunFiletest executes the program in source as a filetest.
+// If opts.Sync is enabled, and the filetest's golden output has changed,
+// the first string is set to the new generated content of the file.
+func (opts *TestOptions) RunFiletest(filename string, source []byte) (string, error) {
+ opts.outWriter.w = opts.Output
+
+ return opts.runFiletest(filename, source)
+}
+
+var reEndOfLineSpaces = func() *regexp.Regexp {
+ re := regexp.MustCompile(" +\n")
+ re.Longest()
+ return re
+}()
+
+func (opts *TestOptions) runFiletest(filename string, source []byte) (string, error) {
+ dirs, err := ParseDirectives(bytes.NewReader(source))
+ if err != nil {
+ return "", fmt.Errorf("error parsing directives: %w", err)
+ }
+
+ // Initialize Machine.Context and Machine.Alloc according to the input directives.
+ pkgPath := dirs.FirstDefault(DirectivePkgPath, "main")
+ coins, err := std.ParseCoins(dirs.FirstDefault(DirectiveSend, ""))
+ if err != nil {
+ return "", err
+ }
+ ctx := Context(
+ pkgPath,
+ coins,
+ )
+ maxAllocRaw := dirs.FirstDefault(DirectiveMaxAlloc, "0")
+ maxAlloc, err := strconv.ParseInt(maxAllocRaw, 10, 64)
+ if err != nil {
+ return "", fmt.Errorf("could not parse MAXALLOC directive: %w", err)
+ }
+
+ // Create machine for execution and run test
+ cw := opts.BaseStore.CacheWrap()
+ m := gno.NewMachineWithOptions(gno.MachineOptions{
+ Output: &opts.outWriter,
+ Store: opts.TestStore.BeginTransaction(cw, cw, nil),
+ Context: ctx,
+ MaxAllocBytes: maxAlloc,
+ })
+ defer m.Release()
+ result := opts.runTest(m, pkgPath, filename, source)
+
+ // updated tells whether the directives have been updated, and as such
+ // a new generated filetest should be returned.
+ // returnErr is used as the return value, and may be a MultiError if
+ // multiple mismatches occurred.
+ updated := false
+ var returnErr error
+ // match verifies the content against dir.Content; if different,
+ // either updates dir.Content (for opts.Sync) or appends a new returnErr.
+ match := func(dir *Directive, actual string) {
+ // Remove end-of-line spaces, as these are removed from `fmt` in the filetests anyway.
+ actual = reEndOfLineSpaces.ReplaceAllString(actual, "\n")
+ if dir.Content != actual {
+ if opts.Sync {
+ dir.Content = actual
+ updated = true
+ } else {
+ returnErr = multierr.Append(
+ returnErr,
+ fmt.Errorf("%s diff:\n%s", dir.Name, unifiedDiff(dir.Content, actual)),
+ )
+ }
+ }
+ }
+
+ // First, check if we have an error, whether we're supposed to get it.
+ if result.Error != "" {
+ // Ensure this error was supposed to happen.
+ errDirective := dirs.First(DirectiveError)
+ if errDirective == nil {
+ return "", fmt.Errorf("unexpected panic: %s\noutput:\n%s\nstack:\n%v",
+ result.Error, result.Output, string(result.GoPanicStack))
+ }
+
+ // The Error directive (and many others) will have one trailing newline,
+ // which is not in the output - so add it there.
+ match(errDirective, result.Error+"\n")
+ } else {
+ err = m.CheckEmpty()
+ if err != nil {
+ return "", fmt.Errorf("machine not empty after main: %w", err)
+ }
+ if gno.HasDebugErrors() {
+ return "", fmt.Errorf("got unexpected debug error(s): %v", gno.GetDebugErrors())
+ }
+ }
+
+ // Check through each directive and verify it against the values from the test.
+ for idx := range dirs {
+ dir := &dirs[idx]
+ switch dir.Name {
+ case DirectiveOutput:
+ if !strings.HasSuffix(result.Output, "\n") {
+ result.Output += "\n"
+ }
+ match(dir, result.Output)
+ case DirectiveRealm:
+ sops := m.Store.SprintStoreOps() + "\n"
+ match(dir, sops)
+ case DirectiveEvents:
+ events := m.Context.(*teststd.TestExecContext).EventLogger.Events()
+ evtjson, err := json.MarshalIndent(events, "", " ")
+ if err != nil {
+ panic(err)
+ }
+ evtstr := string(evtjson) + "\n"
+ match(dir, evtstr)
+ case DirectivePreprocessed:
+ pn := m.Store.GetBlockNode(gno.PackageNodeLocation(pkgPath))
+ pre := pn.(*gno.PackageNode).FileSet.Files[0].String() + "\n"
+ match(dir, pre)
+ case DirectiveStacktrace:
+ match(dir, result.GnoStacktrace)
+ }
+ }
+
+ if updated { // only true if sync == true
+ return dirs.FileTest(), returnErr
+ }
+
+ return "", returnErr
+}
+
+func unifiedDiff(wanted, actual string) string {
+ diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
+ A: difflib.SplitLines(wanted),
+ B: difflib.SplitLines(actual),
+ FromFile: "Expected",
+ FromDate: "",
+ ToFile: "Actual",
+ ToDate: "",
+ Context: 1,
+ })
+ if err != nil {
+ panic(fmt.Errorf("error generating unified diff: %w", err))
+ }
+ return diff
+}
+
+type runResult struct {
+ Output string
+ Error string
+ // Set if there was a panic within gno code.
+ GnoStacktrace string
+ // Set if this was recovered from a panic.
+ GoPanicStack []byte
+}
+
+func (opts *TestOptions) runTest(m *gno.Machine, pkgPath, filename string, content []byte) (rr runResult) {
+ // Eagerly load imports.
+ // This is executed using opts.Store, rather than the transaction store;
+ // it allows us to only have to load the imports once (and re-use the cached
+ // versions). Running the tests in separate "transactions" means that they
+ // don't get the parent store dirty.
+ if err := LoadImports(opts.TestStore, filename, content); err != nil {
+ // NOTE: we perform this here, so we can capture the runResult.
+ return runResult{Error: err.Error()}
+ }
+
+ // Reset and start capturing stdout.
+ opts.filetestBuffer.Reset()
+ revert := opts.outWriter.tee(&opts.filetestBuffer)
+ defer revert()
+
+ defer func() {
+ if r := recover(); r != nil {
+ rr.Output = opts.filetestBuffer.String()
+ rr.GoPanicStack = debug.Stack()
+ switch v := r.(type) {
+ case *gno.TypedValue:
+ rr.Error = v.Sprint(m)
+ case *gno.PreprocessError:
+ rr.Error = v.Unwrap().Error()
+ case gno.UnhandledPanicError:
+ rr.Error = v.Error()
+ rr.GnoStacktrace = m.ExceptionsStacktrace()
+ default:
+ rr.Error = fmt.Sprint(v)
+ rr.GnoStacktrace = m.Stacktrace().String()
+ }
+ }
+ }()
+
+ // Use last element after / (works also if slash is missing).
+ pkgName := gno.Name(pkgPath[strings.LastIndexByte(pkgPath, '/')+1:])
+ if !gno.IsRealmPath(pkgPath) {
+ // Simple case - pure package.
+ pn := gno.NewPackageNode(pkgName, pkgPath, &gno.FileSet{})
+ pv := pn.NewPackage()
+ m.Store.SetBlockNode(pn)
+ m.Store.SetCachePackage(pv)
+ m.SetActivePackage(pv)
+ n := gno.MustParseFile(filename, string(content))
+ m.RunFiles(n)
+ m.RunStatement(gno.S(gno.Call(gno.X("main"))))
+ } else {
+ // Realm case.
+ gno.DisableDebug() // until main call.
+
+ // Remove filetest from name, as that can lead to the package not being
+ // parsed correctly when using RunMemPackage.
+ filename = strings.ReplaceAll(filename, "_filetest", "")
+
+ // save package using realm crawl procedure.
+ memPkg := &gnovm.MemPackage{
+ Name: string(pkgName),
+ Path: pkgPath,
+ Files: []*gnovm.MemFile{
+ {
+ Name: filename,
+ Body: string(content),
+ },
+ },
+ }
+ orig, tx := m.Store, m.Store.BeginTransaction(nil, nil, nil)
+ m.Store = tx
+ // Run decls and init functions.
+ m.RunMemPackage(memPkg, true)
+ // Clear store cache and reconstruct machine from committed info
+ // (mimicking on-chain behaviour).
+ tx.Write()
+ m.Store = orig
+
+ pv2 := m.Store.GetPackage(pkgPath, false)
+ m.SetActivePackage(pv2)
+ gno.EnableDebug()
+ // clear store.opslog from init function(s).
+ m.Store.SetLogStoreOps(true) // resets.
+ m.RunStatement(gno.S(gno.Call(gno.X("main"))))
+ }
+
+ return runResult{
+ Output: opts.filetestBuffer.String(),
+ GnoStacktrace: m.Stacktrace().String(),
+ }
+}
+
+// ---------------------------------------
+// directives and directive parsing
+
+const (
+ // These directives are used to set input variables, which should change for
+ // the specific filetest. They must be specified on a single line.
+ DirectivePkgPath = "PKGPATH"
+ DirectiveMaxAlloc = "MAXALLOC"
+ DirectiveSend = "SEND"
+
+ // These are used to match the result of the filetest against known golden
+ // values.
+ DirectiveOutput = "Output"
+ DirectiveError = "Error"
+ DirectiveRealm = "Realm"
+ DirectiveEvents = "Events"
+ DirectivePreprocessed = "Preprocessed"
+ DirectiveStacktrace = "Stacktrace"
+)
+
+// Directives contains the directives of a file.
+// It may also contains directives with empty names, to indicate parts of the
+// original source file (used to re-construct the filetest at the end).
+type Directives []Directive
+
+// First returns the first directive with the corresponding name.
+func (d Directives) First(name string) *Directive {
+ if name == "" {
+ return nil
+ }
+ for i := range d {
+ if d[i].Name == name {
+ return &d[i]
+ }
+ }
+ return nil
+}
+
+// FirstDefault returns the [Directive.Content] of First(name); if First(name)
+// returns nil, then defaultValue is returned.
+func (d Directives) FirstDefault(name, defaultValue string) string {
+ v := d.First(name)
+ if v == nil {
+ return defaultValue
+ }
+ return v.Content
+}
+
+// FileTest re-generates the filetest from the given directives; the inverse of ParseDirectives.
+func (d Directives) FileTest() string {
+ var bld strings.Builder
+ for _, dir := range d {
+ switch {
+ case dir.Name == "":
+ bld.WriteString(dir.Content)
+ case strings.ToUpper(dir.Name) == dir.Name: // is it all uppercase?
+ bld.WriteString("// " + dir.Name + ": " + dir.Content + "\n")
+ default:
+ bld.WriteString("// " + dir.Name + ":\n")
+ cnt := strings.TrimSuffix(dir.Content, "\n")
+ lines := strings.Split(cnt, "\n")
+ for _, line := range lines {
+ if line == "" {
+ bld.WriteString("//\n")
+ continue
+ }
+ bld.WriteString("// ")
+ bld.WriteString(strings.TrimRight(line, " "))
+ bld.WriteByte('\n')
+ }
+ }
+ }
+ return bld.String()
+}
+
+// Directive represents a directive in a filetest.
+// A [Directives] slice may also contain directives with empty Names:
+// these compose the source file itself, and are used to re-construct the file
+// when a directive is changed.
+type Directive struct {
+ Name string
+ Content string
+}
+
+// Allows either a `ALLCAPS: content` on a single line, or a `PascalCase:`,
+// with content on the following lines.
+var reDirectiveLine = regexp.MustCompile("^(?:([A-Z][a-z]*):|([A-Z]+): ?(.*))$")
+
+// ParseDirectives parses all the directives in the filetest given at source.
+func ParseDirectives(source io.Reader) (Directives, error) {
+ sc := bufio.NewScanner(source)
+ parsed := make(Directives, 0, 8)
+ for sc.Scan() {
+ // Re-append trailing newline.
+ // Useful as we always use it anyway.
+ txt := sc.Text() + "\n"
+ if !strings.HasPrefix(txt, "//") {
+ if len(parsed) == 0 || parsed[len(parsed)-1].Name != "" {
+ parsed = append(parsed, Directive{Content: txt})
+ continue
+ }
+ parsed[len(parsed)-1].Content += txt
+ continue
+ }
+
+ comment := txt[2 : len(txt)-1] // leading double slash, trailing \n
+ comment = strings.TrimPrefix(comment, " ") // leading space (if any)
+
+ // If we're already in a directive, simply append there.
+ if len(parsed) > 0 && parsed[len(parsed)-1].Name != "" {
+ parsed[len(parsed)-1].Content += comment + "\n"
+ continue
+ }
+
+ // Find if there is a colon (indicating a possible directive).
+ subm := reDirectiveLine.FindStringSubmatch(comment)
+ switch {
+ case subm == nil:
+ // Not found; append to parsed as a line, or to the previous
+ // directive if it exists.
+ if len(parsed) == 0 {
+ parsed = append(parsed, Directive{Content: txt})
+ continue
+ }
+ last := &parsed[len(parsed)-1]
+ if last.Name == "" {
+ last.Content += txt
+ } else {
+ last.Content += comment + "\n"
+ }
+ case subm[1] != "": // output directive, with content on newlines
+ parsed = append(parsed, Directive{Name: subm[1]})
+ default: // subm[2] != "", all caps
+ parsed = append(parsed,
+ Directive{Name: subm[2], Content: subm[3]},
+ // enforce new directive later
+ Directive{},
+ )
+ }
+ }
+ return parsed, sc.Err()
+}
diff --git a/gnovm/pkg/test/imports.go b/gnovm/pkg/test/imports.go
new file mode 100644
index 00000000000..731bf9756dd
--- /dev/null
+++ b/gnovm/pkg/test/imports.go
@@ -0,0 +1,261 @@
+package test
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "math/big"
+ "os"
+ "path/filepath"
+ "runtime/debug"
+ "strings"
+ "time"
+
+ gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
+ "github.com/gnolang/gno/gnovm/pkg/packages"
+ teststdlibs "github.com/gnolang/gno/gnovm/tests/stdlibs"
+ teststd "github.com/gnolang/gno/gnovm/tests/stdlibs/std"
+ "github.com/gnolang/gno/tm2/pkg/db/memdb"
+ osm "github.com/gnolang/gno/tm2/pkg/os"
+ "github.com/gnolang/gno/tm2/pkg/std"
+ "github.com/gnolang/gno/tm2/pkg/store/dbadapter"
+ storetypes "github.com/gnolang/gno/tm2/pkg/store/types"
+)
+
+// NOTE: this isn't safe, should only be used for testing.
+func Store(
+ rootDir string,
+ withExtern bool,
+ stdin io.Reader,
+ stdout, stderr io.Writer,
+) (
+ baseStore storetypes.CommitStore,
+ resStore gno.Store,
+) {
+ getPackage := func(pkgPath string, store gno.Store) (pn *gno.PackageNode, pv *gno.PackageValue) {
+ if pkgPath == "" {
+ panic(fmt.Sprintf("invalid zero package path in testStore().pkgGetter"))
+ }
+
+ if withExtern {
+ // if _test package...
+ const testPath = "github.com/gnolang/gno/_test/"
+ if strings.HasPrefix(pkgPath, testPath) {
+ baseDir := filepath.Join(rootDir, "gnovm", "tests", "files", "extern", pkgPath[len(testPath):])
+ memPkg := gno.MustReadMemPackage(baseDir, pkgPath)
+ send := std.Coins{}
+ ctx := Context(pkgPath, send)
+ m2 := gno.NewMachineWithOptions(gno.MachineOptions{
+ PkgPath: "test",
+ Output: stdout,
+ Store: store,
+ Context: ctx,
+ })
+ return m2.RunMemPackage(memPkg, true)
+ }
+ }
+
+ // gonative exceptions.
+ // These are values available using gonative; eventually they should all be removed.
+ switch pkgPath {
+ case "os":
+ pkg := gno.NewPackageNode("os", pkgPath, nil)
+ pkg.DefineGoNativeValue("Stdin", stdin)
+ pkg.DefineGoNativeValue("Stdout", stdout)
+ pkg.DefineGoNativeValue("Stderr", stderr)
+ return pkg, pkg.NewPackage()
+ case "fmt":
+ pkg := gno.NewPackageNode("fmt", pkgPath, nil)
+ pkg.DefineGoNativeValue("Println", func(a ...interface{}) (n int, err error) {
+ // NOTE: uncomment to debug long running tests
+ // fmt.Println(a...)
+ res := fmt.Sprintln(a...)
+ return stdout.Write([]byte(res))
+ })
+ pkg.DefineGoNativeValue("Printf", func(format string, a ...interface{}) (n int, err error) {
+ res := fmt.Sprintf(format, a...)
+ return stdout.Write([]byte(res))
+ })
+ pkg.DefineGoNativeValue("Print", func(a ...interface{}) (n int, err error) {
+ res := fmt.Sprint(a...)
+ return stdout.Write([]byte(res))
+ })
+ pkg.DefineGoNativeValue("Sprint", fmt.Sprint)
+ pkg.DefineGoNativeValue("Sprintf", fmt.Sprintf)
+ pkg.DefineGoNativeValue("Sprintln", fmt.Sprintln)
+ pkg.DefineGoNativeValue("Sscanf", fmt.Sscanf)
+ pkg.DefineGoNativeValue("Errorf", fmt.Errorf)
+ pkg.DefineGoNativeValue("Fprintln", fmt.Fprintln)
+ pkg.DefineGoNativeValue("Fprintf", fmt.Fprintf)
+ pkg.DefineGoNativeValue("Fprint", fmt.Fprint)
+ return pkg, pkg.NewPackage()
+ case "encoding/json":
+ pkg := gno.NewPackageNode("json", pkgPath, nil)
+ pkg.DefineGoNativeValue("Unmarshal", json.Unmarshal)
+ pkg.DefineGoNativeValue("Marshal", json.Marshal)
+ return pkg, pkg.NewPackage()
+ case "internal/os_test":
+ pkg := gno.NewPackageNode("os_test", pkgPath, nil)
+ pkg.DefineNative("Sleep",
+ gno.Flds( // params
+ "d", gno.AnyT(), // NOTE: should be time.Duration
+ ),
+ gno.Flds( // results
+ ),
+ func(m *gno.Machine) {
+ // For testing purposes here, nanoseconds are separately kept track.
+ arg0 := m.LastBlock().GetParams1().TV
+ d := arg0.GetInt64()
+ sec := d / int64(time.Second)
+ nano := d % int64(time.Second)
+ ctx := m.Context.(*teststd.TestExecContext)
+ ctx.Timestamp += sec
+ ctx.TimestampNano += nano
+ if ctx.TimestampNano >= int64(time.Second) {
+ ctx.Timestamp += 1
+ ctx.TimestampNano -= int64(time.Second)
+ }
+ m.Context = ctx
+ },
+ )
+ return pkg, pkg.NewPackage()
+ case "math/big":
+ pkg := gno.NewPackageNode("big", pkgPath, nil)
+ pkg.DefineGoNativeValue("NewInt", big.NewInt)
+ return pkg, pkg.NewPackage()
+ }
+
+ // Load normal stdlib.
+ pn, pv = loadStdlib(rootDir, pkgPath, store, stdout)
+ if pn != nil {
+ return
+ }
+
+ // if examples package...
+ examplePath := filepath.Join(rootDir, "examples", pkgPath)
+ if osm.DirExists(examplePath) {
+ memPkg := gno.MustReadMemPackage(examplePath, pkgPath)
+ if memPkg.IsEmpty() {
+ panic(fmt.Sprintf("found an empty package %q", pkgPath))
+ }
+
+ send := std.Coins{}
+ ctx := Context(pkgPath, send)
+ m2 := gno.NewMachineWithOptions(gno.MachineOptions{
+ PkgPath: "test",
+ Output: stdout,
+ Store: store,
+ Context: ctx,
+ })
+ pn, pv = m2.RunMemPackage(memPkg, true)
+ return
+ }
+ return nil, nil
+ }
+ db := memdb.NewMemDB()
+ baseStore = dbadapter.StoreConstructor(db, storetypes.StoreOptions{})
+ // Make a new store.
+ resStore = gno.NewStore(nil, baseStore, baseStore)
+ resStore.SetPackageGetter(getPackage)
+ resStore.SetNativeResolver(teststdlibs.NativeResolver)
+ return
+}
+
+func loadStdlib(rootDir, pkgPath string, store gno.Store, stdout io.Writer) (*gno.PackageNode, *gno.PackageValue) {
+ dirs := [...]string{
+ // Normal stdlib path.
+ filepath.Join(rootDir, "gnovm", "stdlibs", pkgPath),
+ // Override path. Definitions here override the previous if duplicate.
+ filepath.Join(rootDir, "gnovm", "tests", "stdlibs", pkgPath),
+ }
+ files := make([]string, 0, 32) // pre-alloc 32 as a likely high number of files
+ for _, path := range dirs {
+ dl, err := os.ReadDir(path)
+ if err != nil {
+ if os.IsNotExist(err) {
+ continue
+ }
+ panic(fmt.Errorf("could not access dir %q: %w", path, err))
+ }
+
+ for _, f := range dl {
+ // NOTE: RunMemPackage has other rules; those should be mostly useful
+ // for on-chain packages (ie. include README and gno.mod).
+ if !f.IsDir() && strings.HasSuffix(f.Name(), ".gno") {
+ files = append(files, filepath.Join(path, f.Name()))
+ }
+ }
+ }
+ if len(files) == 0 {
+ return nil, nil
+ }
+
+ memPkg := gno.MustReadMemPackageFromList(files, pkgPath)
+ m2 := gno.NewMachineWithOptions(gno.MachineOptions{
+ // NOTE: see also pkgs/sdk/vm/builtins.go
+ // Needs PkgPath != its name because TestStore.getPackage is the package
+ // getter for the store, which calls loadStdlib, so it would be recursively called.
+ PkgPath: "stdlibload",
+ Output: stdout,
+ Store: store,
+ })
+ return m2.RunMemPackageWithOverrides(memPkg, true)
+}
+
+type stackWrappedError struct {
+ err error
+ stack []byte
+}
+
+func (e *stackWrappedError) Error() string { return e.err.Error() }
+func (e *stackWrappedError) Unwrap() error { return e.err }
+func (e *stackWrappedError) String() string {
+ return fmt.Sprintf("%v\nstack:\n%v", e.err, string(e.stack))
+}
+
+// LoadImports parses the given file and attempts to retrieve all pure packages
+// from the store. This is mostly useful for "eager import loading", whereby all
+// imports are pre-loaded in a permanent store, so that the tests can use
+// ephemeral transaction stores.
+func LoadImports(store gno.Store, filename string, content []byte) (err error) {
+ defer func() {
+ // This is slightly different from the handling below; we do not have a
+ // machine to work with, as this comes from an import; so we need
+ // "machine-less" alternatives. (like v.String instead of v.Sprint)
+ if r := recover(); r != nil {
+ switch v := r.(type) {
+ case *gno.TypedValue:
+ err = errors.New(v.String())
+ case *gno.PreprocessError:
+ err = v.Unwrap()
+ case gno.UnhandledPanicError:
+ err = v
+ case error:
+ err = &stackWrappedError{v, debug.Stack()}
+ default:
+ err = &stackWrappedError{fmt.Errorf("%v", v), debug.Stack()}
+ }
+ }
+ }()
+
+ imports, fset, err := packages.FileImports(filename, string(content))
+ if err != nil {
+ return err
+ }
+ for _, imp := range imports {
+ if imp.Error != nil {
+ return imp.Error
+ }
+ if gno.IsRealmPath(imp.PkgPath) {
+ // Don't eagerly load realms.
+ // Realms persist state and can change the state of other realms in initialization.
+ continue
+ }
+ pkg := store.GetPackage(imp.PkgPath, true)
+ if pkg == nil {
+ return fmt.Errorf("%v: unknown import path %v", fset.Position(imp.Spec.Pos()).String(), imp.PkgPath)
+ }
+ }
+ return nil
+}
diff --git a/gnovm/pkg/test/test.go b/gnovm/pkg/test/test.go
new file mode 100644
index 00000000000..077abbe8984
--- /dev/null
+++ b/gnovm/pkg/test/test.go
@@ -0,0 +1,486 @@
+// Package test contains the code to parse and execute Gno tests and filetests.
+package test
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "math"
+ "os"
+ "path"
+ "path/filepath"
+ "runtime/debug"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/gnolang/gno/gnovm"
+ gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
+ "github.com/gnolang/gno/gnovm/stdlibs"
+ teststd "github.com/gnolang/gno/gnovm/tests/stdlibs/std"
+ "github.com/gnolang/gno/tm2/pkg/crypto"
+ "github.com/gnolang/gno/tm2/pkg/sdk"
+ "github.com/gnolang/gno/tm2/pkg/std"
+ storetypes "github.com/gnolang/gno/tm2/pkg/store/types"
+ "go.uber.org/multierr"
+)
+
+const (
+ // DefaultHeight is the default height used in the [Context].
+ DefaultHeight = 123
+ // DefaultTimestamp is the Timestamp value used by default in [Context].
+ DefaultTimestamp = 1234567890
+ // DefaultCaller is the result of gno.DerivePkgAddr("user1.gno"),
+ // used as the default caller in [Context].
+ DefaultCaller crypto.Bech32Address = "g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm"
+)
+
+// Context returns a TestExecContext. Usable for test purpose only.
+// The returned context has a mock banker, params and event logger. It will give
+// the pkgAddr the coins in `send` by default, and only that.
+// The Height and Timestamp parameters are set to the [DefaultHeight] and
+// [DefaultTimestamp].
+func Context(pkgPath string, send std.Coins) *teststd.TestExecContext {
+ // FIXME: create a better package to manage this, with custom constructors
+ pkgAddr := gno.DerivePkgAddr(pkgPath) // the addr of the pkgPath called.
+
+ banker := &teststd.TestBanker{
+ CoinTable: map[crypto.Bech32Address]std.Coins{
+ pkgAddr.Bech32(): send,
+ },
+ }
+ ctx := stdlibs.ExecContext{
+ ChainID: "dev",
+ ChainDomain: "tests.gno.land",
+ Height: DefaultHeight,
+ Timestamp: DefaultTimestamp,
+ OrigCaller: DefaultCaller,
+ OrigPkgAddr: pkgAddr.Bech32(),
+ OrigSend: send,
+ OrigSendSpent: new(std.Coins),
+ Banker: banker,
+ Params: newTestParams(),
+ EventLogger: sdk.NewEventLogger(),
+ }
+ return &teststd.TestExecContext{
+ ExecContext: ctx,
+ RealmFrames: make(map[*gno.Frame]teststd.RealmOverride),
+ }
+}
+
+// Machine is a minimal machine, set up with just the Store, Output and Context.
+func Machine(testStore gno.Store, output io.Writer, pkgPath string) *gno.Machine {
+ return gno.NewMachineWithOptions(gno.MachineOptions{
+ Store: testStore,
+ Output: output,
+ Context: Context(pkgPath, nil),
+ })
+}
+
+// ----------------------------------------
+// testParams
+
+type testParams struct{}
+
+func newTestParams() *testParams {
+ return &testParams{}
+}
+
+func (tp *testParams) SetBool(key string, val bool) { /* noop */ }
+func (tp *testParams) SetBytes(key string, val []byte) { /* noop */ }
+func (tp *testParams) SetInt64(key string, val int64) { /* noop */ }
+func (tp *testParams) SetUint64(key string, val uint64) { /* noop */ }
+func (tp *testParams) SetString(key string, val string) { /* noop */ }
+
+// ----------------------------------------
+// main test function
+
+// TestOptions is a list of options that must be passed to [Test].
+type TestOptions struct {
+ // BaseStore / TestStore to use for the tests.
+ BaseStore storetypes.CommitStore
+ TestStore gno.Store
+ // Gno root dir.
+ RootDir string
+ // Used for printing program output, during verbose logging.
+ Output io.Writer
+ // Used for os.Stderr, and for printing errors.
+ Error io.Writer
+
+ // Not set by NewTestOptions:
+
+ // Flag to filter tests to run.
+ RunFlag string
+ // Whether to update filetest directives.
+ Sync bool
+ // Uses Error to print when starting a test, and prints test output directly,
+ // unbuffered.
+ Verbose bool
+ // Uses Error to print runtime metrics for tests.
+ Metrics bool
+ // Uses Error to print the events emitted.
+ Events bool
+
+ filetestBuffer bytes.Buffer
+ outWriter proxyWriter
+}
+
+// WriterForStore is the writer that should be passed to [Store], so that
+// [Test] is then able to swap it when needed.
+func (opts *TestOptions) WriterForStore() io.Writer {
+ return &opts.outWriter
+}
+
+// NewTestOptions sets up TestOptions, filling out all "required" parameters.
+func NewTestOptions(rootDir string, stdin io.Reader, stdout, stderr io.Writer) *TestOptions {
+ opts := &TestOptions{
+ RootDir: rootDir,
+ Output: stdout,
+ Error: stderr,
+ }
+ opts.BaseStore, opts.TestStore = Store(
+ rootDir, false,
+ stdin, opts.WriterForStore(), stderr,
+ )
+ return opts
+}
+
+// proxyWriter is a simple wrapper around a io.Writer, it exists so that the
+// underlying writer can be swapped with another when necessary.
+type proxyWriter struct {
+ w io.Writer
+}
+
+func (p *proxyWriter) Write(b []byte) (int, error) {
+ return p.w.Write(b)
+}
+
+// tee temporarily appends the writer w to an underlying MultiWriter, which
+// should then be reverted using revert().
+func (p *proxyWriter) tee(w io.Writer) (revert func()) {
+ save := p.w
+ if save == io.Discard {
+ p.w = w
+ } else {
+ p.w = io.MultiWriter(save, w)
+ }
+ return func() {
+ p.w = save
+ }
+}
+
+// Test runs tests on the specified memPkg.
+// fsDir is the directory on filesystem of package; it's used in case opts.Sync
+// is enabled, and points to the directory where the files are contained if they
+// are to be updated.
+// opts is a required set of options, which is often shared among different
+// tests; you can use [NewTestOptions] for a common base configuration.
+func Test(memPkg *gnovm.MemPackage, fsDir string, opts *TestOptions) error {
+ opts.outWriter.w = opts.Output
+
+ var errs error
+
+ // Stands for "test", "integration test", and "filetest".
+ // "integration test" are the test files with `package xxx_test` (they are
+ // not necessarily integration tests, it's just for our internal reference.)
+ tset, itset, itfiles, ftfiles := parseMemPackageTests(opts.TestStore, memPkg)
+
+ // Testing with *_test.gno
+ if len(tset.Files)+len(itset.Files) > 0 {
+ // Create a common cw/gs for both the `pkg` tests as well as the `pkg_test`
+ // tests. This allows us to "export" symbols from the pkg tests and
+ // import them from the `pkg_test` tests.
+ cw := opts.BaseStore.CacheWrap()
+ gs := opts.TestStore.BeginTransaction(cw, cw, nil)
+
+ // Run test files in pkg.
+ if len(tset.Files) > 0 {
+ err := opts.runTestFiles(memPkg, tset, cw, gs)
+ if err != nil {
+ errs = multierr.Append(errs, err)
+ }
+ }
+
+ // Test xxx_test pkg.
+ if len(itset.Files) > 0 {
+ itPkg := &gnovm.MemPackage{
+ Name: memPkg.Name + "_test",
+ Path: memPkg.Path + "_test",
+ Files: itfiles,
+ }
+
+ err := opts.runTestFiles(itPkg, itset, cw, gs)
+ if err != nil {
+ errs = multierr.Append(errs, err)
+ }
+ }
+ }
+
+ // Testing with *_filetest.gno.
+ if len(ftfiles) > 0 {
+ filter := splitRegexp(opts.RunFlag)
+ for _, testFile := range ftfiles {
+ testFileName := testFile.Name
+ testFilePath := filepath.Join(fsDir, testFileName)
+ testName := "file/" + testFileName
+ if !shouldRun(filter, testName) {
+ continue
+ }
+
+ startedAt := time.Now()
+ if opts.Verbose {
+ fmt.Fprintf(opts.Error, "=== RUN %s\n", testName)
+ }
+
+ changed, err := opts.runFiletest(testFileName, []byte(testFile.Body))
+ if changed != "" {
+ // Note: changed always == "" if opts.Sync == false.
+ err = os.WriteFile(testFilePath, []byte(changed), 0o644)
+ if err != nil {
+ panic(fmt.Errorf("could not fix golden file: %w", err))
+ }
+ }
+
+ duration := time.Since(startedAt)
+ dstr := fmtDuration(duration)
+ if err != nil {
+ fmt.Fprintf(opts.Error, "--- FAIL: %s (%s)\n", testName, dstr)
+ fmt.Fprintln(opts.Error, err.Error())
+ errs = multierr.Append(errs, fmt.Errorf("%s failed", testName))
+ } else if opts.Verbose {
+ fmt.Fprintf(opts.Error, "--- PASS: %s (%s)\n", testName, dstr)
+ }
+
+ // XXX: add per-test metrics
+ }
+ }
+
+ return errs
+}
+
+func (opts *TestOptions) runTestFiles(
+ memPkg *gnovm.MemPackage,
+ files *gno.FileSet,
+ cw storetypes.Store, gs gno.TransactionStore,
+) (errs error) {
+ var m *gno.Machine
+ defer func() {
+ if r := recover(); r != nil {
+ if st := m.ExceptionsStacktrace(); st != "" {
+ errs = multierr.Append(errors.New(st), errs)
+ }
+ errs = multierr.Append(
+ fmt.Errorf("panic: %v\ngo stacktrace:\n%v\ngno machine: %v\ngno stacktrace:\n%v",
+ r, string(debug.Stack()), m.String(), m.Stacktrace()),
+ errs,
+ )
+ }
+ }()
+
+ tests := loadTestFuncs(memPkg.Name, files)
+
+ var alloc *gno.Allocator
+ if opts.Metrics {
+ alloc = gno.NewAllocator(math.MaxInt64)
+ }
+ // reset store ops, if any - we only need them for some filetests.
+ opts.TestStore.SetLogStoreOps(false)
+
+ // Check if we already have the package - it may have been eagerly
+ // loaded.
+ m = Machine(gs, opts.WriterForStore(), memPkg.Path)
+ m.Alloc = alloc
+ if opts.TestStore.GetMemPackage(memPkg.Path) == nil {
+ m.RunMemPackage(memPkg, true)
+ } else {
+ m.SetActivePackage(gs.GetPackage(memPkg.Path, false))
+ }
+ pv := m.Package
+
+ m.RunFiles(files.Files...)
+
+ for _, tf := range tests {
+ // TODO(morgan): we could theoretically use wrapping on the baseStore
+ // and gno store to achieve per-test isolation. However, that requires
+ // some deeper changes, as ideally we'd:
+ // - Run the MemPackage independently (so it can also be run as a
+ // consequence of an import)
+ // - Run the test files before this for loop (but persist it to store;
+ // RunFiles doesn't do that currently)
+ // - Wrap here.
+ m = Machine(gs, opts.Output, memPkg.Path)
+ m.Alloc = alloc
+ m.SetActivePackage(pv)
+
+ testingpv := m.Store.GetPackage("testing", false)
+ testingtv := gno.TypedValue{T: &gno.PackageType{}, V: testingpv}
+ testingcx := &gno.ConstExpr{TypedValue: testingtv}
+
+ eval := m.Eval(gno.Call(
+ gno.Sel(testingcx, "RunTest"), // Call testing.RunTest
+ gno.Str(opts.RunFlag), // run flag
+ gno.Nx(strconv.FormatBool(opts.Verbose)), // is verbose?
+ &gno.CompositeLitExpr{ // Third param, the testing.InternalTest
+ Type: gno.Sel(testingcx, "InternalTest"),
+ Elts: gno.KeyValueExprs{
+ {Key: gno.X("Name"), Value: gno.Str(tf.Name)},
+ {Key: gno.X("F"), Value: gno.Nx(tf.Name)},
+ },
+ },
+ ))
+
+ if opts.Events {
+ events := m.Context.(*teststd.TestExecContext).EventLogger.Events()
+ if events != nil {
+ res, err := json.Marshal(events)
+ if err != nil {
+ panic(err)
+ }
+ fmt.Fprintf(opts.Error, "EVENTS: %s\n", string(res))
+ }
+ }
+
+ ret := eval[0].GetString()
+ if ret == "" {
+ err := fmt.Errorf("failed to execute unit test: %q", tf.Name)
+ errs = multierr.Append(errs, err)
+ fmt.Fprintf(opts.Error, "--- FAIL: %s [internal gno testing error]", tf.Name)
+ continue
+ }
+
+ // TODO: replace with amino or send native type?
+ var rep report
+ err := json.Unmarshal([]byte(ret), &rep)
+ if err != nil {
+ errs = multierr.Append(errs, err)
+ fmt.Fprintf(opts.Error, "--- FAIL: %s [internal gno testing error]", tf.Name)
+ continue
+ }
+
+ if rep.Failed {
+ err := fmt.Errorf("failed: %q", tf.Name)
+ errs = multierr.Append(errs, err)
+ }
+
+ if opts.Metrics {
+ // XXX: store changes
+ // XXX: max mem consumption
+ allocsVal := "n/a"
+ if m.Alloc != nil {
+ maxAllocs, allocs := m.Alloc.Status()
+ allocsVal = fmt.Sprintf("%s(%.2f%%)",
+ prettySize(allocs),
+ float64(allocs)/float64(maxAllocs)*100,
+ )
+ }
+ fmt.Fprintf(opts.Error, "--- runtime: cycle=%s allocs=%s\n",
+ prettySize(m.Cycles),
+ allocsVal,
+ )
+ }
+ }
+
+ return errs
+}
+
+// report is a mirror of Gno's stdlibs/testing.Report.
+type report struct {
+ Failed bool
+ Skipped bool
+}
+
+type testFunc struct {
+ Package string
+ Name string
+}
+
+func loadTestFuncs(pkgName string, tfiles *gno.FileSet) (rt []testFunc) {
+ for _, tf := range tfiles.Files {
+ for _, d := range tf.Decls {
+ if fd, ok := d.(*gno.FuncDecl); ok {
+ fname := string(fd.Name)
+ if strings.HasPrefix(fname, "Test") {
+ tf := testFunc{
+ Package: pkgName,
+ Name: fname,
+ }
+ rt = append(rt, tf)
+ }
+ }
+ }
+ }
+ return
+}
+
+// parseMemPackageTests parses test files (skipping filetests) in the memPkg.
+func parseMemPackageTests(store gno.Store, memPkg *gnovm.MemPackage) (tset, itset *gno.FileSet, itfiles, ftfiles []*gnovm.MemFile) {
+ tset = &gno.FileSet{}
+ itset = &gno.FileSet{}
+ var errs error
+ for _, mfile := range memPkg.Files {
+ if !strings.HasSuffix(mfile.Name, ".gno") {
+ continue // skip this file.
+ }
+
+ if err := LoadImports(store, path.Join(memPkg.Path, mfile.Name), []byte(mfile.Body)); err != nil {
+ errs = multierr.Append(errs, err)
+ }
+
+ n, err := gno.ParseFile(mfile.Name, mfile.Body)
+ if err != nil {
+ errs = multierr.Append(errs, err)
+ continue
+ }
+ if n == nil {
+ panic("should not happen")
+ }
+ switch {
+ case strings.HasSuffix(mfile.Name, "_filetest.gno"):
+ ftfiles = append(ftfiles, mfile)
+ case strings.HasSuffix(mfile.Name, "_test.gno") && memPkg.Name == string(n.PkgName):
+ tset.AddFiles(n)
+ case strings.HasSuffix(mfile.Name, "_test.gno") && memPkg.Name+"_test" == string(n.PkgName):
+ itset.AddFiles(n)
+ itfiles = append(itfiles, mfile)
+ case memPkg.Name == string(n.PkgName):
+ // normal package file
+ default:
+ panic(fmt.Sprintf(
+ "expected package name [%s] or [%s_test] but got [%s] file [%s]",
+ memPkg.Name, memPkg.Name, n.PkgName, mfile))
+ }
+ }
+ if errs != nil {
+ panic(errs)
+ }
+ return
+}
+
+func shouldRun(filter filterMatch, path string) bool {
+ if filter == nil {
+ return true
+ }
+ elem := strings.Split(path, "/")
+ ok, _ := filter.matches(elem, matchString)
+ return ok
+}
+
+// Adapted from https://yourbasic.org/golang/formatting-byte-size-to-human-readable-format/
+func prettySize(nb int64) string {
+ const unit = 1000
+ if nb < unit {
+ return fmt.Sprintf("%d", nb)
+ }
+ div, exp := int64(unit), 0
+ for n := nb / unit; n >= unit; n /= unit {
+ div *= unit
+ exp++
+ }
+ return fmt.Sprintf("%.1f%c", float64(nb)/float64(div), "kMGTPE"[exp])
+}
+
+func fmtDuration(d time.Duration) string {
+ return fmt.Sprintf("%.2fs", d.Seconds())
+}
diff --git a/gnovm/cmd/gno/util_match.go b/gnovm/pkg/test/util_match.go
similarity index 99%
rename from gnovm/cmd/gno/util_match.go
rename to gnovm/pkg/test/util_match.go
index 34181f61254..a3fec22f389 100644
--- a/gnovm/cmd/gno/util_match.go
+++ b/gnovm/pkg/test/util_match.go
@@ -1,4 +1,4 @@
-package main
+package test
// Most of the code in this file is extracted from golang's src/testing/match.go.
//
diff --git a/gnovm/stdlibs/bytes/compare_test.gno b/gnovm/stdlibs/bytes/compare_test.gno
index f2b1e7c692b..5ebeba33889 100644
--- a/gnovm/stdlibs/bytes/compare_test.gno
+++ b/gnovm/stdlibs/bytes/compare_test.gno
@@ -66,6 +66,8 @@ func TestCompareIdenticalSlice(t *testing.T) {
}
func TestCompareBytes(t *testing.T) {
+ t.Skip("This test takes very long to run on Gno at time of writing, even in its short form")
+
lengths := make([]int, 0) // lengths to test in ascending order
for i := 0; i <= 128; i++ {
lengths = append(lengths, i)
diff --git a/gnovm/stdlibs/generated.go b/gnovm/stdlibs/generated.go
index a2d82b0bc60..67b492a34b2 100644
--- a/gnovm/stdlibs/generated.go
+++ b/gnovm/stdlibs/generated.go
@@ -468,6 +468,26 @@ var nativeFuncs = [...]NativeFunc{
))
},
},
+ {
+ "std",
+ "GetChainDomain",
+ []gno.FieldTypeExpr{},
+ []gno.FieldTypeExpr{
+ {Name: gno.N("r0"), Type: gno.X("string")},
+ },
+ true,
+ func(m *gno.Machine) {
+ r0 := libs_std.GetChainDomain(
+ m,
+ )
+
+ m.PushValue(gno.Go2GnoValue(
+ m.Alloc,
+ m.Store,
+ reflect.ValueOf(&r0).Elem(),
+ ))
+ },
+ },
{
"std",
"GetHeight",
diff --git a/gnovm/stdlibs/io/example_test.gno b/gnovm/stdlibs/io/example_test.gno
index c781fb9166e..54a9e74f55a 100644
--- a/gnovm/stdlibs/io/example_test.gno
+++ b/gnovm/stdlibs/io/example_test.gno
@@ -8,7 +8,6 @@ import (
"bytes"
"fmt"
"io"
- "log"
"os"
"strings"
)
@@ -17,7 +16,7 @@ func ExampleCopy() {
r := strings.NewReader("some io.Reader stream to be read\n")
if _, err := io.Copy(os.Stdout, r); err != nil {
- log.Fatal(err)
+ panic(err)
}
// Output:
@@ -31,12 +30,12 @@ func ExampleCopyBuffer() {
// buf is used here...
if _, err := io.CopyBuffer(os.Stdout, r1, buf); err != nil {
- log.Fatal(err)
+ panic(err)
}
// ... reused here also. No need to allocate an extra buffer.
if _, err := io.CopyBuffer(os.Stdout, r2, buf); err != nil {
- log.Fatal(err)
+ panic(err)
}
// Output:
@@ -48,7 +47,7 @@ func ExampleCopyN() {
r := strings.NewReader("some io.Reader stream to be read")
if _, err := io.CopyN(os.Stdout, r, 4); err != nil {
- log.Fatal(err)
+ panic(err)
}
// Output:
@@ -60,7 +59,7 @@ func ExampleReadAtLeast() {
buf := make([]byte, 14)
if _, err := io.ReadAtLeast(r, buf, 4); err != nil {
- log.Fatal(err)
+ panic(err)
}
fmt.Printf("%s\n", buf)
@@ -87,7 +86,7 @@ func ExampleReadFull() {
buf := make([]byte, 4)
if _, err := io.ReadFull(r, buf); err != nil {
- log.Fatal(err)
+ panic(err)
}
fmt.Printf("%s\n", buf)
@@ -104,7 +103,7 @@ func ExampleReadFull() {
func ExampleWriteString() {
if _, err := io.WriteString(os.Stdout, "Hello World"); err != nil {
- log.Fatal(err)
+ panic(err)
}
// Output: Hello World
@@ -115,7 +114,7 @@ func ExampleLimitReader() {
lr := io.LimitReader(r, 4)
if _, err := io.Copy(os.Stdout, lr); err != nil {
- log.Fatal(err)
+ panic(err)
}
// Output:
@@ -129,7 +128,7 @@ func ExampleMultiReader() {
r := io.MultiReader(r1, r2, r3)
if _, err := io.Copy(os.Stdout, r); err != nil {
- log.Fatal(err)
+ panic(err)
}
// Output:
@@ -153,7 +152,7 @@ func ExampleSectionReader() {
s := io.NewSectionReader(r, 5, 17)
if _, err := io.Copy(os.Stdout, s); err != nil {
- log.Fatal(err)
+ panic(err)
}
// Output:
@@ -166,7 +165,7 @@ func ExampleSectionReader_ReadAt() {
buf := make([]byte, 6)
if _, err := s.ReadAt(buf, 10); err != nil {
- log.Fatal(err)
+ panic(err)
}
fmt.Printf("%s\n", buf)
@@ -180,11 +179,11 @@ func ExampleSectionReader_Seek() {
s := io.NewSectionReader(r, 5, 17)
if _, err := s.Seek(10, io.SeekStart); err != nil {
- log.Fatal(err)
+ panic(err)
}
if _, err := io.Copy(os.Stdout, s); err != nil {
- log.Fatal(err)
+ panic(err)
}
// Output:
@@ -196,12 +195,12 @@ func ExampleSeeker_Seek() {
r.Seek(5, io.SeekStart) // move to the 5th char from the start
if _, err := io.Copy(os.Stdout, r); err != nil {
- log.Fatal(err)
+ panic(err)
}
r.Seek(-5, io.SeekEnd)
if _, err := io.Copy(os.Stdout, r); err != nil {
- log.Fatal(err)
+ panic(err)
}
// Output:
@@ -216,7 +215,7 @@ func ExampleMultiWriter() {
w := io.MultiWriter(&buf1, &buf2)
if _, err := io.Copy(w, r); err != nil {
- log.Fatal(err)
+ panic(err)
}
fmt.Print(buf1.String())
@@ -237,7 +236,7 @@ func ExamplePipe() {
}()
if _, err := io.Copy(os.Stdout, r); err != nil {
- log.Fatal(err)
+ panic(err)
}
// Output:
@@ -250,7 +249,7 @@ func ExampleReadAll() {
b, err := io.ReadAll(r)
if err != nil {
- log.Fatal(err)
+ panic(err)
}
fmt.Printf("%s", b)
diff --git a/gnovm/stdlibs/io/multi_test.gno b/gnovm/stdlibs/io/multi_test.gno
index 8932ace2e59..f39895ea776 100644
--- a/gnovm/stdlibs/io/multi_test.gno
+++ b/gnovm/stdlibs/io/multi_test.gno
@@ -6,7 +6,7 @@ package io
import (
"bytes"
- "crypto/sha1"
+ "crypto/sha256"
"fmt"
"strings"
"testing"
@@ -119,8 +119,8 @@ func testMultiWriter(t *testing.T, sink interface {
Stringer
},
) {
- sha1 := sha1.New()
- mw := MultiWriter(sha1, sink)
+ var buf bytes.Buffer
+ mw := MultiWriter(&buf, sink)
sourceString := "My input text."
source := strings.NewReader(sourceString)
@@ -134,9 +134,9 @@ func testMultiWriter(t *testing.T, sink interface {
t.Errorf("unexpected error: %v", err)
}
- sha1hex := fmt.Sprintf("%x", sha1.Sum(nil))
- if sha1hex != "01cb303fa8c30a64123067c5aa6284ba7ec2d31b" {
- t.Error("incorrect sha1 value")
+ sha1hex := fmt.Sprintf("%x", sha256.Sum256(buf.Bytes()))
+ if sha1hex != "d3e9e78d2a7e9c4756a4e8e57db6a57ccfd84c6d656d66b9d2bd2620b4ab81b8" {
+ t.Error("incorrect sha256 value")
}
if sink.String() != sourceString {
diff --git a/gnovm/stdlibs/math/overflow/overflow.gno b/gnovm/stdlibs/math/overflow/overflow.gno
index 9bdeff0720f..0bc2e03a522 100644
--- a/gnovm/stdlibs/math/overflow/overflow.gno
+++ b/gnovm/stdlibs/math/overflow/overflow.gno
@@ -223,7 +223,7 @@ func Div8p(a, b int8) int8 {
func Quo8(a, b int8) (int8, int8, bool) {
if b == 0 {
return 0, 0, false
- } else if b == -1 && a == math.MinInt8 {
+ } else if b == -1 && a == int8(math.MinInt8) {
return 0, 0, false
}
c := a / b
@@ -313,7 +313,7 @@ func Div16p(a, b int16) int16 {
func Quo16(a, b int16) (int16, int16, bool) {
if b == 0 {
return 0, 0, false
- } else if b == -1 && a == math.MinInt16 {
+ } else if b == -1 && a == int16(math.MinInt16) {
return 0, 0, false
}
c := a / b
@@ -403,7 +403,7 @@ func Div32p(a, b int32) int32 {
func Quo32(a, b int32) (int32, int32, bool) {
if b == 0 {
return 0, 0, false
- } else if b == -1 && a == math.MinInt32 {
+ } else if b == -1 && a == int32(math.MinInt32) {
return 0, 0, false
}
c := a / b
diff --git a/gnovm/stdlibs/std/banker.gno b/gnovm/stdlibs/std/banker.gno
index 5412b73281c..4c20e8d4b61 100644
--- a/gnovm/stdlibs/std/banker.gno
+++ b/gnovm/stdlibs/std/banker.gno
@@ -2,6 +2,7 @@ package std
import (
"strconv"
+ "strings"
)
// Realm functions can call std.GetBanker(options) to get
@@ -126,6 +127,7 @@ func (b banker) IssueCoin(addr Address, denom string, amount int64) {
if b.bt != BankerTypeRealmIssue {
panic(b.bt.String() + " cannot issue coins")
}
+ assertCoinDenom(denom)
bankerIssueCoin(uint8(b.bt), string(addr), denom, amount)
}
@@ -133,5 +135,35 @@ func (b banker) RemoveCoin(addr Address, denom string, amount int64) {
if b.bt != BankerTypeRealmIssue {
panic(b.bt.String() + " cannot remove coins")
}
+ assertCoinDenom(denom)
bankerRemoveCoin(uint8(b.bt), string(addr), denom, amount)
}
+
+func assertCoinDenom(denom string) {
+ prefix := "/" + CurrentRealm().PkgPath() + ":"
+ if !strings.HasPrefix(denom, prefix) {
+ panic("invalid denom, can only issue/remove coins with the realm's prefix: " + prefix)
+ }
+
+ baseDenom := denom[len(prefix):]
+ if !isValidBaseDenom(baseDenom) {
+ panic("cannot issue coins with invalid denom base name, it should start by a lowercase letter and be followed by 2-15 lowercase letters or digits")
+ }
+}
+
+// check start by a lowercase letter and be followed by 2-15 lowercase letters or digits
+func isValidBaseDenom(denom string) bool {
+ length := len(denom)
+ if length < 3 || length > 16 {
+ return false
+ }
+ for i, c := range denom {
+ switch {
+ case c >= 'a' && c <= 'z',
+ i > 0 && (c >= '0' && c <= '9'): // continue
+ default:
+ return false
+ }
+ }
+ return true
+}
diff --git a/gnovm/stdlibs/std/banker.go b/gnovm/stdlibs/std/banker.go
index 892af94777f..c57ba8529ed 100644
--- a/gnovm/stdlibs/std/banker.go
+++ b/gnovm/stdlibs/std/banker.go
@@ -2,7 +2,6 @@ package std
import (
"fmt"
- "regexp"
gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
"github.com/gnolang/gno/tm2/pkg/crypto"
@@ -33,9 +32,6 @@ const (
btRealmIssue
)
-// regexp for denom format
-var reDenom = regexp.MustCompile("[a-z][a-z0-9]{2,15}")
-
func X_bankerGetCoins(m *gno.Machine, bt uint8, addr string) (denoms []string, amounts []int64) {
coins := GetContext(m).Banker.GetCoins(crypto.Bech32Address(addr))
return ExpandCoins(coins)
@@ -74,31 +70,9 @@ func X_bankerTotalCoin(m *gno.Machine, bt uint8, denom string) int64 {
}
func X_bankerIssueCoin(m *gno.Machine, bt uint8, addr string, denom string, amount int64) {
- // gno checks for bt == RealmIssue
-
- // check origin denom format
- matched := reDenom.MatchString(denom)
- if !matched {
- m.Panic(typedString("invalid denom format to issue coin, must be " + reDenom.String()))
- return
- }
-
- // Similar to ibc spec
- // ibc_denom := 'ibc/' + hash('path' + 'base_denom')
- // gno_realm_denom := '/' + 'pkg_path' + ':' + 'base_denom'
- newDenom := "/" + m.Realm.Path + ":" + denom
- GetContext(m).Banker.IssueCoin(crypto.Bech32Address(addr), newDenom, amount)
+ GetContext(m).Banker.IssueCoin(crypto.Bech32Address(addr), denom, amount)
}
func X_bankerRemoveCoin(m *gno.Machine, bt uint8, addr string, denom string, amount int64) {
- // gno checks for bt == RealmIssue
-
- matched := reDenom.MatchString(denom)
- if !matched {
- m.Panic(typedString("invalid denom format to remove coin, must be " + reDenom.String()))
- return
- }
-
- newDenom := "/" + m.Realm.Path + ":" + denom
- GetContext(m).Banker.RemoveCoin(crypto.Bech32Address(addr), newDenom, amount)
+ GetContext(m).Banker.RemoveCoin(crypto.Bech32Address(addr), denom, amount)
}
diff --git a/gnovm/stdlibs/std/context.go b/gnovm/stdlibs/std/context.go
index a0dafe5dc44..a8ef500c346 100644
--- a/gnovm/stdlibs/std/context.go
+++ b/gnovm/stdlibs/std/context.go
@@ -9,10 +9,10 @@ import (
type ExecContext struct {
ChainID string
+ ChainDomain string
Height int64
Timestamp int64 // seconds
TimestampNano int64 // nanoseconds, only used for testing.
- Msg sdk.Msg
OrigCaller crypto.Bech32Address
OrigPkgAddr crypto.Bech32Address
OrigSend std.Coins
diff --git a/gnovm/stdlibs/std/frame.gno b/gnovm/stdlibs/std/frame.gno
index bc3a000f5a0..1709f8cb8b5 100644
--- a/gnovm/stdlibs/std/frame.gno
+++ b/gnovm/stdlibs/std/frame.gno
@@ -16,3 +16,15 @@ func (r Realm) PkgPath() string {
func (r Realm) IsUser() bool {
return r.pkgPath == ""
}
+
+func (r Realm) CoinDenom(coinName string) string {
+ return CoinDenom(r.pkgPath, coinName)
+}
+
+func CoinDenom(pkgPath, coinName string) string {
+ // TODO: Possibly remove after https://github.com/gnolang/gno/issues/3164
+ // Similar to ibc spec
+ // ibc_denom := 'ibc/' + hash('path' + 'base_denom')
+ // gno_qualified_denom := '/' + 'pkg_path' + ':' + 'base_denom'
+ return "/" + pkgPath + ":" + coinName
+}
diff --git a/gnovm/stdlibs/std/native.gno b/gnovm/stdlibs/std/native.gno
index 5421e231de2..0dcde1148e1 100644
--- a/gnovm/stdlibs/std/native.gno
+++ b/gnovm/stdlibs/std/native.gno
@@ -10,8 +10,9 @@ func AssertOriginCall() // injected
// MsgRun.
func IsOriginCall() bool // injected
-func GetChainID() string // injected
-func GetHeight() int64 // injected
+func GetChainID() string // injected
+func GetChainDomain() string // injected
+func GetHeight() int64 // injected
func GetOrigSend() Coins {
den, amt := origSend()
diff --git a/gnovm/stdlibs/std/native.go b/gnovm/stdlibs/std/native.go
index 3fe5fbb9889..fb181d9be31 100644
--- a/gnovm/stdlibs/std/native.go
+++ b/gnovm/stdlibs/std/native.go
@@ -27,6 +27,10 @@ func GetChainID(m *gno.Machine) string {
return GetContext(m).ChainID
}
+func GetChainDomain(m *gno.Machine) string {
+ return GetContext(m).ChainDomain
+}
+
func GetHeight(m *gno.Machine) int64 {
return GetContext(m).Height
}
diff --git a/gnovm/stdlibs/stdlibs.go b/gnovm/stdlibs/stdlibs.go
index c9b16815ab5..3b8b88c1fde 100644
--- a/gnovm/stdlibs/stdlibs.go
+++ b/gnovm/stdlibs/stdlibs.go
@@ -24,10 +24,10 @@ func FindNative(pkgPath string, name gno.Name) *NativeFunc {
return nil
}
-// NativeStore is used by the GnoVM to determine if the given function,
+// NativeResolver is used by the GnoVM to determine if the given function,
// specified by its pkgPath and name, has a native implementation; and if so
// retrieve it.
-func NativeStore(pkgPath string, name gno.Name) func(*gno.Machine) {
+func NativeResolver(pkgPath string, name gno.Name) func(*gno.Machine) {
nt := FindNative(pkgPath, name)
if nt == nil {
return nil
diff --git a/gnovm/stdlibs/strconv/example_test.gno b/gnovm/stdlibs/strconv/example_test.gno
index 428fde4e660..d3ef2cc4244 100644
--- a/gnovm/stdlibs/strconv/example_test.gno
+++ b/gnovm/stdlibs/strconv/example_test.gno
@@ -6,7 +6,6 @@ package strconv_test
import (
"fmt"
- "log"
"strconv"
)
@@ -409,7 +408,7 @@ func ExampleUnquote() {
func ExampleUnquoteChar() {
v, mb, t, err := strconv.UnquoteChar(`\"Fran & Freddie's Diner\"`, '"')
if err != nil {
- log.Fatal(err)
+ panic(err)
}
fmt.Println("value:", string(v))
diff --git a/gnovm/stdlibs/testing/match.gno b/gnovm/stdlibs/testing/match.gno
index 3b22602890d..8b099f37624 100644
--- a/gnovm/stdlibs/testing/match.gno
+++ b/gnovm/stdlibs/testing/match.gno
@@ -16,11 +16,11 @@ import (
type filterMatch interface {
// matches checks the name against the receiver's pattern strings using the
// given match function.
- matches(name []string, matchString func(pat, str string) (bool, error)) (ok, partial bool)
+ matches(name []string) (ok, partial bool)
// verify checks that the receiver's pattern strings are valid filters by
// calling the given match function.
- verify(name string, matchString func(pat, str string) (bool, error)) error
+ verify(name string) error
}
// simpleMatch matches a test name if all of the pattern strings match in
@@ -30,43 +30,43 @@ type simpleMatch []string
// alternationMatch matches a test name if one of the alternations match.
type alternationMatch []filterMatch
-func (m simpleMatch) matches(name []string, matchString func(pat, str string) (bool, error)) (ok, partial bool) {
+func (m simpleMatch) matches(name []string) (ok, partial bool) {
for i, s := range name {
if i >= len(m) {
break
}
- if ok, _ := matchString(m[i], s); !ok {
+ if ok, _ := regexp.MatchString(m[i], s); !ok {
return false, false
}
}
return true, len(name) < len(m)
}
-func (m simpleMatch) verify(name string, matchString func(pat, str string) (bool, error)) error {
+func (m simpleMatch) verify(name string) error {
for i, s := range m {
m[i] = rewrite(s)
}
// Verify filters before doing any processing.
for i, s := range m {
- if _, err := matchString(s, "non-empty"); err != nil {
+ if _, err := regexp.MatchString(s, "non-empty"); err != nil {
return fmt.Errorf("element %d of %s (%q): %s", i, name, s, err)
}
}
return nil
}
-func (m alternationMatch) matches(name []string, matchString func(pat, str string) (bool, error)) (ok, partial bool) {
+func (m alternationMatch) matches(name []string) (ok, partial bool) {
for _, m := range m {
- if ok, partial = m.matches(name, matchString); ok {
+ if ok, partial = m.matches(name); ok {
return ok, partial
}
}
return false, false
}
-func (m alternationMatch) verify(name string, matchString func(pat, str string) (bool, error)) error {
+func (m alternationMatch) verify(name string) error {
for i, m := range m {
- if err := m.verify(name, matchString); err != nil {
+ if err := m.verify(name); err != nil {
return fmt.Errorf("alternation %d of %s", i, err)
}
}
@@ -164,20 +164,3 @@ func isSpace(r rune) bool {
}
return false
}
-
-var (
- matchPat string
- matchRe *regexp.Regexp
-)
-
-// based on testing/internal/testdeps.TestDeps.MatchString.
-func matchString(pat, str string) (result bool, err error) {
- if matchRe == nil || matchPat != pat {
- matchPat = pat
- matchRe, err = regexp.Compile(matchPat)
- if err != nil {
- return
- }
- }
- return matchRe.MatchString(str), nil
-}
diff --git a/gnovm/stdlibs/testing/testing.gno b/gnovm/stdlibs/testing/testing.gno
index 6e55c5cc283..fdafd9652ba 100644
--- a/gnovm/stdlibs/testing/testing.gno
+++ b/gnovm/stdlibs/testing/testing.gno
@@ -280,7 +280,7 @@ func (t *T) shouldRun(name string) bool {
}
elem := strings.Split(name, "/")
- ok, partial := t.runFilter.matches(elem, matchString)
+ ok, partial := t.runFilter.matches(elem)
_ = partial // we don't care right now
return ok
}
diff --git a/gnovm/tests/README.md b/gnovm/tests/README.md
index 378d5d9dc1b..d35c6590e2f 100644
--- a/gnovm/tests/README.md
+++ b/gnovm/tests/README.md
@@ -1 +1,35 @@
-All the files in the ./files directory are meant to be those derived/borrowed from Yaegi, Apache2.0.
+# tests
+
+This directory contains integration tests for the GnoVM. This file aims to provide a brief overview.
+
+GnoVM tests and filetests run in a special context relating to its imports.
+You can see the additional Gonative functions in [gnovm/pkg/test/imports.go](../pkg/test/imports.go).
+You can see additional standard libraries and standard library functions
+available in testing in [gnovm/tests/stdlibs](./stdlibs).
+
+## `files`: GnoVM filetests
+
+The most important directory is `files`, which contains filetests for the Gno
+project. These are executed by the `TestFiles` test in the `gnovm/pkg/gnolang`
+directory.
+
+The `files/extern` directory contains several packages used to test the import
+system. The packages here are imported with the prefix
+`github.com/gnolang/gno/_test/`, exclusively within these filetests.
+
+Tests with the `_long` suffix are skipped when the `-short` flag is passed.
+
+These tests are largely derived from Yaegi, licensed under Apache 2.0.
+
+## `stdlibs`: testing standard libraries
+
+These contain standard libraries which are only available in testing, and
+extensions of them, like `std.TestSkipHeights`.
+
+## other directories
+
+- `backup` has been here since forever; and somebody should come around and delete it at some point.
+- `challenges` contains code that supposedly doesn't work, but should.
+- `integ` contains some files for integration tests which likely should have
+ been in some `testdata` directory to begin with. You guessed it,
+ they're here until someone bothers to move them out.
diff --git a/gnovm/tests/file.go b/gnovm/tests/file.go
deleted file mode 100644
index 98dbab6ac0e..00000000000
--- a/gnovm/tests/file.go
+++ /dev/null
@@ -1,713 +0,0 @@
-package tests
-
-import (
- "bytes"
- "encoding/json"
- "fmt"
- "go/ast"
- "go/parser"
- "go/token"
- "io"
- "os"
- "regexp"
- rtdb "runtime/debug"
- "strconv"
- "strings"
-
- "github.com/gnolang/gno/gno.land/pkg/gnoland/ugnot"
- "github.com/gnolang/gno/gnovm"
- gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
- "github.com/gnolang/gno/gnovm/stdlibs"
- teststd "github.com/gnolang/gno/gnovm/tests/stdlibs/std"
- "github.com/gnolang/gno/tm2/pkg/crypto"
- osm "github.com/gnolang/gno/tm2/pkg/os"
- "github.com/gnolang/gno/tm2/pkg/sdk"
- "github.com/gnolang/gno/tm2/pkg/std"
- "github.com/pmezard/go-difflib/difflib"
-)
-
-type loggerFunc func(args ...interface{})
-
-func TestMachine(store gno.Store, stdout io.Writer, pkgPath string) *gno.Machine {
- // default values
- var (
- send std.Coins
- maxAlloc int64
- )
-
- return testMachineCustom(store, pkgPath, stdout, maxAlloc, send)
-}
-
-func testMachineCustom(store gno.Store, pkgPath string, stdout io.Writer, maxAlloc int64, send std.Coins) *gno.Machine {
- ctx := TestContext(pkgPath, send)
- m := gno.NewMachineWithOptions(gno.MachineOptions{
- PkgPath: "", // set later.
- Output: stdout,
- Store: store,
- Context: ctx,
- MaxAllocBytes: maxAlloc,
- })
- return m
-}
-
-// TestContext returns a TestExecContext. Usable for test purpose only.
-func TestContext(pkgPath string, send std.Coins) *teststd.TestExecContext {
- // FIXME: create a better package to manage this, with custom constructors
- pkgAddr := gno.DerivePkgAddr(pkgPath) // the addr of the pkgPath called.
- caller := gno.DerivePkgAddr("user1.gno")
-
- pkgCoins := std.MustParseCoins(ugnot.ValueString(200_000_000)).Add(send) // >= send.
- banker := newTestBanker(pkgAddr.Bech32(), pkgCoins)
- params := newTestParams()
- ctx := stdlibs.ExecContext{
- ChainID: "dev",
- Height: 123,
- Timestamp: 1234567890,
- Msg: nil,
- OrigCaller: caller.Bech32(),
- OrigPkgAddr: pkgAddr.Bech32(),
- OrigSend: send,
- OrigSendSpent: new(std.Coins),
- Banker: banker,
- Params: params,
- EventLogger: sdk.NewEventLogger(),
- }
- return &teststd.TestExecContext{
- ExecContext: ctx,
- RealmFrames: make(map[*gno.Frame]teststd.RealmOverride),
- }
-}
-
-// CleanupMachine can be called during two tests while reusing the same Machine instance.
-func CleanupMachine(m *gno.Machine) {
- prevCtx := m.Context.(*teststd.TestExecContext)
- prevSend := prevCtx.OrigSend
-
- newCtx := TestContext("", prevCtx.OrigSend)
- pkgCoins := std.MustParseCoins(ugnot.ValueString(200_000_000)).Add(prevSend) // >= send.
- banker := newTestBanker(prevCtx.OrigPkgAddr, pkgCoins)
- newCtx.OrigPkgAddr = prevCtx.OrigPkgAddr
- newCtx.Banker = banker
- m.Context = newCtx
-}
-
-type runFileTestOptions struct {
- nativeLibs bool
- logger loggerFunc
- syncWanted bool
-}
-
-// RunFileTestOptions specify changing options in [RunFileTest], deviating
-// from the zero value.
-type RunFileTestOption func(*runFileTestOptions)
-
-// WithNativeLibs enables using go native libraries (ie, [ImportModeNativePreferred])
-// instead of using stdlibs/*.
-func WithNativeLibs() RunFileTestOption {
- return func(r *runFileTestOptions) { r.nativeLibs = true }
-}
-
-// WithLoggerFunc sets a logging function for [RunFileTest].
-func WithLoggerFunc(f func(args ...interface{})) RunFileTestOption {
- return func(r *runFileTestOptions) { r.logger = f }
-}
-
-// WithSyncWanted sets the syncWanted flag to true.
-// It rewrites tests files so that the values of Output: and of Realm:
-// comments match the actual output or realm state after the test.
-func WithSyncWanted(v bool) RunFileTestOption {
- return func(r *runFileTestOptions) { r.syncWanted = v }
-}
-
-// RunFileTest executes the filetest at the given path, using rootDir as
-// the directory where to find the "stdlibs" directory.
-func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error {
- var f runFileTestOptions
- for _, opt := range opts {
- opt(&f)
- }
-
- directives, pkgPath, resWanted, errWanted, rops, eventsWanted, stacktraceWanted, maxAlloc, send, preWanted := wantedFromComment(path)
- if pkgPath == "" {
- pkgPath = "main"
- }
- pkgName := DefaultPkgName(pkgPath)
- stdin := new(bytes.Buffer)
- stdout := new(bytes.Buffer)
- stderr := new(bytes.Buffer)
- mode := ImportModeStdlibsPreferred
- if f.nativeLibs {
- mode = ImportModeNativePreferred
- }
- store := TestStore(rootDir, "./files", stdin, stdout, stderr, mode)
- store.SetLogStoreOps(true)
- m := testMachineCustom(store, pkgPath, stdout, maxAlloc, send)
- checkMachineIsEmpty := true
-
- // TODO support stdlib groups, but make testing safe;
- // e.g. not be able to make network connections.
- // interp.New(interp.Options{GoPath: goPath, Stdout: &stdout, Stderr: &stderr})
- // m.Use(interp.Symbols)
- // m.Use(stdlib.Symbols)
- // m.Use(unsafe.Symbols)
- bz, err := os.ReadFile(path)
- if err != nil {
- return err
- }
- { // Validate result, errors, etc.
- var pnc interface{}
- func() {
- defer func() {
- if r := recover(); r != nil {
- // print output.
- fmt.Printf("OUTPUT:\n%s\n", stdout.String())
- pnc = r
- err := strings.TrimSpace(fmt.Sprintf("%v", pnc))
- // print stack if unexpected error.
- if errWanted == "" ||
- !strings.Contains(err, errWanted) {
- fmt.Printf("ERROR:\n%s\n", err)
- // error didn't match: print stack
- // NOTE: will fail testcase later.
- rtdb.PrintStack()
- }
- }
- }()
- if f.logger != nil {
- f.logger("========================================")
- f.logger("RUN FILES & INIT")
- f.logger("========================================")
- }
- if !gno.IsRealmPath(pkgPath) {
- // simple case.
- pn := gno.NewPackageNode(pkgName, pkgPath, &gno.FileSet{})
- pv := pn.NewPackage()
- store.SetBlockNode(pn)
- store.SetCachePackage(pv)
- m.SetActivePackage(pv)
- n := gno.MustParseFile(path, string(bz)) // "main.gno", string(bz))
- m.RunFiles(n)
- if f.logger != nil {
- f.logger("========================================")
- f.logger("RUN MAIN")
- f.logger("========================================")
- }
- m.RunMain()
- if f.logger != nil {
- f.logger("========================================")
- f.logger("RUN MAIN END")
- f.logger("========================================")
- }
- } else {
- // realm case.
- store.SetStrictGo2GnoMapping(true) // in gno.land, natives must be registered.
- gno.DisableDebug() // until main call.
- // save package using realm crawl procedure.
- memPkg := &gnovm.MemPackage{
- Name: string(pkgName),
- Path: pkgPath,
- Files: []*gnovm.MemFile{
- {
- Name: "main.gno", // dontcare
- Body: string(bz),
- },
- },
- }
- // run decls and init functions.
- m.RunMemPackage(memPkg, true)
- // reconstruct machine and clear store cache.
- // whether package is realm or not, since non-realm
- // may call realm packages too.
- if f.logger != nil {
- f.logger("========================================")
- f.logger("CLEAR STORE CACHE")
- f.logger("========================================")
- }
- store.ClearCache()
- /*
- m = gno.NewMachineWithOptions(gno.MachineOptions{
- PkgPath: "",
- Output: stdout,
- Store: store,
- Context: ctx,
- MaxAllocBytes: maxAlloc,
- })
- */
- if f.logger != nil {
- store.Print()
- f.logger("========================================")
- f.logger("PREPROCESS ALL FILES")
- f.logger("========================================")
- }
- m.PreprocessAllFilesAndSaveBlockNodes()
- if f.logger != nil {
- f.logger("========================================")
- f.logger("RUN MAIN")
- f.logger("========================================")
- store.Print()
- }
- pv2 := store.GetPackage(pkgPath, false)
- m.SetActivePackage(pv2)
- gno.EnableDebug()
- if rops != "" {
- // clear store.opslog from init function(s),
- // and PreprocessAllFilesAndSaveBlockNodes().
- store.SetLogStoreOps(true) // resets.
- }
- m.RunMain()
- if f.logger != nil {
- f.logger("========================================")
- f.logger("RUN MAIN END")
- f.logger("========================================")
- }
- }
- }()
-
- for _, directive := range directives {
- switch directive {
- case "Error":
- // errWanted given
- if errWanted != "" {
- if pnc == nil {
- panic(fmt.Sprintf("fail on %s: got nil error, want: %q", path, errWanted))
- }
-
- errstr := ""
- switch v := pnc.(type) {
- case *gno.TypedValue:
- errstr = v.Sprint(m)
- case *gno.PreprocessError:
- errstr = v.Unwrap().Error()
- case gno.UnhandledPanicError:
- errstr = v.Error()
- default:
- errstr = strings.TrimSpace(fmt.Sprintf("%v", pnc))
- }
-
- parts := strings.SplitN(errstr, ":\n--- preprocess stack ---", 2)
- if len(parts) == 2 {
- fmt.Println(parts[0])
- errstr = parts[0]
- }
- if errstr != errWanted {
- if f.syncWanted {
- // write error to file
- replaceWantedInPlace(path, "Error", errstr)
- } else {
- panic(fmt.Sprintf("fail on %s: got %q, want: %q", path, errstr, errWanted))
- }
- }
-
- // NOTE: ignores any gno.GetDebugErrors().
- gno.ClearDebugErrors()
- checkMachineIsEmpty = false // nothing more to do.
- } else {
- // record errors when errWanted is empty and pnc not nil
- if pnc != nil {
- errstr := ""
- if tv, ok := pnc.(*gno.TypedValue); ok {
- errstr = tv.Sprint(m)
- } else {
- errstr = strings.TrimSpace(fmt.Sprintf("%v", pnc))
- }
- parts := strings.SplitN(errstr, ":\n--- preprocess stack ---", 2)
- if len(parts) == 2 {
- fmt.Println(parts[0])
- errstr = parts[0]
- }
- // check tip line, write to file
- ctl := errstr +
- "\n*** CHECK THE ERR MESSAGES ABOVE, MAKE SURE IT'S WHAT YOU EXPECTED, " +
- "DELETE THIS LINE AND RUN TEST AGAIN ***"
- // write error to file
- replaceWantedInPlace(path, "Error", ctl)
- panic(fmt.Sprintf("fail on %s: err recorded, check the message and run test again", path))
- }
- // check gno debug errors when errWanted is empty, pnc is nil
- if gno.HasDebugErrors() {
- panic(fmt.Sprintf("fail on %s: got unexpected debug error(s): %v", path, gno.GetDebugErrors()))
- }
- // pnc is nil, errWanted empty, no gno debug errors
- checkMachineIsEmpty = false
- }
- case "Output":
- // panic if got unexpected error
- if pnc != nil {
- if tv, ok := pnc.(*gno.TypedValue); ok {
- panic(fmt.Sprintf("fail on %s: got unexpected error: %s", path, tv.Sprint(m)))
- } else { // happens on 'unknown import path ...'
- panic(fmt.Sprintf("fail on %s: got unexpected error: %v", path, pnc))
- }
- }
- // check result
- res := strings.TrimSpace(stdout.String())
- res = trimTrailingSpaces(res)
- if res != resWanted {
- if f.syncWanted {
- // write output to file.
- replaceWantedInPlace(path, "Output", res)
- } else {
- // panic so tests immediately fail (for now).
- if resWanted == "" {
- panic(fmt.Sprintf("fail on %s: got unexpected output: %s", path, res))
- } else {
- diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
- A: difflib.SplitLines(resWanted),
- B: difflib.SplitLines(res),
- FromFile: "Expected",
- FromDate: "",
- ToFile: "Actual",
- ToDate: "",
- Context: 1,
- })
- panic(fmt.Sprintf("fail on %s: diff:\n%s\n", path, diff))
- }
- }
- }
- case "Events":
- // panic if got unexpected error
-
- if pnc != nil {
- if tv, ok := pnc.(*gno.TypedValue); ok {
- panic(fmt.Sprintf("fail on %s: got unexpected error: %s", path, tv.Sprint(m)))
- } else { // happens on 'unknown import path ...'
- panic(fmt.Sprintf("fail on %s: got unexpected error: %v", path, pnc))
- }
- }
- // check result
- events := m.Context.(*teststd.TestExecContext).EventLogger.Events()
- evtjson, err := json.MarshalIndent(events, "", " ")
- if err != nil {
- panic(err)
- }
- evtstr := trimTrailingSpaces(string(evtjson))
- if evtstr != eventsWanted {
- if f.syncWanted {
- // write output to file.
- replaceWantedInPlace(path, "Events", evtstr)
- } else {
- // panic so tests immediately fail (for now).
- if eventsWanted == "" {
- panic(fmt.Sprintf("fail on %s: got unexpected events: %s", path, evtstr))
- } else {
- diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
- A: difflib.SplitLines(eventsWanted),
- B: difflib.SplitLines(evtstr),
- FromFile: "Expected",
- FromDate: "",
- ToFile: "Actual",
- ToDate: "",
- Context: 1,
- })
- panic(fmt.Sprintf("fail on %s: diff:\n%s\n", path, diff))
- }
- }
- }
- case "Realm":
- // panic if got unexpected error
- if pnc != nil {
- if tv, ok := pnc.(*gno.TypedValue); ok {
- panic(fmt.Sprintf("fail on %s: got unexpected error: %s", path, tv.Sprint(m)))
- } else { // TODO: does this happen?
- panic(fmt.Sprintf("fail on %s: got unexpected error: %v", path, pnc))
- }
- }
- // check realm ops
- if rops != "" {
- rops2 := strings.TrimSpace(store.SprintStoreOps())
- if rops != rops2 {
- if f.syncWanted {
- // write output to file.
- replaceWantedInPlace(path, "Realm", rops2)
- } else {
- diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
- A: difflib.SplitLines(rops),
- B: difflib.SplitLines(rops2),
- FromFile: "Expected",
- FromDate: "",
- ToFile: "Actual",
- ToDate: "",
- Context: 1,
- })
- panic(fmt.Sprintf("fail on %s: diff:\n%s\n", path, diff))
- }
- }
- }
- case "Preprocessed":
- // check preprocessed AST.
- pn := store.GetBlockNode(gno.PackageNodeLocation(pkgPath))
- pre := pn.(*gno.PackageNode).FileSet.Files[0].String()
- if pre != preWanted {
- if f.syncWanted {
- // write error to file
- replaceWantedInPlace(path, "Preprocessed", pre)
- } else {
- // panic so tests immediately fail (for now).
- diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
- A: difflib.SplitLines(preWanted),
- B: difflib.SplitLines(pre),
- FromFile: "Expected",
- FromDate: "",
- ToFile: "Actual",
- ToDate: "",
- Context: 1,
- })
- panic(fmt.Sprintf("fail on %s: diff:\n%s\n", path, diff))
- }
- }
- case "Stacktrace":
- if stacktraceWanted != "" {
- var stacktrace string
-
- switch pnc.(type) {
- case gno.UnhandledPanicError:
- stacktrace = m.ExceptionsStacktrace()
- default:
- stacktrace = m.Stacktrace().String()
- }
-
- if f.syncWanted {
- // write stacktrace to file
- replaceWantedInPlace(path, "Stacktrace", stacktrace)
- } else {
- if !strings.Contains(stacktrace, stacktraceWanted) {
- diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
- A: difflib.SplitLines(stacktraceWanted),
- B: difflib.SplitLines(stacktrace),
- FromFile: "Expected",
- FromDate: "",
- ToFile: "Actual",
- ToDate: "",
- Context: 1,
- })
- panic(fmt.Sprintf("fail on %s: diff:\n%s\n", path, diff))
- }
- }
- }
- checkMachineIsEmpty = false
- default:
- return nil
- }
- }
- }
-
- if checkMachineIsEmpty {
- // Check that machine is empty.
- err = m.CheckEmpty()
- if err != nil {
- if f.logger != nil {
- f.logger("last state: \n", m.String())
- }
- panic(fmt.Sprintf("fail on %s: machine not empty after main: %v", path, err))
- }
- }
- return nil
-}
-
-func wantedFromComment(p string) (directives []string, pkgPath, res, err, rops, events, stacktrace string, maxAlloc int64, send std.Coins, pre string) {
- fset := token.NewFileSet()
- f, err2 := parser.ParseFile(fset, p, nil, parser.ParseComments)
- if err2 != nil {
- panic(err2)
- }
- if len(f.Comments) == 0 {
- return
- }
- for _, comments := range f.Comments {
- text := readComments(comments)
- if strings.HasPrefix(text, "PKGPATH:") {
- line := strings.SplitN(text, "\n", 2)[0]
- pkgPath = strings.TrimSpace(strings.TrimPrefix(line, "PKGPATH:"))
- } else if strings.HasPrefix(text, "MAXALLOC:") {
- line := strings.SplitN(text, "\n", 2)[0]
- maxstr := strings.TrimSpace(strings.TrimPrefix(line, "MAXALLOC:"))
- maxint, err := strconv.Atoi(maxstr)
- if err != nil {
- panic(fmt.Sprintf("invalid maxalloc amount: %v", maxstr))
- }
- maxAlloc = int64(maxint)
- } else if strings.HasPrefix(text, "SEND:") {
- line := strings.SplitN(text, "\n", 2)[0]
- sendstr := strings.TrimSpace(strings.TrimPrefix(line, "SEND:"))
- send = std.MustParseCoins(sendstr)
- } else if strings.HasPrefix(text, "Output:\n") {
- res = strings.TrimPrefix(text, "Output:\n")
- res = strings.TrimSpace(res)
- directives = append(directives, "Output")
- } else if strings.HasPrefix(text, "Error:\n") {
- err = strings.TrimPrefix(text, "Error:\n")
- err = strings.TrimSpace(err)
- // XXX temporary until we support line:column.
- // If error starts with line:column, trim it.
- re := regexp.MustCompile(`^[0-9]+:[0-9]+: `)
- err = re.ReplaceAllString(err, "")
- directives = append(directives, "Error")
- } else if strings.HasPrefix(text, "Realm:\n") {
- rops = strings.TrimPrefix(text, "Realm:\n")
- rops = strings.TrimSpace(rops)
- directives = append(directives, "Realm")
- } else if strings.HasPrefix(text, "Events:\n") {
- events = strings.TrimPrefix(text, "Events:\n")
- events = strings.TrimSpace(events)
- directives = append(directives, "Events")
- } else if strings.HasPrefix(text, "Preprocessed:\n") {
- pre = strings.TrimPrefix(text, "Preprocessed:\n")
- pre = strings.TrimSpace(pre)
- directives = append(directives, "Preprocessed")
- } else if strings.HasPrefix(text, "Stacktrace:\n") {
- stacktrace = strings.TrimPrefix(text, "Stacktrace:\n")
- stacktrace = strings.TrimSpace(stacktrace)
- directives = append(directives, "Stacktrace")
- } else {
- // ignore unexpected.
- }
- }
- return
-}
-
-// readComments returns //-style comments from cg, but without truncating empty
-// lines like cg.Text().
-func readComments(cg *ast.CommentGroup) string {
- var b strings.Builder
- for _, c := range cg.List {
- if len(c.Text) < 2 || c.Text[:2] != "//" {
- // ignore no //-style comment
- break
- }
- s := strings.TrimPrefix(c.Text[2:], " ")
- b.WriteString(s + "\n")
- }
- return b.String()
-}
-
-// Replace comment in file with given output given directive.
-func replaceWantedInPlace(path string, directive string, output string) {
- bz := osm.MustReadFile(path)
- body := string(bz)
- lines := strings.Split(body, "\n")
- isReplacing := false
- wroteDirective := false
- newlines := []string(nil)
- for _, line := range lines {
- if line == "// "+directive+":" {
- if wroteDirective {
- isReplacing = true
- continue
- } else {
- wroteDirective = true
- isReplacing = true
- newlines = append(newlines, "// "+directive+":")
- outlines := strings.Split(output, "\n")
- for _, outline := range outlines {
- newlines = append(newlines,
- strings.TrimRight("// "+outline, " "))
- }
- continue
- }
- } else if isReplacing {
- if strings.HasPrefix(line, "//") {
- continue
- } else {
- isReplacing = false
- }
- }
- newlines = append(newlines, line)
- }
- osm.MustWriteFile(path, []byte(strings.Join(newlines, "\n")), 0o644)
-}
-
-func DefaultPkgName(gopkgPath string) gno.Name {
- parts := strings.Split(gopkgPath, "/")
- last := parts[len(parts)-1]
- parts = strings.Split(last, "-")
- name := parts[len(parts)-1]
- name = strings.ToLower(name)
- return gno.Name(name)
-}
-
-// go comments strip trailing spaces.
-func trimTrailingSpaces(result string) string {
- lines := strings.Split(result, "\n")
- for i, line := range lines {
- lines[i] = strings.TrimRight(line, " \t")
- }
- return strings.Join(lines, "\n")
-}
-
-// ----------------------------------------
-// testParams
-type testParams struct{}
-
-func newTestParams() *testParams {
- return &testParams{}
-}
-
-func (tp *testParams) SetBool(key string, val bool) { /* noop */ }
-func (tp *testParams) SetBytes(key string, val []byte) { /* noop */ }
-func (tp *testParams) SetInt64(key string, val int64) { /* noop */ }
-func (tp *testParams) SetUint64(key string, val uint64) { /* noop */ }
-func (tp *testParams) SetString(key string, val string) { /* noop */ }
-
-// ----------------------------------------
-// testBanker
-
-type testBanker struct {
- coinTable map[crypto.Bech32Address]std.Coins
-}
-
-func newTestBanker(args ...interface{}) *testBanker {
- coinTable := make(map[crypto.Bech32Address]std.Coins)
- if len(args)%2 != 0 {
- panic("newTestBanker requires even number of arguments; addr followed by coins")
- }
- for i := 0; i < len(args); i += 2 {
- addr := args[i].(crypto.Bech32Address)
- amount := args[i+1].(std.Coins)
- coinTable[addr] = amount
- }
- return &testBanker{
- coinTable: coinTable,
- }
-}
-
-func (tb *testBanker) GetCoins(addr crypto.Bech32Address) (dst std.Coins) {
- return tb.coinTable[addr]
-}
-
-func (tb *testBanker) SendCoins(from, to crypto.Bech32Address, amt std.Coins) {
- fcoins, fexists := tb.coinTable[from]
- if !fexists {
- panic(fmt.Sprintf(
- "source address %s does not exist",
- from.String()))
- }
- if !fcoins.IsAllGTE(amt) {
- panic(fmt.Sprintf(
- "source address %s has %s; cannot send %s",
- from.String(), fcoins, amt))
- }
- // First, subtract from 'from'.
- frest := fcoins.Sub(amt)
- tb.coinTable[from] = frest
- // Second, add to 'to'.
- // NOTE: even works when from==to, due to 2-step isolation.
- tcoins, _ := tb.coinTable[to]
- tsum := tcoins.Add(amt)
- tb.coinTable[to] = tsum
-}
-
-func (tb *testBanker) TotalCoin(denom string) int64 {
- panic("not yet implemented")
-}
-
-func (tb *testBanker) IssueCoin(addr crypto.Bech32Address, denom string, amt int64) {
- coins, _ := tb.coinTable[addr]
- sum := coins.Add(std.Coins{{Denom: denom, Amount: amt}})
- tb.coinTable[addr] = sum
-}
-
-func (tb *testBanker) RemoveCoin(addr crypto.Bech32Address, denom string, amt int64) {
- coins, _ := tb.coinTable[addr]
- rest := coins.Sub(std.Coins{{Denom: denom, Amount: amt}})
- tb.coinTable[addr] = rest
-}
diff --git a/gnovm/tests/file_test.go b/gnovm/tests/file_test.go
deleted file mode 100644
index 4313fd88645..00000000000
--- a/gnovm/tests/file_test.go
+++ /dev/null
@@ -1,144 +0,0 @@
-package tests
-
-import (
- "flag"
- "io/fs"
- "os"
- "path"
- "path/filepath"
- "strings"
- "testing"
-
- gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
-)
-
-var withSync = flag.Bool("update-golden-tests", false, "rewrite tests updating Realm: and Output: with new values where changed")
-
-func TestFileStr(t *testing.T) {
- filePath := filepath.Join(".", "files", "str.gno")
- runFileTest(t, filePath, WithNativeLibs())
-}
-
-// Run tests in the `files` directory using shims from stdlib
-// to native go standard library.
-func TestFilesNative(t *testing.T) {
- baseDir := filepath.Join(".", "files")
- runFileTests(t, baseDir, []string{"*_stdlibs*"}, WithNativeLibs())
-}
-
-// Test files using standard library in stdlibs/.
-func TestFiles(t *testing.T) {
- baseDir := filepath.Join(".", "files")
- runFileTests(t, baseDir, []string{"*_native*"})
-}
-
-func TestChallenges(t *testing.T) {
- t.Skip("Challenge tests, skipping.")
- baseDir := filepath.Join(".", "challenges")
- runFileTests(t, baseDir, nil)
-}
-
-type testFile struct {
- path string
- fs.DirEntry
-}
-
-// ignore are glob patterns to ignore
-func runFileTests(t *testing.T, baseDir string, ignore []string, opts ...RunFileTestOption) {
- t.Helper()
-
- opts = append([]RunFileTestOption{WithSyncWanted(*withSync)}, opts...)
-
- files, err := readFiles(t, baseDir)
- if err != nil {
- t.Fatal(err)
- }
-
- files = filterFileTests(t, files, ignore)
- var path string
- var name string
- for _, file := range files {
- path = file.path
- name = strings.TrimPrefix(file.path, baseDir+string(os.PathSeparator))
- t.Run(name, func(t *testing.T) {
- runFileTest(t, path, opts...)
- })
- }
-}
-
-// it reads all files recursively in the directory
-func readFiles(t *testing.T, dir string) ([]testFile, error) {
- t.Helper()
- var files []testFile
-
- err := filepath.WalkDir(dir, func(path string, de fs.DirEntry, err error) error {
- if err != nil {
- return err
- }
- if de.IsDir() && de.Name() == "extern" {
- return filepath.SkipDir
- }
- f := testFile{path: path, DirEntry: de}
-
- files = append(files, f)
- return nil
- })
- return files, err
-}
-
-func filterFileTests(t *testing.T, files []testFile, ignore []string) []testFile {
- t.Helper()
- filtered := make([]testFile, 0, 1000)
- var name string
-
- for _, f := range files {
- // skip none .gno files
- name = f.DirEntry.Name()
- if filepath.Ext(name) != ".gno" {
- continue
- }
- // skip ignored files
- if isIgnored(t, name, ignore) {
- continue
- }
- // skip _long file if we only want to test regular file.
- if testing.Short() && strings.Contains(name, "_long") {
- t.Logf("skipping test %s in short mode.", name)
- continue
- }
- filtered = append(filtered, f)
- }
- return filtered
-}
-
-func isIgnored(t *testing.T, name string, ignore []string) bool {
- t.Helper()
- isIgnore := false
- for _, is := range ignore {
- match, err := path.Match(is, name)
- if err != nil {
- t.Fatalf("error parsing glob pattern %q: %v", is, err)
- }
- if match {
- isIgnore = true
- break
- }
- }
- return isIgnore
-}
-
-func runFileTest(t *testing.T, path string, opts ...RunFileTestOption) {
- t.Helper()
-
- opts = append([]RunFileTestOption{WithSyncWanted(*withSync)}, opts...)
-
- var logger loggerFunc
- if gno.IsDebug() && testing.Verbose() {
- logger = t.Log
- }
- rootDir := filepath.Join("..", "..")
- err := RunFileTest(rootDir, path, append(opts, WithLoggerFunc(logger))...)
- if err != nil {
- t.Fatalf("got error: %v", err)
- }
-}
diff --git a/gnovm/tests/files/access0_stdlibs.gno b/gnovm/tests/files/access0.gno
similarity index 100%
rename from gnovm/tests/files/access0_stdlibs.gno
rename to gnovm/tests/files/access0.gno
diff --git a/gnovm/tests/files/access1_stdlibs.gno b/gnovm/tests/files/access1.gno
similarity index 52%
rename from gnovm/tests/files/access1_stdlibs.gno
rename to gnovm/tests/files/access1.gno
index 5a1bf4cc12e..bcbfdb2829c 100644
--- a/gnovm/tests/files/access1_stdlibs.gno
+++ b/gnovm/tests/files/access1.gno
@@ -9,4 +9,4 @@ func main() {
}
// Error:
-// main/files/access1_stdlibs.gno:8:10: cannot access gno.land/p/demo/testutils.testVar2 from main
+// main/files/access1.gno:8:10: cannot access gno.land/p/demo/testutils.testVar2 from main
diff --git a/gnovm/tests/files/access2_stdlibs.gno b/gnovm/tests/files/access2.gno
similarity index 100%
rename from gnovm/tests/files/access2_stdlibs.gno
rename to gnovm/tests/files/access2.gno
diff --git a/gnovm/tests/files/access3_stdlibs.gno b/gnovm/tests/files/access3.gno
similarity index 100%
rename from gnovm/tests/files/access3_stdlibs.gno
rename to gnovm/tests/files/access3.gno
diff --git a/gnovm/tests/files/access4_stdlibs.gno b/gnovm/tests/files/access4.gno
similarity index 56%
rename from gnovm/tests/files/access4_stdlibs.gno
rename to gnovm/tests/files/access4.gno
index e38a6d2ea4a..72c4f926ce4 100644
--- a/gnovm/tests/files/access4_stdlibs.gno
+++ b/gnovm/tests/files/access4.gno
@@ -10,4 +10,4 @@ func main() {
}
// Error:
-// main/files/access4_stdlibs.gno:9:10: cannot access gno.land/p/demo/testutils.TestAccessStruct.privateField from main
+// main/files/access4.gno:9:10: cannot access gno.land/p/demo/testutils.TestAccessStruct.privateField from main
diff --git a/gnovm/tests/files/access5_stdlibs.gno b/gnovm/tests/files/access5.gno
similarity index 100%
rename from gnovm/tests/files/access5_stdlibs.gno
rename to gnovm/tests/files/access5.gno
diff --git a/gnovm/tests/files/access6_stdlibs.gno b/gnovm/tests/files/access6.gno
similarity index 61%
rename from gnovm/tests/files/access6_stdlibs.gno
rename to gnovm/tests/files/access6.gno
index 443f2f5291d..04778a8f5bb 100644
--- a/gnovm/tests/files/access6_stdlibs.gno
+++ b/gnovm/tests/files/access6.gno
@@ -16,4 +16,4 @@ func main() {
}
// Error:
-// main/files/access6_stdlibs.gno:15:2: main.mystruct does not implement gno.land/p/demo/testutils.PrivateInterface (missing method privateMethod)
+// main/files/access6.gno:15:2: main.mystruct does not implement gno.land/p/demo/testutils.PrivateInterface (missing method privateMethod)
diff --git a/gnovm/tests/files/access7_stdlibs.gno b/gnovm/tests/files/access7.gno
similarity index 67%
rename from gnovm/tests/files/access7_stdlibs.gno
rename to gnovm/tests/files/access7.gno
index 01c9ed83fa0..3874ad98971 100644
--- a/gnovm/tests/files/access7_stdlibs.gno
+++ b/gnovm/tests/files/access7.gno
@@ -20,4 +20,4 @@ func main() {
}
// Error:
-// main/files/access7_stdlibs.gno:19:2: main.PrivateInterface2 does not implement gno.land/p/demo/testutils.PrivateInterface (missing method privateMethod)
+// main/files/access7.gno:19:2: main.PrivateInterface2 does not implement gno.land/p/demo/testutils.PrivateInterface (missing method privateMethod)
diff --git a/gnovm/tests/files/add3.gno b/gnovm/tests/files/add3.gno
new file mode 100644
index 00000000000..c52e2c502dc
--- /dev/null
+++ b/gnovm/tests/files/add3.gno
@@ -0,0 +1,9 @@
+package main
+
+func main() {
+ a := 1
+ i := a + nil
+}
+
+// Error:
+// main/files/add3.gno:5:7: invalid operation: a + (const (undefined)) (mismatched types int and untyped nil)
diff --git a/gnovm/tests/files/addr0b_stdlibs.gno b/gnovm/tests/files/addr0b.gno
similarity index 100%
rename from gnovm/tests/files/addr0b_stdlibs.gno
rename to gnovm/tests/files/addr0b.gno
diff --git a/gnovm/tests/files/addr0b_native.gno b/gnovm/tests/files/addr0b_native.gno
deleted file mode 100644
index 86846500e42..00000000000
--- a/gnovm/tests/files/addr0b_native.gno
+++ /dev/null
@@ -1,25 +0,0 @@
-package main
-
-import (
- "fmt"
-
- "github.com/gnolang/gno/_test/net/http"
-)
-
-type extendedRequest struct {
- Request http.Request
-
- Data string
-}
-
-func main() {
- r := extendedRequest{}
- req := &r.Request
-
- fmt.Println(r)
- fmt.Println(req)
-}
-
-// Output:
-// {{ 0 0 map[] 0 [] false map[] map[] map[] } }
-// &{ 0 0 map[] 0 [] false map[] map[] map[] }
diff --git a/gnovm/tests/files/addr2b.gno b/gnovm/tests/files/addr2b.gno
index 04342c00574..59a18904bea 100644
--- a/gnovm/tests/files/addr2b.gno
+++ b/gnovm/tests/files/addr2b.gno
@@ -1,24 +1,22 @@
package main
import (
- "encoding/xml"
+ "encoding/json"
"fmt"
)
type Email struct {
- Where string `xml:"where,attr"`
+ Where string
Addr string
}
func f(s string, r interface{}) interface{} {
- return xml.Unmarshal([]byte(s), &r)
+ return json.Unmarshal([]byte(s), &r)
}
func main() {
data := `
-
- bob@work.com
-
+ {"Where": "work", "Addr": "bob@work.com"}
`
v := Email{}
err := f(data, &v)
diff --git a/gnovm/tests/files/assign0b_stdlibs.gno b/gnovm/tests/files/assign0b.gno
similarity index 100%
rename from gnovm/tests/files/assign0b_stdlibs.gno
rename to gnovm/tests/files/assign0b.gno
diff --git a/gnovm/tests/files/assign0b_native.gno b/gnovm/tests/files/assign0b_native.gno
deleted file mode 100644
index 42faa57634d..00000000000
--- a/gnovm/tests/files/assign0b_native.gno
+++ /dev/null
@@ -1,19 +0,0 @@
-package main
-
-import (
- "fmt"
- "time"
-
- "github.com/gnolang/gno/_test/net/http"
-)
-
-func main() {
- http.DefaultClient.Timeout = time.Second * 10
- fmt.Println(http.DefaultClient)
- http.DefaultClient = &http.Client{}
- fmt.Println(http.DefaultClient)
-}
-
-// Output:
-// &{ 10s}
-// &{ 0s}
diff --git a/gnovm/tests/files/assign38.gno b/gnovm/tests/files/assign38.gno
new file mode 100644
index 00000000000..5ef3549ccf6
--- /dev/null
+++ b/gnovm/tests/files/assign38.gno
@@ -0,0 +1,10 @@
+package main
+
+func main() {
+ a := 1
+ a = nil
+ println(a)
+}
+
+// Error:
+// main/files/assign38.gno:5:2: cannot use nil as int value in assignment
diff --git a/gnovm/tests/files/assign_unnamed_type/more/cross_realm_compositelit_filetest_stdlibs.gno b/gnovm/tests/files/assign_unnamed_type/more/cross_realm_compositelit_filetest.gno
similarity index 100%
rename from gnovm/tests/files/assign_unnamed_type/more/cross_realm_compositelit_filetest_stdlibs.gno
rename to gnovm/tests/files/assign_unnamed_type/more/cross_realm_compositelit_filetest.gno
diff --git a/gnovm/tests/files/assign_unnamed_type/more/realm_compositelit_filetest.gno b/gnovm/tests/files/assign_unnamed_type/more/realm_compositelit_filetest.gno
index d61170334d7..45f83bade5f 100644
--- a/gnovm/tests/files/assign_unnamed_type/more/realm_compositelit_filetest.gno
+++ b/gnovm/tests/files/assign_unnamed_type/more/realm_compositelit_filetest.gno
@@ -210,7 +210,7 @@ func main() {
// "Escaped": true,
// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3"
// },
-// "FileName": "main.gno",
+// "FileName": "files/assign_unnamed_type/more/realm_compositelit.gno",
// "IsMethod": false,
// "Name": "main",
// "NativeName": "",
@@ -221,7 +221,7 @@ func main() {
// "BlockNode": null,
// "Location": {
// "Column": "1",
-// "File": "main.gno",
+// "File": "files/assign_unnamed_type/more/realm_compositelit.gno",
// "Line": "16",
// "PkgPath": "gno.land/r/test"
// }
diff --git a/gnovm/tests/files/bin1.gno b/gnovm/tests/files/bin1.gno
index 792651f60bf..e0e5a6d663a 100644
--- a/gnovm/tests/files/bin1.gno
+++ b/gnovm/tests/files/bin1.gno
@@ -1,16 +1,14 @@
package main
import (
- "crypto/sha1"
+ "crypto/sha256"
"fmt"
)
func main() {
- d := sha1.New()
- d.Write([]byte("password"))
- a := d.Sum(nil)
+ a := sha256.Sum256([]byte("password"))
fmt.Println(a)
}
// Output:
-// [91 170 97 228 201 185 63 63 6 130 37 11 108 248 51 27 126 230 143 216]
+// [94 136 72 152 218 40 4 113 81 208 229 111 141 198 41 39 115 96 61 13 106 171 189 214 42 17 239 114 29 21 66 216]
diff --git a/gnovm/tests/files/bin5.gno b/gnovm/tests/files/bin5.gno
deleted file mode 100644
index d471d4e0fd2..00000000000
--- a/gnovm/tests/files/bin5.gno
+++ /dev/null
@@ -1,15 +0,0 @@
-package main
-
-import (
- "fmt"
- "net"
-)
-
-func main() {
- addr := net.TCPAddr{IP: net.IPv4(1, 1, 1, 1), Port: 80}
- var s fmt.Stringer = &addr
- fmt.Println(s.String())
-}
-
-// Output:
-// 1.1.1.1:80
diff --git a/gnovm/tests/files/binstruct_ptr_map0.gno b/gnovm/tests/files/binstruct_ptr_map0.gno
index 329ece209e4..5eddca44f6e 100644
--- a/gnovm/tests/files/binstruct_ptr_map0.gno
+++ b/gnovm/tests/files/binstruct_ptr_map0.gno
@@ -2,11 +2,12 @@ package main
import (
"fmt"
- "image"
)
+type Point struct{ X, Y int }
+
func main() {
- v := map[string]*image.Point{
+ v := map[string]*Point{
"foo": {X: 3, Y: 2},
"bar": {X: 4, Y: 5},
}
@@ -14,4 +15,4 @@ func main() {
}
// Output:
-// (3,2) (4,5)
+// &{3 2} &{4 5}
diff --git a/gnovm/tests/files/binstruct_ptr_slice0.gno b/gnovm/tests/files/binstruct_ptr_slice0.gno
deleted file mode 100644
index 1ceea6cab70..00000000000
--- a/gnovm/tests/files/binstruct_ptr_slice0.gno
+++ /dev/null
@@ -1,17 +0,0 @@
-package main
-
-import (
- "fmt"
- "image"
-)
-
-func main() {
- v := []*image.Point{
- {X: 3, Y: 2},
- {X: 4, Y: 5},
- }
- fmt.Println(v)
-}
-
-// Output:
-// [(3,2) (4,5)]
diff --git a/gnovm/tests/files/binstruct_slice0.gno b/gnovm/tests/files/binstruct_slice0.gno
index 211f60faf01..3cdc455a66f 100644
--- a/gnovm/tests/files/binstruct_slice0.gno
+++ b/gnovm/tests/files/binstruct_slice0.gno
@@ -2,15 +2,16 @@ package main
import (
"fmt"
- "image"
)
+type Point struct{ X, Y int }
+
func main() {
- v := []image.Point{
+ v := []Point{
{X: 3, Y: 2},
}
fmt.Println(v)
}
// Output:
-// [(3,2)]
+// [{3 2}]
diff --git a/gnovm/tests/files/blankidentifier0.gno b/gnovm/tests/files/blankidentifier0.gno
new file mode 100644
index 00000000000..a7447a22a6a
--- /dev/null
+++ b/gnovm/tests/files/blankidentifier0.gno
@@ -0,0 +1,8 @@
+package main
+
+func main() {
+ _ = _
+}
+
+// Error:
+// main/files/blankidentifier0.gno:4:6: cannot use _ as value or type
diff --git a/gnovm/tests/files/blankidentifier1.gno b/gnovm/tests/files/blankidentifier1.gno
new file mode 100644
index 00000000000..9c93ff08f10
--- /dev/null
+++ b/gnovm/tests/files/blankidentifier1.gno
@@ -0,0 +1,12 @@
+package main
+
+type zilch interface{}
+
+func main() {
+ _ = zilch(nil)
+
+ println("ok")
+}
+
+// Output:
+// ok
diff --git a/gnovm/tests/files/blankidentifier2.gno b/gnovm/tests/files/blankidentifier2.gno
new file mode 100644
index 00000000000..a8d06cdabdc
--- /dev/null
+++ b/gnovm/tests/files/blankidentifier2.gno
@@ -0,0 +1,9 @@
+package main
+
+func main() {
+ var i _
+ println(i)
+}
+
+// Error:
+// main/files/blankidentifier2.gno:4:6: cannot use _ as value or type
diff --git a/gnovm/tests/files/blankidentifier3.gno b/gnovm/tests/files/blankidentifier3.gno
new file mode 100644
index 00000000000..aab1388c92d
--- /dev/null
+++ b/gnovm/tests/files/blankidentifier3.gno
@@ -0,0 +1,10 @@
+package main
+
+type S _
+
+func main() {
+ println("hey")
+}
+
+// Error:
+// main/files/blankidentifier3.gno:3:6: cannot use _ as value or type
diff --git a/gnovm/tests/files/blankidentifier4.gno b/gnovm/tests/files/blankidentifier4.gno
new file mode 100644
index 00000000000..214102e6c98
--- /dev/null
+++ b/gnovm/tests/files/blankidentifier4.gno
@@ -0,0 +1,9 @@
+package main
+
+func main() {
+ _ = 1
+ println("ok")
+}
+
+// Output:
+// ok
diff --git a/gnovm/tests/files/blankidentifier5.gno b/gnovm/tests/files/blankidentifier5.gno
new file mode 100644
index 00000000000..0de62bb77c3
--- /dev/null
+++ b/gnovm/tests/files/blankidentifier5.gno
@@ -0,0 +1,12 @@
+package main
+
+type foo struct{}
+
+var _ int = 10
+
+func main() {
+ println("ok")
+}
+
+// Output:
+// ok
diff --git a/gnovm/tests/files/blankidentifier6.gno b/gnovm/tests/files/blankidentifier6.gno
new file mode 100644
index 00000000000..a59ea246ad6
--- /dev/null
+++ b/gnovm/tests/files/blankidentifier6.gno
@@ -0,0 +1,24 @@
+package main
+
+type Animal interface {
+ Sound() string
+}
+
+type Dog struct {
+ name string
+}
+
+func (d Dog) Sound() string {
+ return "Woof!"
+}
+
+func main() {
+ var a Animal = Dog{name: "Rex"}
+
+ v := a.(_)
+
+ println(v)
+}
+
+// Error:
+// main/files/blankidentifier6.gno:18:13: cannot use _ as value or type
diff --git a/gnovm/tests/files/blankidentifier7.gno b/gnovm/tests/files/blankidentifier7.gno
new file mode 100644
index 00000000000..4b3a50d2135
--- /dev/null
+++ b/gnovm/tests/files/blankidentifier7.gno
@@ -0,0 +1,13 @@
+package main
+
+import "strconv"
+
+var value int = 0
+func Foo(_ string) string { return strconv.Itoa(value) }
+
+func main() {
+ println(Foo(""))
+}
+
+// Output:
+// 0
diff --git a/gnovm/tests/files/block0.gno b/gnovm/tests/files/block0.gno
new file mode 100644
index 00000000000..b6d554ce500
--- /dev/null
+++ b/gnovm/tests/files/block0.gno
@@ -0,0 +1,14 @@
+package main
+
+func foo() int {
+ {
+ return 1
+ }
+}
+
+func main() {
+ println(foo())
+}
+
+// Output:
+// 1
diff --git a/gnovm/tests/files/bool8.gno b/gnovm/tests/files/bool8.gno
new file mode 100644
index 00000000000..9efbbbe6da2
--- /dev/null
+++ b/gnovm/tests/files/bool8.gno
@@ -0,0 +1,17 @@
+package main
+
+// results from comparisons should not be untyped bools
+
+var a interface{} = true
+
+func main() {
+ buf := "hello="
+ isEqual(a, (buf[len(buf)-1] == '='))
+}
+
+func isEqual(v1, v2 interface{}) {
+ println("v1 == v2", v1 == v2)
+}
+
+// Output:
+// v1 == v2 true
diff --git a/gnovm/tests/files/composite11.gno b/gnovm/tests/files/composite11.gno
index 85f71018202..2d989022f25 100644
--- a/gnovm/tests/files/composite11.gno
+++ b/gnovm/tests/files/composite11.gno
@@ -2,11 +2,14 @@ package main
import (
"fmt"
- "image/color"
)
+type NRGBA64 struct {
+ R, G, B, A uint16
+}
+
func main() {
- c := color.NRGBA64{1, 1, 1, 1}
+ c := NRGBA64{1, 1, 1, 1}
fmt.Println(c)
}
diff --git a/gnovm/tests/files/const14.gno b/gnovm/tests/files/const14.gno
index 835858f712d..93d7975e20f 100644
--- a/gnovm/tests/files/const14.gno
+++ b/gnovm/tests/files/const14.gno
@@ -1,13 +1,13 @@
package main
-import "compress/flate"
+import "math"
-func f1(i int) { println("i:", i) }
+func f1(i float64) { println("i:", i) }
func main() {
- i := flate.BestSpeed
+ i := math.Pi
f1(i)
}
// Output:
-// i: 1
+// i: 3.141592653589793
diff --git a/gnovm/tests/files/const22.gno b/gnovm/tests/files/const22.gno
index 42842066265..f92fcd4d910 100644
--- a/gnovm/tests/files/const22.gno
+++ b/gnovm/tests/files/const22.gno
@@ -31,7 +31,7 @@ func main() {
fmt.Printf("%x", ha)
fmt.Printf("%x", hb)
- fmt.Printf("%x", ho)
+ fmt.Printf("%x\n", ho)
}
// Output:
diff --git a/gnovm/tests/files/context.gno b/gnovm/tests/files/context.gno
deleted file mode 100644
index 0dcd8d73a22..00000000000
--- a/gnovm/tests/files/context.gno
+++ /dev/null
@@ -1,19 +0,0 @@
-package main
-
-import "context"
-
-func get(ctx context.Context, k string) string {
- var r string
- if v := ctx.Value(k); v != nil {
- r = v.(string)
- }
- return r
-}
-
-func main() {
- ctx := context.WithValue(context.Background(), "hello", "world")
- println(get(ctx, "hello"))
-}
-
-// Output:
-// world
diff --git a/gnovm/tests/files/context2.gno b/gnovm/tests/files/context2.gno
deleted file mode 100644
index 457fb03b735..00000000000
--- a/gnovm/tests/files/context2.gno
+++ /dev/null
@@ -1,22 +0,0 @@
-package main
-
-import "context"
-
-func get(ctx context.Context, k string) string {
- var r string
- var ok bool
- if v := ctx.Value(k); v != nil {
- r, ok = v.(string)
- println(ok)
- }
- return r
-}
-
-func main() {
- ctx := context.WithValue(context.Background(), "hello", "world")
- println(get(ctx, "hello"))
-}
-
-// Output:
-// true
-// world
diff --git a/gnovm/tests/files/defer4.gno b/gnovm/tests/files/defer4.gno
deleted file mode 100644
index e9baa5ac250..00000000000
--- a/gnovm/tests/files/defer4.gno
+++ /dev/null
@@ -1,23 +0,0 @@
-package main
-
-import "sync"
-
-type T struct {
- mu sync.RWMutex
- name string
-}
-
-func (t *T) get() string {
- t.mu.RLock()
- defer t.mu.RUnlock()
- return t.name
-}
-
-var d = T{name: "test"}
-
-func main() {
- println(d.get())
-}
-
-// Output:
-// test
diff --git a/gnovm/tests/files/extern/p1/s1.gno b/gnovm/tests/files/extern/p1/s1.gno
deleted file mode 100644
index ff2d89d4462..00000000000
--- a/gnovm/tests/files/extern/p1/s1.gno
+++ /dev/null
@@ -1,5 +0,0 @@
-package p1
-
-import "crypto/rand"
-
-var Prime = rand.Prime
diff --git a/gnovm/tests/files/extern/timtadh/data_structures/types/string.gno b/gnovm/tests/files/extern/timtadh/data_structures/types/string.gno
index 2411bd2081f..13f94950dfa 100644
--- a/gnovm/tests/files/extern/timtadh/data_structures/types/string.gno
+++ b/gnovm/tests/files/extern/timtadh/data_structures/types/string.gno
@@ -2,7 +2,7 @@ package types
import (
"bytes"
- "hash/fnv"
+ "crypto/sha256"
)
type (
@@ -36,9 +36,7 @@ func (self String) Less(other Sortable) bool {
}
func (self String) Hash() int {
- h := fnv.New32a()
- h.Write([]byte(string(self)))
- return int(h.Sum32())
+ return int(hash([]byte(self)))
}
func (self *ByteSlice) MarshalBinary() ([]byte, error) {
@@ -67,7 +65,13 @@ func (self ByteSlice) Less(other Sortable) bool {
}
func (self ByteSlice) Hash() int {
- h := fnv.New32a()
- h.Write([]byte(self))
- return int(h.Sum32())
+ return int(hash([]byte(self)))
+}
+
+func hash(s []byte) int {
+ res := sha256.Sum256(s)
+ return int(s[0]) |
+ int(s[1]<<8) |
+ int(s[2]<<16) |
+ int(s[3]<<24)
}
diff --git a/gnovm/tests/files/float5_stdlibs.gno b/gnovm/tests/files/float5.gno
similarity index 100%
rename from gnovm/tests/files/float5_stdlibs.gno
rename to gnovm/tests/files/float5.gno
diff --git a/gnovm/tests/files/fun28.gno b/gnovm/tests/files/fun28.gno
new file mode 100644
index 00000000000..cf969f9f34b
--- /dev/null
+++ b/gnovm/tests/files/fun28.gno
@@ -0,0 +1,10 @@
+package main
+
+func f(i int) {}
+
+func main() {
+ f(nil)
+}
+
+// Error:
+// main/files/fun28.gno:6:2: cannot use nil as int value in argument to f
diff --git a/gnovm/tests/files/fun6.gno b/gnovm/tests/files/fun6.gno
deleted file mode 100644
index c5ec644afd5..00000000000
--- a/gnovm/tests/files/fun6.gno
+++ /dev/null
@@ -1,21 +0,0 @@
-package main
-
-import (
- "fmt"
- "sync"
-)
-
-func NewPool() Pool { return Pool{} }
-
-type Pool struct {
- P *sync.Pool
-}
-
-var _pool = NewPool()
-
-func main() {
- fmt.Println(_pool)
-}
-
-// Output:
-// {}
diff --git a/gnovm/tests/files/fun6b.gno b/gnovm/tests/files/fun6b.gno
deleted file mode 100644
index 17b0473b33b..00000000000
--- a/gnovm/tests/files/fun6b.gno
+++ /dev/null
@@ -1,20 +0,0 @@
-package main
-
-import (
- "sync"
-)
-
-func NewPool() Pool { return Pool{} }
-
-type Pool struct {
- p *sync.Pool
-}
-
-var _pool = NewPool()
-
-func main() {
- println(_pool)
-}
-
-// Output:
-// (struct{(gonative{} gonative{*sync.Pool})} main.Pool)
diff --git a/gnovm/tests/files/fun7.gno b/gnovm/tests/files/fun7.gno
deleted file mode 100644
index ee8f813a527..00000000000
--- a/gnovm/tests/files/fun7.gno
+++ /dev/null
@@ -1,18 +0,0 @@
-package main
-
-import (
- goflag "flag"
- "fmt"
-)
-
-func Foo(goflag *goflag.Flag) {
- fmt.Println(goflag)
-}
-
-func main() {
- g := &goflag.Flag{}
- Foo(g)
-}
-
-// Output:
-// &{ }
diff --git a/gnovm/tests/files/goto_empty_stmt.gno b/gnovm/tests/files/goto_empty_stmt.gno
new file mode 100644
index 00000000000..fd939de1045
--- /dev/null
+++ b/gnovm/tests/files/goto_empty_stmt.gno
@@ -0,0 +1,10 @@
+package main
+
+func main() {
+ println("Hi")
+ goto done
+done:
+}
+
+// Output:
+// Hi
\ No newline at end of file
diff --git a/gnovm/tests/files/heap_alloc_forloop9_1.gno b/gnovm/tests/files/heap_alloc_forloop9_1.gno
index 5e3b9af74f6..2576b9b4da6 100644
--- a/gnovm/tests/files/heap_alloc_forloop9_1.gno
+++ b/gnovm/tests/files/heap_alloc_forloop9_1.gno
@@ -19,7 +19,7 @@ func main() {
// file{ package main; func Search(n (const-type int), f func(.arg_0 (const-type int)) (const-type bool)) (const-type int) { f((const (1 int))); return (const (0 int)) }; func main() { for x := (const (0 int)); x<~VPBlock(1,0)> < (const (2 int)); x<~VPBlock(1,0)>++ { count := (const (0 int)); (const (println func(xs ...interface{})()))((const (" first: count: " string)), count<~VPBlock(1,1)>); Search((const (1 int)), func func(i (const-type int)) (const-type bool){ count<~VPBlock(1,2)>++; return (const-type bool)(i >= x<~VPBlock(1,3)>) }, x<()~VPBlock(1,0)>>); (const (println func(xs ...interface{})()))((const ("second: count: " string)), count<~VPBlock(1,1)>) } } }
// Output:
-// first: count: 0
+// first: count: 0
// second: count: 1
// first: count: 0
// second: count: 1
diff --git a/gnovm/tests/files/heap_item_value.gno b/gnovm/tests/files/heap_item_value.gno
index 40ec05d3ba1..80bf702bec2 100644
--- a/gnovm/tests/files/heap_item_value.gno
+++ b/gnovm/tests/files/heap_item_value.gno
@@ -151,7 +151,7 @@ func main() {
// "Escaped": true,
// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3"
// },
-// "FileName": "main.gno",
+// "FileName": "files/heap_item_value.gno",
// "IsMethod": false,
// "Name": "main",
// "NativeName": "",
@@ -162,7 +162,7 @@ func main() {
// "BlockNode": null,
// "Location": {
// "Column": "1",
-// "File": "main.gno",
+// "File": "files/heap_item_value.gno",
// "Line": "10",
// "PkgPath": "gno.land/r/test"
// }
diff --git a/gnovm/tests/files/heap_item_value_init.gno b/gnovm/tests/files/heap_item_value_init.gno
index 72f065326f1..2722cce8675 100644
--- a/gnovm/tests/files/heap_item_value_init.gno
+++ b/gnovm/tests/files/heap_item_value_init.gno
@@ -122,7 +122,7 @@ func main() {
// "Escaped": true,
// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3"
// },
-// "FileName": "main.gno",
+// "FileName": "files/heap_item_value_init.gno",
// "IsMethod": false,
// "Name": "init.3",
// "NativeName": "",
@@ -133,7 +133,7 @@ func main() {
// "BlockNode": null,
// "Location": {
// "Column": "1",
-// "File": "main.gno",
+// "File": "files/heap_item_value_init.gno",
// "Line": "10",
// "PkgPath": "gno.land/r/test"
// }
@@ -158,7 +158,7 @@ func main() {
// "Escaped": true,
// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3"
// },
-// "FileName": "main.gno",
+// "FileName": "files/heap_item_value_init.gno",
// "IsMethod": false,
// "Name": "main",
// "NativeName": "",
@@ -169,7 +169,7 @@ func main() {
// "BlockNode": null,
// "Location": {
// "Column": "1",
-// "File": "main.gno",
+// "File": "files/heap_item_value_init.gno",
// "Line": "16",
// "PkgPath": "gno.land/r/test"
// }
diff --git a/gnovm/tests/files/import11.gno b/gnovm/tests/files/import11.gno
new file mode 100644
index 00000000000..594e9f10698
--- /dev/null
+++ b/gnovm/tests/files/import11.gno
@@ -0,0 +1,13 @@
+// PKGPATH: gno.land/p/demo/bar
+package bar
+
+import (
+ "gno.land/r/demo/tests"
+)
+
+func main() {
+ println(tests.Counter())
+}
+
+// Error:
+// gno.land/p/demo/bar/files/import11.gno:5:2: pure package path "gno.land/p/demo/bar" cannot import realm path "gno.land/r/demo/tests"
diff --git a/gnovm/tests/files/import3.gno b/gnovm/tests/files/import3.gno
index c16ac626299..c63ed8a055c 100644
--- a/gnovm/tests/files/import3.gno
+++ b/gnovm/tests/files/import3.gno
@@ -4,7 +4,8 @@ import "github.com/gnolang/gno/_test/foo"
func main() { println(foo.Bar, foo.Boo) }
+// Init functions of dependencies are executed separatedly from the test itself,
+// so they don't print with the test proper.
+
// Output:
-// init boo
-// init foo
// BARR Boo
diff --git a/gnovm/tests/files/import5.gno b/gnovm/tests/files/import5.gno
index 609364d85b1..b270d0b0d3c 100644
--- a/gnovm/tests/files/import5.gno
+++ b/gnovm/tests/files/import5.gno
@@ -5,6 +5,4 @@ import boo "github.com/gnolang/gno/_test/foo"
func main() { println(boo.Bar, boo.Boo, boo.Bir) }
// Output:
-// init boo
-// init foo
// BARR Boo Boo22
diff --git a/gnovm/tests/files/interp.gi b/gnovm/tests/files/interp.gi
deleted file mode 100644
index ace895a356c..00000000000
--- a/gnovm/tests/files/interp.gi
+++ /dev/null
@@ -1,13 +0,0 @@
-package main
-
-import (
- "github.com/gnolang/gno/interp"
-)
-
-func main() {
- i := interp.New(interp.Opt{})
- i.Eval(`println("Hello")`)
-}
-
-// Output:
-// Hello
diff --git a/gnovm/tests/files/interp2.gi b/gnovm/tests/files/interp2.gi
deleted file mode 100644
index af3ffd75f18..00000000000
--- a/gnovm/tests/files/interp2.gi
+++ /dev/null
@@ -1,16 +0,0 @@
-package main
-
-import (
- "github.com/gnolang/gno/interp"
-)
-
-func main() {
- i := interp.New(interp.Opt{})
- i.Use(interp.ExportValue, interp.ExportType)
- i.Eval(`import "github.com/gnolang/gno/interp"`)
- i.Eval(`i := interp.New(interp.Opt{})`)
- i.Eval(`i.Eval("println(42)")`)
-}
-
-// Output:
-// 42
diff --git a/gnovm/tests/files/io0_stdlibs.gno b/gnovm/tests/files/io0.gno
similarity index 100%
rename from gnovm/tests/files/io0_stdlibs.gno
rename to gnovm/tests/files/io0.gno
diff --git a/gnovm/tests/files/io0_native.gno b/gnovm/tests/files/io0_native.gno
deleted file mode 100644
index 6486a9ba558..00000000000
--- a/gnovm/tests/files/io0_native.gno
+++ /dev/null
@@ -1,18 +0,0 @@
-package main
-
-import (
- "crypto/rand"
- "fmt"
- "io"
-)
-
-func main() {
- var buf [16]byte
- fmt.Println(buf)
- io.ReadFull(rand.Reader, buf[:])
- fmt.Println(buf)
-}
-
-// Output:
-// [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
-// [100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115]
diff --git a/gnovm/tests/files/io2.gno b/gnovm/tests/files/io2.gno
index 24655f5040c..d0637c44e16 100644
--- a/gnovm/tests/files/io2.gno
+++ b/gnovm/tests/files/io2.gno
@@ -3,7 +3,6 @@ package main
import (
"fmt"
"io"
- "log"
"strings"
)
@@ -12,9 +11,9 @@ func main() {
b, err := io.ReadAll(r)
if err != nil {
- log.Fatal(err)
+ panic(err)
}
- fmt.Printf("%s", b)
+ fmt.Printf("%s\n", b)
}
// Output:
diff --git a/gnovm/tests/files/issue_558b_stdlibs.gno b/gnovm/tests/files/issue_558b.gno
similarity index 97%
rename from gnovm/tests/files/issue_558b_stdlibs.gno
rename to gnovm/tests/files/issue_558b.gno
index 55eba88c985..51ddee56e0a 100644
--- a/gnovm/tests/files/issue_558b_stdlibs.gno
+++ b/gnovm/tests/files/issue_558b.gno
@@ -3,8 +3,6 @@ package main
import (
"fmt"
"io"
- "io"
- "log"
"strings"
)
@@ -68,7 +66,7 @@ func main() {
p.Reader = newReadAutoCloser(strings.NewReader("test"))
b, err := ReadAll(p.Reader)
if err != nil {
- log.Fatal(err)
+ panic(err)
}
fmt.Println(string(b))
}
diff --git a/gnovm/tests/files/issue_782.gno b/gnovm/tests/files/issue_782.gno
index 9d89a90bd30..4dc938ceaec 100644
--- a/gnovm/tests/files/issue_782.gno
+++ b/gnovm/tests/files/issue_782.gno
@@ -7,7 +7,7 @@ func main() {
from := uint32(2)
to := uint32(4)
b := a[from:to]
- fmt.Print(b)
+ fmt.Println(b)
}
// Output:
diff --git a/gnovm/tests/files/l3_long.gno b/gnovm/tests/files/l3_long.gno
deleted file mode 100644
index 64e75f522b2..00000000000
--- a/gnovm/tests/files/l3_long.gno
+++ /dev/null
@@ -1,163 +0,0 @@
-package main
-
-func main() {
- for a := 0; a < 20000000; a++ {
- if a&0x8ffff == 0x80000 {
- println(a)
- }
- }
-}
-
-// Output:
-// 524288
-// 589824
-// 655360
-// 720896
-// 786432
-// 851968
-// 917504
-// 983040
-// 1572864
-// 1638400
-// 1703936
-// 1769472
-// 1835008
-// 1900544
-// 1966080
-// 2031616
-// 2621440
-// 2686976
-// 2752512
-// 2818048
-// 2883584
-// 2949120
-// 3014656
-// 3080192
-// 3670016
-// 3735552
-// 3801088
-// 3866624
-// 3932160
-// 3997696
-// 4063232
-// 4128768
-// 4718592
-// 4784128
-// 4849664
-// 4915200
-// 4980736
-// 5046272
-// 5111808
-// 5177344
-// 5767168
-// 5832704
-// 5898240
-// 5963776
-// 6029312
-// 6094848
-// 6160384
-// 6225920
-// 6815744
-// 6881280
-// 6946816
-// 7012352
-// 7077888
-// 7143424
-// 7208960
-// 7274496
-// 7864320
-// 7929856
-// 7995392
-// 8060928
-// 8126464
-// 8192000
-// 8257536
-// 8323072
-// 8912896
-// 8978432
-// 9043968
-// 9109504
-// 9175040
-// 9240576
-// 9306112
-// 9371648
-// 9961472
-// 10027008
-// 10092544
-// 10158080
-// 10223616
-// 10289152
-// 10354688
-// 10420224
-// 11010048
-// 11075584
-// 11141120
-// 11206656
-// 11272192
-// 11337728
-// 11403264
-// 11468800
-// 12058624
-// 12124160
-// 12189696
-// 12255232
-// 12320768
-// 12386304
-// 12451840
-// 12517376
-// 13107200
-// 13172736
-// 13238272
-// 13303808
-// 13369344
-// 13434880
-// 13500416
-// 13565952
-// 14155776
-// 14221312
-// 14286848
-// 14352384
-// 14417920
-// 14483456
-// 14548992
-// 14614528
-// 15204352
-// 15269888
-// 15335424
-// 15400960
-// 15466496
-// 15532032
-// 15597568
-// 15663104
-// 16252928
-// 16318464
-// 16384000
-// 16449536
-// 16515072
-// 16580608
-// 16646144
-// 16711680
-// 17301504
-// 17367040
-// 17432576
-// 17498112
-// 17563648
-// 17629184
-// 17694720
-// 17760256
-// 18350080
-// 18415616
-// 18481152
-// 18546688
-// 18612224
-// 18677760
-// 18743296
-// 18808832
-// 19398656
-// 19464192
-// 19529728
-// 19595264
-// 19660800
-// 19726336
-// 19791872
-// 19857408
diff --git a/gnovm/tests/files/l4_long.gno b/gnovm/tests/files/l4_long.gno
deleted file mode 100644
index f91542999b3..00000000000
--- a/gnovm/tests/files/l4_long.gno
+++ /dev/null
@@ -1,7 +0,0 @@
-package main
-
-func main() { println(f(5)) }
-func f(i int) int { return i + 1 }
-
-// Output:
-// 6
diff --git a/gnovm/tests/files/l5_long.gno b/gnovm/tests/files/l5_long.gno
deleted file mode 100644
index c72357d2e15..00000000000
--- a/gnovm/tests/files/l5_long.gno
+++ /dev/null
@@ -1,164 +0,0 @@
-package main
-
-func main() {
- for a := 0; a < 20000000; {
- if a&0x8ffff == 0x80000 {
- println(a)
- }
- a = a + 1
- }
-}
-
-// Output:
-// 524288
-// 589824
-// 655360
-// 720896
-// 786432
-// 851968
-// 917504
-// 983040
-// 1572864
-// 1638400
-// 1703936
-// 1769472
-// 1835008
-// 1900544
-// 1966080
-// 2031616
-// 2621440
-// 2686976
-// 2752512
-// 2818048
-// 2883584
-// 2949120
-// 3014656
-// 3080192
-// 3670016
-// 3735552
-// 3801088
-// 3866624
-// 3932160
-// 3997696
-// 4063232
-// 4128768
-// 4718592
-// 4784128
-// 4849664
-// 4915200
-// 4980736
-// 5046272
-// 5111808
-// 5177344
-// 5767168
-// 5832704
-// 5898240
-// 5963776
-// 6029312
-// 6094848
-// 6160384
-// 6225920
-// 6815744
-// 6881280
-// 6946816
-// 7012352
-// 7077888
-// 7143424
-// 7208960
-// 7274496
-// 7864320
-// 7929856
-// 7995392
-// 8060928
-// 8126464
-// 8192000
-// 8257536
-// 8323072
-// 8912896
-// 8978432
-// 9043968
-// 9109504
-// 9175040
-// 9240576
-// 9306112
-// 9371648
-// 9961472
-// 10027008
-// 10092544
-// 10158080
-// 10223616
-// 10289152
-// 10354688
-// 10420224
-// 11010048
-// 11075584
-// 11141120
-// 11206656
-// 11272192
-// 11337728
-// 11403264
-// 11468800
-// 12058624
-// 12124160
-// 12189696
-// 12255232
-// 12320768
-// 12386304
-// 12451840
-// 12517376
-// 13107200
-// 13172736
-// 13238272
-// 13303808
-// 13369344
-// 13434880
-// 13500416
-// 13565952
-// 14155776
-// 14221312
-// 14286848
-// 14352384
-// 14417920
-// 14483456
-// 14548992
-// 14614528
-// 15204352
-// 15269888
-// 15335424
-// 15400960
-// 15466496
-// 15532032
-// 15597568
-// 15663104
-// 16252928
-// 16318464
-// 16384000
-// 16449536
-// 16515072
-// 16580608
-// 16646144
-// 16711680
-// 17301504
-// 17367040
-// 17432576
-// 17498112
-// 17563648
-// 17629184
-// 17694720
-// 17760256
-// 18350080
-// 18415616
-// 18481152
-// 18546688
-// 18612224
-// 18677760
-// 18743296
-// 18808832
-// 19398656
-// 19464192
-// 19529728
-// 19595264
-// 19660800
-// 19726336
-// 19791872
-// 19857408
diff --git a/gnovm/tests/files/l2_long.gno b/gnovm/tests/files/loop0.gno
similarity index 100%
rename from gnovm/tests/files/l2_long.gno
rename to gnovm/tests/files/loop0.gno
diff --git a/gnovm/tests/files/loop1.gno b/gnovm/tests/files/loop1.gno
new file mode 100644
index 00000000000..4a61fbfba5b
--- /dev/null
+++ b/gnovm/tests/files/loop1.gno
@@ -0,0 +1,51 @@
+package main
+
+func main() {
+ for a := 0; a < 20000; {
+ if (a & 0x8ff) == 0x800 {
+ println(a)
+ }
+ a = a + 1
+ }
+}
+
+// Output:
+// 2048
+// 2304
+// 2560
+// 2816
+// 3072
+// 3328
+// 3584
+// 3840
+// 6144
+// 6400
+// 6656
+// 6912
+// 7168
+// 7424
+// 7680
+// 7936
+// 10240
+// 10496
+// 10752
+// 11008
+// 11264
+// 11520
+// 11776
+// 12032
+// 14336
+// 14592
+// 14848
+// 15104
+// 15360
+// 15616
+// 15872
+// 16128
+// 18432
+// 18688
+// 18944
+// 19200
+// 19456
+// 19712
+// 19968
diff --git a/gnovm/tests/files/map27.gno b/gnovm/tests/files/map27.gno
index 5d76ffc21c7..578788d144e 100644
--- a/gnovm/tests/files/map27.gno
+++ b/gnovm/tests/files/map27.gno
@@ -2,7 +2,6 @@ package main
import (
"fmt"
- "text/template"
)
type fm map[string]interface{}
@@ -14,7 +13,7 @@ func main() {
a["foo"] = &foo{}
fmt.Println(a["foo"])
- b := make(template.FuncMap) // type FuncMap map[string]interface{}
+ b := make(map[string]interface{})
b["foo"] = &foo{}
fmt.Println(b["foo"])
}
diff --git a/gnovm/tests/files/map29_stdlibs.gno b/gnovm/tests/files/map29.gno
similarity index 100%
rename from gnovm/tests/files/map29_stdlibs.gno
rename to gnovm/tests/files/map29.gno
diff --git a/gnovm/tests/files/map29_native.gno b/gnovm/tests/files/map29_native.gno
deleted file mode 100644
index b4a4129cd39..00000000000
--- a/gnovm/tests/files/map29_native.gno
+++ /dev/null
@@ -1,26 +0,0 @@
-package main
-
-import (
- "fmt"
- "time"
-)
-
-type Item struct {
- Object interface{}
- Expiry time.Duration
-}
-
-func main() {
- items := map[string]Item{}
-
- items["test"] = Item{
- Object: "test",
- Expiry: time.Second,
- }
-
- item := items["test"]
- fmt.Println(item)
-}
-
-// Output:
-// {test 1s}
diff --git a/gnovm/tests/files/math0_stdlibs.gno b/gnovm/tests/files/math0.gno
similarity index 100%
rename from gnovm/tests/files/math0_stdlibs.gno
rename to gnovm/tests/files/math0.gno
diff --git a/gnovm/tests/files/math3.gno b/gnovm/tests/files/math3.gno
index 592af0aa89d..a0ed2e1aa1e 100644
--- a/gnovm/tests/files/math3.gno
+++ b/gnovm/tests/files/math3.gno
@@ -1,32 +1,27 @@
package main
import (
- "crypto/md5"
+ "crypto/sha256"
"fmt"
)
-func md5Crypt(password, salt, magic []byte) []byte {
- d := md5.New()
- d.Write(password)
- d.Write(magic)
- d.Write(salt)
+func sha256Crypt(password, salt, magic string) []byte {
+ toHash := password + magic + salt
+ mixin := sha256.Sum256([]byte(password + salt))
- d2 := md5.New()
- d2.Write(password)
- d2.Write(salt)
-
- for i, mixin := 0, d2.Sum(nil); i < len(password); i++ {
- d.Write([]byte{mixin[i%16]})
+ for i := 0; i < len(password); i++ {
+ toHash += string(mixin[i%32])
}
- return ([]byte)(d.Sum(nil)) // gonative{[]byte} -> []byte
+ res := sha256.Sum256([]byte(toHash))
+ return res[:]
}
func main() {
- b := md5Crypt([]byte("1"), []byte("2"), []byte("3"))
+ b := sha256Crypt("1", "2", "3")
fmt.Println(b)
}
// Output:
-// [187 141 73 89 101 229 33 106 226 63 117 234 117 149 230 21]
+// [172 65 148 29 23 72 77 86 46 80 184 188 192 158 154 11 145 11 197 253 206 210 141 253 188 27 157 126 89 142 179 143]
diff --git a/gnovm/tests/files/math_native.gno b/gnovm/tests/files/math5.gno
similarity index 100%
rename from gnovm/tests/files/math_native.gno
rename to gnovm/tests/files/math5.gno
diff --git a/gnovm/tests/files/method16b.gno b/gnovm/tests/files/method16b.gno
index 421a9f44e7b..4f36f48aa37 100644
--- a/gnovm/tests/files/method16b.gno
+++ b/gnovm/tests/files/method16b.gno
@@ -9,7 +9,7 @@ type Cheese struct {
}
func (t *Cheese) Hello(param string) {
- fmt.Printf("%+v %+v", t, param)
+ fmt.Printf("%+v %+v\n", t, param)
}
func main() {
diff --git a/gnovm/tests/files/method18.gno b/gnovm/tests/files/method18.gno
deleted file mode 100644
index 3da9580dc02..00000000000
--- a/gnovm/tests/files/method18.gno
+++ /dev/null
@@ -1,29 +0,0 @@
-package main
-
-import (
- "compress/gzip"
- "fmt"
-
- "github.com/gnolang/gno/_test/net/http"
-)
-
-type GzipResponseWriter struct {
- http.ResponseWriter
- index int
- gw *gzip.Writer
-}
-
-type GzipResponseWriterWithCloseNotify struct {
- *GzipResponseWriter
-}
-
-func (w GzipResponseWriterWithCloseNotify) CloseNotify() <-chan bool {
- return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
-}
-
-func main() {
- fmt.Println("hello")
-}
-
-// Output:
-// hello
diff --git a/gnovm/tests/files/method20.gno b/gnovm/tests/files/method20.gno
index 7561451b699..7f3bce4c806 100644
--- a/gnovm/tests/files/method20.gno
+++ b/gnovm/tests/files/method20.gno
@@ -2,16 +2,11 @@ package main
import (
"fmt"
- "sync"
)
-type Hello struct {
- mu sync.Mutex
-}
+type Hello struct{}
func (h *Hello) Hi() string {
- h.mu.Lock()
- h.mu.Unlock()
return "hi"
}
diff --git a/gnovm/tests/files/method24.gno b/gnovm/tests/files/method24.gno
deleted file mode 100644
index 624f4397b68..00000000000
--- a/gnovm/tests/files/method24.gno
+++ /dev/null
@@ -1,33 +0,0 @@
-package main
-
-import (
- "fmt"
- "sync"
-)
-
-type Pool struct {
- P *sync.Pool
-}
-
-func (p Pool) Get() *Buffer { return &Buffer{} }
-
-func NewPool() Pool { return Pool{} }
-
-type Buffer struct {
- Bs []byte
- Pool Pool
-}
-
-var (
- _pool = NewPool()
- Get = _pool.Get
-)
-
-func main() {
- fmt.Println(_pool)
- fmt.Println(Get())
-}
-
-// Output:
-// {}
-// &{[] {}}
diff --git a/gnovm/tests/files/method25.gno b/gnovm/tests/files/method25.gno
deleted file mode 100644
index a9dff18b6fb..00000000000
--- a/gnovm/tests/files/method25.gno
+++ /dev/null
@@ -1,33 +0,0 @@
-package main
-
-import (
- "fmt"
- "sync"
-)
-
-func (p Pool) Get() *Buffer { return &Buffer{} }
-
-func NewPool() Pool { return Pool{} }
-
-type Buffer struct {
- Bs []byte
- Pool Pool
-}
-
-type Pool struct {
- P *sync.Pool
-}
-
-var (
- _pool = NewPool()
- Get = _pool.Get
-)
-
-func main() {
- fmt.Println(_pool)
- fmt.Println(Get())
-}
-
-// Output:
-// {}
-// &{[] {}}
diff --git a/gnovm/tests/files/op0.gno b/gnovm/tests/files/op0.gno
index 860f525a3bd..3c599928be6 100644
--- a/gnovm/tests/files/op0.gno
+++ b/gnovm/tests/files/op0.gno
@@ -7,7 +7,7 @@ func main() {
a = 64
b = 64
c = a * b
- fmt.Printf("c: %v %T", c, c)
+ fmt.Printf("c: %v %T\n", c, c)
}
// Output:
diff --git a/gnovm/tests/files/print0.gno b/gnovm/tests/files/print0.gno
index 43cdcf19d39..cab6a7943d1 100644
--- a/gnovm/tests/files/print0.gno
+++ b/gnovm/tests/files/print0.gno
@@ -2,6 +2,7 @@ package main
func main() {
print("hello")
+ println()
}
// Output:
diff --git a/gnovm/tests/files/range8.gno b/gnovm/tests/files/range8.gno
new file mode 100644
index 00000000000..1db05fb1977
--- /dev/null
+++ b/gnovm/tests/files/range8.gno
@@ -0,0 +1,10 @@
+package main
+
+func main() {
+ for i, v := range nil {
+ println(i, v)
+ }
+}
+
+// Error:
+// main/files/range8.gno:4:2: cannot range over nil
diff --git a/gnovm/tests/files/rune3.gno b/gnovm/tests/files/rune3.gno
new file mode 100644
index 00000000000..e848565e3a4
--- /dev/null
+++ b/gnovm/tests/files/rune3.gno
@@ -0,0 +1,10 @@
+package main
+
+const overflow = '\xff'
+
+func main() {
+ println(overflow)
+}
+
+// Output:
+// 255
diff --git a/gnovm/tests/files/sample.plugin b/gnovm/tests/files/sample.plugin
deleted file mode 100644
index cbe637b73af..00000000000
--- a/gnovm/tests/files/sample.plugin
+++ /dev/null
@@ -1,19 +0,0 @@
-package sample
-
-import (
- "fmt"
- "net/http"
-)
-
-type Sample struct{}
-
-func (s *Sample) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
- r.Header.Set("X-sample-test", "Hello")
- if next != nil {
- next(w, r)
- }
-}
-
-func Test() {
- fmt.Println("Hello from toto.Test()")
-}
diff --git a/gnovm/tests/files/secure.gi b/gnovm/tests/files/secure.gi
deleted file mode 100644
index 3ac731a85ff..00000000000
--- a/gnovm/tests/files/secure.gi
+++ /dev/null
@@ -1,33 +0,0 @@
-package main
-
-import (
- "net/http"
-
- "github.com/unrolled/secure" // or "gopkg.in/unrolled/secure.v1"
-)
-
-var myHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- w.Write([]byte("hello world"))
-})
-
-func main() {
- secureMiddleware := secure.New(secure.Options{
- AllowedHosts: []string{"example.com", "ssl.example.com"},
- HostsProxyHeaders: []string{"X-Forwarded-Host"},
- SSLRedirect: true,
- SSLHost: "ssl.example.com",
- SSLProxyHeaders: map[string]string{"X-Forwarded-Proto": "https"},
- STSSeconds: 315360000,
- STSIncludeSubdomains: true,
- STSPreload: true,
- FrameDeny: true,
- ContentTypeNosniff: true,
- BrowserXssFilter: true,
- ContentSecurityPolicy: "script-src $NONCE",
- PublicKey: `pin-sha256="base64+primary=="; pin-sha256="base64+backup=="; max-age=5184000; includeSubdomains; report-uri="https://www.example.com/hpkp-report"`,
- IsDevelopment: false,
- })
-
- app := secureMiddleware.Handler(myHandler)
- http.ListenAndServe("127.0.0.1:3000", app)
-}
diff --git a/gnovm/tests/files/slice3.gno b/gnovm/tests/files/slice3.gno
new file mode 100644
index 00000000000..1132da01420
--- /dev/null
+++ b/gnovm/tests/files/slice3.gno
@@ -0,0 +1,9 @@
+package main
+
+func main() {
+ i := []string{nil}
+ println(i)
+}
+
+// Error:
+// main/files/slice3.gno:4:7: cannot use nil as string value in array, slice literal or map literal
diff --git a/gnovm/tests/files/std0_stdlibs.gno b/gnovm/tests/files/std0.gno
similarity index 100%
rename from gnovm/tests/files/std0_stdlibs.gno
rename to gnovm/tests/files/std0.gno
diff --git a/gnovm/tests/files/std10_stdlibs.gno b/gnovm/tests/files/std10.gno
similarity index 100%
rename from gnovm/tests/files/std10_stdlibs.gno
rename to gnovm/tests/files/std10.gno
diff --git a/gnovm/tests/files/std11_stdlibs.gno b/gnovm/tests/files/std11.gno
similarity index 100%
rename from gnovm/tests/files/std11_stdlibs.gno
rename to gnovm/tests/files/std11.gno
diff --git a/gnovm/tests/files/std2_stdlibs.gno b/gnovm/tests/files/std2.gno
similarity index 100%
rename from gnovm/tests/files/std2_stdlibs.gno
rename to gnovm/tests/files/std2.gno
diff --git a/gnovm/tests/files/std3_stdlibs.gno b/gnovm/tests/files/std3.gno
similarity index 100%
rename from gnovm/tests/files/std3_stdlibs.gno
rename to gnovm/tests/files/std3.gno
diff --git a/gnovm/tests/files/std4_stdlibs.gno b/gnovm/tests/files/std4.gno
similarity index 100%
rename from gnovm/tests/files/std4_stdlibs.gno
rename to gnovm/tests/files/std4.gno
diff --git a/gnovm/tests/files/std5_stdlibs.gno b/gnovm/tests/files/std5.gno
similarity index 74%
rename from gnovm/tests/files/std5_stdlibs.gno
rename to gnovm/tests/files/std5.gno
index 4afa09da8d3..e339d7a6364 100644
--- a/gnovm/tests/files/std5_stdlibs.gno
+++ b/gnovm/tests/files/std5.gno
@@ -13,12 +13,12 @@ func main() {
// Stacktrace:
// panic: frame not found
-// callerAt(n)
+// callerAt(n)
// gonative:std.callerAt
// std.GetCallerAt(2)
-// std/native.gno:44
+// std/native.gno:45
// main()
-// main/files/std5_stdlibs.gno:10
+// main/files/std5.gno:10
// Error:
// frame not found
diff --git a/gnovm/tests/files/std6_stdlibs.gno b/gnovm/tests/files/std6.gno
similarity index 100%
rename from gnovm/tests/files/std6_stdlibs.gno
rename to gnovm/tests/files/std6.gno
diff --git a/gnovm/tests/files/std7_stdlibs.gno b/gnovm/tests/files/std7.gno
similarity index 100%
rename from gnovm/tests/files/std7_stdlibs.gno
rename to gnovm/tests/files/std7.gno
diff --git a/gnovm/tests/files/std8_stdlibs.gno b/gnovm/tests/files/std8.gno
similarity index 80%
rename from gnovm/tests/files/std8_stdlibs.gno
rename to gnovm/tests/files/std8.gno
index ab5e15bd618..ee717bf16be 100644
--- a/gnovm/tests/files/std8_stdlibs.gno
+++ b/gnovm/tests/files/std8.gno
@@ -23,16 +23,16 @@ func main() {
// Stacktrace:
// panic: frame not found
-// callerAt(n)
+// callerAt(n)
// gonative:std.callerAt
// std.GetCallerAt(4)
-// std/native.gno:44
+// std/native.gno:45
// fn()
-// main/files/std8_stdlibs.gno:16
+// main/files/std8.gno:16
// testutils.WrapCall(inner)
// gno.land/p/demo/testutils/misc.gno:5
// main()
-// main/files/std8_stdlibs.gno:21
+// main/files/std8.gno:21
// Error:
// frame not found
diff --git a/gnovm/tests/files/std9_stdlibs.gno b/gnovm/tests/files/std9.gno
similarity index 100%
rename from gnovm/tests/files/std9_stdlibs.gno
rename to gnovm/tests/files/std9.gno
diff --git a/gnovm/tests/files/stdbanker_stdlibs.gno b/gnovm/tests/files/stdbanker.gno
similarity index 100%
rename from gnovm/tests/files/stdbanker_stdlibs.gno
rename to gnovm/tests/files/stdbanker.gno
diff --git a/gnovm/tests/files/stdlibs_stdlibs.gno b/gnovm/tests/files/stdlibs.gno
similarity index 100%
rename from gnovm/tests/files/stdlibs_stdlibs.gno
rename to gnovm/tests/files/stdlibs.gno
diff --git a/gnovm/tests/files/struct13_stdlibs.gno b/gnovm/tests/files/struct13.gno
similarity index 100%
rename from gnovm/tests/files/struct13_stdlibs.gno
rename to gnovm/tests/files/struct13.gno
diff --git a/gnovm/tests/files/struct13_native.gno b/gnovm/tests/files/struct13_native.gno
deleted file mode 100644
index 85515555f50..00000000000
--- a/gnovm/tests/files/struct13_native.gno
+++ /dev/null
@@ -1,19 +0,0 @@
-package main
-
-import (
- "fmt"
-
- "github.com/gnolang/gno/_test/net/http"
-)
-
-type Fromage struct {
- http.Server
-}
-
-func main() {
- a := Fromage{}
- fmt.Println(a.Server.WriteTimeout)
-}
-
-// Output:
-// 0s
diff --git a/gnovm/tests/files/switch21.gno b/gnovm/tests/files/switch21.gno
index b13867d4512..5dd70e2a188 100644
--- a/gnovm/tests/files/switch21.gno
+++ b/gnovm/tests/files/switch21.gno
@@ -6,7 +6,7 @@ func main() {
var err error
switch v := err.(type) {
- case fmt.Formatter:
+ case interface{ Format() string }:
println("formatter")
default:
fmt.Println(v)
diff --git a/gnovm/tests/files/time0_stdlibs.gno b/gnovm/tests/files/time0.gno
similarity index 100%
rename from gnovm/tests/files/time0_stdlibs.gno
rename to gnovm/tests/files/time0.gno
diff --git a/gnovm/tests/files/time0_native.gno b/gnovm/tests/files/time0_native.gno
deleted file mode 100644
index 52c5b4d6727..00000000000
--- a/gnovm/tests/files/time0_native.gno
+++ /dev/null
@@ -1,13 +0,0 @@
-package main
-
-import (
- "fmt"
- "time"
-)
-
-func main() {
- fmt.Println(time.Now())
-}
-
-// Output:
-// 1970-01-01 00:00:00 +0000 UTC
diff --git a/gnovm/tests/files/time1_stdlibs.gno b/gnovm/tests/files/time1.gno
similarity index 100%
rename from gnovm/tests/files/time1_stdlibs.gno
rename to gnovm/tests/files/time1.gno
diff --git a/gnovm/tests/files/time11_stdlibs.gno b/gnovm/tests/files/time11.gno
similarity index 100%
rename from gnovm/tests/files/time11_stdlibs.gno
rename to gnovm/tests/files/time11.gno
diff --git a/gnovm/tests/files/time11_native.gno b/gnovm/tests/files/time11_native.gno
deleted file mode 100644
index 641ab4e6e4d..00000000000
--- a/gnovm/tests/files/time11_native.gno
+++ /dev/null
@@ -1,15 +0,0 @@
-package main
-
-import (
- "fmt"
- "time"
-)
-
-const df = time.Minute * 30
-
-func main() {
- fmt.Printf("df: %v %T\n", df, df)
-}
-
-// Output:
-// df: 30m0s time.Duration
diff --git a/gnovm/tests/files/time12_stdlibs.gno b/gnovm/tests/files/time12.gno
similarity index 100%
rename from gnovm/tests/files/time12_stdlibs.gno
rename to gnovm/tests/files/time12.gno
diff --git a/gnovm/tests/files/time12_native.gno b/gnovm/tests/files/time12_native.gno
deleted file mode 100644
index 890e49cd1f0..00000000000
--- a/gnovm/tests/files/time12_native.gno
+++ /dev/null
@@ -1,15 +0,0 @@
-package main
-
-import (
- "fmt"
- "time"
-)
-
-var twentyFourHours = time.Duration(24 * time.Hour)
-
-func main() {
- fmt.Println(twentyFourHours.Hours())
-}
-
-// Output:
-// 24
diff --git a/gnovm/tests/files/time13_stdlibs.gno b/gnovm/tests/files/time13.gno
similarity index 100%
rename from gnovm/tests/files/time13_stdlibs.gno
rename to gnovm/tests/files/time13.gno
diff --git a/gnovm/tests/files/time13_native.gno b/gnovm/tests/files/time13_native.gno
deleted file mode 100644
index a2eedafe880..00000000000
--- a/gnovm/tests/files/time13_native.gno
+++ /dev/null
@@ -1,18 +0,0 @@
-package main
-
-import (
- "fmt"
- "time"
-)
-
-var dummy = 1
-
-var t time.Time = time.Date(2007, time.November, 10, 23, 4, 5, 0, time.UTC)
-
-func main() {
- t = time.Date(2009, time.November, 10, 23, 4, 5, 0, time.UTC)
- fmt.Println(t.Clock())
-}
-
-// Output:
-// 23 4 5
diff --git a/gnovm/tests/files/time14_stdlibs.gno b/gnovm/tests/files/time14.gno
similarity index 100%
rename from gnovm/tests/files/time14_stdlibs.gno
rename to gnovm/tests/files/time14.gno
diff --git a/gnovm/tests/files/time14_native.gno b/gnovm/tests/files/time14_native.gno
deleted file mode 100644
index 9f28c57d006..00000000000
--- a/gnovm/tests/files/time14_native.gno
+++ /dev/null
@@ -1,20 +0,0 @@
-package main
-
-import (
- "fmt"
- "time"
-)
-
-var t time.Time
-
-func f() time.Time {
- time := t
- return time
-}
-
-func main() {
- fmt.Println(f())
-}
-
-// Output:
-// 0001-01-01 00:00:00 +0000 UTC
diff --git a/gnovm/tests/files/time16_native.gno b/gnovm/tests/files/time16_native.gno
deleted file mode 100644
index 4010667b41c..00000000000
--- a/gnovm/tests/files/time16_native.gno
+++ /dev/null
@@ -1,14 +0,0 @@
-package main
-
-import (
- "fmt"
- "time"
-)
-
-func main() {
- var a int64 = 2
- fmt.Println(time.Second * a)
-}
-
-// Error:
-// main/files/time16_native.gno:10:14: incompatible operands in binary expression: go:time.Duration MUL int64
diff --git a/gnovm/tests/files/time17_native.gno b/gnovm/tests/files/time17_native.gno
deleted file mode 100644
index 6733c1381cb..00000000000
--- a/gnovm/tests/files/time17_native.gno
+++ /dev/null
@@ -1,20 +0,0 @@
-package main
-
-import (
- "fmt"
- "time"
-)
-
-func main() {
- now := time.Now()
- now.In(nil)
-}
-
-// Error:
-// time: missing Location in call to Time.In
-
-// Stacktrace:
-// now.In(gonative{*time.Location})
-// gofunction:func(*time.Location) time.Time
-// main()
-// main/files/time17_native.gno:10
diff --git a/gnovm/tests/files/time1_native.gno b/gnovm/tests/files/time1_native.gno
deleted file mode 100644
index 9749d472e08..00000000000
--- a/gnovm/tests/files/time1_native.gno
+++ /dev/null
@@ -1,15 +0,0 @@
-package main
-
-import (
- "fmt"
- "time"
-)
-
-func main() {
- t := time.Date(2009, time.November, 10, 23, 4, 5, 0, time.UTC)
- m := t.Minute()
- fmt.Println(t, m)
-}
-
-// Output:
-// 2009-11-10 23:04:05 +0000 UTC 4
diff --git a/gnovm/tests/files/time2_stdlibs.gno b/gnovm/tests/files/time2.gno
similarity index 100%
rename from gnovm/tests/files/time2_stdlibs.gno
rename to gnovm/tests/files/time2.gno
diff --git a/gnovm/tests/files/time2_native.gno b/gnovm/tests/files/time2_native.gno
deleted file mode 100644
index 03ea3a2be96..00000000000
--- a/gnovm/tests/files/time2_native.gno
+++ /dev/null
@@ -1,15 +0,0 @@
-package main
-
-import (
- "fmt"
- "time"
-)
-
-func main() {
- t := time.Date(2009, time.November, 10, 23, 4, 5, 0, time.UTC)
- h, m, s := t.Clock()
- fmt.Println(h, m, s)
-}
-
-// Output:
-// 23 4 5
diff --git a/gnovm/tests/files/time3_stdlibs.gno b/gnovm/tests/files/time3.gno
similarity index 100%
rename from gnovm/tests/files/time3_stdlibs.gno
rename to gnovm/tests/files/time3.gno
diff --git a/gnovm/tests/files/time3_native.gno b/gnovm/tests/files/time3_native.gno
deleted file mode 100644
index 0848abd9a13..00000000000
--- a/gnovm/tests/files/time3_native.gno
+++ /dev/null
@@ -1,15 +0,0 @@
-package main
-
-import (
- "fmt"
- "time"
-)
-
-// FIXME related to named returns
-func main() {
- t := time.Date(2009, time.November, 10, 23, 4, 5, 0, time.UTC)
- fmt.Println(t.Clock())
-}
-
-// Output:
-// 23 4 5
diff --git a/gnovm/tests/files/time4_stdlibs.gno b/gnovm/tests/files/time4.gno
similarity index 100%
rename from gnovm/tests/files/time4_stdlibs.gno
rename to gnovm/tests/files/time4.gno
diff --git a/gnovm/tests/files/time4_native.gno b/gnovm/tests/files/time4_native.gno
deleted file mode 100644
index 3662e35cb01..00000000000
--- a/gnovm/tests/files/time4_native.gno
+++ /dev/null
@@ -1,15 +0,0 @@
-package main
-
-import (
- "fmt"
- "time"
-)
-
-func main() {
- var m time.Month
- m = 9
- fmt.Println(m)
-}
-
-// Output:
-// September
diff --git a/gnovm/tests/files/time6_stdlibs.gno b/gnovm/tests/files/time6.gno
similarity index 100%
rename from gnovm/tests/files/time6_stdlibs.gno
rename to gnovm/tests/files/time6.gno
diff --git a/gnovm/tests/files/time6_native.gno b/gnovm/tests/files/time6_native.gno
deleted file mode 100644
index c88d3ab8115..00000000000
--- a/gnovm/tests/files/time6_native.gno
+++ /dev/null
@@ -1,16 +0,0 @@
-package main
-
-import (
- "fmt"
- "time"
-)
-
-func main() {
- t := &time.Time{}
- t.UnmarshalText([]byte("1985-04-12T23:20:50.52Z"))
-
- fmt.Println(t)
-}
-
-// Output:
-// 1985-04-12 23:20:50.52 +0000 UTC
diff --git a/gnovm/tests/files/time7_stdlibs.gno b/gnovm/tests/files/time7.gno
similarity index 100%
rename from gnovm/tests/files/time7_stdlibs.gno
rename to gnovm/tests/files/time7.gno
diff --git a/gnovm/tests/files/time7_native.gno b/gnovm/tests/files/time7_native.gno
deleted file mode 100644
index 1e02defc80d..00000000000
--- a/gnovm/tests/files/time7_native.gno
+++ /dev/null
@@ -1,13 +0,0 @@
-package main
-
-import (
- "fmt"
- "time"
-)
-
-var d = 2 * time.Second
-
-func main() { fmt.Println(d) }
-
-// Output:
-// 2s
diff --git a/gnovm/tests/files/time9_stdlibs.gno b/gnovm/tests/files/time9.gno
similarity index 100%
rename from gnovm/tests/files/time9_stdlibs.gno
rename to gnovm/tests/files/time9.gno
diff --git a/gnovm/tests/files/time9_native.gno b/gnovm/tests/files/time9_native.gno
deleted file mode 100644
index a87b4560d1a..00000000000
--- a/gnovm/tests/files/time9_native.gno
+++ /dev/null
@@ -1,13 +0,0 @@
-package main
-
-import (
- "fmt"
- "time"
-)
-
-func main() {
- fmt.Println((5 * time.Minute).Seconds())
-}
-
-// Output:
-// 300
diff --git a/gnovm/tests/files/type11.gno b/gnovm/tests/files/type11.gno
index a95be962a59..28aaee838dc 100644
--- a/gnovm/tests/files/type11.gno
+++ b/gnovm/tests/files/type11.gno
@@ -1,16 +1,17 @@
package main
import (
- "compress/gzip"
"fmt"
- "sync"
+ "time"
)
-var gzipWriterPools [gzip.BestCompression - gzip.BestSpeed + 2]*sync.Pool
+const i1 = int(time.Nanosecond)
+
+var weirdArray [i1 + len("123456789")]time.Duration
func main() {
- fmt.Printf("%T\n", gzipWriterPools)
+ fmt.Printf("%T\n", weirdArray)
}
// Output:
-// [10]*sync.Pool
+// [10]int64
diff --git a/gnovm/tests/files/type2_stdlibs.gno b/gnovm/tests/files/type2.gno
similarity index 100%
rename from gnovm/tests/files/type2_stdlibs.gno
rename to gnovm/tests/files/type2.gno
diff --git a/gnovm/tests/files/type2_native.gno b/gnovm/tests/files/type2_native.gno
deleted file mode 100644
index 453fd4b64e7..00000000000
--- a/gnovm/tests/files/type2_native.gno
+++ /dev/null
@@ -1,24 +0,0 @@
-package main
-
-import (
- "fmt"
- "time"
-)
-
-type Options struct {
- debug bool
-}
-
-type T1 struct {
- opt Options
- time time.Time
-}
-
-func main() {
- t := T1{}
- t.time = time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
- fmt.Println(t.time)
-}
-
-// Output:
-// 2009-11-10 23:00:00 +0000 UTC
diff --git a/gnovm/tests/files/type40.gno b/gnovm/tests/files/type40.gno
new file mode 100644
index 00000000000..fe312e220e0
--- /dev/null
+++ b/gnovm/tests/files/type40.gno
@@ -0,0 +1,46 @@
+package main
+
+type (
+ // PrimitiveType
+ Number = int32
+ Number2 = Number
+
+ // PointerType
+ Pointer = *int32
+ Pointer2 = Pointer
+
+ // Interface
+ Interface = interface{}
+ Interface2 = Interface
+
+ // S
+ Struct = struct{Name string}
+ Struct2 = Struct
+)
+
+func fNumber(n Number) { println(n) }
+func fPointer(p Pointer) { println(*p) }
+func fInterface(i Interface) { println(i) }
+func fStruct(s Struct) { println(s.Name) }
+
+func main() {
+ var n Number2 = 5
+ fNumber(n)
+
+ var num int32 = 6
+ var p Pointer2 = &num
+ fPointer(p)
+
+ var i Interface2
+ i = 7
+ fInterface(i)
+
+ var s Struct2 = Struct2{Name: "yo"}
+ fStruct(s)
+}
+
+// Output:
+// 5
+// 6
+// 7
+// yo
diff --git a/gnovm/tests/files/type41.gno b/gnovm/tests/files/type41.gno
new file mode 100644
index 00000000000..ea1a3b1df24
--- /dev/null
+++ b/gnovm/tests/files/type41.gno
@@ -0,0 +1,9 @@
+package main
+
+type A nil
+
+func main() {
+}
+
+// Error:
+// main/files/type41.gno:3:6: nil is not a type
diff --git a/gnovm/tests/files/typeassert10.gno b/gnovm/tests/files/typeassert10.gno
new file mode 100644
index 00000000000..5876111b324
--- /dev/null
+++ b/gnovm/tests/files/typeassert10.gno
@@ -0,0 +1,17 @@
+package main
+
+type (
+ nat []word
+ word int
+)
+
+func main() {
+ var a nat
+ b := []word{0}
+ a = b
+
+ println(a)
+}
+
+// Output:
+// (slice[(0 main.word)] main.nat)
diff --git a/gnovm/tests/files/typeassert7_native.gno b/gnovm/tests/files/typeassert7.gno
similarity index 100%
rename from gnovm/tests/files/typeassert7_native.gno
rename to gnovm/tests/files/typeassert7.gno
diff --git a/gnovm/tests/files/typeassert7a_native.gno b/gnovm/tests/files/typeassert7a.gno
similarity index 87%
rename from gnovm/tests/files/typeassert7a_native.gno
rename to gnovm/tests/files/typeassert7a.gno
index cafb27b6a6b..6e0aa0e8dca 100644
--- a/gnovm/tests/files/typeassert7a_native.gno
+++ b/gnovm/tests/files/typeassert7a.gno
@@ -39,5 +39,5 @@ func main() {
// Output:
// ok
-// interface conversion: interface is nil, not gonative{io.Reader}
+// interface conversion: interface is nil, not io.Reader
// ok
diff --git a/gnovm/tests/files/types/add_assign_f0_stdlibs.gno b/gnovm/tests/files/types/add_assign_f0.gno
similarity index 71%
rename from gnovm/tests/files/types/add_assign_f0_stdlibs.gno
rename to gnovm/tests/files/types/add_assign_f0.gno
index 67c6777d085..a3df217aa7d 100644
--- a/gnovm/tests/files/types/add_assign_f0_stdlibs.gno
+++ b/gnovm/tests/files/types/add_assign_f0.gno
@@ -22,4 +22,4 @@ func main() {
}
// Error:
-// main/files/types/add_assign_f0_stdlibs.gno:20:2: invalid operation: mismatched types int and .uverse.error
+// main/files/types/add_assign_f0.gno:20:2: invalid operation: mismatched types int and .uverse.error
diff --git a/gnovm/tests/files/types/add_assign_f1_stdlibs.gno b/gnovm/tests/files/types/add_assign_f1.gno
similarity index 81%
rename from gnovm/tests/files/types/add_assign_f1_stdlibs.gno
rename to gnovm/tests/files/types/add_assign_f1.gno
index d83a66359c9..195d8ab1c1c 100644
--- a/gnovm/tests/files/types/add_assign_f1_stdlibs.gno
+++ b/gnovm/tests/files/types/add_assign_f1.gno
@@ -25,4 +25,4 @@ func main() {
}
// Error:
-// main/files/types/add_assign_f1_stdlibs.gno:21:2: invalid operation: mismatched types main.Error and .uverse.error
+// main/files/types/add_assign_f1.gno:21:2: invalid operation: mismatched types main.Error and .uverse.error
diff --git a/gnovm/tests/files/types/add_assign_f2_stdlibs.gno b/gnovm/tests/files/types/add_assign_f2.gno
similarity index 75%
rename from gnovm/tests/files/types/add_assign_f2_stdlibs.gno
rename to gnovm/tests/files/types/add_assign_f2.gno
index 8be6b3cfb7b..2603ee273d6 100644
--- a/gnovm/tests/files/types/add_assign_f2_stdlibs.gno
+++ b/gnovm/tests/files/types/add_assign_f2.gno
@@ -22,4 +22,4 @@ func main() {
}
// Error:
-// main/files/types/add_assign_f2_stdlibs.gno:20:2: operator += not defined on: InterfaceKind
+// main/files/types/add_assign_f2.gno:20:2: operator += not defined on: InterfaceKind
diff --git a/gnovm/tests/files/types/add_f0_stdlibs.gno b/gnovm/tests/files/types/add_f0.gno
similarity index 74%
rename from gnovm/tests/files/types/add_f0_stdlibs.gno
rename to gnovm/tests/files/types/add_f0.gno
index 33e0346d44f..4497efd41f1 100644
--- a/gnovm/tests/files/types/add_f0_stdlibs.gno
+++ b/gnovm/tests/files/types/add_f0.gno
@@ -20,4 +20,4 @@ func main() {
}
// Error:
-// main/files/types/add_f0_stdlibs.gno:19:10: operator + not defined on: InterfaceKind
+// main/files/types/add_f0.gno:19:10: operator + not defined on: InterfaceKind
diff --git a/gnovm/tests/files/types/add_f1_stdlibs.gno b/gnovm/tests/files/types/add_f1.gno
similarity index 75%
rename from gnovm/tests/files/types/add_f1_stdlibs.gno
rename to gnovm/tests/files/types/add_f1.gno
index e46d67e93d7..0c403aff6a4 100644
--- a/gnovm/tests/files/types/add_f1_stdlibs.gno
+++ b/gnovm/tests/files/types/add_f1.gno
@@ -20,4 +20,4 @@ func main() {
}
// Error:
-// main/files/types/add_f1_stdlibs.gno:19:10: operator + not defined on: InterfaceKind
+// main/files/types/add_f1.gno:19:10: operator + not defined on: InterfaceKind
diff --git a/gnovm/tests/files/types/and_f0_stdlibs.gno b/gnovm/tests/files/types/and_f0.gno
similarity index 74%
rename from gnovm/tests/files/types/and_f0_stdlibs.gno
rename to gnovm/tests/files/types/and_f0.gno
index e80f69332a8..2c82610b932 100644
--- a/gnovm/tests/files/types/and_f0_stdlibs.gno
+++ b/gnovm/tests/files/types/and_f0.gno
@@ -20,4 +20,4 @@ func main() {
}
// Error:
-// main/files/types/and_f0_stdlibs.gno:19:10: operator & not defined on: InterfaceKind
+// main/files/types/and_f0.gno:19:10: operator & not defined on: InterfaceKind
diff --git a/gnovm/tests/files/types/and_f1_stdlibs.gno b/gnovm/tests/files/types/and_f1.gno
similarity index 75%
rename from gnovm/tests/files/types/and_f1_stdlibs.gno
rename to gnovm/tests/files/types/and_f1.gno
index 42a6aa4b466..41a72899ee2 100644
--- a/gnovm/tests/files/types/and_f1_stdlibs.gno
+++ b/gnovm/tests/files/types/and_f1.gno
@@ -20,4 +20,4 @@ func main() {
}
// Error:
-// main/files/types/and_f1_stdlibs.gno:19:10: operator & not defined on: InterfaceKind
+// main/files/types/and_f1.gno:19:10: operator & not defined on: InterfaceKind
diff --git a/gnovm/tests/files/types/cmp_iface_0_stdlibs.gno b/gnovm/tests/files/types/cmp_iface_0.gno
similarity index 100%
rename from gnovm/tests/files/types/cmp_iface_0_stdlibs.gno
rename to gnovm/tests/files/types/cmp_iface_0.gno
diff --git a/gnovm/tests/files/types/cmp_iface_3_stdlibs.gno b/gnovm/tests/files/types/cmp_iface_3.gno
similarity index 100%
rename from gnovm/tests/files/types/cmp_iface_3_stdlibs.gno
rename to gnovm/tests/files/types/cmp_iface_3.gno
diff --git a/gnovm/tests/files/types/eql_0f8_stdlibs.gno b/gnovm/tests/files/types/cmp_iface_5.gno
similarity index 75%
rename from gnovm/tests/files/types/eql_0f8_stdlibs.gno
rename to gnovm/tests/files/types/cmp_iface_5.gno
index a6e24110432..7d748bacef3 100644
--- a/gnovm/tests/files/types/eql_0f8_stdlibs.gno
+++ b/gnovm/tests/files/types/cmp_iface_5.gno
@@ -24,4 +24,4 @@ func main() {
}
// Error:
-// main/files/types/eql_0f8_stdlibs.gno:19:5: int64 does not implement .uverse.error (missing method Error)
+// main/files/types/cmp_iface_5.gno:19:5: int64 does not implement .uverse.error (missing method Error)
diff --git a/gnovm/tests/files/types/eql_0b4_native.gno b/gnovm/tests/files/types/eql_0b4.gno
similarity index 50%
rename from gnovm/tests/files/types/eql_0b4_native.gno
rename to gnovm/tests/files/types/eql_0b4.gno
index 7c7baf01924..14a719c41d0 100644
--- a/gnovm/tests/files/types/eql_0b4_native.gno
+++ b/gnovm/tests/files/types/eql_0b4.gno
@@ -10,4 +10,4 @@ func main() {
}
// Error:
-// main/files/types/eql_0b4_native.gno:9:10: unexpected type pair: cannot use bigint as gonative{error}
+// main/files/types/eql_0b4.gno:9:10: bigint does not implement .uverse.error (missing method Error)
diff --git a/gnovm/tests/files/types/eql_0b4_stdlibs.gno b/gnovm/tests/files/types/eql_0b4_stdlibs.gno
deleted file mode 100644
index eac923c6d31..00000000000
--- a/gnovm/tests/files/types/eql_0b4_stdlibs.gno
+++ /dev/null
@@ -1,13 +0,0 @@
-package main
-
-import (
- "errors"
-)
-
-func main() {
- errCmp := errors.New("xxx")
- println(5 == errCmp)
-}
-
-// Error:
-// main/files/types/eql_0b4_stdlibs.gno:9:10: bigint does not implement .uverse.error (missing method Error)
diff --git a/gnovm/tests/files/types/eql_0f0_native.gno b/gnovm/tests/files/types/eql_0f0.gno
similarity index 75%
rename from gnovm/tests/files/types/eql_0f0_native.gno
rename to gnovm/tests/files/types/eql_0f0.gno
index e32325f5cf6..f609c0b5ced 100644
--- a/gnovm/tests/files/types/eql_0f0_native.gno
+++ b/gnovm/tests/files/types/eql_0f0.gno
@@ -25,4 +25,4 @@ func main() {
}
// Error:
-// main/files/types/eql_0f0_native.gno:19:5: unexpected type pair: cannot use bigint as gonative{error}
+// main/files/types/eql_0f0.gno:19:5: bigint does not implement .uverse.error (missing method Error)
diff --git a/gnovm/tests/files/types/eql_0f0_stdlibs.gno b/gnovm/tests/files/types/eql_0f0_stdlibs.gno
deleted file mode 100644
index 4947627cba4..00000000000
--- a/gnovm/tests/files/types/eql_0f0_stdlibs.gno
+++ /dev/null
@@ -1,28 +0,0 @@
-package main
-
-import (
- "errors"
- "strconv"
-)
-
-type Error int64
-
-func (e Error) Error() string {
- return "error: " + strconv.Itoa(int(e))
-}
-
-var errCmp = errors.New("XXXX")
-
-// special case:
-// one is interface
-func main() {
- if 1 == errCmp {
- //if errCmp == 1 {
- println("what the firetruck?")
- } else {
- println("something else")
- }
-}
-
-// Error:
-// main/files/types/eql_0f0_stdlibs.gno:19:5: bigint does not implement .uverse.error (missing method Error)
diff --git a/gnovm/tests/files/types/eql_0f1_stdlibs.gno b/gnovm/tests/files/types/eql_0f1.gno
similarity index 76%
rename from gnovm/tests/files/types/eql_0f1_stdlibs.gno
rename to gnovm/tests/files/types/eql_0f1.gno
index cab7fcfab33..fd40dfcd29b 100644
--- a/gnovm/tests/files/types/eql_0f1_stdlibs.gno
+++ b/gnovm/tests/files/types/eql_0f1.gno
@@ -25,4 +25,4 @@ func main() {
}
// Error:
-// main/files/types/eql_0f1_stdlibs.gno:19:5: int64 does not implement .uverse.error (missing method Error)
+// main/files/types/eql_0f1.gno:19:5: int64 does not implement .uverse.error (missing method Error)
diff --git a/gnovm/tests/files/types/eql_0f27_stdlibs.gno b/gnovm/tests/files/types/eql_0f27.gno
similarity index 75%
rename from gnovm/tests/files/types/eql_0f27_stdlibs.gno
rename to gnovm/tests/files/types/eql_0f27.gno
index 188153aeb51..e90bbab9ca5 100644
--- a/gnovm/tests/files/types/eql_0f27_stdlibs.gno
+++ b/gnovm/tests/files/types/eql_0f27.gno
@@ -18,4 +18,4 @@ func main() {
}
// Error:
-// main/files/types/eql_0f27_stdlibs.gno:13:5: operator > not defined on: InterfaceKind
+// main/files/types/eql_0f27.gno:13:5: operator > not defined on: InterfaceKind
diff --git a/gnovm/tests/files/types/eql_0f2b_native.gno b/gnovm/tests/files/types/eql_0f2b.gno
similarity index 80%
rename from gnovm/tests/files/types/eql_0f2b_native.gno
rename to gnovm/tests/files/types/eql_0f2b.gno
index 9de6155c5be..14a94dfde9a 100644
--- a/gnovm/tests/files/types/eql_0f2b_native.gno
+++ b/gnovm/tests/files/types/eql_0f2b.gno
@@ -25,4 +25,4 @@ func main() {
}
// Error:
-// main/files/types/eql_0f2b_native.gno:19:5: operator <= not defined on: InterfaceKind
+// main/files/types/eql_0f2b.gno:19:5: operator <= not defined on: InterfaceKind
diff --git a/gnovm/tests/files/types/eql_0f2b_stdlibs.gno b/gnovm/tests/files/types/eql_0f2b_stdlibs.gno
deleted file mode 100644
index ac3616d163d..00000000000
--- a/gnovm/tests/files/types/eql_0f2b_stdlibs.gno
+++ /dev/null
@@ -1,28 +0,0 @@
-package main
-
-import (
- "errors"
- "strconv"
-)
-
-type Error int64
-
-func (e Error) Error() string {
- return "error: " + strconv.Itoa(int(e))
-}
-
-var errCmp = errors.New("XXXX")
-
-// special case:
-// one is interface
-func main() {
- if Error(0) <= errCmp {
- //if errCmp == 1 {
- println("what the firetruck?")
- } else {
- println("something else")
- }
-}
-
-// Error:
-// main/files/types/eql_0f2b_stdlibs.gno:19:5: operator <= not defined on: InterfaceKind
diff --git a/gnovm/tests/files/types/eql_0f2c_native.gno b/gnovm/tests/files/types/eql_0f2c.gno
similarity index 80%
rename from gnovm/tests/files/types/eql_0f2c_native.gno
rename to gnovm/tests/files/types/eql_0f2c.gno
index edd5ac3f23a..3374357a145 100644
--- a/gnovm/tests/files/types/eql_0f2c_native.gno
+++ b/gnovm/tests/files/types/eql_0f2c.gno
@@ -25,4 +25,4 @@ func main() {
}
// Error:
-// main/files/types/eql_0f2c_native.gno:19:5: operator < not defined on: InterfaceKind
+// main/files/types/eql_0f2c.gno:19:5: operator < not defined on: InterfaceKind
diff --git a/gnovm/tests/files/types/eql_0f2c_stdlibs.gno b/gnovm/tests/files/types/eql_0f2c_stdlibs.gno
deleted file mode 100644
index 3a6ac3395b6..00000000000
--- a/gnovm/tests/files/types/eql_0f2c_stdlibs.gno
+++ /dev/null
@@ -1,28 +0,0 @@
-package main
-
-import (
- "errors"
- "strconv"
-)
-
-type Error int64
-
-func (e Error) Error() string {
- return "error: " + strconv.Itoa(int(e))
-}
-
-var errCmp = errors.New("XXXX")
-
-// special case:
-// one is interface
-func main() {
- if Error(0) < errCmp {
- //if errCmp == 1 {
- println("what the firetruck?")
- } else {
- println("something else")
- }
-}
-
-// Error:
-// main/files/types/eql_0f2c_stdlibs.gno:19:5: operator < not defined on: InterfaceKind
diff --git a/gnovm/tests/files/types/eql_0f40_stdlibs.gno b/gnovm/tests/files/types/eql_0f40.gno
similarity index 100%
rename from gnovm/tests/files/types/eql_0f40_stdlibs.gno
rename to gnovm/tests/files/types/eql_0f40.gno
diff --git a/gnovm/tests/files/types/eql_0f41_stdlibs.gno b/gnovm/tests/files/types/eql_0f41.gno
similarity index 77%
rename from gnovm/tests/files/types/eql_0f41_stdlibs.gno
rename to gnovm/tests/files/types/eql_0f41.gno
index be78ea6ed79..162586e2977 100644
--- a/gnovm/tests/files/types/eql_0f41_stdlibs.gno
+++ b/gnovm/tests/files/types/eql_0f41.gno
@@ -32,4 +32,4 @@ func main() {
}
// Error:
-// main/files/types/eql_0f41_stdlibs.gno:27:5: main.animal does not implement .uverse.error (missing method Error)
+// main/files/types/eql_0f41.gno:27:5: main.animal does not implement .uverse.error (missing method Error)
diff --git a/gnovm/tests/files/types/cmp_iface_5_stdlibs.gno b/gnovm/tests/files/types/eql_0f8.gno
similarity index 75%
rename from gnovm/tests/files/types/cmp_iface_5_stdlibs.gno
rename to gnovm/tests/files/types/eql_0f8.gno
index e706c74808e..17bb5f9002d 100644
--- a/gnovm/tests/files/types/cmp_iface_5_stdlibs.gno
+++ b/gnovm/tests/files/types/eql_0f8.gno
@@ -24,4 +24,4 @@ func main() {
}
// Error:
-// main/files/types/cmp_iface_5_stdlibs.gno:19:5: int64 does not implement .uverse.error (missing method Error)
+// main/files/types/eql_0f8.gno:19:5: int64 does not implement .uverse.error (missing method Error)
diff --git a/gnovm/tests/files/types/explicit_conversion_0.gno b/gnovm/tests/files/types/explicit_conversion_0.gno
index ac5e8c2eb94..be7800590e5 100644
--- a/gnovm/tests/files/types/explicit_conversion_0.gno
+++ b/gnovm/tests/files/types/explicit_conversion_0.gno
@@ -5,7 +5,7 @@ import "fmt"
func main() {
r := int(uint(1))
println(r)
- fmt.Printf("%T \n", r)
+ fmt.Printf("%T\n", r)
}
// Output:
diff --git a/gnovm/tests/files/types/explicit_conversion_1.gno b/gnovm/tests/files/types/explicit_conversion_1.gno
index 60fc7b95b64..472a430fdc5 100644
--- a/gnovm/tests/files/types/explicit_conversion_1.gno
+++ b/gnovm/tests/files/types/explicit_conversion_1.gno
@@ -6,7 +6,7 @@ import "fmt"
func main() {
r := int(uint(string("hello")))
println(r)
- fmt.Printf("%T \n", r)
+ fmt.Printf("%T\n", r)
}
// Error:
diff --git a/gnovm/tests/files/types/explicit_conversion_2.gno b/gnovm/tests/files/types/explicit_conversion_2.gno
index f932a970a9a..30da1d31154 100644
--- a/gnovm/tests/files/types/explicit_conversion_2.gno
+++ b/gnovm/tests/files/types/explicit_conversion_2.gno
@@ -6,7 +6,7 @@ func main() {
x := 1
r := uint(+x)
println(r)
- fmt.Printf("%T \n", r)
+ fmt.Printf("%T\n", r)
}
// Output:
diff --git a/gnovm/tests/files/types/or_f0_stdlibs.gno b/gnovm/tests/files/types/or_f0.gno
similarity index 75%
rename from gnovm/tests/files/types/or_f0_stdlibs.gno
rename to gnovm/tests/files/types/or_f0.gno
index 8e6fb54772a..34ffdaa87fe 100644
--- a/gnovm/tests/files/types/or_f0_stdlibs.gno
+++ b/gnovm/tests/files/types/or_f0.gno
@@ -20,4 +20,4 @@ func main() {
}
// Error:
-// main/files/types/or_f0_stdlibs.gno:19:10: operator | not defined on: InterfaceKind
+// main/files/types/or_f0.gno:19:10: operator | not defined on: InterfaceKind
diff --git a/gnovm/tests/files/types/or_f1_stdlibs.gno b/gnovm/tests/files/types/or_f1.gno
similarity index 75%
rename from gnovm/tests/files/types/or_f1_stdlibs.gno
rename to gnovm/tests/files/types/or_f1.gno
index 5013126c9fa..96a68632320 100644
--- a/gnovm/tests/files/types/or_f1_stdlibs.gno
+++ b/gnovm/tests/files/types/or_f1.gno
@@ -20,4 +20,4 @@ func main() {
}
// Error:
-// main/files/types/or_f1_stdlibs.gno:19:10: operator | not defined on: InterfaceKind
+// main/files/types/or_f1.gno:19:10: operator | not defined on: InterfaceKind
diff --git a/gnovm/tests/files/types/shift_b0.gno b/gnovm/tests/files/types/shift_b0.gno
index fa9ee4ed2a0..9717f04a56d 100644
--- a/gnovm/tests/files/types/shift_b0.gno
+++ b/gnovm/tests/files/types/shift_b0.gno
@@ -6,7 +6,7 @@ func main() {
x := 2
r := uint64(1 << x)
println(r)
- fmt.Printf("%T \n", r)
+ fmt.Printf("%T\n", r)
}
// Output:
diff --git a/gnovm/tests/files/types/shift_b1.gno b/gnovm/tests/files/types/shift_b1.gno
index 403887269c0..8f2615ba93d 100644
--- a/gnovm/tests/files/types/shift_b1.gno
+++ b/gnovm/tests/files/types/shift_b1.gno
@@ -6,7 +6,7 @@ func main() {
x := 2
r := uint64(1< r/demo/tests -> r/demo/tests/subtests",
callerFn: rtests.GetRSubtestsPrevRealm,
},
- {
- callStackAdd: " -> p/demo/tests -> r/demo/tests",
- callerFn: ptests.GetRTestsGetPrevRealm,
- },
}
println("---") // needed to have space prefixes
@@ -140,7 +137,3 @@ func printColumns(left, right string) {
// user1.gno -> r/crossrealm_test.main -> r/crossrealm_test.Exec -> r/demo/tests -> r/demo/tests/subtests = gno.land/r/demo/tests
// user1.gno -> r/crossrealm_test.main -> r/demo/tests.Exec -> r/demo/tests -> r/demo/tests/subtests = gno.land/r/demo/tests
// user1.gno -> r/crossrealm_test.main -> p/demo/tests.Exec -> r/demo/tests -> r/demo/tests/subtests = gno.land/r/demo/tests
-// user1.gno -> r/crossrealm_test.main -> p/demo/tests -> r/demo/tests = gno.land/r/crossrealm_test
-// user1.gno -> r/crossrealm_test.main -> r/crossrealm_test.Exec -> p/demo/tests -> r/demo/tests = gno.land/r/crossrealm_test
-// user1.gno -> r/crossrealm_test.main -> r/demo/tests.Exec -> p/demo/tests -> r/demo/tests = gno.land/r/crossrealm_test
-// user1.gno -> r/crossrealm_test.main -> p/demo/tests.Exec -> p/demo/tests -> r/demo/tests = gno.land/r/crossrealm_test
diff --git a/gnovm/tests/files/zrealm_crossrealm12_stdlibs.gno b/gnovm/tests/files/zrealm_crossrealm12.gno
similarity index 100%
rename from gnovm/tests/files/zrealm_crossrealm12_stdlibs.gno
rename to gnovm/tests/files/zrealm_crossrealm12.gno
diff --git a/gnovm/tests/files/zrealm_crossrealm13_stdlibs.gno b/gnovm/tests/files/zrealm_crossrealm13.gno
similarity index 100%
rename from gnovm/tests/files/zrealm_crossrealm13_stdlibs.gno
rename to gnovm/tests/files/zrealm_crossrealm13.gno
diff --git a/gnovm/tests/files/zrealm_crossrealm13a_stdlibs.gno b/gnovm/tests/files/zrealm_crossrealm13a.gno
similarity index 100%
rename from gnovm/tests/files/zrealm_crossrealm13a_stdlibs.gno
rename to gnovm/tests/files/zrealm_crossrealm13a.gno
diff --git a/gnovm/tests/files/zrealm_crossrealm15.gno b/gnovm/tests/files/zrealm_crossrealm15.gno
new file mode 100644
index 00000000000..b6f38d81abb
--- /dev/null
+++ b/gnovm/tests/files/zrealm_crossrealm15.gno
@@ -0,0 +1,27 @@
+// PKGPATH: gno.land/r/crossrealm_test
+package crossrealm_test
+
+import (
+ "std"
+
+ crossrealm "gno.land/r/demo/tests/crossrealm"
+)
+
+type fooer struct{}
+
+func (fooer) Foo() { println("hello " + std.CurrentRealm().PkgPath()) }
+
+var f *fooer
+
+func init() {
+ f = &fooer{}
+ crossrealm.SetFooer(f)
+ crossrealm.CallFooerFoo()
+}
+
+func main() {
+ print(".")
+}
+
+// Error:
+// new escaped mark has no object ID
diff --git a/gnovm/tests/files/zrealm_crossrealm16.gno b/gnovm/tests/files/zrealm_crossrealm16.gno
new file mode 100644
index 00000000000..e1b4001801c
--- /dev/null
+++ b/gnovm/tests/files/zrealm_crossrealm16.gno
@@ -0,0 +1,24 @@
+// PKGPATH: gno.land/r/crossrealm_test
+package crossrealm_test
+
+import (
+ "std"
+
+ crossrealm "gno.land/r/demo/tests/crossrealm"
+)
+
+type fooer struct{}
+
+func (fooer) Foo() { println("hello " + std.CurrentRealm().PkgPath()) }
+
+var f *fooer
+
+func main() {
+ f = &fooer{}
+ crossrealm.SetFooer(f)
+ crossrealm.CallFooerFoo()
+ print(".")
+}
+
+// Error:
+// new escaped mark has no object ID
diff --git a/gnovm/tests/files/zrealm_crossrealm17.gno b/gnovm/tests/files/zrealm_crossrealm17.gno
new file mode 100644
index 00000000000..9abb918689a
--- /dev/null
+++ b/gnovm/tests/files/zrealm_crossrealm17.gno
@@ -0,0 +1,27 @@
+// PKGPATH: gno.land/r/crossrealm_test
+package crossrealm_test
+
+import (
+ "std"
+
+ crossrealm "gno.land/r/demo/tests/crossrealm"
+)
+
+type container struct{ *fooer }
+
+func (container) Foo() { println("hello container " + std.CurrentRealm().PkgPath()) }
+
+type fooer struct{}
+
+var f *fooer
+
+func main() {
+ f = &fooer{}
+ c := &container{f}
+ crossrealm.SetFooer(c)
+ crossrealm.CallFooerFoo()
+ print(".")
+}
+
+// Error:
+// new escaped mark has no object ID
diff --git a/gnovm/tests/files/zrealm_crossrealm18.gno b/gnovm/tests/files/zrealm_crossrealm18.gno
new file mode 100644
index 00000000000..f7a318ed3a0
--- /dev/null
+++ b/gnovm/tests/files/zrealm_crossrealm18.gno
@@ -0,0 +1,35 @@
+// PKGPATH: gno.land/r/crossrealm_test
+package crossrealm_test
+
+import (
+ "std"
+
+ crossrealm "gno.land/r/demo/tests/crossrealm"
+)
+
+type fooer struct{}
+
+func (fooer) Foo() { println("hello " + std.CurrentRealm().PkgPath()) }
+
+var f crossrealm.Fooer = crossrealm.SetFooer(&fooer{})
+
+func init() {
+ crossrealm.CallFooerFoo()
+}
+
+func main() {
+ crossrealm.CallFooerFoo()
+ print(".")
+}
+
+// Output:
+// hello gno.land/r/crossrealm_test
+// hello gno.land/r/crossrealm_test
+// .
+
+// Error:
+
+// Realm:
+// switchrealm["gno.land/r/crossrealm_test"]
+// switchrealm["gno.land/r/demo/tests/crossrealm"]
+// switchrealm["gno.land/r/crossrealm_test"]
diff --git a/gnovm/tests/files/zrealm_crossrealm19_stdlibs.gno b/gnovm/tests/files/zrealm_crossrealm19_stdlibs.gno
new file mode 100644
index 00000000000..a3b864755fd
--- /dev/null
+++ b/gnovm/tests/files/zrealm_crossrealm19_stdlibs.gno
@@ -0,0 +1,32 @@
+// PKGPATH: gno.land/r/crossrealm_test
+package crossrealm_test
+
+import (
+ "std"
+
+ crossrealm "gno.land/r/demo/tests/crossrealm"
+)
+
+type fooer struct {
+ s string
+}
+
+func (f *fooer) Foo() {
+ f.s = "B"
+ println("hello " + f.s + " " + std.CurrentRealm().PkgPath())
+}
+
+var f *fooer
+
+func init() {
+ f = &fooer{s: "A"}
+ crossrealm.SetFooer(f)
+ crossrealm.CallFooerFoo()
+}
+
+func main() {
+ print(".")
+}
+
+// Error:
+// new escaped mark has no object ID
diff --git a/gnovm/tests/files/zrealm_crossrealm2_stdlibs.gno b/gnovm/tests/files/zrealm_crossrealm2.gno
similarity index 100%
rename from gnovm/tests/files/zrealm_crossrealm2_stdlibs.gno
rename to gnovm/tests/files/zrealm_crossrealm2.gno
diff --git a/gnovm/tests/files/zrealm_crossrealm20.gno b/gnovm/tests/files/zrealm_crossrealm20.gno
new file mode 100644
index 00000000000..32fac2e95b9
--- /dev/null
+++ b/gnovm/tests/files/zrealm_crossrealm20.gno
@@ -0,0 +1,43 @@
+// PKGPATH: gno.land/r/crossrealm_test
+package crossrealm_test
+
+import (
+ "std"
+
+ crossrealm "gno.land/r/demo/tests/crossrealm"
+)
+
+type fooer struct {
+ s string
+}
+
+func (f *fooer) Foo() {
+ f.s = "B"
+ println("hello " + f.s + " " + std.CurrentRealm().PkgPath())
+}
+
+var f *fooer
+
+func init() {
+ f = &fooer{s: "A"}
+ fg := func() crossrealm.Fooer { return f }
+ crossrealm.SetFooerGetter(fg)
+ crossrealm.CallFooerGetterFoo()
+ f.s = "C"
+ crossrealm.CallFooerGetterFoo()
+}
+
+func main() {
+ print(".")
+}
+
+// Output:
+// hello B gno.land/r/crossrealm_test
+// hello B gno.land/r/crossrealm_test
+// .
+
+// Realm:
+// switchrealm["gno.land/r/crossrealm_test"]
+
+// Error:
+//
diff --git a/gnovm/tests/files/zrealm_crossrealm21.gno b/gnovm/tests/files/zrealm_crossrealm21.gno
new file mode 100644
index 00000000000..634fbea13c8
--- /dev/null
+++ b/gnovm/tests/files/zrealm_crossrealm21.gno
@@ -0,0 +1,723 @@
+// PKGPATH: gno.land/r/crossrealm_test
+package crossrealm_test
+
+import (
+ "std"
+
+ "gno.land/r/demo/tests/crossrealm"
+ "gno.land/r/demo/tests/crossrealm_b"
+)
+
+func main() {
+ f := crossrealm_b.Fooer
+ crossrealm.SetFooer(f)
+ crossrealm.CallFooerFoo()
+ f.SetS("B")
+ crossrealm.CallFooerFoo()
+ print(".")
+}
+
+// Output:
+// hello A cur=gno.land/r/demo/tests/crossrealm_b prev=gno.land/r/demo/tests/crossrealm
+// hello B cur=gno.land/r/demo/tests/crossrealm_b prev=gno.land/r/demo/tests/crossrealm
+// .
+
+// Realm:
+// switchrealm["gno.land/r/demo/tests/crossrealm"]
+// u[1712ac7adcfdc8e58a67e5615e20fb312394c4df:2]={
+// "Blank": {},
+// "ObjectInfo": {
+// "ID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:2",
+// "IsEscaped": true,
+// "ModTime": "5",
+// "RefCount": "2"
+// },
+// "Parent": null,
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "0",
+// "File": "",
+// "Line": "0",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Values": [
+// {
+// "T": {
+// "@type": "/gno.TypeType"
+// },
+// "V": {
+// "@type": "/gno.TypeValue",
+// "Type": {
+// "@type": "/gno.DeclaredType",
+// "Base": {
+// "@type": "/gno.StructType",
+// "Fields": [
+// {
+// "Embedded": false,
+// "Name": "A",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.PrimitiveType",
+// "value": "32"
+// }
+// }
+// ],
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// },
+// "Methods": [
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [
+// {
+// "Embedded": false,
+// "Name": "ls",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.PointerType",
+// "Elt": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.LocalStruct"
+// }
+// }
+// }
+// ],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.PrimitiveType",
+// "value": "16"
+// }
+// }
+// ]
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": null,
+// "FileName": "crossrealm.gno",
+// "IsMethod": true,
+// "Name": "String",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "12",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [
+// {
+// "Embedded": false,
+// "Name": "ls",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.PointerType",
+// "Elt": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.LocalStruct"
+// }
+// }
+// }
+// ],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.PrimitiveType",
+// "value": "16"
+// }
+// }
+// ]
+// }
+// }
+// }
+// ],
+// "Name": "LocalStruct",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.PointerType",
+// "Elt": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.LocalStruct"
+// }
+// },
+// "V": {
+// "@type": "/gno.PointerValue",
+// "Base": {
+// "@type": "/gno.RefValue",
+// "Hash": "a75fdb389fedfcbbaa7f446d528c1e149726347c",
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:4"
+// },
+// "Index": "0",
+// "TV": null
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": []
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "init.2",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "19",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": []
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.PointerType",
+// "Elt": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/p/demo/tests/p_crossrealm.Container"
+// }
+// }
+// }
+// ]
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "Make1",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "24",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.PointerType",
+// "Elt": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/p/demo/tests/p_crossrealm.Container"
+// }
+// }
+// }
+// ]
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.TypeType"
+// },
+// "V": {
+// "@type": "/gno.TypeValue",
+// "Type": {
+// "@type": "/gno.DeclaredType",
+// "Base": {
+// "@type": "/gno.InterfaceType",
+// "Generic": "",
+// "Methods": [
+// {
+// "Embedded": false,
+// "Name": "Foo",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": []
+// }
+// }
+// ],
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// },
+// "Methods": [],
+// "Name": "Fooer",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.PointerType",
+// "Elt": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm_b.fooer"
+// }
+// },
+// "V": {
+// "@type": "/gno.PointerValue",
+// "Base": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "0edc46caf30c00efd87b6c272673239eafbd051e:3"
+// },
+// "Index": "0",
+// "TV": null
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [
+// {
+// "Embedded": false,
+// "Name": "f",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ]
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "SetFooer",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "35",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [
+// {
+// "Embedded": false,
+// "Name": "f",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ]
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ]
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "GetFooer",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "40",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ]
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": []
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "CallFooerFoo",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "42",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": []
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.TypeType"
+// },
+// "V": {
+// "@type": "/gno.TypeValue",
+// "Type": {
+// "@type": "/gno.DeclaredType",
+// "Base": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ]
+// },
+// "Methods": [],
+// "Name": "FooerGetter",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter"
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [
+// {
+// "Embedded": false,
+// "Name": "fg",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter"
+// }
+// }
+// ],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter"
+// }
+// }
+// ]
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "SetFooerGetter",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "48",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [
+// {
+// "Embedded": false,
+// "Name": "fg",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter"
+// }
+// }
+// ],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter"
+// }
+// }
+// ]
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter"
+// }
+// }
+// ]
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "GetFooerGetter",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "53",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter"
+// }
+// }
+// ]
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": []
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "CallFooerGetterFoo",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "57",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": []
+// }
+// }
+// }
+// ]
+// }
+// switchrealm["gno.land/r/demo/tests/crossrealm_b"]
+// switchrealm["gno.land/r/demo/tests/crossrealm"]
+// switchrealm["gno.land/r/demo/tests/crossrealm_b"]
+// u[0edc46caf30c00efd87b6c272673239eafbd051e:4]={
+// "Fields": [
+// {
+// "T": {
+// "@type": "/gno.PrimitiveType",
+// "value": "16"
+// },
+// "V": {
+// "@type": "/gno.StringValue",
+// "value": "B"
+// }
+// }
+// ],
+// "ObjectInfo": {
+// "ID": "0edc46caf30c00efd87b6c272673239eafbd051e:4",
+// "ModTime": "5",
+// "OwnerID": "0edc46caf30c00efd87b6c272673239eafbd051e:3",
+// "RefCount": "1"
+// }
+// }
+// switchrealm["gno.land/r/demo/tests/crossrealm_b"]
+// switchrealm["gno.land/r/demo/tests/crossrealm"]
+// switchrealm["gno.land/r/crossrealm_test"]
+
+// Error:
+//
diff --git a/gnovm/tests/files/zrealm_crossrealm22.gno b/gnovm/tests/files/zrealm_crossrealm22.gno
new file mode 100644
index 00000000000..18985f7719d
--- /dev/null
+++ b/gnovm/tests/files/zrealm_crossrealm22.gno
@@ -0,0 +1,2282 @@
+// PKGPATH: gno.land/r/crossrealm_test
+package crossrealm_test
+
+import (
+ "std"
+
+ "gno.land/r/demo/tests/crossrealm"
+ "gno.land/r/demo/tests/crossrealm_b"
+)
+
+func main() {
+ f := crossrealm_b.Fooer
+ crossrealm.SetFooerGetter(func() crossrealm.Fooer { return f })
+ crossrealm.CallFooerGetterFoo()
+ f.SetS("B")
+ crossrealm.CallFooerGetterFoo()
+ println(".")
+
+ f.SetS("C")
+ crossrealm.SetFooerGetter(crossrealm_b.FooerGetter)
+ crossrealm.CallFooerGetterFoo()
+ println(".")
+
+ f.SetS("D")
+ crossrealm.SetFooerGetter(crossrealm_b.FooerGetterBuilder())
+ crossrealm.CallFooerGetterFoo()
+ println(".")
+}
+
+// Output:
+// hello A cur=gno.land/r/demo/tests/crossrealm_b prev=gno.land/r/demo/tests/crossrealm
+// hello B cur=gno.land/r/demo/tests/crossrealm_b prev=gno.land/r/demo/tests/crossrealm
+// .
+// hello C cur=gno.land/r/demo/tests/crossrealm_b prev=gno.land/r/demo/tests/crossrealm
+// .
+// hello D cur=gno.land/r/demo/tests/crossrealm_b prev=gno.land/r/demo/tests/crossrealm
+// .
+
+// Realm:
+// switchrealm["gno.land/r/demo/tests/crossrealm"]
+// c[1712ac7adcfdc8e58a67e5615e20fb312394c4df:6]={
+// "Blank": {},
+// "ObjectInfo": {
+// "ID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:6",
+// "ModTime": "0",
+// "OwnerID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:2",
+// "RefCount": "1"
+// },
+// "Parent": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "f5a516808f8976c33939133293d598ce3bca4e8d:3"
+// },
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "files/zrealm_crossrealm22.gno",
+// "Line": "11",
+// "PkgPath": "gno.land/r/crossrealm_test"
+// }
+// },
+// "Values": [
+// {
+// "T": {
+// "@type": "/gno.PointerType",
+// "Elt": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm_b.fooer"
+// }
+// },
+// "V": {
+// "@type": "/gno.PointerValue",
+// "Base": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "0edc46caf30c00efd87b6c272673239eafbd051e:3"
+// },
+// "Index": "0",
+// "TV": null
+// }
+// }
+// ]
+// }
+// u[1712ac7adcfdc8e58a67e5615e20fb312394c4df:2]={
+// "Blank": {},
+// "ObjectInfo": {
+// "ID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:2",
+// "IsEscaped": true,
+// "ModTime": "5",
+// "RefCount": "2"
+// },
+// "Parent": null,
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "0",
+// "File": "",
+// "Line": "0",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Values": [
+// {
+// "T": {
+// "@type": "/gno.TypeType"
+// },
+// "V": {
+// "@type": "/gno.TypeValue",
+// "Type": {
+// "@type": "/gno.DeclaredType",
+// "Base": {
+// "@type": "/gno.StructType",
+// "Fields": [
+// {
+// "Embedded": false,
+// "Name": "A",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.PrimitiveType",
+// "value": "32"
+// }
+// }
+// ],
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// },
+// "Methods": [
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [
+// {
+// "Embedded": false,
+// "Name": "ls",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.PointerType",
+// "Elt": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.LocalStruct"
+// }
+// }
+// }
+// ],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.PrimitiveType",
+// "value": "16"
+// }
+// }
+// ]
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": null,
+// "FileName": "crossrealm.gno",
+// "IsMethod": true,
+// "Name": "String",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "12",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [
+// {
+// "Embedded": false,
+// "Name": "ls",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.PointerType",
+// "Elt": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.LocalStruct"
+// }
+// }
+// }
+// ],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.PrimitiveType",
+// "value": "16"
+// }
+// }
+// ]
+// }
+// }
+// }
+// ],
+// "Name": "LocalStruct",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.PointerType",
+// "Elt": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.LocalStruct"
+// }
+// },
+// "V": {
+// "@type": "/gno.PointerValue",
+// "Base": {
+// "@type": "/gno.RefValue",
+// "Hash": "a75fdb389fedfcbbaa7f446d528c1e149726347c",
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:4"
+// },
+// "Index": "0",
+// "TV": null
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": []
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "init.2",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "19",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": []
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.PointerType",
+// "Elt": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/p/demo/tests/p_crossrealm.Container"
+// }
+// }
+// }
+// ]
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "Make1",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "24",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.PointerType",
+// "Elt": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/p/demo/tests/p_crossrealm.Container"
+// }
+// }
+// }
+// ]
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.TypeType"
+// },
+// "V": {
+// "@type": "/gno.TypeValue",
+// "Type": {
+// "@type": "/gno.DeclaredType",
+// "Base": {
+// "@type": "/gno.InterfaceType",
+// "Generic": "",
+// "Methods": [
+// {
+// "Embedded": false,
+// "Name": "Foo",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": []
+// }
+// }
+// ],
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// },
+// "Methods": [],
+// "Name": "Fooer",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [
+// {
+// "Embedded": false,
+// "Name": "f",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ]
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "SetFooer",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "35",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [
+// {
+// "Embedded": false,
+// "Name": "f",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ]
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ]
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "GetFooer",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "40",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ]
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": []
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "CallFooerFoo",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "42",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": []
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.TypeType"
+// },
+// "V": {
+// "@type": "/gno.TypeValue",
+// "Type": {
+// "@type": "/gno.DeclaredType",
+// "Base": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ]
+// },
+// "Methods": [],
+// "Name": "FooerGetter",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter"
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Hash": "23de97a577d573252d00394ce9b71c24b0646546",
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:6"
+// },
+// "FileName": "",
+// "IsMethod": false,
+// "Name": "",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/crossrealm_test",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "28",
+// "File": "files/zrealm_crossrealm22.gno",
+// "Line": "13",
+// "PkgPath": "gno.land/r/crossrealm_test"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ]
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [
+// {
+// "Embedded": false,
+// "Name": "fg",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter"
+// }
+// }
+// ],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter"
+// }
+// }
+// ]
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "SetFooerGetter",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "48",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [
+// {
+// "Embedded": false,
+// "Name": "fg",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter"
+// }
+// }
+// ],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter"
+// }
+// }
+// ]
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter"
+// }
+// }
+// ]
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "GetFooerGetter",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "53",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter"
+// }
+// }
+// ]
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": []
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "CallFooerGetterFoo",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "57",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": []
+// }
+// }
+// }
+// ]
+// }
+// switchrealm["gno.land/r/crossrealm_test"]
+// switchrealm["gno.land/r/demo/tests/crossrealm_b"]
+// switchrealm["gno.land/r/demo/tests/crossrealm"]
+// switchrealm["gno.land/r/demo/tests/crossrealm_b"]
+// u[0edc46caf30c00efd87b6c272673239eafbd051e:4]={
+// "Fields": [
+// {
+// "T": {
+// "@type": "/gno.PrimitiveType",
+// "value": "16"
+// },
+// "V": {
+// "@type": "/gno.StringValue",
+// "value": "B"
+// }
+// }
+// ],
+// "ObjectInfo": {
+// "ID": "0edc46caf30c00efd87b6c272673239eafbd051e:4",
+// "ModTime": "5",
+// "OwnerID": "0edc46caf30c00efd87b6c272673239eafbd051e:3",
+// "RefCount": "1"
+// }
+// }
+// switchrealm["gno.land/r/crossrealm_test"]
+// switchrealm["gno.land/r/demo/tests/crossrealm_b"]
+// switchrealm["gno.land/r/demo/tests/crossrealm"]
+// switchrealm["gno.land/r/demo/tests/crossrealm_b"]
+// u[0edc46caf30c00efd87b6c272673239eafbd051e:4]={
+// "Fields": [
+// {
+// "T": {
+// "@type": "/gno.PrimitiveType",
+// "value": "16"
+// },
+// "V": {
+// "@type": "/gno.StringValue",
+// "value": "C"
+// }
+// }
+// ],
+// "ObjectInfo": {
+// "ID": "0edc46caf30c00efd87b6c272673239eafbd051e:4",
+// "ModTime": "5",
+// "OwnerID": "0edc46caf30c00efd87b6c272673239eafbd051e:3",
+// "RefCount": "1"
+// }
+// }
+// switchrealm["gno.land/r/demo/tests/crossrealm"]
+// u[1712ac7adcfdc8e58a67e5615e20fb312394c4df:2]={
+// "Blank": {},
+// "ObjectInfo": {
+// "ID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:2",
+// "IsEscaped": true,
+// "ModTime": "6",
+// "RefCount": "2"
+// },
+// "Parent": null,
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "0",
+// "File": "",
+// "Line": "0",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Values": [
+// {
+// "T": {
+// "@type": "/gno.TypeType"
+// },
+// "V": {
+// "@type": "/gno.TypeValue",
+// "Type": {
+// "@type": "/gno.DeclaredType",
+// "Base": {
+// "@type": "/gno.StructType",
+// "Fields": [
+// {
+// "Embedded": false,
+// "Name": "A",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.PrimitiveType",
+// "value": "32"
+// }
+// }
+// ],
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// },
+// "Methods": [
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [
+// {
+// "Embedded": false,
+// "Name": "ls",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.PointerType",
+// "Elt": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.LocalStruct"
+// }
+// }
+// }
+// ],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.PrimitiveType",
+// "value": "16"
+// }
+// }
+// ]
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": null,
+// "FileName": "crossrealm.gno",
+// "IsMethod": true,
+// "Name": "String",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "12",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [
+// {
+// "Embedded": false,
+// "Name": "ls",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.PointerType",
+// "Elt": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.LocalStruct"
+// }
+// }
+// }
+// ],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.PrimitiveType",
+// "value": "16"
+// }
+// }
+// ]
+// }
+// }
+// }
+// ],
+// "Name": "LocalStruct",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.PointerType",
+// "Elt": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.LocalStruct"
+// }
+// },
+// "V": {
+// "@type": "/gno.PointerValue",
+// "Base": {
+// "@type": "/gno.RefValue",
+// "Hash": "a75fdb389fedfcbbaa7f446d528c1e149726347c",
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:4"
+// },
+// "Index": "0",
+// "TV": null
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": []
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "init.2",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "19",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": []
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.PointerType",
+// "Elt": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/p/demo/tests/p_crossrealm.Container"
+// }
+// }
+// }
+// ]
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "Make1",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "24",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.PointerType",
+// "Elt": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/p/demo/tests/p_crossrealm.Container"
+// }
+// }
+// }
+// ]
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.TypeType"
+// },
+// "V": {
+// "@type": "/gno.TypeValue",
+// "Type": {
+// "@type": "/gno.DeclaredType",
+// "Base": {
+// "@type": "/gno.InterfaceType",
+// "Generic": "",
+// "Methods": [
+// {
+// "Embedded": false,
+// "Name": "Foo",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": []
+// }
+// }
+// ],
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// },
+// "Methods": [],
+// "Name": "Fooer",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [
+// {
+// "Embedded": false,
+// "Name": "f",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ]
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "SetFooer",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "35",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [
+// {
+// "Embedded": false,
+// "Name": "f",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ]
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ]
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "GetFooer",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "40",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ]
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": []
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "CallFooerFoo",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "42",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": []
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.TypeType"
+// },
+// "V": {
+// "@type": "/gno.TypeValue",
+// "Type": {
+// "@type": "/gno.DeclaredType",
+// "Base": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ]
+// },
+// "Methods": [],
+// "Name": "FooerGetter",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter"
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "0edc46caf30c00efd87b6c272673239eafbd051e:5"
+// },
+// "FileName": "",
+// "IsMethod": false,
+// "Name": "",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm_b",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "23",
+// "File": "crossrealm.gno",
+// "Line": "23",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm_b"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ]
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [
+// {
+// "Embedded": false,
+// "Name": "fg",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter"
+// }
+// }
+// ],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter"
+// }
+// }
+// ]
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "SetFooerGetter",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "48",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [
+// {
+// "Embedded": false,
+// "Name": "fg",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter"
+// }
+// }
+// ],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter"
+// }
+// }
+// ]
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter"
+// }
+// }
+// ]
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "GetFooerGetter",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "53",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter"
+// }
+// }
+// ]
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": []
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "CallFooerGetterFoo",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "57",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": []
+// }
+// }
+// }
+// ]
+// }
+// d[1712ac7adcfdc8e58a67e5615e20fb312394c4df:6]
+// switchrealm["gno.land/r/demo/tests/crossrealm_b"]
+// switchrealm["gno.land/r/demo/tests/crossrealm_b"]
+// switchrealm["gno.land/r/demo/tests/crossrealm"]
+// switchrealm["gno.land/r/demo/tests/crossrealm_b"]
+// u[0edc46caf30c00efd87b6c272673239eafbd051e:4]={
+// "Fields": [
+// {
+// "T": {
+// "@type": "/gno.PrimitiveType",
+// "value": "16"
+// },
+// "V": {
+// "@type": "/gno.StringValue",
+// "value": "D"
+// }
+// }
+// ],
+// "ObjectInfo": {
+// "ID": "0edc46caf30c00efd87b6c272673239eafbd051e:4",
+// "ModTime": "5",
+// "OwnerID": "0edc46caf30c00efd87b6c272673239eafbd051e:3",
+// "RefCount": "1"
+// }
+// }
+// switchrealm["gno.land/r/demo/tests/crossrealm_b"]
+// switchrealm["gno.land/r/demo/tests/crossrealm"]
+// c[1712ac7adcfdc8e58a67e5615e20fb312394c4df:7]={
+// "Blank": {},
+// "ObjectInfo": {
+// "ID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:7",
+// "ModTime": "0",
+// "OwnerID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:2",
+// "RefCount": "1"
+// },
+// "Parent": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "0edc46caf30c00efd87b6c272673239eafbd051e:5"
+// },
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "23",
+// "File": "crossrealm.gno",
+// "Line": "24",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm_b"
+// }
+// },
+// "Values": [
+// {
+// "T": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter"
+// }
+// }
+// ]
+// }
+// u[1712ac7adcfdc8e58a67e5615e20fb312394c4df:2]={
+// "Blank": {},
+// "ObjectInfo": {
+// "ID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:2",
+// "IsEscaped": true,
+// "ModTime": "6",
+// "RefCount": "2"
+// },
+// "Parent": null,
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "0",
+// "File": "",
+// "Line": "0",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Values": [
+// {
+// "T": {
+// "@type": "/gno.TypeType"
+// },
+// "V": {
+// "@type": "/gno.TypeValue",
+// "Type": {
+// "@type": "/gno.DeclaredType",
+// "Base": {
+// "@type": "/gno.StructType",
+// "Fields": [
+// {
+// "Embedded": false,
+// "Name": "A",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.PrimitiveType",
+// "value": "32"
+// }
+// }
+// ],
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// },
+// "Methods": [
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [
+// {
+// "Embedded": false,
+// "Name": "ls",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.PointerType",
+// "Elt": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.LocalStruct"
+// }
+// }
+// }
+// ],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.PrimitiveType",
+// "value": "16"
+// }
+// }
+// ]
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": null,
+// "FileName": "crossrealm.gno",
+// "IsMethod": true,
+// "Name": "String",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "12",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [
+// {
+// "Embedded": false,
+// "Name": "ls",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.PointerType",
+// "Elt": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.LocalStruct"
+// }
+// }
+// }
+// ],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.PrimitiveType",
+// "value": "16"
+// }
+// }
+// ]
+// }
+// }
+// }
+// ],
+// "Name": "LocalStruct",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.PointerType",
+// "Elt": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.LocalStruct"
+// }
+// },
+// "V": {
+// "@type": "/gno.PointerValue",
+// "Base": {
+// "@type": "/gno.RefValue",
+// "Hash": "a75fdb389fedfcbbaa7f446d528c1e149726347c",
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:4"
+// },
+// "Index": "0",
+// "TV": null
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": []
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "init.2",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "19",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": []
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.PointerType",
+// "Elt": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/p/demo/tests/p_crossrealm.Container"
+// }
+// }
+// }
+// ]
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "Make1",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "24",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.PointerType",
+// "Elt": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/p/demo/tests/p_crossrealm.Container"
+// }
+// }
+// }
+// ]
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.TypeType"
+// },
+// "V": {
+// "@type": "/gno.TypeValue",
+// "Type": {
+// "@type": "/gno.DeclaredType",
+// "Base": {
+// "@type": "/gno.InterfaceType",
+// "Generic": "",
+// "Methods": [
+// {
+// "Embedded": false,
+// "Name": "Foo",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": []
+// }
+// }
+// ],
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// },
+// "Methods": [],
+// "Name": "Fooer",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [
+// {
+// "Embedded": false,
+// "Name": "f",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ]
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "SetFooer",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "35",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [
+// {
+// "Embedded": false,
+// "Name": "f",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ]
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ]
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "GetFooer",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "40",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ]
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": []
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "CallFooerFoo",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "42",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": []
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.TypeType"
+// },
+// "V": {
+// "@type": "/gno.TypeValue",
+// "Type": {
+// "@type": "/gno.DeclaredType",
+// "Base": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ]
+// },
+// "Methods": [],
+// "Name": "FooerGetter",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter"
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Hash": "89352b352826005a86eee78e6c832b43ae0ab6a6",
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:7"
+// },
+// "FileName": "",
+// "IsMethod": false,
+// "Name": "",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm_b",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "62",
+// "File": "crossrealm.gno",
+// "Line": "24",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm_b"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.Fooer"
+// }
+// }
+// ]
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [
+// {
+// "Embedded": false,
+// "Name": "fg",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter"
+// }
+// }
+// ],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter"
+// }
+// }
+// ]
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "SetFooerGetter",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "48",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [
+// {
+// "Embedded": false,
+// "Name": "fg",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter"
+// }
+// }
+// ],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter"
+// }
+// }
+// ]
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter"
+// }
+// }
+// ]
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "GetFooerGetter",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "53",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": [
+// {
+// "Embedded": false,
+// "Name": "",
+// "Tag": "",
+// "Type": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter"
+// }
+// }
+// ]
+// }
+// }
+// },
+// {
+// "T": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": []
+// },
+// "V": {
+// "@type": "/gno.FuncValue",
+// "Closure": {
+// "@type": "/gno.RefValue",
+// "Escaped": true,
+// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3"
+// },
+// "FileName": "crossrealm.gno",
+// "IsMethod": false,
+// "Name": "CallFooerGetterFoo",
+// "NativeName": "",
+// "NativePkg": "",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm",
+// "Source": {
+// "@type": "/gno.RefNode",
+// "BlockNode": null,
+// "Location": {
+// "Column": "1",
+// "File": "crossrealm.gno",
+// "Line": "57",
+// "PkgPath": "gno.land/r/demo/tests/crossrealm"
+// }
+// },
+// "Type": {
+// "@type": "/gno.FuncType",
+// "Params": [],
+// "Results": []
+// }
+// }
+// }
+// ]
+// }
+// switchrealm["gno.land/r/demo/tests/crossrealm_b"]
+// switchrealm["gno.land/r/demo/tests/crossrealm_b"]
+// switchrealm["gno.land/r/demo/tests/crossrealm"]
+// switchrealm["gno.land/r/crossrealm_test"]
+
+// Error:
+//
diff --git a/gnovm/tests/files/zrealm_crossrealm3_stdlibs.gno b/gnovm/tests/files/zrealm_crossrealm3.gno
similarity index 100%
rename from gnovm/tests/files/zrealm_crossrealm3_stdlibs.gno
rename to gnovm/tests/files/zrealm_crossrealm3.gno
diff --git a/gnovm/tests/files/zrealm_crossrealm4.gno b/gnovm/tests/files/zrealm_crossrealm4.gno
new file mode 100644
index 00000000000..ed73b7ad6bb
--- /dev/null
+++ b/gnovm/tests/files/zrealm_crossrealm4.gno
@@ -0,0 +1,22 @@
+// PKGPATH: gno.land/r/crossrealm_test
+package crossrealm_test
+
+import (
+ "gno.land/r/demo/tests"
+)
+
+// NOTE: it is valid to persist a pointer to an external object
+var somevalue *tests.TestRealmObject
+
+func init() {
+ somevalue = &tests.TestRealmObjectValue
+}
+
+func main() {
+ // NOTE: it is valid to modify it using the external realm function.
+ somevalue.Modify()
+ println(somevalue)
+}
+
+// Output:
+// &(struct{("_modified" string)} gno.land/r/demo/tests.TestRealmObject)
diff --git a/gnovm/tests/files/zrealm_crossrealm4_stdlibs.gno b/gnovm/tests/files/zrealm_crossrealm5.gno
similarity index 69%
rename from gnovm/tests/files/zrealm_crossrealm4_stdlibs.gno
rename to gnovm/tests/files/zrealm_crossrealm5.gno
index 6aa9c5247d8..c7560b21463 100644
--- a/gnovm/tests/files/zrealm_crossrealm4_stdlibs.gno
+++ b/gnovm/tests/files/zrealm_crossrealm5.gno
@@ -6,15 +6,15 @@ import (
)
// NOTE: it is valid to persist external realm types.
-var somevalue tests.TestRealmObject
+var somevalue *tests.TestRealmObject
func init() {
- somevalue.Field = "test"
+ somevalue = &tests.TestRealmObjectValue
}
func main() {
- // NOTE: but it is invalid to modify it using an external realm function.
- somevalue.Modify()
+ // NOTE: but it is invalid to modify it directly.
+ somevalue.Field = "test"
println(somevalue)
}
diff --git a/gnovm/tests/files/zrealm_crossrealm5_stdlibs.gno b/gnovm/tests/files/zrealm_crossrealm5_stdlibs.gno
deleted file mode 100644
index 6aa9c5247d8..00000000000
--- a/gnovm/tests/files/zrealm_crossrealm5_stdlibs.gno
+++ /dev/null
@@ -1,22 +0,0 @@
-// PKGPATH: gno.land/r/crossrealm_test
-package crossrealm_test
-
-import (
- "gno.land/r/demo/tests"
-)
-
-// NOTE: it is valid to persist external realm types.
-var somevalue tests.TestRealmObject
-
-func init() {
- somevalue.Field = "test"
-}
-
-func main() {
- // NOTE: but it is invalid to modify it using an external realm function.
- somevalue.Modify()
- println(somevalue)
-}
-
-// Error:
-// cannot modify external-realm or non-realm object
diff --git a/gnovm/tests/files/zrealm_crossrealm6_stdlibs.gno b/gnovm/tests/files/zrealm_crossrealm6.gno
similarity index 100%
rename from gnovm/tests/files/zrealm_crossrealm6_stdlibs.gno
rename to gnovm/tests/files/zrealm_crossrealm6.gno
diff --git a/gnovm/tests/files/zrealm_crossrealm7_stdlibs.gno b/gnovm/tests/files/zrealm_crossrealm7.gno
similarity index 100%
rename from gnovm/tests/files/zrealm_crossrealm7_stdlibs.gno
rename to gnovm/tests/files/zrealm_crossrealm7.gno
diff --git a/gnovm/tests/files/zrealm_crossrealm8_stdlibs.gno b/gnovm/tests/files/zrealm_crossrealm8.gno
similarity index 100%
rename from gnovm/tests/files/zrealm_crossrealm8_stdlibs.gno
rename to gnovm/tests/files/zrealm_crossrealm8.gno
diff --git a/gnovm/tests/files/zrealm_crossrealm9_stdlibs.gno b/gnovm/tests/files/zrealm_crossrealm9.gno
similarity index 100%
rename from gnovm/tests/files/zrealm_crossrealm9_stdlibs.gno
rename to gnovm/tests/files/zrealm_crossrealm9.gno
diff --git a/gnovm/tests/files/zrealm_initctx_stdlibs.gno b/gnovm/tests/files/zrealm_initctx.gno
similarity index 100%
rename from gnovm/tests/files/zrealm_initctx_stdlibs.gno
rename to gnovm/tests/files/zrealm_initctx.gno
diff --git a/gnovm/tests/files/zrealm_natbind0_stdlibs.gno b/gnovm/tests/files/zrealm_natbind0.gno
similarity index 95%
rename from gnovm/tests/files/zrealm_natbind0_stdlibs.gno
rename to gnovm/tests/files/zrealm_natbind0.gno
index c852f4a09f7..8e5f641e734 100644
--- a/gnovm/tests/files/zrealm_natbind0_stdlibs.gno
+++ b/gnovm/tests/files/zrealm_natbind0.gno
@@ -69,7 +69,7 @@ func main() {
// "Closure": {
// "@type": "/gno.RefValue",
// "Escaped": true,
-// "ObjectID": "a7f5397443359ea76c50be82c77f1f893a060925:8"
+// "ObjectID": "a7f5397443359ea76c50be82c77f1f893a060925:9"
// },
// "FileName": "native.gno",
// "IsMethod": false,
@@ -117,7 +117,7 @@ func main() {
// "Escaped": true,
// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3"
// },
-// "FileName": "main.gno",
+// "FileName": "files/zrealm_natbind0.gno",
// "IsMethod": false,
// "Name": "init.1",
// "NativeName": "",
@@ -128,7 +128,7 @@ func main() {
// "BlockNode": null,
// "Location": {
// "Column": "1",
-// "File": "main.gno",
+// "File": "files/zrealm_natbind0.gno",
// "Line": "10",
// "PkgPath": "gno.land/r/test"
// }
@@ -153,7 +153,7 @@ func main() {
// "Escaped": true,
// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3"
// },
-// "FileName": "main.gno",
+// "FileName": "files/zrealm_natbind0.gno",
// "IsMethod": false,
// "Name": "main",
// "NativeName": "",
@@ -164,7 +164,7 @@ func main() {
// "BlockNode": null,
// "Location": {
// "Column": "1",
-// "File": "main.gno",
+// "File": "files/zrealm_natbind0.gno",
// "Line": "14",
// "PkgPath": "gno.land/r/test"
// }
diff --git a/gnovm/tests/files/zrealm_natbind1_stdlibs.gno b/gnovm/tests/files/zrealm_natbind1_stdlibs.gno
new file mode 100644
index 00000000000..f44b6ab4fcf
--- /dev/null
+++ b/gnovm/tests/files/zrealm_natbind1_stdlibs.gno
@@ -0,0 +1,16 @@
+// PKGPATH: gno.land/r/test
+package test
+
+import (
+ "std"
+)
+
+func main() {
+ println(std.GetChainDomain())
+}
+
+// Output:
+// tests.gno.land
+
+// Realm:
+// switchrealm["gno.land/r/test"]
diff --git a/gnovm/tests/files/zrealm_panic.gno b/gnovm/tests/files/zrealm_panic.gno
index 3864e2a7f7f..154a93ac8bf 100644
--- a/gnovm/tests/files/zrealm_panic.gno
+++ b/gnovm/tests/files/zrealm_panic.gno
@@ -12,9 +12,12 @@ func main() {
ms.Panic()
}
+// Error:
+// panic
+
// Stacktrace:
// panic: panic
// ms.Panic()
-// gno.land/r/test/main.gno:7
+// gno.land/r/test/files/zrealm_panic.gno:7
// main()
-// gno.land/r/test/main.gno:12
+// gno.land/r/test/files/zrealm_panic.gno:12
diff --git a/gnovm/tests/files/zrealm_std0_stdlibs.gno b/gnovm/tests/files/zrealm_std0.gno
similarity index 100%
rename from gnovm/tests/files/zrealm_std0_stdlibs.gno
rename to gnovm/tests/files/zrealm_std0.gno
diff --git a/gnovm/tests/files/zrealm_std1_stdlibs.gno b/gnovm/tests/files/zrealm_std1.gno
similarity index 100%
rename from gnovm/tests/files/zrealm_std1_stdlibs.gno
rename to gnovm/tests/files/zrealm_std1.gno
diff --git a/gnovm/tests/files/zrealm_std2_stdlibs.gno b/gnovm/tests/files/zrealm_std2.gno
similarity index 100%
rename from gnovm/tests/files/zrealm_std2_stdlibs.gno
rename to gnovm/tests/files/zrealm_std2.gno
diff --git a/gnovm/tests/files/zrealm_std3_stdlibs.gno b/gnovm/tests/files/zrealm_std3.gno
similarity index 100%
rename from gnovm/tests/files/zrealm_std3_stdlibs.gno
rename to gnovm/tests/files/zrealm_std3.gno
diff --git a/gnovm/tests/files/zrealm_std4_stdlibs.gno b/gnovm/tests/files/zrealm_std4.gno
similarity index 100%
rename from gnovm/tests/files/zrealm_std4_stdlibs.gno
rename to gnovm/tests/files/zrealm_std4.gno
diff --git a/gnovm/tests/files/zrealm_std5_stdlibs.gno b/gnovm/tests/files/zrealm_std5.gno
similarity index 100%
rename from gnovm/tests/files/zrealm_std5_stdlibs.gno
rename to gnovm/tests/files/zrealm_std5.gno
diff --git a/gnovm/tests/files/zrealm_std6_stdlibs.gno b/gnovm/tests/files/zrealm_std6.gno
similarity index 100%
rename from gnovm/tests/files/zrealm_std6_stdlibs.gno
rename to gnovm/tests/files/zrealm_std6.gno
diff --git a/gnovm/tests/files/zrealm_tests0_stdlibs.gno b/gnovm/tests/files/zrealm_tests0.gno
similarity index 97%
rename from gnovm/tests/files/zrealm_tests0_stdlibs.gno
rename to gnovm/tests/files/zrealm_tests0.gno
index d11701505e5..afb7e4a7c3b 100644
--- a/gnovm/tests/files/zrealm_tests0_stdlibs.gno
+++ b/gnovm/tests/files/zrealm_tests0.gno
@@ -14,16 +14,19 @@ func init() {
func main() {
tests_foo.AddFooStringer("three")
println(tests.Render(""))
+ println("end")
}
// Output:
// 0: &FooStringer{one}
// 1: &FooStringer{two}
// 2: &FooStringer{three}
+//
+// end
// Realm:
// switchrealm["gno.land/r/demo/tests"]
-// c[0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:18]={
+// c[0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:19]={
// "Fields": [
// {
// "T": {
@@ -37,17 +40,17 @@ func main() {
// }
// ],
// "ObjectInfo": {
-// "ID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:18",
+// "ID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:19",
// "ModTime": "0",
-// "OwnerID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:17",
+// "OwnerID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:18",
// "RefCount": "1"
// }
// }
-// c[0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:17]={
+// c[0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:18]={
// "ObjectInfo": {
-// "ID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:17",
+// "ID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:18",
// "ModTime": "0",
-// "OwnerID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:16",
+// "OwnerID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:17",
// "RefCount": "1"
// },
// "Value": {
@@ -57,12 +60,12 @@ func main() {
// },
// "V": {
// "@type": "/gno.RefValue",
-// "Hash": "d3d6ffa52602f2bc976051d79294d219750aca64",
-// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:18"
+// "Hash": "6b9b731f6118c2419f23ba57e1481679f17f4a8f",
+// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:19"
// }
// }
// }
-// c[0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:16]={
+// c[0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:17]={
// "Data": null,
// "List": [
// {
@@ -77,8 +80,8 @@ func main() {
// "@type": "/gno.PointerValue",
// "Base": {
// "@type": "/gno.RefValue",
-// "Hash": "4ea1e08156f3849b74a0f41f92cd4b48fb94926b",
-// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:11"
+// "Hash": "148d314678615253ee2032d7ecff6b144b474baf",
+// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:12"
// },
// "Index": "0",
// "TV": null
@@ -96,8 +99,8 @@ func main() {
// "@type": "/gno.PointerValue",
// "Base": {
// "@type": "/gno.RefValue",
-// "Hash": "ce86ea1156e75a44cd9d7ba2261819b100aa4ed1",
-// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:14"
+// "Hash": "fa414e1770821b8deb8e6d46d97828c47f7d5fa5",
+// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:15"
// },
// "Index": "0",
// "TV": null
@@ -115,8 +118,8 @@ func main() {
// "@type": "/gno.PointerValue",
// "Base": {
// "@type": "/gno.RefValue",
-// "Hash": "b66192fbd8a8dde79b6f854b5cc3c4cc965cfd92",
-// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:17"
+// "Hash": "aaa64d049cf8660d689780acac9f546f270eaa4e",
+// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:18"
// },
// "Index": "0",
// "TV": null
@@ -124,7 +127,7 @@ func main() {
// }
// ],
// "ObjectInfo": {
-// "ID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:16",
+// "ID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:17",
// "ModTime": "0",
// "OwnerID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:2",
// "RefCount": "1"
@@ -135,7 +138,7 @@ func main() {
// "ObjectInfo": {
// "ID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:2",
// "IsEscaped": true,
-// "ModTime": "15",
+// "ModTime": "16",
// "RefCount": "5"
// },
// "Parent": null,
@@ -204,8 +207,8 @@ func main() {
// "@type": "/gno.SliceValue",
// "Base": {
// "@type": "/gno.RefValue",
-// "Hash": "ad25f70f66c8c53042afd1377e5ff5ab744bf1a5",
-// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:16"
+// "Hash": "3c58838c5667649add1ff8ee48a1cdc187fcd2ef",
+// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:17"
// },
// "Length": "3",
// "Maxcap": "3",
@@ -1150,7 +1153,7 @@ func main() {
// "Location": {
// "Column": "1",
// "File": "tests.gno",
-// "Line": "57",
+// "Line": "59",
// "PkgPath": "gno.land/r/demo/tests"
// }
// },
@@ -1182,6 +1185,17 @@ func main() {
// },
// {
// "T": {
+// "@type": "/gno.RefType",
+// "ID": "gno.land/r/demo/tests.TestRealmObject"
+// },
+// "V": {
+// "@type": "/gno.RefValue",
+// "Hash": "5e56ba76fc0add1a3a67f7a8b6709f4f27215f93",
+// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:10"
+// }
+// },
+// {
+// "T": {
// "@type": "/gno.FuncType",
// "Params": [
// {
@@ -1218,7 +1232,7 @@ func main() {
// "Location": {
// "Column": "1",
// "File": "tests.gno",
-// "Line": "53",
+// "Line": "55",
// "PkgPath": "gno.land/r/demo/tests"
// }
// },
@@ -1335,7 +1349,7 @@ func main() {
// "Location": {
// "Column": "1",
// "File": "tests.gno",
-// "Line": "75",
+// "Line": "77",
// "PkgPath": "gno.land/r/demo/tests"
// }
// },
@@ -1371,7 +1385,7 @@ func main() {
// "Location": {
// "Column": "1",
// "File": "tests.gno",
-// "Line": "80",
+// "Line": "82",
// "PkgPath": "gno.land/r/demo/tests"
// }
// },
@@ -1407,7 +1421,7 @@ func main() {
// "Location": {
// "Column": "1",
// "File": "tests.gno",
-// "Line": "88",
+// "Line": "90",
// "PkgPath": "gno.land/r/demo/tests"
// }
// },
@@ -1453,7 +1467,7 @@ func main() {
// "Location": {
// "Column": "1",
// "File": "tests.gno",
-// "Line": "92",
+// "Line": "94",
// "PkgPath": "gno.land/r/demo/tests"
// }
// },
@@ -1509,7 +1523,7 @@ func main() {
// "Location": {
// "Column": "1",
// "File": "tests.gno",
-// "Line": "96",
+// "Line": "98",
// "PkgPath": "gno.land/r/demo/tests"
// }
// },
@@ -1566,7 +1580,7 @@ func main() {
// "Location": {
// "Column": "1",
// "File": "tests.gno",
-// "Line": "100",
+// "Line": "102",
// "PkgPath": "gno.land/r/demo/tests"
// }
// },
@@ -1623,7 +1637,7 @@ func main() {
// "Location": {
// "Column": "1",
// "File": "tests.gno",
-// "Line": "104",
+// "Line": "106",
// "PkgPath": "gno.land/r/demo/tests"
// }
// },
@@ -1679,7 +1693,7 @@ func main() {
// "Location": {
// "Column": "1",
// "File": "tests.gno",
-// "Line": "108",
+// "Line": "110",
// "PkgPath": "gno.land/r/demo/tests"
// }
// },
@@ -1735,7 +1749,7 @@ func main() {
// "Location": {
// "Column": "1",
// "File": "tests.gno",
-// "Line": "112",
+// "Line": "114",
// "PkgPath": "gno.land/r/demo/tests"
// }
// },
@@ -1758,7 +1772,7 @@ func main() {
// }
// ]
// }
-// d[0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:13]
+// d[0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:14]
// switchrealm["gno.land/r/demo/tests_foo"]
// switchrealm["gno.land/r/demo/tests_foo"]
// switchrealm["gno.land/r/demo/tests_foo"]
diff --git a/gnovm/tests/files/zrealm_testutils0_stdlibs.gno b/gnovm/tests/files/zrealm_testutils0.gno
similarity index 100%
rename from gnovm/tests/files/zrealm_testutils0_stdlibs.gno
rename to gnovm/tests/files/zrealm_testutils0.gno
diff --git a/gnovm/tests/files/zregexp_stdlibs.gno b/gnovm/tests/files/zregexp.gno
similarity index 100%
rename from gnovm/tests/files/zregexp_stdlibs.gno
rename to gnovm/tests/files/zregexp.gno
diff --git a/gnovm/tests/imports.go b/gnovm/tests/imports.go
deleted file mode 100644
index 66398ba5f50..00000000000
--- a/gnovm/tests/imports.go
+++ /dev/null
@@ -1,492 +0,0 @@
-package tests
-
-import (
- "bufio"
- "bytes"
- "compress/flate"
- "compress/gzip"
- "context"
- "crypto/md5" //nolint:gosec
- crand "crypto/rand"
- "crypto/sha1" //nolint:gosec
- "encoding/base64"
- "encoding/binary"
- "encoding/json"
- "encoding/xml"
- "errors"
- "flag"
- "fmt"
- "hash/fnv"
- "image"
- "image/color"
- "io"
- "log"
- "math"
- "math/big"
- "math/rand/v2"
- "net"
- "net/url"
- "os"
- "path/filepath"
- "reflect"
- "strconv"
- "strings"
- "sync"
- "sync/atomic"
- "text/template"
- "time"
- "unicode/utf8"
-
- gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
- teststdlibs "github.com/gnolang/gno/gnovm/tests/stdlibs"
- teststd "github.com/gnolang/gno/gnovm/tests/stdlibs/std"
- "github.com/gnolang/gno/tm2/pkg/db/memdb"
- osm "github.com/gnolang/gno/tm2/pkg/os"
- "github.com/gnolang/gno/tm2/pkg/std"
- "github.com/gnolang/gno/tm2/pkg/store/dbadapter"
- "github.com/gnolang/gno/tm2/pkg/store/iavl"
- stypes "github.com/gnolang/gno/tm2/pkg/store/types"
-)
-
-type importMode uint64
-
-// Import modes to control the import behaviour of TestStore.
-const (
- // use stdlibs/* only (except a few exceptions). for stdlibs/* and examples/* testing.
- ImportModeStdlibsOnly importMode = iota
- // use stdlibs/* if present, otherwise use native. used in files/tests, excluded for *_native.go
- ImportModeStdlibsPreferred
- // do not use stdlibs/* if native registered. used in files/tests, excluded for *_stdlibs.go
- ImportModeNativePreferred
-)
-
-// NOTE: this isn't safe, should only be used for testing.
-func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Writer, mode importMode) (resStore gno.Store) {
- getPackage := func(pkgPath string, store gno.Store) (pn *gno.PackageNode, pv *gno.PackageValue) {
- if pkgPath == "" {
- panic(fmt.Sprintf("invalid zero package path in testStore().pkgGetter"))
- }
- if mode != ImportModeStdlibsOnly &&
- mode != ImportModeStdlibsPreferred &&
- mode != ImportModeNativePreferred {
- panic(fmt.Sprintf("unrecognized import mode"))
- }
-
- if filesPath != "" {
- // if _test package...
- const testPath = "github.com/gnolang/gno/_test/"
- if strings.HasPrefix(pkgPath, testPath) {
- baseDir := filepath.Join(filesPath, "extern", pkgPath[len(testPath):])
- memPkg := gno.ReadMemPackage(baseDir, pkgPath)
- send := std.Coins{}
- ctx := TestContext(pkgPath, send)
- m2 := gno.NewMachineWithOptions(gno.MachineOptions{
- PkgPath: "test",
- Output: stdout,
- Store: store,
- Context: ctx,
- })
- // pkg := gno.NewPackageNode(gno.Name(memPkg.Name), memPkg.Path, nil)
- // pv := pkg.NewPackage()
- // m2.SetActivePackage(pv)
- // XXX remove second arg 'false' and remove all gonative stuff.
- return m2.RunMemPackage(memPkg, false)
- }
- }
-
- // if stdlibs package is preferred , try to load it first.
- if mode == ImportModeStdlibsOnly ||
- mode == ImportModeStdlibsPreferred {
- pn, pv = loadStdlib(rootDir, pkgPath, store, stdout)
- if pn != nil {
- return
- }
- }
-
- // if native package is allowed, return it.
- if pkgPath == "os" || // special cases even when StdlibsOnly (for tests).
- pkgPath == "fmt" || // TODO: try to minimize these exceptions over time.
- pkgPath == "log" ||
- pkgPath == "crypto/rand" ||
- pkgPath == "crypto/md5" ||
- pkgPath == "crypto/sha1" ||
- pkgPath == "encoding/binary" ||
- pkgPath == "encoding/json" ||
- pkgPath == "encoding/xml" ||
- pkgPath == "internal/os_test" ||
- pkgPath == "math/big" ||
- mode == ImportModeStdlibsPreferred ||
- mode == ImportModeNativePreferred {
- switch pkgPath {
- case "os":
- pkg := gno.NewPackageNode("os", pkgPath, nil)
- pkg.DefineGoNativeValue("Stdin", stdin)
- pkg.DefineGoNativeValue("Stdout", stdout)
- pkg.DefineGoNativeValue("Stderr", stderr)
- return pkg, pkg.NewPackage()
- case "fmt":
- pkg := gno.NewPackageNode("fmt", pkgPath, nil)
- pkg.DefineGoNativeType(reflect.TypeOf((*fmt.Stringer)(nil)).Elem())
- pkg.DefineGoNativeType(reflect.TypeOf((*fmt.Formatter)(nil)).Elem())
- pkg.DefineGoNativeValue("Println", func(a ...interface{}) (n int, err error) {
- // NOTE: uncomment to debug long running tests
- // fmt.Println(a...)
- res := fmt.Sprintln(a...)
- return stdout.Write([]byte(res))
- })
- pkg.DefineGoNativeValue("Printf", func(format string, a ...interface{}) (n int, err error) {
- res := fmt.Sprintf(format, a...)
- return stdout.Write([]byte(res))
- })
- pkg.DefineGoNativeValue("Print", func(a ...interface{}) (n int, err error) {
- res := fmt.Sprint(a...)
- return stdout.Write([]byte(res))
- })
- pkg.DefineGoNativeValue("Sprint", fmt.Sprint)
- pkg.DefineGoNativeValue("Sprintf", fmt.Sprintf)
- pkg.DefineGoNativeValue("Sprintln", fmt.Sprintln)
- pkg.DefineGoNativeValue("Sscanf", fmt.Sscanf)
- pkg.DefineGoNativeValue("Errorf", fmt.Errorf)
- pkg.DefineGoNativeValue("Fprintln", fmt.Fprintln)
- pkg.DefineGoNativeValue("Fprintf", fmt.Fprintf)
- pkg.DefineGoNativeValue("Fprint", fmt.Fprint)
- return pkg, pkg.NewPackage()
- case "encoding/base64":
- pkg := gno.NewPackageNode("base64", pkgPath, nil)
- pkg.DefineGoNativeValue("RawStdEncoding", base64.RawStdEncoding)
- pkg.DefineGoNativeValue("StdEncoding", base64.StdEncoding)
- pkg.DefineGoNativeValue("NewDecoder", base64.NewDecoder)
- return pkg, pkg.NewPackage()
- case "encoding/binary":
- pkg := gno.NewPackageNode("binary", pkgPath, nil)
- pkg.DefineGoNativeValue("LittleEndian", binary.LittleEndian)
- pkg.DefineGoNativeValue("BigEndian", binary.BigEndian)
- pkg.DefineGoNativeValue("Write", binary.BigEndian) // warn: use reflection
- return pkg, pkg.NewPackage()
- case "encoding/json":
- pkg := gno.NewPackageNode("json", pkgPath, nil)
- pkg.DefineGoNativeValue("Unmarshal", json.Unmarshal)
- pkg.DefineGoNativeValue("Marshal", json.Marshal)
- return pkg, pkg.NewPackage()
- case "encoding/xml":
- pkg := gno.NewPackageNode("xml", pkgPath, nil)
- pkg.DefineGoNativeValue("Unmarshal", xml.Unmarshal)
- return pkg, pkg.NewPackage()
- case "internal/os_test":
- pkg := gno.NewPackageNode("os_test", pkgPath, nil)
- pkg.DefineNative("Sleep",
- gno.Flds( // params
- "d", gno.AnyT(), // NOTE: should be time.Duration
- ),
- gno.Flds( // results
- ),
- func(m *gno.Machine) {
- // For testing purposes here, nanoseconds are separately kept track.
- arg0 := m.LastBlock().GetParams1().TV
- d := arg0.GetInt64()
- sec := d / int64(time.Second)
- nano := d % int64(time.Second)
- ctx := m.Context.(*teststd.TestExecContext)
- ctx.Timestamp += sec
- ctx.TimestampNano += nano
- if ctx.TimestampNano >= int64(time.Second) {
- ctx.Timestamp += 1
- ctx.TimestampNano -= int64(time.Second)
- }
- m.Context = ctx
- },
- )
- return pkg, pkg.NewPackage()
- case "net":
- pkg := gno.NewPackageNode("net", pkgPath, nil)
- pkg.DefineGoNativeType(reflect.TypeOf(net.TCPAddr{}))
- pkg.DefineGoNativeValue("IPv4", net.IPv4)
- return pkg, pkg.NewPackage()
- case "net/url":
- pkg := gno.NewPackageNode("url", pkgPath, nil)
- pkg.DefineGoNativeType(reflect.TypeOf(url.Values{}))
- return pkg, pkg.NewPackage()
- case "bufio":
- pkg := gno.NewPackageNode("bufio", pkgPath, nil)
- pkg.DefineGoNativeValue("NewScanner", bufio.NewScanner)
- pkg.DefineGoNativeType(reflect.TypeOf(bufio.SplitFunc(nil)))
- return pkg, pkg.NewPackage()
- case "bytes":
- pkg := gno.NewPackageNode("bytes", pkgPath, nil)
- pkg.DefineGoNativeValue("Equal", bytes.Equal)
- pkg.DefineGoNativeValue("Compare", bytes.Compare)
- pkg.DefineGoNativeValue("NewReader", bytes.NewReader)
- pkg.DefineGoNativeValue("NewBuffer", bytes.NewBuffer)
- pkg.DefineGoNativeValue("Repeat", bytes.Repeat)
- pkg.DefineGoNativeType(reflect.TypeOf(bytes.Buffer{}))
- return pkg, pkg.NewPackage()
- case "time":
- pkg := gno.NewPackageNode("time", pkgPath, nil)
- pkg.DefineGoNativeConstValue("Millisecond", time.Millisecond)
- pkg.DefineGoNativeConstValue("Second", time.Second)
- pkg.DefineGoNativeConstValue("Minute", time.Minute)
- pkg.DefineGoNativeConstValue("Hour", time.Hour)
- pkg.DefineGoNativeConstValue("Date", time.Date)
- pkg.DefineGoNativeConstValue("Now", func() time.Time { return time.Unix(0, 0).UTC() }) // deterministic
- pkg.DefineGoNativeConstValue("January", time.January)
- pkg.DefineGoNativeConstValue("February", time.February)
- pkg.DefineGoNativeConstValue("March", time.March)
- pkg.DefineGoNativeConstValue("April", time.April)
- pkg.DefineGoNativeConstValue("May", time.May)
- pkg.DefineGoNativeConstValue("June", time.June)
- pkg.DefineGoNativeConstValue("July", time.July)
- pkg.DefineGoNativeConstValue("August", time.August)
- pkg.DefineGoNativeConstValue("September", time.September)
- pkg.DefineGoNativeConstValue("November", time.November)
- pkg.DefineGoNativeConstValue("December", time.December)
- pkg.DefineGoNativeValue("UTC", time.UTC)
- pkg.DefineGoNativeValue("Unix", time.Unix)
- pkg.DefineGoNativeType(reflect.TypeOf(time.Time{}))
- pkg.DefineGoNativeType(reflect.TypeOf(time.Duration(0)))
- pkg.DefineGoNativeType(reflect.TypeOf(time.Month(0)))
- pkg.DefineGoNativeValue("LoadLocation", time.LoadLocation)
- return pkg, pkg.NewPackage()
- case "strconv":
- pkg := gno.NewPackageNode("strconv", pkgPath, nil)
- pkg.DefineGoNativeValue("Itoa", strconv.Itoa)
- pkg.DefineGoNativeValue("Atoi", strconv.Atoi)
- pkg.DefineGoNativeValue("ParseInt", strconv.ParseInt)
- pkg.DefineGoNativeValue("Quote", strconv.Quote)
- pkg.DefineGoNativeValue("FormatUint", strconv.FormatUint)
- pkg.DefineGoNativeType(reflect.TypeOf(strconv.NumError{}))
- return pkg, pkg.NewPackage()
- case "strings":
- pkg := gno.NewPackageNode("strings", pkgPath, nil)
- pkg.DefineGoNativeValue("Split", strings.Split)
- pkg.DefineGoNativeValue("SplitN", strings.SplitN)
- pkg.DefineGoNativeValue("Contains", strings.Contains)
- pkg.DefineGoNativeValue("TrimSpace", strings.TrimSpace)
- pkg.DefineGoNativeValue("HasPrefix", strings.HasPrefix)
- pkg.DefineGoNativeValue("NewReader", strings.NewReader)
- pkg.DefineGoNativeValue("Index", strings.Index)
- pkg.DefineGoNativeValue("IndexRune", strings.IndexRune)
- pkg.DefineGoNativeValue("Join", strings.Join)
- pkg.DefineGoNativeType(reflect.TypeOf(strings.Builder{}))
- return pkg, pkg.NewPackage()
- case "math":
- pkg := gno.NewPackageNode("math", pkgPath, nil)
- pkg.DefineGoNativeValue("Abs", math.Abs)
- pkg.DefineGoNativeValue("Cos", math.Cos)
- pkg.DefineGoNativeConstValue("Pi", math.Pi)
- pkg.DefineGoNativeValue("Float64bits", math.Float64bits)
- pkg.DefineGoNativeConstValue("MaxFloat32", math.MaxFloat32)
- pkg.DefineGoNativeConstValue("MaxFloat64", math.MaxFloat64)
- pkg.DefineGoNativeConstValue("MaxUint32", uint32(math.MaxUint32))
- pkg.DefineGoNativeConstValue("MaxUint64", uint64(math.MaxUint64))
- pkg.DefineGoNativeConstValue("MinInt8", math.MinInt8)
- pkg.DefineGoNativeConstValue("MinInt16", math.MinInt16)
- pkg.DefineGoNativeConstValue("MinInt32", math.MinInt32)
- pkg.DefineGoNativeConstValue("MinInt64", int64(math.MinInt64))
- pkg.DefineGoNativeConstValue("MaxInt8", math.MaxInt8)
- pkg.DefineGoNativeConstValue("MaxInt16", math.MaxInt16)
- pkg.DefineGoNativeConstValue("MaxInt32", math.MaxInt32)
- pkg.DefineGoNativeConstValue("MaxInt64", int64(math.MaxInt64))
- return pkg, pkg.NewPackage()
- case "math/rand":
- // XXX only expose for tests.
- pkg := gno.NewPackageNode("rand", pkgPath, nil)
- // make native rand same as gno rand.
- rnd := rand.New(rand.NewPCG(0, 0)) //nolint:gosec
- pkg.DefineGoNativeValue("IntN", rnd.IntN)
- pkg.DefineGoNativeValue("Uint32", rnd.Uint32)
- return pkg, pkg.NewPackage()
- case "crypto/rand":
- pkg := gno.NewPackageNode("rand", pkgPath, nil)
- pkg.DefineGoNativeValue("Prime", crand.Prime)
- // for determinism:
- // pkg.DefineGoNativeValue("Reader", crand.Reader)
- pkg.DefineGoNativeValue("Reader", &dummyReader{})
- return pkg, pkg.NewPackage()
- case "crypto/md5":
- pkg := gno.NewPackageNode("md5", pkgPath, nil)
- pkg.DefineGoNativeValue("New", md5.New)
- return pkg, pkg.NewPackage()
- case "crypto/sha1":
- pkg := gno.NewPackageNode("sha1", pkgPath, nil)
- pkg.DefineGoNativeValue("New", sha1.New)
- return pkg, pkg.NewPackage()
- case "image":
- pkg := gno.NewPackageNode("image", pkgPath, nil)
- pkg.DefineGoNativeType(reflect.TypeOf(image.Point{}))
- return pkg, pkg.NewPackage()
- case "image/color":
- pkg := gno.NewPackageNode("color", pkgPath, nil)
- pkg.DefineGoNativeType(reflect.TypeOf(color.NRGBA64{}))
- return pkg, pkg.NewPackage()
- case "compress/flate":
- pkg := gno.NewPackageNode("flate", pkgPath, nil)
- pkg.DefineGoNativeConstValue("BestSpeed", flate.BestSpeed)
- return pkg, pkg.NewPackage()
- case "compress/gzip":
- pkg := gno.NewPackageNode("gzip", pkgPath, nil)
- pkg.DefineGoNativeType(reflect.TypeOf(gzip.Writer{}))
- pkg.DefineGoNativeConstValue("BestCompression", gzip.BestCompression)
- pkg.DefineGoNativeConstValue("BestSpeed", gzip.BestSpeed)
- return pkg, pkg.NewPackage()
- case "context":
- pkg := gno.NewPackageNode("context", pkgPath, nil)
- pkg.DefineGoNativeType(reflect.TypeOf((*context.Context)(nil)).Elem())
- pkg.DefineGoNativeValue("WithValue", context.WithValue)
- pkg.DefineGoNativeValue("Background", context.Background)
- return pkg, pkg.NewPackage()
- case "sync":
- pkg := gno.NewPackageNode("sync", pkgPath, nil)
- pkg.DefineGoNativeType(reflect.TypeOf(sync.Mutex{}))
- pkg.DefineGoNativeType(reflect.TypeOf(sync.RWMutex{}))
- pkg.DefineGoNativeType(reflect.TypeOf(sync.Pool{}))
- return pkg, pkg.NewPackage()
- case "sync/atomic":
- pkg := gno.NewPackageNode("atomic", pkgPath, nil)
- pkg.DefineGoNativeType(reflect.TypeOf(atomic.Value{}))
- return pkg, pkg.NewPackage()
- case "math/big":
- pkg := gno.NewPackageNode("big", pkgPath, nil)
- pkg.DefineGoNativeValue("NewInt", big.NewInt)
- return pkg, pkg.NewPackage()
- case "flag":
- pkg := gno.NewPackageNode("flag", pkgPath, nil)
- pkg.DefineGoNativeType(reflect.TypeOf(flag.Flag{}))
- return pkg, pkg.NewPackage()
- case "io":
- pkg := gno.NewPackageNode("io", pkgPath, nil)
- pkg.DefineGoNativeValue("EOF", io.EOF)
- pkg.DefineGoNativeValue("NopCloser", io.NopCloser)
- pkg.DefineGoNativeValue("ReadFull", io.ReadFull)
- pkg.DefineGoNativeValue("ReadAll", io.ReadAll)
- pkg.DefineGoNativeType(reflect.TypeOf((*io.ReadCloser)(nil)).Elem())
- pkg.DefineGoNativeType(reflect.TypeOf((*io.Closer)(nil)).Elem())
- pkg.DefineGoNativeType(reflect.TypeOf((*io.Reader)(nil)).Elem())
- return pkg, pkg.NewPackage()
- case "log":
- pkg := gno.NewPackageNode("log", pkgPath, nil)
- pkg.DefineGoNativeValue("Fatal", log.Fatal)
- return pkg, pkg.NewPackage()
- case "text/template":
- pkg := gno.NewPackageNode("template", pkgPath, nil)
- pkg.DefineGoNativeType(reflect.TypeOf(template.FuncMap{}))
- return pkg, pkg.NewPackage()
- case "unicode/utf8":
- pkg := gno.NewPackageNode("utf8", pkgPath, nil)
- pkg.DefineGoNativeValue("DecodeRuneInString", utf8.DecodeRuneInString)
- tv := gno.TypedValue{T: gno.UntypedRuneType} // TODO dry
- tv.SetInt32(utf8.RuneSelf) // ..
- pkg.Define("RuneSelf", tv) // ..
- return pkg, pkg.NewPackage()
- case "errors":
- pkg := gno.NewPackageNode("errors", pkgPath, nil)
- pkg.DefineGoNativeValue("New", errors.New)
- return pkg, pkg.NewPackage()
- case "hash/fnv":
- pkg := gno.NewPackageNode("fnv", pkgPath, nil)
- pkg.DefineGoNativeValue("New32a", fnv.New32a)
- return pkg, pkg.NewPackage()
- default:
- // continue on...
- }
- }
-
- // if native package is preferred, try to load stdlibs/* as backup.
- if mode == ImportModeNativePreferred {
- pn, pv = loadStdlib(rootDir, pkgPath, store, stdout)
- if pn != nil {
- return
- }
- }
-
- // if examples package...
- examplePath := filepath.Join(rootDir, "examples", pkgPath)
- if osm.DirExists(examplePath) {
- memPkg := gno.ReadMemPackage(examplePath, pkgPath)
- if memPkg.IsEmpty() {
- panic(fmt.Sprintf("found an empty package %q", pkgPath))
- }
-
- send := std.Coins{}
- ctx := TestContext(pkgPath, send)
- m2 := gno.NewMachineWithOptions(gno.MachineOptions{
- PkgPath: "test",
- Output: stdout,
- Store: store,
- Context: ctx,
- })
- pn, pv = m2.RunMemPackage(memPkg, true)
- return
- }
- return nil, nil
- }
- db := memdb.NewMemDB()
- baseStore := dbadapter.StoreConstructor(db, stypes.StoreOptions{})
- iavlStore := iavl.StoreConstructor(db, stypes.StoreOptions{})
- // make a new store
- resStore = gno.NewStore(nil, baseStore, iavlStore)
- resStore.SetPackageGetter(getPackage)
- resStore.SetNativeStore(teststdlibs.NativeStore)
- resStore.SetStrictGo2GnoMapping(false)
- return
-}
-
-func loadStdlib(rootDir, pkgPath string, store gno.Store, stdout io.Writer) (*gno.PackageNode, *gno.PackageValue) {
- dirs := [...]string{
- // normal stdlib path.
- filepath.Join(rootDir, "gnovm", "stdlibs", pkgPath),
- // override path. definitions here override the previous if duplicate.
- filepath.Join(rootDir, "gnovm", "tests", "stdlibs", pkgPath),
- }
- files := make([]string, 0, 32) // pre-alloc 32 as a likely high number of files
- for _, path := range dirs {
- dl, err := os.ReadDir(path)
- if err != nil {
- if os.IsNotExist(err) {
- continue
- }
- panic(fmt.Errorf("could not access dir %q: %w", path, err))
- }
-
- for _, f := range dl {
- // NOTE: RunMemPackage has other rules; those should be mostly useful
- // for on-chain packages (ie. include README and gno.mod).
- if !f.IsDir() && strings.HasSuffix(f.Name(), ".gno") {
- files = append(files, filepath.Join(path, f.Name()))
- }
- }
- }
- if len(files) == 0 {
- return nil, nil
- }
-
- memPkg := gno.ReadMemPackageFromList(files, pkgPath)
- m2 := gno.NewMachineWithOptions(gno.MachineOptions{
- // NOTE: see also pkgs/sdk/vm/builtins.go
- // Needs PkgPath != its name because TestStore.getPackage is the package
- // getter for the store, which calls loadStdlib, so it would be recursively called.
- PkgPath: "stdlibload",
- Output: stdout,
- Store: store,
- })
- save := pkgPath != "testing" // never save the "testing" package
- return m2.RunMemPackageWithOverrides(memPkg, save)
-}
-
-type dummyReader struct{}
-
-func (*dummyReader) Read(b []byte) (n int, err error) {
- for i := 0; i < len(b); i++ {
- b[i] = byte((100 + i) % 256)
- }
- return len(b), nil
-}
-
-// ----------------------------------------
-
-type TestReport struct {
- Name string
- Verbose bool
- Failed bool
- Skipped bool
- Output string
-}
diff --git a/gnovm/tests/integ/invalid_module_version1/gno.mod b/gnovm/tests/integ/invalid_module_version1/gno.mod
deleted file mode 100644
index e4c64e3106f..00000000000
--- a/gnovm/tests/integ/invalid_module_version1/gno.mod
+++ /dev/null
@@ -1,5 +0,0 @@
-module tmp
-
-require (
- "gno.land/p/demo/avl" //missing version
-)
diff --git a/gnovm/tests/integ/invalid_module_version2/gno.mod b/gnovm/tests/integ/invalid_module_version2/gno.mod
deleted file mode 100644
index 0a3088b454a..00000000000
--- a/gnovm/tests/integ/invalid_module_version2/gno.mod
+++ /dev/null
@@ -1,5 +0,0 @@
-module tmp
-
-require (
- "gno.land/p/demo/avl" version-2 //invalid versioning
-)
diff --git a/gnovm/tests/integ/replace_with_dir/gno.mod b/gnovm/tests/integ/replace_with_dir/gno.mod
index 6a7b1b664c8..69ae753a58a 100644
--- a/gnovm/tests/integ/replace_with_dir/gno.mod
+++ b/gnovm/tests/integ/replace_with_dir/gno.mod
@@ -1,9 +1,5 @@
module gno.land/tests/replaceavl
-require (
- "gno.land/p/demo/notexists" v0.0.0
-)
-
replace (
"gno.land/p/demo/notexists" => /path/to/dir
)
diff --git a/gnovm/tests/integ/replace_with_invalid_module/gno.mod b/gnovm/tests/integ/replace_with_invalid_module/gno.mod
index ee90787ff0e..2a9527da7d6 100644
--- a/gnovm/tests/integ/replace_with_invalid_module/gno.mod
+++ b/gnovm/tests/integ/replace_with_invalid_module/gno.mod
@@ -1,9 +1,5 @@
module gno.land/tests/replaceavl
-require (
- "gno.land/p/demo/avl" v0.0.0
-)
-
replace (
- "gno.land/p/demo/avl" => "gno.land/p/demo/avlll" v0.0.0
+ "gno.land/p/demo/avl" => "gno.land/p/demo/notexists"
)
diff --git a/gnovm/tests/integ/replace_with_invalid_module/main.gno b/gnovm/tests/integ/replace_with_invalid_module/main.gno
new file mode 100644
index 00000000000..7f78497fa02
--- /dev/null
+++ b/gnovm/tests/integ/replace_with_invalid_module/main.gno
@@ -0,0 +1,7 @@
+package main
+
+import (
+ "gno.land/p/demo/avl"
+)
+
+var foo = avl.Bar
diff --git a/gnovm/tests/integ/replace_with_module/gno.mod b/gnovm/tests/integ/replace_with_module/gno.mod
index 09c77df7a95..de730c90a53 100644
--- a/gnovm/tests/integ/replace_with_module/gno.mod
+++ b/gnovm/tests/integ/replace_with_module/gno.mod
@@ -1,9 +1,5 @@
module gno.land/tests/replaceavl
-require (
- "gno.land/p/demo/avl" v0.0.2
-)
-
replace (
- "gno.land/p/demo/avl" v0.0.2 => "gno.land/p/demo/avl" v1.0.0
+ "gno.land/p/demo/avl" => "gno.land/p/demo/users"
)
diff --git a/gnovm/tests/integ/replace_with_module/main.gno b/gnovm/tests/integ/replace_with_module/main.gno
new file mode 100644
index 00000000000..7f78497fa02
--- /dev/null
+++ b/gnovm/tests/integ/replace_with_module/main.gno
@@ -0,0 +1,7 @@
+package main
+
+import (
+ "gno.land/p/demo/avl"
+)
+
+var foo = avl.Bar
diff --git a/gnovm/tests/integ/require_invalid_module/gno.mod b/gnovm/tests/integ/require_invalid_module/gno.mod
index f0b455f128b..f10dff8c8d5 100644
--- a/gnovm/tests/integ/require_invalid_module/gno.mod
+++ b/gnovm/tests/integ/require_invalid_module/gno.mod
@@ -1,5 +1 @@
-module gno.land/tests/reqinvalidmodule
-
-require (
- "gno.land/p/demo/notexists" v1.2.3
-)
+module gno.land/tests/reqinvalidmodule
\ No newline at end of file
diff --git a/gnovm/tests/integ/require_invalid_module/main.gno b/gnovm/tests/integ/require_invalid_module/main.gno
new file mode 100644
index 00000000000..703ec65ee5a
--- /dev/null
+++ b/gnovm/tests/integ/require_invalid_module/main.gno
@@ -0,0 +1,7 @@
+package main
+
+import (
+ "gno.land/p/demo/notexists"
+)
+
+var foo = notexists.Bar
diff --git a/gnovm/tests/integ/require_remote_module/gno.mod b/gnovm/tests/integ/require_remote_module/gno.mod
index 4823c72585d..946f41398ba 100644
--- a/gnovm/tests/integ/require_remote_module/gno.mod
+++ b/gnovm/tests/integ/require_remote_module/gno.mod
@@ -1,5 +1 @@
module gno.land/tests/importavl
-
-require (
- "gno.land/p/demo/avl" v0.0.0
-)
diff --git a/gnovm/tests/integ/require_std_lib/gno.mod b/gnovm/tests/integ/require_std_lib/gno.mod
new file mode 100644
index 00000000000..f10dff8c8d5
--- /dev/null
+++ b/gnovm/tests/integ/require_std_lib/gno.mod
@@ -0,0 +1 @@
+module gno.land/tests/reqinvalidmodule
\ No newline at end of file
diff --git a/gnovm/tests/integ/require_std_lib/main.gno b/gnovm/tests/integ/require_std_lib/main.gno
new file mode 100644
index 00000000000..920d238cccc
--- /dev/null
+++ b/gnovm/tests/integ/require_std_lib/main.gno
@@ -0,0 +1,7 @@
+package main
+
+import (
+ "std"
+)
+
+var foo std.Address
diff --git a/gnovm/tests/integ/typecheck_missing_return/gno.mod b/gnovm/tests/integ/typecheck_missing_return/gno.mod
new file mode 100644
index 00000000000..3eaaa374994
--- /dev/null
+++ b/gnovm/tests/integ/typecheck_missing_return/gno.mod
@@ -0,0 +1 @@
+module gno.land/p/integ/valid
diff --git a/gnovm/tests/integ/typecheck_missing_return/main.gno b/gnovm/tests/integ/typecheck_missing_return/main.gno
new file mode 100644
index 00000000000..5d6e547097c
--- /dev/null
+++ b/gnovm/tests/integ/typecheck_missing_return/main.gno
@@ -0,0 +1,5 @@
+package valid
+
+func Hello() int {
+ // no return
+}
diff --git a/gnovm/tests/integ/valid2/gno.mod b/gnovm/tests/integ/valid2/gno.mod
index 98a5a0dacc1..3eaaa374994 100644
--- a/gnovm/tests/integ/valid2/gno.mod
+++ b/gnovm/tests/integ/valid2/gno.mod
@@ -1,3 +1 @@
module gno.land/p/integ/valid
-
-require gno.land/p/demo/avl v0.0.0-latest
diff --git a/gnovm/tests/machine_test.go b/gnovm/tests/machine_test.go
deleted file mode 100644
index a67d67f1ff2..00000000000
--- a/gnovm/tests/machine_test.go
+++ /dev/null
@@ -1,65 +0,0 @@
-package tests
-
-import (
- "os"
- "path/filepath"
- "testing"
-
- "github.com/stretchr/testify/assert"
-
- gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
-)
-
-func TestMachineTestMemPackage(t *testing.T) {
- matchFunc := func(pat, str string) (bool, error) { return true, nil }
-
- tests := []struct {
- name string
- path string
- shouldSucceed bool
- }{
- {
- name: "TestSuccess",
- path: "testdata/TestMemPackage/success",
- shouldSucceed: true,
- },
- {
- name: "TestFail",
- path: "testdata/TestMemPackage/fail",
- shouldSucceed: false,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- // NOTE: Because the purpose of this test is to ensure testing.T.Failed()
- // returns true if a gno test is failing, and because we don't want this
- // to affect the current testing.T, we are creating an other one thanks
- // to testing.RunTests() function.
- testing.RunTests(matchFunc, []testing.InternalTest{
- {
- Name: tt.name,
- F: func(t2 *testing.T) { //nolint:thelper
- rootDir := filepath.Join("..", "..")
- store := TestStore(rootDir, "test", os.Stdin, os.Stdout, os.Stderr, ImportModeStdlibsOnly)
- store.SetLogStoreOps(true)
- m := gno.NewMachineWithOptions(gno.MachineOptions{
- PkgPath: "test",
- Output: os.Stdout,
- Store: store,
- Context: nil,
- })
- memPkg := gno.ReadMemPackage(tt.path, "test")
-
- m.TestMemPackage(t2, memPkg)
-
- if tt.shouldSucceed {
- assert.False(t, t2.Failed(), "test %q should have succeed", tt.name)
- } else {
- assert.True(t, t2.Failed(), "test %q should have failed", tt.name)
- }
- },
- },
- })
- })
- }
-}
diff --git a/gnovm/tests/package_test.go b/gnovm/tests/package_test.go
deleted file mode 100644
index d4ddfc9a4f0..00000000000
--- a/gnovm/tests/package_test.go
+++ /dev/null
@@ -1,90 +0,0 @@
-package tests
-
-import (
- "bytes"
- "fmt"
- "io/fs"
- "log"
- "os"
- "path/filepath"
- "strings"
- "testing"
-
- "github.com/stretchr/testify/require"
-
- gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
-)
-
-func TestStdlibs(t *testing.T) {
- t.Parallel()
-
- // NOTE: this test only works using _test.gno files;
- // filetests are not meant to be used for testing standard libraries.
- // The examples directory is tested directly using `gno test`u
-
- // find all packages with *_test.gno files.
- rootDirs := []string{
- filepath.Join("..", "stdlibs"),
- }
- testDirs := map[string]string{} // aggregate here, pkgPath -> dir
- pkgPaths := []string{}
- for _, rootDir := range rootDirs {
- fileSystem := os.DirFS(rootDir)
- fs.WalkDir(fileSystem, ".", func(path string, d fs.DirEntry, err error) error {
- if err != nil {
- log.Fatal(err)
- }
- if d.IsDir() {
- return nil
- }
- if strings.HasSuffix(path, "_test.gno") {
- dirPath := filepath.Dir(path)
- if _, exists := testDirs[dirPath]; exists {
- // already exists.
- } else {
- testDirs[dirPath] = filepath.Join(rootDir, dirPath)
- pkgPaths = append(pkgPaths, dirPath)
- }
- }
- return nil
- })
- }
- // For each package with testfiles (in testDirs), call Machine.TestMemPackage.
- for _, pkgPath := range pkgPaths {
- testDir := testDirs[pkgPath]
- t.Run(pkgPath, func(t *testing.T) {
- pkgPath := pkgPath
- t.Parallel()
- runPackageTest(t, testDir, pkgPath)
- })
- }
-}
-
-func runPackageTest(t *testing.T, dir string, path string) {
- t.Helper()
-
- memPkg := gno.ReadMemPackage(dir, path)
- require.False(t, memPkg.IsEmpty())
-
- stdin := new(bytes.Buffer)
- // stdout := new(bytes.Buffer)
- stdout := os.Stdout
- stderr := new(bytes.Buffer)
- rootDir := filepath.Join("..", "..")
- store := TestStore(rootDir, path, stdin, stdout, stderr, ImportModeStdlibsOnly)
- store.SetLogStoreOps(true)
- m := gno.NewMachineWithOptions(gno.MachineOptions{
- PkgPath: "test",
- Output: stdout,
- Store: store,
- Context: nil,
- })
- m.TestMemPackage(t, memPkg)
-
- // Check that machine is empty.
- err := m.CheckEmpty()
- if err != nil {
- t.Log("last state: \n", m.String())
- panic(fmt.Sprintf("machine not empty after main: %v", err))
- }
-}
diff --git a/gnovm/tests/selector_test.go b/gnovm/tests/selector_test.go
deleted file mode 100644
index 1f0b400555b..00000000000
--- a/gnovm/tests/selector_test.go
+++ /dev/null
@@ -1,174 +0,0 @@
-package tests
-
-import (
- "fmt"
- "reflect"
- "testing"
-)
-
-/*
-This attempts to show a sufficiently exhaustive list of ValuePaths for
-different types of selectors. As can be seen, even a simple selector
-expression can represent a number of different types of selectors.
-*/
-
-// S1 struct
-type S1 struct {
- F0 int
-}
-
-func (S1) Hello() {
-}
-
-func (*S1) Bye() {
-}
-
-// Pointer to S1
-type S1P *S1
-
-// Like S1 but pointer struct
-type PS1 *struct {
- F0 int
-}
-
-type S7 struct {
- S1
-}
-
-type S9 struct {
- *S1
-}
-
-type S10PD *struct {
- S1
-}
-
-func _printValue(x interface{}) {
- if reflect.TypeOf(x).Kind() == reflect.Func {
- fmt.Println("function")
- } else {
- fmt.Println(x)
- }
-}
-
-func TestSelectors(t *testing.T) {
- t.Parallel()
-
- x0 := struct{ F0 int }{1}
- _printValue(x0.F0) // *ST.F0
- // F:0
- // VPField{depth:0,index:0}
- x1 := S1{1}
- _printValue(x1.F0) // *DT(S1)>*ST.F0
- // +1 F:0
- // VPField{depth:1,index:0}
- _printValue(x1.Hello) // *DT(S1).Hello
- // +1 M:0
- // VPValMethod{index:0}
- _printValue(x1.Bye) // *PT(implied)>*DT(S1).Bye
- // +D +1 *M:1
- // VPDerefPtrMethod{index:1}
- x2 := &x0
- _printValue(x2.F0) // *PT>*ST.F0
- // +D F:0
- // VPDerefField{depth:0,index:0}
- var x3 PS1 = &struct{ F0 int }{1}
- _printValue(x3.F0) // *DT(S1P)>*PT>*ST.F0
- // +1 +D F:0
- // VPDerefField{depth:1,index:0}
- x4 := &S1{1}
- _printValue(x4.F0) // *PT>*DT(S1P)>*ST.F0
- // +D +1 F:0
- // VPDerefField{depth:2,index:0}
- var x5 S1P = &S1{1}
- _printValue(x5.F0) // *DT(S1P)>*PT>*DT(S1)>*ST.F0
- // +1 +D +1 F:0
- // VPDerefField{depth:3,index:0}
- x6 := &x5
- _printValue(x6)
- // _printValue(x6.F0) *PT>*DT(S1P)??? > *PT>*DT(S1)>*ST.F0
- // +D +1 +D +1 F:0
- // VPDerefField{depth:1,index:0}(WRONG!!!) > VPDerefField{depth:1,index:0} XXX ERROR
- x7 := S7{S1{1}}
- _printValue(x7.F0) // *DT(S7)>*ST.S1 > *DT(S1)>*ST.F0
- // +1 F:0 +1 F:0
- // VPField{depth:1,index:0} > VPField{depth:1,index:0}
- x8 := &x7
- _printValue(x8.F0) // *PT>*DT(S7)>*ST.S1 > *DT(S1)>*ST.F0
- // +D +1 F:0 +1 F:0
- // VPDerefField{depth:1,index:0} > VPField{depth:1,index:0}
- x9 := S9{x5}
- _printValue(x9.F0) // *DT(S9)>*ST.S1 > *PT>*DT(S1)>*ST.F0
- // +1 F:0 +D +1 F:0
- // VPField{depth:1,index:0} > VPDerefField{depth:1,index:0}
- x10 := struct{ S1 }{S1{1}}
- _printValue(x10.F0) // *ST.S1 > *DT(S1)>*ST.F0
- // F:0 +1 F:0
- // VPField{depth:0,index:0} > VPField{depth:1,index:0}
- _printValue(x10.Hello) // *ST.S1 > *DT(S1).Hello
- // F:0 +1 M:0
- // VPField{depth:0,index:0} > VPValMethod{index:0}
- _printValue(x10.Bye) // (*PT>)*ST.S1 > *DT(S1).Bye
- // +S F:0 +1 *M:1
- // VPSubrefField{depth:0,index:0} > VPDerefPtrMethod{index:1}
- x10p := &x10
- _printValue(x10p.F0) // *PT>*ST.S1 > *DT(S1)>*ST.F0
- // +D F:0 +1 F:0
- // VPDerefField{depth:0,index:0} > VPField{depth:1,index:0}
- _printValue(x10p.Hello) // *PT>*ST.S1 > *DT(S1).Hello
- // +D F:0 +1 M:0
- // VPDerefField{depth:0,index:0} > VPValMethod{index:0}
- _printValue(x10p.Bye) // *PT>*ST.S1 > *DT(S1).Bye
- // +D F:0 +1 *M:1
- // VPSubrefField{depth:0,index:0} > VPDerefPtrMethod{index:1}
- var x10pd S10PD = &struct{ S1 }{S1{1}}
- _printValue(x10pd.F0) // *DT(S10PD)>*PT>*ST.S1 > *DT(S1)>*ST.F0
- // +1 +D F:0 +1 F:0
- // VPDerefField{depth:1,index:0} > VPField{depth:1,index:0}
- // _printValue(x10pd.Hello) *DT(S10PD)>*PT>*ST.S1 > *DT(S1).Hello XXX weird, doesn't work.
- // +1 +D F:0 +1 M:0
- // VPDerefField{depth:1,index:0} > VPValMethod{index:0}
- _printValue(x10p.Bye) // *DT(S10PD)>*PT>*ST.S1 > *DT(S1).Bye
- // +1 +D F:0 +1 *M:1
- // VPSubrefField{depth:1,index:0} > VPDerefPtrMethod{index:1}
- x11 := S7{S1{1}}
- _printValue(x11.F0) // *DT(S7)>*ST.S1 > *DT(S1)>*ST.F0 NOTE same as x7.
- // +1 F:0 +1 F:0
- // VPField{depth:1,index:0} > VPField{depth:1,index:0}
- _printValue(x11.Hello) // *DT(S7)>*ST.S1 > *DT(S1)>*ST.Hello
- // +1 F:0 +1 M:0
- // VPField{depth:1,index:0} > VPValMethod{index:0}
- _printValue(x11.Bye) // (*PT>)*DT(S7)>*ST.S1 > *DT(S1).Bye
- // +S +1 F:0 +1 *M:1
- // VPSubrefField{depth:2,index:0} > VPDerefPtrMethod{index:1}
- x11p := &S7{S1{1}}
- _printValue(x11p.F0) // *PT>*DT(S7)>*ST.S1 > *DT(S1)>*ST.F0
- // +1 F:0 +1 F:0
- // VPDerefField{depth:2,index:0} > VPField{depth:1,index:0}
- _printValue(x11p.Hello) // *PT>*DT(S7)>*ST.S1 > *DT(S1).Hello
- // +1 F:0 +1 M:0
- // VPDerefField{depth:2,index:0} > VPValMethod{index:0}
- _printValue(x11p.Bye) // *PT>*DT(S7)>*ST.S1 > *DT(S1).Bye
- // +1 F:0 +1 *M:1
- // VPSubrefField{depth:2,index:0} > VPDerefPtrMethod{index:1}
- x12 := struct{ *S1 }{&S1{1}}
- _printValue(x12.F0) // *ST.S1 > *PT>*DT(S1)>*ST.F0
- // F:0 +D +1 F:0
- // VPField{depth:0,index:0} > VPDerefField{depth:1,index:0}
- _printValue(x12.Hello) // *ST.S1 > *PT>*DT(S1).Hello
- // F:0 +D +1 M:0
- // VPField{depth:0,index:0} > VPDerefValMethod{index:0}
- _printValue(x12.Bye) // *ST.S1 > *PT>*DT(S1).Bye
- // F:0 +D +1 *M:1
- // VPField{depth:0,index:0} > VPDerefPtrMethod{index:1}
- x13 := &x12
- _printValue(x13.F0) // *PT>*ST.S1 > *PT>*DT(S1)>*ST.F0
- // +D F:0 +D +1 F:0
- // VPDerefField{depth:0,index:0} > VPDerefField{depth:1,index:0}
- _printValue(x13.Hello) // *PT>*ST.S1 > *PT>*DT(S1).Hello
- // +D F:0 +D +1 M:0
- // VPDerefField{depth:0,index:0} > VPDerefValMethod{index:0}
- _printValue(x13.Bye) // *PT>*ST.S1 > *PT>*DT(S1).Bye
- // +D F:0 +D +1 *M:1
- // VPDerefField{depth:0,index:0} > VPDerefPtrMethod{index:1}
-}
diff --git a/gnovm/tests/stdlibs/generated.go b/gnovm/tests/stdlibs/generated.go
index f3d74e214eb..db5ecdec05d 100644
--- a/gnovm/tests/stdlibs/generated.go
+++ b/gnovm/tests/stdlibs/generated.go
@@ -9,6 +9,7 @@ import (
gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
testlibs_std "github.com/gnolang/gno/gnovm/tests/stdlibs/std"
testlibs_testing "github.com/gnolang/gno/gnovm/tests/stdlibs/testing"
+ testlibs_unicode "github.com/gnolang/gno/gnovm/tests/stdlibs/unicode"
)
// NativeFunc represents a function in the standard library which has a native
@@ -84,18 +85,6 @@ var nativeFuncs = [...]NativeFunc{
p0)
},
},
- {
- "std",
- "ClearStoreCache",
- []gno.FieldTypeExpr{},
- []gno.FieldTypeExpr{},
- true,
- func(m *gno.Machine) {
- testlibs_std.ClearStoreCache(
- m,
- )
- },
- },
{
"std",
"callerAt",
@@ -337,6 +326,118 @@ var nativeFuncs = [...]NativeFunc{
func(m *gno.Machine) {
r0 := testlibs_testing.X_unixNano()
+ m.PushValue(gno.Go2GnoValue(
+ m.Alloc,
+ m.Store,
+ reflect.ValueOf(&r0).Elem(),
+ ))
+ },
+ },
+ {
+ "unicode",
+ "IsPrint",
+ []gno.FieldTypeExpr{
+ {Name: gno.N("p0"), Type: gno.X("rune")},
+ },
+ []gno.FieldTypeExpr{
+ {Name: gno.N("r0"), Type: gno.X("bool")},
+ },
+ false,
+ func(m *gno.Machine) {
+ b := m.LastBlock()
+ var (
+ p0 rune
+ rp0 = reflect.ValueOf(&p0).Elem()
+ )
+
+ gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0)
+
+ r0 := testlibs_unicode.IsPrint(p0)
+
+ m.PushValue(gno.Go2GnoValue(
+ m.Alloc,
+ m.Store,
+ reflect.ValueOf(&r0).Elem(),
+ ))
+ },
+ },
+ {
+ "unicode",
+ "IsGraphic",
+ []gno.FieldTypeExpr{
+ {Name: gno.N("p0"), Type: gno.X("rune")},
+ },
+ []gno.FieldTypeExpr{
+ {Name: gno.N("r0"), Type: gno.X("bool")},
+ },
+ false,
+ func(m *gno.Machine) {
+ b := m.LastBlock()
+ var (
+ p0 rune
+ rp0 = reflect.ValueOf(&p0).Elem()
+ )
+
+ gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0)
+
+ r0 := testlibs_unicode.IsGraphic(p0)
+
+ m.PushValue(gno.Go2GnoValue(
+ m.Alloc,
+ m.Store,
+ reflect.ValueOf(&r0).Elem(),
+ ))
+ },
+ },
+ {
+ "unicode",
+ "SimpleFold",
+ []gno.FieldTypeExpr{
+ {Name: gno.N("p0"), Type: gno.X("rune")},
+ },
+ []gno.FieldTypeExpr{
+ {Name: gno.N("r0"), Type: gno.X("rune")},
+ },
+ false,
+ func(m *gno.Machine) {
+ b := m.LastBlock()
+ var (
+ p0 rune
+ rp0 = reflect.ValueOf(&p0).Elem()
+ )
+
+ gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0)
+
+ r0 := testlibs_unicode.SimpleFold(p0)
+
+ m.PushValue(gno.Go2GnoValue(
+ m.Alloc,
+ m.Store,
+ reflect.ValueOf(&r0).Elem(),
+ ))
+ },
+ },
+ {
+ "unicode",
+ "IsUpper",
+ []gno.FieldTypeExpr{
+ {Name: gno.N("p0"), Type: gno.X("rune")},
+ },
+ []gno.FieldTypeExpr{
+ {Name: gno.N("r0"), Type: gno.X("bool")},
+ },
+ false,
+ func(m *gno.Machine) {
+ b := m.LastBlock()
+ var (
+ p0 rune
+ rp0 = reflect.ValueOf(&p0).Elem()
+ )
+
+ gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0)
+
+ r0 := testlibs_unicode.IsUpper(p0)
+
m.PushValue(gno.Go2GnoValue(
m.Alloc,
m.Store,
@@ -349,6 +450,7 @@ var nativeFuncs = [...]NativeFunc{
var initOrder = [...]string{
"std",
"testing",
+ "unicode",
}
// InitOrder returns the initialization order of the standard libraries.
diff --git a/gnovm/tests/stdlibs/std/std.gno b/gnovm/tests/stdlibs/std/std.gno
index 3a56ecc1c47..dcb5a64dbb3 100644
--- a/gnovm/tests/stdlibs/std/std.gno
+++ b/gnovm/tests/stdlibs/std/std.gno
@@ -3,7 +3,6 @@ package std
func AssertOriginCall() // injected
func IsOriginCall() bool // injected
func TestSkipHeights(count int64) // injected
-func ClearStoreCache() // injected
func TestSetOrigCaller(addr Address) { testSetOrigCaller(string(addr)) }
func TestSetOrigPkgAddr(addr Address) { testSetOrigPkgAddr(string(addr)) }
diff --git a/gnovm/tests/stdlibs/std/std.go b/gnovm/tests/stdlibs/std/std.go
index d580572e9c5..675194b252f 100644
--- a/gnovm/tests/stdlibs/std/std.go
+++ b/gnovm/tests/stdlibs/std/std.go
@@ -3,11 +3,11 @@ package std
import (
"fmt"
"strings"
- "testing"
gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
"github.com/gnolang/gno/gnovm/stdlibs/std"
"github.com/gnolang/gno/tm2/pkg/crypto"
+ tm2std "github.com/gnolang/gno/tm2/pkg/std"
)
// TestExecContext is the testing extension of the exec context.
@@ -41,9 +41,17 @@ func IsOriginCall(m *gno.Machine) bool {
tname := m.Frames[0].Func.Name
switch tname {
case "main": // test is a _filetest
+ // 0. main
+ // 1. $RealmFuncName
+ // 2. std.IsOriginCall
return len(m.Frames) == 3
- case "runtest": // test is a _test
- return len(m.Frames) == 7
+ case "RunTest": // test is a _test
+ // 0. testing.RunTest
+ // 1. tRunner
+ // 2. $TestFuncName
+ // 3. $RealmFuncName
+ // 4. std.IsOriginCall
+ return len(m.Frames) == 5
}
// support init() in _filetest
// XXX do we need to distinguish from 'runtest'/_test?
@@ -61,23 +69,6 @@ func TestSkipHeights(m *gno.Machine, count int64) {
m.Context = ctx
}
-func ClearStoreCache(m *gno.Machine) {
- if gno.IsDebug() && testing.Verbose() {
- m.Store.Print()
- fmt.Println("========================================")
- fmt.Println("CLEAR CACHE (RUNTIME)")
- fmt.Println("========================================")
- }
- m.Store.ClearCache()
- m.PreprocessAllFilesAndSaveBlockNodes()
- if gno.IsDebug() && testing.Verbose() {
- m.Store.Print()
- fmt.Println("========================================")
- fmt.Println("CLEAR CACHE DONE")
- fmt.Println("========================================")
- }
-}
-
func X_callerAt(m *gno.Machine, n int) string {
if n <= 0 {
m.Panic(typedString("GetCallerAt requires positive arg"))
@@ -188,6 +179,60 @@ func X_testSetOrigSend(m *gno.Machine,
m.Context = ctx
}
+// TestBanker is a banker that can be used as a mock banker in test contexts.
+type TestBanker struct {
+ CoinTable map[crypto.Bech32Address]tm2std.Coins
+}
+
+var _ std.BankerInterface = &TestBanker{}
+
+// GetCoins implements the Banker interface.
+func (tb *TestBanker) GetCoins(addr crypto.Bech32Address) (dst tm2std.Coins) {
+ return tb.CoinTable[addr]
+}
+
+// SendCoins implements the Banker interface.
+func (tb *TestBanker) SendCoins(from, to crypto.Bech32Address, amt tm2std.Coins) {
+ fcoins, fexists := tb.CoinTable[from]
+ if !fexists {
+ panic(fmt.Sprintf(
+ "source address %s does not exist",
+ from.String()))
+ }
+ if !fcoins.IsAllGTE(amt) {
+ panic(fmt.Sprintf(
+ "source address %s has %s; cannot send %s",
+ from.String(), fcoins, amt))
+ }
+ // First, subtract from 'from'.
+ frest := fcoins.Sub(amt)
+ tb.CoinTable[from] = frest
+ // Second, add to 'to'.
+ // NOTE: even works when from==to, due to 2-step isolation.
+ tcoins, _ := tb.CoinTable[to]
+ tsum := tcoins.Add(amt)
+ tb.CoinTable[to] = tsum
+}
+
+// TotalCoin implements the Banker interface.
+func (tb *TestBanker) TotalCoin(denom string) int64 {
+ panic("not yet implemented")
+}
+
+// IssueCoin implements the Banker interface.
+func (tb *TestBanker) IssueCoin(addr crypto.Bech32Address, denom string, amt int64) {
+ coins, _ := tb.CoinTable[addr]
+ sum := coins.Add(tm2std.Coins{{Denom: denom, Amount: amt}})
+ tb.CoinTable[addr] = sum
+}
+
+// RemoveCoin implements the Banker interface.
+func (tb *TestBanker) RemoveCoin(addr crypto.Bech32Address, denom string, amt int64) {
+ coins, _ := tb.CoinTable[addr]
+ rest := coins.Sub(tm2std.Coins{{Denom: denom, Amount: amt}})
+ tb.CoinTable[addr] = rest
+}
+
func X_testIssueCoins(m *gno.Machine, addr string, denom []string, amt []int64) {
ctx := m.Context.(*TestExecContext)
banker := ctx.Banker
diff --git a/gnovm/tests/stdlibs/stdlibs.go b/gnovm/tests/stdlibs/stdlibs.go
index b0a1050af41..92316bf41fd 100644
--- a/gnovm/tests/stdlibs/stdlibs.go
+++ b/gnovm/tests/stdlibs/stdlibs.go
@@ -8,11 +8,11 @@ import (
//go:generate go run github.com/gnolang/gno/misc/genstd
-func NativeStore(pkgPath string, name gno.Name) func(*gno.Machine) {
+func NativeResolver(pkgPath string, name gno.Name) func(*gno.Machine) {
for _, nf := range nativeFuncs {
if nf.gnoPkg == pkgPath && name == nf.gnoFunc {
return nf.f
}
}
- return stdlibs.NativeStore(pkgPath, name)
+ return stdlibs.NativeResolver(pkgPath, name)
}
diff --git a/gnovm/tests/stdlibs/unicode/natives.gno b/gnovm/tests/stdlibs/unicode/natives.gno
new file mode 100644
index 00000000000..c7efaac70cc
--- /dev/null
+++ b/gnovm/tests/stdlibs/unicode/natives.gno
@@ -0,0 +1,8 @@
+package unicode
+
+// Optimized as native bindings in tests.
+
+func IsPrint(r rune) bool
+func IsGraphic(r rune) bool
+func SimpleFold(r rune) rune
+func IsUpper(r rune) bool
diff --git a/gnovm/tests/stdlibs/unicode/natives.go b/gnovm/tests/stdlibs/unicode/natives.go
new file mode 100644
index 00000000000..e627f4fe6be
--- /dev/null
+++ b/gnovm/tests/stdlibs/unicode/natives.go
@@ -0,0 +1,8 @@
+package unicode
+
+import "unicode"
+
+func IsPrint(r rune) bool { return unicode.IsPrint(r) }
+func IsGraphic(r rune) bool { return unicode.IsGraphic(r) }
+func SimpleFold(r rune) rune { return unicode.SimpleFold(r) }
+func IsUpper(r rune) bool { return unicode.IsUpper(r) }
diff --git a/gnovm/tests/testdata/TestMemPackage/fail/file_test.gno b/gnovm/tests/testdata/TestMemPackage/fail/file_test.gno
deleted file mode 100644
index b202c40bc46..00000000000
--- a/gnovm/tests/testdata/TestMemPackage/fail/file_test.gno
+++ /dev/null
@@ -1,7 +0,0 @@
-package test
-
-import "testing"
-
-func TestFail(t *testing.T) {
- t.Errorf("OUPS")
-}
diff --git a/gnovm/tests/testdata/TestMemPackage/success/file_test.gno b/gnovm/tests/testdata/TestMemPackage/success/file_test.gno
deleted file mode 100644
index 0fc1d898199..00000000000
--- a/gnovm/tests/testdata/TestMemPackage/success/file_test.gno
+++ /dev/null
@@ -1,5 +0,0 @@
-package test
-
-import "testing"
-
-func TestSucess(t *testing.T) {}
diff --git a/go.mod b/go.mod
index f73ba1926e6..9862ce018f2 100644
--- a/go.mod
+++ b/go.mod
@@ -6,6 +6,7 @@ toolchain go1.22.4
require (
dario.cat/mergo v1.0.1
+ github.com/alecthomas/chroma/v2 v2.14.0
github.com/btcsuite/btcd/btcec/v2 v2.3.4
github.com/btcsuite/btcd/btcutil v1.1.6
github.com/cockroachdb/apd/v3 v3.2.1
@@ -13,11 +14,8 @@ require (
github.com/davecgh/go-spew v1.1.1
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0
github.com/fortytw2/leaktest v1.3.0
- github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216
github.com/google/gofuzz v1.2.0
- github.com/gorilla/mux v1.8.1
github.com/gorilla/websocket v1.5.3
- github.com/gotuna/gotuna v0.6.0
github.com/libp2p/go-buffer-pool v0.1.0
github.com/pelletier/go-toml v1.9.5
github.com/peterbourgon/ff/v3 v3.4.0
@@ -27,6 +25,7 @@ require (
github.com/rs/xid v1.6.0
github.com/stretchr/testify v1.9.0
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7
+ github.com/yuin/goldmark v1.7.2
go.etcd.io/bbolt v1.3.11
go.opentelemetry.io/otel v1.29.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.29.0
@@ -49,13 +48,12 @@ require (
require (
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
+ github.com/dlclark/regexp2 v1.11.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/uuid v1.6.0 // indirect
- github.com/gorilla/securecookie v1.1.1 // indirect
- github.com/gorilla/sessions v1.2.1 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
github.com/nxadm/tail v1.4.11 // indirect
github.com/pkg/errors v0.9.1 // indirect
diff --git a/go.sum b/go.sum
index 78d60eeea90..39b8e0efa38 100644
--- a/go.sum
+++ b/go.sum
@@ -1,6 +1,12 @@
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
+github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE=
+github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
+github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E=
+github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I=
+github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
+github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M=
github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A=
@@ -45,14 +51,14 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeC
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
+github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
+github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
-github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 h1:GKvsK3oLWG9B1GL7WP/VqwM6C92j5tIvB844oggL9Lk=
-github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216/go.mod h1:xJhtEL7ahjM1WJipt89gel8tHzfIl/LyMY+lCYh38d8=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
@@ -78,21 +84,13 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/gorilla/csrf v1.7.0/go.mod h1:+a/4tCmqhG6/w4oafeAZ9pEa3/NZOWYVbD9fV0FwIQA=
-github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
-github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
-github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
-github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
-github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
-github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
-github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/gotuna/gotuna v0.6.0 h1:N1lQKXEi/lwRp8u3sccTYLhzOffA4QasExz/1M5Riws=
-github.com/gotuna/gotuna v0.6.0/go.mod h1:F/ecRt29ChB6Ycy1AFIBpBiMNK0j7Heq+gFbLWquhjc=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I=
+github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
+github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
@@ -144,6 +142,8 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
+github.com/yuin/goldmark v1.7.2 h1:NjGd7lO7zrUn/A7eKwn5PEOt4ONYGqpxSEeZuduvgxc=
+github.com/yuin/goldmark v1.7.2/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U=
github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM=
github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw=
diff --git a/misc/autocounterd/go.mod b/misc/autocounterd/go.mod
index 5de1d3c2974..30a6f23b458 100644
--- a/misc/autocounterd/go.mod
+++ b/misc/autocounterd/go.mod
@@ -14,7 +14,6 @@ require (
github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
- github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
diff --git a/misc/autocounterd/go.sum b/misc/autocounterd/go.sum
index b34cbde0c00..5d624ca18cb 100644
--- a/misc/autocounterd/go.sum
+++ b/misc/autocounterd/go.sum
@@ -50,8 +50,6 @@ github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHqu
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 h1:GKvsK3oLWG9B1GL7WP/VqwM6C92j5tIvB844oggL9Lk=
-github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216/go.mod h1:xJhtEL7ahjM1WJipt89gel8tHzfIl/LyMY+lCYh38d8=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
diff --git a/misc/deployments/staging.gno.land/docker-compose.yml b/misc/deployments/staging.gno.land/docker-compose.yml
index 7d264a34dbd..7e70ad5da62 100644
--- a/misc/deployments/staging.gno.land/docker-compose.yml
+++ b/misc/deployments/staging.gno.land/docker-compose.yml
@@ -60,10 +60,9 @@ services:
- gnoweb
- --bind=0.0.0.0:8888
- --remote=http://traefik:26657
- - --faucet-url=https://faucet-api.staging.gno.land
- - --captcha-site=$CAPTCHA_SITE_KEY
- --with-analytics
- - --help-chainid=staging
+ - --chainid=staging
+ - --faucet-url=https://faucet-api.staging.gno.land
- --help-remote=https://rpc.staging.gno.land:443
labels:
com.centurylinklabs.watchtower.enable: "true"
diff --git a/misc/deployments/test4.gno.land/README.md b/misc/deployments/test4.gno.land/README.md
index 6277ea996ec..7a3a7d06b28 100644
--- a/misc/deployments/test4.gno.land/README.md
+++ b/misc/deployments/test4.gno.land/README.md
@@ -4,7 +4,7 @@ This deployment folder contains minimal information needed to launch a full test
## `genesis.json`
-The initial `genesis.json` validator set is consisted of 3 entities (7 validators in total):
+The initial `genesis.json` validator set consisted of 3 entities (7 validators in total):
- Gno Core - the gno core team (**4 validators**)
- Gno DevX - the gno devX team (**2 validators**)
@@ -37,4 +37,4 @@ Some configuration params are required, while others are advised to be set.
- `rpc.laddr` - the JSON-RPC listen address, **specific to every node deployment**.
- `telemetry.enabled` - flag indicating if telemetry should be turned on. **Advised to be `true`**.
- `telemetry.exporter_endpoint` - endpoint for the otel exported. ⚠️ **Required if `telemetry.enabled=true`** ⚠️.
-- `telemetry.service_instance_id` - unique ID of the node telemetry instance, **specific to every node deployment**.
\ No newline at end of file
+- `telemetry.service_instance_id` - unique ID of the node telemetry instance, **specific to every node deployment**.
diff --git a/misc/deployments/test5.gno.land/README.md b/misc/deployments/test5.gno.land/README.md
index 3dcbf79f2ec..40da91f3b74 100644
--- a/misc/deployments/test5.gno.land/README.md
+++ b/misc/deployments/test5.gno.land/README.md
@@ -9,7 +9,7 @@ The initial `genesis.json` validator set is consisted of 6 entities (17 validato
- Gno Core - the gno core team (**6 validators**)
- Gno DevX - the gno devX team (**4 validators**)
- AiB - the AiB DevOps team (**3 validators**)
-- Onbloc - the [Onbloc](https://onbloc.xyz/) team (**2 validator**)
+- Onbloc - the [Onbloc](https://onbloc.xyz/) team (**2 validators**)
- Teritori - the [Teritori](https://teritori.com/) team (**1 validator**)
- Berty - the [Berty](https://berty.tech/) team (**1 validator**)
diff --git a/misc/devdeps/deps.go b/misc/devdeps/deps.go
index a011868e4c2..f7da2b10c12 100644
--- a/misc/devdeps/deps.go
+++ b/misc/devdeps/deps.go
@@ -15,7 +15,6 @@ import (
_ "golang.org/x/tools/cmd/goimports"
// required for formatting, linting, pls.
- _ "golang.org/x/tools/gopls"
_ "mvdan.cc/gofumpt"
// protoc, genproto
diff --git a/misc/devdeps/go.mod b/misc/devdeps/go.mod
index c07b82fd11d..d3b40b73b52 100644
--- a/misc/devdeps/go.mod
+++ b/misc/devdeps/go.mod
@@ -1,46 +1,42 @@
module github.com/gnolang/gno/misc/devdeps
-go 1.22
-
-toolchain go1.22.4
+go 1.22.1
require (
- github.com/golangci/golangci-lint v1.59.1 // sync with github action
- golang.org/x/tools v0.22.1-0.20240628205440-9c895dd76b34
- golang.org/x/tools/gopls v0.16.1
+ github.com/campoy/embedmd v1.0.0
+ github.com/golangci/golangci-lint v1.62.2 // sync with github action
+ golang.org/x/tools v0.27.0
google.golang.org/protobuf v1.35.1
moul.io/testman v1.5.0
- mvdan.cc/gofumpt v0.6.0
+ mvdan.cc/gofumpt v0.7.0
)
-require github.com/campoy/embedmd v1.0.0
-
require (
4d63.com/gocheckcompilerdirectives v1.2.1 // indirect
4d63.com/gochecknoglobals v0.2.1 // indirect
github.com/4meepo/tagalign v1.3.4 // indirect
- github.com/Abirdcfly/dupword v0.0.14 // indirect
- github.com/Antonboom/errname v0.1.13 // indirect
- github.com/Antonboom/nilnil v0.1.9 // indirect
- github.com/Antonboom/testifylint v1.3.1 // indirect
- github.com/BurntSushi/toml v1.4.0 // indirect
- github.com/Crocmagnon/fatcontext v0.2.2 // indirect
+ github.com/Abirdcfly/dupword v0.1.3 // indirect
+ github.com/Antonboom/errname v1.0.0 // indirect
+ github.com/Antonboom/nilnil v1.0.0 // indirect
+ github.com/Antonboom/testifylint v1.5.2 // indirect
+ github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect
+ github.com/Crocmagnon/fatcontext v0.5.3 // indirect
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect
- github.com/GaijinEntertainment/go-exhaustruct/v3 v3.2.0 // indirect
- github.com/Masterminds/semver/v3 v3.2.1 // indirect
+ github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0 // indirect
+ github.com/Masterminds/semver/v3 v3.3.0 // indirect
github.com/OpenPeeDeeP/depguard/v2 v2.2.0 // indirect
- github.com/alecthomas/go-check-sumtype v0.1.4 // indirect
- github.com/alexkohler/nakedret/v2 v2.0.4 // indirect
+ github.com/alecthomas/go-check-sumtype v0.2.0 // indirect
+ github.com/alexkohler/nakedret/v2 v2.0.5 // indirect
github.com/alexkohler/prealloc v1.0.0 // indirect
github.com/alingse/asasalint v0.0.11 // indirect
github.com/ashanbrown/forbidigo v1.6.0 // indirect
github.com/ashanbrown/makezero v1.1.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
- github.com/bkielbasa/cyclop v1.2.1 // indirect
+ github.com/bkielbasa/cyclop v1.2.3 // indirect
github.com/blizzy78/varnamelen v0.8.0 // indirect
- github.com/bombsimon/wsl/v4 v4.2.1 // indirect
- github.com/breml/bidichk v0.2.7 // indirect
- github.com/breml/errchkjson v0.3.6 // indirect
+ github.com/bombsimon/wsl/v4 v4.4.1 // indirect
+ github.com/breml/bidichk v0.3.2 // indirect
+ github.com/breml/errchkjson v0.4.0 // indirect
github.com/butuzov/ireturn v0.3.0 // indirect
github.com/butuzov/mirror v1.2.0 // indirect
github.com/catenacyber/perfsprint v0.7.1 // indirect
@@ -48,19 +44,19 @@ require (
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/charithe/durationcheck v0.0.10 // indirect
github.com/chavacava/garif v0.1.0 // indirect
- github.com/ckaznocha/intrange v0.1.2 // indirect
+ github.com/ckaznocha/intrange v0.2.1 // indirect
github.com/curioswitch/go-reassign v0.2.0 // indirect
- github.com/daixiang0/gci v0.13.4 // indirect
+ github.com/daixiang0/gci v0.13.5 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/denis-tingaikin/go-header v0.5.0 // indirect
github.com/ettle/strcase v0.2.0 // indirect
- github.com/fatih/color v1.17.0 // indirect
+ github.com/fatih/color v1.18.0 // indirect
github.com/fatih/structtag v1.2.0 // indirect
github.com/firefart/nonamedreturns v1.0.5 // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/fzipp/gocyclo v0.6.0 // indirect
- github.com/ghostiam/protogetter v0.3.6 // indirect
- github.com/go-critic/go-critic v0.11.4 // indirect
+ github.com/ghostiam/protogetter v0.3.8 // indirect
+ github.com/go-critic/go-critic v0.11.5 // indirect
github.com/go-toolsmith/astcast v1.1.0 // indirect
github.com/go-toolsmith/astcopy v1.1.0 // indirect
github.com/go-toolsmith/astequal v1.2.0 // indirect
@@ -68,13 +64,14 @@ require (
github.com/go-toolsmith/astp v1.1.0 // indirect
github.com/go-toolsmith/strparse v1.1.0 // indirect
github.com/go-toolsmith/typep v1.1.0 // indirect
- github.com/go-viper/mapstructure/v2 v2.0.0 // indirect
+ github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/go-xmlfmt/xmlfmt v1.1.2 // indirect
github.com/gobwas/glob v0.2.3 // indirect
- github.com/gofrs/flock v0.8.1 // indirect
+ github.com/gofrs/flock v0.12.1 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a // indirect
- github.com/golangci/gofmt v0.0.0-20231018234816-f50ced29576e // indirect
+ github.com/golangci/go-printf-func-name v0.1.0 // indirect
+ github.com/golangci/gofmt v0.0.0-20240816233607-d8596aa466a9 // indirect
github.com/golangci/misspell v0.6.0 // indirect
github.com/golangci/modinfo v0.3.4 // indirect
github.com/golangci/plugin-module-register v0.1.1 // indirect
@@ -92,20 +89,18 @@ require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jgautheron/goconst v1.7.1 // indirect
github.com/jingyugao/rowserrcheck v1.1.1 // indirect
- github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af // indirect
- github.com/jjti/go-spancheck v0.6.1 // indirect
+ github.com/jjti/go-spancheck v0.6.2 // indirect
github.com/julz/importas v0.1.0 // indirect
github.com/karamaru-alpha/copyloopvar v1.1.0 // indirect
- github.com/kisielk/errcheck v1.7.0 // indirect
+ github.com/kisielk/errcheck v1.8.0 // indirect
github.com/kkHAIKE/contextcheck v1.1.5 // indirect
github.com/kulti/thelper v0.6.3 // indirect
github.com/kunwardeep/paralleltest v1.0.10 // indirect
github.com/kyoh86/exportloopref v0.1.11 // indirect
- github.com/lasiar/canonicalheader v1.1.1 // indirect
+ github.com/lasiar/canonicalheader v1.1.2 // indirect
github.com/ldez/gomoddirectives v0.2.4 // indirect
github.com/ldez/tagliatelle v0.5.0 // indirect
github.com/leonklingele/grouper v1.1.2 // indirect
- github.com/lufeee/execinquery v1.2.1 // indirect
github.com/macabu/inamedparam v0.1.3 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/maratori/testableexamples v1.0.0 // indirect
@@ -113,91 +108,91 @@ require (
github.com/matoous/godox v0.0.0-20230222163458-006bad1f9d26 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
- github.com/mattn/go-runewidth v0.0.9 // indirect
+ github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
- github.com/mgechev/revive v1.3.7 // indirect
+ github.com/mgechev/revive v1.5.1 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
- github.com/moricho/tparallel v0.3.1 // indirect
+ github.com/moricho/tparallel v0.3.2 // indirect
github.com/nakabonne/nestif v0.3.1 // indirect
github.com/nishanths/exhaustive v0.12.0 // indirect
github.com/nishanths/predeclared v0.2.2 // indirect
- github.com/nunnatsa/ginkgolinter v0.16.2 // indirect
+ github.com/nunnatsa/ginkgolinter v0.18.3 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
- github.com/pelletier/go-toml/v2 v2.2.2 // indirect
+ github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/peterbourgon/ff/v3 v3.3.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
- github.com/polyfloyd/go-errorlint v1.5.2 // indirect
+ github.com/polyfloyd/go-errorlint v1.7.0 // indirect
github.com/prometheus/client_golang v1.12.1 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
- github.com/quasilyte/go-ruleguard v0.4.2 // indirect
+ github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1 // indirect
github.com/quasilyte/go-ruleguard/dsl v0.3.22 // indirect
github.com/quasilyte/gogrep v0.5.0 // indirect
github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect
- github.com/ryancurrah/gomodguard v1.3.2 // indirect
+ github.com/raeperd/recvcheck v0.1.2 // indirect
+ github.com/rivo/uniseg v0.4.7 // indirect
+ github.com/rogpeppe/go-internal v1.13.1 // indirect
+ github.com/ryancurrah/gomodguard v1.3.5 // indirect
github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect
github.com/sanposhiho/wastedassign/v2 v2.0.7 // indirect
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect
github.com/sashamelentyev/interfacebloat v1.1.0 // indirect
- github.com/sashamelentyev/usestdlibvars v1.26.0 // indirect
- github.com/securego/gosec/v2 v2.20.1-0.20240525090044-5f0084eb01a9 // indirect
+ github.com/sashamelentyev/usestdlibvars v1.27.0 // indirect
+ github.com/securego/gosec/v2 v2.21.4 // indirect
github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/sivchari/containedctx v1.0.3 // indirect
- github.com/sivchari/tenv v1.7.1 // indirect
- github.com/sonatard/noctx v0.0.2 // indirect
+ github.com/sivchari/tenv v1.12.1 // indirect
+ github.com/sonatard/noctx v0.1.0 // indirect
github.com/sourcegraph/go-diff v0.7.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.5.0 // indirect
- github.com/spf13/cobra v1.7.0 // indirect
+ github.com/spf13/cobra v1.8.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.12.0 // indirect
github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect
github.com/stbenjam/no-sprintf-host-port v0.1.1 // indirect
github.com/stretchr/objx v0.5.2 // indirect
- github.com/stretchr/testify v1.9.0 // indirect
+ github.com/stretchr/testify v1.10.0 // indirect
github.com/subosito/gotenv v1.4.1 // indirect
- github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c // indirect
github.com/tdakkota/asciicheck v0.2.0 // indirect
- github.com/tetafro/godot v1.4.16 // indirect
+ github.com/tetafro/godot v1.4.18 // indirect
github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 // indirect
- github.com/timonwong/loggercheck v0.9.4 // indirect
- github.com/tomarrell/wrapcheck/v2 v2.8.3 // indirect
+ github.com/timonwong/loggercheck v0.10.1 // indirect
+ github.com/tomarrell/wrapcheck/v2 v2.9.0 // indirect
github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect
github.com/ultraware/funlen v0.1.0 // indirect
github.com/ultraware/whitespace v0.1.1 // indirect
- github.com/uudashr/gocognit v1.1.2 // indirect
+ github.com/uudashr/gocognit v1.1.3 // indirect
+ github.com/uudashr/iface v1.2.1 // indirect
github.com/xen0n/gosmopolitan v1.2.2 // indirect
github.com/yagipy/maintidx v1.0.0 // indirect
github.com/yeya24/promlinter v0.3.0 // indirect
github.com/ykadowak/zerologlint v0.1.5 // indirect
github.com/yuin/goldmark v1.4.13 // indirect
gitlab.com/bosi/decorder v0.4.2 // indirect
- go-simpler.org/musttag v0.12.2 // indirect
- go-simpler.org/sloglint v0.7.1 // indirect
- go.uber.org/automaxprocs v1.5.3 // indirect
+ go-simpler.org/musttag v0.13.0 // indirect
+ go-simpler.org/sloglint v0.7.2 // indirect
+ go.uber.org/automaxprocs v1.6.0 // indirect
go.uber.org/multierr v1.10.0 // indirect
go.uber.org/zap v1.26.0 // indirect
- golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc // indirect
- golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f // indirect
- golang.org/x/mod v0.18.0 // indirect
- golang.org/x/sync v0.7.0 // indirect
- golang.org/x/sys v0.21.0 // indirect
- golang.org/x/telemetry v0.0.0-20240607193123-221703e18637 // indirect
- golang.org/x/text v0.16.0 // indirect
- golang.org/x/vuln v1.0.4 // indirect
+ golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
+ golang.org/x/exp/typeparams v0.0.0-20241108190413-2d47ceb2692f // indirect
+ golang.org/x/mod v0.22.0 // indirect
+ golang.org/x/sync v0.9.0 // indirect
+ golang.org/x/sys v0.27.0 // indirect
+ golang.org/x/text v0.18.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
- honnef.co/go/tools v0.4.7 // indirect
+ honnef.co/go/tools v0.5.1 // indirect
moul.io/banner v1.0.1 // indirect
moul.io/motd v1.0.0 // indirect
moul.io/u v1.27.0 // indirect
mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f // indirect
- mvdan.cc/xurls/v2 v2.5.0 // indirect
)
diff --git a/misc/devdeps/go.sum b/misc/devdeps/go.sum
index e19e47d0c56..fcba3fba624 100644
--- a/misc/devdeps/go.sum
+++ b/misc/devdeps/go.sum
@@ -37,32 +37,32 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/4meepo/tagalign v1.3.4 h1:P51VcvBnf04YkHzjfclN6BbsopfJR5rxs1n+5zHt+w8=
github.com/4meepo/tagalign v1.3.4/go.mod h1:M+pnkHH2vG8+qhE5bVc/zeP7HS/j910Fwa9TUSyZVI0=
-github.com/Abirdcfly/dupword v0.0.14 h1:3U4ulkc8EUo+CaT105/GJ1BQwtgyj6+VaBVbAX11Ba8=
-github.com/Abirdcfly/dupword v0.0.14/go.mod h1:VKDAbxdY8YbKUByLGg8EETzYSuC4crm9WwI6Y3S0cLI=
-github.com/Antonboom/errname v0.1.13 h1:JHICqsewj/fNckzrfVSe+T33svwQxmjC+1ntDsHOVvM=
-github.com/Antonboom/errname v0.1.13/go.mod h1:uWyefRYRN54lBg6HseYCFhs6Qjcy41Y3Jl/dVhA87Ns=
-github.com/Antonboom/nilnil v0.1.9 h1:eKFMejSxPSA9eLSensFmjW2XTgTwJMjZ8hUHtV4s/SQ=
-github.com/Antonboom/nilnil v0.1.9/go.mod h1:iGe2rYwCq5/Me1khrysB4nwI7swQvjclR8/YRPl5ihQ=
-github.com/Antonboom/testifylint v1.3.1 h1:Uam4q1Q+2b6H7gvk9RQFw6jyVDdpzIirFOOrbs14eG4=
-github.com/Antonboom/testifylint v1.3.1/go.mod h1:NV0hTlteCkViPW9mSR4wEMfwp+Hs1T3dY60bkvSfhpM=
+github.com/Abirdcfly/dupword v0.1.3 h1:9Pa1NuAsZvpFPi9Pqkd93I7LIYRURj+A//dFd5tgBeE=
+github.com/Abirdcfly/dupword v0.1.3/go.mod h1:8VbB2t7e10KRNdwTVoxdBaxla6avbhGzb8sCTygUMhw=
+github.com/Antonboom/errname v1.0.0 h1:oJOOWR07vS1kRusl6YRSlat7HFnb3mSfMl6sDMRoTBA=
+github.com/Antonboom/errname v1.0.0/go.mod h1:gMOBFzK/vrTiXN9Oh+HFs+e6Ndl0eTFbtsRTSRdXyGI=
+github.com/Antonboom/nilnil v1.0.0 h1:n+v+B12dsE5tbAqRODXmEKfZv9j2KcTBrp+LkoM4HZk=
+github.com/Antonboom/nilnil v1.0.0/go.mod h1:fDJ1FSFoLN6yoG65ANb1WihItf6qt9PJVTn/s2IrcII=
+github.com/Antonboom/testifylint v1.5.2 h1:4s3Xhuv5AvdIgbd8wOOEeo0uZG7PbDKQyKY5lGoQazk=
+github.com/Antonboom/testifylint v1.5.2/go.mod h1:vxy8VJ0bc6NavlYqjZfmp6EfqXMtBgQ4+mhCojwC1P8=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
-github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
+github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs=
+github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
-github.com/Crocmagnon/fatcontext v0.2.2 h1:OrFlsDdOj9hW/oBEJBNSuH7QWf+E9WPVHw+x52bXVbk=
-github.com/Crocmagnon/fatcontext v0.2.2/go.mod h1:WSn/c/+MMNiD8Pri0ahRj0o9jVpeowzavOQplBJw6u0=
+github.com/Crocmagnon/fatcontext v0.5.3 h1:zCh/wjc9oyeF+Gmp+V60wetm8ph2tlsxocgg/J0hOps=
+github.com/Crocmagnon/fatcontext v0.5.3/go.mod h1:XoCQYY1J+XTfyv74qLXvNw4xFunr3L1wkopIIKG7wGM=
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM=
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs=
-github.com/GaijinEntertainment/go-exhaustruct/v3 v3.2.0 h1:sATXp1x6/axKxz2Gjxv8MALP0bXaNRfQinEwyfMcx8c=
-github.com/GaijinEntertainment/go-exhaustruct/v3 v3.2.0/go.mod h1:Nl76DrGNJTA1KJ0LePKBw/vznBX1EHbAZX8mwjR82nI=
-github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
-github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
+github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0 h1:/fTUt5vmbkAcMBt4YQiuC23cV0kEsN1MVMNqeOW43cU=
+github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0/go.mod h1:ONJg5sxcbsdQQ4pOW8TGdTidT2TMAUy/2Xhr8mrYaao=
+github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
+github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/OpenPeeDeeP/depguard/v2 v2.2.0 h1:vDfG60vDtIuf0MEOhmLlLLSzqaRM8EMcgJPdp74zmpA=
github.com/OpenPeeDeeP/depguard/v2 v2.2.0/go.mod h1:CIzddKRvLBC4Au5aYP/i3nyaWQ+ClszLIuVocRiCYFQ=
github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk=
github.com/alecthomas/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ=
-github.com/alecthomas/go-check-sumtype v0.1.4 h1:WCvlB3l5Vq5dZQTFmodqL2g68uHiSwwlWcT5a2FGK0c=
-github.com/alecthomas/go-check-sumtype v0.1.4/go.mod h1:WyYPfhfkdhyrdaligV6svFopZV8Lqdzn5pyVBaV6jhQ=
+github.com/alecthomas/go-check-sumtype v0.2.0 h1:Bo+e4DFf3rs7ME9w/0SU/g6nmzJaphduP8Cjiz0gbwY=
+github.com/alecthomas/go-check-sumtype v0.2.0/go.mod h1:WyYPfhfkdhyrdaligV6svFopZV8Lqdzn5pyVBaV6jhQ=
github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@@ -70,8 +70,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
-github.com/alexkohler/nakedret/v2 v2.0.4 h1:yZuKmjqGi0pSmjGpOC016LtPJysIL0WEUiaXW5SUnNg=
-github.com/alexkohler/nakedret/v2 v2.0.4/go.mod h1:bF5i0zF2Wo2o4X4USt9ntUWve6JbFv02Ff4vlkmS/VU=
+github.com/alexkohler/nakedret/v2 v2.0.5 h1:fP5qLgtwbx9EJE8dGEERT02YwS8En4r9nnZ71RK+EVU=
+github.com/alexkohler/nakedret/v2 v2.0.5/go.mod h1:bF5i0zF2Wo2o4X4USt9ntUWve6JbFv02Ff4vlkmS/VU=
github.com/alexkohler/prealloc v1.0.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pOcUuw=
github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE=
github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQRnw=
@@ -84,16 +84,16 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
-github.com/bkielbasa/cyclop v1.2.1 h1:AeF71HZDob1P2/pRm1so9cd1alZnrpyc4q2uP2l0gJY=
-github.com/bkielbasa/cyclop v1.2.1/go.mod h1:K/dT/M0FPAiYjBgQGau7tz+3TMh4FWAEqlMhzFWCrgM=
+github.com/bkielbasa/cyclop v1.2.3 h1:faIVMIGDIANuGPWH031CZJTi2ymOQBULs9H21HSMa5w=
+github.com/bkielbasa/cyclop v1.2.3/go.mod h1:kHTwA9Q0uZqOADdupvcFJQtp/ksSnytRMe8ztxG8Fuo=
github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M=
github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k=
-github.com/bombsimon/wsl/v4 v4.2.1 h1:Cxg6u+XDWff75SIFFmNsqnIOgob+Q9hG6y/ioKbRFiM=
-github.com/bombsimon/wsl/v4 v4.2.1/go.mod h1:Xu/kDxGZTofQcDGCtQe9KCzhHphIe0fDuyWTxER9Feo=
-github.com/breml/bidichk v0.2.7 h1:dAkKQPLl/Qrk7hnP6P+E0xOodrq8Us7+U0o4UBOAlQY=
-github.com/breml/bidichk v0.2.7/go.mod h1:YodjipAGI9fGcYM7II6wFvGhdMYsC5pHDlGzqvEW3tQ=
-github.com/breml/errchkjson v0.3.6 h1:VLhVkqSBH96AvXEyclMR37rZslRrY2kcyq+31HCsVrA=
-github.com/breml/errchkjson v0.3.6/go.mod h1:jhSDoFheAF2RSDOlCfhHO9KqhZgAYLyvHe7bRCX8f/U=
+github.com/bombsimon/wsl/v4 v4.4.1 h1:jfUaCkN+aUpobrMO24zwyAMwMAV5eSziCkOKEauOLdw=
+github.com/bombsimon/wsl/v4 v4.4.1/go.mod h1:Xu/kDxGZTofQcDGCtQe9KCzhHphIe0fDuyWTxER9Feo=
+github.com/breml/bidichk v0.3.2 h1:xV4flJ9V5xWTqxL+/PMFF6dtJPvZLPsyixAoPe8BGJs=
+github.com/breml/bidichk v0.3.2/go.mod h1:VzFLBxuYtT23z5+iVkamXO386OB+/sVwZOpIj6zXGos=
+github.com/breml/errchkjson v0.4.0 h1:gftf6uWZMtIa/Is3XJgibewBm2ksAQSY/kABDNFTAdk=
+github.com/breml/errchkjson v0.4.0/go.mod h1:AuBOSTHyLSaaAFlWsRSuRBIroCh3eh7ZHh5YeelDIk8=
github.com/butuzov/ireturn v0.3.0 h1:hTjMqWw3y5JC3kpnC5vXmFJAWI/m31jaCYQqzkS6PL0=
github.com/butuzov/ireturn v0.3.0/go.mod h1:A09nIiwiqzN/IoVo9ogpa0Hzi9fex1kd9PSD6edP5ZA=
github.com/butuzov/mirror v1.2.0 h1:9YVK1qIjNspaqWutSv8gsge2e/Xpq1eqEkslEUHy5cs=
@@ -115,16 +115,16 @@ github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+U
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/ckaznocha/intrange v0.1.2 h1:3Y4JAxcMntgb/wABQ6e8Q8leMd26JbX2790lIss9MTI=
-github.com/ckaznocha/intrange v0.1.2/go.mod h1:RWffCw/vKBwHeOEwWdCikAtY0q4gGt8VhJZEEA5n+RE=
+github.com/ckaznocha/intrange v0.2.1 h1:M07spnNEQoALOJhwrImSrJLaxwuiQK+hA2DeajBlwYk=
+github.com/ckaznocha/intrange v0.2.1/go.mod h1:7NEhVyf8fzZO5Ds7CRaqPEm52Ut83hsTiL5zbER/HYk=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
-github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/curioswitch/go-reassign v0.2.0 h1:G9UZyOcpk/d7Gd6mqYgd8XYWFMw/znxwGDUstnC9DIo=
github.com/curioswitch/go-reassign v0.2.0/go.mod h1:x6OpXuWvgfQaMGks2BZybTngWjT84hqJfKoO8Tt/Roc=
-github.com/daixiang0/gci v0.13.4 h1:61UGkmpoAcxHM2hhNkZEf5SzwQtWJXTSws7jaPyqwlw=
-github.com/daixiang0/gci v0.13.4/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk=
+github.com/daixiang0/gci v0.13.5 h1:kThgmH1yBmZSBCh1EJVxQ7JsHpm5Oms0AMed/0LaH4c=
+github.com/daixiang0/gci v0.13.5/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -136,22 +136,22 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q=
github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp+ED1A=
-github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
-github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
+github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
+github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
github.com/firefart/nonamedreturns v1.0.5 h1:tM+Me2ZaXs8tfdDw3X6DOX++wMCOqzYUho6tUTYIdRA=
github.com/firefart/nonamedreturns v1.0.5/go.mod h1:gHJjDqhGM4WyPt639SOZs+G89Ko7QKH5R5BhnO6xJhw=
-github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
-github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
+github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
+github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo=
github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
-github.com/ghostiam/protogetter v0.3.6 h1:R7qEWaSgFCsy20yYHNIJsU9ZOb8TziSRRxuAOTVKeOk=
-github.com/ghostiam/protogetter v0.3.6/go.mod h1:7lpeDnEJ1ZjL/YtyoN99ljO4z0pd3H0d18/t2dPBxHw=
-github.com/go-critic/go-critic v0.11.4 h1:O7kGOCx0NDIni4czrkRIXTnit0mkyKOCePh3My6OyEU=
-github.com/go-critic/go-critic v0.11.4/go.mod h1:2QAdo4iuLik5S9YG0rT4wcZ8QxwHYkrr6/2MWAiv/vc=
+github.com/ghostiam/protogetter v0.3.8 h1:LYcXbYvybUyTIxN2Mj9h6rHrDZBDwZloPoKctWrFyJY=
+github.com/ghostiam/protogetter v0.3.8/go.mod h1:WZ0nw9pfzsgxuRsPOFQomgDVSWtDLJRfQJEhsGbmQMA=
+github.com/go-critic/go-critic v0.11.5 h1:TkDTOn5v7EEngMxu8KbuFqFR43USaaH8XRJLz1jhVYA=
+github.com/go-critic/go-critic v0.11.5/go.mod h1:wu6U7ny9PiaHaZHcvMDmdysMqvDem162Rh3zWTrqk8M=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@@ -161,8 +161,10 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
-github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
-github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
+github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
+github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
@@ -185,14 +187,14 @@ github.com/go-toolsmith/strparse v1.1.0 h1:GAioeZUK9TGxnLS+qfdqNbA4z0SSm5zVNtCQi
github.com/go-toolsmith/strparse v1.1.0/go.mod h1:7ksGy58fsaQkGQlY8WVoBFNyEPMGuJin1rfoPS4lBSQ=
github.com/go-toolsmith/typep v1.1.0 h1:fIRYDyF+JywLfqzyhdiHzRop/GQDxxNhLGQ6gFUNHus=
github.com/go-toolsmith/typep v1.1.0/go.mod h1:fVIw+7zjdsMxDA3ITWnH1yOiw1rnTQKCsF/sk2H/qig=
-github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc=
-github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
+github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
+github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/go-xmlfmt/xmlfmt v1.1.2 h1:Nea7b4icn8s57fTx1M5AI4qQT5HEM3rVUO8MuE6g80U=
github.com/go-xmlfmt/xmlfmt v1.1.2/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
-github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
-github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
+github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
+github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@@ -226,10 +228,12 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM=
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk=
-github.com/golangci/gofmt v0.0.0-20231018234816-f50ced29576e h1:ULcKCDV1LOZPFxGZaA6TlQbiM3J2GCPnkx/bGF6sX/g=
-github.com/golangci/gofmt v0.0.0-20231018234816-f50ced29576e/go.mod h1:Pm5KhLPA8gSnQwrQ6ukebRcapGb/BG9iUkdaiCcGHJM=
-github.com/golangci/golangci-lint v1.59.1 h1:CRRLu1JbhK5avLABFJ/OHVSQ0Ie5c4ulsOId1h3TTks=
-github.com/golangci/golangci-lint v1.59.1/go.mod h1:jX5Oif4C7P0j9++YB2MMJmoNrb01NJ8ITqKWNLewThg=
+github.com/golangci/go-printf-func-name v0.1.0 h1:dVokQP+NMTO7jwO4bwsRwLWeudOVUPPyAKJuzv8pEJU=
+github.com/golangci/go-printf-func-name v0.1.0/go.mod h1:wqhWFH5mUdJQhweRnldEywnR5021wTdZSNgwYceV14s=
+github.com/golangci/gofmt v0.0.0-20240816233607-d8596aa466a9 h1:/1322Qns6BtQxUZDTAT4SdcoxknUki7IAoK4SAXr8ME=
+github.com/golangci/gofmt v0.0.0-20240816233607-d8596aa466a9/go.mod h1:Oesb/0uFAyWoaw1U1qS5zyjCg5NP9C9iwjnI4tIsXEE=
+github.com/golangci/golangci-lint v1.62.2 h1:b8K5K9PN+rZN1+mKLtsZHz2XXS9aYKzQ9i25x3Qnxxw=
+github.com/golangci/golangci-lint v1.62.2/go.mod h1:ILWWyeFUrctpHVGMa1dg2xZPKoMUTc5OIMgW7HZr34g=
github.com/golangci/misspell v0.6.0 h1:JCle2HUTNWirNlDIAUO44hUsKhOFqGPoC4LZxlaSXDs=
github.com/golangci/misspell v0.6.0/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo=
github.com/golangci/modinfo v0.3.4 h1:oU5huX3fbxqQXdfspamej74DFX0kyGLkw1ppvXoJ8GA=
@@ -266,11 +270,9 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
-github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
+github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA=
+github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
-github.com/google/safehtml v0.1.0 h1:EwLKo8qawTKfsi0orxcQAZzu07cICaBeFMegAU9eaT8=
-github.com/google/safehtml v0.1.0/go.mod h1:L4KWwDsUJdECRAEpZoBn3O64bQaywRscowZjJAzjHnU=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gordonklaus/ineffassign v0.1.0 h1:y2Gd/9I7MdY1oEIt+n+rowjBNDcLQq3RsH5hwJd0f9s=
@@ -299,16 +301,12 @@ github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSo
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
-github.com/jba/templatecheck v0.7.0 h1:wjTb/VhGgSFeim5zjWVePBdaMo28X74bGLSABZV+zIA=
-github.com/jba/templatecheck v0.7.0/go.mod h1:n1Etw+Rrw1mDDD8dDRsEKTwMZsJ98EkktgNJC6wLUGo=
github.com/jgautheron/goconst v1.7.1 h1:VpdAG7Ca7yvvJk5n8dMwQhfEZJh95kl/Hl9S1OI5Jkk=
github.com/jgautheron/goconst v1.7.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4=
github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs=
github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c=
-github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af h1:KA9BjwUk7KlCh6S9EAGWBt1oExIUv9WyNCiRz5amv48=
-github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0=
-github.com/jjti/go-spancheck v0.6.1 h1:ZK/wE5Kyi1VX3PJpUO2oEgeoI4FWOUm7Shb2Gbv5obI=
-github.com/jjti/go-spancheck v0.6.1/go.mod h1:vF1QkOO159prdo6mHRxak2CpzDpHAfKiPUDP/NeRnX8=
+github.com/jjti/go-spancheck v0.6.2 h1:iYtoxqPMzHUPp7St+5yA8+cONdyXD3ug6KK15n7Pklk=
+github.com/jjti/go-spancheck v0.6.2/go.mod h1:+X7lvIrR5ZdUTkxFYqzJ0abr8Sb5LOo80uOhWNqIrYA=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@@ -323,8 +321,8 @@ github.com/julz/importas v0.1.0/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSX
github.com/karamaru-alpha/copyloopvar v1.1.0 h1:x7gNyKcC2vRBO1H2Mks5u1VxQtYvFiym7fCjIP8RPos=
github.com/karamaru-alpha/copyloopvar v1.1.0/go.mod h1:u7CIfztblY0jZLOQZgH3oYsJzpC2A7S6u/lfgSXHy0k=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
-github.com/kisielk/errcheck v1.7.0 h1:+SbscKmWJ5mOK/bO1zS60F5I9WwZDWOfRsC4RwfwRV0=
-github.com/kisielk/errcheck v1.7.0/go.mod h1:1kLL+jV4e+CFfueBmI1dSK2ADDyQnlrnrY/FqKluHJQ=
+github.com/kisielk/errcheck v1.8.0 h1:ZX/URYa7ilESY19ik/vBmCn6zdGQLxACwjAcWbHlYlg=
+github.com/kisielk/errcheck v1.8.0/go.mod h1:1kLL+jV4e+CFfueBmI1dSK2ADDyQnlrnrY/FqKluHJQ=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kkHAIKE/contextcheck v1.1.5 h1:CdnJh63tcDe53vG+RebdpdXJTc9atMgGqdx8LXxiilg=
github.com/kkHAIKE/contextcheck v1.1.5/go.mod h1:O930cpht4xb1YQpK+1+AgoM3mFsvxr7uyFptcnWTYUA=
@@ -345,16 +343,14 @@ github.com/kunwardeep/paralleltest v1.0.10 h1:wrodoaKYzS2mdNVnc4/w31YaXFtsc21PCT
github.com/kunwardeep/paralleltest v1.0.10/go.mod h1:2C7s65hONVqY7Q5Efj5aLzRCNLjw2h4eMc9EcypGjcY=
github.com/kyoh86/exportloopref v0.1.11 h1:1Z0bcmTypkL3Q4k+IDHMWTcnCliEZcaPiIe0/ymEyhQ=
github.com/kyoh86/exportloopref v0.1.11/go.mod h1:qkV4UF1zGl6EkF1ox8L5t9SwyeBAZ3qLMd6up458uqA=
-github.com/lasiar/canonicalheader v1.1.1 h1:wC+dY9ZfiqiPwAexUApFush/csSPXeIi4QqyxXmng8I=
-github.com/lasiar/canonicalheader v1.1.1/go.mod h1:cXkb3Dlk6XXy+8MVQnF23CYKWlyA7kfQhSw2CcZtZb0=
+github.com/lasiar/canonicalheader v1.1.2 h1:vZ5uqwvDbyJCnMhmFYimgMZnJMjwljN5VGY0VKbMXb4=
+github.com/lasiar/canonicalheader v1.1.2/go.mod h1:qJCeLFS0G/QlLQ506T+Fk/fWMa2VmBUiEI2cuMK4djI=
github.com/ldez/gomoddirectives v0.2.4 h1:j3YjBIjEBbqZ0NKtBNzr8rtMHTOrLPeiwTkfUJZ3alg=
github.com/ldez/gomoddirectives v0.2.4/go.mod h1:oWu9i62VcQDYp9EQ0ONTfqLNh+mDLWWDO+SO0qSQw5g=
github.com/ldez/tagliatelle v0.5.0 h1:epgfuYt9v0CG3fms0pEgIMNPuFf/LpPIfjk4kyqSioo=
github.com/ldez/tagliatelle v0.5.0/go.mod h1:rj1HmWiL1MiKQuOONhd09iySTEkUuE/8+5jtPYz9xa4=
github.com/leonklingele/grouper v1.1.2 h1:o1ARBDLOmmasUaNDesWqWCIFH3u7hoFlM84YrjT3mIY=
github.com/leonklingele/grouper v1.1.2/go.mod h1:6D0M/HVkhs2yRKRFZUoGjeDy7EZTfFBE9gl4kjmIGkA=
-github.com/lufeee/execinquery v1.2.1 h1:hf0Ems4SHcUGBxpGN7Jz78z1ppVkP/837ZlETPCEtOM=
-github.com/lufeee/execinquery v1.2.1/go.mod h1:EC7DrEKView09ocscGHC+apXMIaorh4xqSxS/dy8SbM=
github.com/macabu/inamedparam v0.1.3 h1:2tk/phHkMlEL/1GNe/Yf6kkR/hkcUdAEY3L0hjYV1Mk=
github.com/macabu/inamedparam v0.1.3/go.mod h1:93FLICAIk/quk7eaPPQvbzihUdn/QkGDwIZEoLtpH6I=
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
@@ -372,12 +368,13 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
+github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
+github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
-github.com/mgechev/revive v1.3.7 h1:502QY0vQGe9KtYJ9FpxMz9rL+Fc/P13CI5POL4uHCcE=
-github.com/mgechev/revive v1.3.7/go.mod h1:RJ16jUbF0OWC3co/+XTxmFNgEpUPwnnA0BRllX2aDNA=
+github.com/mgechev/revive v1.5.1 h1:hE+QPeq0/wIzJwOphdVyUJ82njdd8Khp4fUIHGZHW3M=
+github.com/mgechev/revive v1.5.1/go.mod h1:lC9AhkJIBs5zwx8wkudyHrU+IJkrEKmpCmGMnIJPk4o=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
@@ -387,8 +384,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
-github.com/moricho/tparallel v0.3.1 h1:fQKD4U1wRMAYNngDonW5XupoB/ZGJHdpzrWqgyg9krA=
-github.com/moricho/tparallel v0.3.1/go.mod h1:leENX2cUv7Sv2qDgdi0D0fCftN8fRC67Bcn8pqzeYNI=
+github.com/moricho/tparallel v0.3.2 h1:odr8aZVFA3NZrNybggMkYO3rgPRcqjeQUlBBFVxKHTI=
+github.com/moricho/tparallel v0.3.2/go.mod h1:OQ+K3b4Ln3l2TZveGCywybl68glfLEwFGqvnjok8b+U=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nakabonne/nestif v0.3.1 h1:wm28nZjhQY5HyYPx+weN3Q65k6ilSBxDb8v5S81B81U=
@@ -397,14 +394,14 @@ github.com/nishanths/exhaustive v0.12.0 h1:vIY9sALmw6T/yxiASewa4TQcFsVYZQQRUQJhK
github.com/nishanths/exhaustive v0.12.0/go.mod h1:mEZ95wPIZW+x8kC4TgC+9YCUgiST7ecevsVDTgc2obs=
github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk=
github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c=
-github.com/nunnatsa/ginkgolinter v0.16.2 h1:8iLqHIZvN4fTLDC0Ke9tbSZVcyVHoBs0HIbnVSxfHJk=
-github.com/nunnatsa/ginkgolinter v0.16.2/go.mod h1:4tWRinDN1FeJgU+iJANW/kz7xKN5nYRAOfJDQUS9dOQ=
+github.com/nunnatsa/ginkgolinter v0.18.3 h1:WgS7X3zzmni3vwHSBhvSgqrRgUecN6PQUcfB0j1noDw=
+github.com/nunnatsa/ginkgolinter v0.18.3/go.mod h1:BE1xyB/PNtXXG1azrvrqJW5eFH0hSRylNzFy8QHPwzs=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
-github.com/onsi/ginkgo/v2 v2.17.3 h1:oJcvKpIb7/8uLpDDtnQuf18xVnwKp8DTD7DQ6gTd/MU=
-github.com/onsi/ginkgo/v2 v2.17.3/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc=
-github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
-github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
+github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4=
+github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag=
+github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8=
+github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc=
github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw=
github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU=
github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w=
@@ -415,8 +412,8 @@ github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH
github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
-github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
-github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
+github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
+github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/peterbourgon/ff/v3 v3.0.0/go.mod h1:UILIFjRH5a/ar8TjXYLTkIvSvekZqPm5Eb/qbGk6CT0=
github.com/peterbourgon/ff/v3 v3.3.0 h1:PaKe7GW8orVFh8Unb5jNHS+JZBwWUMa2se0HM6/BI24=
github.com/peterbourgon/ff/v3 v3.3.0/go.mod h1:zjJVUhx+twciwfDl0zBcFzl4dW8axCRyXE/eKY9RztQ=
@@ -427,8 +424,8 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/polyfloyd/go-errorlint v1.5.2 h1:SJhVik3Umsjh7mte1vE0fVZ5T1gznasQG3PV7U5xFdA=
-github.com/polyfloyd/go-errorlint v1.5.2/go.mod h1:sH1QC1pxxi0fFecsVIzBmxtrgd9IF/SkJpA6wqyKAJs=
+github.com/polyfloyd/go-errorlint v1.7.0 h1:Zp6lzCK4hpBDj8y8a237YK4EPrMXQWvOe3nGoH4pFrU=
+github.com/polyfloyd/go-errorlint v1.7.0/go.mod h1:dGWKu85mGHnegQ2SWpEybFityCg3j7ZbwsVUxAOk9gY=
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
@@ -453,8 +450,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
-github.com/quasilyte/go-ruleguard v0.4.2 h1:htXcXDK6/rO12kiTHKfHuqR4kr3Y4M0J0rOL6CH/BYs=
-github.com/quasilyte/go-ruleguard v0.4.2/go.mod h1:GJLgqsLeo4qgavUoL8JeGFNS7qcisx3awV/w9eWTmNI=
+github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1 h1:+Wl/0aFp0hpuHM3H//KMft64WQ1yX9LdJY64Qm/gFCo=
+github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1/go.mod h1:GJLgqsLeo4qgavUoL8JeGFNS7qcisx3awV/w9eWTmNI=
github.com/quasilyte/go-ruleguard/dsl v0.3.22 h1:wd8zkOhSNr+I+8Qeciml08ivDt1pSXe60+5DqOpCjPE=
github.com/quasilyte/go-ruleguard/dsl v0.3.22/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
github.com/quasilyte/gogrep v0.5.0 h1:eTKODPXbI8ffJMN+W2aE0+oL0z/nh8/5eNdiO34SOAo=
@@ -463,12 +460,17 @@ github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 h1:TCg2WBOl
github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0=
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 h1:M8mH9eK4OUR4lu7Gd+PU1fV2/qnDNfzT635KRSObncs=
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ=
+github.com/raeperd/recvcheck v0.1.2 h1:SjdquRsRXJc26eSonWIo8b7IMtKD3OAT2Lb5G3ZX1+4=
+github.com/raeperd/recvcheck v0.1.2/go.mod h1:n04eYkwIR0JbgD73wT8wL4JjPC3wm0nFtzBnWNocnYU=
+github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
+github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
-github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
+github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
+github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/ryancurrah/gomodguard v1.3.2 h1:CuG27ulzEB1Gu5Dk5gP8PFxSOZ3ptSdP5iI/3IXxM18=
-github.com/ryancurrah/gomodguard v1.3.2/go.mod h1:LqdemiFomEjcxOqirbQCb3JFvSxH2JUYMerTFd3sF2o=
+github.com/ryancurrah/gomodguard v1.3.5 h1:cShyguSwUEeC0jS7ylOiG/idnd1TpJ1LfHGpV3oJmPU=
+github.com/ryancurrah/gomodguard v1.3.5/go.mod h1:MXlEPQRxgfPQa62O8wzK3Ozbkv9Rkqr+wKjSxTdsNJE=
github.com/ryanrolds/sqlclosecheck v0.5.1 h1:dibWW826u0P8jNLsLN+En7+RqWWTYrjCB9fJfSfdyCU=
github.com/ryanrolds/sqlclosecheck v0.5.1/go.mod h1:2g3dUjoS6AL4huFdv6wn55WpLIDjY7ZgUR4J8HOO/XQ=
github.com/sanposhiho/wastedassign/v2 v2.0.7 h1:J+6nrY4VW+gC9xFzUc+XjPD3g3wF3je/NsJFwFK7Uxc=
@@ -477,10 +479,10 @@ github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6Ng
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY=
github.com/sashamelentyev/interfacebloat v1.1.0 h1:xdRdJp0irL086OyW1H/RTZTr1h/tMEOsumirXcOJqAw=
github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ=
-github.com/sashamelentyev/usestdlibvars v1.26.0 h1:LONR2hNVKxRmzIrZR0PhSF3mhCAzvnr+DcUiHgREfXE=
-github.com/sashamelentyev/usestdlibvars v1.26.0/go.mod h1:9nl0jgOfHKWNFS43Ojw0i7aRoS4j6EBye3YBhmAIRF8=
-github.com/securego/gosec/v2 v2.20.1-0.20240525090044-5f0084eb01a9 h1:rnO6Zp1YMQwv8AyxzuwsVohljJgp4L0ZqiCgtACsPsc=
-github.com/securego/gosec/v2 v2.20.1-0.20240525090044-5f0084eb01a9/go.mod h1:dg7lPlu/xK/Ut9SedURCoZbVCR4yC7fM65DtH9/CDHs=
+github.com/sashamelentyev/usestdlibvars v1.27.0 h1:t/3jZpSXtRPRf2xr0m63i32ZrusyurIGT9E5wAvXQnI=
+github.com/sashamelentyev/usestdlibvars v1.27.0/go.mod h1:9nl0jgOfHKWNFS43Ojw0i7aRoS4j6EBye3YBhmAIRF8=
+github.com/securego/gosec/v2 v2.21.4 h1:Le8MSj0PDmOnHJgUATjD96PaXRvCpKC+DGJvwyy0Mlk=
+github.com/securego/gosec/v2 v2.21.4/go.mod h1:Jtb/MwRQfRxCXyCm1rfM1BEiiiTfUOdyzzAhlr6lUTA=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c h1:W65qqJCIOVP4jpqPQ0YvHYKwcMEMVWIzWC5iNQQfBTU=
github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs=
@@ -493,18 +495,18 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sivchari/containedctx v1.0.3 h1:x+etemjbsh2fB5ewm5FeLNi5bUjK0V8n0RB+Wwfd0XE=
github.com/sivchari/containedctx v1.0.3/go.mod h1:c1RDvCbnJLtH4lLcYD/GqwiBSSf4F5Qk0xld2rBqzJ4=
-github.com/sivchari/tenv v1.7.1 h1:PSpuD4bu6fSmtWMxSGWcvqUUgIn7k3yOJhOIzVWn8Ak=
-github.com/sivchari/tenv v1.7.1/go.mod h1:64yStXKSOxDfX47NlhVwND4dHwfZDdbp2Lyl018Icvg=
-github.com/sonatard/noctx v0.0.2 h1:L7Dz4De2zDQhW8S0t+KUjY0MAQJd6SgVwhzNIc4ok00=
-github.com/sonatard/noctx v0.0.2/go.mod h1:kzFz+CzWSjQ2OzIm46uJZoXuBpa2+0y3T36U18dWqIo=
+github.com/sivchari/tenv v1.12.1 h1:+E0QzjktdnExv/wwsnnyk4oqZBUfuh89YMQT1cyuvSY=
+github.com/sivchari/tenv v1.12.1/go.mod h1:1LjSOUCc25snIr5n3DtGGrENhX3LuWefcplwVGC24mw=
+github.com/sonatard/noctx v0.1.0 h1:JjqOc2WN16ISWAjAk8M5ej0RfExEXtkEyExl2hLW+OM=
+github.com/sonatard/noctx v0.1.0/go.mod h1:0RvBxqY8D4j9cTTTWE8ylt2vqj2EPI8fHmrxHdsaZ2c=
github.com/sourcegraph/go-diff v0.7.0 h1:9uLlrd5T46OXs5qpp8L/MTltk0zikUGi0sNNyCpA8G0=
github.com/sourcegraph/go-diff v0.7.0/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
-github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
-github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
+github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
+github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
@@ -530,12 +532,10 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
-github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
-github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c h1:+aPplBwWcHBo6q9xrfWdMrT9o4kltkmmvpemgIjep/8=
-github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c/go.mod h1:SbErYREK7xXdsRiigaQiQkI9McGRzYMvlKYaP3Nimdk=
github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8=
github.com/tdakkota/asciicheck v0.2.0 h1:o8jvnUANo0qXtnslk2d3nMKTFNlOnJjRrNcj0j9qkHM=
github.com/tdakkota/asciicheck v0.2.0/go.mod h1:Qb7Y9EgjCLJGup51gDHFzbI08/gbGhL/UVhYIPWG2rg=
@@ -543,22 +543,24 @@ github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA
github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0=
github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpRQGxTSkNYKJ51yaw6ChIqO+Je8UqsTKN/cDag=
github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY=
-github.com/tetafro/godot v1.4.16 h1:4ChfhveiNLk4NveAZ9Pu2AN8QZ2nkUGFuadM9lrr5D0=
-github.com/tetafro/godot v1.4.16/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio=
+github.com/tetafro/godot v1.4.18 h1:ouX3XGiziKDypbpXqShBfnNLTSjR8r3/HVzrtJ+bHlI=
+github.com/tetafro/godot v1.4.18/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio=
github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 h1:quvGphlmUVU+nhpFa4gg4yJyTRJ13reZMDHrKwYw53M=
github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966/go.mod h1:27bSVNWSBOHm+qRp1T9qzaIpsWEP6TbUnei/43HK+PQ=
-github.com/timonwong/loggercheck v0.9.4 h1:HKKhqrjcVj8sxL7K77beXh0adEm6DLjV/QOGeMXEVi4=
-github.com/timonwong/loggercheck v0.9.4/go.mod h1:caz4zlPcgvpEkXgVnAJGowHAMW2NwHaNlpS8xDbVhTg=
-github.com/tomarrell/wrapcheck/v2 v2.8.3 h1:5ov+Cbhlgi7s/a42BprYoxsr73CbdMUTzE3bRDFASUs=
-github.com/tomarrell/wrapcheck/v2 v2.8.3/go.mod h1:g9vNIyhb5/9TQgumxQyOEqDHsmGYcGsVMOx/xGkqdMo=
+github.com/timonwong/loggercheck v0.10.1 h1:uVZYClxQFpw55eh+PIoqM7uAOHMrhVcDoWDery9R8Lg=
+github.com/timonwong/loggercheck v0.10.1/go.mod h1:HEAWU8djynujaAVX7QI65Myb8qgfcZ1uKbdpg3ZzKl8=
+github.com/tomarrell/wrapcheck/v2 v2.9.0 h1:801U2YCAjLhdN8zhZ/7tdjB3EnAoRlJHt/s+9hijLQ4=
+github.com/tomarrell/wrapcheck/v2 v2.9.0/go.mod h1:g9vNIyhb5/9TQgumxQyOEqDHsmGYcGsVMOx/xGkqdMo=
github.com/tommy-muehle/go-mnd/v2 v2.5.1 h1:NowYhSdyE/1zwK9QCLeRb6USWdoif80Ie+v+yU8u1Zw=
github.com/tommy-muehle/go-mnd/v2 v2.5.1/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw=
github.com/ultraware/funlen v0.1.0 h1:BuqclbkY6pO+cvxoq7OsktIXZpgBSkYTQtmwhAK81vI=
github.com/ultraware/funlen v0.1.0/go.mod h1:XJqmOQja6DpxarLj6Jj1U7JuoS8PvL4nEqDaQhy22p4=
github.com/ultraware/whitespace v0.1.1 h1:bTPOGejYFulW3PkcrqkeQwOd6NKOOXvmGD9bo/Gk8VQ=
github.com/ultraware/whitespace v0.1.1/go.mod h1:XcP1RLD81eV4BW8UhQlpaR+SDc2givTvyI8a586WjW8=
-github.com/uudashr/gocognit v1.1.2 h1:l6BAEKJqQH2UpKAPKdMfZf5kE4W/2xk8pfU1OVLvniI=
-github.com/uudashr/gocognit v1.1.2/go.mod h1:aAVdLURqcanke8h3vg35BC++eseDm66Z7KmchI5et4k=
+github.com/uudashr/gocognit v1.1.3 h1:l+a111VcDbKfynh+airAy/DJQKaXh2m9vkoysMPSZyM=
+github.com/uudashr/gocognit v1.1.3/go.mod h1:aKH8/e8xbTRBwjbCkwZ8qt4l2EpKXl31KMHgSS+lZ2U=
+github.com/uudashr/iface v1.2.1 h1:vHHyzAUmWZ64Olq6NZT3vg/z1Ws56kyPdBOd5kTXDF8=
+github.com/uudashr/iface v1.2.1/go.mod h1:4QvspiRd3JLPAEXBQ9AiZpLbJlrWWgRChOKDJEuQTdg=
github.com/xen0n/gosmopolitan v1.2.2 h1:/p2KTnMzwRexIW8GlKawsTWOxn7UHA+jCMF/V8HHtvU=
github.com/xen0n/gosmopolitan v1.2.2/go.mod h1:7XX7Mj61uLYrj0qmeN0zi7XDon9JRAEhYQqAPLVNTeg=
github.com/yagipy/maintidx v1.0.0 h1:h5NvIsCz+nRDapQ0exNv4aJ0yXSI0420omVANTv3GJM=
@@ -579,18 +581,18 @@ gitlab.com/bosi/decorder v0.4.2 h1:qbQaV3zgwnBZ4zPMhGLW4KZe7A7NwxEhJx39R3shffo=
gitlab.com/bosi/decorder v0.4.2/go.mod h1:muuhHoaJkA9QLcYHq4Mj8FJUwDZ+EirSHRiaTcTf6T8=
go-simpler.org/assert v0.9.0 h1:PfpmcSvL7yAnWyChSjOz6Sp6m9j5lyK8Ok9pEL31YkQ=
go-simpler.org/assert v0.9.0/go.mod h1:74Eqh5eI6vCK6Y5l3PI8ZYFXG4Sa+tkr70OIPJAUr28=
-go-simpler.org/musttag v0.12.2 h1:J7lRc2ysXOq7eM8rwaTYnNrHd5JwjppzB6mScysB2Cs=
-go-simpler.org/musttag v0.12.2/go.mod h1:uN1DVIasMTQKk6XSik7yrJoEysGtR2GRqvWnI9S7TYM=
-go-simpler.org/sloglint v0.7.1 h1:qlGLiqHbN5islOxjeLXoPtUdZXb669RW+BDQ+xOSNoU=
-go-simpler.org/sloglint v0.7.1/go.mod h1:OlaVDRh/FKKd4X4sIMbsz8st97vomydceL146Fthh/c=
+go-simpler.org/musttag v0.13.0 h1:Q/YAW0AHvaoaIbsPj3bvEI5/QFP7w696IMUpnKXQfCE=
+go-simpler.org/musttag v0.13.0/go.mod h1:FTzIGeK6OkKlUDVpj0iQUXZLUO1Js9+mvykDQy9C5yM=
+go-simpler.org/sloglint v0.7.2 h1:Wc9Em/Zeuu7JYpl+oKoYOsQSy2X560aVueCW/m6IijY=
+go-simpler.org/sloglint v0.7.2/go.mod h1:US+9C80ppl7VsThQclkM7BkCHQAzuz8kHLsW3ppuluo=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
-go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
-go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
+go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
+go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
@@ -617,12 +619,12 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
-golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcHCgR0s52IfwutMfEbdM=
-golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
+golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
+golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
-golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f h1:phY1HzDcf18Aq9A8KkmRtY9WvOFIxN8wgfvy6Zm1DV8=
-golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
+golang.org/x/exp/typeparams v0.0.0-20241108190413-2d47ceb2692f h1:WTyX8eCCyfdqiPYkRGm0MqElSfYFH3yR1+rl/mct9sA=
+golang.org/x/exp/typeparams v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -652,8 +654,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
-golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
+golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -692,8 +694,8 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
-golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
+golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
+golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -713,8 +715,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
-golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
+golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -760,7 +762,6 @@ golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220702020025-31831981b65f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -769,10 +770,8 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
-golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/telemetry v0.0.0-20240607193123-221703e18637 h1:3Wt8mZlbFwG8llny+t18kh7AXxyWePFycXMuVdHxnyM=
-golang.org/x/telemetry v0.0.0-20240607193123-221703e18637/go.mod h1:n38mvGdgc4dA684EC4NwQwoPKSw4jyKw8/DgZHDA1Dk=
+golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
+golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -789,8 +788,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
-golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
+golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
+golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -851,18 +850,13 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
-golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
-golang.org/x/tools v0.22.1-0.20240628205440-9c895dd76b34 h1:Kd+Z5Pm6uwYx3T2KEkeHMHUMZxDPb/q6b1m+zEcy62c=
-golang.org/x/tools v0.22.1-0.20240628205440-9c895dd76b34/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
-golang.org/x/tools/gopls v0.16.1 h1:1hO/dCeUvjEYx3V0rVvCtOkwnpEpqS29paE+Jw4dcAc=
-golang.org/x/tools/gopls v0.16.1/go.mod h1:Mwg8NfkbmP57kHtr/qsiU1+7kyEpuCvlPs7MH6sr988=
-golang.org/x/vuln v1.0.4 h1:SP0mPeg2PmGCu03V+61EcQiOjmpri2XijexKdzv8Z1I=
-golang.org/x/vuln v1.0.4/go.mod h1:NbJdUQhX8jY++FtuhrXs2Eyx0yePo9pF7nPlIjo9aaQ=
+golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
+golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -972,8 +966,8 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-honnef.co/go/tools v0.4.7 h1:9MDAWxMoSnB6QoSqiVr7P5mtkT9pOc1kSxchzPCnqJs=
-honnef.co/go/tools v0.4.7/go.mod h1:+rnGS1THNh8zMwnd2oVOTL9QF6vmfyG6ZXBULae2uc0=
+honnef.co/go/tools v0.5.1 h1:4bH5o3b5ZULQ4UrBmP+63W9r7qIkqJClEA9ko5YKx+I=
+honnef.co/go/tools v0.5.1/go.mod h1:e9irvo83WDG9/irijV44wr3tbhcFeRnfpVlRqVwpzMs=
moul.io/banner v1.0.1 h1:+WsemGLhj2pOajw2eR5VYjLhOIqs0XhIRYchzTyMLk0=
moul.io/banner v1.0.1/go.mod h1:XwvIGKkhKRKyN1vIdmR5oaKQLIkMhkMqrsHpS94QzAU=
moul.io/godev v1.7.0/go.mod h1:5lgSpI1oH7xWpLl2Ew/Nsgk8DiNM6FzN9WV9+lgW8RQ=
@@ -984,12 +978,10 @@ moul.io/testman v1.5.0/go.mod h1:b4/5+lMsMDJtwuh25Cr0eVJ5Y4B2lSPfkzDtfct070g=
moul.io/u v1.6.0/go.mod h1:yd3/IoYRIJaZWAJV2rYHvM2EPp/Pp0zSNraB5IPX+hw=
moul.io/u v1.27.0 h1:rF0p184mludn2DzL0unA8Gf/mFWMBerdqOh8cyuQYzQ=
moul.io/u v1.27.0/go.mod h1:ggYDXxUjoHpfDsMPD3STqkUZTyA741PZiQhSd+7kRnA=
-mvdan.cc/gofumpt v0.6.0 h1:G3QvahNDmpD+Aek/bNOLrFR2XC6ZAdo62dZu65gmwGo=
-mvdan.cc/gofumpt v0.6.0/go.mod h1:4L0wf+kgIPZtcCWXynNS2e6bhmj73umwnuXSZarixzA=
+mvdan.cc/gofumpt v0.7.0 h1:bg91ttqXmi9y2xawvkuMXyvAA/1ZGJqYAEGjXuP0JXU=
+mvdan.cc/gofumpt v0.7.0/go.mod h1:txVFJy/Sc/mvaycET54pV8SW8gWxTlUuGHVEcncmNUo=
mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f h1:lMpcwN6GxNbWtbpI1+xzFLSW8XzX0u72NttUGVFjO3U=
mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f/go.mod h1:RSLa7mKKCNeTTMHBw5Hsy2rfJmd6O2ivt9Dw9ZqCQpQ=
-mvdan.cc/xurls/v2 v2.5.0 h1:lyBNOm8Wo71UknhUs4QTFUNNMyxy2JEIaKKo0RWOh+8=
-mvdan.cc/xurls/v2 v2.5.0/go.mod h1:yQgaGQ1rFtJUzkmKiHYSSfuQxqfYmd//X6PxvholpeE=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
diff --git a/misc/loop/cmd/snapshotter.go b/misc/loop/cmd/snapshotter.go
index 2dda5d568d9..0173f9aad03 100644
--- a/misc/loop/cmd/snapshotter.go
+++ b/misc/loop/cmd/snapshotter.go
@@ -150,6 +150,7 @@ func (s snapshotter) startPortalLoopContainer(ctx context.Context) (*types.Conta
Env: []string{
"MONIKER=the-portal-loop",
"GENESIS_BACKUP_FILE=/backups/backup.jsonl",
+ "GENESIS_BALANCES_FILE=/backups/balances.jsonl",
},
Entrypoint: []string{"/scripts/start.sh"},
ExposedPorts: nat.PortSet{
diff --git a/misc/loop/docker-compose.yml b/misc/loop/docker-compose.yml
index c3adc6a39ea..a0890235502 100644
--- a/misc/loop/docker-compose.yml
+++ b/misc/loop/docker-compose.yml
@@ -44,10 +44,9 @@ services:
- gnoweb
- --bind=0.0.0.0:8888
- --remote=traefik:26657
- - --faucet-url=https://faucet-api.gno.land
- - --captcha-site=$CAPTCHA_SITE_KEY
- --with-analytics
- - --help-chainid=portal-loop
+ - --faucet-url=https://faucet-api.gno.land
+ - --chainid=portal-loop
- --help-remote=https://rpc.gno.land:443
networks:
- portal-loop
diff --git a/misc/loop/go.mod b/misc/loop/go.mod
index f1c09cd9f82..af7783e57bb 100644
--- a/misc/loop/go.mod
+++ b/misc/loop/go.mod
@@ -8,7 +8,7 @@ require (
github.com/docker/docker v24.0.7+incompatible
github.com/docker/go-connections v0.4.0
github.com/gnolang/gno v0.1.0-nightly.20240627
- github.com/gnolang/tx-archive v0.4.0
+ github.com/gnolang/tx-archive v0.4.2
github.com/prometheus/client_golang v1.17.0
github.com/sirupsen/logrus v1.9.3
)
@@ -29,7 +29,6 @@ require (
github.com/distribution/reference v0.5.0 // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/go-units v0.5.0 // indirect
- github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
@@ -73,7 +72,6 @@ require (
golang.org/x/term v0.23.0 // indirect
golang.org/x/text v0.17.0 // indirect
golang.org/x/time v0.5.0 // indirect
- golang.org/x/tools v0.24.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd // indirect
google.golang.org/grpc v1.65.0 // indirect
diff --git a/misc/loop/go.sum b/misc/loop/go.sum
index 740cc629a21..0d235f2cfb1 100644
--- a/misc/loop/go.sum
+++ b/misc/loop/go.sum
@@ -68,10 +68,8 @@ github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHqu
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 h1:GKvsK3oLWG9B1GL7WP/VqwM6C92j5tIvB844oggL9Lk=
-github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216/go.mod h1:xJhtEL7ahjM1WJipt89gel8tHzfIl/LyMY+lCYh38d8=
-github.com/gnolang/tx-archive v0.4.0 h1:+1Rgo0U0HjLQLq/xqeGdJwtAzo9xWj09t1oZLvrL3bU=
-github.com/gnolang/tx-archive v0.4.0/go.mod h1:seKHGnvxUnDgH/mSsCEdwG0dHY/FrpbUm6Hd0+KMd9w=
+github.com/gnolang/tx-archive v0.4.2 h1:xBBqLLKY9riv9yxpQgVhItCWxIji2rX6xNFmCY1cEOQ=
+github.com/gnolang/tx-archive v0.4.2/go.mod h1:AGUBGO+DCLuKL80a1GJRnpcJ5gxVd9L4jEJXQB9uXp4=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
@@ -258,8 +256,6 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
-golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/misc/loop/scripts/pull-gh.sh b/misc/loop/scripts/pull-gh.sh
index efbb360d551..55ee0f7762c 100755
--- a/misc/loop/scripts/pull-gh.sh
+++ b/misc/loop/scripts/pull-gh.sh
@@ -6,6 +6,10 @@ TMP_DIR=temp-tx-exports
# that the portal loop use when looping (generating the genesis)
MASTER_BACKUP_FILE="backup.jsonl"
+# The master balances file will contain the ultimate balances
+# backup that the portal loop uses when looping (generating the genesis)
+MASTER_BALANCES_FILE="balances.jsonl"
+
# Clones the portal loop backups subdirectory, located in BACKUPS_REPO (tx-exports)
pullGHBackups () {
BACKUPS_REPO=https://github.com/gnolang/tx-exports.git
@@ -32,8 +36,10 @@ pullGHBackups
# Combine the pulled backups into a single backup file
TXS_BACKUPS_PREFIX="backup_portal_loop_txs_"
+BALANCES_BACKUP_NAME="backup_portal_loop_balances.jsonl"
find . -type f -name "${TXS_BACKUPS_PREFIX}*.jsonl" | sort | xargs cat > "temp_$MASTER_BACKUP_FILE"
+find . -type f -name "${BALANCES_BACKUP_NAME}" | sort | xargs cat > "temp_$MASTER_BALANCES_FILE"
BACKUPS_DIR="../backups"
TIMESTAMP=$(date +%s)
@@ -47,10 +53,22 @@ if [ -e "$BACKUPS_DIR/$MASTER_BACKUP_FILE" ]; then
echo "Renamed $MASTER_BACKUP_FILE to ${MASTER_BACKUP_FILE}-legacy-$TIMESTAMP"
fi
+# Check if the master balances backup file already exists
+if [ -e "$BACKUPS_DIR/$MASTER_BALANCES_FILE" ]; then
+ # Back up the existing master txs file
+ echo "Master balances backup file exists, backing up..."
+ mv "$BACKUPS_DIR/$MASTER_BALANCES_FILE" "$BACKUPS_DIR/${MASTER_BALANCES_FILE}-legacy-$TIMESTAMP"
+
+ echo "Renamed $MASTER_BALANCES_FILE to ${MASTER_BALANCES_FILE}-legacy-$TIMESTAMP"
+fi
+
# Use the GitHub state as the canonical backup
mv "temp_$MASTER_BACKUP_FILE" "$BACKUPS_DIR/$MASTER_BACKUP_FILE"
echo "Moved temp_$MASTER_BACKUP_FILE to $BACKUPS_DIR/$MASTER_BACKUP_FILE"
+mv "temp_$MASTER_BALANCES_FILE" "$BACKUPS_DIR/$MASTER_BALANCES_FILE"
+echo "Moved temp_$MASTER_BALANCES_FILE to $BACKUPS_DIR/$MASTER_BALANCES_FILE"
+
# Clean up the temporary directory
cd ..
rm -rf $TMP_DIR
diff --git a/misc/loop/scripts/start.sh b/misc/loop/scripts/start.sh
index 6dd57b2c041..db36de39f2a 100755
--- a/misc/loop/scripts/start.sh
+++ b/misc/loop/scripts/start.sh
@@ -7,12 +7,15 @@ RPC_LADDR=${RPC_LADDR:-"tcp://0.0.0.0:26657"}
CHAIN_ID=${CHAIN_ID:-"portal-loop"}
GENESIS_BACKUP_FILE=${GENESIS_BACKUP_FILE:-""}
+GENESIS_BALANCES_FILE=${GENESIS_BALANCES_FILE:-""}
SEEDS=${SEEDS:-""}
PERSISTENT_PEERS=${PERSISTENT_PEERS:-""}
echo "" >> /gnoroot/gno.land/genesis/genesis_txs.jsonl
+echo "" >> /gnoroot/gno.land/genesis/genesis_balances.jsonl
cat "${GENESIS_BACKUP_FILE}" >> /gnoroot/gno.land/genesis/genesis_txs.jsonl
+cat "${GENESIS_BALANCES_FILE}" >> /gnoroot/gno.land/genesis/genesis_balances.jsonl
# Initialize the secrets
gnoland secrets init
diff --git a/misc/stdlib_diff/.gitignore b/misc/stdlib_diff/.gitignore
new file mode 100644
index 00000000000..466602bd0d4
--- /dev/null
+++ b/misc/stdlib_diff/.gitignore
@@ -0,0 +1 @@
+stdlib_diff/
\ No newline at end of file
diff --git a/misc/stdlib_diff/Makefile b/misc/stdlib_diff/Makefile
new file mode 100644
index 00000000000..439af22c586
--- /dev/null
+++ b/misc/stdlib_diff/Makefile
@@ -0,0 +1,7 @@
+all: clean gen
+
+gen:
+ go run . -src $(GOROOT)/src -dst ../../gnovm/stdlibs -out ./stdlib_diff
+
+clean:
+ rm -rf stdlib_diff
diff --git a/misc/stdlib_diff/README.md b/misc/stdlib_diff/README.md
new file mode 100644
index 00000000000..32c3cbcd93d
--- /dev/null
+++ b/misc/stdlib_diff/README.md
@@ -0,0 +1,30 @@
+# stdlibs_diff
+
+stdlibs_diff is a tool that generates an html report indicating differences between gno standard libraries and go standrad libraries
+
+## Usage
+
+Compare the `go` standard libraries the `gno` standard libraries
+
+```shell
+./stdlibs_diff -src -dst -out
+```
+
+Compare the `gno` standard libraries the `go` standard libraries
+
+```shell
+./stdlibs_diff -src -dst -out
+```
+
+
+## Parameters
+
+| Flag | Description | Default value |
+| ---------- | ------------------------------------------------------------------ | ------------- |
+| src | Directory containing packages that will be compared to destination | None |
+| dst | Directory containing packages; used to compare src packages | None |
+| out | Directory where the report will be created | None |
+
+## Tips
+
+An index.html is generated at the root of the report location. Utilize it to navigate easily through the report.
\ No newline at end of file
diff --git a/misc/stdlib_diff/diffstatus.go b/misc/stdlib_diff/diffstatus.go
new file mode 100644
index 00000000000..23829619f64
--- /dev/null
+++ b/misc/stdlib_diff/diffstatus.go
@@ -0,0 +1,25 @@
+package main
+
+type diffStatus uint
+
+const (
+ missingInSrc diffStatus = iota
+ missingInDst
+ hasDiff
+ noDiff
+)
+
+func (status diffStatus) String() string {
+ switch status {
+ case missingInSrc:
+ return "missing in src"
+ case missingInDst:
+ return "missing in dst"
+ case hasDiff:
+ return "files differ"
+ case noDiff:
+ return "files are equal"
+ default:
+ return "Unknown"
+ }
+}
diff --git a/misc/stdlib_diff/filediff.go b/misc/stdlib_diff/filediff.go
new file mode 100644
index 00000000000..a097c9afcc9
--- /dev/null
+++ b/misc/stdlib_diff/filediff.go
@@ -0,0 +1,196 @@
+package main
+
+import (
+ "fmt"
+ "os"
+ "strings"
+
+ "github.com/hexops/gotextdiff"
+ "github.com/hexops/gotextdiff/myers"
+ "github.com/hexops/gotextdiff/span"
+)
+
+// DiffChecker is a struct for comparing differences between two files.
+type DiffChecker struct {
+ Src string // Name of the source file.
+ Dst string // Name of the destination file.
+ srcContent string // Content of the source file.
+ dstContent string // Content of the destination file.
+ srcLines []string // Lines of the source file.
+ dstLines []string // Lines of the destination file.
+}
+
+// LineDifferrence represents a difference in a line during file comparison.
+type LineDifferrence struct {
+ SrcLine string // The line on Src.
+ DestLine string // The line on Src.
+ SrcOperation operation // The operation performed on the line (e.g., "add", "delete", "equal").
+ DestOperation operation
+ SrcNumber int
+ DestNumber int
+}
+type Diff struct {
+ Diffs []LineDifferrence
+ MissingSrc bool
+ MissingDst bool
+}
+
+// NewDiffChecker creates a new DiffChecker instance for comparing differences between
+// the specified source and destination files. It initializes the source and
+// destination file lines .
+func NewDiffChecker(srcPath, dstPath string) (*DiffChecker, error) {
+ src, err := getFileContent(srcPath)
+ if err != nil {
+ return nil, fmt.Errorf("can't read src file: %w", err)
+ }
+
+ dst, err := getFileContent(dstPath)
+ if err != nil {
+ return nil, fmt.Errorf("can't read dst file: %w", err)
+ }
+
+ return &DiffChecker{
+ srcContent: src,
+ dstContent: dst,
+ srcLines: strings.Split(src, "\n"),
+ dstLines: strings.Split(dst, "\n"),
+ Src: srcPath,
+ Dst: dstPath,
+ }, nil
+}
+
+// Differences returns the differences in lines between the source and
+// destination files using the configured diff algorithm.
+func (f *DiffChecker) Differences() *Diff {
+ var (
+ srcIndex, dstIndex int
+ insertCount, deleteCount int
+ diff []LineDifferrence
+ )
+
+ if len(f.dstContent) == 0 {
+ return f.destEmpty()
+ }
+
+ if len(f.srcContent) == 0 {
+ return f.srcEmpty()
+ }
+
+ /* printUntil prints all the lines than do not appear on the computed edits from gotextdiff
+ so we need to add them manually looping always from the current value of
+ srcIndex until the line before the start of the hunk computed diff, hunk.FromLine-1
+
+ We need to print all the lines before each hunk and then ensure the end of the file is printed too
+ */
+ printUntil := func(until int) {
+ for i := srcIndex; i < until; i++ {
+ diff = append(diff, LineDifferrence{
+ SrcLine: f.srcLines[srcIndex],
+ DestLine: f.srcLines[srcIndex],
+ DestOperation: equal,
+ SrcOperation: equal,
+ SrcNumber: srcIndex + 1,
+ DestNumber: dstIndex + 1,
+ })
+
+ srcIndex++
+ dstIndex++
+ }
+ }
+
+ edits := myers.ComputeEdits(span.URIFromPath(f.Src), f.srcContent, f.dstContent)
+ unified := gotextdiff.ToUnified(f.Src, f.Dst, f.srcContent, edits)
+ for _, hunk := range unified.Hunks {
+ printUntil(hunk.FromLine - 1)
+
+ currentLine := LineDifferrence{}
+ for _, line := range hunk.Lines {
+ switch line.Kind {
+ case gotextdiff.Insert:
+ if currentLine.DestLine != "" {
+ diff = append(diff, currentLine)
+ currentLine = LineDifferrence{}
+ }
+
+ insertCount++
+ dstIndex++
+
+ currentLine.DestLine = line.Content
+ currentLine.DestOperation = insert
+ currentLine.DestNumber = dstIndex
+
+ case gotextdiff.Equal:
+ if currentLine.DestLine != "" || currentLine.SrcLine != "" {
+ diff = append(diff, currentLine)
+ currentLine = LineDifferrence{}
+ }
+
+ srcIndex++
+ dstIndex++
+
+ currentLine = LineDifferrence{
+ SrcLine: line.Content,
+ DestLine: line.Content,
+ DestOperation: equal,
+ SrcOperation: equal,
+ SrcNumber: srcIndex,
+ DestNumber: dstIndex,
+ }
+
+ case gotextdiff.Delete:
+ if currentLine.SrcLine != "" {
+ diff = append(diff, currentLine)
+ currentLine = LineDifferrence{}
+ }
+ srcIndex++
+ deleteCount++
+ currentLine.SrcLine = line.Content
+ currentLine.SrcOperation = delete
+ currentLine.SrcNumber = srcIndex
+ }
+ }
+ diff = append(diff, currentLine)
+ }
+
+ printUntil(len(f.srcLines))
+
+ return &Diff{
+ Diffs: diff,
+ }
+}
+
+func (f *DiffChecker) destEmpty() *Diff {
+ diffs := []LineDifferrence{}
+ for index, line := range f.srcLines {
+ diffs = append(diffs, LineDifferrence{SrcLine: line, SrcOperation: delete, SrcNumber: index + 1})
+ }
+
+ return &Diff{
+ Diffs: diffs,
+ MissingDst: true,
+ }
+}
+
+func (f *DiffChecker) srcEmpty() *Diff {
+ diffs := []LineDifferrence{}
+ for index, line := range f.dstLines {
+ diffs = append(diffs, LineDifferrence{DestLine: line, DestOperation: insert, DestNumber: index + 1})
+ }
+
+ return &Diff{
+ Diffs: diffs,
+ MissingSrc: true,
+ }
+}
+
+// getFileContent reads and returns the lines of a file given its path.
+func getFileContent(p string) (string, error) {
+ data, err := os.ReadFile(p)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return "", nil
+ }
+ return "", err
+ }
+ return strings.ReplaceAll(string(data), "\t", " "), nil
+}
diff --git a/misc/stdlib_diff/go.mod b/misc/stdlib_diff/go.mod
new file mode 100644
index 00000000000..4e200f56ebb
--- /dev/null
+++ b/misc/stdlib_diff/go.mod
@@ -0,0 +1,5 @@
+module github.com/gnolang/gno/misc/stdlib_diff
+
+go 1.21.0
+
+require github.com/hexops/gotextdiff v1.0.3
diff --git a/misc/stdlib_diff/go.sum b/misc/stdlib_diff/go.sum
new file mode 100644
index 00000000000..e71200ae5ce
--- /dev/null
+++ b/misc/stdlib_diff/go.sum
@@ -0,0 +1,2 @@
+github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
+github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
diff --git a/misc/stdlib_diff/main.go b/misc/stdlib_diff/main.go
new file mode 100644
index 00000000000..0b29abd18fd
--- /dev/null
+++ b/misc/stdlib_diff/main.go
@@ -0,0 +1,28 @@
+package main
+
+import (
+ "flag"
+ "log"
+)
+
+func main() {
+ var srcPath string
+ var dstPath string
+ var outDirectory string
+
+ flag.StringVar(&srcPath, "src", "", "Directory containing packages that will be compared to destination")
+ flag.StringVar(&dstPath, "dst", "", "Directory containing packages; used to compare src packages")
+ flag.StringVar(&outDirectory, "out", "", "Directory where the report will be created")
+ flag.Parse()
+
+ reportBuilder, err := NewReportBuilder(srcPath, dstPath, outDirectory)
+ if err != nil {
+ log.Fatal("can't build report builder: ", err.Error())
+ }
+
+ log.Println("Building report...")
+ if err := reportBuilder.Build(); err != nil {
+ log.Fatalln("can't build report: ", err.Error())
+ }
+ log.Println("Report generation done!")
+}
diff --git a/misc/stdlib_diff/operation.go b/misc/stdlib_diff/operation.go
new file mode 100644
index 00000000000..5ebc632a90e
--- /dev/null
+++ b/misc/stdlib_diff/operation.go
@@ -0,0 +1,28 @@
+package main
+
+// operation is an enumeration type representing different types of operations. Used in diff algorithm
+// to indicates differences between files.
+type operation uint
+
+const (
+ // insert represents an insertion operation.
+ insert operation = iota + 1
+ // delete represents a deletion operation.
+ delete
+ // equal represents an equal operation.
+ equal
+)
+
+// String returns a string representation of the operation.
+func (op operation) String() string {
+ switch op {
+ case insert:
+ return "INS"
+ case delete:
+ return "DEL"
+ case equal:
+ return "EQ"
+ default:
+ return "UNKNOWN"
+ }
+}
diff --git a/misc/stdlib_diff/packagediff.go b/misc/stdlib_diff/packagediff.go
new file mode 100644
index 00000000000..f99578874b5
--- /dev/null
+++ b/misc/stdlib_diff/packagediff.go
@@ -0,0 +1,177 @@
+package main
+
+import (
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+// PackageDiffChecker is a struct for comparing and identifying differences
+// between files in two directories.
+type PackageDiffChecker struct {
+ SrcFiles []string // List of source files.
+ SrcPath string // Source directory path.
+ DstFiles []string // List of destination files.
+ DstPath string // Destination directory path.
+}
+
+// Differences represents the differences between source and destination packages.
+type Differences struct {
+ SameNumberOfFiles bool // Indicates whether the source and destination have the same number of files.
+ FilesDifferences []FileDifference // Differences in individual files.
+}
+
+// FileDifference represents the differences between source and destination files.
+type FileDifference struct {
+ Status string // Diff status of the processed files.
+ SourceName string // Name of the source file.
+ DestinationName string // Name of the destination file.
+ LineDiffferrences []LineDifferrence // Differences in source file lines.
+}
+
+// NewPackageDiffChecker creates a new PackageDiffChecker instance with the specified
+// source and destination paths. It initializes the SrcFiles and DstFiles fields by
+// listing files in the corresponding directories.
+func NewPackageDiffChecker(srcPath, dstPath string) (*PackageDiffChecker, error) {
+ srcFiles, err := listDirFiles(srcPath)
+ if err != nil {
+ return nil, err
+ }
+
+ dstFiles, err := listDirFiles(dstPath)
+ if err != nil {
+ return nil, err
+ }
+
+ return &PackageDiffChecker{
+ SrcFiles: srcFiles,
+ SrcPath: srcPath,
+ DstFiles: dstFiles,
+ DstPath: dstPath,
+ }, nil
+}
+
+// Differences calculates and returns the differences between source and destination
+// packages. It compares files line by line using the Myers algorithm.
+func (p *PackageDiffChecker) Differences() (*Differences, error) {
+ d := &Differences{
+ SameNumberOfFiles: p.hasSameNumberOfFiles(),
+ FilesDifferences: make([]FileDifference, 0),
+ }
+
+ srcFilesExt, dstFileExt := p.inferFileExtensions()
+ allFiles := p.listAllPossibleFiles()
+
+ for _, trimmedFileName := range allFiles {
+ srcFileName := trimmedFileName + srcFilesExt
+ srcFilePath := p.SrcPath + "/" + srcFileName
+ dstFileName := trimmedFileName + dstFileExt
+ dstFilePath := p.DstPath + "/" + dstFileName
+
+ diffChecker, err := NewDiffChecker(srcFilePath, dstFilePath)
+ if err != nil {
+ return nil, err
+ }
+
+ diff := diffChecker.Differences()
+
+ d.FilesDifferences = append(d.FilesDifferences, FileDifference{
+ Status: p.getStatus(diff).String(),
+ SourceName: srcFileName,
+ DestinationName: dstFileName,
+ LineDiffferrences: diff.Diffs,
+ })
+ }
+
+ return d, nil
+}
+
+// listAllPossibleFiles returns a list of unique file names without extensions
+// from both source and destination directories.
+func (p *PackageDiffChecker) listAllPossibleFiles() []string {
+ files := p.SrcFiles
+ files = append(files, p.DstFiles...)
+
+ for i := 0; i < len(files); i++ {
+ files[i] = strings.TrimSuffix(files[i], ".go")
+ files[i] = strings.TrimSuffix(files[i], ".gno")
+ }
+
+ unique := make(map[string]bool, len(files))
+ uniqueFiles := make([]string, len(unique))
+ for _, file := range files {
+ if len(file) != 0 {
+ if !unique[file] {
+ uniqueFiles = append(uniqueFiles, file)
+ unique[file] = true
+ }
+ }
+ }
+
+ return uniqueFiles
+}
+
+// inferFileExtensions by returning the src and dst files extensions.
+func (p *PackageDiffChecker) inferFileExtensions() (string, string) {
+ var goFiles, gnoFiles int
+ for _, file := range p.SrcFiles {
+ switch filepath.Ext(file) {
+ case ".go":
+ goFiles++
+ case ".gno":
+ gnoFiles++
+ }
+ }
+ if goFiles > gnoFiles {
+ return ".go", ".gno"
+ }
+
+ return ".gno", ".go"
+}
+
+// getStatus determines the diff status based on the differences in source and destination.
+// It returns a diffStatus indicating whether there is no difference, missing in source, missing in destination, or differences exist.
+func (p *PackageDiffChecker) getStatus(diff *Diff) diffStatus {
+ if diff.MissingSrc {
+ return missingInSrc
+ }
+
+ if diff.MissingDst {
+ return missingInDst
+ }
+
+ for _, diff := range diff.Diffs {
+ if diff.SrcOperation == delete || diff.DestOperation == insert {
+ return hasDiff
+ }
+ }
+
+ return noDiff
+}
+
+// hasSameNumberOfFiles checks if the source and destination have the same number of files.
+func (p *PackageDiffChecker) hasSameNumberOfFiles() bool {
+ return len(p.SrcFiles) == len(p.DstFiles)
+}
+
+// listDirFiles returns a list of file names in the specified directory.
+func listDirFiles(dirPath string) ([]string, error) {
+ fileNames := make([]string, 0)
+ dirEntries, err := os.ReadDir(dirPath)
+ if err != nil && !os.IsNotExist(err) {
+ return nil, err
+ }
+
+ for _, dirEntry := range dirEntries {
+ if dirEntry.IsDir() {
+ continue
+ }
+ // Only list .go and .gno files
+ if !strings.Contains(dirEntry.Name(), ".go") && !strings.Contains(dirEntry.Name(), ".gno") {
+ continue
+ }
+ fileNames = append(fileNames, dirEntry.Name())
+ }
+
+ return fileNames, nil
+}
diff --git a/misc/stdlib_diff/report.go b/misc/stdlib_diff/report.go
new file mode 100644
index 00000000000..6ad9d002d53
--- /dev/null
+++ b/misc/stdlib_diff/report.go
@@ -0,0 +1,314 @@
+package main
+
+import (
+ "bytes"
+ _ "embed"
+ "errors"
+ "fmt"
+ "html/template"
+ "io/fs"
+ "os"
+ "path/filepath"
+ "slices"
+ "strings"
+)
+
+var (
+ //go:embed templates/package_diff_template.html
+ packageDiffTemplate string
+ //go:embed templates/index_template.html
+ indexTemplate string
+)
+
+// ReportBuilder is a struct for building reports based on the differences
+// between source and destination directories.
+type ReportBuilder struct {
+ SrcPath string // Source directory path.
+ DstPath string // Destination directory path.
+ OutDir string // Output directory path for the reports.
+ packageTemplate *template.Template // Template for generating reports.
+ indexTemplate *template.Template // Template for generating index file of the reports.
+}
+
+// PackageDiffTemplateData represents the template data structure for a package's
+// differences between source and destination directories.
+type PackageDiffTemplateData struct {
+ PackageName string // Package name.
+ SrcFilesCount int // Number of files in the source package.
+ SrcPackageLocation string // Location of source files in the source directory.
+ DstFileCount int // Number of destination files in the package.
+ DstPackageLocation string // Location of destination files in the destination directory.
+ FilesDifferences []FileDifference // Differences in individual files.
+}
+
+type IndexTemplate struct {
+ Reports []LinkToReport
+}
+
+type LinkToReport struct {
+ PathToReport string
+ PackageName string
+ MissingGo bool
+ MissingGno bool
+ Subdirectories []LinkToReport
+}
+
+// NewReportBuilder creates a new ReportBuilder instance with the specified
+// source path, destination path, and output directory. It also initializes
+// the packageTemplate using the provided HTML template file.
+func NewReportBuilder(srcPath, dstPath, outDir string) (*ReportBuilder, error) {
+ packageTemplate, err := template.New("").Parse(packageDiffTemplate)
+ if err != nil {
+ return nil, err
+ }
+
+ indexTemplate, err := template.New("").Parse(indexTemplate)
+ if err != nil {
+ return nil, err
+ }
+
+ //filepath.EvalSymlinks will return the original path if there are no simlinks associated to the given path
+ realSrcPath, err := filepath.EvalSymlinks(srcPath)
+ if err != nil {
+ return nil, err
+ }
+
+ realDstPath, err := filepath.EvalSymlinks(dstPath)
+ if err != nil {
+ return nil, err
+ }
+
+ realOutPath, err := filepath.EvalSymlinks(outDir)
+ if err != nil {
+ if !errors.Is(err, fs.ErrNotExist) {
+ return nil, err
+ }
+ // Create output if not exist
+ err = os.MkdirAll(outDir, 0777)
+ if err != nil {
+ return nil, err
+ }
+ realOutPath = outDir
+ }
+ return &ReportBuilder{
+ // Trim suffix / in order to standardize paths accept path with or without `/`
+ SrcPath: strings.TrimSuffix(realSrcPath, `/`),
+ DstPath: strings.TrimSuffix(realDstPath, `/`),
+ OutDir: strings.TrimSuffix(realOutPath, `/`),
+ packageTemplate: packageTemplate,
+ indexTemplate: indexTemplate,
+ }, nil
+}
+
+// Build generates reports for differences between packages in the source and
+// destination directories. It iterates through each directory, calculates
+// differences using PackageDiffChecker, and generates reports using the
+// packageTemplate.
+func (builder *ReportBuilder) Build() error {
+ directories, err := builder.listDirectories()
+ if err != nil {
+ return err
+ }
+
+ indexTemplateData := &IndexTemplate{
+ Reports: make([]LinkToReport, 0),
+ }
+
+ for _, directory := range directories {
+ if err := builder.ExecuteDiffTemplate(directory); err != nil {
+ return err
+ }
+ report := LinkToReport{
+ PathToReport: "./" + directory.Path + "/report.html",
+ PackageName: directory.Path,
+ MissingGno: !directory.FoundInDest,
+ MissingGo: !directory.FoundInSrc,
+ Subdirectories: make([]LinkToReport, 0),
+ }
+ for _, subDirectory := range directory.Children {
+ if err := builder.ExecuteDiffTemplate(subDirectory); err != nil {
+ return err
+ }
+ report.Subdirectories = append(report.Subdirectories, LinkToReport{
+ PathToReport: "./" + subDirectory.Path + "/report.html",
+ PackageName: subDirectory.Path,
+ MissingGno: !subDirectory.FoundInDest,
+ MissingGo: !subDirectory.FoundInSrc,
+ })
+
+ }
+ indexTemplateData.Reports = append(indexTemplateData.Reports, report)
+
+ }
+
+ if err := builder.writeIndexTemplate(indexTemplateData); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (builder *ReportBuilder) ExecuteDiffTemplate(directory *Directory) error {
+ if !directory.FoundInDest {
+ return nil
+ }
+
+ srcPackagePath := builder.SrcPath + "/" + directory.Path
+ dstPackagePath := builder.DstPath + "/" + directory.Path
+ packageChecker, err := NewPackageDiffChecker(srcPackagePath, dstPackagePath)
+ if err != nil {
+ return fmt.Errorf("can't create new PackageDiffChecker: %w", err)
+ }
+
+ differences, err := packageChecker.Differences()
+ if err != nil {
+ return fmt.Errorf("can't compute differences: %w", err)
+ }
+
+ data := &PackageDiffTemplateData{
+ PackageName: directory.Path,
+ SrcFilesCount: len(packageChecker.SrcFiles),
+ SrcPackageLocation: srcPackagePath,
+ DstFileCount: len(packageChecker.DstFiles),
+ DstPackageLocation: dstPackagePath,
+ FilesDifferences: differences.FilesDifferences,
+ }
+
+ return builder.writePackageTemplate(data, directory.Path)
+}
+
+type Directory struct {
+ Path string
+ FoundInDest bool
+ FoundInSrc bool
+ Children []*Directory
+}
+
+// listDirectories retrieves a list of directories in the source path.
+func (builder *ReportBuilder) listDirectories() ([]*Directory, error) {
+ allSubdirectories, srcDirectories, destDirectories, err := builder.findDirectories()
+
+ if err != nil {
+ return nil, err
+ }
+
+ notfound := []string{}
+ directories := make(map[string]*Directory)
+ res := make([]*Directory, 0)
+
+ for _, folderName := range allSubdirectories {
+ if slices.ContainsFunc(notfound, func(s string) bool {
+ return strings.HasPrefix(folderName, s)
+ }) {
+ // this directory is not found in either source or destination skipping subsdirectories
+ continue
+ }
+
+ newDir := &Directory{
+ Path: folderName,
+ FoundInDest: destDirectories[folderName],
+ FoundInSrc: srcDirectories[folderName],
+ Children: make([]*Directory, 0),
+ }
+
+ if isRootFolder(folderName) {
+ directories[folderName] = newDir
+ res = append(res, newDir)
+ } else {
+ directory := directories[getRootFolder(folderName)]
+ directory.Children = append(directory.Children, newDir)
+ directories[getRootFolder(folderName)] = directory
+ }
+
+ if !newDir.FoundInDest && !newDir.FoundInSrc {
+ notfound = append(notfound, folderName)
+ }
+ }
+
+ return res, err
+}
+func isRootFolder(path string) bool {
+ return !strings.Contains(path, "/")
+}
+func getRootFolder(path string) string {
+ return strings.Split(path, "/")[0]
+}
+func (builder *ReportBuilder) getAllSubdirectories(rootPath string) ([]string, error) {
+ directories := make([]string, 0)
+ err := filepath.WalkDir(rootPath, func(path string, dirEntry fs.DirEntry, err error) error {
+ if path == rootPath {
+ return nil
+ }
+
+ if dirEntry.IsDir() {
+ folderName := strings.TrimPrefix(path, rootPath+"/")
+ directories = append(directories, folderName)
+ }
+ return nil
+ })
+ return directories, err
+}
+
+// writeIndexTemplate generates and writes the index template with the given output paths.
+func (builder *ReportBuilder) writeIndexTemplate(data *IndexTemplate) error {
+ resolvedTemplate := new(bytes.Buffer)
+ if err := builder.indexTemplate.Execute(resolvedTemplate, data); err != nil {
+ return err
+ }
+
+ if err := os.WriteFile(builder.OutDir+"/index.html", resolvedTemplate.Bytes(), 0644); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// writePackageTemplate executes the template with the provided data and
+// writes the generated report to the output directory.
+func (builder *ReportBuilder) writePackageTemplate(templateData any, packageName string) error {
+ resolvedTemplate := new(bytes.Buffer)
+ if err := builder.packageTemplate.Execute(resolvedTemplate, templateData); err != nil {
+ return err
+ }
+
+ if err := os.MkdirAll(builder.OutDir+"/"+packageName, 0777); err != nil {
+ return err
+ }
+
+ if err := os.WriteFile(builder.OutDir+"/"+packageName+"/report.html", resolvedTemplate.Bytes(), 0644); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (builder *ReportBuilder) findDirectories() ([]string, map[string]bool, map[string]bool, error) {
+ destDirectories, err := builder.getAllSubdirectories(builder.DstPath)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ srcDirectories, err := builder.getAllSubdirectories(builder.SrcPath)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ res := make([]string, 0, len(srcDirectories)+len(destDirectories))
+ srcMap := make(map[string]bool)
+ dstMap := make(map[string]bool)
+ for _, path := range srcDirectories {
+ res = append(res, path)
+ srcMap[path] = true
+ }
+
+ for _, path := range destDirectories {
+ dstMap[path] = true
+ if !srcMap[path] {
+ res = append(res, path)
+ }
+ }
+
+ slices.Sort(res)
+
+ return res, srcMap, dstMap, nil
+}
diff --git a/misc/stdlib_diff/templates/index_template.html b/misc/stdlib_diff/templates/index_template.html
new file mode 100644
index 00000000000..9d7d687b5ab
--- /dev/null
+++ b/misc/stdlib_diff/templates/index_template.html
@@ -0,0 +1,83 @@
+
+
+
+
+
+ Index
+
+
+
+ List of packages processed
+
+ {{- range .Reports}}
+ {{- if .MissingGno}}
+ {{.PackageName}}
: missing in gno
+ {{- else if .MissingGo}}
+ {{.PackageName}}
: missing in go
+ {{- else}}
+ {{if .Subdirectories}}
+
+ {{.PackageName}}
+
+ {{- range .Subdirectories}}
+ {{- if .MissingGno}}
+
+ {{.PackageName}}
: missing in gno
+
+ {{- else if .MissingGo}}
+
+ {{.PackageName}}
: missing in go
+
+ {{- else}}
+
+ {{- end}}
+ {{- end}}
+
+ {{- else}}
+ {{.PackageName}}
+ {{- end}}
+ {{- end}}
+
+ {{- end}}
+
+
+
\ No newline at end of file
diff --git a/misc/stdlib_diff/templates/package_diff_template.html b/misc/stdlib_diff/templates/package_diff_template.html
new file mode 100644
index 00000000000..bc5e173fc95
--- /dev/null
+++ b/misc/stdlib_diff/templates/package_diff_template.html
@@ -0,0 +1,135 @@
+{{define "file-viewer" }}
+{{if ne .Status "missing in src"}}
+
+
+ {{- range .LineDiffferrences}}
+ {{- if eq .SrcOperation 2}}
+
+
{{.SrcNumber}} {{.SrcLine}}
+
+
+ {{- else if eq .SrcOperation 3}}
+
+
{{.SrcNumber}}
+
{{.SrcLine}}
+
+ {{- else}}
+
+ {{- end}}
+ {{- end}}
+
+
+{{end}}
+ {{if ne .Status "missing in dst"}}
+
+
+ {{- range .LineDiffferrences}}
+ {{- if eq .DestOperation 1}}
+
+
{{.DestNumber}} {{.DestLine}}
+
+ {{- else if eq .DestOperation 3}}
+
+
{{.DestNumber}}
+
{{.DestLine}}
+
+ {{- else}}
+
+ {{- end}}
+ {{- end}}
+
+
+ {{end}}
+{{end}}
+
+
+
+
+
+
+ {{ .PackageName }}
+
+
+
+
+ {{ .PackageName }} package differences
+
+ Package information
+
+ Sources location
+
+ SRC: {{.SrcPackageLocation}}
+ DST: {{.DstPackageLocation}}
+
+
+ Number of files
+
+ SRC: {{.SrcFilesCount}}
+ DST: {{.DstFileCount}}
+
+
+ {{- range .FilesDifferences}}
+ {{- if eq .Status "files differ"}}
+
+ ~ {{.SourceName}} ({{.Status}})
+ {{- else if eq .Status "missing in dst"}}
+
+ - {{.SourceName}} ({{.Status}})
+ {{- else if eq .Status "missing in src"}}
+
+ + {{.SourceName}} ({{.Status}})
+ {{- else}}
+
+ = {{.SourceName}} ({{.Status}})
+ {{- end}}
+
+ {{template "file-viewer" .}}
+
+
+ {{- end}}
+
+
diff --git a/tm2/pkg/amino/genproto/example/proto/github.com/tendermint/go-amino/genproto/example/submodule/types.proto b/tm2/pkg/amino/genproto/example/proto/github.com/tendermint/go-amino/genproto/example/submodule/types.proto
deleted file mode 100644
index ce83012f548..00000000000
--- a/tm2/pkg/amino/genproto/example/proto/github.com/tendermint/go-amino/genproto/example/submodule/types.proto
+++ /dev/null
@@ -1,14 +0,0 @@
-syntax = "proto3";
-package submodule;
-
-option go_package = "github.com/gnolang/gno/tm2/pkg/amino/genproto/example/submodule/pb";
-
-// imports
-import "github.com/gnolang/gno/tm2/pkg/amino/genproto/example/submodule2/types.proto";
-
-// messages
-message StructSM {
- sint64 FieldA = 1;
- string FieldB = 2;
- submodule2.StructSM2 FieldC = 3;
-}
\ No newline at end of file
diff --git a/tm2/pkg/amino/genproto/example/proto/github.com/tendermint/go-amino/genproto/example/submodule2/types.proto b/tm2/pkg/amino/genproto/example/proto/github.com/tendermint/go-amino/genproto/example/submodule2/types.proto
deleted file mode 100644
index 4c98a1263fb..00000000000
--- a/tm2/pkg/amino/genproto/example/proto/github.com/tendermint/go-amino/genproto/example/submodule2/types.proto
+++ /dev/null
@@ -1,10 +0,0 @@
-syntax = "proto3";
-package submodule2;
-
-option go_package = "github.com/gnolang/gno/tm2/pkg/amino/genproto/example/submodule2/pb";
-
-// messages
-message StructSM2 {
- sint64 FieldA = 1;
- string FieldB = 2;
-}
\ No newline at end of file
diff --git a/tm2/pkg/amino/genproto/example/proto/main/types.proto b/tm2/pkg/amino/genproto/example/proto/main/types.proto
deleted file mode 100644
index ed107ee5495..00000000000
--- a/tm2/pkg/amino/genproto/example/proto/main/types.proto
+++ /dev/null
@@ -1,21 +0,0 @@
-syntax = "proto3";
-package main;
-
-option go_package = "github.com/gnolang/gno/tm2/pkg/amino/genproto/example/pb";
-
-// imports
-import "github.com/gnolang/gno/tm2/pkg/amino/genproto/example/submodule/types.proto";
-import "google/protobuf/any.proto";
-
-// messages
-message StructA {
- sint64 FieldC = 1;
- uint32 FieldD = 2;
-}
-message StructB {
- sint64 FieldC = 1;
- uint32 FieldD = 2;
- submodule.StructSM FieldE = 3;
- StructA FieldF = 4;
- google.protobuf.Any FieldG = 5;
-}
\ No newline at end of file
diff --git a/tm2/pkg/bft/blockchain/pool.go b/tm2/pkg/bft/blockchain/pool.go
index 5a82eb4d1d6..b610a0c0e7a 100644
--- a/tm2/pkg/bft/blockchain/pool.go
+++ b/tm2/pkg/bft/blockchain/pool.go
@@ -330,13 +330,13 @@ func (pool *BlockPool) removePeer(peerID p2p.ID) {
// If no peers are left, maxPeerHeight is set to 0.
func (pool *BlockPool) updateMaxPeerHeight() {
- var max int64
+ var maxVal int64
for _, peer := range pool.peers {
- if peer.height > max {
- max = peer.height
+ if peer.height > maxVal {
+ maxVal = peer.height
}
}
- pool.maxPeerHeight = max
+ pool.maxPeerHeight = maxVal
}
// Pick an available peer with at least the given minHeight.
diff --git a/tm2/pkg/bft/mempool/clist_mempool.go b/tm2/pkg/bft/mempool/clist_mempool.go
index 2cad23c68e7..a2bf4301e63 100644
--- a/tm2/pkg/bft/mempool/clist_mempool.go
+++ b/tm2/pkg/bft/mempool/clist_mempool.go
@@ -505,12 +505,12 @@ func (mem *CListMempool) ReapMaxBytesMaxGas(maxDataBytes, maxGas int64) types.Tx
return txs
}
-func (mem *CListMempool) ReapMaxTxs(max int) types.Txs {
+func (mem *CListMempool) ReapMaxTxs(maxVal int) types.Txs {
mem.mtx.Lock()
defer mem.mtx.Unlock()
- if max < 0 {
- max = mem.txs.Len()
+ if maxVal < 0 {
+ maxVal = mem.txs.Len()
}
for atomic.LoadInt32(&mem.rechecking) > 0 {
@@ -518,8 +518,8 @@ func (mem *CListMempool) ReapMaxTxs(max int) types.Txs {
time.Sleep(time.Millisecond * 10)
}
- txs := make([]types.Tx, 0, min(mem.txs.Len(), max))
- for e := mem.txs.Front(); e != nil && len(txs) <= max; e = e.Next() {
+ txs := make([]types.Tx, 0, min(mem.txs.Len(), maxVal))
+ for e := mem.txs.Front(); e != nil && len(txs) <= maxVal; e = e.Next() {
memTx := e.Value.(*mempoolTx)
txs = append(txs, memTx.tx)
}
diff --git a/tm2/pkg/bft/mempool/mempool.go b/tm2/pkg/bft/mempool/mempool.go
index 6f822eb99ff..482d8dd2d42 100644
--- a/tm2/pkg/bft/mempool/mempool.go
+++ b/tm2/pkg/bft/mempool/mempool.go
@@ -30,7 +30,7 @@ type Mempool interface {
// ReapMaxTxs reaps up to max transactions from the mempool.
// If max is negative, there is no cap on the size of all returned
// transactions (~ all available transactions).
- ReapMaxTxs(max int) types.Txs
+ ReapMaxTxs(maxVal int) types.Txs
// Lock locks the mempool. The consensus must be able to hold lock to safely update.
Lock()
diff --git a/tm2/pkg/bft/rpc/core/blocks.go b/tm2/pkg/bft/rpc/core/blocks.go
index 53ed25ade11..9ca4e05a46f 100644
--- a/tm2/pkg/bft/rpc/core/blocks.go
+++ b/tm2/pkg/bft/rpc/core/blocks.go
@@ -421,11 +421,11 @@ func getHeight(currentHeight int64, heightPtr *int64) (int64, error) {
return getHeightWithMin(currentHeight, heightPtr, 1)
}
-func getHeightWithMin(currentHeight int64, heightPtr *int64, min int64) (int64, error) {
+func getHeightWithMin(currentHeight int64, heightPtr *int64, minVal int64) (int64, error) {
if heightPtr != nil {
height := *heightPtr
- if height < min {
- return 0, fmt.Errorf("height must be greater than or equal to %d", min)
+ if height < minVal {
+ return 0, fmt.Errorf("height must be greater than or equal to %d", minVal)
}
if height > currentHeight {
return 0, fmt.Errorf("height must be less than or equal to the current blockchain height")
diff --git a/tm2/pkg/bft/rpc/core/blocks_test.go b/tm2/pkg/bft/rpc/core/blocks_test.go
index 550cc1542c9..dd55784ada0 100644
--- a/tm2/pkg/bft/rpc/core/blocks_test.go
+++ b/tm2/pkg/bft/rpc/core/blocks_test.go
@@ -11,11 +11,11 @@ func TestBlockchainInfo(t *testing.T) {
t.Parallel()
cases := []struct {
- min, max int64
- height int64
- limit int64
- resultLength int64
- wantErr bool
+ minVal, maxVal int64
+ height int64
+ limit int64
+ resultLength int64
+ wantErr bool
}{
// min > max
{0, 0, 0, 10, 0, true}, // min set to 1
@@ -46,12 +46,12 @@ func TestBlockchainInfo(t *testing.T) {
for i, c := range cases {
caseString := fmt.Sprintf("test %d failed", i)
- min, max, err := filterMinMax(c.height, c.min, c.max, c.limit)
+ minVal, maxVal, err := filterMinMax(c.height, c.minVal, c.maxVal, c.limit)
if c.wantErr {
require.Error(t, err, caseString)
} else {
require.NoError(t, err, caseString)
- require.Equal(t, 1+max-min, c.resultLength, caseString)
+ require.Equal(t, 1+maxVal-minVal, c.resultLength, caseString)
}
}
}
@@ -62,7 +62,7 @@ func TestGetHeight(t *testing.T) {
cases := []struct {
currentHeight int64
heightPtr *int64
- min int64
+ minVal int64
res int64
wantErr bool
}{
@@ -79,7 +79,7 @@ func TestGetHeight(t *testing.T) {
for i, c := range cases {
caseString := fmt.Sprintf("test %d failed", i)
- res, err := getHeightWithMin(c.currentHeight, c.heightPtr, c.min)
+ res, err := getHeightWithMin(c.currentHeight, c.heightPtr, c.minVal)
if c.wantErr {
require.Error(t, err, caseString)
} else {
diff --git a/tm2/pkg/bft/rpc/lib/server/http_server_test.go b/tm2/pkg/bft/rpc/lib/server/http_server_test.go
index 6c6d9ad14d6..f089d262a71 100644
--- a/tm2/pkg/bft/rpc/lib/server/http_server_test.go
+++ b/tm2/pkg/bft/rpc/lib/server/http_server_test.go
@@ -22,28 +22,28 @@ import (
func TestMaxOpenConnections(t *testing.T) {
t.Parallel()
- const max = 5 // max simultaneous connections
+ const maxVal = 5 // max simultaneous connections
// Start the server.
var open int32
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
- if n := atomic.AddInt32(&open, 1); n > int32(max) {
- t.Errorf("%d open connections, want <= %d", n, max)
+ if n := atomic.AddInt32(&open, 1); n > int32(maxVal) {
+ t.Errorf("%d open connections, want <= %d", n, maxVal)
}
defer atomic.AddInt32(&open, -1)
time.Sleep(10 * time.Millisecond)
fmt.Fprint(w, "some body")
})
config := DefaultConfig()
- config.MaxOpenConnections = max
+ config.MaxOpenConnections = maxVal
l, err := Listen("tcp://127.0.0.1:0", config)
require.NoError(t, err)
defer l.Close()
go StartHTTPServer(l, mux, log.NewTestingLogger(t), config)
// Make N GET calls to the server.
- attempts := max * 2
+ attempts := maxVal * 2
var wg sync.WaitGroup
var failed int32
for i := 0; i < attempts; i++ {
diff --git a/tm2/pkg/bft/types/evidence.go b/tm2/pkg/bft/types/evidence.go
index c11021e3976..85b08df6ba9 100644
--- a/tm2/pkg/bft/types/evidence.go
+++ b/tm2/pkg/bft/types/evidence.go
@@ -38,8 +38,8 @@ type EvidenceOverflowError struct {
}
// NewErrEvidenceOverflow returns a new EvidenceOverflowError where got > max.
-func NewErrEvidenceOverflow(max, got int64) *EvidenceOverflowError {
- return &EvidenceOverflowError{max, got}
+func NewErrEvidenceOverflow(maxVal, got int64) *EvidenceOverflowError {
+ return &EvidenceOverflowError{maxVal, got}
}
// Error returns a string representation of the error.
diff --git a/tm2/pkg/bft/types/genesis.go b/tm2/pkg/bft/types/genesis.go
index c03f7acc09e..b927b7f8f0c 100644
--- a/tm2/pkg/bft/types/genesis.go
+++ b/tm2/pkg/bft/types/genesis.go
@@ -179,7 +179,7 @@ func GenesisDocFromFile(genDocFile string) (*GenesisDoc, error) {
}
genDoc, err := GenesisDocFromJSON(jsonBlob)
if err != nil {
- return nil, errors.Wrap(err, fmt.Sprintf("Error reading GenesisDoc at %v", genDocFile))
+ return nil, errors.Wrapf(err, "Error reading GenesisDoc at %v", genDocFile)
}
return genDoc, nil
}
diff --git a/tm2/pkg/bft/types/validator_set.go b/tm2/pkg/bft/types/validator_set.go
index 80ed994ca39..c5dc5be1291 100644
--- a/tm2/pkg/bft/types/validator_set.go
+++ b/tm2/pkg/bft/types/validator_set.go
@@ -162,17 +162,17 @@ func computeMaxMinPriorityDiff(vals *ValidatorSet) int64 {
if vals.IsNilOrEmpty() {
panic("empty validator set")
}
- max := int64(math.MinInt64)
- min := int64(math.MaxInt64)
+ maxVal := int64(math.MinInt64)
+ minVal := int64(math.MaxInt64)
for _, v := range vals.Validators {
- if v.ProposerPriority < min {
- min = v.ProposerPriority
+ if v.ProposerPriority < minVal {
+ minVal = v.ProposerPriority
}
- if v.ProposerPriority > max {
- max = v.ProposerPriority
+ if v.ProposerPriority > maxVal {
+ maxVal = v.ProposerPriority
}
}
- diff := max - min
+ diff := maxVal - minVal
if diff < 0 {
return -1 * diff
} else {
diff --git a/tm2/pkg/bft/types/vote_set.go b/tm2/pkg/bft/types/vote_set.go
index bf6200bff15..496b9b37d60 100644
--- a/tm2/pkg/bft/types/vote_set.go
+++ b/tm2/pkg/bft/types/vote_set.go
@@ -167,7 +167,7 @@ func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) {
if (vote.Height != voteSet.height) ||
(vote.Round != voteSet.round) ||
(vote.Type != voteSet.type_) {
- return false, errors.Wrap(ErrVoteUnexpectedStep, "Expected %d/%d/%d, but got %d/%d/%d",
+ return false, errors.Wrapf(ErrVoteUnexpectedStep, "Expected %d/%d/%d, but got %d/%d/%d",
voteSet.height, voteSet.round, voteSet.type_,
vote.Height, vote.Round, vote.Type)
}
@@ -175,13 +175,13 @@ func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) {
// Ensure that signer is a validator.
lookupAddr, val := voteSet.valSet.GetByIndex(valIndex)
if val == nil {
- return false, errors.Wrap(ErrVoteInvalidValidatorIndex,
+ return false, errors.Wrapf(ErrVoteInvalidValidatorIndex,
"Cannot find validator %d in valSet of size %d", valIndex, voteSet.valSet.Size())
}
// Ensure that the signer has the right address.
if valAddr != lookupAddr {
- return false, errors.Wrap(ErrVoteInvalidValidatorAddress,
+ return false, errors.Wrapf(ErrVoteInvalidValidatorAddress,
"vote.ValidatorAddress (%X) does not match address (%X) for vote.ValidatorIndex (%d)\nEnsure the genesis file is correct across all validators.",
valAddr, lookupAddr, valIndex)
}
@@ -191,12 +191,12 @@ func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) {
if bytes.Equal(existing.Signature, vote.Signature) {
return false, nil // duplicate
}
- return false, errors.Wrap(ErrVoteNonDeterministicSignature, "Existing vote: %v; New vote: %v", existing, vote)
+ return false, errors.Wrapf(ErrVoteNonDeterministicSignature, "Existing vote: %v; New vote: %v", existing, vote)
}
// Check signature.
if err := vote.Verify(voteSet.chainID, val.PubKey); err != nil {
- return false, errors.Wrap(err, "Failed to verify vote with ChainID %s and PubKey %s", voteSet.chainID, val.PubKey)
+ return false, errors.Wrapf(err, "Failed to verify vote with ChainID %s and PubKey %s", voteSet.chainID, val.PubKey)
}
// Add vote and get conflicting vote if any.
diff --git a/tm2/pkg/bft/wal/wal.go b/tm2/pkg/bft/wal/wal.go
index 2424f45dfd2..09fed44b2b1 100644
--- a/tm2/pkg/bft/wal/wal.go
+++ b/tm2/pkg/bft/wal/wal.go
@@ -278,8 +278,8 @@ func (wal *baseWAL) SearchForHeight(height int64, options *WALSearchOptions) (rd
// NOTE: starting from the last file in the group because we're usually
// searching for the last height. See replay.go
- min, max := wal.group.MinIndex(), wal.group.MaxIndex()
- wal.Logger.Info("Searching for height", "height", height, "min", min, "max", max)
+ minVal, maxVal := wal.group.MinIndex(), wal.group.MaxIndex()
+ wal.Logger.Info("Searching for height", "height", height, "min", minVal, "max", maxVal)
var (
mode = WALSearchModeBackwards
@@ -293,18 +293,18 @@ func (wal *baseWAL) SearchForHeight(height int64, options *WALSearchOptions) (rd
}
OUTER_LOOP:
- for min <= max {
+ for minVal <= maxVal {
var index int
// set index depending on mode.
switch mode {
case WALSearchModeBackwards:
- index = max + backoff + idxoff
- if max < index {
+ index = maxVal + backoff + idxoff
+ if maxVal < index {
// (max+backoff)+ doesn't contain any height.
// adjust max & backoff accordingly.
idxoff = 0
- max = max + backoff - 1
+ maxVal = maxVal + backoff - 1
if backoff == 0 {
backoff = -1
} else {
@@ -312,16 +312,16 @@ OUTER_LOOP:
}
continue OUTER_LOOP
}
- if index < min {
+ if index < minVal {
panic("should not happen")
}
case WALSearchModeBinary:
- index = (min+max+1)/2 + idxoff
- if max < index {
+ index = (minVal+maxVal+1)/2 + idxoff
+ if maxVal < index {
// ((min+max+1)/2)+ doesn't contain any height.
// adjust max & binary search accordingly.
idxoff = 0
- max = (min+max+1)/2 - 1
+ maxVal = (minVal+maxVal+1)/2 - 1
continue OUTER_LOOP
}
}
@@ -360,24 +360,24 @@ OUTER_LOOP:
case WALSearchModeBackwards:
idxoff = 0
if backoff == 0 {
- max--
+ maxVal--
backoff = -1
} else {
- max += backoff
+ maxVal += backoff
backoff *= 2
}
// convert to binary search if backoff is too big.
// max+backoff would work but max+(backoff*2) is smoother.
- if max+(backoff*2) <= min {
+ if maxVal+(backoff*2) <= minVal {
wal.Logger.Info("Converting to binary search",
- "height", height, "min", min,
- "max", max, "backoff", backoff)
+ "height", height, "min", minVal,
+ "max", maxVal, "backoff", backoff)
backoff = 0
mode = WALSearchModeBinary
}
case WALSearchModeBinary:
idxoff = 0
- max = (min+max+1)/2 - 1
+ maxVal = (minVal+maxVal+1)/2 - 1
}
dec.Close()
continue OUTER_LOOP
@@ -398,21 +398,21 @@ OUTER_LOOP:
} else {
// convert to binary search with index as new min.
wal.Logger.Info("Converting to binary search with new min",
- "height", height, "min", min,
- "max", max, "backoff", backoff)
+ "height", height, "min", minVal,
+ "max", maxVal, "backoff", backoff)
idxoff = 0
backoff = 0
- min = index
+ minVal = index
mode = WALSearchModeBinary
dec.Close()
continue OUTER_LOOP
}
case WALSearchModeBinary:
- if index < max {
+ if index < maxVal {
// maybe in @index, but first try binary search
// between @index and max.
idxoff = 0
- min = index
+ minVal = index
dec.Close()
continue OUTER_LOOP
} else { // index == max
diff --git a/tm2/pkg/crypto/keys/client/maketx.go b/tm2/pkg/crypto/keys/client/maketx.go
index 7e67392ebe7..0801fcfe227 100644
--- a/tm2/pkg/crypto/keys/client/maketx.go
+++ b/tm2/pkg/crypto/keys/client/maketx.go
@@ -208,11 +208,11 @@ func ExecSignAndBroadcast(
return errors.Wrap(err, "broadcast tx")
}
if bres.CheckTx.IsErr() {
- return errors.Wrap(bres.CheckTx.Error, "check transaction failed: log:%s", bres.CheckTx.Log)
+ return errors.Wrapf(bres.CheckTx.Error, "check transaction failed: log:%s", bres.CheckTx.Log)
}
if bres.DeliverTx.IsErr() {
io.Println("TX HASH: ", base64.StdEncoding.EncodeToString(bres.Hash))
- return errors.Wrap(bres.DeliverTx.Error, "deliver transaction failed: log:%s", bres.DeliverTx.Log)
+ return errors.Wrapf(bres.DeliverTx.Error, "deliver transaction failed: log:%s", bres.DeliverTx.Log)
}
io.Println(string(bres.DeliverTx.Data))
diff --git a/tm2/pkg/crypto/keys/keybase.go b/tm2/pkg/crypto/keys/keybase.go
index ea3d0546fa0..23c4237151a 100644
--- a/tm2/pkg/crypto/keys/keybase.go
+++ b/tm2/pkg/crypto/keys/keybase.go
@@ -298,7 +298,7 @@ func (kb dbKeybase) ExportPrivateKeyObject(nameOrBech32 string, passphrase strin
func (kb dbKeybase) Export(nameOrBech32 string) (astr string, err error) {
info, err := kb.GetByNameOrAddress(nameOrBech32)
if err != nil {
- return "", errors.Wrap(err, "getting info for name %s", nameOrBech32)
+ return "", errors.Wrapf(err, "getting info for name %s", nameOrBech32)
}
bz := kb.db.Get(infoKey(info.GetName()))
if bz == nil {
@@ -313,7 +313,7 @@ func (kb dbKeybase) Export(nameOrBech32 string) (astr string, err error) {
func (kb dbKeybase) ExportPubKey(nameOrBech32 string) (astr string, err error) {
info, err := kb.GetByNameOrAddress(nameOrBech32)
if err != nil {
- return "", errors.Wrap(err, "getting info for name %s", nameOrBech32)
+ return "", errors.Wrapf(err, "getting info for name %s", nameOrBech32)
}
return armor.ArmorPubKeyBytes(info.GetPubKey().Bytes()), nil
}
@@ -523,10 +523,17 @@ func (kb dbKeybase) writeInfo(name string, info Info) error {
kb.db.DeleteSync(addrKey(oldInfo.GetAddress()))
}
+ addressKey := addrKey(info.GetAddress())
+ nameKeyForAddress := kb.db.Get(addressKey)
+ if len(nameKeyForAddress) > 0 {
+ // Enforce 1-to-1 name to address. Remove the info by the old name with the same address
+ kb.db.DeleteSync(nameKeyForAddress)
+ }
+
serializedInfo := writeInfo(info)
kb.db.SetSync(key, serializedInfo)
// store a pointer to the infokey by address for fast lookup
- kb.db.SetSync(addrKey(info.GetAddress()), key)
+ kb.db.SetSync(addressKey, key)
return nil
}
diff --git a/tm2/pkg/crypto/keys/keybase_test.go b/tm2/pkg/crypto/keys/keybase_test.go
index bfb21b46fad..25306e62635 100644
--- a/tm2/pkg/crypto/keys/keybase_test.go
+++ b/tm2/pkg/crypto/keys/keybase_test.go
@@ -149,11 +149,12 @@ func TestSignVerify(t *testing.T) {
i2, err := cstore.CreateAccount(n2, mn2, bip39Passphrase, p2, 0, 0)
require.Nil(t, err)
- // Import a public key
+ // Import a public key into a new store
armor, err := cstore.ExportPubKey(n2)
require.Nil(t, err)
- cstore.ImportPubKey(n3, armor)
- i3, err := cstore.GetByName(n3)
+ cstore2 := NewInMemory()
+ cstore2.ImportPubKey(n3, armor)
+ i3, err := cstore2.GetByName(n3)
require.NoError(t, err)
require.Equal(t, i3.GetName(), n3)
@@ -174,6 +175,7 @@ func TestSignVerify(t *testing.T) {
s21, pub2, err := cstore.Sign(n2, p2, d1)
require.Nil(t, err)
require.Equal(t, i2.GetPubKey(), pub2)
+ require.Equal(t, i3.GetPubKey(), pub2)
s22, pub2, err := cstore.Sign(n2, p2, d2)
require.Nil(t, err)
@@ -282,11 +284,10 @@ func TestExportImportPubKey(t *testing.T) {
require.NoError(t, err)
// Compare the public keys
require.True(t, john.GetPubKey().Equals(john2.GetPubKey()))
- // Ensure the original key hasn't changed
- john, err = cstore.GetByName("john")
+ // Ensure that storing with the address of "john-pubkey-only" removed the entry for "john"
+ has, err := cstore.HasByName("john")
require.NoError(t, err)
- require.Equal(t, john.GetPubKey().Address(), addr)
- require.Equal(t, john.GetName(), "john")
+ require.False(t, has)
// Ensure keys cannot be overwritten
err = cstore.ImportPubKey("john-pubkey-only", armor)
diff --git a/tm2/pkg/crypto/keys/types.go b/tm2/pkg/crypto/keys/types.go
index 3865951168e..bdaf39caa54 100644
--- a/tm2/pkg/crypto/keys/types.go
+++ b/tm2/pkg/crypto/keys/types.go
@@ -27,10 +27,12 @@ type Keybase interface {
// CreateAccount creates an account based using the BIP44 path (44'/118'/{account}'/0/{index}
// Encrypt the key to disk using encryptPasswd.
+ // If an account exists with the same address but a different name, it is replaced by the new name.
// See https://github.com/tendermint/classic/sdk/issues/2095
CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd string, account uint32, index uint32) (Info, error)
// Like CreateAccount but from general bip44 params.
+ // If an account exists with the same address but a different name, it is replaced by the new name.
CreateAccountBip44(name, mnemonic, bip39Passwd, encryptPasswd string, params hd.BIP44Params) (Info, error)
// CreateLedger creates, stores, and returns a new Ledger key reference
@@ -43,6 +45,7 @@ type Keybase interface {
CreateMulti(name string, pubkey crypto.PubKey) (info Info, err error)
// The following operations will *only* work on locally-stored keys
+ // In all import operations, if an account exists with the same address but a different name, it is replaced by the new name.
Rotate(name, oldpass string, getNewpass func() (string, error)) error
Import(name string, armor string) (err error)
ImportPrivKey(name, armor, decryptPassphrase, encryptPassphrase string) error
diff --git a/tm2/pkg/crypto/merkle/proof_key_path.go b/tm2/pkg/crypto/merkle/proof_key_path.go
index 278f782833c..469a69bf2bc 100644
--- a/tm2/pkg/crypto/merkle/proof_key_path.go
+++ b/tm2/pkg/crypto/merkle/proof_key_path.go
@@ -96,13 +96,13 @@ func KeyPathToKeys(path string) (keys [][]byte, err error) {
hexPart := part[2:]
key, err := hex.DecodeString(hexPart)
if err != nil {
- return nil, errors.Wrap(err, "decoding hex-encoded part #%d: /%s", i, part)
+ return nil, errors.Wrapf(err, "decoding hex-encoded part #%d: /%s", i, part)
}
keys[i] = key
} else {
key, err := url.PathUnescape(part)
if err != nil {
- return nil, errors.Wrap(err, "decoding url-encoded part #%d: /%s", i, part)
+ return nil, errors.Wrapf(err, "decoding url-encoded part #%d: /%s", i, part)
}
keys[i] = []byte(key) // TODO Test this with random bytes, I'm not sure that it works for arbitrary bytes...
}
diff --git a/tm2/pkg/crypto/secp256k1/secp256k1.go b/tm2/pkg/crypto/secp256k1/secp256k1.go
index 03f51f5ebf9..c9bb3f39c26 100644
--- a/tm2/pkg/crypto/secp256k1/secp256k1.go
+++ b/tm2/pkg/crypto/secp256k1/secp256k1.go
@@ -8,7 +8,7 @@ import (
"math/big"
secp256k1 "github.com/btcsuite/btcd/btcec/v2"
- "golang.org/x/crypto/ripemd160"
+ "golang.org/x/crypto/ripemd160" //nolint:gosec
"github.com/gnolang/gno/tm2/pkg/amino"
"github.com/gnolang/gno/tm2/pkg/crypto"
@@ -124,8 +124,8 @@ func (pubKey PubKeySecp256k1) Address() crypto.Address {
hasherSHA256.Write(pubKey[:]) // does not error
sha := hasherSHA256.Sum(nil)
- hasherRIPEMD160 := ripemd160.New()
- hasherRIPEMD160.Write(sha) // does not error
+ hasherRIPEMD160 := ripemd160.New() //nolint:gosec
+ hasherRIPEMD160.Write(sha) // does not error
return crypto.AddressFromBytes(hasherRIPEMD160.Sum(nil))
}
diff --git a/tm2/pkg/errors/errors.go b/tm2/pkg/errors/errors.go
index c72d9c64680..1b40c903c41 100644
--- a/tm2/pkg/errors/errors.go
+++ b/tm2/pkg/errors/errors.go
@@ -8,19 +8,26 @@ import (
// ----------------------------------------
// Convenience method.
-func Wrap(cause interface{}, format string, args ...interface{}) Error {
+func Wrap(cause interface{}, msg string) Error {
if causeCmnError, ok := cause.(*cmnError); ok { //nolint:gocritic
- msg := fmt.Sprintf(format, args...)
return causeCmnError.Stacktrace().Trace(1, msg)
} else if cause == nil {
- return newCmnError(FmtError{format, args}).Stacktrace()
+ return newCmnError(FmtError{format: msg, args: []interface{}{}}).Stacktrace()
} else {
// NOTE: causeCmnError is a typed nil here.
- msg := fmt.Sprintf(format, args...)
return newCmnError(cause).Stacktrace().Trace(1, msg)
}
}
+func Wrapf(cause interface{}, format string, args ...interface{}) Error {
+ if cause == nil {
+ return newCmnError(FmtError{format, args}).Stacktrace()
+ }
+
+ msg := fmt.Sprintf(format, args...)
+ return Wrap(cause, msg)
+}
+
func Cause(err error) error {
if cerr, ok := err.(*cmnError); ok {
return cerr.Data().(error)
diff --git a/tm2/pkg/errors/errors_test.go b/tm2/pkg/errors/errors_test.go
index 21115c21862..ab7a7086ad4 100644
--- a/tm2/pkg/errors/errors_test.go
+++ b/tm2/pkg/errors/errors_test.go
@@ -35,7 +35,7 @@ func TestErrorPanic(t *testing.T) {
func TestWrapSomething(t *testing.T) {
t.Parallel()
- err := Wrap("something", "formatter%v%v", 0, 1)
+ err := Wrapf("something", "formatter%v%v", 0, 1)
assert.Equal(t, "something", err.Data())
assert.Equal(t, "something", fmt.Sprintf("%v", err))
@@ -46,7 +46,7 @@ func TestWrapSomething(t *testing.T) {
func TestWrapNothing(t *testing.T) {
t.Parallel()
- err := Wrap(nil, "formatter%v%v", 0, 1)
+ err := Wrapf(nil, "formatter%v%v", 0, 1)
assert.Equal(t,
FmtError{"formatter%v%v", []interface{}{0, 1}},
diff --git a/tm2/pkg/iavl/proof_range.go b/tm2/pkg/iavl/proof_range.go
index ea6bce24fc0..0ce8ebdf057 100644
--- a/tm2/pkg/iavl/proof_range.go
+++ b/tm2/pkg/iavl/proof_range.go
@@ -273,7 +273,7 @@ func (proof *RangeProof) _computeRootHash() (rootHash []byte, treeEnd bool, err
return nil, treeEnd, false, errors.Wrap(err, "recursive COMPUTEHASH call")
}
if !bytes.Equal(derivedRoot, lpath.Right) {
- return nil, treeEnd, false, errors.Wrap(ErrInvalidRoot, "intermediate root hash %X doesn't match, got %X", lpath.Right, derivedRoot)
+ return nil, treeEnd, false, errors.Wrapf(ErrInvalidRoot, "intermediate root hash %X doesn't match, got %X", lpath.Right, derivedRoot)
}
if done {
return hash, treeEnd, true, nil
diff --git a/tm2/pkg/os/os.go b/tm2/pkg/os/os.go
index f0e5825cb14..63601ded92a 100644
--- a/tm2/pkg/os/os.go
+++ b/tm2/pkg/os/os.go
@@ -33,7 +33,7 @@ func Kill() error {
}
func Exit(s string) {
- fmt.Printf(s + "\n")
+ fmt.Print(s + "\n")
os.Exit(1)
}
diff --git a/tm2/pkg/overflow/README.md b/tm2/pkg/overflow/README.md
new file mode 100644
index 00000000000..55a9ba4c327
--- /dev/null
+++ b/tm2/pkg/overflow/README.md
@@ -0,0 +1,66 @@
+# overflow
+
+Check for int/int8/int16/int64/int32 integer overflow in Golang arithmetic.
+
+Forked from https://github.com/JohnCGriffin/overflow
+
+### Install
+```
+go get github.com/johncgriffin/overflow
+```
+Note that because Go has no template types, the majority of repetitive code is
+generated by overflow_template.sh. If you have to change an
+algorithm, change it there and regenerate the Go code via:
+```
+go generate
+```
+### Synopsis
+
+```
+package main
+
+import "fmt"
+import "math"
+import "github.com/JohnCGriffin/overflow"
+
+func main() {
+
+ addend := math.MaxInt64 - 5
+
+ for i := 0; i < 10; i++ {
+ sum, ok := overflow.Add(addend, i)
+ fmt.Printf("%v+%v -> (%v,%v)\n",
+ addend, i, sum, ok)
+ }
+
+}
+```
+yields the output
+```
+9223372036854775802+0 -> (9223372036854775802,true)
+9223372036854775802+1 -> (9223372036854775803,true)
+9223372036854775802+2 -> (9223372036854775804,true)
+9223372036854775802+3 -> (9223372036854775805,true)
+9223372036854775802+4 -> (9223372036854775806,true)
+9223372036854775802+5 -> (9223372036854775807,true)
+9223372036854775802+6 -> (0,false)
+9223372036854775802+7 -> (0,false)
+9223372036854775802+8 -> (0,false)
+9223372036854775802+9 -> (0,false)
+```
+
+For int, int64, and int32 types, provide Add, Add32, Add64, Sub, Sub32, Sub64, etc.
+Unsigned types not covered at the moment, but such additions are welcome.
+
+### Stay calm and panic
+
+There's a good case to be made that a panic is an unidiomatic but proper response. Iff you
+believe that there's no valid way to continue your program after math goes wayward, you can
+use the easier Addp, Mulp, Subp, and Divp versions which return the normal result or panic.
+
+
+
+
+
+
+
diff --git a/tm2/pkg/overflow/overflow.go b/tm2/pkg/overflow/overflow.go
new file mode 100644
index 00000000000..b476ea5776e
--- /dev/null
+++ b/tm2/pkg/overflow/overflow.go
@@ -0,0 +1,131 @@
+/*
+Package overflow offers overflow-checked integer arithmetic operations
+for int, int32, and int64. Each of the operations returns a
+result,bool combination. This was prompted by the need to know when
+to flow into higher precision types from the math.big library.
+
+For instance, assuing a 64 bit machine:
+
+10 + 20 -> 30
+int(math.MaxInt64) + 1 -> -9223372036854775808
+
+whereas
+
+overflow.Add(10,20) -> (30, true)
+overflow.Add(math.MaxInt64,1) -> (0, false)
+
+Add, Sub, Mul, Div are for int. Add64, Add32, etc. are specifically sized.
+
+If anybody wishes an unsigned version, submit a pull request for code
+and new tests.
+*/
+package overflow
+
+//go:generate ./overflow_template.sh
+
+import "math"
+
+func _is64Bit() bool {
+ maxU32 := uint(math.MaxUint32)
+ return ((maxU32 << 1) >> 1) == maxU32
+}
+
+/********** PARTIAL TEST COVERAGE FROM HERE DOWN *************
+
+The only way that I could see to do this is a combination of
+my normal 64 bit system and a GopherJS running on Node. My
+understanding is that its ints are 32 bit.
+
+So, FEEL FREE to carefully review the code visually.
+
+*************************************************************/
+
+// Unspecified size, i.e. normal signed int
+
+// Add sums two ints, returning the result and a boolean status.
+func Add(a, b int) (int, bool) {
+ if _is64Bit() {
+ r64, ok := Add64(int64(a), int64(b))
+ return int(r64), ok
+ }
+ r32, ok := Add32(int32(a), int32(b))
+ return int(r32), ok
+}
+
+// Sub returns the difference of two ints and a boolean status.
+func Sub(a, b int) (int, bool) {
+ if _is64Bit() {
+ r64, ok := Sub64(int64(a), int64(b))
+ return int(r64), ok
+ }
+ r32, ok := Sub32(int32(a), int32(b))
+ return int(r32), ok
+}
+
+// Mul returns the product of two ints and a boolean status.
+func Mul(a, b int) (int, bool) {
+ if _is64Bit() {
+ r64, ok := Mul64(int64(a), int64(b))
+ return int(r64), ok
+ }
+ r32, ok := Mul32(int32(a), int32(b))
+ return int(r32), ok
+}
+
+// Div returns the quotient of two ints and a boolean status
+func Div(a, b int) (int, bool) {
+ if _is64Bit() {
+ r64, ok := Div64(int64(a), int64(b))
+ return int(r64), ok
+ }
+ r32, ok := Div32(int32(a), int32(b))
+ return int(r32), ok
+}
+
+// Quotient returns the quotient, remainder and status of two ints
+func Quotient(a, b int) (int, int, bool) {
+ if _is64Bit() {
+ q64, r64, ok := Quotient64(int64(a), int64(b))
+ return int(q64), int(r64), ok
+ }
+ q32, r32, ok := Quotient32(int32(a), int32(b))
+ return int(q32), int(r32), ok
+}
+
+/************* Panic versions for int ****************/
+
+// Addp returns the sum of two ints, panicking on overflow
+func Addp(a, b int) int {
+ r, ok := Add(a, b)
+ if !ok {
+ panic("addition overflow")
+ }
+ return r
+}
+
+// Subp returns the difference of two ints, panicking on overflow.
+func Subp(a, b int) int {
+ r, ok := Sub(a, b)
+ if !ok {
+ panic("subtraction overflow")
+ }
+ return r
+}
+
+// Mulp returns the product of two ints, panicking on overflow.
+func Mulp(a, b int) int {
+ r, ok := Mul(a, b)
+ if !ok {
+ panic("multiplication overflow")
+ }
+ return r
+}
+
+// Divp returns the quotient of two ints, panicking on overflow.
+func Divp(a, b int) int {
+ r, ok := Div(a, b)
+ if !ok {
+ panic("division failure")
+ }
+ return r
+}
diff --git a/tm2/pkg/overflow/overflow_impl.go b/tm2/pkg/overflow/overflow_impl.go
new file mode 100644
index 00000000000..a9a90c43835
--- /dev/null
+++ b/tm2/pkg/overflow/overflow_impl.go
@@ -0,0 +1,360 @@
+package overflow
+
+// This is generated code, created by overflow_template.sh executed
+// by "go generate"
+
+// Add8 performs + operation on two int8 operands
+// returning a result and status
+func Add8(a, b int8) (int8, bool) {
+ c := a + b
+ if (c > a) == (b > 0) {
+ return c, true
+ }
+ return c, false
+}
+
+// Add8p is the unchecked panicing version of Add8
+func Add8p(a, b int8) int8 {
+ r, ok := Add8(a, b)
+ if !ok {
+ panic("addition overflow")
+ }
+ return r
+}
+
+// Sub8 performs - operation on two int8 operands
+// returning a result and status
+func Sub8(a, b int8) (int8, bool) {
+ c := a - b
+ if (c < a) == (b > 0) {
+ return c, true
+ }
+ return c, false
+}
+
+// Sub8p is the unchecked panicing version of Sub8
+func Sub8p(a, b int8) int8 {
+ r, ok := Sub8(a, b)
+ if !ok {
+ panic("subtraction overflow")
+ }
+ return r
+}
+
+// Mul8 performs * operation on two int8 operands
+// returning a result and status
+func Mul8(a, b int8) (int8, bool) {
+ if a == 0 || b == 0 {
+ return 0, true
+ }
+ c := a * b
+ if (c < 0) == ((a < 0) != (b < 0)) {
+ if c/b == a {
+ return c, true
+ }
+ }
+ return c, false
+}
+
+// Mul8p is the unchecked panicing version of Mul8
+func Mul8p(a, b int8) int8 {
+ r, ok := Mul8(a, b)
+ if !ok {
+ panic("multiplication overflow")
+ }
+ return r
+}
+
+// Div8 performs / operation on two int8 operands
+// returning a result and status
+func Div8(a, b int8) (int8, bool) {
+ q, _, ok := Quotient8(a, b)
+ return q, ok
+}
+
+// Div8p is the unchecked panicing version of Div8
+func Div8p(a, b int8) int8 {
+ r, ok := Div8(a, b)
+ if !ok {
+ panic("division failure")
+ }
+ return r
+}
+
+// Quotient8 performs + operation on two int8 operands
+// returning a quotient, a remainder and status
+func Quotient8(a, b int8) (int8, int8, bool) {
+ if b == 0 {
+ return 0, 0, false
+ }
+ c := a / b
+ status := (c < 0) == ((a < 0) != (b < 0))
+ return c, a % b, status
+}
+
+// Add16 performs + operation on two int16 operands
+// returning a result and status
+func Add16(a, b int16) (int16, bool) {
+ c := a + b
+ if (c > a) == (b > 0) {
+ return c, true
+ }
+ return c, false
+}
+
+// Add16p is the unchecked panicing version of Add16
+func Add16p(a, b int16) int16 {
+ r, ok := Add16(a, b)
+ if !ok {
+ panic("addition overflow")
+ }
+ return r
+}
+
+// Sub16 performs - operation on two int16 operands
+// returning a result and status
+func Sub16(a, b int16) (int16, bool) {
+ c := a - b
+ if (c < a) == (b > 0) {
+ return c, true
+ }
+ return c, false
+}
+
+// Sub16p is the unchecked panicing version of Sub16
+func Sub16p(a, b int16) int16 {
+ r, ok := Sub16(a, b)
+ if !ok {
+ panic("subtraction overflow")
+ }
+ return r
+}
+
+// Mul16 performs * operation on two int16 operands
+// returning a result and status
+func Mul16(a, b int16) (int16, bool) {
+ if a == 0 || b == 0 {
+ return 0, true
+ }
+ c := a * b
+ if (c < 0) == ((a < 0) != (b < 0)) {
+ if c/b == a {
+ return c, true
+ }
+ }
+ return c, false
+}
+
+// Mul16p is the unchecked panicing version of Mul16
+func Mul16p(a, b int16) int16 {
+ r, ok := Mul16(a, b)
+ if !ok {
+ panic("multiplication overflow")
+ }
+ return r
+}
+
+// Div16 performs / operation on two int16 operands
+// returning a result and status
+func Div16(a, b int16) (int16, bool) {
+ q, _, ok := Quotient16(a, b)
+ return q, ok
+}
+
+// Div16p is the unchecked panicing version of Div16
+func Div16p(a, b int16) int16 {
+ r, ok := Div16(a, b)
+ if !ok {
+ panic("division failure")
+ }
+ return r
+}
+
+// Quotient16 performs + operation on two int16 operands
+// returning a quotient, a remainder and status
+func Quotient16(a, b int16) (int16, int16, bool) {
+ if b == 0 {
+ return 0, 0, false
+ }
+ c := a / b
+ status := (c < 0) == ((a < 0) != (b < 0))
+ return c, a % b, status
+}
+
+// Add32 performs + operation on two int32 operands
+// returning a result and status
+func Add32(a, b int32) (int32, bool) {
+ c := a + b
+ if (c > a) == (b > 0) {
+ return c, true
+ }
+ return c, false
+}
+
+// Add32p is the unchecked panicing version of Add32
+func Add32p(a, b int32) int32 {
+ r, ok := Add32(a, b)
+ if !ok {
+ panic("addition overflow")
+ }
+ return r
+}
+
+// Sub32 performs - operation on two int32 operands
+// returning a result and status
+func Sub32(a, b int32) (int32, bool) {
+ c := a - b
+ if (c < a) == (b > 0) {
+ return c, true
+ }
+ return c, false
+}
+
+// Sub32p is the unchecked panicing version of Sub32
+func Sub32p(a, b int32) int32 {
+ r, ok := Sub32(a, b)
+ if !ok {
+ panic("subtraction overflow")
+ }
+ return r
+}
+
+// Mul32 performs * operation on two int32 operands
+// returning a result and status
+func Mul32(a, b int32) (int32, bool) {
+ if a == 0 || b == 0 {
+ return 0, true
+ }
+ c := a * b
+ if (c < 0) == ((a < 0) != (b < 0)) {
+ if c/b == a {
+ return c, true
+ }
+ }
+ return c, false
+}
+
+// Mul32p is the unchecked panicing version of Mul32
+func Mul32p(a, b int32) int32 {
+ r, ok := Mul32(a, b)
+ if !ok {
+ panic("multiplication overflow")
+ }
+ return r
+}
+
+// Div32 performs / operation on two int32 operands
+// returning a result and status
+func Div32(a, b int32) (int32, bool) {
+ q, _, ok := Quotient32(a, b)
+ return q, ok
+}
+
+// Div32p is the unchecked panicing version of Div32
+func Div32p(a, b int32) int32 {
+ r, ok := Div32(a, b)
+ if !ok {
+ panic("division failure")
+ }
+ return r
+}
+
+// Quotient32 performs + operation on two int32 operands
+// returning a quotient, a remainder and status
+func Quotient32(a, b int32) (int32, int32, bool) {
+ if b == 0 {
+ return 0, 0, false
+ }
+ c := a / b
+ status := (c < 0) == ((a < 0) != (b < 0))
+ return c, a % b, status
+}
+
+// Add64 performs + operation on two int64 operands
+// returning a result and status
+func Add64(a, b int64) (int64, bool) {
+ c := a + b
+ if (c > a) == (b > 0) {
+ return c, true
+ }
+ return c, false
+}
+
+// Add64p is the unchecked panicing version of Add64
+func Add64p(a, b int64) int64 {
+ r, ok := Add64(a, b)
+ if !ok {
+ panic("addition overflow")
+ }
+ return r
+}
+
+// Sub64 performs - operation on two int64 operands
+// returning a result and status
+func Sub64(a, b int64) (int64, bool) {
+ c := a - b
+ if (c < a) == (b > 0) {
+ return c, true
+ }
+ return c, false
+}
+
+// Sub64p is the unchecked panicing version of Sub64
+func Sub64p(a, b int64) int64 {
+ r, ok := Sub64(a, b)
+ if !ok {
+ panic("subtraction overflow")
+ }
+ return r
+}
+
+// Mul64 performs * operation on two int64 operands
+// returning a result and status
+func Mul64(a, b int64) (int64, bool) {
+ if a == 0 || b == 0 {
+ return 0, true
+ }
+ c := a * b
+ if (c < 0) == ((a < 0) != (b < 0)) {
+ if c/b == a {
+ return c, true
+ }
+ }
+ return c, false
+}
+
+// Mul64p is the unchecked panicing version of Mul64
+func Mul64p(a, b int64) int64 {
+ r, ok := Mul64(a, b)
+ if !ok {
+ panic("multiplication overflow")
+ }
+ return r
+}
+
+// Div64 performs / operation on two int64 operands
+// returning a result and status
+func Div64(a, b int64) (int64, bool) {
+ q, _, ok := Quotient64(a, b)
+ return q, ok
+}
+
+// Div64p is the unchecked panicing version of Div64
+func Div64p(a, b int64) int64 {
+ r, ok := Div64(a, b)
+ if !ok {
+ panic("division failure")
+ }
+ return r
+}
+
+// Quotient64 performs + operation on two int64 operands
+// returning a quotient, a remainder and status
+func Quotient64(a, b int64) (int64, int64, bool) {
+ if b == 0 {
+ return 0, 0, false
+ }
+ c := a / b
+ status := (c < 0) == ((a < 0) != (b < 0))
+ return c, a % b, status
+}
diff --git a/tm2/pkg/overflow/overflow_template.sh b/tm2/pkg/overflow/overflow_template.sh
new file mode 100755
index 00000000000..a2a85f2c581
--- /dev/null
+++ b/tm2/pkg/overflow/overflow_template.sh
@@ -0,0 +1,112 @@
+#!/bin/sh
+
+exec > overflow_impl.go
+
+echo "package overflow
+
+// This is generated code, created by overflow_template.sh executed
+// by \"go generate\"
+
+"
+
+
+for SIZE in 8 16 32 64
+do
+echo "
+
+// Add${SIZE} performs + operation on two int${SIZE} operands
+// returning a result and status
+func Add${SIZE}(a, b int${SIZE}) (int${SIZE}, bool) {
+ c := a + b
+ if (c > a) == (b > 0) {
+ return c, true
+ }
+ return c, false
+}
+
+// Add${SIZE}p is the unchecked panicing version of Add${SIZE}
+func Add${SIZE}p(a, b int${SIZE}) int${SIZE} {
+ r, ok := Add${SIZE}(a, b)
+ if !ok {
+ panic(\"addition overflow\")
+ }
+ return r
+}
+
+
+// Sub${SIZE} performs - operation on two int${SIZE} operands
+// returning a result and status
+func Sub${SIZE}(a, b int${SIZE}) (int${SIZE}, bool) {
+ c := a - b
+ if (c < a) == (b > 0) {
+ return c, true
+ }
+ return c, false
+}
+
+// Sub${SIZE}p is the unchecked panicing version of Sub${SIZE}
+func Sub${SIZE}p(a, b int${SIZE}) int${SIZE} {
+ r, ok := Sub${SIZE}(a, b)
+ if !ok {
+ panic(\"subtraction overflow\")
+ }
+ return r
+}
+
+
+// Mul${SIZE} performs * operation on two int${SIZE} operands
+// returning a result and status
+func Mul${SIZE}(a, b int${SIZE}) (int${SIZE}, bool) {
+ if a == 0 || b == 0 {
+ return 0, true
+ }
+ c := a * b
+ if (c < 0) == ((a < 0) != (b < 0)) {
+ if c/b == a {
+ return c, true
+ }
+ }
+ return c, false
+}
+
+// Mul${SIZE}p is the unchecked panicing version of Mul${SIZE}
+func Mul${SIZE}p(a, b int${SIZE}) int${SIZE} {
+ r, ok := Mul${SIZE}(a, b)
+ if !ok {
+ panic(\"multiplication overflow\")
+ }
+ return r
+}
+
+
+
+// Div${SIZE} performs / operation on two int${SIZE} operands
+// returning a result and status
+func Div${SIZE}(a, b int${SIZE}) (int${SIZE}, bool) {
+ q, _, ok := Quotient${SIZE}(a, b)
+ return q, ok
+}
+
+// Div${SIZE}p is the unchecked panicing version of Div${SIZE}
+func Div${SIZE}p(a, b int${SIZE}) int${SIZE} {
+ r, ok := Div${SIZE}(a, b)
+ if !ok {
+ panic(\"division failure\")
+ }
+ return r
+}
+
+// Quotient${SIZE} performs + operation on two int${SIZE} operands
+// returning a quotient, a remainder and status
+func Quotient${SIZE}(a, b int${SIZE}) (int${SIZE}, int${SIZE}, bool) {
+ if b == 0 {
+ return 0, 0, false
+ }
+ c := a / b
+ status := (c < 0) == ((a < 0) != (b < 0))
+ return c, a % b, status
+}
+"
+done
+
+go run -modfile ../../../misc/devdeps/go.mod mvdan.cc/gofumpt -w overflow_impl.go
diff --git a/tm2/pkg/overflow/overflow_test.go b/tm2/pkg/overflow/overflow_test.go
new file mode 100644
index 00000000000..2b2d345b55d
--- /dev/null
+++ b/tm2/pkg/overflow/overflow_test.go
@@ -0,0 +1,115 @@
+package overflow
+
+import (
+ "fmt"
+ "math"
+ "testing"
+)
+
+// sample all possibilities of 8 bit numbers
+// by checking against 64 bit numbers
+
+func TestAlgorithms(t *testing.T) {
+ errors := 0
+
+ for a64 := int64(math.MinInt8); a64 <= int64(math.MaxInt8); a64++ {
+ for b64 := int64(math.MinInt8); b64 <= int64(math.MaxInt8) && errors < 10; b64++ {
+ a8 := int8(a64)
+ b8 := int8(b64)
+
+ if int64(a8) != a64 || int64(b8) != b64 {
+ t.Fatal("LOGIC FAILURE IN TEST")
+ }
+
+ // ADDITION
+ {
+ r64 := a64 + b64
+
+ // now the verification
+ result, ok := Add8(a8, b8)
+ if ok && int64(result) != r64 {
+ t.Errorf("failed to fail on %v + %v = %v instead of %v\n",
+ a8, b8, result, r64)
+ errors++
+ }
+ if !ok && int64(result) == r64 {
+ t.Fail()
+ errors++
+ }
+ }
+
+ // SUBTRACTION
+ {
+ r64 := a64 - b64
+
+ // now the verification
+ result, ok := Sub8(a8, b8)
+ if ok && int64(result) != r64 {
+ t.Errorf("failed to fail on %v - %v = %v instead of %v\n",
+ a8, b8, result, r64)
+ }
+ if !ok && int64(result) == r64 {
+ t.Fail()
+ errors++
+ }
+ }
+
+ // MULTIPLICATION
+ {
+ r64 := a64 * b64
+
+ // now the verification
+ result, ok := Mul8(a8, b8)
+ if ok && int64(result) != r64 {
+ t.Errorf("failed to fail on %v * %v = %v instead of %v\n",
+ a8, b8, result, r64)
+ errors++
+ }
+ if !ok && int64(result) == r64 {
+ t.Fail()
+ errors++
+ }
+ }
+
+ // DIVISION
+ if b8 != 0 {
+ r64 := a64 / b64
+
+ // now the verification
+ result, _, ok := Quotient8(a8, b8)
+ if ok && int64(result) != r64 {
+ t.Errorf("failed to fail on %v / %v = %v instead of %v\n",
+ a8, b8, result, r64)
+ errors++
+ }
+ if !ok && result != 0 && int64(result) == r64 {
+ t.Fail()
+ errors++
+ }
+ }
+ }
+ }
+}
+
+func TestQuotient(t *testing.T) {
+ q, r, ok := Quotient(100, 3)
+ if r != 1 || q != 33 || !ok {
+ t.Errorf("expected 100/3 => 33, r=1")
+ }
+ if _, _, ok = Quotient(1, 0); ok {
+ t.Error("unexpected lack of failure")
+ }
+}
+
+//func TestAdditionInt(t *testing.T) {
+// fmt.Printf("\nminint8 = %v\n", math.MinInt8)
+// fmt.Printf("maxint8 = %v\n\n", math.MaxInt8)
+// fmt.Printf("maxint32 = %v\n", math.MaxInt32)
+// fmt.Printf("minint32 = %v\n\n", math.MinInt32)
+// fmt.Printf("maxint64 = %v\n", math.MaxInt64)
+// fmt.Printf("minint64 = %v\n\n", math.MinInt64)
+//}
+
+func Test64(t *testing.T) {
+ fmt.Println("64bit:", _is64Bit())
+}
diff --git a/tm2/pkg/p2p/netaddress.go b/tm2/pkg/p2p/netaddress.go
index 1ce34afff34..77f89b2a4b3 100644
--- a/tm2/pkg/p2p/netaddress.go
+++ b/tm2/pkg/p2p/netaddress.go
@@ -134,7 +134,7 @@ func NewNetAddressFromStrings(idaddrs []string) ([]*NetAddress, []error) {
return netAddrs, errs
}
-// NewNetAddressIPPort returns a new NetAddress using the provided IP
+// NewNetAddressFromIPPort returns a new NetAddress using the provided IP
// and port number.
func NewNetAddressFromIPPort(id ID, ip net.IP, port uint16) *NetAddress {
return &NetAddress{
diff --git a/tm2/pkg/p2p/switch.go b/tm2/pkg/p2p/switch.go
index b2de68e1ae3..317f34e496b 100644
--- a/tm2/pkg/p2p/switch.go
+++ b/tm2/pkg/p2p/switch.go
@@ -203,7 +203,7 @@ func (sw *Switch) OnStart() error {
for _, reactor := range sw.reactors {
err := reactor.Start()
if err != nil {
- return errors.Wrap(err, "failed to start %v", reactor)
+ return errors.Wrapf(err, "failed to start %v", reactor)
}
}
diff --git a/tm2/pkg/sdk/auth/abci.go b/tm2/pkg/sdk/auth/abci.go
new file mode 100644
index 00000000000..86cbf962fad
--- /dev/null
+++ b/tm2/pkg/sdk/auth/abci.go
@@ -0,0 +1,19 @@
+package auth
+
+import (
+ "github.com/gnolang/gno/tm2/pkg/sdk"
+ "github.com/gnolang/gno/tm2/pkg/std"
+)
+
+// EndBlocker is called in the EndBlock(), it calcuates the minimum gas price
+// for the next gas price
+func EndBlocker(ctx sdk.Context, gk GasPriceKeeperI) {
+ gk.UpdateGasPrice(ctx)
+}
+
+// InitChainer is called in the InitChain(), it set the initial gas price in the
+// GasPriceKeeper store
+// for the next gas price
+func InitChainer(ctx sdk.Context, gk GasPriceKeeper, gp std.GasPrice) {
+ gk.SetGasPrice(ctx, gp)
+}
diff --git a/tm2/pkg/sdk/auth/ante.go b/tm2/pkg/sdk/auth/ante.go
index 49662b47a55..4495a1729ad 100644
--- a/tm2/pkg/sdk/auth/ante.go
+++ b/tm2/pkg/sdk/auth/ante.go
@@ -15,11 +15,8 @@ import (
"github.com/gnolang/gno/tm2/pkg/store"
)
-var (
- // simulation signature values used to estimate gas consumption
- simSecp256k1Pubkey secp256k1.PubKeySecp256k1
- simSecp256k1Sig [64]byte
-)
+// simulation signature values used to estimate gas consumption
+var simSecp256k1Pubkey secp256k1.PubKeySecp256k1
func init() {
// This decodes a valid hex string into a sepc256k1Pubkey for use in transaction simulation
@@ -228,14 +225,6 @@ func processSig(
return nil, abciResult(std.ErrInternal("setting PubKey on signer's account"))
}
- if simulate {
- // Simulated txs should not contain a signature and are not required to
- // contain a pubkey, so we must account for tx size of including a
- // std.Signature (Amino encoding) and simulate gas consumption
- // (assuming a SECP256k1 simulation key).
- consumeSimSigGas(ctx.GasMeter(), pubKey, sig, params)
- }
-
if res := sigGasConsumer(ctx.GasMeter(), sig.Signature, pubKey, params); !res.IsOK() {
return nil, res
}
@@ -251,42 +240,12 @@ func processSig(
return acc, res
}
-func consumeSimSigGas(gasmeter store.GasMeter, pubkey crypto.PubKey, sig std.Signature, params Params) {
- simSig := std.Signature{PubKey: pubkey}
- if len(sig.Signature) == 0 {
- simSig.Signature = simSecp256k1Sig[:]
- }
-
- sigBz := amino.MustMarshalSized(simSig)
- cost := store.Gas(len(sigBz) + 6)
-
- // If the pubkey is a multi-signature pubkey, then we estimate for the maximum
- // number of signers.
- if _, ok := pubkey.(multisig.PubKeyMultisigThreshold); ok {
- cost *= params.TxSigLimit
- }
-
- gasmeter.ConsumeGas(params.TxSizeCostPerByte*cost, "txSize")
-}
-
// ProcessPubKey verifies that the given account address matches that of the
// std.Signature. In addition, it will set the public key of the account if it
// has not been set.
func ProcessPubKey(acc std.Account, sig std.Signature, simulate bool) (crypto.PubKey, sdk.Result) {
// If pubkey is not known for account, set it from the std.Signature.
pubKey := acc.GetPubKey()
- if simulate {
- // In simulate mode the transaction comes with no signatures, thus if the
- // account's pubkey is nil, both signature verification and gasKVStore.Set()
- // shall consume the largest amount, i.e. it takes more gas to verify
- // secp256k1 keys than ed25519 ones.
- if pubKey == nil {
- return simSecp256k1Pubkey, sdk.Result{}
- }
-
- return pubKey, sdk.Result{}
- }
-
if pubKey == nil {
pubKey = sig.PubKey
if pubKey == nil {
@@ -379,6 +338,31 @@ func DeductFees(bank BankKeeperI, ctx sdk.Context, acc std.Account, fees std.Coi
// consensus.
func EnsureSufficientMempoolFees(ctx sdk.Context, fee std.Fee) sdk.Result {
minGasPrices := ctx.MinGasPrices()
+ blockGasPrice := ctx.Value(GasPriceContextKey{}).(std.GasPrice)
+ feeGasPrice := std.GasPrice{
+ Gas: fee.GasWanted,
+ Price: std.Coin{
+ Amount: fee.GasFee.Amount,
+ Denom: fee.GasFee.Denom,
+ },
+ }
+ // check the block gas price
+ if blockGasPrice.Price.IsValid() && !blockGasPrice.Price.IsZero() {
+ ok, err := feeGasPrice.IsGTE(blockGasPrice)
+ if err != nil {
+ return abciResult(std.ErrInsufficientFee(
+ err.Error(),
+ ))
+ }
+ if !ok {
+ return abciResult(std.ErrInsufficientFee(
+ fmt.Sprintf(
+ "insufficient fees; got: {Gas-Wanted: %d, Gas-Fee %s}, fee required: %+v as block gas price", feeGasPrice.Gas, feeGasPrice.Price, blockGasPrice,
+ ),
+ ))
+ }
+ }
+ // check min gas price set by the node.
if len(minGasPrices) == 0 {
// no minimum gas price (not recommended)
// TODO: allow for selective filtering of 0 fee txs.
@@ -405,7 +389,7 @@ func EnsureSufficientMempoolFees(ctx sdk.Context, fee std.Fee) sdk.Result {
} else {
return abciResult(std.ErrInsufficientFee(
fmt.Sprintf(
- "insufficient fees; got: %q required: %q", fee.GasFee, gp,
+ "insufficient fees; got: {Gas-Wanted: %d, Gas-Fee %s}, fee required: %+v as minimum gas price set by the node", feeGasPrice.Gas, feeGasPrice.Price, gp,
),
))
}
@@ -415,7 +399,7 @@ func EnsureSufficientMempoolFees(ctx sdk.Context, fee std.Fee) sdk.Result {
return abciResult(std.ErrInsufficientFee(
fmt.Sprintf(
- "insufficient fees; got: %q required (one of): %q", fee.GasFee, minGasPrices,
+ "insufficient fees; got: {Gas-Wanted: %d, Gas-Fee %s}, required (one of): %q", feeGasPrice.Gas, feeGasPrice.Price, minGasPrices,
),
))
}
diff --git a/tm2/pkg/sdk/auth/ante_test.go b/tm2/pkg/sdk/auth/ante_test.go
index be4167a6238..78018b415eb 100644
--- a/tm2/pkg/sdk/auth/ante_test.go
+++ b/tm2/pkg/sdk/auth/ante_test.go
@@ -7,6 +7,7 @@ import (
"strings"
"testing"
+ "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/gnolang/gno/tm2/pkg/amino"
@@ -611,10 +612,11 @@ func TestProcessPubKey(t *testing.T) {
wantErr bool
}{
{"no sigs, simulate off", args{acc1, std.Signature{}, false}, true},
- {"no sigs, simulate on", args{acc1, std.Signature{}, true}, false},
+ {"no sigs, simulate on", args{acc1, std.Signature{}, true}, true},
+ {"no sigs, account with pub, simulate off", args{acc2, std.Signature{}, false}, false},
{"no sigs, account with pub, simulate on", args{acc2, std.Signature{}, true}, false},
{"pubkey doesn't match addr, simulate off", args{acc1, std.Signature{PubKey: priv2.PubKey()}, false}, true},
- {"pubkey doesn't match addr, simulate on", args{acc1, std.Signature{PubKey: priv2.PubKey()}, true}, false},
+ {"pubkey doesn't match addr, simulate on", args{acc1, std.Signature{PubKey: priv2.PubKey()}, true}, true},
}
for _, tt := range tests {
tt := tt
@@ -809,6 +811,8 @@ func TestEnsureSufficientMempoolFees(t *testing.T) {
{std.NewFee(200000, std.NewCoin("stake", 2)), true},
{std.NewFee(200000, std.NewCoin("atom", 5)), false},
}
+ // Do not set the block gas price
+ ctx = ctx.WithValue(GasPriceContextKey{}, std.GasPrice{})
for i, tc := range testCases {
res := EnsureSufficientMempoolFees(ctx, tc.input)
@@ -866,3 +870,80 @@ func TestCustomSignatureVerificationGasConsumer(t *testing.T) {
tx = tu.NewTestTx(t, ctx.ChainID(), msgs, privs, accnums, seqs, fee)
checkValidTx(t, anteHandler, ctx, tx, false)
}
+
+func TestEnsureBlockGasPrice(t *testing.T) {
+ p1, err := std.ParseGasPrice("3ugnot/10gas") // 0.3ugnot
+ require.NoError(t, err)
+
+ p2, err := std.ParseGasPrice("400ugnot/2000gas") // 0.2ugnot
+ require.NoError(t, err)
+
+ userFeeCases := []struct {
+ minGasPrice std.GasPrice
+ blockGasPrice std.GasPrice
+ input std.Fee
+ expectedOK bool
+ }{
+ // user's gas wanted and gas fee: 0.1ugnot to 0.5ugnot
+ // validator's minGasPrice: 0.3 ugnot
+ // block gas price: 0.2ugnot
+
+ {p1, p2, std.NewFee(100, std.NewCoin("ugnot", 10)), false},
+ {p1, p2, std.NewFee(100, std.NewCoin("ugnot", 20)), false},
+ {p1, p2, std.NewFee(100, std.NewCoin("ugnot", 30)), true},
+ {p1, p2, std.NewFee(100, std.NewCoin("ugnot", 40)), true},
+ {p1, p2, std.NewFee(100, std.NewCoin("ugnot", 50)), true},
+
+ // validator's minGasPrice: 0.2 ugnot
+ // block gas price2: 0.3ugnot
+ {p2, p1, std.NewFee(100, std.NewCoin("ugnot", 10)), false},
+ {p2, p1, std.NewFee(100, std.NewCoin("ugnot", 20)), false},
+ {p2, p1, std.NewFee(100, std.NewCoin("ugnot", 30)), true},
+ {p2, p1, std.NewFee(100, std.NewCoin("ugnot", 40)), true},
+ {p2, p1, std.NewFee(100, std.NewCoin("ugnot", 50)), true},
+ }
+
+ // setup
+ env := setupTestEnv()
+ ctx := env.ctx
+ // validator min gas price // 0.3 ugnot per gas
+ for i, c := range userFeeCases {
+ ctx = ctx.WithMinGasPrices(
+ []std.GasPrice{c.minGasPrice},
+ )
+ ctx = ctx.WithValue(GasPriceContextKey{}, c.blockGasPrice)
+
+ res := EnsureSufficientMempoolFees(ctx, c.input)
+ require.Equal(
+ t, c.expectedOK, res.IsOK(),
+ "unexpected result; case #%d, input: %v, log: %v", i, c.input, res.Log,
+ )
+ }
+}
+
+func TestInvalidUserFee(t *testing.T) {
+ minGasPrice, err := std.ParseGasPrice("3ugnot/10gas") // 0.3ugnot
+ require.NoError(t, err)
+
+ blockGasPrice, err := std.ParseGasPrice("400ugnot/2000gas") // 0.2ugnot
+ require.NoError(t, err)
+
+ userFee1 := std.NewFee(0, std.NewCoin("ugnot", 50))
+ userFee2 := std.NewFee(100, std.NewCoin("uatom", 50))
+
+ // setup
+ env := setupTestEnv()
+ ctx := env.ctx
+
+ ctx = ctx.WithMinGasPrices(
+ []std.GasPrice{minGasPrice},
+ )
+ ctx = ctx.WithValue(GasPriceContextKey{}, blockGasPrice)
+ res1 := EnsureSufficientMempoolFees(ctx, userFee1)
+ require.False(t, res1.IsOK())
+ assert.Contains(t, res1.Log, "GasPrice.Gas cannot be zero;")
+
+ res2 := EnsureSufficientMempoolFees(ctx, userFee2)
+ require.False(t, res2.IsOK())
+ assert.Contains(t, res2.Log, "Gas price denominations should be equal;")
+}
diff --git a/tm2/pkg/sdk/auth/consts.go b/tm2/pkg/sdk/auth/consts.go
index 09bbb15cdbc..462ca0cd64d 100644
--- a/tm2/pkg/sdk/auth/consts.go
+++ b/tm2/pkg/sdk/auth/consts.go
@@ -19,7 +19,8 @@ const (
// AddressStoreKeyPrefix prefix for account-by-address store
AddressStoreKeyPrefix = "/a/"
-
+ // key for gas price
+ GasPriceKey = "gasPrice"
// param key for global account number
GlobalAccountNumberKey = "globalAccountNumber"
)
diff --git a/tm2/pkg/sdk/auth/genesis.go b/tm2/pkg/sdk/auth/genesis.go
new file mode 100644
index 00000000000..c863c237a41
--- /dev/null
+++ b/tm2/pkg/sdk/auth/genesis.go
@@ -0,0 +1,31 @@
+package auth
+
+import (
+ "github.com/gnolang/gno/tm2/pkg/amino"
+ "github.com/gnolang/gno/tm2/pkg/sdk"
+)
+
+// InitGenesis - Init store state from genesis data
+func (ak AccountKeeper) InitGenesis(ctx sdk.Context, data GenesisState) {
+ if amino.DeepEqual(data, GenesisState{}) {
+ if err := ak.SetParams(ctx, DefaultParams()); err != nil {
+ panic(err)
+ }
+ return
+ }
+
+ if err := ValidateGenesis(data); err != nil {
+ panic(err)
+ }
+
+ if err := ak.SetParams(ctx, data.Params); err != nil {
+ panic(err)
+ }
+}
+
+// ExportGenesis returns a GenesisState for a given context and keeper
+func (ak AccountKeeper) ExportGenesis(ctx sdk.Context) GenesisState {
+ params := ak.GetParams(ctx)
+
+ return NewGenesisState(params)
+}
diff --git a/tm2/pkg/sdk/auth/keeper.go b/tm2/pkg/sdk/auth/keeper.go
index 7669b8ace73..fc83997fdc4 100644
--- a/tm2/pkg/sdk/auth/keeper.go
+++ b/tm2/pkg/sdk/auth/keeper.go
@@ -3,10 +3,12 @@ package auth
import (
"fmt"
"log/slog"
+ "math/big"
"github.com/gnolang/gno/tm2/pkg/amino"
"github.com/gnolang/gno/tm2/pkg/crypto"
"github.com/gnolang/gno/tm2/pkg/sdk"
+ "github.com/gnolang/gno/tm2/pkg/sdk/params"
"github.com/gnolang/gno/tm2/pkg/std"
"github.com/gnolang/gno/tm2/pkg/store"
)
@@ -15,7 +17,8 @@ import (
type AccountKeeper struct {
// The (unexposed) key used to access the store from the Context.
key store.StoreKey
-
+ // The keeper used to store auth parameters
+ paramk params.ParamsKeeper
// The prototypical Account constructor.
proto func() std.Account
}
@@ -23,11 +26,12 @@ type AccountKeeper struct {
// NewAccountKeeper returns a new AccountKeeper that uses go-amino to
// (binary) encode and decode concrete std.Accounts.
func NewAccountKeeper(
- key store.StoreKey, proto func() std.Account,
+ key store.StoreKey, pk params.ParamsKeeper, proto func() std.Account,
) AccountKeeper {
return AccountKeeper{
- key: key,
- proto: proto,
+ key: key,
+ paramk: pk,
+ proto: proto,
}
}
@@ -55,7 +59,7 @@ func (ak AccountKeeper) Logger(ctx sdk.Context) *slog.Logger {
// GetAccount returns a specific account in the AccountKeeper.
func (ak AccountKeeper) GetAccount(ctx sdk.Context, addr crypto.Address) std.Account {
- stor := ctx.Store(ak.key)
+ stor := ctx.GasStore(ak.key)
bz := stor.Get(AddressStoreKey(addr))
if bz == nil {
return nil
@@ -78,7 +82,7 @@ func (ak AccountKeeper) GetAllAccounts(ctx sdk.Context) []std.Account {
// SetAccount implements AccountKeeper.
func (ak AccountKeeper) SetAccount(ctx sdk.Context, acc std.Account) {
addr := acc.GetAddress()
- stor := ctx.Store(ak.key)
+ stor := ctx.GasStore(ak.key)
bz, err := amino.MarshalAny(acc)
if err != nil {
panic(err)
@@ -90,13 +94,13 @@ func (ak AccountKeeper) SetAccount(ctx sdk.Context, acc std.Account) {
// NOTE: this will cause supply invariant violation if called
func (ak AccountKeeper) RemoveAccount(ctx sdk.Context, acc std.Account) {
addr := acc.GetAddress()
- stor := ctx.Store(ak.key)
+ stor := ctx.GasStore(ak.key)
stor.Delete(AddressStoreKey(addr))
}
// IterateAccounts implements AccountKeeper.
func (ak AccountKeeper) IterateAccounts(ctx sdk.Context, process func(std.Account) (stop bool)) {
- stor := ctx.Store(ak.key)
+ stor := ctx.GasStore(ak.key)
iter := store.PrefixIterator(stor, []byte(AddressStoreKeyPrefix))
defer iter.Close()
for {
@@ -133,7 +137,7 @@ func (ak AccountKeeper) GetSequence(ctx sdk.Context, addr crypto.Address) (uint6
// GetNextAccountNumber Returns and increments the global account number counter
func (ak AccountKeeper) GetNextAccountNumber(ctx sdk.Context) uint64 {
var accNumber uint64
- stor := ctx.Store(ak.key)
+ stor := ctx.GasStore(ak.key)
bz := stor.Get([]byte(GlobalAccountNumberKey))
if bz == nil {
accNumber = 0 // start with 0.
@@ -152,7 +156,6 @@ func (ak AccountKeeper) GetNextAccountNumber(ctx sdk.Context) uint64 {
// -----------------------------------------------------------------------------
// Misc.
-
func (ak AccountKeeper) decodeAccount(bz []byte) (acc std.Account) {
err := amino.Unmarshal(bz, &acc)
if err != nil {
@@ -160,3 +163,147 @@ func (ak AccountKeeper) decodeAccount(bz []byte) (acc std.Account) {
}
return
}
+
+type GasPriceContextKey struct{}
+
+type GasPriceKeeper struct {
+ key store.StoreKey
+}
+
+// GasPriceKeeper
+// The GasPriceKeeper stores the history of gas prices and calculates
+// new gas price with formula parameters
+func NewGasPriceKeeper(key store.StoreKey) GasPriceKeeper {
+ return GasPriceKeeper{
+ key: key,
+ }
+}
+
+// SetGasPrice is called in InitChainer to store initial gas price set in the genesis
+func (gk GasPriceKeeper) SetGasPrice(ctx sdk.Context, gp std.GasPrice) {
+ if (gp == std.GasPrice{}) {
+ return
+ }
+ stor := ctx.Store(gk.key)
+ bz, err := amino.Marshal(gp)
+ if err != nil {
+ panic(err)
+ }
+ stor.Set([]byte(GasPriceKey), bz)
+}
+
+// We store the history. If the formula changes, we can replay blocks
+// and apply the formula to a specific block range. The new gas price is
+// calculated in EndBlock().
+func (gk GasPriceKeeper) UpdateGasPrice(ctx sdk.Context) {
+ params := ctx.Value(AuthParamsContextKey{}).(Params)
+ gasUsed := ctx.BlockGasMeter().GasConsumed()
+ maxBlockGas := ctx.ConsensusParams().Block.MaxGas
+ lgp := gk.LastGasPrice(ctx)
+ newGasPrice := gk.calcBlockGasPrice(lgp, gasUsed, maxBlockGas, params)
+ gk.SetGasPrice(ctx, newGasPrice)
+}
+
+// calcBlockGasPrice calculates the minGasPrice for the txs to be included in the next block.
+// newGasPrice = lastPrice + lastPrice*(gasUsed-TargetBlockGas)/TargetBlockGas/GasCompressor)
+//
+// The math formula is an abstraction of a simple solution for the underlying problem we're trying to solve.
+// 1. What do we do if the gas used is less than the target gas in a block?
+// 2. How do we bring the gas used back to the target level, if gas used is more than the target?
+// We simplify the solution with a one-line formula to explain the idea. However, in reality, we need to treat
+// two scenarios differently. For example, in the first case, we need to increase the gas by at least 1 unit,
+// instead of round down for the integer divisions, and in the second case, we should set a floor
+// as the target gas price. This is just a starting point. Down the line, the solution might not be even
+// representable by one simple formula
+func (gk GasPriceKeeper) calcBlockGasPrice(lastGasPrice std.GasPrice, gasUsed int64, maxGas int64, params Params) std.GasPrice {
+ // If no block gas price is set, there is no need to change the last gas price.
+ if lastGasPrice.Price.Amount == 0 {
+ return lastGasPrice
+ }
+
+ // This is also a configuration to indicate that there is no need to change the last gas price.
+ if params.TargetGasRatio == 0 {
+ return lastGasPrice
+ }
+ // if no gas used, no need to change the lastPrice
+ if gasUsed == 0 {
+ return lastGasPrice
+ }
+ var (
+ num = new(big.Int)
+ denom = new(big.Int)
+ )
+
+ // targetGas = maxGax*TargetGasRatio/100
+
+ num.Mul(big.NewInt(maxGas), big.NewInt(params.TargetGasRatio))
+ num.Div(num, big.NewInt(int64(100)))
+ targetGasInt := new(big.Int).Set(num)
+
+ // if used gas is right on target, no need to change
+ gasUsedInt := big.NewInt(gasUsed)
+ if targetGasInt.Cmp(gasUsedInt) == 0 {
+ return lastGasPrice
+ }
+
+ c := params.GasPricesChangeCompressor
+ lastPriceInt := big.NewInt(lastGasPrice.Price.Amount)
+
+ bigOne := big.NewInt(1)
+ if gasUsedInt.Cmp(targetGasInt) == 1 { // gas used is more than the target
+ // increase gas price
+ num = num.Sub(gasUsedInt, targetGasInt)
+ num.Mul(num, lastPriceInt)
+ num.Div(num, targetGasInt)
+ num.Div(num, denom.SetInt64(c))
+ // increase at least 1
+ diff := maxBig(num, bigOne)
+ num.Add(lastPriceInt, diff)
+ // XXX should we cap it with a max gas price?
+ } else { // gas used is less than the target
+ // decrease gas price down to initial gas price
+ initPriceInt := big.NewInt(params.InitialGasPrice.Price.Amount)
+ if lastPriceInt.Cmp(initPriceInt) == -1 {
+ return params.InitialGasPrice
+ }
+ num.Sub(targetGasInt, gasUsedInt)
+ num.Mul(num, lastPriceInt)
+ num.Div(num, targetGasInt)
+ num.Div(num, denom.SetInt64(c))
+
+ num.Sub(lastPriceInt, num)
+ // gas price should not be less than the initial gas price,
+ num = maxBig(num, initPriceInt)
+ }
+
+ if !num.IsInt64() {
+ panic("The min gas price is out of int64 range")
+ }
+
+ lastGasPrice.Price.Amount = num.Int64()
+ return lastGasPrice
+}
+
+// max returns the larger of x or y.
+func maxBig(x, y *big.Int) *big.Int {
+ if x.Cmp(y) < 0 {
+ return y
+ }
+ return x
+}
+
+// It returns the gas price for the last block.
+func (gk GasPriceKeeper) LastGasPrice(ctx sdk.Context) std.GasPrice {
+ stor := ctx.Store(gk.key)
+ bz := stor.Get([]byte(GasPriceKey))
+ if bz == nil {
+ return std.GasPrice{}
+ }
+
+ gp := std.GasPrice{}
+ err := amino.Unmarshal(bz, &gp)
+ if err != nil {
+ panic(err)
+ }
+ return gp
+}
diff --git a/tm2/pkg/sdk/auth/keeper_test.go b/tm2/pkg/sdk/auth/keeper_test.go
index d40d96cdb4b..4622fba1a87 100644
--- a/tm2/pkg/sdk/auth/keeper_test.go
+++ b/tm2/pkg/sdk/auth/keeper_test.go
@@ -1,11 +1,13 @@
package auth
import (
+ "math/big"
"testing"
"github.com/stretchr/testify/require"
"github.com/gnolang/gno/tm2/pkg/crypto"
+ "github.com/gnolang/gno/tm2/pkg/std"
)
func TestAccountMapperGetSet(t *testing.T) {
@@ -71,3 +73,106 @@ func TestAccountMapperRemoveAccount(t *testing.T) {
require.NotNil(t, acc2)
require.Equal(t, accSeq2, acc2.GetSequence())
}
+
+func TestAccountKeeperParams(t *testing.T) {
+ env := setupTestEnv()
+
+ dp := DefaultParams()
+ err := env.acck.SetParams(env.ctx, dp)
+ require.NoError(t, err)
+
+ dp2 := env.acck.GetParams(env.ctx)
+ require.True(t, dp.Equals(dp2))
+}
+
+func TestGasPrice(t *testing.T) {
+ env := setupTestEnv()
+ gp := std.GasPrice{
+ Gas: 100,
+ Price: std.Coin{
+ Denom: "token",
+ Amount: 10,
+ },
+ }
+ env.gk.SetGasPrice(env.ctx, gp)
+ gp2 := env.gk.LastGasPrice(env.ctx)
+ require.True(t, gp == gp2)
+}
+
+func TestMax(t *testing.T) {
+ tests := []struct {
+ name string
+ x, y *big.Int
+ expected *big.Int
+ }{
+ {
+ name: "X is less than Y",
+ x: big.NewInt(5),
+ y: big.NewInt(10),
+ expected: big.NewInt(10),
+ },
+ {
+ name: "X is greater than Y",
+ x: big.NewInt(15),
+ y: big.NewInt(10),
+ expected: big.NewInt(15),
+ },
+ {
+ name: "X is equal to Y",
+ x: big.NewInt(10),
+ y: big.NewInt(10),
+ expected: big.NewInt(10),
+ },
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ result := maxBig(tc.x, tc.y)
+ require.Equal(t, tc.expected, result)
+ })
+ }
+}
+
+func TestCalcBlockGasPrice(t *testing.T) {
+ gk := GasPriceKeeper{}
+
+ lastGasPrice := std.GasPrice{
+ Price: std.Coin{
+ Amount: 100,
+ Denom: "atom",
+ },
+ }
+ gasUsed := int64(5000)
+ maxGas := int64(10000)
+ params := Params{
+ TargetGasRatio: 50,
+ GasPricesChangeCompressor: 2,
+ }
+
+ // Test with normal parameters
+ newGasPrice := gk.calcBlockGasPrice(lastGasPrice, gasUsed, maxGas, params)
+ expectedAmount := big.NewInt(100)
+ num := big.NewInt(gasUsed - maxGas*params.TargetGasRatio/100)
+ num.Mul(num, expectedAmount)
+ num.Div(num, big.NewInt(maxGas*params.TargetGasRatio/100))
+ num.Div(num, big.NewInt(params.GasPricesChangeCompressor))
+ expectedAmount.Add(expectedAmount, num)
+ require.Equal(t, expectedAmount.Int64(), newGasPrice.Price.Amount)
+
+ // Test with lastGasPrice amount as 0
+ lastGasPrice.Price.Amount = 0
+ newGasPrice = gk.calcBlockGasPrice(lastGasPrice, gasUsed, maxGas, params)
+ require.Equal(t, int64(0), newGasPrice.Price.Amount)
+
+ // Test with TargetGasRatio as 0 (should not change the last price)
+ params.TargetGasRatio = 0
+ newGasPrice = gk.calcBlockGasPrice(lastGasPrice, gasUsed, maxGas, params)
+ require.Equal(t, int64(0), newGasPrice.Price.Amount)
+
+ // Test with gasUsed as 0 (should not change the last price)
+ params.TargetGasRatio = 50
+ lastGasPrice.Price.Amount = 100
+ gasUsed = 0
+ newGasPrice = gk.calcBlockGasPrice(lastGasPrice, gasUsed, maxGas, params)
+ require.Equal(t, int64(100), newGasPrice.Price.Amount)
+}
diff --git a/tm2/pkg/sdk/auth/params.go b/tm2/pkg/sdk/auth/params.go
index dfeaa73af71..3fe08ed444d 100644
--- a/tm2/pkg/sdk/auth/params.go
+++ b/tm2/pkg/sdk/auth/params.go
@@ -5,38 +5,47 @@ import (
"strings"
"github.com/gnolang/gno/tm2/pkg/amino"
+ "github.com/gnolang/gno/tm2/pkg/sdk"
+ "github.com/gnolang/gno/tm2/pkg/std"
)
type AuthParamsContextKey struct{}
// Default parameter values
const (
- DefaultMaxMemoBytes int64 = 65536
- DefaultTxSigLimit int64 = 7
- DefaultTxSizeCostPerByte int64 = 10
- DefaultSigVerifyCostED25519 int64 = 590
- DefaultSigVerifyCostSecp256k1 int64 = 1000
+ DefaultMaxMemoBytes int64 = 65536
+ DefaultTxSigLimit int64 = 7
+ DefaultTxSizeCostPerByte int64 = 10
+ DefaultSigVerifyCostED25519 int64 = 590
+ DefaultSigVerifyCostSecp256k1 int64 = 1000
+ DefaultGasPricesChangeCompressor int64 = 10
+ DefaultTargetGasRatio int64 = 70 // 70% of the MaxGas in a block
)
// Params defines the parameters for the auth module.
type Params struct {
- MaxMemoBytes int64 `json:"max_memo_bytes" yaml:"max_memo_bytes"`
- TxSigLimit int64 `json:"tx_sig_limit" yaml:"tx_sig_limit"`
- TxSizeCostPerByte int64 `json:"tx_size_cost_per_byte" yaml:"tx_size_cost_per_byte"`
- SigVerifyCostED25519 int64 `json:"sig_verify_cost_ed25519" yaml:"sig_verify_cost_ed25519"`
- SigVerifyCostSecp256k1 int64 `json:"sig_verify_cost_secp256k1" yaml:"sig_verify_cost_secp256k1"`
+ MaxMemoBytes int64 `json:"max_memo_bytes" yaml:"max_memo_bytes"`
+ TxSigLimit int64 `json:"tx_sig_limit" yaml:"tx_sig_limit"`
+ TxSizeCostPerByte int64 `json:"tx_size_cost_per_byte" yaml:"tx_size_cost_per_byte"`
+ SigVerifyCostED25519 int64 `json:"sig_verify_cost_ed25519" yaml:"sig_verify_cost_ed25519"`
+ SigVerifyCostSecp256k1 int64 `json:"sig_verify_cost_secp256k1" yaml:"sig_verify_cost_secp256k1"`
+ GasPricesChangeCompressor int64 `json:"gas_price_change_compressor" yaml:"gas_price_change_compressor"`
+ TargetGasRatio int64 `json:"target_gas_ratio" yaml:"target_gas_ratio"`
+ InitialGasPrice std.GasPrice `json:"initial_gasprice"`
}
// NewParams creates a new Params object
func NewParams(maxMemoBytes, txSigLimit, txSizeCostPerByte,
- sigVerifyCostED25519, sigVerifyCostSecp256k1 int64,
+ sigVerifyCostED25519, sigVerifyCostSecp256k1, gasPricesChangeCompressor, targetGasRatio int64,
) Params {
return Params{
- MaxMemoBytes: maxMemoBytes,
- TxSigLimit: txSigLimit,
- TxSizeCostPerByte: txSizeCostPerByte,
- SigVerifyCostED25519: sigVerifyCostED25519,
- SigVerifyCostSecp256k1: sigVerifyCostSecp256k1,
+ MaxMemoBytes: maxMemoBytes,
+ TxSigLimit: txSigLimit,
+ TxSizeCostPerByte: txSizeCostPerByte,
+ SigVerifyCostED25519: sigVerifyCostED25519,
+ SigVerifyCostSecp256k1: sigVerifyCostSecp256k1,
+ GasPricesChangeCompressor: gasPricesChangeCompressor,
+ TargetGasRatio: targetGasRatio,
}
}
@@ -48,11 +57,13 @@ func (p Params) Equals(p2 Params) bool {
// DefaultParams returns a default set of parameters.
func DefaultParams() Params {
return Params{
- MaxMemoBytes: DefaultMaxMemoBytes,
- TxSigLimit: DefaultTxSigLimit,
- TxSizeCostPerByte: DefaultTxSizeCostPerByte,
- SigVerifyCostED25519: DefaultSigVerifyCostED25519,
- SigVerifyCostSecp256k1: DefaultSigVerifyCostSecp256k1,
+ MaxMemoBytes: DefaultMaxMemoBytes,
+ TxSigLimit: DefaultTxSigLimit,
+ TxSizeCostPerByte: DefaultTxSizeCostPerByte,
+ SigVerifyCostED25519: DefaultSigVerifyCostED25519,
+ SigVerifyCostSecp256k1: DefaultSigVerifyCostSecp256k1,
+ GasPricesChangeCompressor: DefaultGasPricesChangeCompressor,
+ TargetGasRatio: DefaultTargetGasRatio,
}
}
@@ -65,5 +76,51 @@ func (p Params) String() string {
sb.WriteString(fmt.Sprintf("TxSizeCostPerByte: %d\n", p.TxSizeCostPerByte))
sb.WriteString(fmt.Sprintf("SigVerifyCostED25519: %d\n", p.SigVerifyCostED25519))
sb.WriteString(fmt.Sprintf("SigVerifyCostSecp256k1: %d\n", p.SigVerifyCostSecp256k1))
+ sb.WriteString(fmt.Sprintf("GasPricesChangeCompressor: %d\n", p.GasPricesChangeCompressor))
+ sb.WriteString(fmt.Sprintf("TargetGasRatio: %d\n", p.TargetGasRatio))
return sb.String()
}
+
+func (p Params) Validate() error {
+ if p.TxSigLimit == 0 {
+ return fmt.Errorf("invalid tx signature limit: %d", p.TxSigLimit)
+ }
+ if p.SigVerifyCostED25519 == 0 {
+ return fmt.Errorf("invalid ED25519 signature verification cost: %d", p.SigVerifyCostED25519)
+ }
+ if p.SigVerifyCostSecp256k1 == 0 {
+ return fmt.Errorf("invalid SECK256k1 signature verification cost: %d", p.SigVerifyCostSecp256k1)
+ }
+ if p.TxSizeCostPerByte == 0 {
+ return fmt.Errorf("invalid tx size cost per byte: %d", p.TxSizeCostPerByte)
+ }
+ if p.GasPricesChangeCompressor <= 0 {
+ return fmt.Errorf("invalid gas prices change compressor: %d, it should be larger or equal to 1", p.GasPricesChangeCompressor)
+ }
+ if p.TargetGasRatio < 0 || p.TargetGasRatio > 100 {
+ return fmt.Errorf("invalid target block gas ratio: %d, it should be between 0 and 100, 0 is unlimited", p.TargetGasRatio)
+ }
+ return nil
+}
+
+func (ak AccountKeeper) SetParams(ctx sdk.Context, params Params) error {
+ if err := params.Validate(); err != nil {
+ return err
+ }
+ err := ak.paramk.SetParams(ctx, ModuleName, params)
+ return err
+}
+
+func (ak AccountKeeper) GetParams(ctx sdk.Context) Params {
+ params := &Params{}
+
+ ok, err := ak.paramk.GetParams(ctx, ModuleName, params)
+
+ if !ok {
+ panic("params key " + ModuleName + " does not exist")
+ }
+ if err != nil {
+ panic(err.Error())
+ }
+ return *params
+}
diff --git a/tm2/pkg/sdk/auth/params_test.go b/tm2/pkg/sdk/auth/params_test.go
new file mode 100644
index 00000000000..4b5a6b15789
--- /dev/null
+++ b/tm2/pkg/sdk/auth/params_test.go
@@ -0,0 +1,107 @@
+package auth
+
+import (
+ "reflect"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestValidate(t *testing.T) {
+ tests := []struct {
+ name string
+ params Params
+ expectsError bool
+ }{
+ {
+ name: "Valid Params",
+ params: Params{
+ MaxMemoBytes: 256,
+ TxSigLimit: 10,
+ TxSizeCostPerByte: 1,
+ SigVerifyCostED25519: 100,
+ SigVerifyCostSecp256k1: 200,
+ GasPricesChangeCompressor: 1,
+ TargetGasRatio: 50,
+ },
+ expectsError: false,
+ },
+ {
+ name: "Invalid TxSigLimit",
+ params: Params{
+ TxSigLimit: 0,
+ },
+ expectsError: true,
+ },
+ {
+ name: "Invalid SigVerifyCostED25519",
+ params: Params{
+ SigVerifyCostED25519: 0,
+ },
+ expectsError: true,
+ },
+ {
+ name: "Invalid GasPricesChangeCompressor",
+ params: Params{
+ GasPricesChangeCompressor: 0,
+ },
+ expectsError: true,
+ },
+ {
+ name: "Invalid TargetGasRatio",
+ params: Params{
+ TargetGasRatio: 150,
+ },
+ expectsError: true,
+ },
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ err := tc.params.Validate()
+ if tc.expectsError {
+ require.Error(t, err)
+ } else {
+ require.NoError(t, err)
+ }
+ })
+ }
+}
+
+func TestNewParams(t *testing.T) {
+ // Define expected values for each parameter
+ maxMemoBytes := int64(256)
+ txSigLimit := int64(10)
+ txSizeCostPerByte := int64(5)
+ sigVerifyCostED25519 := int64(100)
+ sigVerifyCostSecp256k1 := int64(200)
+ gasPricesChangeCompressor := int64(50)
+ targetGasRatio := int64(75)
+
+ // Call NewParams with the values
+ params := NewParams(
+ maxMemoBytes,
+ txSigLimit,
+ txSizeCostPerByte,
+ sigVerifyCostED25519,
+ sigVerifyCostSecp256k1,
+ gasPricesChangeCompressor,
+ targetGasRatio,
+ )
+
+ // Create an expected Params struct with the same values
+ expectedParams := Params{
+ MaxMemoBytes: maxMemoBytes,
+ TxSigLimit: txSigLimit,
+ TxSizeCostPerByte: txSizeCostPerByte,
+ SigVerifyCostED25519: sigVerifyCostED25519,
+ SigVerifyCostSecp256k1: sigVerifyCostSecp256k1,
+ GasPricesChangeCompressor: gasPricesChangeCompressor,
+ TargetGasRatio: targetGasRatio,
+ }
+
+ // Check if the returned params struct matches the expected struct
+ if !reflect.DeepEqual(params, expectedParams) {
+ t.Errorf("NewParams() = %+v, want %+v", params, expectedParams)
+ }
+}
diff --git a/tm2/pkg/sdk/auth/test_common.go b/tm2/pkg/sdk/auth/test_common.go
index f833a0b0564..e0a6316bead 100644
--- a/tm2/pkg/sdk/auth/test_common.go
+++ b/tm2/pkg/sdk/auth/test_common.go
@@ -6,8 +6,8 @@ import (
"github.com/gnolang/gno/tm2/pkg/crypto"
"github.com/gnolang/gno/tm2/pkg/db/memdb"
"github.com/gnolang/gno/tm2/pkg/log"
-
"github.com/gnolang/gno/tm2/pkg/sdk"
+ "github.com/gnolang/gno/tm2/pkg/sdk/params"
"github.com/gnolang/gno/tm2/pkg/std"
"github.com/gnolang/gno/tm2/pkg/store"
"github.com/gnolang/gno/tm2/pkg/store/iavl"
@@ -17,6 +17,7 @@ type testEnv struct {
ctx sdk.Context
acck AccountKeeper
bank BankKeeperI
+ gk GasPriceKeeper
}
func setupTestEnv() testEnv {
@@ -28,8 +29,10 @@ func setupTestEnv() testEnv {
ms.MountStoreWithDB(authCapKey, iavl.StoreConstructor, db)
ms.LoadLatestVersion()
- acck := NewAccountKeeper(authCapKey, std.ProtoBaseAccount)
+ paramk := params.NewParamsKeeper(authCapKey, "")
+ acck := NewAccountKeeper(authCapKey, paramk, std.ProtoBaseAccount)
bank := NewDummyBankKeeper(acck)
+ gk := NewGasPriceKeeper(authCapKey)
ctx := sdk.NewContext(sdk.RunTxModeDeliver, ms, &bft.Header{Height: 1, ChainID: "test-chain-id"}, log.NewNoopLogger())
ctx = ctx.WithValue(AuthParamsContextKey{}, DefaultParams())
@@ -46,7 +49,7 @@ func setupTestEnv() testEnv {
},
})
- return testEnv{ctx: ctx, acck: acck, bank: bank}
+ return testEnv{ctx: ctx, acck: acck, bank: bank, gk: gk}
}
// DummyBankKeeper defines a supply keeper used only for testing to avoid
diff --git a/tm2/pkg/sdk/auth/types.go b/tm2/pkg/sdk/auth/types.go
index 8bbc5e39e3b..3fb2d10fbb5 100644
--- a/tm2/pkg/sdk/auth/types.go
+++ b/tm2/pkg/sdk/auth/types.go
@@ -13,6 +13,8 @@ type AccountKeeperI interface {
GetAllAccounts(ctx sdk.Context) []std.Account
SetAccount(ctx sdk.Context, acc std.Account)
IterateAccounts(ctx sdk.Context, process func(std.Account) bool)
+ InitGenesis(ctx sdk.Context, data GenesisState)
+ GetParams(ctx sdk.Context) Params
}
var _ AccountKeeperI = AccountKeeper{}
@@ -21,3 +23,32 @@ var _ AccountKeeperI = AccountKeeper{}
type BankKeeperI interface {
SendCoins(ctx sdk.Context, fromAddr crypto.Address, toAddr crypto.Address, amt std.Coins) error
}
+
+type GasPriceKeeperI interface {
+ LastGasPrice(ctx sdk.Context) std.GasPrice
+ SetGasPrice(ctx sdk.Context, gp std.GasPrice)
+ UpdateGasPrice(ctx sdk.Context)
+}
+
+var _ GasPriceKeeperI = GasPriceKeeper{}
+
+// GenesisState - all auth state that must be provided at genesis
+type GenesisState struct {
+ Params Params `json:"params"`
+}
+
+// NewGenesisState - Create a new genesis state
+func NewGenesisState(params Params) GenesisState {
+ return GenesisState{params}
+}
+
+// DefaultGenesisState - Return a default genesis state
+func DefaultGenesisState() GenesisState {
+ return NewGenesisState(DefaultParams())
+}
+
+// ValidateGenesis performs basic validation of auth genesis data returning an
+// error for any failed validation criteria.
+func ValidateGenesis(data GenesisState) error {
+ return data.Params.Validate()
+}
diff --git a/tm2/pkg/sdk/bank/common_test.go b/tm2/pkg/sdk/bank/common_test.go
index 95b93157165..c8210be7175 100644
--- a/tm2/pkg/sdk/bank/common_test.go
+++ b/tm2/pkg/sdk/bank/common_test.go
@@ -9,6 +9,7 @@ import (
"github.com/gnolang/gno/tm2/pkg/sdk"
"github.com/gnolang/gno/tm2/pkg/sdk/auth"
+ "github.com/gnolang/gno/tm2/pkg/sdk/params"
"github.com/gnolang/gno/tm2/pkg/std"
"github.com/gnolang/gno/tm2/pkg/store"
"github.com/gnolang/gno/tm2/pkg/store/iavl"
@@ -28,10 +29,10 @@ func setupTestEnv() testEnv {
ms := store.NewCommitMultiStore(db)
ms.MountStoreWithDB(authCapKey, iavl.StoreConstructor, db)
ms.LoadLatestVersion()
-
+ paramk := params.NewParamsKeeper(authCapKey, "")
ctx := sdk.NewContext(sdk.RunTxModeDeliver, ms, &bft.Header{ChainID: "test-chain-id"}, log.NewNoopLogger())
acck := auth.NewAccountKeeper(
- authCapKey, std.ProtoBaseAccount,
+ authCapKey, paramk, std.ProtoBaseAccount,
)
bank := NewBankKeeper(acck)
diff --git a/tm2/pkg/sdk/baseapp.go b/tm2/pkg/sdk/baseapp.go
index c11f81d852a..ea729abd6ae 100644
--- a/tm2/pkg/sdk/baseapp.go
+++ b/tm2/pkg/sdk/baseapp.go
@@ -179,7 +179,13 @@ func (app *BaseApp) initFromMainStore() error {
// Load the consensus params from the main store. If the consensus params are
// nil, it will be saved later during InitChain.
//
- // TODO: assert that InitChain hasn't yet been called.
+ // assert that InitChain hasn't yet been called.
+ // the app.checkState will be set in InitChain.
+ // We assert that InitChain hasn't yet been called so
+ // we don't over write the consensus params in the app.
+ if app.checkState != nil {
+ panic("Consensus Params are already set in app, we should not overwrite it here")
+ }
consensusParamsBz := mainStore.Get(mainConsensusParamsKey)
if consensusParamsBz != nil {
consensusParams := &abci.ConsensusParams{}
@@ -262,7 +268,7 @@ func (app *BaseApp) setConsensusParams(consensusParams *abci.ConsensusParams) {
app.consensusParams = consensusParams
}
-// setConsensusParams stores the consensus params to the main store.
+// storeConsensusParams stores the consensus params to the main store.
func (app *BaseApp) storeConsensusParams(consensusParams *abci.ConsensusParams) {
consensusParamsBz, err := amino.Marshal(consensusParams)
if err != nil {
@@ -354,6 +360,12 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC
}
}
}
+ // In app.initChainer(), we set the initial parameter values in the params keeper.
+ // The params keeper store needs to be accessible in the CheckTx state so that
+ // the first CheckTx can verify the gas price set right after the chain is initialized
+ // with the genesis state.
+ app.checkState.ctx.ms = app.deliverState.ctx.ms
+ app.checkState.ms = app.deliverState.ms
// NOTE: We don't commit, but BeginBlock for block 1 starts from this
// deliverState.
@@ -409,8 +421,16 @@ func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) (res abc
} else {
result = app.Simulate(txBytes, tx)
}
+
res.Height = req.Height
- res.Value = amino.MustMarshal(result)
+
+ bytes, err := amino.Marshal(result)
+ if err != nil {
+ res.Error = ABCIError(std.ErrInternal(fmt.Sprintf("cannot encode to JSON: %s", err)))
+ } else {
+ res.Value = bytes
+ }
+
return res
case "version":
res.Height = req.Height
@@ -860,7 +880,10 @@ func (app *BaseApp) runTx(ctx Context, tx Tx) (result Result) {
// EndBlock implements the ABCI interface.
func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBlock) {
if app.endBlocker != nil {
- res = app.endBlocker(app.deliverState.ctx, req)
+ // we need to load consensusParams to the end blocker Context
+ // end blocker use consensusParams to calculat the gas price changes.
+ ctx := app.deliverState.ctx.WithConsensusParams(app.consensusParams)
+ res = app.endBlocker(ctx, req)
}
return
diff --git a/tm2/pkg/sdk/baseapp_test.go b/tm2/pkg/sdk/baseapp_test.go
index 08e8191170a..67a880c9ffa 100644
--- a/tm2/pkg/sdk/baseapp_test.go
+++ b/tm2/pkg/sdk/baseapp_test.go
@@ -353,11 +353,6 @@ func TestInitChainer(t *testing.T) {
Data: key,
}
- // initChainer is nil - nothing happens
- app.InitChain(abci.RequestInitChain{ChainID: "test-chain"})
- res := app.Query(query)
- require.Equal(t, 0, len(res.Value))
-
// set initChainer and try again - should see the value
app.SetInitChainer(initChainer)
@@ -366,6 +361,11 @@ func TestInitChainer(t *testing.T) {
require.Nil(t, err)
require.Equal(t, int64(0), app.LastBlockHeight())
+ // initChainer is nil - nothing happens
+ app.InitChain(abci.RequestInitChain{ChainID: "test-chain"})
+ res := app.Query(query)
+ require.Equal(t, 0, len(res.Value))
+
app.InitChain(abci.RequestInitChain{AppState: nil, ChainID: "test-chain-id"}) // must have valid JSON genesis file, even if empty
// assert that chainID is set correctly in InitChain
@@ -447,7 +447,7 @@ func anteHandlerTxTest(t *testing.T, capKey store.StoreKey, storeKey []byte) Ant
t.Helper()
return func(ctx Context, tx std.Tx, simulate bool) (newCtx Context, res Result, abort bool) {
- store := ctx.Store(capKey)
+ store := ctx.GasStore(capKey)
if getFailOnAnte(tx) {
res.Error = ABCIError(std.ErrInternal("ante handler failure"))
return newCtx, res, true
@@ -634,6 +634,38 @@ func TestDeliverTx(t *testing.T) {
}
}
+// Test that the gas used between Simulate and DeliverTx is the same.
+func TestGasUsedBetweenSimulateAndDeliver(t *testing.T) {
+ t.Parallel()
+
+ anteKey := []byte("ante-key")
+ anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(anteHandlerTxTest(t, mainKey, anteKey)) }
+
+ deliverKey := []byte("deliver-key")
+ routerOpt := func(bapp *BaseApp) {
+ bapp.Router().AddRoute(routeMsgCounter, newMsgCounterHandler(t, mainKey, deliverKey))
+ }
+
+ app := setupBaseApp(t, anteOpt, routerOpt)
+ app.InitChain(abci.RequestInitChain{ChainID: "test-chain"})
+
+ header := &bft.Header{ChainID: "test-chain", Height: 1}
+ app.BeginBlock(abci.RequestBeginBlock{Header: header})
+
+ tx := newTxCounter(0, 0)
+ txBytes, err := amino.Marshal(tx)
+ require.Nil(t, err)
+
+ simulateRes := app.Simulate(txBytes, tx)
+ require.True(t, simulateRes.IsOK(), fmt.Sprintf("%v", simulateRes))
+ require.Greater(t, simulateRes.GasUsed, int64(0)) // gas used should be greater than 0
+
+ deliverRes := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes})
+ require.True(t, deliverRes.IsOK(), fmt.Sprintf("%v", deliverRes))
+
+ require.Equal(t, simulateRes.GasUsed, deliverRes.GasUsed) // gas used should be the same from simulate and deliver
+}
+
// One call to DeliverTx should process all the messages, in order.
func TestMultiMsgDeliverTx(t *testing.T) {
t.Parallel()
@@ -753,7 +785,7 @@ func TestSimulateTx(t *testing.T) {
require.True(t, queryResult.IsOK(), queryResult.Log)
var res Result
- amino.MustUnmarshal(queryResult.Value, &res)
+ require.NoError(t, amino.Unmarshal(queryResult.Value, &res))
require.Nil(t, err, "Result unmarshalling failed")
require.True(t, res.IsOK(), res.Log)
require.Equal(t, gasConsumed, res.GasUsed, res.Log)
diff --git a/tm2/pkg/sdk/context.go b/tm2/pkg/sdk/context.go
index 63c5a50f8eb..f9d9949bfbe 100644
--- a/tm2/pkg/sdk/context.go
+++ b/tm2/pkg/sdk/context.go
@@ -171,10 +171,14 @@ func (c Context) Value(key interface{}) interface{} {
// ----------------------------------------------------------------------------
// Store fetches a Store from the MultiStore, but wrapped for gas calculation.
-func (c Context) Store(key store.StoreKey) store.Store {
+func (c Context) GasStore(key store.StoreKey) store.Store {
return gas.New(c.MultiStore().GetStore(key), c.GasMeter(), store.DefaultGasConfig())
}
+func (c Context) Store(key store.StoreKey) store.Store {
+ return c.MultiStore().GetStore(key)
+}
+
// CacheContext returns a new Context with the multi-store cached and a new
// EventLogger . The cached context is written to the context when writeCache
// is called.
diff --git a/tm2/pkg/sdk/params/keeper.go b/tm2/pkg/sdk/params/keeper.go
index 523e8d54f69..c99b9dbfde1 100644
--- a/tm2/pkg/sdk/params/keeper.go
+++ b/tm2/pkg/sdk/params/keeper.go
@@ -11,9 +11,16 @@ import (
const (
ModuleName = "params"
- StoreKey = ModuleName
+
+ StoreKey = ModuleName
+ // ValueStorePrevfix is "/pv/" for param value.
+ ValueStoreKeyPrefix = "/pv/"
)
+func ValueStoreKey(key string) []byte {
+ return append([]byte(ValueStoreKeyPrefix), []byte(key)...)
+}
+
type ParamsKeeperI interface {
GetString(ctx sdk.Context, key string, ptr *string)
GetInt64(ctx sdk.Context, key string, ptr *int64)
@@ -49,7 +56,30 @@ func NewParamsKeeper(key store.StoreKey, prefix string) ParamsKeeper {
}
}
-// Logger returns a module-specific logger.
+// GetParam gets a param value from the global param store.
+func (pk ParamsKeeper) GetParams(ctx sdk.Context, key string, target interface{}) (bool, error) {
+ stor := ctx.Store(pk.key)
+
+ bz := stor.Get(ValueStoreKey(key))
+ if bz == nil {
+ return false, nil
+ }
+
+ return true, amino.UnmarshalJSON(bz, target)
+}
+
+// SetParam sets a param value to the global param store.
+func (pk ParamsKeeper) SetParams(ctx sdk.Context, key string, param interface{}) error {
+ stor := ctx.Store(pk.key)
+ bz, err := amino.MarshalJSON(param)
+ if err != nil {
+ return err
+ }
+
+ stor.Set(ValueStoreKey(key), bz)
+ return nil
+}
+
// XXX: why do we expose this?
func (pk ParamsKeeper) Logger(ctx sdk.Context) *slog.Logger {
return ctx.Logger().With("module", ModuleName)
diff --git a/tm2/pkg/sdk/params/keeper_test.go b/tm2/pkg/sdk/params/keeper_test.go
index 832d16229ee..aedfaa9d5a3 100644
--- a/tm2/pkg/sdk/params/keeper_test.go
+++ b/tm2/pkg/sdk/params/keeper_test.go
@@ -140,3 +140,24 @@ func TestKeeper_internal(t *testing.T) {
type s struct{ I int }
func indirect(ptr interface{}) interface{} { return reflect.ValueOf(ptr).Elem().Interface() }
+
+type Params struct {
+ p1 int
+ p2 string
+}
+
+func TestGetAndSetParams(t *testing.T) {
+ env := setupTestEnv()
+ ctx := env.ctx
+ keeper := env.keeper
+ // SetParams
+ a := Params{p1: 1, p2: "a"}
+ err := keeper.SetParams(ctx, ModuleName, a)
+ require.NoError(t, err)
+
+ // GetParams
+ a1 := Params{}
+ _, err1 := keeper.GetParams(ctx, ModuleName, &a1)
+ require.NoError(t, err1)
+ require.True(t, amino.DeepEqual(a, a1), "a and a1 should equal")
+}
diff --git a/tm2/pkg/std/coin.go b/tm2/pkg/std/coin.go
index 4f36757efc0..fba20a5ba78 100644
--- a/tm2/pkg/std/coin.go
+++ b/tm2/pkg/std/coin.go
@@ -8,7 +8,7 @@ import (
"strings"
"github.com/gnolang/gno/tm2/pkg/errors"
- "github.com/gnolang/overflow"
+ "github.com/gnolang/gno/tm2/pkg/overflow"
)
// -----------------------------------------------------------------------------
@@ -658,7 +658,7 @@ func ParseCoin(coinStr string) (coin Coin, err error) {
amount, err := strconv.ParseInt(amountStr, 10, 64)
if err != nil {
- return Coin{}, errors.Wrap(err, "failed to parse coin amount: %s", amountStr)
+ return Coin{}, errors.Wrapf(err, "failed to parse coin amount: %s", amountStr)
}
if err := validateDenom(denomStr); err != nil {
diff --git a/tm2/pkg/std/gasprice.go b/tm2/pkg/std/gasprice.go
index 5acc934fb71..fd082a93371 100644
--- a/tm2/pkg/std/gasprice.go
+++ b/tm2/pkg/std/gasprice.go
@@ -1,6 +1,7 @@
package std
import (
+ "math/big"
"strings"
"github.com/gnolang/gno/tm2/pkg/errors"
@@ -19,15 +20,18 @@ func ParseGasPrice(gasprice string) (GasPrice, error) {
}
price, err := ParseCoin(parts[0])
if err != nil {
- return GasPrice{}, errors.Wrap(err, "invalid gas price: %s (invalid price)", gasprice)
+ return GasPrice{}, errors.Wrapf(err, "invalid gas price: %s (invalid price)", gasprice)
}
gas, err := ParseCoin(parts[1])
if err != nil {
- return GasPrice{}, errors.Wrap(err, "invalid gas price: %s (invalid gas denom)", gasprice)
+ return GasPrice{}, errors.Wrapf(err, "invalid gas price: %s (invalid gas denom)", gasprice)
}
if gas.Denom != "gas" {
return GasPrice{}, errors.New("invalid gas price: %s (invalid gas denom)", gasprice)
}
+ if gas.Amount == 0 {
+ return GasPrice{}, errors.New("invalid gas price: %s (gas can not be zero)", gasprice)
+ }
return GasPrice{
Gas: gas.Amount,
Price: price,
@@ -43,8 +47,33 @@ func ParseGasPrices(gasprices string) (res []GasPrice, err error) {
for i, part := range parts {
res[i], err = ParseGasPrice(part)
if err != nil {
- return nil, errors.Wrap(err, "invalid gas prices: %s", gasprices)
+ return nil, errors.Wrapf(err, "invalid gas prices: %s", gasprices)
}
}
return res, nil
}
+
+// IsGTE compares the GasPrice with another gas price B. If the coin denom matches AND the fee per gas
+// is greater than or equal to gas price B, return true; otherwise, return false.
+func (gp GasPrice) IsGTE(gpB GasPrice) (bool, error) {
+ if gp.Price.Denom != gpB.Price.Denom {
+ return false, errors.New("Gas price denominations should be equal; %s, %s", gp.Price.Denom, gpB.Price.Denom)
+ }
+ if gp.Gas == 0 || gpB.Gas == 0 {
+ return false, errors.New("GasPrice.Gas cannot be zero; %+v, %+v", gp, gpB)
+ }
+
+ gpg := big.NewInt(gp.Gas)
+ gpa := big.NewInt(gp.Price.Amount)
+
+ gpBg := big.NewInt(gpB.Gas)
+ gpBa := big.NewInt(gpB.Price.Amount)
+
+ prod1 := big.NewInt(0).Mul(gpa, gpBg) // gp's price amount * gpB's gas
+ prod2 := big.NewInt(0).Mul(gpg, gpBa) // gpB's gas * pg's price amount
+ // This is equivalent to checking
+ // That the Fee / GasWanted ratio is greater than or equal to the minimum GasPrice per gas.
+ // This approach helps us avoid dealing with configurations where the value of
+ // the minimum gas price is set to 0.00001ugnot/gas.
+ return prod1.Cmp(prod2) >= 0, nil
+}
diff --git a/tm2/pkg/std/gasprice_test.go b/tm2/pkg/std/gasprice_test.go
new file mode 100644
index 00000000000..d4ec0832b88
--- /dev/null
+++ b/tm2/pkg/std/gasprice_test.go
@@ -0,0 +1,156 @@
+package std
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestGasPriceGTE(t *testing.T) {
+ t.Parallel()
+ tests := []struct {
+ name string
+ gp GasPrice
+ gpB GasPrice
+ expectError bool
+ errorMsg string
+ expected bool // for non-error cases: whether gp.IsGTE(gpB) should return true or false
+ }{
+ // Error cases: Different denominations
+ {
+ name: "Different denominations error",
+ gp: GasPrice{
+ Gas: 100,
+ Price: Coin{
+ Denom: "atom",
+ Amount: 500,
+ },
+ },
+ gpB: GasPrice{
+ Gas: 100,
+ Price: Coin{
+ Denom: "btc", // Different denomination
+ Amount: 500,
+ },
+ },
+ expectError: true,
+ errorMsg: "Gas price denominations should be equal;",
+ },
+ // Error cases: Zero Gas values
+ {
+ name: "Zero Gas in gp error",
+ gp: GasPrice{
+ Gas: 0, // Zero Gas in gp
+ Price: Coin{
+ Denom: "atom",
+ Amount: 500,
+ },
+ },
+ gpB: GasPrice{
+ Gas: 100,
+ Price: Coin{
+ Denom: "atom",
+ Amount: 500,
+ },
+ },
+ expectError: true,
+ errorMsg: "GasPrice.Gas cannot be zero;",
+ },
+ {
+ name: "Zero Gas in gpB error",
+ gp: GasPrice{
+ Gas: 100,
+ Price: Coin{
+ Denom: "atom",
+ Amount: 500,
+ },
+ },
+ gpB: GasPrice{
+ Gas: 0, // Zero Gas in gpB
+ Price: Coin{
+ Denom: "atom",
+ Amount: 500,
+ },
+ },
+ expectError: true,
+ errorMsg: "GasPrice.Gas cannot be zero;",
+ },
+ // Valid cases: No errors, just compare gas prices
+ {
+ name: "Greater Gas Price",
+ gp: GasPrice{
+ Gas: 100,
+ Price: Coin{
+ Denom: "atom",
+ Amount: 600, // Greater price
+ },
+ },
+ gpB: GasPrice{
+ Gas: 100,
+ Price: Coin{
+ Denom: "atom",
+ Amount: 500,
+ },
+ },
+ expectError: false,
+ expected: true,
+ },
+ {
+ name: "Equal Gas Price",
+ gp: GasPrice{
+ Gas: 100,
+ Price: Coin{
+ Denom: "atom",
+ Amount: 500,
+ },
+ },
+ gpB: GasPrice{
+ Gas: 100,
+ Price: Coin{
+ Denom: "atom",
+ Amount: 500,
+ },
+ },
+ expectError: false,
+ expected: true,
+ },
+ {
+ name: "Lesser Gas Price",
+ gp: GasPrice{
+ Gas: 100,
+ Price: Coin{
+ Denom: "atom",
+ Amount: 400, // Lesser price
+ },
+ },
+ gpB: GasPrice{
+ Gas: 100,
+ Price: Coin{
+ Denom: "atom",
+ Amount: 500,
+ },
+ },
+ expectError: false,
+ expected: false,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+ got, err := tt.gp.IsGTE(tt.gpB)
+ if !tt.expectError {
+ require.NoError(t, err)
+ assert.Equal(t, tt.expected, got, "Expect that %v is less than %v", tt.gp, tt.gpB)
+ if got != tt.expected {
+ t.Errorf("Test %s failed: expected result %v, got %v", tt.name, tt.expected, got)
+ }
+ } else {
+ require.Error(t, err)
+ errorMsg := err.Error()
+ assert.Contains(t, errorMsg, tt.errorMsg, "expected error message containing %q, but got %q", tt.errorMsg, errorMsg)
+ }
+ })
+ }
+}
diff --git a/tm2/pkg/std/package.go b/tm2/pkg/std/package.go
index 3f71c69f0ce..a1aadc17cb6 100644
--- a/tm2/pkg/std/package.go
+++ b/tm2/pkg/std/package.go
@@ -12,6 +12,10 @@ var Package = amino.RegisterPackage(amino.NewPackage(
// Account
&BaseAccount{}, "BaseAccount",
+ // Coin
+ &Coin{}, "Coin",
+ // GasPrice
+ &GasPrice{}, "GasPrice",
// Errors
InternalError{}, "InternalError",
diff --git a/tm2/pkg/std/package_test.go b/tm2/pkg/std/package_test.go
index 0a21188737b..2659d9b6955 100644
--- a/tm2/pkg/std/package_test.go
+++ b/tm2/pkg/std/package_test.go
@@ -24,3 +24,56 @@ func TestAminoBaseAccount(t *testing.T) {
err := amino.UnmarshalJSON(b, &acc)
require.NoError(t, err)
}
+
+func TestAminoGasPrice(t *testing.T) {
+ gp := std.GasPrice{
+ Gas: 100,
+ Price: std.Coin{
+ Denom: "token",
+ Amount: 10,
+ },
+ }
+ // Binary
+ bz, err := amino.Marshal(gp)
+ require.NoError(t, err)
+ err = amino.Unmarshal(bz, &gp)
+ require.NoError(t, err)
+
+ // JSON
+ bz, err = amino.MarshalJSON(gp)
+ require.NoError(t, err)
+
+ err = amino.UnmarshalJSON(bz, &gp)
+ require.NoError(t, err)
+
+ bz = []byte(`{
+ "gas": "10",
+ "price": "100token"
+ }`)
+ err = amino.UnmarshalJSON(bz, &gp)
+ require.NoError(t, err)
+}
+
+func TestAminoCoin(t *testing.T) {
+ coin := std.Coin{
+ Denom: "token",
+ Amount: 10,
+ }
+
+ // Binary
+ bz, err := amino.Marshal(coin)
+ require.NoError(t, err)
+
+ err = amino.Unmarshal(bz, &coin)
+ require.NoError(t, err)
+
+ // JSON
+ bz, err = amino.MarshalJSON(coin)
+ require.NoError(t, err)
+ err = amino.UnmarshalJSON(bz, &coin)
+ require.NoError(t, err)
+
+ bz = []byte(`"10token"`)
+ err = amino.UnmarshalJSON(bz, &coin)
+ require.NoError(t, err)
+}
diff --git a/tm2/pkg/store/cache/store_test.go b/tm2/pkg/store/cache/store_test.go
index 1caf51ea52c..1cb1d0b60d9 100644
--- a/tm2/pkg/store/cache/store_test.go
+++ b/tm2/pkg/store/cache/store_test.go
@@ -359,12 +359,12 @@ func TestCacheKVMergeIteratorRandom(t *testing.T) {
truth := memdb.NewMemDB()
start, end := 25, 975
- max := 1000
+ maxVal := 1000
setRange(st, truth, start, end)
// do an op, test the iterator
for i := 0; i < 2000; i++ {
- doRandomOp(st, truth, max)
+ doRandomOp(st, truth, maxVal)
assertIterateDomainCompare(t, st, truth)
}
}
diff --git a/tm2/pkg/store/gas/store.go b/tm2/pkg/store/gas/store.go
index db5ea7a79b0..81e898a90d8 100644
--- a/tm2/pkg/store/gas/store.go
+++ b/tm2/pkg/store/gas/store.go
@@ -1,9 +1,9 @@
package gas
import (
+ "github.com/gnolang/gno/tm2/pkg/overflow"
"github.com/gnolang/gno/tm2/pkg/store/types"
"github.com/gnolang/gno/tm2/pkg/store/utils"
- "github.com/gnolang/overflow"
)
var _ types.Store = &Store{}
diff --git a/tm2/pkg/store/types/gas.go b/tm2/pkg/store/types/gas.go
index fd631dd3259..9d1f3d70c28 100644
--- a/tm2/pkg/store/types/gas.go
+++ b/tm2/pkg/store/types/gas.go
@@ -3,7 +3,7 @@ package types
import (
"math"
- "github.com/gnolang/overflow"
+ "github.com/gnolang/gno/tm2/pkg/overflow"
)
// Gas consumption descriptors.
diff --git a/tm2/pkg/store/types/gas_test.go b/tm2/pkg/store/types/gas_test.go
index 410ba0b7e92..115d347bd5e 100644
--- a/tm2/pkg/store/types/gas_test.go
+++ b/tm2/pkg/store/types/gas_test.go
@@ -4,7 +4,7 @@ import (
"math"
"testing"
- "github.com/gnolang/overflow"
+ "github.com/gnolang/gno/tm2/pkg/overflow"
"github.com/stretchr/testify/require"
)
diff --git a/tm2/pkg/telemetry/metrics/metrics.go b/tm2/pkg/telemetry/metrics/metrics.go
index 7a3e182e06d..e3ae932612f 100644
--- a/tm2/pkg/telemetry/metrics/metrics.go
+++ b/tm2/pkg/telemetry/metrics/metrics.go
@@ -35,6 +35,7 @@ const (
blockIntervalKey = "block_interval_hist"
blockTxsKey = "block_txs_hist"
blockSizeKey = "block_size_hist"
+ gasPriceKey = "block_gas_price_hist"
httpRequestTimeKey = "http_request_time_hist"
wsRequestTimeKey = "ws_request_time_hist"
@@ -96,6 +97,9 @@ var (
// BlockSizeBytes measures the size of the latest block in bytes
BlockSizeBytes metric.Int64Histogram
+ // BlockGasPriceAmount measures the block gas price of the last block
+ BlockGasPriceAmount metric.Int64Histogram
+
// RPC //
// HTTPRequestTime measures the HTTP request response time
@@ -271,6 +275,13 @@ func Init(config config.Config) error {
return fmt.Errorf("unable to create histogram, %w", err)
}
+ if BlockGasPriceAmount, err = meter.Int64Histogram(
+ gasPriceKey,
+ metric.WithDescription("block gas price"),
+ metric.WithUnit("token"),
+ ); err != nil {
+ return fmt.Errorf("unable to create histogram, %w", err)
+ }
// RPC //
if HTTPRequestTime, err = meter.Int64Histogram(