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(gnovm): improved native bindings #859

Merged
merged 73 commits into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
a938f69
initial poc
thehowl May 30, 2023
5358970
Merge branch 'master' of github.com:gnolang/gno into dev/native-bindi…
thehowl Jun 9, 2023
6153b6a
changes
thehowl Jun 20, 2023
0002819
add injector call
thehowl Jun 20, 2023
cd94b0c
log ptrs
thehowl Jun 20, 2023
6248c80
change approach to "NativeStore"
thehowl Jun 23, 2023
b70c124
Merge branch 'master' of github.com:gnolang/gno into dev/native-bindi…
thehowl Jun 23, 2023
9fc487a
remove old debug call
thehowl Jun 23, 2023
da4f9a8
make AssertOriginCall native gno
thehowl Jul 7, 2023
9edecac
more permanent location and name for stdgen
thehowl Jul 7, 2023
c4094d2
allow overriding std functions in test context
thehowl Jul 10, 2023
bc2d558
implement SkipHeights, add NativeStore elsewhere
thehowl Jul 10, 2023
d27a7ca
fix native bindings in realms
thehowl Jul 12, 2023
c876012
Merge branch 'master' of github.com:gnolang/gno into dev/native-bindi…
thehowl Jul 12, 2023
0f54a0c
fix low-hanging failing tests
thehowl Jul 12, 2023
3a985fc
remove unused code, add more docs
thehowl Jul 12, 2023
f2d8894
more docs & text fixes
thehowl Jul 12, 2023
daa58c9
Merge branch 'master' into dev/native-bindings-poc
thehowl Jul 24, 2023
50c919d
ci: add workflow to ensure go generate is executed
thehowl Jul 24, 2023
73e411e
add generate to make
thehowl Jul 24, 2023
b1d6a3d
convert all injections to new native bindings
thehowl Aug 23, 2023
3560f42
add native.go files as generated
thehowl Aug 23, 2023
82e113c
Merge branch 'master' of github.com:gnolang/gno into dev/native-bindi…
thehowl Aug 23, 2023
6452305
add // injected comment on all injected fns
thehowl Aug 23, 2023
ed81331
address PR comments
thehowl Aug 23, 2023
b661430
support returning gno.TypedValue, for specialcasing GetBanker
thehowl Aug 23, 2023
5bda745
fix failing cmd tests
thehowl Aug 23, 2023
e1bf401
refactor genstd field iteration
thehowl Aug 23, 2023
60525e3
typo
thehowl Aug 23, 2023
b6a6c5d
Update gnovm/pkg/gnolang/gonative.go
thehowl Aug 30, 2023
62afd22
add tests
thehowl Aug 31, 2023
0e787fa
update makefiles & workflows
thehowl Aug 31, 2023
9f3f261
add misc to codecov
thehowl Aug 31, 2023
ec9f5da
address TODO in filterDuplicates
thehowl Sep 6, 2023
d7de22f
Merge branch 'master' of github.com:gnolang/gno into dev/native-bindi…
thehowl Sep 6, 2023
a3b25e5
add stdlibs documentation file;
thehowl Sep 6, 2023
a68cfe4
fix ci
thehowl Sep 6, 2023
a89302f
remove 1.20 from versions for genstd
thehowl Sep 6, 2023
c3200a6
Merge branch 'master' of github.com:gnolang/gno into dev/native-bindi…
thehowl Sep 16, 2023
c6a4be3
fix: copy native store on Fork()
thehowl Sep 20, 2023
dcf4904
Merge branch 'master' of github.com:gnolang/gno into dev/native-bindi…
thehowl Oct 11, 2023
42a11cc
Update docs/stdlibs.md
thehowl Oct 20, 2023
c64aaac
allow duplicate definitions in preprocessor/vm; switch to checkDuplic…
thehowl Oct 21, 2023
f874119
add comment on exprstring;
thehowl Oct 21, 2023
237bce9
add godoc to FuncD
thehowl Oct 21, 2023
bef0473
doc changes
thehowl Oct 21, 2023
361667a
base64 change doc
thehowl Oct 21, 2023
5592265
fixes on new method
thehowl Oct 21, 2023
b5b46d6
lint fix
thehowl Oct 21, 2023
c7f0c1a
jae trying new approach
jaekwon Oct 21, 2023
9477f86
Merge branch 'thehowl/dev/native-bindings-poc' of github.com:gnolang/…
thehowl Oct 21, 2023
1475790
also update the FuncValue.Type
jaekwon Oct 21, 2023
5bc3815
Merge branch 'thehowl/dev/native-bindings-poc' of github.com:gnolang/…
thehowl Oct 21, 2023
bb613a7
help for debugging
thehowl Oct 21, 2023
b910ac9
Merge branch 'master' of github.com:gnolang/gno into dev/native-bindi…
thehowl Oct 21, 2023
ce0185e
gofmt
thehowl Oct 21, 2023
1f647e0
redeclaration tests; better redeclaration handling; still WIP
jaekwon Oct 21, 2023
f6c83ae
do not redeclare methods upon restart // hack
jaekwon Oct 21, 2023
7874fb7
cleanup
jaekwon Oct 23, 2023
5930f1a
recover and revertToOld upon panic in preprocessor
jaekwon Oct 24, 2023
c065637
Merge branch 'thehowl/dev/native-bindings-poc' of github.com:gnolang/…
thehowl Oct 27, 2023
d8d93c8
workflow changes
thehowl Oct 27, 2023
c9deb06
fmt
thehowl Oct 27, 2023
fc858af
Merge branch 'master' of github.com:gnolang/gno into dev/native-bindi…
thehowl Oct 27, 2023
9439ac3
make error understandable
thehowl Oct 27, 2023
b89bf91
move invalid packages to gnovm/tests/files/extern
thehowl Oct 27, 2023
7ca0167
make gno test . work for math, and other native bindings stdlibs
thehowl Oct 27, 2023
2ff3f63
add test to check for revertToOld
thehowl Oct 28, 2023
6f6a621
prefer comparing locations directly
thehowl Dec 18, 2023
12a837b
Merge branch 'master' of github.com:gnolang/gno into dev/native-bindi…
thehowl Dec 18, 2023
ac66975
fix lint
thehowl Dec 18, 2023
108acc1
cleanup
thehowl Dec 18, 2023
9d13896
remove old stdlib doc
thehowl Dec 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions gnovm/pkg/gnolang/go2gno.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,11 @@ func MustParseExpr(expr string) Expr {
return x
}

// filename must not include the path.
// ParseFile uses the Go parser to parse body. It then runs [Go2Gno] on the
// resulting AST -- the resulting FileNode is returned, together with any other
// error (including panics, which are recovered) from [Go2Gno].
func ParseFile(filename string, body string) (fn *FileNode, err error) {
// Parse src but stop after processing the imports.
// Use go parser to parse the body.
fs := token.NewFileSet()
f, err := parser.ParseFile(fs, filename, body, parser.ParseComments|parser.DeclarationErrors)
if err != nil {
Expand All @@ -112,9 +114,9 @@ func ParseFile(filename string, body string) (fn *FileNode, err error) {
defer func() {
if r := recover(); r != nil {
if rerr, ok := r.(error); ok {
err = rerr
err = errors.Wrap(rerr, "parsing file")
} else {
err = errors.New(fmt.Sprintf("%v", r))
err = errors.New(fmt.Sprintf("%v", r)).Stacktrace()
}
return
}
Expand Down Expand Up @@ -431,7 +433,16 @@ func Go2Gno(fs *token.FileSet, gon ast.Node) (n Node) {
}
name := toName(gon.Name)
type_ := Go2Gno(fs, gon.Type).(*FuncTypeExpr)
body := Go2Gno(fs, gon.Body).(*BlockStmt).Body
var body []Stmt
if gon.Body != nil {
body = Go2Gno(fs, gon.Body).(*BlockStmt).Body
} else {
bd, err := ParseExpr(`panic("call to bodyless/external function is invalid outside of standard library")`)
if err != nil {
panic(err)
}
body = []Stmt{&ExprStmt{X: bd}}
}
return &FuncDecl{
IsMethod: isMethod,
Recv: recv,
Expand Down
46 changes: 30 additions & 16 deletions gnovm/pkg/gnolang/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type Machine struct {
Exceptions []*TypedValue // if panic'd unless recovered
NumResults int // number of results returned
Cycles int64 // number of "cpu" cycles
Injector func(store Store, pn *PackageNode)

// Configuration
CheckTypes bool // not yet used
Expand All @@ -45,14 +46,15 @@ type Machine struct {
Context interface{}
}

// machine.Release() must be called on objects
// created via this constructor
// Machine with new package of given path.
// Creates a new MemRealmer for any new realms.
// Looks in store for package of pkgPath; if not found,
// creates new instances as necessary.
// If pkgPath is zero, the machine has no active package
// and one must be set prior to usage.
// NewMachine initializes a new gno virtual machine, acting as a shorthand
// for [NewMachineWithOptions], setting the given options PkgPath and Store.
//
// The machine will run on the package at the given path, which will be
// retrieved through the given store. If it is not set, the machine has no
// active package, and one must be set prior to usage.
//
// Like for [NewMachineWithOptions], Machines initialized through this
// constructor must be finalized with [Machine.Release].
thehowl marked this conversation as resolved.
Show resolved Hide resolved
func NewMachine(pkgPath string, store Store) *Machine {
return NewMachineWithOptions(
MachineOptions{
Expand All @@ -61,16 +63,19 @@ func NewMachine(pkgPath string, store Store) *Machine {
})
}

// MachineOptions is used to pass options to [NewMachineWithOptions].
type MachineOptions struct {
// Active package of the given machine; must be set before execution.
PkgPath string
CheckTypes bool // not yet used
ReadOnly bool
Output io.Writer
Store Store
Output io.Writer // default os.Stdout
Store Store // default NewStore(Alloc, nil, nil)
Context interface{}
Alloc *Allocator // or see MaxAllocBytes.
MaxAllocBytes int64 // or 0 for no limit.
MaxCycles int64 // or 0 for no limit.
Injector func(store Store, pn *PackageNode)
}

// the machine constructor gets spammed
Expand All @@ -86,6 +91,11 @@ var machinePool = sync.Pool{
},
}

// NewMachineWithOptions initializes a new gno virtual machine with the given
thehowl marked this conversation as resolved.
Show resolved Hide resolved
// options.
//
// Machines initialized through this constructor must be finalized with
// [Machine.Release].
func NewMachineWithOptions(opts MachineOptions) *Machine {
checkTypes := opts.CheckTypes
readOnly := opts.ReadOnly
Expand Down Expand Up @@ -129,6 +139,7 @@ func NewMachineWithOptions(opts MachineOptions) *Machine {
Output: output,
Store: store,
Context: context,
Injector: opts.Injector,
}

if pv != nil {
Expand All @@ -147,13 +158,11 @@ var (
valueZeroed [ValueSize]TypedValue
)

// m should not be used after this call
// if m is nil, this will panic
// this is on purpose, to discourage misuse
// and prevent objects that were not taken from
// the pool, to call Release
// Release resets some of the values of *Machine and puts back m into the
// machine pool; for this reason, Release() should be called as a finalizer,
// and m should not be used after this call. Only Machines initialized with this
// package's constructors should be released.
func (m *Machine) Release() {
// copy()
// here we zero in the values for the next user
m.NumOps = 0
m.NumValues = 0
Expand Down Expand Up @@ -494,6 +503,11 @@ func (m *Machine) runFiles(fns ...*FileNode) {
}
}

// Inject native functions
if m.Injector != nil {
m.Injector(m.Store, pn)
}

// Declarations (and variable initializations). This must happen
// after all files are preprocessed, because value decl may be out of
// order and depend on other files.
Expand Down
60 changes: 40 additions & 20 deletions gnovm/pkg/gnolang/nodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -1089,14 +1089,22 @@ func PackageNameFromFileBody(name string, body string) Name {
return n.PkgName
}

// NOTE: panics if package name is invalid.
// ReadMemPackage initializes a new MemPackage by reading the OS directory
// at dir, and saving it with the given pkgPath (import path).
// The resulting MemPackage will contain the names and content of all *.gno files,
// and additionally README.md, LICENSE, and gno.mod.
thehowl marked this conversation as resolved.
Show resolved Hide resolved
//
// ReadMemPackage does not perform validation aside from the package's name;
// the files are not parsed but their contents are merely stored inside a MemFile.
//
// 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) *std.MemPackage {
files, err := ioutil.ReadDir(dir)
if err != nil {
panic(err)
}
memPkg := &std.MemPackage{Path: pkgPath}
var pkgName Name
allowedFiles := []string{ // make case insensitive?
"gno.mod",
"LICENSE",
Expand All @@ -1105,6 +1113,7 @@ func ReadMemPackage(dir string, pkgPath string) *std.MemPackage {
allowedFileExtensions := []string{
".gno",
}
var pkgName Name
for _, file := range files {
if file.IsDir() ||
strings.HasPrefix(file.Name(), ".") ||
Expand All @@ -1116,6 +1125,7 @@ func ReadMemPackage(dir string, pkgPath string) *std.MemPackage {
if err != nil {
panic(err)
}
// XXX: should check that all pkg names are the same (else package is invalid)
if pkgName == "" && strings.HasSuffix(file.Name(), ".gno") {
pkgName = PackageNameFromFileBody(file.Name(), string(bz))
if strings.HasSuffix(string(pkgName), "_test") {
Expand All @@ -1133,33 +1143,29 @@ func ReadMemPackage(dir string, pkgPath string) *std.MemPackage {
return memPkg
}

func PrecompileMemPackage(memPkg *std.MemPackage) error {
return nil
}

// Returns the code fileset minus any spurious or test files.
// ParseMemPackage executes [ParseFile] on each file of the memPkg, excluding
// test and spurious (non-gno) files. The resulting *FileSet is returned.
//
// If one of the files has a different package name than memPkg.Name,
// or [ParseFile] returns an error, ParseMemPackage panics.
func ParseMemPackage(memPkg *std.MemPackage) (fset *FileSet) {
fset = &FileSet{}
for _, mfile := range memPkg.Files {
if !strings.HasSuffix(mfile.Name, ".gno") {
continue // skip spurious file.
if !strings.HasSuffix(mfile.Name, ".gno") ||
endsWith(mfile.Name, []string{"_test.gno", "_filetest.gno"}) {
continue // skip spurious or test file.
}
n, err := ParseFile(mfile.Name, mfile.Body)
if err != nil {
panic(errors.Wrap(err, "parsing file "+mfile.Name))
}
if strings.HasSuffix(mfile.Name, "_test.gno") {
// skip test file.
} else if strings.HasSuffix(mfile.Name, "_filetest.gno") {
// skip test file.
} else if memPkg.Name == string(n.PkgName) {
// add package file.
fset.AddFiles(n)
} else {
if memPkg.Name != string(n.PkgName) {
panic(fmt.Sprintf(
"expected package name [%s] or [%s_test] but got [%s]",
memPkg.Name, memPkg.Name, n.PkgName))
"expected package name [%s] but got [%s]",
memPkg.Name, n.PkgName))
}
// add package file.
fset.AddFiles(n)
}
return fset
}
Expand Down Expand Up @@ -1369,6 +1375,20 @@ func (x *PackageNode) DefineNative(n Name, ps, rs FieldTypeExprs, native func(*M
if native == nil {
panic("DefineNative expects a function, but got nil")
}

if v := x.GetValueRef(nil, n); v != nil {
// redefinition
fv, ok := v.V.(*FuncValue)
if !ok {
panic("cannot redefine non-function as native function")
}
fmt.Printf("REDEFINE %s %p\n", n, fv)
// XXX: type-check
fv.body = nil
fv.nativeBody = native
return
}

fd := FuncD(n, ps, rs, nil)
fd = Preprocess(nil, x, fd).(*FuncDecl)
ft := evalStaticType(nil, x, &fd.Type).(*FuncType)
Expand Down Expand Up @@ -1648,7 +1668,6 @@ func (sb *StaticBlock) GetStaticTypeOfAt(store Store, path ValuePath) Type {
path.Depth -= 1
}
}
panic("should not happen")
}

// Implements BlockNode.
Expand Down Expand Up @@ -2016,6 +2035,7 @@ const (
)

// TODO: consider length restrictions.
// If this function is changed, ReadMemPackage's documentation should be updated accordingly.
func validatePkgName(name string) {
if nameOK, _ := regexp.MatchString(
`^[a-z][a-z0-9_]+$`, name); !nameOK {
Expand Down
3 changes: 3 additions & 0 deletions gnovm/pkg/gnolang/op_call.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ func (m *Machine) doOpCall() {
clo := fr.Func.GetClosure(m.Store)
b := m.Alloc.NewBlock(fr.Func.GetSource(m.Store), clo)
m.PushBlock(b)
if fv.Name == "now" {
fmt.Printf("CALL NOW %p %#v\n", fv, fv)
}
if fv.nativeBody == nil {
fbody := fv.GetBodyFromSource(m.Store)
if len(ft.Results) == 0 {
Expand Down
3 changes: 3 additions & 0 deletions gnovm/stdlibs/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@ type ExecContext struct {
OrigSendSpent *std.Coins // mutable
Banker Banker
}

func (e ExecContext) GetTimestamp() int64 { return e.Timestamp }
func (e ExecContext) GetTimestampNano() int64 { return e.TimestampNano }
8 changes: 1 addition & 7 deletions gnovm/stdlibs/crypto/sha256/sha256.gno
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
package sha256

import (
isha256 "internal/crypto/sha256"
)

const Size = 32

// Sum returns the SHA-256 checksum of the data.
func Sum256(data []byte) [Size]byte {
return isha256.Sum256(data)
}
func Sum256(data []byte) [Size]byte
thehowl marked this conversation as resolved.
Show resolved Hide resolved
7 changes: 7 additions & 0 deletions gnovm/stdlibs/crypto/sha256/sha256.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package sha256

import "crypto/sha256"

func Sum256(data []byte) [32]byte {
return sha256.Sum256(data)
}
thehowl marked this conversation as resolved.
Show resolved Hide resolved
3 changes: 0 additions & 3 deletions gnovm/stdlibs/internal/crypto/sha256/sha256.gno

This file was deleted.

Loading