Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: small improvements for #1702 #2276

Merged
merged 15 commits into from
Jun 24, 2024
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,7 @@ pbbindings.go
# Test coverage leftovers
cover.out
coverage.out

*.swp
*.swo
*.bak
4 changes: 3 additions & 1 deletion gno.land/pkg/gnoland/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,9 @@
}

// Initialize the VMKeeper.
vmKpr.Initialize(baseApp.GetCacheMultiStore())
ms := baseApp.GetCacheMultiStore()
vmKpr.Initialize(ms)
ms.MultiWrite() // XXX why was't this needed?

Check warning on line 125 in gno.land/pkg/gnoland/app.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/gnoland/app.go#L123-L125

Added lines #L123 - L125 were not covered by tests
Copy link
Contributor Author

@jaekwon jaekwon Jun 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it was always requried, but its absence (of MultiWrite) being masked by the fact that later transactions would commit the same side effect, e.g. populate the mem package for dependencies like "time" or "strconv". But i'm not sure. If that's true, then loadpkg in genesis would have no effect for our integration tests?

Can somebody look into it?


return baseApp, nil
}
Expand Down
57 changes: 28 additions & 29 deletions gno.land/pkg/sdk/vm/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,41 @@
"path/filepath"

gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
"github.com/gnolang/gno/gnovm/stdlibs"
"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"
)

func (vm *VMKeeper) initBuiltinPackagesAndTypes(store gno.Store) {
// NOTE: native functions/methods added here must be quick operations,
// or account for gas before operation.
// TODO: define criteria for inclusion, and solve gas calculations.
getPackage := func(pkgPath string, newStore gno.Store) (pn *gno.PackageNode, pv *gno.PackageValue) {
// otherwise, built-in package value.
// first, load from filepath.
stdlibPath := filepath.Join(vm.stdlibsDir, pkgPath)
if !osm.DirExists(stdlibPath) {
// does not exist.
return nil, nil
}
memPkg := gno.ReadMemPackage(stdlibPath, pkgPath)
if memPkg.IsEmpty() {
// no gno files are present, skip this package
return nil, nil
}

m2 := gno.NewMachineWithOptions(gno.MachineOptions{
PkgPath: "gno.land/r/stdlibs/" + pkgPath,
// PkgPath: pkgPath,
Output: os.Stdout,
Store: newStore,
})
defer m2.Release()
return m2.RunMemPackage(memPkg, true)
// NOTE: this function may add loaded dependencies to store if they don't
// already exist, including mem packages. If this happens during a transaction
// with the tx context store, the transaction caller will pay for operations.
// NOTE: native functions/methods added here must be quick operations, or
// account for gas before operation.
// TODO: define criteria for inclusion, and solve gas calculations(???).
func (vm *VMKeeper) getPackage(pkgPath string, store gno.Store) (pn *gno.PackageNode, pv *gno.PackageValue) {
// otherwise, built-in package value.
// first, load from filepath.
stdlibPath := filepath.Join(vm.stdlibsDir, pkgPath)
if !osm.DirExists(stdlibPath) {
// does not exist.
return nil, nil
}
memPkg := gno.ReadMemPackage(stdlibPath, pkgPath)
if memPkg.IsEmpty() {
// no gno files are present, skip this package
return nil, nil

Check warning on line 31 in gno.land/pkg/sdk/vm/builtins.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/sdk/vm/builtins.go#L31

Added line #L31 was not covered by tests
}
store.SetPackageGetter(getPackage)
store.SetNativeStore(stdlibs.NativeStore)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

diff artifact, not actually removed from the original code block.


m2 := gno.NewMachineWithOptions(gno.MachineOptions{
PkgPath: "gno.land/r/stdlibs/" + pkgPath,
// PkgPath: pkgPath,
Output: os.Stdout,
Store: store,
})
defer m2.Release()
pn, pv = m2.RunMemPackage(memPkg, true)
return
}

// ----------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions gno.land/pkg/sdk/vm/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ func (vm *VMKeeper) Initialize(ms store.MultiStore) {
baseSDKStore := ms.GetStore(vm.baseKey)
iavlSDKStore := ms.GetStore(vm.iavlKey)
vm.gnoStore = gno.NewStore(alloc, baseSDKStore, iavlSDKStore)
vm.initBuiltinPackagesAndTypes(vm.gnoStore)
vm.gnoStore.SetPackageGetter(vm.getPackage)
vm.gnoStore.SetNativeStore(stdlibs.NativeStore)
if vm.gnoStore.NumMemPackages() > 0 {
// for now, all mem packages must be re-run after reboot.
// TODO remove this, and generally solve for in-mem garbage collection
Expand Down Expand Up @@ -157,7 +158,6 @@ func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) (err error) {
if pv := gnostore.GetPackage(pkgPath, false); pv != nil {
return ErrInvalidPkgPath("package already exists: " + pkgPath)
}

if gno.ReGnoRunPath.MatchString(pkgPath) {
return ErrInvalidPkgPath("reserved package name: " + pkgPath)
}
Expand Down
1 change: 1 addition & 0 deletions gnovm/pkg/gnolang/go2gno.go
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,7 @@ func Go2Gno(fs *token.FileSet, gon ast.Node) (n Node) {

//----------------------------------------
// type checking (using go/types)
// XXX move to gotypecheck.go.

// MemPackageGetter implements the GetMemPackage() method. It is a subset of
// [Store], separated for ease of testing.
Expand Down
49 changes: 34 additions & 15 deletions gnovm/pkg/gnolang/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,21 @@ import (
"strings"

"github.com/gnolang/gno/tm2/pkg/amino"
"github.com/gnolang/gno/tm2/pkg/colors"
"github.com/gnolang/gno/tm2/pkg/std"
"github.com/gnolang/gno/tm2/pkg/store"
"github.com/gnolang/gno/tm2/pkg/store/types"
"github.com/gnolang/gno/tm2/pkg/store/utils"
stringz "github.com/gnolang/gno/tm2/pkg/strings"
)

// PackageGetter specifies how the store may retrieve packages which are not
// already in its cache. PackageGetter should return nil when the requested
// package does not exist. store should be used to run the machine, or otherwise
// call any methods which may call store.GetPackage; avoid using any "global"
// store as the one passed to the PackageGetter may be a fork of that (ie.
// the original is not meant to be written to).
// the original is not meant to be written to). Loading dependencies may
// cause writes to happen to the store, such as MemPackages to iavlstore.
type PackageGetter func(pkgPath string, store Store) (*PackageNode, *PackageValue)

// inject natives into a new or loaded package (value and node)
Expand Down Expand Up @@ -68,6 +73,8 @@ type Store interface {
LogSwitchRealm(rlmpath string) // to mark change of realm boundaries
ClearCache()
Print()
Write()
Flush()
}

// Used to keep track of in-mem objects during tx.
Expand All @@ -86,7 +93,7 @@ type defaultStore struct {

// transient
opslog []StoreOp // for debugging and testing.
current []string
current []string // for detecting import cycles.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's still detecting import cycles.

}

func NewStore(alloc *Allocator, baseStore, iavlStore store.Store) *defaultStore {
Expand Down Expand Up @@ -115,6 +122,7 @@ func (ds *defaultStore) SetPackageGetter(pg PackageGetter) {

// Gets package from cache, or loads it from baseStore, or gets it from package getter.
func (ds *defaultStore) GetPackage(pkgPath string, isImport bool) *PackageValue {
// helper to detect circular imports
if isImport {
if slices.Contains(ds.current, pkgPath) {
panic(fmt.Sprintf("import cycle detected: %q (through %v)", pkgPath, ds.current))
Expand Down Expand Up @@ -672,8 +680,16 @@ func (ds *defaultStore) GetNative(pkgPath string, name Name) func(m *Machine) {
return nil
}

// Writes one level of cache to store.
func (ds *defaultStore) Write() {
ds.baseStore.(types.Writer).Write()
ds.iavlStore.(types.Writer).Write()
}

// Flush cached writes to disk.
func (ds *defaultStore) Flush() {
// XXX
ds.baseStore.(types.Flusher).Flush()
ds.iavlStore.(types.Flusher).Flush()
}

// ----------------------------------------
Expand Down Expand Up @@ -755,22 +771,25 @@ func (ds *defaultStore) ClearCache() {

// for debugging
func (ds *defaultStore) Print() {
fmt.Println("//----------------------------------------")
fmt.Println("defaultStore:baseStore...")
store.Print(ds.baseStore)
fmt.Println("//----------------------------------------")
fmt.Println("defaultStore:iavlStore...")
store.Print(ds.iavlStore)
fmt.Println("//----------------------------------------")
fmt.Println("defaultStore:cacheTypes...")
fmt.Println(colors.Yellow("//----------------------------------------"))
fmt.Println(colors.Green("defaultStore:baseStore..."))
utils.Print(ds.baseStore)
fmt.Println(colors.Yellow("//----------------------------------------"))
fmt.Println(colors.Green("defaultStore:iavlStore..."))
utils.Print(ds.iavlStore)
fmt.Println(colors.Yellow("//----------------------------------------"))
fmt.Println(colors.Green("defaultStore:cacheTypes..."))
for tid, typ := range ds.cacheTypes {
fmt.Printf("- %v: %v\n", tid, typ)
fmt.Printf("- %v: %v\n", tid,
stringz.TrimN(fmt.Sprintf("%v", typ), 50))
}
fmt.Println("//----------------------------------------")
fmt.Println("defaultStore:cacheNodes...")
fmt.Println(colors.Yellow("//----------------------------------------"))
fmt.Println(colors.Green("defaultStore:cacheNodes..."))
for loc, bn := range ds.cacheNodes {
fmt.Printf("- %v: %v\n", loc, bn)
fmt.Printf("- %v: %v\n", loc,
stringz.TrimN(fmt.Sprintf("%v", bn), 50))
}
fmt.Println(colors.Red("//----------------------------------------"))
}

// ----------------------------------------
Expand Down
24 changes: 12 additions & 12 deletions gnovm/tests/imports.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ const (
)

// 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) (store gno.Store) {
getPackage := func(pkgPath string, newStore gno.Store) (pn *gno.PackageNode, pv *gno.PackageValue) {
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"))
}
Expand All @@ -83,7 +83,7 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri
m2 := gno.NewMachineWithOptions(gno.MachineOptions{
PkgPath: "test",
Output: stdout,
Store: newStore,
Store: store,
Context: ctx,
})
// pkg := gno.NewPackageNode(gno.Name(memPkg.Name), memPkg.Path, nil)
Expand All @@ -96,7 +96,7 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri
// if stdlibs package is preferred , try to load it first.
if mode == ImportModeStdlibsOnly ||
mode == ImportModeStdlibsPreferred {
pn, pv = loadStdlib(rootDir, pkgPath, newStore, stdout)
pn, pv = loadStdlib(rootDir, pkgPath, store, stdout)
if pn != nil {
return
}
Expand Down Expand Up @@ -375,7 +375,7 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri

// if native package is preferred, try to load stdlibs/* as backup.
if mode == ImportModeNativePreferred {
pn, pv = loadStdlib(rootDir, pkgPath, newStore, stdout)
pn, pv = loadStdlib(rootDir, pkgPath, store, stdout)
if pn != nil {
return
}
Expand All @@ -394,23 +394,23 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri
m2 := gno.NewMachineWithOptions(gno.MachineOptions{
PkgPath: "test",
Output: stdout,
Store: newStore,
Store: store,
Context: ctx,
})
pn, pv = m2.RunMemPackage(memPkg, true)
return
}
return nil, nil
}
// NOTE: store is also used in closure above.
db := memdb.NewMemDB()
baseStore := dbadapter.StoreConstructor(db, stypes.StoreOptions{})
iavlStore := iavl.StoreConstructor(db, stypes.StoreOptions{})
store = gno.NewStore(nil, baseStore, iavlStore)
store.SetPackageGetter(getPackage)
store.SetNativeStore(teststdlibs.NativeStore)
store.SetPackageInjector(testPackageInjector)
store.SetStrictGo2GnoMapping(false)
// make a new store
resStore = gno.NewStore(nil, baseStore, iavlStore)
resStore.SetPackageGetter(getPackage)
resStore.SetNativeStore(teststdlibs.NativeStore)
resStore.SetPackageInjector(testPackageInjector)
resStore.SetStrictGo2GnoMapping(false)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they're both new stores, so name neither "newStore" :)

return
}

Expand Down
74 changes: 70 additions & 4 deletions tm2/pkg/colors/colors.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,80 @@ func Gray(args ...interface{}) string {
return treatAll(ANSIFgGray, args...)
}

func ColoredBytes(data []byte, textColor, bytesColor func(...interface{}) string) string {
// result may be 4 ASNSII chars longer than they should be to denote the
// elipses (...), and one for a trailing hex nibble in case the last byte is
// non-ascii.
// NOTE: it is annoying to try make this perfect and always fit within n, so we
// don't do this yet, but left as an exercise. :)
func ColoredBytesN(data []byte, n int, textColor, bytesColor func(...interface{}) string) string {
_n := 0
s := ""
for _, b := range data {
buf := "" // buffer
bufIsText := true // is buf text or hex
for i, b := range data {
RESTART:
if 0x21 <= b && b < 0x7F {
s += textColor(string(b))
if !bufIsText {
s += bytesColor(buf)
buf = ""
bufIsText = true
goto RESTART
}
buf += string(b)
_n += 1
if n != 0 && _n >= n {
if i == len(data)-1 {
// done
s += textColor(buf)
buf = ""
} else {
s += textColor(buf) + "..."
buf = ""
}
break
}
} else {
if bufIsText {
s += textColor(buf)
buf = ""
bufIsText = false
goto RESTART
}
buf += fmt.Sprintf("%02X", b)
_n += 2
if n != 0 && _n >= n {
if i == len(data)-1 {
// done
s += bytesColor(buf)
buf = ""
} else {
s += bytesColor(buf) + "..."
buf = ""
}
break
}
}
}
if buf != "" {
if bufIsText {
s += textColor(buf)
buf = ""
} else {
s += bytesColor(fmt.Sprintf("%02X", b))
s += bytesColor(buf)
buf = ""
Copy link
Contributor Author

@jaekwon jaekwon Jun 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If somebody can write a perfect n len-capped implementation that is simpler than this, I'd be impressed. But it's also probably unnecessary.

}
}
return s
}

func DefaultColoredBytesN(data []byte, n int) string {
return ColoredBytesN(data, n, Blue, Green)
}

func ColoredBytes(data []byte, textColor, bytesColor func(...interface{}) string) string {
return ColoredBytesN(data, 0, textColor, bytesColor)
}

func DefaultColoredBytes(data []byte) string {
return ColoredBytes(data, Blue, Green)
}
7 changes: 4 additions & 3 deletions tm2/pkg/db/goleveldb/go_level_db.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"path/filepath"

"github.com/gnolang/gno/tm2/pkg/colors"
"github.com/gnolang/gno/tm2/pkg/db"
"github.com/gnolang/gno/tm2/pkg/db/internal"
"github.com/syndtr/goleveldb/leveldb"
Expand Down Expand Up @@ -115,9 +116,9 @@ func (db *GoLevelDB) Print() {

itr := db.db.NewIterator(nil, nil)
for itr.Next() {
key := itr.Key()
value := itr.Value()
fmt.Printf("[%X]:\t[%X]\n", key, value)
key := colors.DefaultColoredBytesN(itr.Key(), 50)
value := colors.DefaultColoredBytesN(itr.Value(), 100)
fmt.Printf("%v: %v\n", key, value)
}
}

Expand Down
Loading
Loading