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 19 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
19 changes: 19 additions & 0 deletions .github/workflows/misc.yml → .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,22 @@ jobs:
env -C $path go mod tidy -v || exit 1
done
echo "$sums" | sha256sum -c
generated:
runs-on: ubuntu-latest
steps:
- name: Install Go
uses: actions/setup-go@v4
with:
go-version: 1.20.x

- name: Checkout code
uses: actions/checkout@v3

- name: Check generated files are up to date
run: |
go generate -v ./...
if [ "$(git status -s)" != "" ]; then
echo "command 'go generate' creates file that differ from git tree, please run 'go generate' and commit:"
git status -s
exit 1
fi
4 changes: 4 additions & 0 deletions examples/gno.land/p/demo/avl/z_0_filetest.gno
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,8 @@ func main() {
// "FileName": "main.gno",
// "IsMethod": false,
// "Name": "init.0",
// "NativeName": "",
// "NativePkg": "",
// "PkgPath": "gno.land/r/test",
// "Source": {
// "@type": "/gno.RefNode",
Expand Down Expand Up @@ -301,6 +303,8 @@ func main() {
// "FileName": "main.gno",
// "IsMethod": false,
// "Name": "main",
// "NativeName": "",
// "NativePkg": "",
// "PkgPath": "gno.land/r/test",
// "Source": {
// "@type": "/gno.RefNode",
Expand Down
4 changes: 4 additions & 0 deletions examples/gno.land/p/demo/avl/z_1_filetest.gno
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,8 @@ func main() {
// "FileName": "main.gno",
// "IsMethod": false,
// "Name": "init.0",
// "NativeName": "",
// "NativePkg": "",
// "PkgPath": "gno.land/r/test",
// "Source": {
// "@type": "/gno.RefNode",
Expand Down Expand Up @@ -325,6 +327,8 @@ func main() {
// "FileName": "main.gno",
// "IsMethod": false,
// "Name": "main",
// "NativeName": "",
// "NativePkg": "",
// "PkgPath": "gno.land/r/test",
// "Source": {
// "@type": "/gno.RefNode",
Expand Down
8 changes: 4 additions & 4 deletions examples/gno.land/r/demo/boards/z_4_filetest.gno
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ func main() {
// "Escaped": true,
// "ObjectID": "336074805fc853987abe6f7fe3ad97a6a6f3077a:2"
// },
// "Index": "188",
// "Index": "189",
// "TV": null
// }
// }
Expand Down Expand Up @@ -541,7 +541,7 @@ func main() {
// },
// "V": {
// "@type": "/gno.RefValue",
// "Hash": "8164abed5231309c88497013f7da72a1b5d427b0",
// "Hash": "25ffc45509708ca0ae17271cb4c3a1dfb367b965",
// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:115"
// }
// },
Expand Down Expand Up @@ -847,7 +847,7 @@ func main() {
// },
// "V": {
// "@type": "/gno.RefValue",
// "Hash": "5b4b593f1d4b37cb99166247ea28174f91087fdd",
// "Hash": "a8e67b9881af89ca2ec2f05778bf7528a54a5833",
// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:82"
// }
// },
Expand All @@ -865,7 +865,7 @@ func main() {
// },
// "V": {
// "@type": "/gno.RefValue",
// "Hash": "7e9fd9bb5e90a06c7751585cd80f23aedddde25b",
// "Hash": "d8ae14a4620e3c6dedabd76cd0c5d7e3c205d647",
// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:83"
// }
// },
Expand Down
4 changes: 3 additions & 1 deletion gnovm/pkg/gnolang/gno_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import (
// run empty main().
func TestRunEmptyMain(t *testing.T) {
m := NewMachine("test", nil)
main := FuncD("main", nil, nil, nil)
// []Stmt{} != nil, as nil means that in the source code not even the
// brackets are present and is reserved for external (ie. native) functions.
thehowl marked this conversation as resolved.
Show resolved Hide resolved
main := FuncD("main", nil, nil, []Stmt{})
m.RunDeclaration(main)
m.RunMain()
}
Expand Down
15 changes: 10 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,10 @@ 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
}
return &FuncDecl{
IsMethod: isMethod,
Recv: recv,
Expand Down
12 changes: 12 additions & 0 deletions gnovm/pkg/gnolang/gonative.go
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,9 @@ func go2GnoValueUpdate(alloc *Allocator, rlm *Realm, lvl int, tv *TypedValue, rv
return
}

// used for direct comparison to error types
var tError = reflect.TypeOf(new(error)).Elem()
thehowl marked this conversation as resolved.
Show resolved Hide resolved

// If recursive is false, this function is like go2GnoValue() but less lazy
// (but still not recursive/eager). When recursive is false, it is for
// converting Go types to Gno types upon an explicit conversion (via
Expand Down Expand Up @@ -757,6 +760,15 @@ func go2GnoValue2(alloc *Allocator, store Store, rv reflect.Value, recursive boo
// regardless.
tv.V = alloc.NewNative(rv)
case reflect.Interface:
// special case for errors, which are very often used esepcially in
thehowl marked this conversation as resolved.
Show resolved Hide resolved
// native bindings
if rv.Type() == tError {
tv.T = gErrorType
if !rv.IsNil() {
tv.V = alloc.NewNative(rv.Elem())
}
return
}
panic("not yet implemented")
thehowl marked this conversation as resolved.
Show resolved Hide resolved
case reflect.Map:
panic("not yet implemented")
Expand Down
103 changes: 87 additions & 16 deletions gnovm/pkg/gnolang/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,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,12 +62,14 @@ 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.
Expand All @@ -86,6 +89,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 @@ -147,13 +155,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 All @@ -180,10 +186,19 @@ func (m *Machine) SetActivePackage(pv *PackageValue) {

// Upon restart, preprocess all MemPackage and save blocknodes.
// This is a temporary measure until we optimize/make-lazy.
//
// NOTE: package paths not beginning with gno.land will be allowed to override,
// to support cases of stdlibs processed through [RunMemPackagesWithOverrides].
func (m *Machine) PreprocessAllFilesAndSaveBlockNodes() {
ch := m.Store.IterMemPackage()
for memPkg := range ch {
fset := ParseMemPackage(memPkg)
// Implicitly do filterDuplicates for stdlibs.
// This is done as this function is at the time of writing only used
// in tests, ie. where we use the "override" feature for stdlibs.
if !strings.HasPrefix(memPkg.Path, "gno.land/") {
filterDuplicates(fset)
}
pn := NewPackageNode(Name(memPkg.Name), memPkg.Path, fset)
m.Store.SetBlockNode(pn)
PredefineFileSet(m.Store, pn, fset)
Expand Down Expand Up @@ -214,8 +229,23 @@ func (m *Machine) PreprocessAllFilesAndSaveBlockNodes() {
// and corresponding package node, package value, and types to store. Save
// is set to false for tests where package values may be native.
func (m *Machine) RunMemPackage(memPkg *std.MemPackage, save bool) (*PackageNode, *PackageValue) {
return m.runMemPackage(memPkg, save, false)
}

// RunMemPackageWithOverrides works as [RunMemPackage], however after parsing,
// declarations are filtered removing duplicate declarations.
// To control which declaration overrides which, use [ReadMemPackageFromList],
// putting the overrides at the top of the list.
func (m *Machine) RunMemPackageWithOverrides(memPkg *std.MemPackage, save bool) (*PackageNode, *PackageValue) {
return m.runMemPackage(memPkg, save, true)
}

func (m *Machine) runMemPackage(memPkg *std.MemPackage, save, overrides bool) (*PackageNode, *PackageValue) {
// parse files.
files := ParseMemPackage(memPkg)
if overrides {
filterDuplicates(files)
}
// make and set package if doesn't exist.
pn := (*PackageNode)(nil)
pv := (*PackageValue)(nil)
Expand All @@ -242,6 +272,47 @@ func (m *Machine) RunMemPackage(memPkg *std.MemPackage, save bool) (*PackageNode
return pn, pv
}

func filterDuplicates(fset *FileSet) {
defined := make(map[Name]struct{}, 128)
for _, f := range fset.Files {
for i := 0; i < len(f.Decls); i++ {
d := f.Decls[i]
var name Name
switch d := d.(type) {
case *FuncDecl:
name = d.Name
if d.IsMethod {
name = Name(derefStar(d.Recv.Type).String()) + "." + name
}
case *TypeDecl:
name = d.Name
case *ValueDecl:
if len(d.NameExprs) == 1 {
name = d.NameExprs[0].Name
} else {
// TODO: support multiple names
continue
}
thehowl marked this conversation as resolved.
Show resolved Hide resolved
default:
continue
}
if _, ok := defined[name]; ok {
f.Decls = append(f.Decls[:i], f.Decls[i+1:]...)
i--
} else {
defined[name] = struct{}{}
}
}
}
}

func derefStar(x Expr) Expr {
thehowl marked this conversation as resolved.
Show resolved Hide resolved
if x, ok := x.(*StarExpr); ok {
return x.X
}
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
Expand Down
Loading
Loading