diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index bcf4251abec..f13ce49ef45 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -31,7 +31,6 @@ /examples/gno.land/p/demo/seqid/ @thehowl /examples/gno.land/p/demo/ownable/ @leohhhn /examples/gno.land/p/demo/pausable/ @leohhhn -/examples/gno.land/p/demo/stack/ @harry-hov /examples/gno.land/p/demo/svg/ @moul /examples/gno.land/p/demo/tamagotchi/ @moul /examples/gno.land/p/demo/ui/ @moul @@ -64,11 +63,11 @@ /gnovm/ @jaekwon @moul @piux2 @thehowl /gnovm/stdlibs/ @thehowl /gnovm/tests/ @jaekwon @deelawn @thehowl @mvertes -/gnovm/cmd/gno/ @moul @thehowl @harry-hov +/gnovm/cmd/gno/ @moul @thehowl /gnovm/pkg/gnolang/ @jaekwon @moul @piux2 @deelawn /gnovm/pkg/doc/ @thehowl /gnovm/pkg/repl/ @mvertes @ajnavarro -/gnovm/pkg/gnomod/ @harry-hov +/gnovm/pkg/gnomod/ @thehowl /gnovm/pkg/gnoenv/ @gfanton /gnovm/pkg/transpiler/ @thehowl /gnovm/pkg/integration/ @gfanton @@ -91,5 +90,6 @@ /CONTRIBUTING.md @jaekwon @moul @gnolang/tech-staff /LICENSE.md @jaekwon /.github/ @moul @gnolang/tech-staff +/.github/workflows @ajnavarro @moul /.github/CODEOWNERS @jaekwon @moul /go.mod @gnolang/tech-staff # no unnecessary dependencies diff --git a/.github/workflows/docs-404-checker.yml b/.github/workflows/docs-404-checker.yml new file mode 100644 index 00000000000..0fa8985366c --- /dev/null +++ b/.github/workflows/docs-404-checker.yml @@ -0,0 +1,31 @@ +name: "docs / 404 checker" + +on: + push: + paths: + - master + pull_request: + paths: + - "docs/**" + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: '1.21' + + - name: Install dependencies + run: go mod download + + - name: Build docs + run: make -C docs/ build + + - name: Run linter + run: make -C docs/ lint \ No newline at end of file diff --git a/.github/workflows/gnoland.yml b/.github/workflows/gnoland.yml index 97df3f13a2a..d84e9f62bd6 100644 --- a/.github/workflows/gnoland.yml +++ b/.github/workflows/gnoland.yml @@ -5,9 +5,11 @@ on: branches: - master workflow_dispatch: - pull_request: + pull_request: paths: - "gno.land/**" + - "tm2/**.go" + - "gnovm/**.go" - ".github/**" jobs: @@ -17,4 +19,4 @@ jobs: with: modulepath: "gno.land" secrets: - codecov-token: ${{ secrets.CODECOV_TOKEN }} \ No newline at end of file + codecov-token: ${{ secrets.CODECOV_TOKEN }} diff --git a/contribs/Makefile b/contribs/Makefile index 04b607cb32d..5a2a00fc2ef 100644 --- a/contribs/Makefile +++ b/contribs/Makefile @@ -31,27 +31,27 @@ install: @echo 'To install a tool, go to the subdirectory, then run `make install`.' @echo 'To do a full installation, run `make install_all`.' -install_all: $(addsuffix .install,$(programs)) -%.install: - @echo "[+] make -C $(basename $@) install" - $(MAKE) --no-print-directory -C $(basename $@) install +install_all: $(addprefix install.,$(programs)) +install.%: + @echo "[+] make -C $(subst install.,,$@) install" + $(MAKE) --no-print-directory -C $(subst install.,,$@) install .PHONY: install_all ######################################## # Test suite -test: $(addsuffix .test,$(programs)) -%.test: - @echo "[+] make -C $(basename $@) install" - $(MAKE) --no-print-directory -C $(basename $@) test -.PHONY: test +test: $(addprefix test.,$(programs)) +test.%: + @echo "[+] make -C $(subst test.,,$@) install" + $(MAKE) --no-print-directory -C $(subst test.,,$@) test +.PHONY: test ######################################## # Lint .PHONY: lint -lint: $(addsuffix .lint,$(programs)) -%.lint: - @echo "[+] make -C $(basename $@) install" - $(MAKE) --no-print-directory -C $(basename $@) lint +lint: $(addprefix lint.,$(programs)) +lint.%: + @echo "[+] make -C $(subst lint.,,$@) install" + $(MAKE) --no-print-directory -C $(subst lint.,,$@) lint ######################################## # Dev tools diff --git a/contribs/gnodev/pkg/dev/node.go b/contribs/gnodev/pkg/dev/node.go index d839091d328..cede02d0ebc 100644 --- a/contribs/gnodev/pkg/dev/node.go +++ b/contribs/gnodev/pkg/dev/node.go @@ -237,7 +237,7 @@ func (n *Node) updatePackages(paths ...string) error { pkgsUpdated += len(pkgslist) } - n.logger.Info(fmt.Sprintf("updated %d pacakges", pkgsUpdated)) + n.logger.Info(fmt.Sprintf("updated %d packages", pkgsUpdated)) return nil } diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000000..e5bf557ef75 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,12 @@ +all: build lint + +# Build the linter +build: + cd ../misc/docs-linter && go build -o ./build/ + +# Run the linter for the docs/ folder +lint: + ../misc/docs-linter/build/linter -path . + +clean: + rm -rf ../misc/docs-linter/build \ No newline at end of file diff --git a/docs/assets/how-to-guides/write-simple-dapp/poll-1.gno b/docs/assets/how-to-guides/write-simple-dapp/poll-1.gno index bf2a64743d0..98b0ae4ed7a 100644 --- a/docs/assets/how-to-guides/write-simple-dapp/poll-1.gno +++ b/docs/assets/how-to-guides/write-simple-dapp/poll-1.gno @@ -64,6 +64,8 @@ func (p Poll) VoteCount() (int, int) { if vote == true { yay = yay + 1 } + + return false }) return yay, p.Voters().Size() - yay } diff --git a/docs/concepts/effective-gno.md b/docs/concepts/effective-gno.md index d08a9089487..8e589f41845 100644 --- a/docs/concepts/effective-gno.md +++ b/docs/concepts/effective-gno.md @@ -679,7 +679,7 @@ For example, if you're creating a coin for cross-chain transfers, Coins are your best bet. They're IBC-ready and their strict rules offer top-notch security. -Read about how to use the Banker module [here](stdlibs/banker). +Read about how to use the Banker module [here](stdlibs/banker.md). #### GRC20 tokens diff --git a/docs/concepts/packages.md b/docs/concepts/packages.md index 79f54d4f59e..cd3e2ace96a 100644 --- a/docs/concepts/packages.md +++ b/docs/concepts/packages.md @@ -12,7 +12,7 @@ The full list of pre-deployed available packages can be found under the [demo pa In Go, the classic key/value data type is represented by the `map` construct. However, while Gno also supports the use of `map`, it is not a viable option as it lacks determinism due to its non-sequential nature. -To address this issue, Gno implements the [AVL Tree](https://en.wikipedia.org/wiki/AVL\_tree) (Adelson-Velsky-Landis Tree) as a solution. The AVL Tree is a self-balancing binary search tree. +To address this issue, Gno implements the [AVL Tree](https://en.wikipedia.org/wiki/AVL_tree) (Adelson-Velsky-Landis Tree) as a solution. The AVL Tree is a self-balancing binary search tree. The `avl` package comprises a set of functions that can manipulate the leaves and nodes of the AVL Tree. @@ -72,7 +72,7 @@ func IsApprovedForAll(owner, operator std.Address) bool * `OwnerOf`: Returns the `owner`'s address of a token specified by its `TokenID`. * `SafeTransferFrom`: Equivalent to the `TransferFrom` function of `grc20`. * The `Safe` prefix indicates that the function runs a check to ensure that the `to` address is a valid address that can receive tokens. - * As you can see from the [code](https://github.com/gnolang/gno/blob/master/examples/gno.land/p/demo/grc/grc721/basic\_nft.gno#L341), the concept of `Safe` has yet to be implemented. + * As you can see from the [code](https://github.com/gnolang/gno/blob/master/examples/gno.land/p/demo/grc/grc721/basic_nft.gno#L341), the concept of `Safe` has yet to be implemented. * `SetApprovalForAll`: Approves all tokens owned by the `owner` to an `operator`. * You may not set multiple `operator`s. * `GetApproved`: Returns the `address` of the `operator` for a token, specified with its `ID`. diff --git a/docs/concepts/tendermint2.md b/docs/concepts/tendermint2.md index a6004606a78..4dd43b0819e 100644 --- a/docs/concepts/tendermint2.md +++ b/docs/concepts/tendermint2.md @@ -34,9 +34,7 @@ on https://github.com/tendermint/tendermint2.** proto3 for encoding/decoding optimization through protoc. - MISSION: be the basis for improving the encoding standard from proto3, because proto3 length-prefixing is slow, and we need "proto4" or "amino2". - - LOOK at the auto-generated proto files! - https://github.com/gnolang/gno/blob/master/pkgs/bft/consensus/types/cstypes.proto - for example. + - LOOK at the [auto-generated proto files](https://github.com/gnolang/gno/blob/master/tm2/pkg/bft/consensus/consensus.proto)! - There was work to remove this from the CosmosSDK because Amino wasn't ready, but now that it is, it makes sense to incorporate it into Tendermint2. diff --git a/docs/concepts/testnets.md b/docs/concepts/testnets.md index 7f0734cdc28..dd6d65085cc 100644 --- a/docs/concepts/testnets.md +++ b/docs/concepts/testnets.md @@ -6,7 +6,7 @@ id: testnets This page documents all Gno.land testnets, what their properties are, and how they are meant to be used. For testnet configuration, visit the -[reference section](../reference/network-config). +[reference section](../reference/network-config.md). Gno.land testnets are categorized by 4 main points: - **Persistence of state** diff --git a/docs/getting-started/local-setup/installation.md b/docs/getting-started/local-setup/installation.md index fe96998e239..8700ff9a2b2 100644 --- a/docs/getting-started/local-setup/installation.md +++ b/docs/getting-started/local-setup/installation.md @@ -13,7 +13,7 @@ to run on your machine. ## Prerequisites - **Git** - **`make` (for running Makefiles)** -- **Go 1.19+** +- **Go 1.21+** - **Go Environment Setup**: - Make sure `$GOPATH` is well-defined, and `$GOPATH/bin` is added to your `$PATH` variable. - To do this, you can add the following line to your `.bashrc`, `.zshrc` or other config file: diff --git a/docs/how-to-guides/connecting-from-go.md b/docs/how-to-guides/connecting-from-go.md index cb6ed0547b6..29067fe0ca1 100644 --- a/docs/how-to-guides/connecting-from-go.md +++ b/docs/how-to-guides/connecting-from-go.md @@ -109,7 +109,10 @@ A few things to note: You can initialize the RPC Client used to connect to the Gno.land network with the following line: ```go -rpc := rpcclient.NewHTTPClient("") +rpc, err := rpcclient.NewHTTPClient("") +if err != nil { + panic(err) +} ``` A list of Gno.land network endpoints & chain IDs can be found in the [Gno RPC @@ -139,7 +142,10 @@ func main() { } // Initialize the RPC client - rpc := rpcclient.NewHTTPClient("") + rpc, err := rpcclient.NewHTTPClient("") + if err != nil { + panic(err) + } // Initialize the gnoclient client := gnoclient.Client{ @@ -158,6 +164,13 @@ To send transactions to the chain, we need to know the account number (ID) and sequence (nonce). We can get this information by querying the chain with the `QueryAccount` function: +```go +import ( + ... + "github.com/gnolang/gno/tm2/pkg/crypto" +) +``` + ```go // Convert Gno address string to `crypto.Address` addr, err := crypto.AddressFromBech32("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") // your Gno address diff --git a/docs/how-to-guides/simple-library.md b/docs/how-to-guides/simple-library.md index 1ae231251d0..923fc98922e 100644 --- a/docs/how-to-guides/simple-library.md +++ b/docs/how-to-guides/simple-library.md @@ -34,7 +34,7 @@ and [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=har ::: We discussed Gno folder structures more in detail in -the [simple Smart Contract guide](simple-contract.md#1-setting-up-the-work-directory). +the [simple Smart Contract guide](simple-contract.md#local-setup). For now, we will just follow some rules outlined there. Create the main working directory for our Package: diff --git a/docs/how-to-guides/write-simple-dapp.md b/docs/how-to-guides/write-simple-dapp.md index a46d6688fa9..851320b2a61 100644 --- a/docs/how-to-guides/write-simple-dapp.md +++ b/docs/how-to-guides/write-simple-dapp.md @@ -94,6 +94,8 @@ func (p Poll) VoteCount() (int, int) { if vote == true { yay = yay + 1 } + + return false }) return yay, p.Voters().Size() - yay } diff --git a/docs/reference/network-config.md b/docs/reference/network-config.md index c2ec5409fc9..0da179e8f17 100644 --- a/docs/reference/network-config.md +++ b/docs/reference/network-config.md @@ -4,12 +4,12 @@ id: network-config # Network configurations -| Network | RPC Endpoint | Chain ID | -|-------------|------------------------------------|---------------| -| Portal Loop | https://rpc.gno.land:443 | `portal-loop` | -| Testnet 4 | upcoming | upcoming | -| Testnet 3 | https://rpc.test3.gno.land:443 | `test3` | -| Staging | https://rpc.staging.gno.land:26657 | `test3` | +| Network | RPC Endpoint | Chain ID | +|-------------|-----------------------------------|---------------| +| Portal Loop | https://rpc.gno.land:443 | `portal-loop` | +| Testnet 4 | upcoming | upcoming | +| Testnet 3 | https://rpc.test3.gno.land:443 | `test3` | +| Staging | http://rpc.staging.gno.land:26657 | `staging` | ### WebSocket endpoints All networks follow the same pattern for websocket connections: diff --git a/docs/reference/stdlibs/std/testing.md b/docs/reference/stdlibs/std/testing.md index 102b9ed6d70..7a383478ef5 100644 --- a/docs/reference/stdlibs/std/testing.md +++ b/docs/reference/stdlibs/std/testing.md @@ -10,16 +10,22 @@ func TestSetOrigCaller(addr Address) func TestSetOrigPkgAddr(addr Address) func TestSetOrigSend(sent, spent Coins) func TestIssueCoins(addr Address, coins Coins) +func TestSetRealm(realm Realm) +func NewUserRealm(address Address) +func NewCodeRealm(pkgPath string) ``` --- ## TestSkipHeights + ```go func TestSkipHeights(count int64) ``` Modifies the block height variable by skipping **count** blocks. +It also increases block timestamp by 5 seconds for every single count + #### Usage ```go std.TestSkipHeights(100) @@ -27,6 +33,7 @@ std.TestSkipHeights(100) --- ## TestSetOrigCaller + ```go func TestSetOrigCaller(addr Address) ``` @@ -34,23 +41,26 @@ Sets the current caller of the transaction to **addr**. #### Usage ```go -std.TestSetOrigCaller("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") +std.TestSetOrigCaller(std.Address("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5")) ``` --- ## TestSetOrigPkgAddr + ```go func TestSetOrigPkgAddr(addr Address) ``` -Sets the current realm/package address to **addr**. +Sets the call entry realm address to **addr**. #### Usage ```go -std.TestSetOrigPkgAddr("g1ecely4gjy0yl6s9kt409ll330q9hk2lj9ls3ec") +std.TestSetOrigPkgAddr(std.Address("g1ecely4gjy0yl6s9kt409ll330q9hk2lj9ls3ec")) ``` + --- ## TestSetOrigSend + ```go func TestSetOrigSend(sent, spent Coins) ``` @@ -63,17 +73,78 @@ std.TestSetOrigSend(sent, spent Coins) --- ## TestIssueCoins + ```go func TestIssueCoins(addr Address, coins Coins) ``` + Issues testing context **coins** to **addr**. + #### Usage + ```go issue := std.Coins{{"coin1", 100}, {"coin2", 200}} -addr := "g1ecely4gjy0yl6s9kt409ll330q9hk2lj9ls3ec" +addr := std.Address("g1ecely4gjy0yl6s9kt409ll330q9hk2lj9ls3ec") std.TestIssueCoins(addr, issue) ``` +--- + +## TestSetRealm + +```go +func TestSetRealm(rlm Realm) +``` + +Sets the realm for the current frame. After calling `TestSetRealm()`, calling +[`CurrentRealm()`](chain.md#currentrealm) in the same test function will yield the value of `rlm`, and +any `PrevRealm()` called from a function used after TestSetRealm will yield `rlm`. + +Should be used in combination with [`NewUserRealm`](#newuserrealm) & +[`NewCodeRealm`](#newcoderealm). + +#### Usage +```go +addr := std.Address("g1ecely4gjy0yl6s9kt409ll330q9hk2lj9ls3ec") +std.TestSetRealm(std.NewUserRealm("")) +// or +std.TestSetRealm(std.NewCodeRealm("gno.land/r/demo/users")) +``` + +--- + +## NewUserRealm + +```go +func NewUserRealm(address Address) Realm +``` + +Creates a new user realm for testing purposes. + +#### Usage +```go +addr := std.Address("g1ecely4gjy0yl6s9kt409ll330q9hk2lj9ls3ec") +userRealm := std.NewUserRealm(addr) +``` + +--- + +## NewCodeRealm + +```go +func NewCodeRealm(pkgPath string) +``` + +Creates a new code realm for testing purposes. + +#### Usage +```go +path := "gno.land/r/demo/boards" +codeRealm := std.NewCodeRealm(path) +``` + + + diff --git a/examples/gno.land/p/demo/avl/z_0_filetest.gno b/examples/gno.land/p/demo/avl/z_0_filetest.gno index e91788ac8eb..e3f1363e205 100644 --- a/examples/gno.land/p/demo/avl/z_0_filetest.gno +++ b/examples/gno.land/p/demo/avl/z_0_filetest.gno @@ -25,67 +25,25 @@ func main() { // Realm: // switchrealm["gno.land/r/test"] // u[a8ada09dee16d791fd406d629fe29bb0ed084a30:4]={ -// "Fields": [ -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// }, -// "V": { -// "@type": "/gno.StringValue", -// "value": "key0" -// } -// }, -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// }, -// "V": { -// "@type": "/gno.StringValue", -// "value": "value0" -// } -// }, -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "64" -// } -// }, -// { -// "N": "AQAAAAAAAAA=", -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "32" -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// } -// } -// ], // "ObjectInfo": { // "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", -// "ModTime": "5", -// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", +// "ModTime": "7", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", // "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "627e8e517e7ae5db0f3b753e2a32b607989198b6", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5" +// } // } // } -// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:6]={ +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:9]={ // "Fields": [ // { // "T": { @@ -140,13 +98,32 @@ func main() { // } // ], // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9", // "ModTime": "0", -// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8", // "RefCount": "1" // } // } -// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:5]={ +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:8]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", +// "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "b28057ab7be6383785c0a5503e8a531bdbc21851", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9" +// } +// } +// } +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:7]={ // "Fields": [ // { // "T": { @@ -183,19 +160,13 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "6da365f0d6cacbcdf53cd5a4b125803cddce08c2", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "091729e38bda8724bce4c314f9624b91af679459", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" -// } -// } +// "TV": null // } // }, // { @@ -208,27 +179,40 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "f216afe7b5a17f4ebdbb98dceccedbc22e237596", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "0b5493aa4ea42087780bdfcaebab2c3eec351c15", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" -// } -// } +// "TV": null // } // } // ], // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6", +// "RefCount": "1" +// } +// } +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:6]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6", // "ModTime": "0", // "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", // "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "ff1a50d8489090af37a2c7766d659f0d717939b5", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7" +// } // } // } // u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ @@ -236,7 +220,7 @@ func main() { // "ObjectInfo": { // "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", // "IsEscaped": true, -// "ModTime": "4", +// "ModTime": "5", // "RefCount": "2" // }, // "Parent": null, @@ -333,19 +317,13 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "ae86874f9b47fa5e64c30b3e92e9d07f2ec967a4", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "6c9948281d4c60b2d95233b76388d54d8b1a2fad", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5" -// } -// } +// "TV": null // } // } // ] diff --git a/examples/gno.land/p/demo/avl/z_1_filetest.gno b/examples/gno.land/p/demo/avl/z_1_filetest.gno index cdd56a5ad89..a6d2205e240 100644 --- a/examples/gno.land/p/demo/avl/z_1_filetest.gno +++ b/examples/gno.land/p/demo/avl/z_1_filetest.gno @@ -24,7 +24,7 @@ func main() { // Realm: // switchrealm["gno.land/r/test"] -// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:9]={ +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:15]={ // "Fields": [ // { // "T": { @@ -79,13 +79,32 @@ func main() { // } // ], // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:15", // "ModTime": "0", -// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:14", // "RefCount": "1" // } // } -// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:8]={ +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:14]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:14", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:13", +// "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "143aebc820da33550f7338723fb1e2eec575b196", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:15" +// } +// } +// } +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:13]={ // "Fields": [ // { // "T": { @@ -122,19 +141,13 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "2f3adc5d0f2a3fe0331cfa93572a7abdde14c9aa", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "7a8a63e17a567d7b0891ac89d5cd90072a73787d", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" -// } -// } +// "TV": null // } // }, // { @@ -147,30 +160,43 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "2e733a8e9e74fe14f0a5d10fb0f6728fa53d052d", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:14" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "ab5a297f4eb033d88bdf1677f4dc151ccb9fde9f", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9" -// } -// } +// "TV": null // } // } // ], // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:13", // "ModTime": "0", -// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:12", // "RefCount": "1" // } // } -// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:7]={ +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:12]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:12", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:11", +// "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "fe20a19f956511f274dc77854e9e5468387260f4", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:13" +// } +// } +// } +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:11]={ // "Fields": [ // { // "T": { @@ -207,19 +233,13 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "c89a71bdf045e8bde2059dc9d33839f916e02e5d", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "627e8e517e7ae5db0f3b753e2a32b607989198b6", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5" -// } -// } +// "TV": null // } // }, // { @@ -232,27 +252,40 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "90fa67f8c47db4b9b2a60425dff08d5a3385100f", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:12" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "fe8afd501233fb95375016199f0443b3c6ab1fbc", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8" -// } -// } +// "TV": null // } // } // ], // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:11", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:10", +// "RefCount": "1" +// } +// } +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:10]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:10", // "ModTime": "0", // "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", // "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "83e42caaf53070dd95b5f859053eb51ed900bbda", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:11" +// } // } // } // u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ @@ -260,7 +293,7 @@ func main() { // "ObjectInfo": { // "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", // "IsEscaped": true, -// "ModTime": "6", +// "ModTime": "9", // "RefCount": "2" // }, // "Parent": null, @@ -357,21 +390,16 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "1faa9fa4ba1935121a6d3f0a623772e9d4499b0a", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:10" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "c5eefc40ed065461b4a920c1349ed734ffdead8f", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7" -// } -// } +// "TV": null // } // } // ] // } // d[a8ada09dee16d791fd406d629fe29bb0ed084a30:4] +// d[a8ada09dee16d791fd406d629fe29bb0ed084a30:5] diff --git a/examples/gno.land/p/demo/avl/z_2_filetest.gno b/examples/gno.land/p/demo/avl/z_2_filetest.gno index 65181bffcac..1d3f20651ed 100644 --- a/examples/gno.land/p/demo/avl/z_2_filetest.gno +++ b/examples/gno.land/p/demo/avl/z_2_filetest.gno @@ -23,7 +23,7 @@ func main() { // Realm: // switchrealm["gno.land/r/test"] -// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:10]={ +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:16]={ // "Fields": [ // { // "T": { @@ -78,13 +78,32 @@ func main() { // } // ], // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:10", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:16", // "ModTime": "0", -// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:15", // "RefCount": "1" // } // } -// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:9]={ +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:15]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:15", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:14", +// "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "db333c89cd6773709e031f1f4e4ed4d3fed66c11", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:16" +// } +// } +// } +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:14]={ // "Fields": [ // { // "T": { @@ -121,19 +140,13 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "849a50d6c78d65742752e3c89ad8dd556e2e63cb", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "213baed7e3326f2403b5f30e5d4397510ba4f37d", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7" -// } -// } +// "TV": null // } // }, // { @@ -146,30 +159,43 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "b4fc2fdd2d0fe936c87ed2ace97136cffeed207f", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:15" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "be751422ef4c2bc068a456f9467d2daca27db8ca", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:10" -// } -// } +// "TV": null // } // } // ], // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:14", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:13", +// "RefCount": "1" +// } +// } +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:13]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:13", // "ModTime": "0", -// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:12", // "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "a1160b0060ad752dbfe5fe436f7734bb19136150", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:14" +// } // } // } -// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:8]={ +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:12]={ // "Fields": [ // { // "T": { @@ -206,19 +232,13 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "fd95e08763159ac529e26986d652e752e78b6325", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "af4d0b158681d85eb2a7f6888b39a05ca7b790ee", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" -// } -// } +// "TV": null // } // }, // { @@ -231,27 +251,40 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "3ecdcf148fe2f9e97b72a3bedf303b2ba56d4f4b", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:13" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "ef853d70e334fd2c807d6c2c751da1fcd1e5ad58", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9" -// } -// } +// "TV": null // } // } // ], // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:12", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:11", +// "RefCount": "1" +// } +// } +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:11]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:11", // "ModTime": "0", // "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", // "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "63126557dba88f8556f7a0ccbbfc1d218ae7a302", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:12" +// } // } // } // u[a8ada09dee16d791fd406d629fe29bb0ed084a30:4]={ @@ -266,27 +299,22 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "425bc3bff1f4c36b175d055ed8b2c289123fcca1", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:11" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "3a5af0895c2c45b8a5e894644bcd689f1fdc4785", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8" -// } -// } +// "TV": null // } // } // ], // "ObjectInfo": { // "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", -// "ModTime": "7", +// "ModTime": "10", // "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", // "RefCount": "1" // } // } // d[a8ada09dee16d791fd406d629fe29bb0ed084a30:5] +// d[a8ada09dee16d791fd406d629fe29bb0ed084a30:6] diff --git a/examples/gno.land/p/demo/memeland/memeland_test.gno b/examples/gno.land/p/demo/memeland/memeland_test.gno index 76c44327993..fbfcfec8ae3 100644 --- a/examples/gno.land/p/demo/memeland/memeland_test.gno +++ b/examples/gno.land/p/demo/memeland/memeland_test.gno @@ -6,6 +6,7 @@ import ( "testing" "time" + "gno.land/p/demo/seqid" "gno.land/p/demo/testutils" "gno.land/p/demo/ufmt" ) @@ -97,7 +98,7 @@ func TestGetPostsInRangeByTimestamp(t *testing.T) { // Count the number of posts returned in the JSON string as a rudimentary check for correct pagination/filtering postCount := strings.Count(jsonStr, `"id":"`) - if postCount != m.MemeCounter { + if seqid.ID(postCount) != m.MemeCounter { t.Errorf("Expected %d posts in the JSON string, but found %d", m.MemeCounter, postCount) } @@ -157,7 +158,7 @@ func TestGetPostsInRangeByUpvote(t *testing.T) { // Count the number of posts returned in the JSON string as a rudimentary check for correct pagination/filtering postCount := strings.Count(jsonStr, `"id":"`) - if postCount != m.MemeCounter { + if seqid.ID(postCount) != m.MemeCounter { t.Errorf("Expected %d posts in the JSON string, but found %d", m.MemeCounter, postCount) } diff --git a/examples/gno.land/p/demo/ownable/ownable.gno b/examples/gno.land/p/demo/ownable/ownable.gno index 22c0c33acc6..75ebcde0a28 100644 --- a/examples/gno.land/p/demo/ownable/ownable.gno +++ b/examples/gno.land/p/demo/ownable/ownable.gno @@ -4,6 +4,8 @@ import ( "std" ) +const OwnershipTransferEvent = "OwnershipTransfer" + // Ownable is meant to be used as a top-level object to make your contract ownable OR // being embedded in a Gno object to manage per-object ownership. type Ownable struct { @@ -31,7 +33,13 @@ func (o *Ownable) TransferOwnership(newOwner std.Address) error { return ErrInvalidAddress } + prevOwner := o.owner o.owner = newOwner + std.Emit( + OwnershipTransferEvent, + "from", string(prevOwner), + "to", string(newOwner), + ) return nil } @@ -44,7 +52,15 @@ func (o *Ownable) DropOwnership() error { return err } + prevOwner := o.owner o.owner = "" + + std.Emit( + OwnershipTransferEvent, + "from", string(prevOwner), + "to", "", + ) + return nil } diff --git a/examples/gno.land/p/demo/rand/rand0_filetest.gno b/examples/gno.land/p/demo/rand/rand0_filetest.gno index c5c138924fc..446e04b696d 100644 --- a/examples/gno.land/p/demo/rand/rand0_filetest.gno +++ b/examples/gno.land/p/demo/rand/rand0_filetest.gno @@ -49,8 +49,8 @@ func main() { // 177 // 802 // --- -// 450 -// 78 -// 777 -// 15 -// 339 +// 269 +// 233 +// 591 +// 936 +// 908 diff --git a/examples/gno.land/p/gov/proposal/proposal.gno b/examples/gno.land/p/gov/proposal/proposal.gno index f01d3661a5e..d4f151f935a 100644 --- a/examples/gno.land/p/gov/proposal/proposal.gno +++ b/examples/gno.land/p/gov/proposal/proposal.gno @@ -1,6 +1,10 @@ // Package proposal provides a structure for executing proposals. package proposal +import "std" + +const daoPkgPath = "gno.land/r/gov/dao" // XXX: make it configurable with r/sys/vars? + // NewExecutor creates a new executor with the provided callback function. func NewExecutor(callback func() error) Executor { return &executorImpl{ @@ -21,7 +25,7 @@ func (exec *executorImpl) Execute() error { if exec.done { return ErrAlreadyDone } - // XXX: assertCalledByGovdao + assertCalledByGovdao() err := exec.callback() exec.done = true exec.success = err == nil @@ -37,3 +41,21 @@ func (exec *executorImpl) Done() bool { func (exec *executorImpl) Success() bool { return exec.success } + +func (exec executorImpl) Status() Status { + switch { + case exec.success: + return Success + case exec.done: + return Failed + default: + return NotExecuted + } +} + +func assertCalledByGovdao() { + caller := std.CurrentRealm().PkgPath() + if caller != daoPkgPath { + panic("only gov/dao can execute proposals") + } +} diff --git a/examples/gno.land/p/gov/proposal/types.gno b/examples/gno.land/p/gov/proposal/types.gno index a2ebad6f585..d84d1bfd21f 100644 --- a/examples/gno.land/p/gov/proposal/types.gno +++ b/examples/gno.land/p/gov/proposal/types.gno @@ -8,9 +8,19 @@ import "errors" type Executor interface { Execute() error Done() bool - Success() bool // Done() && !err + Success() bool // Done() && !err + Status() Status // human-readable execution status } // ErrAlreadyDone is the error returned when trying to execute an already // executed proposal. var ErrAlreadyDone = errors.New("already executed") + +// Status enum. +type Status string + +var ( + NotExecuted Status = "not_executed" + Success Status = "success" + Failed Status = "failed" +) diff --git a/examples/gno.land/r/demo/art/gnoface/gno.mod b/examples/gno.land/r/demo/art/gnoface/gno.mod index bc17ee9df3b..6276629cba2 100644 --- a/examples/gno.land/r/demo/art/gnoface/gno.mod +++ b/examples/gno.land/r/demo/art/gnoface/gno.mod @@ -1,3 +1,5 @@ +// Draft + module gno.land/r/demo/art/gnoface require ( diff --git a/examples/gno.land/r/demo/bar20/bar20.gno b/examples/gno.land/r/demo/bar20/bar20.gno new file mode 100644 index 00000000000..7388d87d24d --- /dev/null +++ b/examples/gno.land/r/demo/bar20/bar20.gno @@ -0,0 +1,46 @@ +// Package bar20 is similar to foo20 but exposes a safe-object that can be used +// by `maketx run`, another contract importing foo20, and in the future when +// we'll support `maketx call Token.XXX`. +package bar20 + +import ( + "std" + "strings" + + "gno.land/p/demo/grc/grc20" + "gno.land/p/demo/ufmt" +) + +var ( + banker *grc20.AdminToken // private banker. + Token grc20.IGRC20 // public safe-object. +) + +func init() { + banker = grc20.NewAdminToken("Bar", "BAR", 4) + Token = banker.GRC20() +} + +func Faucet() string { + caller := std.PrevRealm().Addr() + if err := banker.Mint(caller, 1_000_000); err != nil { + return "error: " + err.Error() + } + return "OK" +} + +func Render(path string) string { + parts := strings.Split(path, "/") + c := len(parts) + + switch { + case path == "": + return banker.RenderHome() // XXX: should be Token.RenderHome() + case c == 2 && parts[0] == "balance": + owner := std.Address(parts[1]) + balance, _ := Token.BalanceOf(owner) + return ufmt.Sprintf("%d\n", balance) + default: + return "404\n" + } +} diff --git a/examples/gno.land/r/demo/bar20/bar20_test.gno b/examples/gno.land/r/demo/bar20/bar20_test.gno new file mode 100644 index 00000000000..b2a49ebd864 --- /dev/null +++ b/examples/gno.land/r/demo/bar20/bar20_test.gno @@ -0,0 +1,31 @@ +package bar20 + +import ( + "std" + "testing" + + "gno.land/p/demo/testutils" +) + +func TestPackage(t *testing.T) { + alice := testutils.TestAddress("alice") + std.TestSetRealm(std.NewUserRealm(alice)) + std.TestSetOrigCaller(alice) // XXX: should not need this + + balance, _ := Token.BalanceOf(alice) + expected := uint64(0) + if balance != expected { + t.Errorf("balance should be %d, got %d", expected, balance) + } + + ret := Faucet() + if ret != "OK" { + t.Errorf("faucet should be OK, got %s", ret) + } + + balance, _ = Token.BalanceOf(alice) + expected = uint64(1_000_000) + if balance != expected { + t.Errorf("balance should be %d, got %d", expected, balance) + } +} diff --git a/examples/gno.land/r/demo/bar20/gno.mod b/examples/gno.land/r/demo/bar20/gno.mod new file mode 100644 index 00000000000..fd804beb4c4 --- /dev/null +++ b/examples/gno.land/r/demo/bar20/gno.mod @@ -0,0 +1,7 @@ +module gno.land/r/demo/bar20 + +require ( + gno.land/p/demo/grc/grc20 v0.0.0-latest + gno.land/p/demo/testutils v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest +) diff --git a/examples/gno.land/r/demo/boards/z_4_filetest.gno b/examples/gno.land/r/demo/boards/z_4_filetest.gno index bb98fa289ba..a8b932db892 100644 --- a/examples/gno.land/r/demo/boards/z_4_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_4_filetest.gno @@ -48,83 +48,26 @@ func main() { // Realm: // switchrealm["gno.land/r/demo/users"] // switchrealm["gno.land/r/demo/boards"] -// u[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:101]={ -// "Fields": [ -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// }, -// "V": { -// "@type": "/gno.StringValue", -// "value": "0000000003" -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/boards.Post" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": null, -// "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/boards.Post" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:102" -// } -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "64" -// } -// }, -// { -// "N": "AQAAAAAAAAA=", -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "32" -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// } -// } -// ], +// u[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:111]={ // "ObjectInfo": { -// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:101", -// "ModTime": "109", -// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:109", +// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:111", +// "ModTime": "123", +// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:123", // "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "68663c8895d37d479e417c11e21badfe21345c61", +// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:112" +// } // } // } -// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:110]={ +// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:125]={ // "Fields": [ // { // "T": { @@ -146,19 +89,13 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:126" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/boards.Post" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:111" -// } -// } +// "TV": null // } // }, // { @@ -194,13 +131,32 @@ func main() { // } // ], // "ObjectInfo": { -// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:110", +// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:125", +// "ModTime": "0", +// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:124", +// "RefCount": "1" +// } +// } +// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:124]={ +// "ObjectInfo": { +// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:124", // "ModTime": "0", -// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:109", +// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:123", // "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "3f34ac77289aa1d5f9a2f8b6d083138325816fb0", +// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:125" +// } // } // } -// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:109]={ +// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:123]={ // "Fields": [ // { // "T": { @@ -237,19 +193,13 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "94a6665a44bac6ede7f3e3b87173e537b12f9532", +// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:111" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "b58581159917d8d7ad0992009d7184fc8ca00fcc", -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:101" -// } -// } +// "TV": null // } // }, // { @@ -262,30 +212,43 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "bc8e5b4e782a0bbc4ac9689681f119beb7b34d59", +// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:124" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "fb593e86d35aaf607e0d21e6bd4f84519c44585f", -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:110" -// } -// } +// "TV": null // } // } // ], // "ObjectInfo": { -// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:109", +// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:123", // "ModTime": "0", -// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:96", +// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:122", // "RefCount": "1" // } // } -// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:112]={ +// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:122]={ +// "ObjectInfo": { +// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:122", +// "ModTime": "0", +// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:106", +// "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "9957eadbc91dd32f33b0d815e041a32dbdea0671", +// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:123" +// } +// } +// } +// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:128]={ // "Fields": [ // { // "T": { @@ -298,13 +261,13 @@ func main() { // } // ], // "ObjectInfo": { -// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:112", +// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:128", // "ModTime": "0", -// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:111", +// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:127", // "RefCount": "1" // } // } -// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:113]={ +// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:129]={ // "Fields": [ // { // "T": { @@ -317,13 +280,13 @@ func main() { // } // ], // "ObjectInfo": { -// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:113", +// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:129", // "ModTime": "0", -// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:111", +// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:127", // "RefCount": "1" // } // } -// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:114]={ +// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:130]={ // "Fields": [ // { // "T": { @@ -336,13 +299,13 @@ func main() { // } // ], // "ObjectInfo": { -// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:114", +// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:130", // "ModTime": "0", -// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:111", +// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:127", // "RefCount": "1" // } // } -// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:115]={ +// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:131]={ // "Fields": [ // { // "N": "AAAAgJSeXbo=", @@ -379,13 +342,13 @@ func main() { // } // ], // "ObjectInfo": { -// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:115", +// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:131", // "ModTime": "0", -// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:111", +// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:127", // "RefCount": "1" // } // } -// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:116]={ +// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:132]={ // "Fields": [ // { // "T": { @@ -410,13 +373,13 @@ func main() { // } // ], // "ObjectInfo": { -// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:116", +// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:132", // "ModTime": "0", -// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:111", +// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:127", // "RefCount": "1" // } // } -// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:111]={ +// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:127]={ // "Fields": [ // { // "T": { @@ -428,19 +391,13 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:84" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/boards.Board" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:81" -// } -// } +// "TV": null // } // }, // { @@ -487,8 +444,8 @@ func main() { // }, // "V": { // "@type": "/gno.RefValue", -// "Hash": "130542396d7549d1d516a3ef4a63bb44ef3da06f", -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:112" +// "Hash": "f91e355bd19240f0f3350a7fa0e6a82b72225916", +// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:128" // } // }, // { @@ -498,8 +455,8 @@ func main() { // }, // "V": { // "@type": "/gno.RefValue", -// "Hash": "80acd8746478317194b8546170335c796a4dfb3f", -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:113" +// "Hash": "9ee9c4117be283fc51ffcc5ecd65b75ecef5a9dd", +// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:129" // } // }, // { @@ -509,8 +466,8 @@ func main() { // }, // "V": { // "@type": "/gno.RefValue", -// "Hash": "c1a8f769f3b9d52dd38ac4759116edaca287636f", -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:114" +// "Hash": "eb768b0140a5fe95f9c58747f0960d647dacfd42", +// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:130" // } // }, // { @@ -540,8 +497,8 @@ func main() { // }, // "V": { // "@type": "/gno.RefValue", -// "Hash": "1dd77a196db00a4d1a3471539279df79f6ae950e", -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:115" +// "Hash": "f56a463a97d103e183f1f368bf00a2ce99d4de88", +// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:131" // } // }, // { @@ -551,95 +508,57 @@ func main() { // }, // "V": { // "@type": "/gno.RefValue", -// "Hash": "c3a60b602b564d07677a212372f4ac1cae4270fd", -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:116" +// "Hash": "bed4afa8ffdbbf775451c947fc68b27a345ce32a", +// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:132" // } // } // ], // "ObjectInfo": { -// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:111", +// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:127", +// "ModTime": "0", +// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:126", +// "RefCount": "1" +// } +// } +// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:126]={ +// "ObjectInfo": { +// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:126", // "IsEscaped": true, // "ModTime": "0", // "RefCount": "2" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/demo/boards.Post" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "0651ea376feea18422dbe079f9d7fcbe3dfd32ad", +// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:127" +// } // } // } -// u[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:108]={ -// "Fields": [ -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// }, -// "V": { -// "@type": "/gno.StringValue", -// "value": "0000000003" -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/boards.Post" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": null, -// "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/boards.Post" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:102" -// } -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "64" -// } -// }, -// { -// "N": "AQAAAAAAAAA=", -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "32" -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// } -// } -// ], +// u[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:120]={ // "ObjectInfo": { -// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:108", -// "ModTime": "117", -// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:117", +// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:120", +// "ModTime": "134", +// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:134", // "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "dc1f011553dc53e7a846049e08cc77fa35ea6a51", +// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:121" +// } // } // } -// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:118]={ +// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:136]={ // "Fields": [ // { // "T": { @@ -661,19 +580,13 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:126" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/boards.Post" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:111" -// } -// } +// "TV": null // } // }, // { @@ -709,13 +622,32 @@ func main() { // } // ], // "ObjectInfo": { -// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:118", +// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:136", +// "ModTime": "0", +// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:135", +// "RefCount": "1" +// } +// } +// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:135]={ +// "ObjectInfo": { +// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:135", // "ModTime": "0", -// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:117", +// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:134", // "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "96b86b4585c7f1075d7794180a5581f72733a7ab", +// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:136" +// } // } // } -// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:117]={ +// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:134]={ // "Fields": [ // { // "T": { @@ -752,19 +684,13 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "32274e1f28fb2b97d67a1262afd362d370de7faa", +// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:120" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "6a86bc7763703c8f2b9d286368921159d6db121c", -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:108" -// } -// } +// "TV": null // } // }, // { @@ -777,30 +703,43 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "c2cfd6aec36a462f35bf02e5bf4a127aa1bb7ac2", +// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:135" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "52faa8a2dfefd4b6b6249eff2f9c123ad455e81d", -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:118" -// } -// } +// "TV": null // } // } // ], // "ObjectInfo": { -// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:117", +// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:134", // "ModTime": "0", -// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:97", +// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:133", // "RefCount": "1" // } // } -// u[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:81]={ +// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:133]={ +// "ObjectInfo": { +// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:133", +// "ModTime": "0", +// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:107", +// "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "5cb875179e86d32c517322af7a323b2a5f3e6cc5", +// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:134" +// } +// } +// } +// u[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:85]={ // "Fields": [ // { // "N": "AQAAAAAAAAA=", @@ -846,8 +785,8 @@ func main() { // }, // "V": { // "@type": "/gno.RefValue", -// "Hash": "6e8ecb1c773e0e34ba560dcff1b2dee0bc2e5660", -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:82" +// "Hash": "1d70015c486ccc9bfb7c425152605fabc78be1dd", +// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:86" // } // }, // { @@ -864,8 +803,8 @@ func main() { // }, // "V": { // "@type": "/gno.RefValue", -// "Hash": "796da27e11e6d75db3e0e8f9d633f4559f1b1a82", -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:83" +// "Hash": "b8ee1687a4f45886912ba624c8513a2a1526b94c", +// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:87" // } // }, // { @@ -875,19 +814,19 @@ func main() { // }, // "V": { // "@type": "/gno.RefValue", -// "Hash": "edb1857302fa916c562cd077cdf2a3626e29ae2b", -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:84" +// "Hash": "af6ed0268f99b7f369329094eb6dfaea7812708b", +// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:88" // } // } // ], // "ObjectInfo": { -// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:81", -// "IsEscaped": true, -// "ModTime": "108", -// "RefCount": "6" +// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:85", +// "ModTime": "121", +// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:84", +// "RefCount": "1" // } // } -// u[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:96]={ +// u[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:106]={ // "Fields": [ // { // "T": { @@ -899,30 +838,24 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "9809329dc1ddc5d3556f7a8fa3c2cebcbf65560b", +// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:122" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "609e7f519c65f94503427a14f973b4b83989cdc8", -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:109" -// } -// } +// "TV": null // } // } // ], // "ObjectInfo": { -// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:96", -// "ModTime": "108", -// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:95", +// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:106", +// "ModTime": "121", +// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:105", // "RefCount": "1" // } // } -// u[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:97]={ +// u[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:107]={ // "Fields": [ // { // "T": { @@ -934,26 +867,20 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "ceae9a1c4ed28bb51062e6ccdccfad0caafd1c4f", +// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:133" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "6760340f5b40e05221dc530940683b0b9a422503", -// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:117" -// } -// } +// "TV": null // } // } // ], // "ObjectInfo": { -// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:97", -// "ModTime": "108", -// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:95", +// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:107", +// "ModTime": "121", +// "OwnerID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:105", // "RefCount": "1" // } // } diff --git a/examples/gno.land/r/demo/nft/z_0_filetest.gno b/examples/gno.land/r/demo/nft/z_0_filetest.gno index 9e8010abaf5..0595e76a0ed 100644 --- a/examples/gno.land/r/demo/nft/z_0_filetest.gno +++ b/examples/gno.land/r/demo/nft/z_0_filetest.gno @@ -22,7 +22,7 @@ func main() { // Realm: // switchrealm["gno.land/r/demo/nft"] // switchrealm["gno.land/r/demo/nft"] -// c[67c479d3d51d4056b2f4111d5352912a00be311e:8]={ +// c[67c479d3d51d4056b2f4111d5352912a00be311e:11]={ // "Fields": [ // { // "T": { @@ -62,13 +62,32 @@ func main() { // } // ], // "ObjectInfo": { -// "ID": "67c479d3d51d4056b2f4111d5352912a00be311e:8", +// "ID": "67c479d3d51d4056b2f4111d5352912a00be311e:11", // "ModTime": "0", -// "OwnerID": "67c479d3d51d4056b2f4111d5352912a00be311e:7", +// "OwnerID": "67c479d3d51d4056b2f4111d5352912a00be311e:10", // "RefCount": "1" // } // } -// c[67c479d3d51d4056b2f4111d5352912a00be311e:7]={ +// c[67c479d3d51d4056b2f4111d5352912a00be311e:10]={ +// "ObjectInfo": { +// "ID": "67c479d3d51d4056b2f4111d5352912a00be311e:10", +// "ModTime": "0", +// "OwnerID": "67c479d3d51d4056b2f4111d5352912a00be311e:9", +// "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/demo/nft.NFToken" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "564a9e78be869bd258fc3c9ad56f5a75ed68818f", +// "ObjectID": "67c479d3d51d4056b2f4111d5352912a00be311e:11" +// } +// } +// } +// c[67c479d3d51d4056b2f4111d5352912a00be311e:9]={ // "Fields": [ // { // "T": { @@ -90,19 +109,13 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "b53ffc464e1b5655d19b9d5277f3491717c24aca", +// "ObjectID": "67c479d3d51d4056b2f4111d5352912a00be311e:10" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/nft.NFToken" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "c06f58d0ff2bc26ad3e65e953b127a0d03353e97", -// "ObjectID": "67c479d3d51d4056b2f4111d5352912a00be311e:8" -// } -// } +// "TV": null // } // }, // { @@ -138,13 +151,32 @@ func main() { // } // ], // "ObjectInfo": { -// "ID": "67c479d3d51d4056b2f4111d5352912a00be311e:7", +// "ID": "67c479d3d51d4056b2f4111d5352912a00be311e:9", // "ModTime": "0", -// "OwnerID": "67c479d3d51d4056b2f4111d5352912a00be311e:5", +// "OwnerID": "67c479d3d51d4056b2f4111d5352912a00be311e:8", // "RefCount": "1" // } // } -// u[67c479d3d51d4056b2f4111d5352912a00be311e:5]={ +// c[67c479d3d51d4056b2f4111d5352912a00be311e:8]={ +// "ObjectInfo": { +// "ID": "67c479d3d51d4056b2f4111d5352912a00be311e:8", +// "ModTime": "0", +// "OwnerID": "67c479d3d51d4056b2f4111d5352912a00be311e:6", +// "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "b1d928b3716b147c92730e8d234162bec2f0f2fc", +// "ObjectID": "67c479d3d51d4056b2f4111d5352912a00be311e:9" +// } +// } +// } +// u[67c479d3d51d4056b2f4111d5352912a00be311e:6]={ // "Fields": [ // { // "T": { @@ -156,30 +188,24 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "75850e56fa2c3c8b6d6814b1f150919b75355752", +// "ObjectID": "67c479d3d51d4056b2f4111d5352912a00be311e:8" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "45a64533aa57b49b6b4a1d3f6de79db8bea3a710", -// "ObjectID": "67c479d3d51d4056b2f4111d5352912a00be311e:7" -// } -// } +// "TV": null // } // } // ], // "ObjectInfo": { -// "ID": "67c479d3d51d4056b2f4111d5352912a00be311e:5", -// "ModTime": "6", -// "OwnerID": "67c479d3d51d4056b2f4111d5352912a00be311e:4", +// "ID": "67c479d3d51d4056b2f4111d5352912a00be311e:6", +// "ModTime": "7", +// "OwnerID": "67c479d3d51d4056b2f4111d5352912a00be311e:5", // "RefCount": "1" // } // } -// u[67c479d3d51d4056b2f4111d5352912a00be311e:4]={ +// u[67c479d3d51d4056b2f4111d5352912a00be311e:5]={ // "Fields": [ // {}, // { @@ -196,8 +222,8 @@ func main() { // }, // "V": { // "@type": "/gno.RefValue", -// "Hash": "dad3106a54e1facb92bce473898b8aec0eb930ff", -// "ObjectID": "67c479d3d51d4056b2f4111d5352912a00be311e:5" +// "Hash": "700d932c087f30499941de2b589867dc17aaea5a", +// "ObjectID": "67c479d3d51d4056b2f4111d5352912a00be311e:6" // } // }, // { @@ -207,15 +233,15 @@ func main() { // }, // "V": { // "@type": "/gno.RefValue", -// "Hash": "05ab6746ea84b55ca133806af215d99a1c4b045e", -// "ObjectID": "67c479d3d51d4056b2f4111d5352912a00be311e:6" +// "Hash": "e06585aff551113920c929453ea40250f3cc01bc", +// "ObjectID": "67c479d3d51d4056b2f4111d5352912a00be311e:7" // } // } // ], // "ObjectInfo": { -// "ID": "67c479d3d51d4056b2f4111d5352912a00be311e:4", -// "ModTime": "6", -// "OwnerID": "67c479d3d51d4056b2f4111d5352912a00be311e:2", +// "ID": "67c479d3d51d4056b2f4111d5352912a00be311e:5", +// "ModTime": "7", +// "OwnerID": "67c479d3d51d4056b2f4111d5352912a00be311e:4", // "RefCount": "1" // } // } diff --git a/examples/gno.land/r/gov/dao/dao.gno b/examples/gno.land/r/gov/dao/dao.gno index 37caca9fa19..68f188eb0a4 100644 --- a/examples/gno.land/r/gov/dao/dao.gno +++ b/examples/gno.land/r/gov/dao/dao.gno @@ -2,60 +2,88 @@ package govdao import ( "std" + "strconv" - "gno.land/p/gov/proposal" + "gno.land/p/demo/ufmt" + pproposal "gno.land/p/gov/proposal" ) -var proposals = make([]Proposal, 0) +var proposals = make([]*proposal, 0) // XXX var members ... -// Proposal represents a proposal in the governance system. -type Proposal struct { - author std.Address +type proposal struct { idx int - Comment string - Executor proposal.Executor + author std.Address + comment string + executor pproposal.Executor + // XXX: make "accepted" and "finished" managed by an interface that can have various voting implementations + accepted bool + finished bool +} + +func (p proposal) Status() Status { + if p.executor.Done() { + return Status(p.executor.Status()) + } + if p.accepted { + return Accepted + } + // XXX: timeout + // XXX: not_accepted + return Active } // Propose is designed to be called by another contract or with // `maketx run`, not by a `maketx call`. -func Propose(proposal Proposal) int { +func Propose(comment string, executor pproposal.Executor) int { // XXX: require payment? - // XXX: sanitize proposal + if executor == nil { + panic("missing proposal executor") + } caller := std.PrevRealm().Addr() AssertIsMember(caller) - proposal.author = caller - proposal.idx = len(proposals) - proposals = append(proposals, proposal) - return proposal.idx + + prop := &proposal{ + comment: comment, + executor: executor, + author: caller, + idx: len(proposals), + } + + proposals = append(proposals, prop) + return prop.idx } func VoteOnProposal(idx int, option string) { + assertProposalExists(idx) caller := std.PrevRealm().Addr() AssertIsMember(caller) - panic("not implemented") - // XXX: implement the voting (woudl be cool to have a generic p/) + + prop := getProposal(idx) + if prop.finished { + panic("prop is not active anymore, cannot vote.") + } + // XXX: implement the real voting (would be cool to have a generic p/) + prop.accepted = option == "YES" + prop.finished = true } func ExecuteProposal(idx int) { assertProposalExists(idx) - // XXX: assert voting is finished - // XXX: assert voting result is YES - // XXX: proposal was not already executed - proposal := proposals[idx] - proposal.Executor.Execute() -} - -func assertProposalExists(idx int) { - if idx < 0 || idx >= len(proposals) { - panic("invalid proposal id") + prop := getProposal(idx) + if !prop.finished { + panic("prop is still active, cannot execute.") } + if !prop.accepted { + panic("prop is not accepted, cannot execute.") + } + prop.executor.Execute() } func IsMember(addr std.Address) bool { // XXX: implement - return true + return true // in the meantime, everyone is a DAO member } func AssertIsMember(addr std.Address) { @@ -63,3 +91,47 @@ func AssertIsMember(addr std.Address) { panic("caller is not member of govdao") } } + +func Render(path string) string { + if path == "" { + output := "" + for idx, prop := range proposals { + output += ufmt.Sprintf("- [/r/gov/dao:%d](%d) - %s (by %s)", idx, idx, prop.comment, prop.author) + } + return output + } + + // else display the proposal + idx, err := strconv.Atoi(path) + if err != nil { + return "404" + } + + if !proposalExists(idx) { + return "404" + } + prop := getProposal(idx) + output := "" + output += ufmt.Sprintf("# Prop#%d", prop.idx) + "\n" + output += "\n" + output += prop.comment + output += "\n" + output += ufmt.Sprintf("Status: %s", string(prop.Status())) + output += "\n" + output += ufmt.Sprintf("Author: %s", string(prop.author)) + return output +} + +func getProposal(idx int) *proposal { + return proposals[idx-1] +} + +func proposalExists(idx int) bool { + return idx > 0 && idx <= len(proposals) +} + +func assertProposalExists(idx int) { + if !proposalExists(idx) { + panic("invalid proposal id") + } +} diff --git a/examples/gno.land/r/gov/dao/gno.mod b/examples/gno.land/r/gov/dao/gno.mod index ae296cf121c..0127e84a8e2 100644 --- a/examples/gno.land/r/gov/dao/gno.mod +++ b/examples/gno.land/r/gov/dao/gno.mod @@ -1,3 +1,6 @@ module gno.land/r/gov/dao -require gno.land/p/gov/proposal v0.0.0-latest +require ( + gno.land/p/demo/ufmt v0.0.0-latest + gno.land/p/gov/proposal v0.0.0-latest +) diff --git a/examples/gno.land/r/gov/dao/prop1_filetest.gno b/examples/gno.land/r/gov/dao/prop1_filetest.gno new file mode 100644 index 00000000000..7072618a4a7 --- /dev/null +++ b/examples/gno.land/r/gov/dao/prop1_filetest.gno @@ -0,0 +1,85 @@ +// Please note that this package is intended for demonstration purposes only. +// You could execute this code (the init part) by running a `maketx run` command +// or by uploading a similar package to a personal namespace. +// +// For the specific case of validators, a `r/gnoland/valopers` will be used to +// organize the lifecycle of validators (register, etc), and this more complex +// contract will be responsible to generate proposals. +package main + +import ( + "std" + + govdao "gno.land/r/gov/dao" + "gno.land/r/sys/validators" +) + +func init() { + // Create the validators change proposal. + changesFn := func() []validators.Change { + return []validators.Change{ + // add a new validator. + {Address: std.Address("g12345678"), Power: 1}, + // remove an existing validator. + {Address: std.Address("g000000000"), Power: 0}, + } + } + + // Wraps changesFn to emit a certified event only if executed from a + // complete governance proposal process. + executor := validators.NewPropExecutor(changesFn) + + // Create a proposal. + // XXX: payment + comment := "manual valset changes proposal example" + govdao.Propose(comment, executor) +} + +func main() { + println("--") + println(govdao.Render("")) + println("--") + println(govdao.Render("1")) + println("--") + govdao.VoteOnProposal(1, "YES") + println("--") + println(govdao.Render("1")) + println("--") + println(validators.Render("")) + println("--") + govdao.ExecuteProposal(1) + println("--") + println(govdao.Render("1")) + println("--") + println(validators.Render("")) +} + +// Output: +// -- +// - [/r/gov/dao:0](0) - manual valset changes proposal example (by g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm) +// -- +// # Prop#0 +// +// manual valset changes proposal example +// Status: active +// Author: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm +// -- +// -- +// # Prop#0 +// +// manual valset changes proposal example +// Status: accepted +// Author: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm +// -- +// No valset changes to apply. +// -- +// -- +// # Prop#0 +// +// manual valset changes proposal example +// Status: success +// Author: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm +// -- +// Valset changes to apply: +// - g12345678 (1) +// - g000000000 (0) diff --git a/examples/gno.land/r/gov/dao/types.gno b/examples/gno.land/r/gov/dao/types.gno new file mode 100644 index 00000000000..9e4ca0718c9 --- /dev/null +++ b/examples/gno.land/r/gov/dao/types.gno @@ -0,0 +1,10 @@ +package govdao + +// Status enum. +type Status string + +var ( + Accepted Status = "accepted" + Active Status = "active" + // Timeout, NotAccepted +) diff --git a/examples/gno.land/r/gov/proposals/prop1/gno.mod b/examples/gno.land/r/gov/proposals/prop1/gno.mod deleted file mode 100644 index 97faf2aff27..00000000000 --- a/examples/gno.land/r/gov/proposals/prop1/gno.mod +++ /dev/null @@ -1,6 +0,0 @@ -module gno.land/r/gov/proposals/prop1 - -require ( - gno.land/r/gov/dao v0.0.0-latest - gno.land/r/sys/validators v0.0.0-latest -) diff --git a/examples/gno.land/r/gov/proposals/prop1/prop1.gno b/examples/gno.land/r/gov/proposals/prop1/prop1.gno deleted file mode 100644 index 6e5edb0c97b..00000000000 --- a/examples/gno.land/r/gov/proposals/prop1/prop1.gno +++ /dev/null @@ -1,39 +0,0 @@ -// Package prop1 is an example of proposal creation using a contract. -// -// Please note that this package is intended for demonstration purposes only. -// You could execute this code by running a `maketx run` command or by uploading -// a similar package to a personal namespace. -// -// For the specific case of validators, a `r/gnoland/valopers` will be used to -// organize the lifecycle of validators (register, etc), and this more complex -// contract will be responsible to generate proposals. -package prop1 - -import ( - "std" - - govdao "gno.land/r/gov/dao" - "gno.land/r/sys/validators" -) - -func init() { - // Create the validators change proposal. - changesFn := func() []validators.Change { - return []validators.Change{ - {Address: std.Address("g12345678"), Power: 1}, // add a new validator - {Address: std.Address("g000000000"), Power: 0}, // remove an existing validator - } - } - - // Wraps changesFn to emit a certified event only if executed from a - // complete governance proposal process. - executor := validators.NewProposalExecutor(changesFn) - - // Create a proposal. - // XXX: payment - proposal := govdao.Proposal{ - Comment: "manual valset changes proposal example", - Executor: executor, - } - govdao.Propose(proposal) -} diff --git a/examples/gno.land/r/sys/validators/validators.gno b/examples/gno.land/r/sys/validators/validators.gno index 0e6e132f6e9..669b688727a 100644 --- a/examples/gno.land/r/sys/validators/validators.gno +++ b/examples/gno.land/r/sys/validators/validators.gno @@ -3,6 +3,7 @@ package validators import ( "std" + "strconv" "gno.land/p/gov/proposal" ) @@ -15,10 +16,10 @@ type Change struct { Power int } -// NewProposalExecutor creates a new executor that wraps a changes closure +// NewPropExecutor creates a new executor that wraps a changes closure // proposal. It emits a typed object (subscribed by tm2) only if it passes // through a complete p/gov/proposal process. -func NewProposalExecutor(changesFn func() []Change) proposal.Executor { +func NewPropExecutor(changesFn func() []Change) proposal.Executor { if changesFn == nil { panic("changesFn should not be nil") } @@ -45,3 +46,15 @@ func getAndResetChanges() []Change { unappliedChanges = []Change{} return cpy } + +func Render(_ string) string { + if len(unappliedChanges) == 0 { + return "No valset changes to apply." + } + + output := "Valset changes to apply:\n" + for _, change := range unappliedChanges { + output += "- " + string(change.Address) + " (" + strconv.Itoa(change.Power) + ")\n" + } + return output +} diff --git a/examples/gno.land/r/x/manfred_outfmt/gno.mod b/examples/gno.land/r/x/manfred_outfmt/gno.mod index e6f705c46b9..9804aecc7f1 100644 --- a/examples/gno.land/r/x/manfred_outfmt/gno.mod +++ b/examples/gno.land/r/x/manfred_outfmt/gno.mod @@ -1,3 +1,5 @@ +// Draft + module gno.land/r/x/manfred_outfmt require ( diff --git a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_c/v2/v2.gno b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_c/v2/v2.gno index 7d07337a17d..03ffe876519 100644 --- a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_c/v2/v2.gno +++ b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_c/v2/v2.gno @@ -1,4 +1,4 @@ -package v1 +package v2 import "gno.land/r/x/manfred_upgrade_patterns/upgrade_c/root" diff --git a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_d/v2/v2.gno b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_d/v2/v2.gno index a31219cd4d0..22e43cf7bdd 100644 --- a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_d/v2/v2.gno +++ b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_d/v2/v2.gno @@ -1,4 +1,4 @@ -package v1 +package v2 import ( "gno.land/p/demo/avl" diff --git a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_d/v2/z_filetest.gno b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_d/v2/z_filetest.gno index 961c7a13208..f884b5278ab 100644 --- a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_d/v2/z_filetest.gno +++ b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_d/v2/z_filetest.gno @@ -1,17 +1,15 @@ package main import ( - "fmt" - "gno.land/r/x/manfred_upgrade_patterns/upgrade_d/v2" ) func main() { - println("a", v1.Get("a")) - println("b", v1.Get("b")) - println("c", v1.Get("c")) - println("d", v1.Get("d")) - println("e", v1.Get("e")) + println("a", v2.Get("a")) + println("b", v2.Get("b")) + println("c", v2.Get("c")) + println("d", v2.Get("d")) + println("e", v2.Get("e")) } // Output: diff --git a/examples/gno.land/r/x/skip_height_to_skip_time/skiptime.gno b/examples/gno.land/r/x/skip_height_to_skip_time/skiptime.gno new file mode 100644 index 00000000000..524e6f58ad9 --- /dev/null +++ b/examples/gno.land/r/x/skip_height_to_skip_time/skiptime.gno @@ -0,0 +1 @@ +package skiptime diff --git a/examples/gno.land/r/x/skip_height_to_skip_time/skiptime_test.gno b/examples/gno.land/r/x/skip_height_to_skip_time/skiptime_test.gno new file mode 100644 index 00000000000..52670a5626b --- /dev/null +++ b/examples/gno.land/r/x/skip_height_to_skip_time/skiptime_test.gno @@ -0,0 +1,27 @@ +package skiptime + +import ( + "std" + "testing" + "time" +) + +func TestSkipHeights(t *testing.T) { + oldHeight := std.GetHeight() + shouldEQ(t, oldHeight, 123) + + oldNow := time.Now().Unix() + shouldEQ(t, oldNow, 1234567890) + + // skip 3 blocks == 15 seconds + std.TestSkipHeights(3) + + shouldEQ(t, std.GetHeight()-oldHeight, 3) + shouldEQ(t, time.Now().Unix()-oldNow, 15) +} + +func shouldEQ(t *testing.T, got, expected int64) { + if got != expected { + t.Fatalf("expected %d, got %d.", expected, got) + } +} diff --git a/gno.land/cmd/gnoland/secrets.go b/gno.land/cmd/gnoland/secrets.go index fda2a664c28..f45c751a463 100644 --- a/gno.land/cmd/gnoland/secrets.go +++ b/gno.land/cmd/gnoland/secrets.go @@ -21,7 +21,7 @@ const ( ) const ( - nodeKeyKey = "NodeKey" + nodeIDKey = "NodeID" validatorPrivateKeyKey = "ValidatorPrivateKey" validatorStateKey = "ValidatorState" ) diff --git a/gno.land/cmd/gnoland/secrets_common.go b/gno.land/cmd/gnoland/secrets_common.go index 588307b9b8e..d40e90f6b48 100644 --- a/gno.land/cmd/gnoland/secrets_common.go +++ b/gno.land/cmd/gnoland/secrets_common.go @@ -168,14 +168,14 @@ func verifySecretsKey(args []string) error { // Verify the set key key := args[0] - if key != nodeKeyKey && + if key != nodeIDKey && key != validatorPrivateKeyKey && key != validatorStateKey { return fmt.Errorf( "invalid secrets key value [%s, %s, %s]", validatorPrivateKeyKey, validatorStateKey, - nodeKeyKey, + nodeIDKey, ) } @@ -187,7 +187,7 @@ func getAvailableSecretsKeys() string { return fmt.Sprintf( "[%s, %s, %s]", validatorPrivateKeyKey, - nodeKeyKey, + nodeIDKey, validatorStateKey, ) } diff --git a/gno.land/cmd/gnoland/secrets_get.go b/gno.land/cmd/gnoland/secrets_get.go index 699450702b4..92648314bd1 100644 --- a/gno.land/cmd/gnoland/secrets_get.go +++ b/gno.land/cmd/gnoland/secrets_get.go @@ -5,10 +5,13 @@ import ( "flag" "fmt" "path/filepath" + "strings" "text/tabwriter" + "github.com/gnolang/gno/tm2/pkg/bft/config" "github.com/gnolang/gno/tm2/pkg/bft/privval" "github.com/gnolang/gno/tm2/pkg/commands" + osm "github.com/gnolang/gno/tm2/pkg/os" "github.com/gnolang/gno/tm2/pkg/p2p" ) @@ -75,7 +78,7 @@ func execSecretsGet(cfg *secretsGetCfg, args []string, io commands.IO) error { case validatorStateKey: // Show the validator's last sign state return readAndShowValidatorState(validatorStatePath, io) - case nodeKeyKey: + case nodeIDKey: // Show the node's p2p info return readAndShowNodeKey(nodeKeyPath, io) default: @@ -185,12 +188,30 @@ func readAndShowNodeKey(path string, io commands.IO) error { return fmt.Errorf("unable to read node key, %w", err) } + // Construct the config path + var ( + nodeDir = filepath.Join(filepath.Dir(path), "..") + configPath = constructConfigPath(nodeDir) + + cfg = config.DefaultConfig() + ) + + // Check if there is an existing config file + if osm.FileExists(configPath) { + // Attempt to grab the config from disk + cfg, err = config.LoadConfig(nodeDir) + if err != nil { + return fmt.Errorf("unable to load config file, %w", err) + } + } + w := tabwriter.NewWriter(io.Out(), 0, 0, 2, ' ', 0) if _, err := fmt.Fprintf(w, "[Node P2P Info]\n\n"); err != nil { return err } + // Print the ID info if _, err := fmt.Fprintf( w, "Node ID:\t%s\n", @@ -199,5 +220,35 @@ func readAndShowNodeKey(path string, io commands.IO) error { return err } + // Print the P2P address info + if _, err := fmt.Fprintf( + w, + "P2P Address:\t%s\n", + constructP2PAddress( + nodeKey.ID(), + cfg.P2P.ListenAddress, + ), + ); err != nil { + return err + } + return w.Flush() } + +// constructP2PAddress constructs the P2P address other nodes can use +// to connect directly +func constructP2PAddress(nodeID p2p.ID, listenAddress string) string { + var ( + address string + parts = strings.SplitN(listenAddress, "://", 2) + ) + + switch len(parts) { + case 2: + address = parts[1] + default: + address = listenAddress + } + + return fmt.Sprintf("%s@%s", nodeID, address) +} diff --git a/gno.land/cmd/gnoland/secrets_get_test.go b/gno.land/cmd/gnoland/secrets_get_test.go index 20f1eb2ef35..7d3951ded3e 100644 --- a/gno.land/cmd/gnoland/secrets_get_test.go +++ b/gno.land/cmd/gnoland/secrets_get_test.go @@ -4,10 +4,12 @@ import ( "bytes" "context" "fmt" + "os" "path/filepath" "strconv" "testing" + "github.com/gnolang/gno/tm2/pkg/bft/config" "github.com/gnolang/gno/tm2/pkg/bft/privval" "github.com/gnolang/gno/tm2/pkg/commands" "github.com/gnolang/gno/tm2/pkg/p2p" @@ -95,6 +97,13 @@ func TestSecrets_Get_All(t *testing.T) { nodeKey.ID().String(), ) + // Make sure the node p2p address is displayed + assert.Contains( + t, + output, + constructP2PAddress(nodeKey.ID(), config.DefaultConfig().P2P.ListenAddress), + ) + // Make sure the private key info is displayed assert.Contains( t, @@ -237,9 +246,11 @@ func TestSecrets_Get_Single(t *testing.T) { ) }) - t.Run("node key shown", func(t *testing.T) { + t.Run("node key shown, default config", func(t *testing.T) { t.Parallel() + cfg := config.DefaultConfig() + dirPath := t.TempDir() nodeKeyPath := filepath.Join(dirPath, defaultNodeKeyName) @@ -258,7 +269,7 @@ func TestSecrets_Get_Single(t *testing.T) { "get", "--data-dir", dirPath, - nodeKeyKey, + nodeIDKey, } // Run the command @@ -272,5 +283,69 @@ func TestSecrets_Get_Single(t *testing.T) { output, validNodeKey.ID().String(), ) + + // Make sure the default node p2p address is displayed + assert.Contains( + t, + output, + constructP2PAddress(validNodeKey.ID(), cfg.P2P.ListenAddress), + ) + }) + + t.Run("node key shown, existing config", func(t *testing.T) { + t.Parallel() + + var ( + dirPath = t.TempDir() + configPath = constructConfigPath(dirPath) + secretsPath = constructSecretsPath(dirPath) + nodeKeyPath = filepath.Join(secretsPath, defaultNodeKeyName) + ) + + // Ensure the sub-dirs exist + require.NoError(t, os.MkdirAll(filepath.Dir(configPath), 0o755)) + require.NoError(t, os.MkdirAll(secretsPath, 0o755)) + + // Set up the config + cfg := config.DefaultConfig() + cfg.P2P.ListenAddress = "tcp://127.0.0.1:2525" + + require.NoError(t, config.WriteConfigFile(configPath, cfg)) + + validNodeKey := generateNodeKey() + require.NoError(t, saveSecretData(validNodeKey, nodeKeyPath)) + + mockOutput := bytes.NewBufferString("") + io := commands.NewTestIO() + io.SetOut(commands.WriteNopCloser(mockOutput)) + + // Create the command + cmd := newRootCmd(io) + args := []string{ + "secrets", + "get", + "--data-dir", + secretsPath, + nodeIDKey, + } + + // Run the command + require.NoError(t, cmd.ParseAndRun(context.Background(), args)) + + output := mockOutput.String() + + // Make sure the node p2p key is displayed + assert.Contains( + t, + output, + validNodeKey.ID().String(), + ) + + // Make sure the custom node p2p address is displayed + assert.Contains( + t, + output, + constructP2PAddress(validNodeKey.ID(), cfg.P2P.ListenAddress), + ) }) } diff --git a/gno.land/cmd/gnoland/secrets_init.go b/gno.land/cmd/gnoland/secrets_init.go index 85181d4735c..58dd0783f66 100644 --- a/gno.land/cmd/gnoland/secrets_init.go +++ b/gno.land/cmd/gnoland/secrets_init.go @@ -93,7 +93,7 @@ func execSecretsInit(cfg *secretsInitCfg, args []string, io commands.IO) error { // Initialize and save the validator's private key return initAndSaveValidatorKey(validatorKeyPath, io) - case nodeKeyKey: + case nodeIDKey: if osm.FileExists(nodeKeyPath) && !cfg.forceOverwrite { return fmt.Errorf("unable to overwrite the node' p2p key, %w", errOverwriteNotEnabled) } diff --git a/gno.land/cmd/gnoland/secrets_init_test.go b/gno.land/cmd/gnoland/secrets_init_test.go index 4a707778cc6..20e061447f5 100644 --- a/gno.land/cmd/gnoland/secrets_init_test.go +++ b/gno.land/cmd/gnoland/secrets_init_test.go @@ -149,7 +149,7 @@ func TestSecrets_Init_Single(t *testing.T) { }, { "node p2p key initialized", - nodeKeyKey, + nodeIDKey, defaultNodeKeyName, verifyNodeKey, }, @@ -207,7 +207,7 @@ func TestSecrets_Init_Single_Overwrite(t *testing.T) { }, { "node p2p key not overwritten", - nodeKeyKey, + nodeIDKey, defaultNodeKeyName, }, } diff --git a/gno.land/cmd/gnoland/secrets_verify.go b/gno.land/cmd/gnoland/secrets_verify.go index 7e6c154d1ac..32e563c1c6f 100644 --- a/gno.land/cmd/gnoland/secrets_verify.go +++ b/gno.land/cmd/gnoland/secrets_verify.go @@ -87,7 +87,7 @@ func execSecretsVerify(cfg *secretsVerifyCfg, args []string, io commands.IO) err } return nil - case nodeKeyKey: + case nodeIDKey: return readAndVerifyNodeKey(nodeKeyPath, io) default: // Validate the validator's private key diff --git a/gno.land/cmd/gnoland/secrets_verify_test.go b/gno.land/cmd/gnoland/secrets_verify_test.go index 77ab8523d40..513d7c8b503 100644 --- a/gno.land/cmd/gnoland/secrets_verify_test.go +++ b/gno.land/cmd/gnoland/secrets_verify_test.go @@ -360,7 +360,7 @@ func TestSecrets_Verify_Single(t *testing.T) { "verify", "--data-dir", dirPath, - nodeKeyKey, + nodeIDKey, } // Run the command diff --git a/gno.land/pkg/sdk/vm/vm.proto b/gno.land/pkg/sdk/vm/vm.proto index b99be0a85ff..aa0be4f6e14 100644 --- a/gno.land/pkg/sdk/vm/vm.proto +++ b/gno.land/pkg/sdk/vm/vm.proto @@ -15,6 +15,12 @@ message m_call { repeated string args = 5; } +message m_run { + string caller = 1; + string send = 2; + std.MemPackage package = 3; +} + message m_addpkg { string creator = 1; std.MemPackage package = 2; @@ -28,4 +34,8 @@ message InvalidStmtError { } message InvalidExprError { +} + +message TypeCheckError { + repeated string errors = 1 [json_name = "Errors"]; } \ No newline at end of file diff --git a/gnovm/Makefile b/gnovm/Makefile index ff3f07158c5..aa80c61ac7d 100644 --- a/gnovm/Makefile +++ b/gnovm/Makefile @@ -27,6 +27,9 @@ GNOROOT_DIR ?= $(abspath $(lastword $(MAKEFILE_LIST))/../../) # We can't use '-trimpath' yet as amino use absolute path from call stack # to find some directory: see #1236 GOBUILD_FLAGS ?= -ldflags "-X github.com/gnolang/gno/gnovm/pkg/gnoenv._GNOROOT=$(GNOROOT_DIR)" +# file where to place cover profile; used for coverage commands which are +# more complex than adding -coverprofile, like test.cmd.coverage. +GOTEST_COVER_PROFILE ?= cmd-profile.out ######################################## # Dev tools @@ -66,6 +69,24 @@ test: _test.cmd _test.pkg _test.gnolang _test.cmd: go test ./cmd/... $(GOTEST_FLAGS) +# Run tests on ./cmd/, saving the result of the coverage in +# GOTEST_COVER_PROFILE. +.PHONY: test.cmd.coverage +test.cmd.coverage: + $(eval export TXTARCOVERDIR := $(shell mktemp -d --tmpdir gnovm-make.XXXXXXX)) + go test ./cmd/... -covermode atomic -test.gocoverdir='$(TXTARCOVERDIR)' $(GOTEST_FLAGS) + @echo "coverage results:" + go tool covdata percent -i="$(TXTARCOVERDIR)" + go tool covdata textfmt -v 1 -i="$(TXTARCOVERDIR)" -o '$(GOTEST_COVER_PROFILE)' + rm -rf "$(TXTARCOVERDIR)" + +# Run test.cmd.coverage, then view the result in the HTML browser render +# and delete the original file. +.PHONY: test.cmd.coverage_view +test.cmd.coverage_view: test.cmd.coverage + go tool cover -html='$(GOTEST_COVER_PROFILE)' + rm '$(GOTEST_COVER_PROFILE)' + .PHONY: _test.pkg _test.pkg: go test ./pkg/... $(GOTEST_FLAGS) @@ -74,13 +95,16 @@ _test.pkg: _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|stdshim|strconv|strings|testing|unicode)" $(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) ######################################## # Code gen diff --git a/gnovm/cmd/gno/run_test.go b/gnovm/cmd/gno/run_test.go index f78c15edb34..06f9a8dc3a5 100644 --- a/gnovm/cmd/gno/run_test.go +++ b/gnovm/cmd/gno/run_test.go @@ -75,6 +75,10 @@ func TestRunApp(t *testing.T) { args: []string{"run", "-debug-addr", "invalidhost:17538", "../../tests/integ/debugger/sample.gno"}, errShouldContain: "listen tcp: lookup invalidhost", }, + { + args: []string{"run", "../../tests/integ/invalid_assign/main.gno"}, + recoverShouldContain: "cannot use bool as main.C without explicit conversion", + }, // TODO: a test file // TODO: args // TODO: nativeLibs VS stdlibs diff --git a/gnovm/cmd/gno/test.go b/gnovm/cmd/gno/test.go index 2e966bd32a9..5884463a552 100644 --- a/gnovm/cmd/gno/test.go +++ b/gnovm/cmd/gno/test.go @@ -20,7 +20,6 @@ import ( "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/pkg/transpiler" "github.com/gnolang/gno/gnovm/tests" "github.com/gnolang/gno/tm2/pkg/commands" "github.com/gnolang/gno/tm2/pkg/errors" @@ -259,7 +258,7 @@ func gnoTestPkg( 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 = transpiler.GnoRealmPkgsPrefixBefore + random.RandStr(8) + gnoPkgPath = gno.RealmPathPrefix + random.RandStr(8) } } memPkg := gno.ReadMemPackage(pkgPath, gnoPkgPath) diff --git a/gnovm/cmd/gno/testdata/gno_transpile/05_skip_fmt_flag.txtar b/gnovm/cmd/gno/testdata/gno_transpile/05_skip_fmt_flag.txtar deleted file mode 100644 index c07c670f721..00000000000 --- a/gnovm/cmd/gno/testdata/gno_transpile/05_skip_fmt_flag.txtar +++ /dev/null @@ -1,36 +0,0 @@ -# Run gno transpile with -skip-fmt flag -# NOTE(tb): this flag doesn't actually prevent the code format, because -# `gnolang.Transpile()` calls `format.Node()`. - -gno transpile -skip-fmt . - -! stdout .+ -! stderr .+ - -cmp main.gno.gen.go main.gno.gen.go.golden -cmp sub/sub.gno.gen.go sub/sub.gno.gen.go.golden - --- main.gno -- -package main - -func main(){} - --- sub/sub.gno -- -package sub - --- main.gno.gen.go.golden -- -// Code generated by github.com/gnolang/gno. DO NOT EDIT. - -//go:build gno - -//line main.gno:1:1 -package main - -func main() {} --- sub/sub.gno.gen.go.golden -- -// Code generated by github.com/gnolang/gno. DO NOT EDIT. - -//go:build gno - -//line sub.gno:1:1 -package sub diff --git a/gnovm/cmd/gno/testdata/gno_transpile/06_build_flag.txtar b/gnovm/cmd/gno/testdata/gno_transpile/06_build_flag.txtar deleted file mode 100644 index 110d04959c0..00000000000 --- a/gnovm/cmd/gno/testdata/gno_transpile/06_build_flag.txtar +++ /dev/null @@ -1,38 +0,0 @@ -# Run gno transpile with -gobuild flag - -gno transpile -gobuild . - -! stdout .+ -! stderr .+ - -cmp main.gno.gen.go main.gno.gen.go.golden -cmp sub/sub.gno.gen.go sub/sub.gno.gen.go.golden - --- main.gno -- -package main - -func main() { - var x = 1 - _=x -} --- sub/sub.gno -- -package sub --- main.gno.gen.go.golden -- -// Code generated by github.com/gnolang/gno. DO NOT EDIT. - -//go:build gno - -//line main.gno:1:1 -package main - -func main() { - var x = 1 - _ = x -} --- sub/sub.gno.gen.go.golden -- -// Code generated by github.com/gnolang/gno. DO NOT EDIT. - -//go:build gno - -//line sub.gno:1:1 -package sub diff --git a/gnovm/cmd/gno/testdata/gno_transpile/07_build_flag_with_build_error.txtar b/gnovm/cmd/gno/testdata/gno_transpile/gobuild_flag_build_error.txtar similarity index 100% rename from gnovm/cmd/gno/testdata/gno_transpile/07_build_flag_with_build_error.txtar rename to gnovm/cmd/gno/testdata/gno_transpile/gobuild_flag_build_error.txtar diff --git a/gnovm/cmd/gno/testdata/gno_transpile/08_build_flag_with_parse_error.txtar b/gnovm/cmd/gno/testdata/gno_transpile/gobuild_flag_parse_error.txtar similarity index 100% rename from gnovm/cmd/gno/testdata/gno_transpile/08_build_flag_with_parse_error.txtar rename to gnovm/cmd/gno/testdata/gno_transpile/gobuild_flag_parse_error.txtar diff --git a/gnovm/cmd/gno/testdata/gno_transpile/09_gno_files_whitelist_error.txtar b/gnovm/cmd/gno/testdata/gno_transpile/invalid_import.txtar similarity index 60% rename from gnovm/cmd/gno/testdata/gno_transpile/09_gno_files_whitelist_error.txtar rename to gnovm/cmd/gno/testdata/gno_transpile/invalid_import.txtar index 79d4d6a4a2c..0c51012feb7 100644 --- a/gnovm/cmd/gno/testdata/gno_transpile/09_gno_files_whitelist_error.txtar +++ b/gnovm/cmd/gno/testdata/gno_transpile/invalid_import.txtar @@ -1,10 +1,10 @@ -# Run gno transpile with gno files with whitelist errors +# Run gno transpile with gno files with an invalid import path ! gno transpile . ! stdout .+ -stderr '^main.gno:5:2: import "xxx" is not in the whitelist$' -stderr '^sub/sub.gno:3:8: import "xxx" is not in the whitelist$' +stderr '^main.gno:5:2: import "xxx" does not exist$' +stderr '^sub/sub.gno:3:8: import "xxx" does not exist$' stderr '^2 transpile error\(s\)$' # no *.gen.go files are created diff --git a/gnovm/cmd/gno/testdata/gno_transpile/01_no_args.txtar b/gnovm/cmd/gno/testdata/gno_transpile/no_args.txtar similarity index 100% rename from gnovm/cmd/gno/testdata/gno_transpile/01_no_args.txtar rename to gnovm/cmd/gno/testdata/gno_transpile/no_args.txtar diff --git a/gnovm/cmd/gno/testdata/gno_transpile/03_gno_files_parse_error.txtar b/gnovm/cmd/gno/testdata/gno_transpile/parse_error.txtar similarity index 100% rename from gnovm/cmd/gno/testdata/gno_transpile/03_gno_files_parse_error.txtar rename to gnovm/cmd/gno/testdata/gno_transpile/parse_error.txtar diff --git a/gnovm/cmd/gno/testdata/gno_transpile/02_empty_dir.txtar b/gnovm/cmd/gno/testdata/gno_transpile/valid_empty_dir.txtar similarity index 100% rename from gnovm/cmd/gno/testdata/gno_transpile/02_empty_dir.txtar rename to gnovm/cmd/gno/testdata/gno_transpile/valid_empty_dir.txtar diff --git a/gnovm/cmd/gno/testdata/gno_transpile/valid_gobuild_file.txtar b/gnovm/cmd/gno/testdata/gno_transpile/valid_gobuild_file.txtar new file mode 100644 index 00000000000..40bb1ecb98a --- /dev/null +++ b/gnovm/cmd/gno/testdata/gno_transpile/valid_gobuild_file.txtar @@ -0,0 +1,31 @@ +# Run gno transpile with -gobuild flag on an individual file + +gno transpile -gobuild -v main.gno + +! stdout .+ +cmp stderr stderr.golden + +cmp main.gno.gen.go main.gno.gen.go.golden + +-- stderr.golden -- +main.gno +main.gno [build] +-- main.gno -- +package main + +func main() { + var x = 1 + _=x +} +-- main.gno.gen.go.golden -- +// Code generated by github.com/gnolang/gno. DO NOT EDIT. + +//go:build gno + +//line main.gno:1:1 +package main + +func main() { + var x = 1 + _ = x +} diff --git a/gnovm/cmd/gno/testdata/gno_transpile/valid_gobuild_flag.txtar b/gnovm/cmd/gno/testdata/gno_transpile/valid_gobuild_flag.txtar new file mode 100644 index 00000000000..2eacfb9de60 --- /dev/null +++ b/gnovm/cmd/gno/testdata/gno_transpile/valid_gobuild_flag.txtar @@ -0,0 +1,72 @@ +# Run gno transpile with -gobuild flag + +gno transpile -gobuild -v . + +! stdout .+ +cmp stderr stderr.golden + +# The test file will be excluded from transpilation unless we pass it explicitly. +cmp main.gno.gen.go main.gno.gen.go.golden +! exists .main_test.gno.gen_test.go +cmp sub/sub.gno.gen.go sub/sub.gno.gen.go.golden +rm mai.gno.gen.gosub/sub.gno.gen.go + +# Re-try, but use an absolute path. +gno transpile -gobuild -v $WORK + +! stdout .+ +cmpenv stderr stderr2.golden + +cmp main.gno.gen.go main.gno.gen.go.golden +! exists .main_test.gno.gen_test.go +cmp sub/sub.gno.gen.go sub/sub.gno.gen.go.golden + +-- stderr.golden -- +. +sub +. [build] +sub [build] +-- stderr2.golden -- +$WORK +$WORK/sub +$WORK [build] +$WORK/sub [build] +-- main.gno -- +package main + +func main() { + var x = 1 + _=x +} +-- main.gno.gen.go.golden -- +// Code generated by github.com/gnolang/gno. DO NOT EDIT. + +//go:build gno + +//line main.gno:1:1 +package main + +func main() { + var x = 1 + _ = x +} +-- main_test.gno -- +package main + +import ( + "testing" + "badimport" +) + +func TestMain(t *testing.T) { + badimport.DoesNotExist() +} +-- sub/sub.gno -- +package sub +-- sub/sub.gno.gen.go.golden -- +// Code generated by github.com/gnolang/gno. DO NOT EDIT. + +//go:build gno + +//line sub.gno:1:1 +package sub diff --git a/gnovm/cmd/gno/testdata/gno_transpile/valid_output_flag.txtar b/gnovm/cmd/gno/testdata/gno_transpile/valid_output_flag.txtar new file mode 100644 index 00000000000..b1a63890f46 --- /dev/null +++ b/gnovm/cmd/gno/testdata/gno_transpile/valid_output_flag.txtar @@ -0,0 +1,41 @@ +# Run gno transpile with valid gno files, using the -output flag. + +gno transpile -v -output directory/hello/ . +! stdout .+ +cmp stderr stderr1.golden + +exists directory/hello/main.gno.gen.go +! exists main.gno.gen.go +rm directory + +# Try running using the absolute path to the directory. +gno transpile -v -output directory/hello $WORK +! stdout .+ +cmpenv stderr stderr2.golden + +exists directory/hello$WORK/main.gno.gen.go +! exists directory/hello/main.gno.gen.go +rm directory + +# Try running in subdirectory, using a "relative non-local path." (ie. has "../") +mkdir subdir +cd subdir +gno transpile -v -output hello .. +! stdout .+ +cmpenv stderr ../stderr3.golden + +exists hello$WORK/main.gno.gen.go +! exists main.gno.gen.go + +-- stderr1.golden -- +. +-- stderr2.golden -- +$WORK +-- stderr3.golden -- +.. +-- main.gno -- +package main + +func main() { + println("hello") +} diff --git a/gnovm/cmd/gno/testdata/gno_transpile/valid_output_gobuild.txtar b/gnovm/cmd/gno/testdata/gno_transpile/valid_output_gobuild.txtar new file mode 100644 index 00000000000..3540e865f3e --- /dev/null +++ b/gnovm/cmd/gno/testdata/gno_transpile/valid_output_gobuild.txtar @@ -0,0 +1,44 @@ +# Run gno transpile with valid gno files, using the -output and -gobuild flags together. + +gno transpile -v -output directory/hello/ -gobuild . +! stdout .+ +cmp stderr stderr1.golden + +exists directory/hello/main.gno.gen.go +! exists main.gno.gen.go +rm directory + +# Try running using the absolute path to the directory. +gno transpile -v -output directory/hello -gobuild $WORK +! stdout .+ +cmpenv stderr stderr2.golden + +exists directory/hello$WORK/main.gno.gen.go +! exists directory/hello/main.gno.gen.go +rm directory + +# Try running in subdirectory, using a "relative non-local path." (ie. has "../") +mkdir subdir +cd subdir +gno transpile -v -output hello -gobuild .. +! stdout .+ +cmpenv stderr ../stderr3.golden + +exists hello$WORK/main.gno.gen.go +! exists main.gno.gen.go + +-- stderr1.golden -- +. +directory/hello [build] +-- stderr2.golden -- +$WORK +directory/hello$WORK [build] +-- stderr3.golden -- +.. +hello$WORK [build] +-- main.gno -- +package main + +func main() { + println("hello") +} diff --git a/gnovm/cmd/gno/testdata/gno_transpile/valid_transpile_file.txtar b/gnovm/cmd/gno/testdata/gno_transpile/valid_transpile_file.txtar new file mode 100644 index 00000000000..86cc6f12f7a --- /dev/null +++ b/gnovm/cmd/gno/testdata/gno_transpile/valid_transpile_file.txtar @@ -0,0 +1,65 @@ +# Run gno transpile with an individual file. + +# Running transpile on the current directory should only precompile +# main.gno. +gno transpile -v . + +! stdout .+ +stderr ^\.$ + +exists main.gno.gen.go +! exists .hello_test.gno.gen_test.go +rm main.gno.gen.go + +# Running it using individual filenames should precompile hello_test.gno, as well. +gno transpile -v main.gno hello_test.gno + +! stdout .+ +cmp stderr transpile-files-stderr.golden + +cmp main.gno.gen.go main.gno.gen.go.golden +cmp .hello_test.gno.gen_test.go .hello_test.gno.gen_test.go.golden + +-- transpile-files-stderr.golden -- +main.gno +hello_test.gno +-- main.gno -- +package main + +func main() { + println("hello") +} + +-- hello_test.gno -- +package main + +import "std" + +func hello() { + std.AssertOriginCall() +} + +-- main.gno.gen.go.golden -- +// Code generated by github.com/gnolang/gno. DO NOT EDIT. + +//go:build gno + +//line main.gno:1:1 +package main + +func main() { + println("hello") +} +-- .hello_test.gno.gen_test.go.golden -- +// Code generated by github.com/gnolang/gno. DO NOT EDIT. + +//go:build gno && test + +//line hello_test.gno:1:1 +package main + +import "github.com/gnolang/gno/gnovm/stdlibs/std" + +func hello() { + std.AssertOriginCall(nil) +} diff --git a/gnovm/cmd/gno/testdata/gno_transpile/04_valid_gno_files.txtar b/gnovm/cmd/gno/testdata/gno_transpile/valid_transpile_package.txtar similarity index 100% rename from gnovm/cmd/gno/testdata/gno_transpile/04_valid_gno_files.txtar rename to gnovm/cmd/gno/testdata/gno_transpile/valid_transpile_package.txtar diff --git a/gnovm/cmd/gno/testdata/gno_transpile/valid_transpile_tree.txtar b/gnovm/cmd/gno/testdata/gno_transpile/valid_transpile_tree.txtar new file mode 100644 index 00000000000..a765ab5093b --- /dev/null +++ b/gnovm/cmd/gno/testdata/gno_transpile/valid_transpile_tree.txtar @@ -0,0 +1,94 @@ +# Run gno transpile with dependencies +env GNOROOT=$WORK + +gno transpile -v ./examples + +! stdout .+ +cmpenv stderr stderr.golden + +! exists examples/gno.land/r/question/question.gno.gen.go +cmp examples/gno.land/r/answer/answer.gno.gen.go examples/gno.land/r/answer/answer.gno.gen.go.golden +cmp examples/gno.land/r/answer/anti_answer.gno.gen.go examples/gno.land/r/answer/anti_answer.gno.gen.go.golden +cmp gnovm/stdlibs/math/math.gno.gen.go gnovm/stdlibs/math/math.gno.gen.go.golden + +-- stderr.golden -- +examples/gno.land/r/answer +$WORK/gnovm/stdlibs/math +examples/gno.land/r/question (skipped, gno.mod marks module as draft) +-- examples/gno.land/r/question/gno.mod -- +// Draft + +module gno.land/r/question + +-- examples/gno.land/r/question/question.gno -- +package question + +func Question() string { + return "What is the answer to Life, The Universe and Everything?" + invalid syntax +} + +-- examples/gno.land/r/answer/answer.gno -- +package answer + +import "math" + +func Answer() int { + return math.Sqrt(1764) +} + +-- examples/gno.land/r/answer/answer.gno.gen.go.golden -- +// Code generated by github.com/gnolang/gno. DO NOT EDIT. + +//go:build gno + +//line answer.gno:1:1 +package answer + +import "github.com/gnolang/gno/gnovm/stdlibs/math" + +func Answer() int { + return math.Sqrt(1764) +} +-- examples/gno.land/r/answer/anti_answer.gno -- +package answer + +import "math" + +func AntiAnswer() int { + return -math.Sqrt(1764) +} +-- examples/gno.land/r/answer/anti_answer.gno.gen.go.golden -- +// Code generated by github.com/gnolang/gno. DO NOT EDIT. + +//go:build gno + +//line anti_answer.gno:1:1 +package answer + +import "github.com/gnolang/gno/gnovm/stdlibs/math" + +func AntiAnswer() int { + return -math.Sqrt(1764) +} +-- examples/gno.land/r/answer/gno.mod -- +module gno.land/r/answer + +-- gnovm/stdlibs/math/math.gno -- +package math + +func Sqrt(i int) int { + return 42 +} + +-- gnovm/stdlibs/math/math.gno.gen.go.golden -- +// Code generated by github.com/gnolang/gno. DO NOT EDIT. + +//go:build gno + +//line math.gno:1:1 +package math + +func Sqrt(i int) int { + return 42 +} diff --git a/gnovm/cmd/gno/transpile.go b/gnovm/cmd/gno/transpile.go index 84744451f9d..2e12ee6f4b3 100644 --- a/gnovm/cmd/gno/transpile.go +++ b/gnovm/cmd/gno/transpile.go @@ -5,48 +5,61 @@ import ( "errors" "flag" "fmt" + "go/ast" "go/scanner" - "log" + "go/token" "os" + "os/exec" "path/filepath" + "regexp" + "slices" + "strconv" + "strings" + "github.com/gnolang/gno/gnovm/pkg/gnoenv" + "github.com/gnolang/gno/gnovm/pkg/gnomod" "github.com/gnolang/gno/gnovm/pkg/transpiler" "github.com/gnolang/gno/tm2/pkg/commands" ) -type importPath string - type transpileCfg struct { verbose bool - skipFmt bool + rootDir string skipImports bool gobuild bool goBinary string - gofmtBinary string output string } type transpileOptions struct { cfg *transpileCfg + // CLI output + io commands.IO // transpiled is the set of packages already // transpiled from .gno to .go. - transpiled map[importPath]struct{} + transpiled map[string]struct{} + // skipped packages (gno mod marks them as draft) + skipped []string } -func newTranspileOptions(cfg *transpileCfg) *transpileOptions { - return &transpileOptions{cfg, map[importPath]struct{}{}} +func newTranspileOptions(cfg *transpileCfg, io commands.IO) *transpileOptions { + return &transpileOptions{ + cfg: cfg, + io: io, + transpiled: map[string]struct{}{}, + } } func (p *transpileOptions) getFlags() *transpileCfg { return p.cfg } -func (p *transpileOptions) isTranspiled(pkg importPath) bool { +func (p *transpileOptions) isTranspiled(pkg string) bool { _, transpiled := p.transpiled[pkg] return transpiled } -func (p *transpileOptions) markAsTranspiled(pkg importPath) { +func (p *transpileOptions) markAsTranspiled(pkg string) { p.transpiled[pkg] = struct{}{} } @@ -74,11 +87,11 @@ func (c *transpileCfg) RegisterFlags(fs *flag.FlagSet) { "verbose output when running", ) - fs.BoolVar( - &c.skipFmt, - "skip-fmt", - false, - "do not check syntax of generated .go files", + fs.StringVar( + &c.rootDir, + "root-dir", + "", + "clone location of github.com/gnolang/gno (gno tries to guess it)", ) fs.BoolVar( @@ -102,13 +115,6 @@ func (c *transpileCfg) RegisterFlags(fs *flag.FlagSet) { "go binary to use for building", ) - fs.StringVar( - &c.gofmtBinary, - "go-fmt-binary", - "gofmt", - "gofmt binary to use for syntax checking", - ) - fs.StringVar( &c.output, "output", @@ -122,33 +128,54 @@ func execTranspile(cfg *transpileCfg, args []string, io commands.IO) error { return flag.ErrHelp } - // transpile .gno files. - paths, err := gnoFilesFromArgs(args) + // guess cfg.RootDir + if cfg.rootDir == "" { + cfg.rootDir = gnoenv.RootDir() + } + + // transpile .gno packages and files. + paths, err := gnoPackagesFromArgs(args) if err != nil { return fmt.Errorf("list paths: %w", err) } - opts := newTranspileOptions(cfg) + opts := newTranspileOptions(cfg, io) var errlist scanner.ErrorList - for _, filepath := range paths { - if err := transpileFile(filepath, opts); err != nil { + for _, path := range paths { + st, err := os.Stat(path) + if err != nil { + return err + } + if st.IsDir() { + err = transpilePkg(path, opts) + } else { + if opts.cfg.verbose { + io.ErrPrintln(filepath.Clean(path)) + } + + err = transpileFile(path, opts) + } + if err != nil { var fileErrlist scanner.ErrorList if !errors.As(err, &fileErrlist) { // Not an scanner.ErrorList: return immediately. - return fmt.Errorf("%s: transpile: %w", filepath, err) + return fmt.Errorf("%s: transpile: %w", path, err) } errlist = append(errlist, fileErrlist...) } } if errlist.Len() == 0 && cfg.gobuild { - paths, err := gnoPackagesFromArgs(args) - if err != nil { - return fmt.Errorf("list packages: %w", err) - } - for _, pkgPath := range paths { - err := goBuildFileOrPkg(pkgPath, cfg) + if slices.Contains(opts.skipped, pkgPath) { + continue + } + if cfg.output != "." { + if pkgPath, err = ResolvePath(cfg.output, pkgPath); err != nil { + return fmt.Errorf("resolve output path: %w", err) + } + } + err := goBuildFileOrPkg(io, pkgPath, cfg) if err != nil { var fileErrlist scanner.ErrorList if !errors.As(err, &fileErrlist) { @@ -169,19 +196,41 @@ func execTranspile(cfg *transpileCfg, args []string, io commands.IO) error { return nil } -func transpilePkg(pkgPath importPath, opts *transpileOptions) error { - if opts.isTranspiled(pkgPath) { +// transpilePkg transpiles all non-test files at the given location. +// Additionally, it checks the gno.mod in said location, and skips it if it is +// a draft module +func transpilePkg(dirPath string, opts *transpileOptions) error { + if opts.isTranspiled(dirPath) { + return nil + } + opts.markAsTranspiled(dirPath) + + gmod, err := gnomod.ParseAt(dirPath) + if err != nil && !errors.Is(err, gnomod.ErrGnoModNotFound) { + return err + } + if err == nil && gmod.Draft { + if opts.cfg.verbose { + opts.io.ErrPrintfln("%s (skipped, gno.mod marks module as draft)", filepath.Clean(dirPath)) + } + opts.skipped = append(opts.skipped, dirPath) return nil } - opts.markAsTranspiled(pkgPath) - files, err := filepath.Glob(filepath.Join(string(pkgPath), "*.gno")) + // XXX(morgan): Currently avoiding test files as they contain imports like "fmt". + // The transpiler doesn't currently support "test stdlibs", and even if it + // did all packages like "fmt" would have to exist as standard libraries to work. + // Easier to skip for now. + files, err := listNonTestFiles(dirPath) if err != nil { - log.Fatal(err) + return err } + if opts.cfg.verbose { + opts.io.ErrPrintln(filepath.Clean(dirPath)) + } for _, file := range files { - if err = transpileFile(file, opts); err != nil { + if err := transpileFile(file, opts); err != nil { return fmt.Errorf("%s: %w", file, err) } } @@ -191,14 +240,6 @@ func transpilePkg(pkgPath importPath, opts *transpileOptions) error { func transpileFile(srcPath string, opts *transpileOptions) error { flags := opts.getFlags() - gofmt := flags.gofmtBinary - if gofmt == "" { - gofmt = "gofmt" - } - - if flags.verbose { - fmt.Fprintf(os.Stderr, "%s\n", srcPath) - } // parse .gno. source, err := os.ReadFile(srcPath) @@ -207,7 +248,7 @@ func transpileFile(srcPath string, opts *transpileOptions) error { } // compute attributes based on filename. - targetFilename, tags := transpiler.GetTranspileFilenameAndTags(srcPath) + targetFilename, tags := transpiler.TranspiledFilenameAndTags(srcPath) // preprocess. transpileRes, err := transpiler.Transpile(string(source), tags, srcPath) @@ -218,7 +259,7 @@ func transpileFile(srcPath string, opts *transpileOptions) error { // resolve target path var targetPath string if flags.output != "." { - path, err := ResolvePath(flags.output, importPath(filepath.Dir(srcPath))) + path, err := ResolvePath(flags.output, filepath.Dir(srcPath)) if err != nil { return fmt.Errorf("resolve output path: %w", err) } @@ -233,18 +274,14 @@ func transpileFile(srcPath string, opts *transpileOptions) error { return fmt.Errorf("write .go file: %w", err) } - // check .go fmt, if `SkipFmt` sets to false. - if !flags.skipFmt { - err = transpiler.TranspileVerifyFile(targetPath, gofmt) + // transpile imported packages, if `SkipImports` sets to false + if !flags.skipImports && + !strings.HasSuffix(srcPath, "_filetest.gno") && !strings.HasSuffix(srcPath, "_test.gno") { + dirPaths, err := getPathsFromImportSpec(opts.cfg.rootDir, transpileRes.Imports) if err != nil { - return fmt.Errorf("check .go file: %w", err) + return err } - } - - // transpile imported packages, if `SkipImports` sets to false - if !flags.skipImports { - importPaths := getPathsFromImportSpec(transpileRes.Imports) - for _, path := range importPaths { + for _, path := range dirPaths { if err := transpilePkg(path, opts); err != nil { return err } @@ -254,13 +291,122 @@ func transpileFile(srcPath string, opts *transpileOptions) error { return nil } -func goBuildFileOrPkg(fileOrPkg string, cfg *transpileCfg) error { +func goBuildFileOrPkg(io commands.IO, fileOrPkg string, cfg *transpileCfg) error { verbose := cfg.verbose goBinary := cfg.goBinary if verbose { - fmt.Fprintf(os.Stderr, "%s\n", fileOrPkg) + io.ErrPrintfln("%s [build]", filepath.Clean(fileOrPkg)) + } + + return buildTranspiledPackage(fileOrPkg, goBinary) +} + +// getPathsFromImportSpec returns the directory paths where the code for each +// importSpec is stored (assuming they start with [transpiler.ImportPrefix]). +func getPathsFromImportSpec(rootDir string, importSpec []*ast.ImportSpec) (dirs []string, err error) { + for _, i := range importSpec { + path, err := strconv.Unquote(i.Path.Value) + if err != nil { + return nil, err + } + if strings.HasPrefix(path, transpiler.ImportPrefix) { + res := strings.TrimPrefix(path, transpiler.ImportPrefix) + + dirs = append(dirs, rootDir+filepath.FromSlash(res)) + } + } + return +} + +// buildTranspiledPackage tries to run `go build` against the transpiled .go files. +// +// This method is the most efficient to detect errors but requires that +// all the import are valid and available. +func buildTranspiledPackage(fileOrPkg, goBinary string) error { + // TODO: use cmd/compile instead of exec? + // TODO: find the nearest go.mod file, chdir in the same folder, trim prefix? + // TODO: temporarily create an in-memory go.mod or disable go modules for gno? + // TODO: ignore .go files that were not generated from gno? + + info, err := os.Stat(fileOrPkg) + if err != nil { + return fmt.Errorf("invalid file or package path %s: %w", fileOrPkg, err) + } + var ( + target string + chdir string + ) + if !info.IsDir() { + dstFilename, _ := transpiler.TranspiledFilenameAndTags(fileOrPkg) + // Makes clear to go compiler that this is a relative path, + // rather than a path to a package/module. + // can't use filepath.Join as it cleans its results. + target = filepath.Dir(fileOrPkg) + string(filepath.Separator) + dstFilename + } else { + // Go does not allow building packages using absolute paths, and requires + // relative paths to always be prefixed with `./` (because the argument + // go expects are import paths, not directories). + // To circumvent this, we use the -C flag to chdir into the right + // directory, then run `go build .` + chdir = fileOrPkg + target = "." + } + + // pre-alloc max 5 args + args := append(make([]string, 0, 5), "build") + if chdir != "" { + args = append(args, "-C", chdir) + } + args = append(args, "-tags=gno", target) + cmd := exec.Command(goBinary, args...) + out, err := cmd.CombinedOutput() + if errors.As(err, new(*exec.ExitError)) { + // there was a non-zero exit code; parse the go build errors + return parseGoBuildErrors(string(out)) + } + // other kinds of errors; return + return err +} + +var ( + reGoBuildError = regexp.MustCompile(`(?m)^(\S+):(\d+):(\d+): (.+)$`) + reGoBuildComment = regexp.MustCompile(`(?m)^#.*$`) +) + +// parseGoBuildErrors returns a scanner.ErrorList filled with all errors found +// in out, which is supposed to be the output of the `go build` command. +// +// TODO(tb): update when `go build -json` is released to replace regexp usage. +// See https://github.com/golang/go/issues/62067 +func parseGoBuildErrors(out string) error { + var errList scanner.ErrorList + matches := reGoBuildError.FindAllStringSubmatch(out, -1) + for _, match := range matches { + filename := match[1] + line, err := strconv.Atoi(match[2]) + if err != nil { + return fmt.Errorf("parse line go build error %s: %w", match, err) + } + + column, err := strconv.Atoi(match[3]) + if err != nil { + return fmt.Errorf("parse column go build error %s: %w", match, err) + } + msg := match[4] + errList.Add(token.Position{ + Filename: filename, + Line: line, + Column: column, + }, msg) + } + + replaced := reGoBuildError.ReplaceAllLiteralString(out, "") + replaced = reGoBuildComment.ReplaceAllString(replaced, "") + replaced = strings.TrimSpace(replaced) + if replaced != "" { + errList.Add(token.Position{}, "Additional go build errors:\n"+replaced) } - return transpiler.TranspileBuildPackage(fileOrPkg, goBinary) + return errList.Err() } diff --git a/gnovm/cmd/gno/transpile_test.go b/gnovm/cmd/gno/transpile_test.go index 2770026a01a..827c09e23f1 100644 --- a/gnovm/cmd/gno/transpile_test.go +++ b/gnovm/cmd/gno/transpile_test.go @@ -1,9 +1,13 @@ package main import ( + "go/scanner" + "go/token" + "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" @@ -24,3 +28,79 @@ func Test_ScriptsTranspile(t *testing.T) { testscript.Run(t, p) } + +func Test_parseGoBuildErrors(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + output string + expectedError error + expectedErrorIs error + }{ + { + name: "empty output", + output: "", + expectedError: nil, + }, + { + name: "random output", + output: "xxx", + expectedError: scanner.ErrorList{ + &scanner.Error{ + Msg: "Additional go build errors:\nxxx", + }, + }, + }, + { + name: "some errors", + output: `xxx +main.gno:6:2: nasty error +pkg/file.gno:60:20: ugly error`, + expectedError: scanner.ErrorList{ + &scanner.Error{ + Pos: token.Position{ + Filename: "main.gno", + Line: 6, + Column: 2, + }, + Msg: "nasty error", + }, + &scanner.Error{ + Pos: token.Position{ + Filename: "pkg/file.gno", + Line: 60, + Column: 20, + }, + Msg: "ugly error", + }, + &scanner.Error{ + Msg: "Additional go build errors:\nxxx", + }, + }, + }, + { + name: "line parse error", + output: `main.gno:9000000000000000000000000000000000000000000000000000:11: error`, + expectedErrorIs: strconv.ErrRange, + }, + { + name: "column parse error", + output: `main.gno:1:9000000000000000000000000000000000000000000000000000: error`, + expectedErrorIs: strconv.ErrRange, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + err := parseGoBuildErrors(tt.output) + if eis := tt.expectedErrorIs; eis != nil { + assert.ErrorIs(t, err, eis) + } else { + assert.Equal(t, tt.expectedError, err) + } + }) + } +} diff --git a/gnovm/cmd/gno/util.go b/gnovm/cmd/gno/util.go index d9ec775dfca..480161c2b7e 100644 --- a/gnovm/cmd/gno/util.go +++ b/gnovm/cmd/gno/util.go @@ -2,7 +2,6 @@ package main import ( "fmt" - "go/ast" "io" "io/fs" "os" @@ -10,9 +9,6 @@ import ( "regexp" "strings" "time" - - "github.com/gnolang/gno/gnovm/pkg/gnoenv" - "github.com/gnolang/gno/gnovm/pkg/transpiler" ) func isGnoFile(f fs.DirEntry) bool { @@ -25,84 +21,69 @@ func isFileExist(path string) bool { return err == nil } -func gnoFilesFromArgs(args []string) ([]string, error) { - paths := []string{} - for _, arg := range args { - info, err := os.Stat(arg) +func gnoPackagesFromArgs(args []string) ([]string, error) { + var paths []string + + for _, argPath := range args { + info, err := os.Stat(argPath) if err != nil { return nil, fmt.Errorf("invalid file or package path: %w", err) } + if !info.IsDir() { - curpath := arg - paths = append(paths, curpath) - } else { - err = filepath.WalkDir(arg, func(curpath string, f fs.DirEntry, err error) error { - if err != nil { - return fmt.Errorf("%s: walk dir: %w", arg, err) - } - - if !isGnoFile(f) { - return nil // skip - } - paths = append(paths, curpath) - return nil - }) - if err != nil { - return nil, err - } + paths = append(paths, ensurePathPrefix(argPath)) + + continue + } + + // Gather package paths from the directory + err = walkDirForGnoFiles(argPath, func(path string) { + paths = append(paths, ensurePathPrefix(path)) + }) + if err != nil { + return nil, fmt.Errorf("unable to walk dir: %w", err) } } + return paths, nil } -func gnoPackagesFromArgs(args []string) ([]string, error) { - paths := []string{} - for _, arg := range args { - info, err := os.Stat(arg) +func ensurePathPrefix(path string) string { + if filepath.IsAbs(path) { + return path + } + + // cannot use path.Join or filepath.Join, because we need + // to ensure that ./ is the prefix to pass to go build. + // if not absolute. + return "." + string(filepath.Separator) + path +} + +func walkDirForGnoFiles(root string, addPath func(path string)) error { + visited := make(map[string]struct{}) + + walkFn := func(currPath string, f fs.DirEntry, err error) error { if err != nil { - return nil, fmt.Errorf("invalid file or package path: %w", err) + return fmt.Errorf("%s: walk dir: %w", root, err) } - if !info.IsDir() { - paths = append(paths, arg) - } else { - // if the passed arg is a dir, then we'll recursively walk the dir - // and look for directories containing at least one .gno file. - - visited := map[string]bool{} // used to run the builder only once per folder. - err = filepath.WalkDir(arg, func(curpath string, f fs.DirEntry, err error) error { - if err != nil { - return fmt.Errorf("%s: walk dir: %w", arg, err) - } - if f.IsDir() { - return nil // skip - } - if !isGnoFile(f) { - return nil // skip - } - - parentDir := filepath.Dir(curpath) - if _, found := visited[parentDir]; found { - return nil - } - visited[parentDir] = true - - pkg := parentDir - if !filepath.IsAbs(parentDir) { - // cannot use path.Join or filepath.Join, because we need - // to ensure that ./ is the prefix to pass to go build. - // if not absolute. - pkg = "./" + parentDir - } - - paths = append(paths, pkg) - return nil - }) - if err != nil { - return nil, err - } + + if f.IsDir() || !isGnoFile(f) { + return nil } + + parentDir := filepath.Dir(currPath) + if _, found := visited[parentDir]; found { + return nil + } + + visited[parentDir] = struct{}{} + + addPath(parentDir) + + return nil } - return paths, nil + + return filepath.WalkDir(root, walkFn) } // targetsFromPatterns returns a list of target paths that match the patterns. @@ -192,36 +173,27 @@ func fmtDuration(d time.Duration) string { return fmt.Sprintf("%.2fs", d.Seconds()) } -// getPathsFromImportSpec derive and returns ImportPaths -// without ImportPrefix from *ast.ImportSpec -func getPathsFromImportSpec(importSpec []*ast.ImportSpec) (importPaths []importPath) { - for _, i := range importSpec { - path := i.Path.Value[1 : len(i.Path.Value)-1] // trim leading and trailing `"` - if strings.HasPrefix(path, transpiler.ImportPrefix) { - res := strings.TrimPrefix(path, transpiler.ImportPrefix) - importPaths = append(importPaths, importPath("."+res)) - } +// ResolvePath determines the path where to place output files. +// output is the output directory provided by the user. +// dstPath is the desired output path by the gno program. +// +// If dstPath is relative non-local path (ie. contains ../), the dstPath will +// be made absolute and joined with output. +// +// Otherwise, the result is simply filepath.Join(output, dstPath). +// +// See related test for examples. +func ResolvePath(output, dstPath string) (string, error) { + if filepath.IsAbs(dstPath) || + filepath.IsLocal(dstPath) { + return filepath.Join(output, dstPath), nil } - return -} - -// ResolvePath joins the output dir with relative pkg path -// e.g -// Output Dir: Temp/gno-transpile -// Pkg Path: ../example/gno.land/p/pkg -// Returns -> Temp/gno-transpile/example/gno.land/p/pkg -func ResolvePath(output string, path importPath) (string, error) { - absOutput, err := filepath.Abs(output) + // Make dstPath absolute and join it with output. + absDst, err := filepath.Abs(dstPath) if err != nil { return "", err } - absPkgPath, err := filepath.Abs(string(path)) - if err != nil { - return "", err - } - pkgPath := strings.TrimPrefix(absPkgPath, gnoenv.RootDir()) - - return filepath.Join(absOutput, pkgPath), nil + return filepath.Join(output, absDst), nil } // WriteDirFile write file to the path and also create diff --git a/gnovm/cmd/gno/util_test.go b/gnovm/cmd/gno/util_test.go index 9e9659bfe4f..a92c924e272 100644 --- a/gnovm/cmd/gno/util_test.go +++ b/gnovm/cmd/gno/util_test.go @@ -295,3 +295,50 @@ func createGnoPackages(t *testing.T, tmpDir string) { } } } + +func TestResolvePath(t *testing.T) { + t.Parallel() + + if os.PathSeparator != '/' { + t.Skip("ResolvePath test is only written of UNIX-like filesystems") + } + wd, err := os.Getwd() + require.NoError(t, err) + tt := []struct { + output string + dstPath string + result string + }{ + { + "transpile-result", + "./examples/test/test1.gno.gen.go", + "transpile-result/examples/test/test1.gno.gen.go", + }, + { + "/transpile-result", + "./examples/test/test1.gno.gen.go", + "/transpile-result/examples/test/test1.gno.gen.go", + }, + { + "/transpile-result", + "/home/gno/examples/test/test1.gno.gen.go", + "/transpile-result/home/gno/examples/test/test1.gno.gen.go", + }, + { + "result", + "../hello", + filepath.Join("result", filepath.Join(wd, "../hello")), + }, + } + + for _, tc := range tt { + res, err := ResolvePath(tc.output, tc.dstPath) + // ResolvePath should error only in case we can't get the abs path; + // so never in normal conditions. + require.NoError(t, err) + assert.Equal(t, + tc.result, res, + "unexpected result of ResolvePath(%q, %q)", tc.output, tc.dstPath, + ) + } +} diff --git a/gnovm/pkg/gnolang/alloc.go b/gnovm/pkg/gnolang/alloc.go index 495be0d2dc2..6fef5eda834 100644 --- a/gnovm/pkg/gnolang/alloc.go +++ b/gnovm/pkg/gnolang/alloc.go @@ -62,6 +62,7 @@ const ( // allocPackge = 1 allocAmino = _allocBase + _allocPointer + _allocAny allocAminoByte = 10 // XXX + allocHeapItem = _allocBase + _allocPointer + _allocTypedValue ) func NewAllocator(maxBytes int64) *Allocator { @@ -180,6 +181,10 @@ func (alloc *Allocator) AllocateAmino(l int64) { alloc.Allocate(allocAmino + allocAminoByte*l) } +func (alloc *Allocator) AllocateHeapItem() { + alloc.Allocate(allocHeapItem) +} + //---------------------------------------- // constructor utilities. @@ -291,3 +296,8 @@ func (alloc *Allocator) NewType(t Type) Type { alloc.AllocateType() return t } + +func (alloc *Allocator) NewHeapItem(tv TypedValue) *HeapItemValue { + alloc.AllocateHeapItem() + return &HeapItemValue{Value: tv} +} diff --git a/gnovm/pkg/gnolang/debugger_test.go b/gnovm/pkg/gnolang/debugger_test.go index 1b5a72a183a..3f2f58b9709 100644 --- a/gnovm/pkg/gnolang/debugger_test.go +++ b/gnovm/pkg/gnolang/debugger_test.go @@ -31,9 +31,9 @@ func evalTest(debugAddr, in, file string) (out, err string) { stdout := writeNopCloser{bout} stderr := writeNopCloser{berr} debug := in != "" || debugAddr != "" - mode := tests.ImportModeNativePreferred - if strings.HasSuffix(file, "_stdlibs.gno") { - mode = tests.ImportModeStdlibsPreferred + mode := tests.ImportModeStdlibsPreferred + if strings.HasSuffix(file, "_native.gno") { + mode = tests.ImportModeNativePreferred } defer func() { @@ -196,7 +196,8 @@ func TestRemoteDebug(t *testing.T) { func TestRemoteError(t *testing.T) { _, err := evalTest(":xxx", "", debugTarget) t.Log("err:", err) - if !strings.Contains(err, "tcp/xxx: unknown port") { + if !strings.Contains(err, "tcp/xxx: unknown port") && + !strings.Contains(err, "tcp/xxx: nodename nor servname provided, or not known") { t.Error(err) } } diff --git a/gnovm/pkg/gnolang/gno_test.go b/gnovm/pkg/gnolang/gno_test.go index 24a92a81312..54d808faefc 100644 --- a/gnovm/pkg/gnolang/gno_test.go +++ b/gnovm/pkg/gnolang/gno_test.go @@ -159,6 +159,38 @@ func main() { m.RunMain() } +func BenchmarkPreprocessForLoop(b *testing.B) { + m := NewMachine("test", nil) + c := `package test +func main() { + for i:=0; i<10000; i++ {} +}` + n := MustParseFile("main.go", c) + m.RunFiles(n) + + for i := 0; i < b.N; i++ { + m.RunMain() + } +} + +func BenchmarkIfStatement(b *testing.B) { + m := NewMachine("test", nil) + c := `package test +func main() { + for i:=0; i<10000; i++ { + if i > 10 { + + } + } +}` + n := MustParseFile("main.go", c) + m.RunFiles(n) + + for i := 0; i < b.N; i++ { + m.RunMain() + } +} + func TestDoOpEvalBaseConversion(t *testing.T) { m := NewMachine("test", nil) diff --git a/gnovm/pkg/gnolang/gnolang.proto b/gnovm/pkg/gnolang/gnolang.proto index f7eaa907ec5..9904e26078f 100644 --- a/gnovm/pkg/gnolang/gnolang.proto +++ b/gnovm/pkg/gnolang/gnolang.proto @@ -58,6 +58,8 @@ message FuncValue { google.protobuf.Any closure = 5 [json_name = "Closure"]; string file_name = 6 [json_name = "FileName"]; string pkg_path = 7 [json_name = "PkgPath"]; + string native_pkg = 8 [json_name = "NativePkg"]; + string native_name = 9 [json_name = "NativeName"]; } message MapValue { diff --git a/gnovm/pkg/gnolang/gonative.go b/gnovm/pkg/gnolang/gonative.go index f73a5c58962..6127fa42b07 100644 --- a/gnovm/pkg/gnolang/gonative.go +++ b/gnovm/pkg/gnolang/gonative.go @@ -890,7 +890,7 @@ func gno2GoType(t Type) reflect.Type { // If gno2GoTypeMatches(t, rt) is true, a t value can // be converted to an rt native value using gno2GoValue(v, rv). -// This is called when autoNative is true in checkType(). +// This is called when autoNative is true in assertAssignableTo(). // This is used for all native function calls, and also // for testing whether a native value implements a gno interface. func gno2GoTypeMatches(t Type, rt reflect.Type) (result bool) { diff --git a/gnovm/pkg/gnolang/helpers.go b/gnovm/pkg/gnolang/helpers.go index 564ac0622c2..c6f7e696ea4 100644 --- a/gnovm/pkg/gnolang/helpers.go +++ b/gnovm/pkg/gnolang/helpers.go @@ -7,6 +7,34 @@ import ( "strings" ) +// ---------------------------------------- +// 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/" + +// 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$`) + +// 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 + !ReGnoRunPath.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/") +} + // ---------------------------------------- // AST Construction (Expr) // These are copied over from go-amino-x, but produces Gno ASTs. diff --git a/gnovm/pkg/gnolang/helpers_test.go b/gnovm/pkg/gnolang/helpers_test.go new file mode 100644 index 00000000000..af8fa64ac79 --- /dev/null +++ b/gnovm/pkg/gnolang/helpers_test.go @@ -0,0 +1,55 @@ +package gnolang + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestIsRealmPath(t *testing.T) { + t.Parallel() + tt := []struct { + input string + result bool + }{ + {"gno.land/r/demo/users", true}, + {"gno.land/r/hello", true}, + {"gno.land/p/demo/users", false}, + {"gno.land/p/hello", false}, + {"gno.land/x", false}, + {"std", false}, + } + + for _, tc := range tt { + assert.Equal( + t, + tc.result, + IsRealmPath(tc.input), + "unexpected IsRealmPath(%q) result", tc.input, + ) + } +} + +func TestIsStdlib(t *testing.T) { + t.Parallel() + + tt := []struct { + s string + result bool + }{ + {"std", true}, + {"math", true}, + {"very/long/path/with_underscores", true}, + {"gno.land/r/demo/users", false}, + {"gno.land/hello", false}, + } + + for _, tc := range tt { + assert.Equal( + t, + tc.result, + IsStdlib(tc.s), + "IsStdlib(%q)", tc.s, + ) + } +} diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go index 48a2145af3a..864384ea122 100644 --- a/gnovm/pkg/gnolang/machine.go +++ b/gnovm/pkg/gnolang/machine.go @@ -1956,9 +1956,11 @@ func (m *Machine) PopAsPointer(lx Expr) PointerValue { return ptr case *CompositeLitExpr: // for *RefExpr tv := *m.PopValue() + hv := m.Alloc.NewHeapItem(tv) return PointerValue{ - TV: &tv, // heap alloc - Base: nil, + TV: &hv.Value, + Base: hv, + Index: 0, } default: panic("should not happen") diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index 482f4850b6e..2897fdd5306 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -1115,11 +1115,21 @@ func ReadMemPackage(dir string, pkgPath string) *std.MemPackage { allowedFileExtensions := []string{ ".gno", } + // exceptions to allowedFileExtensions + var rejectedFileExtensions []string + + if IsStdlib(pkgPath) { + // Allows transpilation to work on stdlibs with native fns. + allowedFileExtensions = append(allowedFileExtensions, ".go") + rejectedFileExtensions = []string{".gen.go"} + } + list := make([]string, 0, len(files)) for _, file := range files { if file.IsDir() || strings.HasPrefix(file.Name(), ".") || - (!endsWith(file.Name(), allowedFileExtensions) && !contains(allowedFiles, file.Name())) { + (!endsWith(file.Name(), allowedFileExtensions) && !contains(allowedFiles, file.Name())) || + endsWith(file.Name(), rejectedFileExtensions) { continue } list = append(list, filepath.Join(dir, file.Name())) diff --git a/gnovm/pkg/gnolang/op_assign.go b/gnovm/pkg/gnolang/op_assign.go index 0cc30861355..eb67ffcc351 100644 --- a/gnovm/pkg/gnolang/op_assign.go +++ b/gnovm/pkg/gnolang/op_assign.go @@ -50,7 +50,7 @@ func (m *Machine) doOpAddAssign() { rv := m.PopValue() // only one. lv := m.PopAsPointer(s.Lhs[0]) if debug { - assertSameTypes(lv.TV.T, rv.T) + debugAssertSameTypes(lv.TV.T, rv.T) } // XXX HACK (until value persistence impl'd) @@ -73,7 +73,7 @@ func (m *Machine) doOpSubAssign() { rv := m.PopValue() // only one. lv := m.PopAsPointer(s.Lhs[0]) if debug { - assertSameTypes(lv.TV.T, rv.T) + debugAssertSameTypes(lv.TV.T, rv.T) } // XXX HACK (until value persistence impl'd) @@ -96,7 +96,7 @@ func (m *Machine) doOpMulAssign() { rv := m.PopValue() // only one. lv := m.PopAsPointer(s.Lhs[0]) if debug { - assertSameTypes(lv.TV.T, rv.T) + debugAssertSameTypes(lv.TV.T, rv.T) } // XXX HACK (until value persistence impl'd) @@ -119,7 +119,7 @@ func (m *Machine) doOpQuoAssign() { rv := m.PopValue() // only one. lv := m.PopAsPointer(s.Lhs[0]) if debug { - assertSameTypes(lv.TV.T, rv.T) + debugAssertSameTypes(lv.TV.T, rv.T) } // XXX HACK (until value persistence impl'd) @@ -142,7 +142,7 @@ func (m *Machine) doOpRemAssign() { rv := m.PopValue() // only one. lv := m.PopAsPointer(s.Lhs[0]) if debug { - assertSameTypes(lv.TV.T, rv.T) + debugAssertSameTypes(lv.TV.T, rv.T) } // XXX HACK (until value persistence impl'd) @@ -165,7 +165,7 @@ func (m *Machine) doOpBandAssign() { rv := m.PopValue() // only one. lv := m.PopAsPointer(s.Lhs[0]) if debug { - assertSameTypes(lv.TV.T, rv.T) + debugAssertSameTypes(lv.TV.T, rv.T) } // XXX HACK (until value persistence impl'd) @@ -188,7 +188,7 @@ func (m *Machine) doOpBandnAssign() { rv := m.PopValue() // only one. lv := m.PopAsPointer(s.Lhs[0]) if debug { - assertSameTypes(lv.TV.T, rv.T) + debugAssertSameTypes(lv.TV.T, rv.T) } // XXX HACK (until value persistence impl'd) @@ -211,7 +211,7 @@ func (m *Machine) doOpBorAssign() { rv := m.PopValue() // only one. lv := m.PopAsPointer(s.Lhs[0]) if debug { - assertSameTypes(lv.TV.T, rv.T) + debugAssertSameTypes(lv.TV.T, rv.T) } // XXX HACK (until value persistence impl'd) @@ -234,7 +234,7 @@ func (m *Machine) doOpXorAssign() { rv := m.PopValue() // only one. lv := m.PopAsPointer(s.Lhs[0]) if debug { - assertSameTypes(lv.TV.T, rv.T) + debugAssertSameTypes(lv.TV.T, rv.T) } // XXX HACK (until value persistence impl'd) diff --git a/gnovm/pkg/gnolang/op_binary.go b/gnovm/pkg/gnolang/op_binary.go index 99b56c18a06..a1861ed3aaa 100644 --- a/gnovm/pkg/gnolang/op_binary.go +++ b/gnovm/pkg/gnolang/op_binary.go @@ -45,7 +45,7 @@ func (m *Machine) doOpLor() { rv := m.PopValue() lv := m.PeekValue(1) // also the result if debug { - assertSameTypes(lv.T, rv.T) + debugAssertSameTypes(lv.T, rv.T) } // set result in lv. @@ -60,7 +60,7 @@ func (m *Machine) doOpLand() { rv := m.PopValue() lv := m.PeekValue(1) // also the result if debug { - assertSameTypes(lv.T, rv.T) + debugAssertSameTypes(lv.T, rv.T) } // set result in lv. @@ -77,7 +77,7 @@ func (m *Machine) doOpEql() { rv := m.PopValue() lv := m.PeekValue(1) // also the result if debug { - assertEqualityTypes(lv.T, rv.T) + debugAssertEqualityTypes(lv.T, rv.T) } // set result in lv. @@ -94,7 +94,7 @@ func (m *Machine) doOpNeq() { rv := m.PopValue() lv := m.PeekValue(1) // also the result if debug { - assertEqualityTypes(lv.T, rv.T) + debugAssertEqualityTypes(lv.T, rv.T) } // set result in lv. @@ -111,7 +111,7 @@ func (m *Machine) doOpLss() { rv := m.PopValue() lv := m.PeekValue(1) // also the result if debug { - assertSameTypes(lv.T, rv.T) + debugAssertSameTypes(lv.T, rv.T) } // set the result in lv. @@ -128,7 +128,7 @@ func (m *Machine) doOpLeq() { rv := m.PopValue() lv := m.PeekValue(1) // also the result if debug { - assertSameTypes(lv.T, rv.T) + debugAssertSameTypes(lv.T, rv.T) } // set the result in lv. @@ -145,7 +145,7 @@ func (m *Machine) doOpGtr() { rv := m.PopValue() lv := m.PeekValue(1) // also the result if debug { - assertSameTypes(lv.T, rv.T) + debugAssertSameTypes(lv.T, rv.T) } // set the result in lv. @@ -162,7 +162,7 @@ func (m *Machine) doOpGeq() { rv := m.PopValue() lv := m.PeekValue(1) // also the result if debug { - assertSameTypes(lv.T, rv.T) + debugAssertSameTypes(lv.T, rv.T) } // set the result in lv. @@ -179,7 +179,7 @@ func (m *Machine) doOpAdd() { rv := m.PopValue() lv := m.PeekValue(1) // also result if debug { - assertSameTypes(lv.T, rv.T) + debugAssertSameTypes(lv.T, rv.T) } // add rv to lv. @@ -193,7 +193,7 @@ func (m *Machine) doOpSub() { rv := m.PopValue() lv := m.PeekValue(1) // also result if debug { - assertSameTypes(lv.T, rv.T) + debugAssertSameTypes(lv.T, rv.T) } // sub rv from lv. @@ -207,7 +207,7 @@ func (m *Machine) doOpBor() { rv := m.PopValue() lv := m.PeekValue(1) // also result if debug { - assertSameTypes(lv.T, rv.T) + debugAssertSameTypes(lv.T, rv.T) } // lv | rv @@ -221,7 +221,7 @@ func (m *Machine) doOpXor() { rv := m.PopValue() lv := m.PeekValue(1) // also result if debug { - assertSameTypes(lv.T, rv.T) + debugAssertSameTypes(lv.T, rv.T) } // lv ^ rv @@ -235,7 +235,7 @@ func (m *Machine) doOpMul() { rv := m.PopValue() lv := m.PeekValue(1) // also result if debug { - assertSameTypes(lv.T, rv.T) + debugAssertSameTypes(lv.T, rv.T) } // lv * rv @@ -249,7 +249,7 @@ func (m *Machine) doOpQuo() { rv := m.PopValue() lv := m.PeekValue(1) // also result if debug { - assertSameTypes(lv.T, rv.T) + debugAssertSameTypes(lv.T, rv.T) } // lv / rv @@ -263,7 +263,7 @@ func (m *Machine) doOpRem() { rv := m.PopValue() lv := m.PeekValue(1) // also result if debug { - assertSameTypes(lv.T, rv.T) + debugAssertSameTypes(lv.T, rv.T) } // lv % rv @@ -309,7 +309,7 @@ func (m *Machine) doOpBand() { rv := m.PopValue() lv := m.PeekValue(1) // also result if debug { - assertSameTypes(lv.T, rv.T) + debugAssertSameTypes(lv.T, rv.T) } // lv & rv @@ -323,7 +323,7 @@ func (m *Machine) doOpBandn() { rv := m.PopValue() lv := m.PeekValue(1) // also result if debug { - assertSameTypes(lv.T, rv.T) + debugAssertSameTypes(lv.T, rv.T) } // lv &^ rv diff --git a/gnovm/pkg/gnolang/op_exec.go b/gnovm/pkg/gnolang/op_exec.go index 12e0f9e26e3..c7e8ffd600c 100644 --- a/gnovm/pkg/gnolang/op_exec.go +++ b/gnovm/pkg/gnolang/op_exec.go @@ -957,7 +957,7 @@ func (m *Machine) doOpSwitchClauseCase() { // eval whether cv == tv. if debug { - assertEqualityTypes(cv.T, tv.T) + debugAssertEqualityTypes(cv.T, tv.T) } match := isEql(m.Store, cv, tv) if match { diff --git a/gnovm/pkg/gnolang/op_inc_dec.go b/gnovm/pkg/gnolang/op_inc_dec.go index 84c39716eec..7a8a885bcf0 100644 --- a/gnovm/pkg/gnolang/op_inc_dec.go +++ b/gnovm/pkg/gnolang/op_inc_dec.go @@ -26,6 +26,10 @@ func (m *Machine) doOpInc() { panic("expected lv.V to be nil for primitive type for OpInc") } } + + // here we can't just switch on the value type + // because it could be a type alias + // type num int switch baseOf(lv.T) { case IntType: lv.SetInt(lv.GetInt() + 1) diff --git a/gnovm/pkg/gnolang/ownership.go b/gnovm/pkg/gnolang/ownership.go index f2afc393d05..24d70b5dd84 100644 --- a/gnovm/pkg/gnolang/ownership.go +++ b/gnovm/pkg/gnolang/ownership.go @@ -129,6 +129,7 @@ var ( _ Object = &BoundMethodValue{} _ Object = &MapValue{} _ Object = &Block{} + _ Object = &HeapItemValue{} ) type ObjectInfo struct { @@ -332,11 +333,7 @@ func (tv *TypedValue) GetFirstObject(store Store) Object { // something in it; in that case, ignore the base. That will // likely require maybe a preparation step in persistence // ( or unlikely, a second type of ref-counting). - if cv.Base != nil { - return cv.Base.(Object) - } else { - return cv.TV.GetFirstObject(store) - } + return cv.Base.(Object) case *ArrayValue: return cv case *SliceValue: @@ -359,6 +356,9 @@ func (tv *TypedValue) GetFirstObject(store Store) Object { oo := store.GetObject(cv.ObjectID) tv.V = oo return oo + case *HeapItemValue: + // should only appear in PointerValue.Base + panic("heap item value should only appear as a pointer's base") default: return nil } diff --git a/gnovm/pkg/gnolang/package.go b/gnovm/pkg/gnolang/package.go index 4d4cb5aaf45..e2fdb2580ca 100644 --- a/gnovm/pkg/gnolang/package.go +++ b/gnovm/pkg/gnolang/package.go @@ -31,6 +31,7 @@ var Package = amino.RegisterPackage(amino.NewPackage( // &NativeValue{}, &Block{}, RefValue{}, + &HeapItemValue{}, //---------------------------------------- // Realm/Object diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 459910505b9..8eb2b37fcc2 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -753,66 +753,75 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { case *BinaryExpr: lt := evalStaticTypeOf(store, last, n.Left) rt := evalStaticTypeOf(store, last, n.Right) - // Special (recursive) case if shift and right isn't uint. - isShift := n.Op == SHL || n.Op == SHR - if isShift && baseOf(rt) != UintType { - // convert n.Right to (gno) uint type, - rn := Expr(Call("uint", n.Right)) - // reset/create n2 to preprocess right child. - n2 := &BinaryExpr{ - Left: n.Left, - Op: n.Op, - Right: rn, - } - resn := Preprocess(store, last, n2) - return resn, TRANS_CONTINUE + + lcx, lic := n.Left.(*ConstExpr) + rcx, ric := n.Right.(*ConstExpr) + + if debug { + debug.Printf("Trans_leave, BinaryExpr, OP: %v, lx: %v, rx: %v, lt: %v, rt: %v, isLeftConstExpr: %v, isRightConstExpr %v, isLeftUntyped: %v, isRightUntyped: %v \n", n.Op, n.Left, n.Right, lt, rt, lic, ric, isUntyped(lt), isUntyped(rt)) } - // Left and right hand expressions must evaluate to a boolean typed value if - // the operation is a logical AND or OR. - if (n.Op == LAND || n.Op == LOR) && (lt.Kind() != BoolKind || rt.Kind() != BoolKind) { - panic("operands of boolean operators must evaluate to boolean typed values") + // Special (recursive) case if shift and right isn't uint. + isShift := n.Op == SHL || n.Op == SHR + if isShift { + // check LHS type compatibility + n.checkShiftLhs(lt) + // checkOrConvert RHS + if baseOf(rt) != UintType { + // convert n.Right to (gno) uint type, + rn := Expr(Call("uint", n.Right)) + // reset/create n2 to preprocess right child. + n2 := &BinaryExpr{ + Left: n.Left, + Op: n.Op, + Right: rn, + } + resn := Preprocess(store, last, n2) + return resn, TRANS_CONTINUE + } + // Then, evaluate the expression. + if lic && ric { + cx := evalConst(store, last, n) + return cx, TRANS_CONTINUE + } + return n, TRANS_CONTINUE } + // general cases + n.AssertCompatible(lt, rt) // check compatibility against binaryExprs other than shift expr // General case. - lcx, lic := n.Left.(*ConstExpr) - rcx, ric := n.Right.(*ConstExpr) if lic { if ric { // Left const, Right const ---------------------- // Replace with *ConstExpr if const operands. // First, convert untyped as necessary. - if !isShift { - cmp := cmpSpecificity(lcx.T, rcx.T) - if cmp < 0 { - // convert n.Left to right type. - checkOrConvertType(store, last, &n.Left, rcx.T, false) - } else if cmp == 0 { - // NOTE: the following doesn't work. - // TODO: make it work. - // convert n.Left to right type, - // or check for compatibility. - // (the other way around would work too) - // checkOrConvertType(store, last, n.Left, rcx.T, false) - } else { - // convert n.Right to left type. - checkOrConvertType(store, last, &n.Right, lcx.T, false) - } + if !shouldSwapOnSpecificity(lcx.T, rcx.T) { + // convert n.Left to right type. + checkOrConvertType(store, last, &n.Left, rcx.T, false) + } else { + // convert n.Right to left type. + checkOrConvertType(store, last, &n.Right, lcx.T, false) } // Then, evaluate the expression. cx := evalConst(store, last, n) return cx, TRANS_CONTINUE } else if isUntyped(lcx.T) { // Left untyped const, Right not ---------------- - if rnt, ok := rt.(*NativeType); ok { - if isShift { + if rnt, ok := rt.(*NativeType); ok { // untyped -> gno(native), e.g. 1*time.Second + if isShift { // RHS of shift should not be native panic("should not happen") } // get concrete native base type. - pt := go2GnoBaseType(rnt.Type).(PrimitiveType) + pt, ok := go2GnoBaseType(rnt.Type).(PrimitiveType) + if !ok { + panic(fmt.Sprintf( + "unexpected type pair: cannot use %s as %s", + lt.String(), + rnt.String())) + } // convert n.Left to pt type, checkOrConvertType(store, last, &n.Left, pt, false) - // convert n.Right to (gno) pt type, + // if check pass, convert n.Right to (gno) pt type, rn := Expr(Call(pt.String(), n.Right)) // and convert result back. tx := constType(n, rnt) @@ -822,100 +831,127 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { Op: n.Op, Right: rn, } - resn := Node(Call(tx, n2)) + resn := Node(Call(tx, n2)) // this make current node to gonative{xxx} resn = Preprocess(store, last, resn) return resn, TRANS_CONTINUE // NOTE: binary operations are always computed in // gno, never with reflect. } else { - if isShift { - // nothing to do, right type is (already) uint type. - // we don't yet know what this type should be, - // but another checkOrConvertType() later does. - // (e.g. from AssignStmt or other). - } else { - // convert n.Left to right type. - checkOrConvertType(store, last, &n.Left, rt, false) - } + // convert n.Left to right type. + checkOrConvertType(store, last, &n.Left, rt, false) } - } else if lcx.T == nil { + } else if lcx.T == nil { // LHS is nil. // convert n.Left to typed-nil type. checkOrConvertType(store, last, &n.Left, rt, false) } - } else if ric { + } else if ric { // right is const, left is not if isUntyped(rcx.T) { // Left not, Right untyped const ---------------- - if isShift { - if baseOf(rt) != UintType { - // convert n.Right to (gno) uint type. - checkOrConvertType(store, last, &n.Right, UintType, false) - } else { - // leave n.Left as is and baseOf(n.Right) as UintType. + if lnt, ok := lt.(*NativeType); ok { + // get concrete native base type. + pt, ok := go2GnoBaseType(lnt.Type).(PrimitiveType) + if !ok { + panic(fmt.Sprintf( + "unexpected type pair: cannot use %s as %s", + rt.String(), + lnt.String())) } - } else { - if lnt, ok := lt.(*NativeType); ok { - // get concrete native base type. - pt := go2GnoBaseType(lnt.Type).(PrimitiveType) - // 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) - // and convert result back. - tx := constType(n, lnt) - // reset/create n2 to preprocess left child. - n2 := &BinaryExpr{ - Left: ln, - Op: n.Op, - Right: n.Right, - } - resn := Node(Call(tx, n2)) - resn = Preprocess(store, last, resn) - return resn, TRANS_CONTINUE - // NOTE: binary operations are always computed in - // gno, never with reflect. - } else { - // convert n.Right to left type. - checkOrConvertType(store, last, &n.Right, lt, false) + // 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) + // and convert result back. + tx := constType(n, lnt) + // reset/create n2 to preprocess left child. + n2 := &BinaryExpr{ + Left: ln, + Op: n.Op, + Right: n.Right, } + resn := Node(Call(tx, n2)) + resn = Preprocess(store, last, resn) + return resn, TRANS_CONTINUE + // NOTE: binary operations are always computed in + // gno, never with reflect. + } else { + // convert n.Right to left type. + checkOrConvertType(store, last, &n.Right, lt, false) } - } else if rcx.T == nil { - // convert n.Right to typed-nil type. + } else if rcx.T == nil { // RHS is nil + // refer to tests/files/types/eql_0f20.gno checkOrConvertType(store, last, &n.Right, lt, false) } } else { // Left not const, Right not const ------------------ - if n.Op == EQL || n.Op == NEQ { - // If == or !=, no conversions. - } else if lnt, ok := lt.(*NativeType); ok && isNative(rt) { - if debug { - if !isShift { - assertSameTypes(lt, rt) - } - } - // If left and right are native type, and same type + if lnt, ok := lt.(*NativeType); ok { + // If left and right are native type, // convert left and right to gno, then // convert result back to native. - // - // get concrete native base type. - if lt.TypeID() != rt.TypeID() { + // get native base type. + lpt, ok := go2GnoBaseType(lnt.Type).(PrimitiveType) + if !ok { + panic(fmt.Sprintf( + "unexpected type pair: cannot use %s as %s", + rt.String(), + lnt.String())) + } + // convert n.Left to (gno) pt type, + ln := Expr(Call(lpt.String(), n.Left)) + + rn := n.Right + // e.g. native: time.Second + time.Second, convert both(or it will be converted recursively) + // see tests/files/types/time_native.gno + if rnt, ok := rt.(*NativeType); ok { + rpt, ok := go2GnoBaseType(rnt.Type).(PrimitiveType) + if !ok { + panic(fmt.Sprintf( + "unexpected type pair: cannot use %s as %s", + lt.String(), + rnt.String())) + } + // check assignable, if pass, convert right to gno first + assertAssignableTo(lpt, rpt, false) // both primitive types + rn = Expr(Call(rpt.String(), n.Right)) + } else { // rt not native panic(fmt.Sprintf( - "incompatible types in binary expression: %v %v %v", + "incompatible operands in binary expression: %s %s %s", lt.TypeID(), n.Op, rt.TypeID())) } - pt := go2GnoBaseType(lnt.Type).(PrimitiveType) + + // and convert result back. + tx := constType(n, lnt) + // reset/create n2 to preprocess + // children. + n2 := &BinaryExpr{ + Left: ln, + Op: n.Op, + Right: rn, + } + resn := Node(Call(tx, n2)) + resn = Preprocess(store, last, resn) + return resn, TRANS_CONTINUE + // NOTE: binary operations are always + // computed in gno, never with + // reflect. + } else if rnt, ok := rt.(*NativeType); ok { // e.g. a * time.Second + pt, ok := go2GnoBaseType(rnt.Type).(PrimitiveType) + if !ok { + panic(fmt.Sprintf( + "unexpected type pair: cannot use %s as %s", + lt.String(), + rnt.String())) + } // convert n.Left to (gno) pt type, - ln := Expr(Call(pt.String(), n.Left)) + rn := Expr(Call(pt.String(), n.Right)) // convert n.Right to pt or uint type, - rn := n.Right + ln := n.Left if isShift { - if baseOf(rt) != UintType { - rn = Expr(Call("uint", n.Right)) - } + panic("should not happen") } else { - rn = Expr(Call(pt.String(), n.Right)) + checkOrConvertType(store, last, &n.Left, pt, false) } // and convert result back. - tx := constType(n, lnt) + tx := constType(n, rnt) // reset/create n2 to preprocess // children. n2 := &BinaryExpr{ @@ -929,8 +965,6 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // NOTE: binary operations are always // computed in gno, never with // reflect. - } else if n.Op == SHL || n.Op == SHR { - // shift operator, nothing yet to do. } else { // non-shift non-const binary operator. liu, riu := isUntyped(lt), isUntyped(rt) @@ -941,24 +975,20 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { "incompatible types in binary expression: %v %v %v", lt.TypeID(), n.Op, rt.TypeID())) } - } else { + } else { // left untyped, right typed checkOrConvertType(store, last, &n.Left, rt, false) } - } else { - if riu { - checkOrConvertType(store, last, &n.Right, lt, false) + } else if riu { // left typed, right untyped + checkOrConvertType(store, last, &n.Right, lt, false) + } else { // both typed, refer to 0a1g.gno + if !shouldSwapOnSpecificity(lt, rt) { + checkOrConvertType(store, last, &n.Left, rt, false) } else { - // left is untyped, right is not. - if lt.TypeID() != rt.TypeID() { - panic(fmt.Sprintf( - "incompatible types in binary expression: %v %v %v", - lt.TypeID(), n.Op, rt.TypeID())) - } + checkOrConvertType(store, last, &n.Right, lt, false) } } } } - // TRANS_LEAVE ----------------------- case *CallExpr: // Func type evaluation. @@ -1177,12 +1207,12 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { for i, tv := range argTVs { if hasVarg { if (len(spts) - 1) <= i { - checkType(tv.T, spts[len(spts)-1].Type.Elem(), true) + assertAssignableTo(tv.T, spts[len(spts)-1].Type.Elem(), true) } else { - checkType(tv.T, spts[i].Type, true) + assertAssignableTo(tv.T, spts[i].Type, true) } } else { - checkType(tv.T, spts[i].Type, true) + assertAssignableTo(tv.T, spts[i].Type, true) } } } else { @@ -1223,7 +1253,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { case StringKind, ArrayKind, SliceKind: // Replace const index with int *ConstExpr, // or if not const, assert integer type.. - checkOrConvertIntegerType(store, last, n.Index) + checkOrConvertIntegerKind(store, last, n.Index) case MapKind: mt := baseOf(gnoTypeOf(store, dt)).(*MapType) checkOrConvertType(store, last, &n.Index, mt.Key, false) @@ -1237,9 +1267,9 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { case *SliceExpr: // Replace const L/H/M with int *ConstExpr, // or if not const, assert integer type.. - checkOrConvertIntegerType(store, last, n.Low) - checkOrConvertIntegerType(store, last, n.High) - checkOrConvertIntegerType(store, last, n.Max) + checkOrConvertIntegerKind(store, last, n.Low) + checkOrConvertIntegerKind(store, last, n.High) + checkOrConvertIntegerKind(store, last, n.Max) // TRANS_LEAVE ----------------------- case *TypeAssertExpr: @@ -1278,6 +1308,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // TRANS_LEAVE ----------------------- case *UnaryExpr: xt := evalStaticTypeOf(store, last, n.X) + n.AssertCompatible(xt) if xnt, ok := xt.(*NativeType); ok { // get concrete native base type. pt := go2GnoBaseType(xnt.Type).(PrimitiveType) @@ -1325,12 +1356,12 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } case *ArrayType: for i := 0; i < len(n.Elts); i++ { - checkOrConvertType(store, last, &n.Elts[i].Key, IntType, false) + convertType(store, last, &n.Elts[i].Key, IntType) checkOrConvertType(store, last, &n.Elts[i].Value, cclt.Elt, false) } case *SliceType: for i := 0; i < len(n.Elts); i++ { - checkOrConvertType(store, last, &n.Elts[i].Key, IntType, false) + convertType(store, last, &n.Elts[i].Key, IntType) checkOrConvertType(store, last, &n.Elts[i].Value, cclt.Elt, false) } case *MapType: @@ -1571,6 +1602,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // TRANS_LEAVE ----------------------- case *AssignStmt: + n.AssertCompatible(store, last) // NOTE: keep DEFINE and ASSIGN in sync. if n.Op == DEFINE { // Rhs consts become default *ConstExprs. @@ -1578,22 +1610,13 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // NOTE: does nothing if rx is "nil". convertIfConst(store, last, rx) } + if len(n.Lhs) > len(n.Rhs) { - // Unpack n.Rhs[0] to n.Lhs[:] - if len(n.Rhs) != 1 { - panic("should not happen") - } switch cx := n.Rhs[0].(type) { case *CallExpr: // Call case: a, b := x(...) ift := evalStaticTypeOf(store, last, cx.Func) cft := getGnoFuncTypeOf(store, ift) - if len(n.Lhs) != len(cft.Results) { - panic(fmt.Sprintf( - "assignment mismatch: "+ - "%d variables but %s returns %d values", - len(n.Lhs), cx.Func.String(), len(cft.Results))) - } for i, lx := range n.Lhs { ln := lx.(*NameExpr).Name rf := cft.Results[i] @@ -1601,11 +1624,6 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { last.Define(ln, anyValue(rf.Type)) } case *TypeAssertExpr: - // Type-assert case: a, ok := x.(type) - if len(n.Lhs) != 2 { - panic("should not happen") - } - cx.HasOK = true lhs0 := n.Lhs[0].(*NameExpr).Name lhs1 := n.Lhs[1].(*NameExpr).Name tt := evalStaticType(store, last, cx.Type) @@ -1613,11 +1631,6 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { last.Define(lhs0, anyValue(tt)) last.Define(lhs1, anyValue(BoolType)) case *IndexExpr: - // Index case: v, ok := x[k], x is map. - if len(n.Lhs) != 2 { - panic("should not happen") - } - cx.HasOK = true lhs0 := n.Lhs[0].(*NameExpr).Name lhs1 := n.Lhs[1].(*NameExpr).Name @@ -1649,56 +1662,27 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } } } else { // ASSIGN, or assignment operation (+=, -=, <<=, etc.) - // If this is an assignment operation, ensure there's only 1 - // expr on lhs/rhs. - if n.Op != ASSIGN && - (len(n.Lhs) != 1 || len(n.Rhs) != 1) { - panic("assignment operator " + n.Op.TokenString() + - " requires only one expression on lhs and rhs") - } - // NOTE: Keep in sync with DEFINE above. - if n.Op == SHL_ASSIGN || n.Op == SHR_ASSIGN { - // Special case if shift assign <<= or >>=. - checkOrConvertType(store, last, &n.Rhs[0], UintType, false) - } else if len(n.Lhs) > len(n.Rhs) { - // TODO dry code w/ above. - // Unpack n.Rhs[0] to n.Lhs[:] - if len(n.Rhs) != 1 { - panic("should not happen") - } - switch cx := n.Rhs[0].(type) { - case *CallExpr: - // Call case: a, b = x(...) - ift := evalStaticTypeOf(store, last, cx.Func) - cft := getGnoFuncTypeOf(store, ift) - if len(n.Lhs) != len(cft.Results) { - panic(fmt.Sprintf( - "assignment mismatch: "+ - "%d variables but %s returns %d values", - len(n.Lhs), cx.Func.String(), len(cft.Results))) - } - case *TypeAssertExpr: - // Type-assert case: a, ok := x.(type) - if len(n.Lhs) != 2 { + if len(n.Lhs) > len(n.Rhs) { + // check is done in assertCompatible + } else { // len(Lhs) == len(Rhs) + if n.Op == SHL_ASSIGN || n.Op == SHR_ASSIGN { + if len(n.Lhs) != 1 || len(n.Rhs) != 1 { panic("should not happen") } - cx.HasOK = true - case *IndexExpr: - // Index case: v, ok := x[k], x is map. - if len(n.Lhs) != 2 { - panic("should not happen") + // Special case if shift assign <<= or >>=. + convertType(store, last, &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) + } else { // all else, like BAND_ASSIGN, etc + // General case: a, b = x, y. + for i, lx := range n.Lhs { + lt := evalStaticTypeOf(store, last, lx) + // if lt is interface, nothing will happen + checkOrConvertType(store, last, &n.Rhs[i], lt, true) } - cx.HasOK = true - default: - panic("should not happen") - } - } else { - // General case: a, b = x, y. - for i, lx := range n.Lhs { - lt := evalStaticTypeOf(store, last, lx) - // converts if rx is "nil". - checkOrConvertType(store, last, &n.Rhs[i], lt, false) } } } @@ -1737,19 +1721,24 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { panic("should not happen") } + case *IncDecStmt: + xt := evalStaticTypeOf(store, last, n.X) + n.AssertCompatible(xt) + // TRANS_LEAVE ----------------------- case *ForStmt: // Cond consts become bool *ConstExprs. - checkOrConvertType(store, last, &n.Cond, BoolType, false) + checkOrConvertBoolKind(store, last, n.Cond) // TRANS_LEAVE ----------------------- case *IfStmt: // Cond consts become bool *ConstExprs. - checkOrConvertType(store, last, &n.Cond, BoolType, false) + checkOrConvertBoolKind(store, last, n.Cond) // TRANS_LEAVE ----------------------- case *RangeStmt: // NOTE: k,v already defined @ TRANS_BLOCK. + n.AssertCompatible(store, last) // TRANS_LEAVE ----------------------- case *ReturnStmt: @@ -2438,47 +2427,25 @@ func isConstType(x Expr) bool { return ok } -func cmpSpecificity(t1, t2 Type) int { - t1s, t2s := 0, 0 - if t1p, ok := t1.(PrimitiveType); ok { - t1s = t1p.Specificity() - } - if t2p, ok := t2.(PrimitiveType); ok { - t2s = t2p.Specificity() - } - if t1s < t2s { - // NOTE: higher specificity has lower value, so backwards. - return 1 - } else if t1s == t2s { - return 0 - } else { - return -1 - } -} - -// 1. convert x to t if x is *ConstExpr. -// 2. otherwise, assert that x can be coerced to t. -// autoNative is usually false, but set to true -// for native function calls, where gno values are -// automatically converted to native go types. -// NOTE: also see checkOrConvertIntegerType() +// check before convert type func checkOrConvertType(store Store, last BlockNode, x *Expr, t Type, autoNative bool) { + if debug { + debug.Printf("checkOrConvertType, *x: %v:, t:%v \n", *x, t) + } if cx, ok := (*x).(*ConstExpr); ok { - convertConst(store, last, cx, t) + if _, ok := t.(*NativeType); !ok { // not native type, refer to time4_native.gno. + // e.g. int(1) == int8(1) + assertAssignableTo(cx.T, t, autoNative) + } } else if bx, ok := (*x).(*BinaryExpr); ok && (bx.Op == SHL || bx.Op == SHR) { - // "push" expected type into shift binary's left operand. + // "push" expected type into shift binary's left operand. recursively. checkOrConvertType(store, last, &bx.Left, t, autoNative) } else if *x != nil { // XXX if x != nil && t != nil { - // check type xt := evalStaticTypeOf(store, last, *x) if t != nil { - checkType(xt, t, autoNative) + assertAssignableTo(xt, t, autoNative) } - // convert type - if isUntyped(xt) { // convert if x is untyped literal - if t == nil { - t = defaultTypeOf(xt) - } + if isUntyped(xt) { // Push type into expr if qualifying binary expr. if bx, ok := (*x).(*BinaryExpr); ok { switch bx.Op { @@ -2496,20 +2463,47 @@ func checkOrConvertType(store Store, last BlockNode, x *Expr, t Type, autoNative // default: } } + } + } + // convert recursively + convertType(store, last, x, t) +} + +// 1. convert x to t if x is *ConstExpr. +// 2. otherwise, assert that x can be coerced to t. +// autoNative is usually false, but set to true +// for native function calls, where gno values are +// automatically converted to native go types. +// NOTE: also see checkOrConvertIntegerKind() +func convertType(store Store, last BlockNode, x *Expr, t Type) { + if debug { + debug.Printf("convertType, *x: %v:, t:%v \n", *x, t) + } + if cx, ok := (*x).(*ConstExpr); ok { + convertConst(store, last, cx, t) + } else if *x != nil { + xt := evalStaticTypeOf(store, last, *x) + if isUntyped(xt) { + if t == nil { + t = defaultTypeOf(xt) + } + if debug { + debug.Printf("default type of t: %v \n", t) + } // convert x to destination type t - convertType(store, last, x, t) + doConvertType(store, last, x, t) } else { // if one side is declared name type and the other side is unnamed type if isNamedConversion(xt, t) { // covert right (xt) to the type of the left (t) - convertType(store, last, x, t) + doConvertType(store, last, x, t) } } } } // convert x to destination type t -func convertType(store Store, last BlockNode, x *Expr, t Type) { +func doConvertType(store Store, last BlockNode, x *Expr, t Type) { cx := Expr(Call(constType(nil, t), *x)) cx = Preprocess(store, last, cx).(Expr) *x = cx @@ -2563,7 +2557,7 @@ func convertIfConst(store Store, last BlockNode, x Expr) { func convertConst(store Store, last BlockNode, cx *ConstExpr, t Type) { if t != nil && t.Kind() == InterfaceKind { if cx.T != nil { - checkType(cx.T, t, false) + assertAssignableTo(cx.T, t, false) } t = nil // signifies to convert to default type. } @@ -2577,232 +2571,6 @@ func convertConst(store Store, last BlockNode, cx *ConstExpr, t Type) { } } -// 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 checkType(xt Type, dt Type, autoNative bool) { - // Special case if dt is interface kind: - if dt.Kind() == InterfaceKind { - if idt, ok := baseOf(dt).(*InterfaceType); ok { - if idt.IsEmptyInterface() { - // if dt is an empty Gno interface, any x ok. - return // ok - } else if idt.IsImplementedBy(xt) { - // if dt implements idt, ok. - return // ok - } else { - panic(fmt.Sprintf( - "%s does not implement %s", - xt.String(), - dt.String())) - } - } else if ndt, ok := baseOf(dt).(*NativeType); ok { - nidt := ndt.Type - if nidt.NumMethod() == 0 { - // if dt is an empty Go native interface, ditto. - return // ok - } else if nxt, ok := baseOf(xt).(*NativeType); ok { - // if xt has native base, do the naive native. - if nxt.Type.AssignableTo(nidt) { - return // ok - } else { - panic(fmt.Sprintf( - "cannot use %s as %s", - nxt.String(), - nidt.String())) - } - } else if pxt, ok := baseOf(xt).(*PointerType); ok { - nxt, ok := pxt.Elt.(*NativeType) - if !ok { - panic(fmt.Sprintf( - "pointer to non-native type cannot satisfy non-empty native interface; %s doesn't implement %s", - pxt.String(), - nidt.String())) - } - // if xt has native base, do the naive native. - if reflect.PointerTo(nxt.Type).AssignableTo(nidt) { - return // ok - } else { - panic(fmt.Sprintf( - "cannot use %s as %s", - pxt.String(), - nidt.String())) - } - } else { - panic(fmt.Sprintf( - "unexpected type pair: cannot use %s as %s", - xt.String(), - dt.String())) - } - } else { - panic("should not happen") - } - } - // Special case if xt or dt is *PointerType to *NativeType, - // convert to *NativeType of pointer kind. - if pxt, ok := xt.(*PointerType); ok { - // *gonative{x} is(to) gonative{*x} - //nolint:misspell - if enxt, ok := pxt.Elt.(*NativeType); ok { - xt = &NativeType{ - Type: reflect.PointerTo(enxt.Type), - } - } - } - if pdt, ok := dt.(*PointerType); ok { - // *gonative{x} is gonative{*x} - if endt, ok := pdt.Elt.(*NativeType); ok { - dt = &NativeType{ - Type: reflect.PointerTo(endt.Type), - } - } - } - // Special case of xt or dt is *DeclaredType, - // allow implicit conversion unless both are declared. - // TODO simplify with .IsNamedType(). - if dxt, ok := xt.(*DeclaredType); ok { - if ddt, ok := dt.(*DeclaredType); ok { - // types must match exactly. - if !dxt.sealed && !ddt.sealed && - dxt.PkgPath == ddt.PkgPath && - dxt.Name == ddt.Name { // not yet sealed - return // ok - } else if dxt.TypeID() == ddt.TypeID() { - return // ok - } else { - panic(fmt.Sprintf( - "cannot use %s as %s without explicit conversion", - dxt.String(), - ddt.String())) - } - } else { - // special case if implicitly named primitive type. - // TODO simplify with .IsNamedType(). - if _, ok := dt.(PrimitiveType); ok { - panic(fmt.Sprintf( - "cannot use %s as %s without explicit conversion", - dxt.String(), - dt.String())) - } else { - // carry on with baseOf(dxt) - xt = dxt.Base - } - } - } else if ddt, ok := dt.(*DeclaredType); ok { - // special case if implicitly named primitive type. - // TODO simplify with .IsNamedType(). - if _, ok := xt.(PrimitiveType); ok { - panic(fmt.Sprintf( - "cannot use %s as %s without explicit conversion", - xt.String(), - ddt.String())) - } else { - // carry on with baseOf(ddt) - dt = ddt.Base - } - } - // General cases. - switch cdt := dt.(type) { - case PrimitiveType: - // if xt is untyped, ensure dt is compatible. - switch xt { - case UntypedBoolType: - if dt.Kind() == BoolKind { - return // ok - } else { - panic(fmt.Sprintf( - "cannot use untyped bool as %s", - dt.Kind())) - } - case UntypedStringType: - if dt.Kind() == StringKind { - return // ok - } else { - panic(fmt.Sprintf( - "cannot use untyped string as %s", - dt.Kind())) - } - case UntypedRuneType, UntypedBigintType: - switch dt.Kind() { - case IntKind, Int8Kind, Int16Kind, Int32Kind, - Int64Kind, UintKind, Uint8Kind, Uint16Kind, - Uint32Kind, Uint64Kind: - return // ok - default: - panic(fmt.Sprintf( - "cannot use untyped rune as %s", - dt.Kind())) - } - default: - if isUntyped(xt) { - panic("unexpected untyped type") - } - if xt.TypeID() == cdt.TypeID() { - return // ok - } - } - case *PointerType: - if pt, ok := xt.(*PointerType); ok { - checkType(pt.Elt, cdt.Elt, false) - return // ok - } - case *ArrayType: - if at, ok := xt.(*ArrayType); ok { - checkType(at.Elt, cdt.Elt, false) - return // ok - } - case *SliceType: - if st, ok := xt.(*SliceType); ok { - checkType(st.Elt, cdt.Elt, false) - return // ok - } - case *MapType: - if mt, ok := xt.(*MapType); ok { - checkType(mt.Key, cdt.Key, false) - checkType(mt.Value, cdt.Value, false) - return // ok - } - case *FuncType: - if xt.TypeID() == cdt.TypeID() { - return // ok - } - case *InterfaceType: - panic("should not happen") - case *DeclaredType: - panic("should not happen") - case *StructType, *PackageType, *ChanType: - if xt.TypeID() == cdt.TypeID() { - return // ok - } - case *TypeType: - if xt.TypeID() == cdt.TypeID() { - return // ok - } - case *NativeType: - if !autoNative { - if xt.TypeID() == cdt.TypeID() { - return // ok - } - } else { - // autoNative, so check whether matches. - // xt: any type but a *DeclaredType; could be native. - // cdt: actual concrete native target type. - // ie, if cdt can match against xt. - if gno2GoTypeMatches(xt, cdt.Type) { - return // ok - } - } - default: - panic(fmt.Sprintf( - "unexpected type %s", - dt.String())) - } - panic(fmt.Sprintf( - "cannot use %s as %s", - xt.String(), - dt.String())) -} - // Returns any names not yet defined nor predefined in expr. These happen // upon transcribe:enter from the top, so value paths cannot be used. If no // names are un and x is TypeExpr, evalStaticType(store,last, x) must not @@ -3015,18 +2783,40 @@ func findUndefined2(store Store, last BlockNode, x Expr, t Type) (un Name) { return } -// like checkOrConvertType() but for any integer type. -func checkOrConvertIntegerType(store Store, last BlockNode, x Expr) { +// like checkOrConvertType() but for any typed bool kind. +func checkOrConvertBoolKind(store Store, last BlockNode, x Expr) { + if cx, ok := x.(*ConstExpr); ok { + convertConst(store, last, cx, BoolType) + } else if x != nil { + xt := evalStaticTypeOf(store, last, x) + checkBoolKind(xt) + } +} + +// assert that xt is a typed bool kind. +func checkBoolKind(xt Type) { + switch xt.Kind() { + case BoolKind: + return // ok + default: + panic(fmt.Sprintf( + "expected typed bool kind, but got %v", + xt.Kind())) + } +} + +// like checkOrConvertType() but for any typed integer kind. +func checkOrConvertIntegerKind(store Store, last BlockNode, x Expr) { if cx, ok := x.(*ConstExpr); ok { convertConst(store, last, cx, IntType) } else if x != nil { xt := evalStaticTypeOf(store, last, x) - checkIntegerType(xt) + checkIntegerKind(xt) } } -// assert that xt can be assigned as an integer type. -func checkIntegerType(xt Type) { +// assert that xt is a typed integer kind. +func checkIntegerKind(xt Type) { switch xt.Kind() { case IntKind, Int8Kind, Int16Kind, Int32Kind, Int64Kind, UintKind, Uint8Kind, Uint16Kind, Uint32Kind, Uint64Kind, @@ -3034,7 +2824,7 @@ func checkIntegerType(xt Type) { return // ok default: panic(fmt.Sprintf( - "expected integer type, but got %v", + "expected typed integer kind, but got %v", xt.Kind())) } } diff --git a/gnovm/pkg/gnolang/preprocess_test.go b/gnovm/pkg/gnolang/preprocess_test.go index 2419a385e14..49e6d53fd3d 100644 --- a/gnovm/pkg/gnolang/preprocess_test.go +++ b/gnovm/pkg/gnolang/preprocess_test.go @@ -29,7 +29,7 @@ func main() { defer func() { err := recover() - assert.Contains(t, fmt.Sprint(err), "incompatible types in binary expression") + assert.Contains(t, fmt.Sprint(err), "incompatible operands in binary expression") }() Preprocess(store, pn, n) } @@ -54,7 +54,7 @@ func main() { defer func() { err := recover() - assert.Contains(t, fmt.Sprint(err), "incompatible types in binary expression") + assert.Contains(t, fmt.Sprint(err), "incompatible operands in binary expression") }() Preprocess(store, pn, n) } diff --git a/gnovm/pkg/gnolang/realm.go b/gnovm/pkg/gnolang/realm.go index 85f94d4fcbe..3a55b2e14b4 100644 --- a/gnovm/pkg/gnolang/realm.go +++ b/gnovm/pkg/gnolang/realm.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "reflect" - "regexp" "strings" ) @@ -148,6 +147,12 @@ func (rlm *Realm) DidUpdate(po, xo, co Object) { if po.GetObjectID().PkgID != rlm.ID { panic("cannot modify external-realm or non-realm object") } + + // XXX check if this boosts performance + // XXX with broad integration benchmarking. + // XXX if co == xo { + // XXX } + // From here on, po is real (not new-real). // Updates to .newCreated/.newEscaped /.newDeleted made here. (first gen) // More appends happen during FinalizeRealmTransactions(). (second+ gen) @@ -724,18 +729,6 @@ func (rlm *Realm) saveObject(store Store, oo Object) { if oid.IsZero() { panic("unexpected zero object id") } - /* XXX DELETE - // ensure all types were already saved (@ preprocessor). - if debug { - types := getUnsavedTypes(oo, nil) - for _, typ := range types { - tid := typ.TypeID() - if store.GetType(tid) == nil { - panic("missing type") - } - } - } - */ // set hash to escape index. if oo.GetIsNewEscaped() { oo.SetIsNewEscaped(false) @@ -823,11 +816,10 @@ func getChildObjects(val Value, more []Value) []Value { case DataByteValue: panic("cannot get children from data byte objects") case PointerValue: - if cv.Base != nil { - more = getSelfOrChildObjects(cv.Base, more) - } else { - more = getSelfOrChildObjects(cv.TV.V, more) + if cv.Base == nil { + panic("should not happen") } + more = getSelfOrChildObjects(cv.Base, more) return more case *ArrayValue: for _, ctv := range cv.List { @@ -869,8 +861,14 @@ func getChildObjects(val Value, more []Value) []Value { for _, ctv := range cv.Values { more = getSelfOrChildObjects(ctv.V, more) } + // Generally the parent block must also be persisted. + // Otherwise NamePath may not resolve when referencing + // a parent block. more = getSelfOrChildObjects(cv.Parent, more) return more + case *HeapItemValue: + more = getSelfOrChildObjects(cv.Value.V, more) + return more case *NativeValue: panic("native values not supported") default: @@ -936,7 +934,7 @@ func copyMethods(methods []TypedValue) []TypedValue { // gets saved (e.g. from *Machine.savePackage()). res[i] = TypedValue{ T: copyTypeWithRefs(mtv.T), - V: copyValueWithRefs(nil, mtv.V), + V: copyValueWithRefs(mtv.V), } } return res @@ -1055,7 +1053,7 @@ func copyTypeWithRefs(typ Type) Type { // persistence bytes serialization. // Also checks for integrity of immediate children -- they must already be // persistent (real), and not dirty, or else this function panics. -func copyValueWithRefs(parent Object, val Value) Value { +func copyValueWithRefs(val Value) Value { switch cv := val.(type) { case nil: return nil @@ -1068,33 +1066,25 @@ func copyValueWithRefs(parent Object, val Value) Value { case DataByteValue: panic("cannot copy data byte value with references") case PointerValue: - if cv.Base != nil { - return PointerValue{ - /* - already represented in .Base[Index]: - TypedValue: &TypedValue{ - T: cv.TypedValue.T, - V: copyValueWithRefs(cv.TypedValue.V), - }, - */ - Base: toRefValue(parent, cv.Base), - Index: cv.Index, - } - } else { - etv := refOrCopyValue(parent, *cv.TV) - return PointerValue{ - TV: &etv, - /* - Base: nil, - Index: 0, - */ - } + if cv.Base == nil { + panic("should not happen") + } + return PointerValue{ + /* + already represented in .Base[Index]: + TypedValue: &TypedValue{ + T: cv.TypedValue.T, + V: copyValueWithRefs(cv.TypedValue.V), + }, + */ + Base: toRefValue(cv.Base), + Index: cv.Index, } case *ArrayValue: if cv.Data == nil { list := make([]TypedValue, len(cv.List)) for i, etv := range cv.List { - list[i] = refOrCopyValue(cv, etv) + list[i] = refOrCopyValue(etv) } return &ArrayValue{ ObjectInfo: cv.ObjectInfo.Copy(), @@ -1108,7 +1098,7 @@ func copyValueWithRefs(parent Object, val Value) Value { } case *SliceValue: return &SliceValue{ - Base: toRefValue(parent, cv.Base), + Base: toRefValue(cv.Base), Offset: cv.Offset, Length: cv.Length, Maxcap: cv.Maxcap, @@ -1116,7 +1106,7 @@ func copyValueWithRefs(parent Object, val Value) Value { case *StructValue: fields := make([]TypedValue, len(cv.Fields)) for i, ftv := range cv.Fields { - fields[i] = refOrCopyValue(cv, ftv) + fields[i] = refOrCopyValue(ftv) } return &StructValue{ ObjectInfo: cv.ObjectInfo.Copy(), @@ -1130,7 +1120,7 @@ func copyValueWithRefs(parent Object, val Value) Value { } var closure Value if cv.Closure != nil { - closure = toRefValue(parent, cv.Closure) + closure = toRefValue(cv.Closure) } // nativeBody funcs which don't come from NativeStore (and thus don't // have NativePkg/Name) can't be persisted, and should not be able @@ -1151,8 +1141,8 @@ func copyValueWithRefs(parent Object, val Value) Value { NativeName: cv.NativeName, } case *BoundMethodValue: - fnc := copyValueWithRefs(cv, cv.Func).(*FuncValue) - rtv := refOrCopyValue(cv, cv.Receiver) + fnc := copyValueWithRefs(cv.Func).(*FuncValue) + rtv := refOrCopyValue(cv.Receiver) return &BoundMethodValue{ ObjectInfo: cv.ObjectInfo.Copy(), // XXX ??? Func: fnc, @@ -1161,8 +1151,8 @@ func copyValueWithRefs(parent Object, val Value) Value { case *MapValue: list := &MapList{} for cur := cv.List.Head; cur != nil; cur = cur.Next { - key2 := refOrCopyValue(cv, cur.Key) - val2 := refOrCopyValue(cv, cur.Value) + key2 := refOrCopyValue(cur.Key) + val2 := refOrCopyValue(cur.Value) list.Append(nilAllocator, key2).Value = val2 } return &MapValue{ @@ -1172,10 +1162,10 @@ func copyValueWithRefs(parent Object, val Value) Value { case TypeValue: return toTypeValue(copyTypeWithRefs(cv.Type)) case *PackageValue: - block := toRefValue(cv, cv.Block) + block := toRefValue(cv.Block) fblocks := make([]Value, len(cv.FBlocks)) for i, fb := range cv.FBlocks { - fblocks[i] = toRefValue(cv, fb) + fblocks[i] = toRefValue(fb) } return &PackageValue{ ObjectInfo: cv.ObjectInfo.Copy(), @@ -1190,11 +1180,11 @@ func copyValueWithRefs(parent Object, val Value) Value { source := toRefNode(cv.Source) vals := make([]TypedValue, len(cv.Values)) for i, tv := range cv.Values { - vals[i] = refOrCopyValue(cv, tv) + vals[i] = refOrCopyValue(tv) } var bparent Value if cv.Parent != nil { - bparent = toRefValue(parent, cv.Parent) + bparent = toRefValue(cv.Parent) } bl := &Block{ ObjectInfo: cv.ObjectInfo.Copy(), @@ -1206,6 +1196,24 @@ func copyValueWithRefs(parent Object, val Value) Value { return bl case RefValue: return cv + case *HeapItemValue: + // NOTE: While this could be eliminated sometimes with some + // intelligence prior to persistence, to unwrap the + // HeapItemValue in case where the HeapItemValue only has + // refcount of 1, + // + // 1. The HeapItemValue is necessary when the .Value is a + // primitive non-object anyways, and + // 2. This would mean PointerValue.Base is nil, and we'd need + // additional logic to re-wrap when necessary, and + // 3. And with the above point, it's not clear the result + // would be any faster. But this is something we could + // explore after launch. + hiv := &HeapItemValue{ + ObjectInfo: cv.ObjectInfo.Copy(), + Value: refOrCopyValue(cv.Value), + } + return hiv case *NativeValue: panic("native values not supported") default: @@ -1377,6 +1385,9 @@ func fillTypesOfValue(store Store, val Value) Value { panic("native values not supported") case RefValue: // do nothing return cv + case *HeapItemValue: + fillTypesTV(store, &cv.Value) + return cv default: panic(fmt.Sprintf( "unexpected type %v", @@ -1424,7 +1435,7 @@ func toRefNode(bn BlockNode) RefNode { } } -func toRefValue(parent Object, val Value) RefValue { +func toRefValue(val Value) RefValue { // TODO use type switch stmt. if ref, ok := val.(RefValue); ok { return ref @@ -1497,15 +1508,15 @@ func ensureUniq(oozz ...[]Object) { } } -func refOrCopyValue(parent Object, tv TypedValue) TypedValue { +func refOrCopyValue(tv TypedValue) TypedValue { if tv.T != nil { tv.T = refOrCopyType(tv.T) } if obj, ok := tv.V.(Object); ok { - tv.V = toRefValue(parent, obj) + tv.V = toRefValue(obj) return tv } else { - tv.V = copyValueWithRefs(parent, tv.V) + tv.V = copyValueWithRefs(tv.V) return tv } } @@ -1514,20 +1525,6 @@ func isUnsaved(oo Object) bool { return oo.GetIsNewReal() || oo.GetIsDirty() } -// 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/" - -var ReGnoRunPath = regexp.MustCompile(`^gno\.land/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 - !ReGnoRunPath.MatchString(pkgPath) -} - func prettyJSON(jstr []byte) []byte { var c interface{} err := json.Unmarshal(jstr, &c) diff --git a/gnovm/pkg/gnolang/store.go b/gnovm/pkg/gnolang/store.go index 3db53213f8b..b2ec25e2778 100644 --- a/gnovm/pkg/gnolang/store.go +++ b/gnovm/pkg/gnolang/store.go @@ -305,7 +305,7 @@ func (ds *defaultStore) loadObjectSafe(oid ObjectID) Object { func (ds *defaultStore) SetObject(oo Object) { oid := oo.GetObjectID() // replace children/fields with Ref. - o2 := copyValueWithRefs(nil, oo) + o2 := copyValueWithRefs(oo) // marshal to binary. bz := amino.MustMarshalAny(o2) // set hash. diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go new file mode 100644 index 00000000000..870eb10b690 --- /dev/null +++ b/gnovm/pkg/gnolang/type_check.go @@ -0,0 +1,926 @@ +package gnolang + +import ( + "fmt" + "reflect" + + "github.com/gnolang/gno/tm2/pkg/errors" +) + +// here are a range of rules predefined for preprocessor to check the compatibility between operands and operators +// e,g. for binary expr x + y, x, y can only be numeric or string, 1+2, "a" + "b" +// this is used in assertCompatible()s. +var ( + binaryChecker = map[Word]func(t Type) bool{ + ADD: isNumericOrString, + SUB: isNumeric, + MUL: isNumeric, + QUO: isNumeric, + REM: isIntNum, + SHL: isIntNum, + SHR: isIntNum, + BAND: isIntNum, // bit ops + XOR: isIntNum, + BOR: isIntNum, + BAND_NOT: isIntNum, + LAND: isBoolean, // logic + LOR: isBoolean, + LSS: isOrdered, // compare + LEQ: isOrdered, + GTR: isOrdered, + GEQ: isOrdered, + } + // TODO: star, addressable + unaryChecker = map[Word]func(t Type) bool{ + ADD: isNumeric, + SUB: isNumeric, + XOR: isIntNum, + NOT: isBoolean, + } + IncDecStmtChecker = map[Word]func(t Type) bool{ + INC: isNumeric, + DEC: isNumeric, + } + AssignStmtChecker = map[Word]func(t Type) bool{ + ADD_ASSIGN: isNumericOrString, + SUB_ASSIGN: isNumeric, + MUL_ASSIGN: isNumeric, + QUO_ASSIGN: isNumeric, + REM_ASSIGN: isIntNum, + SHL_ASSIGN: isNumeric, + SHR_ASSIGN: isNumeric, + BAND_ASSIGN: isIntNum, + XOR_ASSIGN: isIntNum, + BOR_ASSIGN: isIntNum, + BAND_NOT_ASSIGN: isIntNum, + } +) + +type category int + +const ( + IsBoolean category = 1 << iota + IsInteger + IsFloat + IsString + IsBigInt + IsBigDec + + IsNumeric = IsInteger | IsFloat | IsBigInt | IsBigDec + IsOrdered = IsNumeric | IsString +) + +func (pt PrimitiveType) category() category { + switch pt.Kind() { + case BoolKind: + return IsBoolean + case StringKind: + return IsString + case IntKind, Int8Kind, Int16Kind, Int32Kind, Int64Kind, UintKind, Uint8Kind, Uint16Kind, Uint32Kind, Uint64Kind: + return IsInteger // UntypedRuneType is int32kind, DataByteType is uint8 kind + case Float32Kind, Float64Kind: + return IsFloat + case BigintKind: + return IsBigInt + case BigdecKind: + return IsBigDec + default: + panic(fmt.Sprintf("unexpected primitive type %v", pt)) + } +} + +func isOrdered(t Type) bool { + switch t := baseOf(t).(type) { + case PrimitiveType: + return t.category()&IsOrdered != 0 + default: + return false + } +} + +func isBoolean(t Type) bool { + switch t := baseOf(t).(type) { + case PrimitiveType: + return t.category()&IsBoolean != 0 + default: + return false + } +} + +// rune can be numeric and string +func isNumeric(t Type) bool { + switch t := baseOf(t).(type) { + case PrimitiveType: + return t.category()&IsNumeric != 0 + default: + return false + } +} + +func isIntNum(t Type) bool { + switch t := baseOf(t).(type) { + case PrimitiveType: + return t.category()&IsInteger != 0 || t.category()&IsBigInt != 0 + default: + return false + } +} + +func isNumericOrString(t Type) bool { + switch t := baseOf(t).(type) { + case PrimitiveType: + return t.category()&IsNumeric != 0 || t.category()&IsString != 0 + default: + return false + } +} + +// =========================================================== +func assertComparable(xt, dt Type) { + switch baseOf(dt).(type) { + case *SliceType, *FuncType, *MapType: + if xt != nil { + panic(fmt.Sprintf("%v can only be compared to nil", dt)) + } + } + assertComparable2(dt) +} + +// assert value with dt is comparable +func assertComparable2(dt Type) { + if debug { + debug.Printf("assertComparable2 dt: %v \n", dt) + } + switch cdt := baseOf(dt).(type) { + case PrimitiveType: + case *ArrayType: + switch baseOf(cdt.Elem()).(type) { + case PrimitiveType, *PointerType, *InterfaceType, *NativeType, *ArrayType, *StructType, *ChanType: + assertComparable2(cdt.Elem()) + default: + panic(fmt.Sprintf("%v is not comparable", dt)) + } + case *StructType: + for _, f := range cdt.Fields { + switch cft := baseOf(f.Type).(type) { + case PrimitiveType, *PointerType, *InterfaceType, *NativeType, *ArrayType, *StructType: + assertComparable2(cft) + default: + panic(fmt.Sprintf("%v is not comparable", dt)) + } + } + case *PointerType: // &a == &b + case *InterfaceType: + case *SliceType, *FuncType, *MapType: + case *NativeType: + if !cdt.Type.Comparable() { + panic(fmt.Sprintf("%v is not comparable", dt)) + } + default: + panic(fmt.Sprintf("%v is not comparable", dt)) + } +} + +func maybeNil(t Type) bool { + switch cxt := baseOf(t).(type) { + case *SliceType, *FuncType, *MapType, *InterfaceType, *PointerType, *ChanType: // we don't have unsafePointer + return true + case *NativeType: + switch nk := cxt.Type.Kind(); nk { + case reflect.Slice, reflect.Func, reflect.Map, reflect.Interface, reflect.Pointer: + return true + default: + return false + } + default: + return false + } +} + +func checkSame(at, bt Type, msg string) error { + if debug { + debug.Printf("checkSame, at: %v bt: %v \n", at, bt) + } + if at.TypeID() != bt.TypeID() { + return errors.New("incompatible types %v and %v %s", + at.TypeID(), bt.TypeID(), msg) + } + return nil +} + +func assertAssignableTo(xt, dt Type, autoNative bool) { + err := checkAssignableTo(xt, dt, autoNative) + if err != nil { + panic(err.Error()) + } +} + +// 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 { + if debug { + debug.Printf("checkAssignableTo, xt: %v dt: %v \n", xt, dt) + } + // case0 + if xt == nil { // see test/files/types/eql_0f18 + if !maybeNil(dt) { + panic(fmt.Sprintf("invalid operation, nil can not be compared to %v", dt)) + } + return nil + } else if dt == nil { // _ = xxx, assign8.gno, 0f31. else cases? + return nil + } + // case3 + if dt.Kind() == InterfaceKind { // note native interface + if idt, ok := baseOf(dt).(*InterfaceType); ok { + if idt.IsEmptyInterface() { // XXX, can this be merged with IsImplementedBy? + // if dt is an empty Gno interface, any x ok. + return nil // ok + } else if idt.IsImplementedBy(xt) { + // if dt implements idt, ok. + return nil // ok + } else { + return errors.New( + "%s does not implement %s", + xt.String(), + dt.String()) + } + } else if ndt, ok := baseOf(dt).(*NativeType); ok { + nidt := ndt.Type + if nidt.NumMethod() == 0 { + // if dt is an empty Go native interface, ditto. + return nil // ok + } else if nxt, ok := baseOf(xt).(*NativeType); ok { + // if xt has native base, do the naive native. + if nxt.Type.AssignableTo(nidt) { + return nil // ok + } else { + return errors.New( + "cannot use %s as %s", + nxt.String(), + nidt.String()) + } + } else if pxt, ok := baseOf(xt).(*PointerType); ok { + nxt, ok := pxt.Elt.(*NativeType) + if !ok { + return errors.New( + "pointer to non-native type cannot satisfy non-empty native interface; %s doesn't implement %s", + pxt.String(), + nidt.String()) + } + // if xt has native base, do the naive native. + if reflect.PtrTo(nxt.Type).AssignableTo(nidt) { + return nil // ok + } else { + return errors.New( + "cannot use %s as %s", + pxt.String(), + nidt.String()) + } + } else if xdt, ok := xt.(*DeclaredType); ok { + if gno2GoTypeMatches(baseOf(xdt), ndt.Type) { + return nil + } // not check against native interface + } else { + return errors.New( + "unexpected type pair: cannot use %s as %s", + xt.String(), + dt.String()) + } + } else { + return errors.New("should not happen") + } + } + + // case2 + // Special case if xt or dt is *PointerType to *NativeType, + // convert to *NativeType of pointer kind. + if pxt, ok := xt.(*PointerType); ok { + // *gonative{x} is gonative{*x} + //nolint:misspell + if enxt, ok := pxt.Elt.(*NativeType); ok { + xt = &NativeType{ + Type: reflect.PtrTo(enxt.Type), + } + } + } + if pdt, ok := dt.(*PointerType); ok { + // *gonative{x} is gonative{*x} + if endt, ok := pdt.Elt.(*NativeType); ok { + dt = &NativeType{ + Type: reflect.PtrTo(endt.Type), + } + } + } + + // Special case of xt or dt is *DeclaredType, + // allow implicit conversion unless both are declared. + // TODO simplify with .IsNamedType(). + if dxt, ok := xt.(*DeclaredType); ok { + if ddt, ok := dt.(*DeclaredType); ok { + // types must match exactly. + if !dxt.sealed && !ddt.sealed && + dxt.PkgPath == ddt.PkgPath && + dxt.Name == ddt.Name { // not yet sealed + return nil // ok + } else if dxt.TypeID() == ddt.TypeID() { + return nil // ok + } else { + return errors.New( + "cannot use %s as %s without explicit conversion", + dxt.String(), + ddt.String()) + } + } else { + // special case if implicitly named primitive type. + // TODO simplify with .IsNamedType(). + if _, ok := dt.(PrimitiveType); ok { + return errors.New( + "cannot use %s as %s without explicit conversion", + dxt.String(), + dt.String()) + } else { + // carry on with baseOf(dxt) + xt = dxt.Base // set as base to do the rest check + } + } + } else if ddt, ok := dt.(*DeclaredType); ok { + // special case if implicitly named primitive type. + // TODO simplify with .IsNamedType(). + if _, ok := xt.(PrimitiveType); ok { // e.g. 1 == Int(1) + if debug { + debug.Printf("xt is primitiveType: %v, ddt: %v \n", xt, ddt) + } + // this is special when dt is the declared type of x + if !isUntyped(xt) { + return errors.New( + "cannot use %s as %s without explicit conversion", + xt.String(), + ddt.String()) + } else { // xt untyped, carry on with check below + dt = ddt.Base + } + } else { + dt = ddt.Base + } + } + + // General cases. + switch cdt := dt.(type) { + case PrimitiveType: // case 1 + // if xt is untyped, ensure dt is compatible. + switch xt { + case UntypedBoolType: + if dt.Kind() == BoolKind { + return nil // ok + } else { + return errors.New( + "cannot use untyped bool as %s", + dt.Kind()) + } + case UntypedStringType: + if dt.Kind() == StringKind { + return nil // ok + } else { + return errors.New( + "cannot use untyped string as %s", + dt.Kind()) + } + // XXX, this is a loose check, we don't have the context + // to check if it is an exact integer, e.g. 1.2 or 1.0(1.0 can be converted to int). + // this ensure expr like (a % 1.0) pass check, while + // expr like (a % 1.2) panic at ConvertUntypedTo, which is a delayed assertion after const evaluated. + // assignable does not guarantee convertible. + case UntypedBigdecType: + switch dt.Kind() { + case IntKind, Int8Kind, Int16Kind, Int32Kind, + Int64Kind, UintKind, Uint8Kind, Uint16Kind, + Uint32Kind, Uint64Kind, BigdecKind, Float32Kind, Float64Kind: + return nil // ok + default: + panic(fmt.Sprintf( + "cannot use untyped Bigdec as %s", + dt.Kind())) + } + case UntypedBigintType: + switch dt.Kind() { + case IntKind, Int8Kind, Int16Kind, Int32Kind, + Int64Kind, UintKind, Uint8Kind, Uint16Kind, + Uint32Kind, Uint64Kind, BigintKind, BigdecKind, Float32Kind, Float64Kind: // see 0d0 + return nil // ok + default: + return errors.New( + "cannot use untyped Bigint as %s", + dt.Kind()) + } + case UntypedRuneType: + switch dt.Kind() { + case IntKind, Int8Kind, Int16Kind, Int32Kind, + Int64Kind, UintKind, Uint8Kind, Uint16Kind, + Uint32Kind, Uint64Kind, BigintKind, BigdecKind, Float32Kind, Float64Kind: + return nil // ok + default: + return errors.New( + "cannot use untyped rune as %s", + dt.Kind()) + } + + default: + if isUntyped(xt) { + panic("unexpected untyped type") + } + if xt.TypeID() == cdt.TypeID() { + return nil // ok + } + } + case *PointerType: // case 4 from here on + if pt, ok := xt.(*PointerType); ok { + return checkAssignableTo(pt.Elt, cdt.Elt, false) + } + case *ArrayType: + if at, ok := xt.(*ArrayType); ok { + if at.Len != cdt.Len { + return errors.New( + "cannot use %s as %s", + at.String(), + cdt.String()) + } + err := checkSame(at.Elt, cdt.Elt, "") + if err != nil { + return errors.New( + "cannot use %s as %s", + at.String(), + cdt.String()) + } + return nil + } + case *SliceType: + if st, ok := xt.(*SliceType); ok { + if cdt.Vrd { + return checkAssignableTo(st.Elt, cdt.Elt, false) + } else { + err := checkSame(st.Elt, cdt.Elt, "") + if err != nil { + return errors.New( + "cannot use %s as %s", + st.String(), + cdt.String()) + } + return nil + } + } + case *MapType: + if mt, ok := xt.(*MapType); ok { + err := checkSame(mt.Key, cdt.Key, "") + if err != nil { + return errors.New( + "cannot use %s as %s", + mt.String(), + cdt.String()).Stacktrace() + } + return nil + } + case *InterfaceType: + return errors.New("should not happen") + case *DeclaredType: + panic("should not happen") + case *FuncType, *StructType, *PackageType, *ChanType, *TypeType: + if xt.TypeID() == cdt.TypeID() { + return nil // ok + } + case *NativeType: + if !autoNative { + if debug { + debug.Printf("native type, xt.TypeID: %v, cdt.TypeID: %v \n", xt.TypeID(), cdt.TypeID()) + } + if xt.TypeID() == cdt.TypeID() { + return nil // ok + } + } else { + // autoNative, so check whether matches. + // xt: any type but a *DeclaredType; could be native. + // cdt: actual concrete native target type. + // ie, if cdt can match against xt. + if gno2GoTypeMatches(xt, cdt.Type) { + return nil // ok + } + } + default: + return errors.New( + "unexpected type %s", + dt.String()) + } + return errors.New( + "cannot use %s as %s", + xt.String(), + dt.String()).Stacktrace() +} + +// =========================================================== +func (x *BinaryExpr) checkShiftLhs(dt Type) { + if checker, ok := binaryChecker[x.Op]; ok { + if !checker(dt) { + panic(fmt.Sprintf("operator %s not defined on: %v", x.Op.TokenString(), kindString(dt))) + } + } else { + panic(fmt.Sprintf("checker for %s does not exist", x.Op)) + } +} + +// AssertCompatible works as a pre-check prior to checkOrConvertType. +// It checks against expressions to ensure the compatibility between operands and operators. +// e.g. "a" << 1, the left hand operand is not compatible with <<, it will fail the check. +// Overall,it efficiently filters out incompatible expressions, stopping before the next +// checkOrConvertType() operation to optimize performance. +func (x *BinaryExpr) AssertCompatible(lt, rt Type) { + // native type will be converted to gno in latter logic, + // this check logic will be conduct again from trans_leave *BinaryExpr. + lnt, lin := lt.(*NativeType) + rnt, rin := rt.(*NativeType) + if lin && rin { + if lt.TypeID() != rt.TypeID() { + panic(fmt.Sprintf( + "incompatible operands in binary expression: %s %s %s", + lt.TypeID(), x.Op, rt.TypeID())) + } + } + if lin { + if _, ok := go2GnoBaseType(lnt.Type).(PrimitiveType); ok { + return + } + } + if rin { + if _, ok := go2GnoBaseType(rnt.Type).(PrimitiveType); ok { + return + } + } + + xt, dt := lt, rt + if shouldSwapOnSpecificity(lt, rt) { + xt, dt = dt, xt + } + + if isComparison(x.Op) { + switch x.Op { + case EQL, NEQ: + assertComparable(xt, dt) + if !isUntyped(xt) && !isUntyped(dt) { + assertAssignableTo(xt, dt, false) + } + case LSS, LEQ, GTR, GEQ: + if checker, ok := binaryChecker[x.Op]; ok { + x.checkCompatibility(xt, dt, checker, x.Op.TokenString()) + } else { + panic(fmt.Sprintf("checker for %s does not exist", x.Op)) + } + default: + panic("invalid comparison operator") + } + } else { + if checker, ok := binaryChecker[x.Op]; ok { + x.checkCompatibility(xt, dt, checker, x.Op.TokenString()) + } else { + panic(fmt.Sprintf("checker for %s does not exist", x.Op)) + } + + switch x.Op { + case QUO, REM: + // special case of zero divisor + if isQuoOrRem(x.Op) { + if rcx, ok := x.Right.(*ConstExpr); ok { + if rcx.TypedValue.isZero() { + panic("invalid operation: division by zero") + } + } + } + default: + // do nothing + } + } +} + +// Check compatibility of the destination type (dt) with the operator. +// If both source type (xt) and destination type (dt) are typed: +// Verify that xt is assignable to dt. +// If xt is untyped: +// 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) { + 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) + if err != nil { + panic(fmt.Sprintf("invalid operation: mismatched types %v and %v", xt, dt)) + } + } +} + +func (x *UnaryExpr) AssertCompatible(t Type) { + if nt, ok := t.(*NativeType); ok { + if _, ok := go2GnoBaseType(nt.Type).(PrimitiveType); ok { + return + } + } + // check compatible + if checker, ok := unaryChecker[x.Op]; ok { + if !checker(t) { + panic(fmt.Sprintf("operator %s not defined on: %v", x.Op.TokenString(), kindString(t))) + } + } else { + panic(fmt.Sprintf("checker for %s does not exist", x.Op)) + } +} + +func (x *IncDecStmt) AssertCompatible(t Type) { + if nt, ok := t.(*NativeType); ok { + if _, ok := go2GnoBaseType(nt.Type).(PrimitiveType); ok { + return + } + } + // check compatible + if checker, ok := IncDecStmtChecker[x.Op]; ok { + if !checker(t) { + panic(fmt.Sprintf("operator %s not defined on: %v", x.Op.TokenString(), kindString(t))) + } + } else { + panic(fmt.Sprintf("checker for %s does not exist", x.Op)) + } +} + +func assertIndexTypeIsInt(kt Type) { + if kt.Kind() != IntKind { + panic(fmt.Sprintf("index type should be int, but got %v", kt)) + } +} + +func (x *RangeStmt) AssertCompatible(store Store, last BlockNode) { + if x.Op != ASSIGN { + return + } + if isBlankIdentifier(x.Key) && isBlankIdentifier(x.Value) { + // both "_" + return + } + assertValidAssignLhs(store, last, x.Key) + // if is valid left value + + kt := evalStaticTypeOf(store, last, x.Key) + var vt Type + if x.Value != nil { + vt = evalStaticTypeOf(store, last, x.Value) + } + + xt := evalStaticTypeOf(store, last, x.X) + switch cxt := xt.(type) { + case *MapType: + assertAssignableTo(cxt.Key, kt, false) + if vt != nil { + assertAssignableTo(cxt.Value, vt, false) + } + case *SliceType: + assertIndexTypeIsInt(kt) + if vt != nil { + assertAssignableTo(cxt.Elt, vt, false) + } + case *ArrayType: + assertIndexTypeIsInt(kt) + if vt != nil { + assertAssignableTo(cxt.Elt, vt, false) + } + case PrimitiveType: + if cxt.Kind() == StringKind { + if kt != nil && kt.Kind() != IntKind { + panic(fmt.Sprintf("index type should be int, but got %v", kt)) + } + if vt != nil { + if vt.Kind() != Int32Kind { // rune + panic(fmt.Sprintf("value type should be int32, but got %v", kt)) + } + } + } + } +} + +func (x *AssignStmt) AssertCompatible(store Store, last BlockNode) { + if x.Op == ASSIGN || x.Op == DEFINE { + if len(x.Lhs) > len(x.Rhs) { + if len(x.Rhs) != 1 { + panic(fmt.Sprintf("assignment mismatch: %d variables but %d values", len(x.Lhs), len(x.Rhs))) + } + switch cx := x.Rhs[0].(type) { + case *CallExpr: + // Call case: a, b = x(...) + ift := evalStaticTypeOf(store, last, cx.Func) + cft := getGnoFuncTypeOf(store, ift) + if len(x.Lhs) != len(cft.Results) { + panic(fmt.Sprintf( + "assignment mismatch: "+ + "%d variables but %s returns %d values", + len(x.Lhs), cx.Func.String(), len(cft.Results))) + } + if x.Op == ASSIGN { + // check assignable + for i, lx := range x.Lhs { + if !isBlankIdentifier(lx) { + assertValidAssignLhs(store, last, lx) + lxt := evalStaticTypeOf(store, last, lx) + assertAssignableTo(cft.Results[i].Type, lxt, false) + } + } + } + case *TypeAssertExpr: + // Type-assert case: a, ok := x.(type) + if len(x.Lhs) != 2 { + panic("should not happen") + } + if x.Op == ASSIGN { + // check assignable to first value + if !isBlankIdentifier(x.Lhs[0]) { // see composite3.gno + assertValidAssignLhs(store, last, x.Lhs[0]) + dt := evalStaticTypeOf(store, last, x.Lhs[0]) + ift := evalStaticTypeOf(store, last, cx) + assertAssignableTo(ift, dt, false) + } + if !isBlankIdentifier(x.Lhs[1]) { // see composite3.gno + assertValidAssignLhs(store, last, x.Lhs[1]) + dt := evalStaticTypeOf(store, last, x.Lhs[1]) + if dt.Kind() != BoolKind { // typed, not bool + panic(fmt.Sprintf("want bool type got %v", dt)) + } + } + } + cx.HasOK = true + case *IndexExpr: // must be with map type when len(Lhs) > len(Rhs) + if len(x.Lhs) != 2 { + panic("should not happen") + } + if x.Op == ASSIGN { + if !isBlankIdentifier(x.Lhs[0]) { + assertValidAssignLhs(store, last, x.Lhs[0]) + lt := evalStaticTypeOf(store, last, x.Lhs[0]) + if _, ok := cx.X.(*NameExpr); ok { + rt := evalStaticTypeOf(store, last, cx.X) + if mt, ok := rt.(*MapType); ok { + assertAssignableTo(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) + } else { + panic("should not happen") + } + } + } + if !isBlankIdentifier(x.Lhs[1]) { + assertValidAssignLhs(store, last, x.Lhs[1]) + dt := evalStaticTypeOf(store, last, x.Lhs[1]) + if dt != nil && dt.Kind() != BoolKind { // typed, not bool + panic(fmt.Sprintf("want bool type got %v", dt)) + } + } + } + cx.HasOK = true + default: + panic(fmt.Sprintf("RHS should not be %v when len(Lhs) > len(Rhs)", cx)) + } + } else { // len(Lhs) == len(Rhs) + if x.Op == ASSIGN { + // assert valid left value + for _, lx := range x.Lhs { + assertValidAssignLhs(store, last, lx) + } + } + } + } else { // Ops other than assign and define + // If this is an assignment operation, ensure there's only 1 + // expr on lhs/rhs. + if len(x.Lhs) != 1 || len(x.Rhs) != 1 { + panic("assignment operator " + x.Op.TokenString() + + " requires only one expression on lhs and rhs") + } + for i, lx := range x.Lhs { + lt := evalStaticTypeOf(store, last, lx) + rt := evalStaticTypeOf(store, last, x.Rhs[i]) + + if checker, ok := AssignStmtChecker[x.Op]; ok { + if !checker(lt) { + panic(fmt.Sprintf("operator %s not defined on: %v", x.Op.TokenString(), kindString(lt))) + } + switch x.Op { + case ADD_ASSIGN, SUB_ASSIGN, MUL_ASSIGN, QUO_ASSIGN, REM_ASSIGN, BAND_ASSIGN, BOR_ASSIGN, BAND_NOT_ASSIGN, XOR_ASSIGN: + // check when both typed + if !isUntyped(lt) && !isUntyped(rt) { // in this stage, lt or rt maybe untyped, not converted yet + if lt != nil && rt != nil { + if lt.TypeID() != rt.TypeID() { + panic(fmt.Sprintf("invalid operation: mismatched types %v and %v", lt, rt)) + } + } + } + default: + // do nothing + } + } else { + panic(fmt.Sprintf("checker for %s does not exist", x.Op)) + } + } + } +} + +// misc +func assertValidAssignLhs(store Store, last BlockNode, lx Expr) { + shouldPanic := true + switch clx := lx.(type) { + case *NameExpr, *StarExpr, *SelectorExpr: + shouldPanic = false + case *IndexExpr: + xt := evalStaticTypeOf(store, last, clx.X) + shouldPanic = xt != nil && xt.Kind() == StringKind + default: + } + if shouldPanic { + panic(fmt.Sprintf("cannot assign to %v", lx)) + } +} + +func kindString(xt Type) string { + if xt != nil { + return xt.Kind().String() + } + return "nil" +} + +func isQuoOrRem(op Word) bool { + switch op { + case QUO, QUO_ASSIGN, REM, REM_ASSIGN: + return true + default: + return false + } +} + +func isComparison(op Word) bool { + switch op { + case EQL, NEQ, LSS, LEQ, GTR, GEQ: + return true + default: + return false + } +} + +// shouldSwapOnSpecificity determines the potential direction for +// checkOrConvertType. it checks whether a swap is needed between two types +// based on their specificity. If t2 has a lower specificity than t1, it returns +// false, indicating no swap is needed. If t1 has a lower specificity than t2, +// it returns true, indicating a swap is needed. +func shouldSwapOnSpecificity(t1, t2 Type) bool { + // check nil + if t1 == nil { // see test file 0f46 + return false // also with both nil + } else if t2 == nil { + return true + } + + // check interface + if it1, ok := baseOf(t1).(*InterfaceType); ok { + if it1.IsEmptyInterface() { + return true // left empty interface + } else { + if _, ok := baseOf(t2).(*InterfaceType); ok { + return false + } else { + return true // right not interface + } + } + } else if _, ok := t2.(*InterfaceType); ok { + return false // left not interface, right is interface + } + + // primitive types + t1s, t2s := 0, 0 + if t1p, ok := t1.(PrimitiveType); ok { + t1s = t1p.Specificity() + } + if t2p, ok := t2.(PrimitiveType); ok { + t2s = t2p.Specificity() + } + if t1s < t2s { + // NOTE: higher specificity has lower value, so backwards. + return true + } else { + return false + } +} + +func isBlankIdentifier(x Expr) bool { + if nx, ok := x.(*NameExpr); ok { + return nx.Name == "_" + } + return false +} diff --git a/gnovm/pkg/gnolang/types.go b/gnovm/pkg/gnolang/types.go index fcfcd7d9d37..ab8e9effdc8 100644 --- a/gnovm/pkg/gnolang/types.go +++ b/gnovm/pkg/gnolang/types.go @@ -2203,11 +2203,11 @@ func KindOf(t Type) Kind { // ---------------------------------------- // main type-assertion functions. -// TODO: document what class of problems its for. +// Only for runtime debugging. // One of them can be nil, and this lets uninitialized primitives // and others serve as empty values. See doOpAdd() -// usage: if debug { assertSameTypes() } -func assertSameTypes(lt, rt Type) { +// usage: if debug { debugAssertSameTypes() } +func debugAssertSameTypes(lt, rt Type) { if lt == nil && rt == nil { // both are nil. } else if lt == nil || rt == nil { @@ -2232,8 +2232,10 @@ func assertSameTypes(lt, rt Type) { } } -// Like assertSameTypes(), but more relaxed, for == and !=. -func assertEqualityTypes(lt, rt Type) { +// Only for runtime debugging. +// Like debugAssertSameTypes(), but more relaxed, for == and !=. +// usage: if debug { debugAssertEqualityTypes() } +func debugAssertEqualityTypes(lt, rt Type) { if lt == nil && rt == nil { // both are nil. } else if lt == nil || rt == nil { @@ -2362,6 +2364,7 @@ func fillEmbeddedName(ft *FieldType) { ft.Embedded = true } +// TODO: empty interface? refer to assertAssignableTo func IsImplementedBy(it Type, ot Type) bool { switch cbt := baseOf(it).(type) { case *InterfaceType: @@ -2496,7 +2499,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 { - checkType(spec, match.Elem(), false) + assertAssignableTo(spec, match.Elem(), false) return // ok } else { // Panic here, because we don't know whether T @@ -2510,7 +2513,7 @@ func specifyType(store Store, lookup map[Name]Type, tmpl Type, spec Type, specTy } else { match, ok := lookup[ct.Generic] if ok { - checkType(spec, match, false) + assertAssignableTo(spec, match, false) return // ok } else { if isUntyped(spec) { diff --git a/gnovm/pkg/gnolang/uverse.go b/gnovm/pkg/gnolang/uverse.go index df16ecf0ad9..880a75396ca 100644 --- a/gnovm/pkg/gnolang/uverse.go +++ b/gnovm/pkg/gnolang/uverse.go @@ -911,16 +911,18 @@ func UverseNode() *PackageNode { tt := arg0.TV.GetType() vv := defaultValue(m.Alloc, tt) m.Alloc.AllocatePointer() + hi := m.Alloc.NewHeapItem(TypedValue{ + T: tt, + V: vv, + }) m.PushValue(TypedValue{ T: m.Alloc.NewType(&PointerType{ Elt: tt, }), V: PointerValue{ - TV: &TypedValue{ - T: tt, - V: vv, - }, - Base: nil, + TV: &hi.Value, + Base: hi, + Index: 0, }, }) return diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index b0996b98b6f..d38c083428c 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -41,6 +41,7 @@ func (*PackageValue) assertValue() {} func (*NativeValue) assertValue() {} func (*Block) assertValue() {} func (RefValue) assertValue() {} +func (*HeapItemValue) assertValue() {} const ( nilStr = "nil" @@ -64,6 +65,7 @@ var ( _ Value = &NativeValue{} _ Value = &Block{} _ Value = RefValue{} + _ Value = &HeapItemValue{} ) // ---------------------------------------- @@ -166,14 +168,20 @@ func (dbv DataByteValue) SetByte(b byte) { // Index is -1 for the shared "_" block var, // and -2 for (gno and native) map items. // +// A pointer constructed via a &x{} composite lit expression or constructed via +// new() or make() will have a virtual HeapItemValue as base. +// // Allocation for PointerValue is not immediate, // as usually PointerValues are temporary for assignment // or binary operations. When a pointer is to be // allocated, *Allocator.AllocatePointer() is called separately, // as in OpRef. +// +// Since PointerValue is used internally for assignment etc, +// it MUST stay minimal for computational efficiency. type PointerValue struct { TV *TypedValue // escape val if pointer to var. - Base Value // array/struct/block. + Base Value // array/struct/block, or heapitem. Index int // list/fields/values index, or -1 or -2 (see below). Key *TypedValue `json:",omitempty"` // for maps. } @@ -1494,6 +1502,88 @@ func (tv *TypedValue) GetBigDec() *apd.Decimal { return tv.V.(BigdecValue).V } +// returns true if tv is zero +func (tv *TypedValue) isZero() bool { + if tv.T == nil { + panic("type should not be nil") + } + switch tv.T.Kind() { + case IntKind: + v := tv.GetInt() + if v == 0 { + return true + } + case Int8Kind: + v := tv.GetInt8() + if v == 0 { + return true + } + case Int16Kind: + v := tv.GetInt16() + if v == 0 { + return true + } + case Int32Kind: + v := tv.GetInt32() + if v == 0 { + return true + } + case Int64Kind: + v := tv.GetInt64() + if v == 0 { + return true + } + case UintKind: + v := tv.GetUint() + if v == 0 { + return true + } + case Uint8Kind: + v := tv.GetUint8() + if v == 0 { + return true + } + case Uint16Kind: + v := tv.GetUint16() + if v == 0 { + return true + } + case Uint32Kind: + v := tv.GetUint32() + if v == 0 { + return true + } + case Uint64Kind: + v := tv.GetUint64() + if v == 0 { + return true + } + case Float32Kind: + v := tv.GetFloat32() + if v == 0 { + return true + } + case Float64Kind: + v := tv.GetFloat64() + if v == 0 { + return true + } + case BigintKind: + v := tv.GetBigInt() + if v.Sign() == 0 { + return true + } + case BigdecKind: + v := tv.GetBigDec() + if v.Sign() == 0 { + return true + } + default: + panic("not numeric") + } + return false +} + func (tv *TypedValue) ComputeMapKey(store Store, omitType bool) MapKey { // Special case when nil: has no separator. if tv.T == nil { @@ -2254,12 +2344,12 @@ func (tv *TypedValue) GetSlice2(alloc *Allocator, low, high, max int) TypedValue // TODO rename to BlockValue. type Block struct { - ObjectInfo // for closures - Source BlockNode - Values []TypedValue - Parent Value - Blank TypedValue // captures "_" // XXX remove and replace with global instance. - bodyStmt bodyStmt // XXX expose for persistence, not needed for MVP. + ObjectInfo + Source BlockNode + Values []TypedValue + Parent Value + Blank TypedValue // captures "_" // XXX remove and replace with global instance. + bodyStmt bodyStmt // XXX expose for persistence, not needed for MVP. } // NOTE: for allocation, use *Allocator.NewBlock. @@ -2419,6 +2509,15 @@ type RefValue struct { Hash ValueHash `json:",omitempty"` } +// Base for a detached singleton (e.g. new(int) or &struct{}) +// Conceptually like a Block that holds one value. +// NOTE: could be renamed to HeapItemBaseValue. +// See also note in realm.go about auto-unwrapping. +type HeapItemValue struct { + ObjectInfo + Value TypedValue +} + // ---------------------------------------- func defaultStructFields(alloc *Allocator, st *StructType) []TypedValue { @@ -2547,6 +2646,8 @@ func fillValueTV(store Store, tv *TypedValue) *TypedValue { case *Block: vpv := cb.GetPointerToInt(store, cv.Index) cv.TV = vpv.TV // TODO optimize? + case *HeapItemValue: + cv.TV = &cb.Value default: panic("should not happen") } diff --git a/gnovm/pkg/gnolang/values_conversions.go b/gnovm/pkg/gnolang/values_conversions.go index b4888878c7a..c5ddc232fcb 100644 --- a/gnovm/pkg/gnolang/values_conversions.go +++ b/gnovm/pkg/gnolang/values_conversions.go @@ -881,6 +881,11 @@ GNO_CASE: // Panics if conversion is illegal. // TODO: method on TypedValue? func ConvertUntypedTo(tv *TypedValue, t Type) { + if debug { + defer func() { + debug.Printf("ConvertUntypedTo done, tv: %v \n", tv) + }() + } if debug { if !isUntyped(tv.T) { panic(fmt.Sprintf( diff --git a/gnovm/pkg/gnolang/values_string.go b/gnovm/pkg/gnolang/values_string.go index 4ae05c8a97d..204fab62c86 100644 --- a/gnovm/pkg/gnolang/values_string.go +++ b/gnovm/pkg/gnolang/values_string.go @@ -258,6 +258,11 @@ func (v RefValue) String() string { v.PkgPath) } +func (v *HeapItemValue) String() string { + return fmt.Sprintf("heapitem(%v)", + v.Value) +} + // ---------------------------------------- // *TypedValue.Sprint diff --git a/gnovm/pkg/gnomod/file.go b/gnovm/pkg/gnomod/file.go index fda9263914e..b6ee95acac8 100644 --- a/gnovm/pkg/gnomod/file.go +++ b/gnovm/pkg/gnomod/file.go @@ -17,7 +17,7 @@ import ( "path/filepath" "strings" - "github.com/gnolang/gno/gnovm/pkg/transpiler" + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" "golang.org/x/mod/modfile" "golang.org/x/mod/module" ) @@ -183,13 +183,8 @@ func (f *File) FetchDeps(path string, remote string, verbose bool) error { if strings.HasSuffix(path, modFile.Module.Mod.Path) { continue } - // skip if `std`, special case. - if path == transpiler.GnoStdPkgAfter { - continue - } - if strings.HasPrefix(path, transpiler.ImportPrefix) { - path = strings.TrimPrefix(path, transpiler.ImportPrefix+"/examples/") + if !gno.IsStdlib(path) { modFile.AddNewRequire(path, "v0.0.0-latest", true) } } diff --git a/gnovm/pkg/gnomod/gnomod.go b/gnovm/pkg/gnomod/gnomod.go index 014873b8faa..0effa532107 100644 --- a/gnovm/pkg/gnomod/gnomod.go +++ b/gnovm/pkg/gnomod/gnomod.go @@ -3,6 +3,8 @@ package gnomod import ( "errors" "fmt" + "go/parser" + gotoken "go/token" "os" "path/filepath" "strings" @@ -63,23 +65,12 @@ func writePackage(remote, basePath, pkgPath string) (requirements []string, err } else { // Is File // Transpile and write generated go file - if strings.HasSuffix(fileName, ".gno") { - filePath := filepath.Join(basePath, pkgPath) - targetFilename, _ := transpiler.GetTranspileFilenameAndTags(filePath) - transpileRes, err := transpiler.Transpile(string(res.Data), "", fileName) - if err != nil { - return nil, fmt.Errorf("transpile: %w", err) - } - - for _, i := range transpileRes.Imports { - requirements = append(requirements, i.Path.Value) - } - - targetFileNameWithPath := filepath.Join(basePath, dirPath, targetFilename) - err = os.WriteFile(targetFileNameWithPath, []byte(transpileRes.Translated), 0o644) - if err != nil { - return nil, fmt.Errorf("writefile %q: %w", targetFileNameWithPath, err) - } + 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 @@ -96,11 +87,12 @@ func writePackage(remote, basePath, pkgPath string) (requirements []string, err // 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 := GetGnoModPath() - if strings.HasPrefix(f.Module.Mod.Path, transpiler.GnoRealmPkgsPrefixBefore) || - strings.HasPrefix(f.Module.Mod.Path, transpiler.GnoPurePkgsPrefixBefore) { - f.AddModuleStmt(transpiler.ImportPrefix + "/examples/" + f.Module.Mod.Path) + if !gnolang.IsStdlib(f.Module.Mod.Path) { + f.AddModuleStmt(transpiler.TranspileImportPath(f.Module.Mod.Path)) } for i := range f.Require { @@ -111,14 +103,13 @@ func GnoToGoMod(f File) (*File, error) { } } path := f.Require[i].Mod.Path - if strings.HasPrefix(f.Require[i].Mod.Path, transpiler.GnoRealmPkgsPrefixBefore) || - strings.HasPrefix(f.Require[i].Mod.Path, transpiler.GnoPurePkgsPrefixBefore) { + if !gnolang.IsStdlib(path) { // Add dependency with a modified import path - f.AddRequire(transpiler.ImportPrefix+"/examples/"+f.Require[i].Mod.Path, f.Require[i].Mod.Version) + f.AddRequire(transpiler.TranspileImportPath(path), f.Require[i].Mod.Version) } - f.AddReplace(f.Require[i].Mod.Path, f.Require[i].Mod.Version, filepath.Join(gnoModPath, path), "") + 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(f.Require[i].Mod.Path) + f.DropRequire(path) } // Remove replacements that are not replaced by directories. diff --git a/gnovm/pkg/integration/gno.go b/gnovm/pkg/integration/gno.go index ee0216fa9e8..a389b6a9b24 100644 --- a/gnovm/pkg/integration/gno.go +++ b/gnovm/pkg/integration/gno.go @@ -68,6 +68,8 @@ func SetupGno(p *testscript.Params, buildDir string) error { return fmt.Errorf("unable to create temporary home directory: %w", err) } env.Setenv("HOME", home) + // Avoids go command printing errors relating to lack of go.mod. + env.Setenv("GO111MODULE", "off") // Cleanup home folder env.Defer(func() { os.RemoveAll(home) }) diff --git a/gnovm/pkg/transpiler/transpiler.go b/gnovm/pkg/transpiler/transpiler.go index 8a91ae4a486..bd4bb1b1bc9 100644 --- a/gnovm/pkg/transpiler/transpiler.go +++ b/gnovm/pkg/transpiler/transpiler.go @@ -1,3 +1,5 @@ +// Package transpiler implements a source-to-source compiler for translating Gno +// code into Go code. package transpiler import ( @@ -9,102 +11,52 @@ import ( goscanner "go/scanner" "go/token" "os" - "os/exec" + "path" "path/filepath" - "regexp" - "sort" "strconv" "strings" + "github.com/gnolang/gno/gnovm/pkg/gnoenv" + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" + "github.com/gnolang/gno/gnovm/stdlibs" "golang.org/x/tools/go/ast/astutil" ) -const ( - GnoRealmPkgsPrefixBefore = "gno.land/r/" - GnoRealmPkgsPrefixAfter = "github.com/gnolang/gno/examples/gno.land/r/" - GnoPurePkgsPrefixBefore = "gno.land/p/" - GnoPurePkgsPrefixAfter = "github.com/gnolang/gno/examples/gno.land/p/" - GnoStdPkgBefore = "std" - GnoStdPkgAfter = "github.com/gnolang/gno/gnovm/stdlibs/stdshim" -) - -var stdlibWhitelist = []string{ - // go - "bufio", - "bytes", - "compress/gzip", - "context", - "crypto/md5", - "crypto/sha1", - "crypto/chacha20", - "crypto/cipher", - "crypto/sha256", - "encoding/base64", - "encoding/binary", - "encoding/hex", - "encoding/json", - "encoding/xml", - "errors", - "hash", - "hash/adler32", - "internal/bytealg", - "internal/os", - "flag", - "fmt", - "io", - "io/util", - "math", - "math/big", - "math/bits", - "math/rand", - "net/url", - "path", - "regexp", - "sort", - "strconv", - "strings", - "text/template", - "time", - "unicode", - "unicode/utf8", - "unicode/utf16", +// ImportPrefix is the import path to the root of the gno repository, which should +// be used to create go import paths. +const ImportPrefix = "github.com/gnolang/gno" - // gno - "std", +// TranspileImportPath takes an import path s, and converts it into the full +// import path relative to the Gno repository. +func TranspileImportPath(s string) string { + return ImportPrefix + "/" + PackageDirLocation(s) } -var importPrefixWhitelist = []string{ - "github.com/gnolang/gno/_test", +// PackageDirLocation provides the supposed directory of the package, relative to the root dir. +// +// TODO(morgan): move out, this should go in a "resolver" package. +func PackageDirLocation(s string) string { + switch { + case !gno.IsStdlib(s): + return "examples/" + s + default: + return "gnovm/stdlibs/" + s + } } -const ImportPrefix = "github.com/gnolang/gno" - -type transpileResult struct { +// Result is returned by Transpile, returning the file's imports and output +// out the transpilation. +type Result struct { Imports []*ast.ImportSpec Translated string + File *ast.File } // TODO: func TranspileFile: supports caching. // TODO: func TranspilePkg: supports directories. -func guessRootDir(fileOrPkg string, goBinary string) (string, error) { - abs, err := filepath.Abs(fileOrPkg) - if err != nil { - return "", err - } - args := []string{"list", "-m", "-mod=mod", "-f", "{{.Dir}}", ImportPrefix} - cmd := exec.Command(goBinary, args...) - cmd.Dir = abs - out, err := cmd.CombinedOutput() - if err != nil { - return "", fmt.Errorf("can't guess --root-dir") - } - rootDir := strings.TrimSpace(string(out)) - return rootDir, nil -} - -// GetTranspileFilenameAndTags returns the filename and tags for transpiled files. -func GetTranspileFilenameAndTags(gnoFilePath string) (targetFilename, tags string) { +// TranspiledFilenameAndTags returns the filename and tags for transpiled files. +func TranspiledFilenameAndTags(gnoFilePath string) (targetFilename, tags string) { nameNoExtension := strings.TrimSuffix(filepath.Base(gnoFilePath), ".gno") switch { case strings.HasSuffix(gnoFilePath, "_filetest.gno"): @@ -120,17 +72,43 @@ func GetTranspileFilenameAndTags(gnoFilePath string) (targetFilename, tags strin return } -func Transpile(source string, tags string, filename string) (*transpileResult, error) { +// Transpile performs transpilation on the given source code. tags can be used +// to specify build tags; and filename helps generate useful error messages and +// discriminate between test and normal source files. +func Transpile(source, tags, filename string) (*Result, error) { fset := token.NewFileSet() - f, err := parser.ParseFile(fset, filename, source, parser.ParseComments) + f, err := parser.ParseFile(fset, filename, source, + // SkipObjectResolution -- unused here. + // ParseComments -- so that they show up when re-building the AST. + parser.SkipObjectResolution|parser.ParseComments) if err != nil { return nil, fmt.Errorf("parse: %w", err) } isTestFile := strings.HasSuffix(filename, "_test.gno") || strings.HasSuffix(filename, "_filetest.gno") - shouldCheckWhitelist := !isTestFile + ctx := &transpileCtx{ + rootDir: gnoenv.RootDir(), + } + stdlibPrefix := filepath.Join(ctx.rootDir, "gnovm", "stdlibs") + if isTestFile { + // XXX(morgan): this disables checking that a package exists (in examples or stdlibs) + // when transpiling a test file. After all Gno functions, including those in + // tests/imports.go are converted to native bindings, support should + // be added for transpiling stdlibs only available in tests/stdlibs, and + // enable as such "package checking" also on test files. + ctx.rootDir = "" + } + if strings.HasPrefix(filename, stdlibPrefix) { + // this is a standard library. Mark it in the options so the native + // bindings resolve correctly. + path := strings.TrimPrefix(filename, stdlibPrefix) + path = filepath.ToSlash(filepath.Dir(path)) + path = strings.TrimLeft(path, "/") + + ctx.stdlibPath = path + } - transformed, err := transpileAST(fset, f, shouldCheckWhitelist) + transformed, err := ctx.transformFile(fset, f) if err != nil { return nil, fmt.Errorf("transpileAST: %w", err) } @@ -152,190 +130,64 @@ func Transpile(source string, tags string, filename string) (*transpileResult, e return nil, fmt.Errorf("format.Node: %w", err) } - res := &transpileResult{ + res := &Result{ Imports: f.Imports, Translated: out.String(), + File: transformed, } return res, nil } -// TranspileVerifyFile tries to run `go fmt` against a transpiled .go file. -// -// This is fast and won't look the imports. -func TranspileVerifyFile(path string, gofmtBinary string) error { - // TODO: use cmd/parser instead of exec? +type transpileCtx struct { + // If rootDir is given, we will check that the directory of the import path + // exists (using rootDir/packageDirLocation()). + rootDir string + // This should be set if we're working with a file from a standard library. + // This allows us to easily check if a function has a native binding, and as + // such modify its call expressions appropriately. + stdlibPath string - args := strings.Split(gofmtBinary, " ") - args = append(args, []string{"-l", "-e", path}...) - cmd := exec.Command(args[0], args[1:]...) - out, err := cmd.CombinedOutput() - if err != nil { - fmt.Fprintln(os.Stderr, string(out)) - return fmt.Errorf("%s: %w", gofmtBinary, err) - } - return nil -} - -// TranspileBuildPackage tries to run `go build` against the transpiled .go files. -// -// This method is the most efficient to detect errors but requires that -// all the import are valid and available. -func TranspileBuildPackage(fileOrPkg, goBinary string) error { - // TODO: use cmd/compile instead of exec? - // TODO: find the nearest go.mod file, chdir in the same folder, rim prefix? - // TODO: temporarily create an in-memory go.mod or disable go modules for gno? - // TODO: ignore .go files that were not generated from gno? - // TODO: automatically transpile if not yet done. - - files := []string{} - - info, err := os.Stat(fileOrPkg) - if err != nil { - return fmt.Errorf("invalid file or package path %s: %w", fileOrPkg, err) - } - if !info.IsDir() { - file := fileOrPkg - files = append(files, file) - } else { - pkgDir := fileOrPkg - goGlob := filepath.Join(pkgDir, "*.go") - goMatches, err := filepath.Glob(goGlob) - if err != nil { - return fmt.Errorf("glob %s: %w", goGlob, err) - } - for _, goMatch := range goMatches { - switch { - case strings.HasPrefix(goMatch, "."): // skip - case strings.HasSuffix(goMatch, "_filetest.go"): // skip - case strings.HasSuffix(goMatch, "_filetest.gno.gen.go"): // skip - case strings.HasSuffix(goMatch, "_test.go"): // skip - case strings.HasSuffix(goMatch, "_test.gno.gen.go"): // skip - default: - files = append(files, goMatch) - } - } - } - - sort.Strings(files) - args := append([]string{"build", "-v", "-tags=gno"}, files...) - cmd := exec.Command(goBinary, args...) - rootDir, err := guessRootDir(fileOrPkg, goBinary) - if err == nil { - cmd.Dir = rootDir - } - out, err := cmd.CombinedOutput() - if _, ok := err.(*exec.ExitError); ok { - // exit error - return parseGoBuildErrors(string(out)) - } - return err -} - -var reGoBuildError = regexp.MustCompile(`(?m)^(\S+):(\d+):(\d+): (.+)$`) - -// parseGoBuildErrors returns a scanner.ErrorList filled with all errors found -// in out, which is supposed to be the output of the `go build` command. -// -// TODO(tb): update when `go build -json` is released to replace regexp usage. -// See https://github.com/golang/go/issues/62067 -func parseGoBuildErrors(out string) error { - var errList goscanner.ErrorList - matches := reGoBuildError.FindAllStringSubmatch(out, -1) - for _, match := range matches { - filename := match[1] - line, err := strconv.Atoi(match[2]) - if err != nil { - return fmt.Errorf("parse line go build error %s: %w", match, err) - } - - column, err := strconv.Atoi(match[3]) - if err != nil { - return fmt.Errorf("parse column go build error %s: %w", match, err) - } - msg := match[4] - errList.Add(token.Position{ - Filename: filename, - Line: line, - Column: column, - }, msg) - } - return errList.Err() + stdlibImports map[string]string // symbol -> import path } -func transpileAST(fset *token.FileSet, f *ast.File, checkWhitelist bool) (ast.Node, error) { +func (ctx *transpileCtx) transformFile(fset *token.FileSet, f *ast.File) (*ast.File, error) { var errs goscanner.ErrorList imports := astutil.Imports(fset, f) + ctx.stdlibImports = make(map[string]string) - // import whitelist - if checkWhitelist { - for _, paragraph := range imports { - for _, importSpec := range paragraph { - importPath := strings.TrimPrefix(strings.TrimSuffix(importSpec.Path.Value, `"`), `"`) - - if strings.HasPrefix(importPath, GnoRealmPkgsPrefixBefore) { - continue - } - - if strings.HasPrefix(importPath, GnoPurePkgsPrefixBefore) { - continue - } - - valid := false - for _, whitelisted := range stdlibWhitelist { - if importPath == whitelisted { - valid = true - break - } - } - if valid { - continue - } - - for _, whitelisted := range importPrefixWhitelist { - if strings.HasPrefix(importPath, whitelisted) { - valid = true - break - } - } - if valid { - continue - } - - errs.Add(fset.Position(importSpec.Pos()), fmt.Sprintf("import %q is not in the whitelist", importPath)) - } - } - } - - // rewrite imports + // rewrite imports to point to stdlibs/ or examples/ for _, paragraph := range imports { for _, importSpec := range paragraph { - importPath := strings.TrimPrefix(strings.TrimSuffix(importSpec.Path.Value, `"`), `"`) - - // std package - if importPath == GnoStdPkgBefore { - if !astutil.RewriteImport(fset, f, GnoStdPkgBefore, GnoStdPkgAfter) { - errs.Add(fset.Position(importSpec.Pos()), fmt.Sprintf("failed to replace the %q package with %q", GnoStdPkgBefore, GnoStdPkgAfter)) - } + importPath, err := strconv.Unquote(importSpec.Path.Value) + if err != nil { + errs.Add(fset.Position(importSpec.Pos()), fmt.Sprintf("can't unquote import path %s: %v", importSpec.Path.Value, err)) + continue } - // p/pkg packages - if strings.HasPrefix(importPath, GnoPurePkgsPrefixBefore) { - target := GnoPurePkgsPrefixAfter + strings.TrimPrefix(importPath, GnoPurePkgsPrefixBefore) - - if !astutil.RewriteImport(fset, f, importPath, target) { - errs.Add(fset.Position(importSpec.Pos()), fmt.Sprintf("failed to replace the %q package with %q", importPath, target)) + if ctx.rootDir != "" { + dirPath := filepath.Join(ctx.rootDir, PackageDirLocation(importPath)) + if _, err := os.Stat(dirPath); err != nil { + if !os.IsNotExist(err) { + return nil, err + } + errs.Add(fset.Position(importSpec.Pos()), fmt.Sprintf("import %q does not exist", importPath)) + continue } } - // r/realm packages - if strings.HasPrefix(importPath, GnoRealmPkgsPrefixBefore) { - target := GnoRealmPkgsPrefixAfter + strings.TrimPrefix(importPath, GnoRealmPkgsPrefixBefore) - - if !astutil.RewriteImport(fset, f, importPath, target) { - errs.Add(fset.Position(importSpec.Pos()), fmt.Sprintf("failed to replace the %q package with %q", importPath, target)) + // Create mapping + if gno.IsStdlib(importPath) { + if importSpec.Name != nil { + ctx.stdlibImports[importSpec.Name.Name] = importPath + } else { + // XXX: imperfect, see comment on transformCallExpr + ctx.stdlibImports[path.Base(importPath)] = importPath } } + + transp := TranspileImportPath(importPath) + importSpec.Path.Value = strconv.Quote(transp) } } @@ -343,14 +195,72 @@ func transpileAST(fset *token.FileSet, f *ast.File, checkWhitelist bool) (ast.No node := astutil.Apply(f, // pre func(c *astutil.Cursor) bool { - // do things here + node := c.Node() + // is function declaration without body? + // -> delete (native binding) + if fd, ok := node.(*ast.FuncDecl); ok && fd.Body == nil { + c.Delete() + return false // don't attempt to traverse children + } + + // is function call to a native function? + // -> rename if unexported, apply `nil,` for the first arg if necessary + if ce, ok := node.(*ast.CallExpr); ok { + return ctx.transformCallExpr(c, ce) + } + return true }, + // post func(c *astutil.Cursor) bool { - // and here return true }, ) - return node, errs.Err() + return node.(*ast.File), errs.Err() +} + +func (ctx *transpileCtx) transformCallExpr(_ *astutil.Cursor, ce *ast.CallExpr) bool { + switch fe := ce.Fun.(type) { + case *ast.SelectorExpr: + // XXX: This is not correct in 100% of cases. If I shadow the `std` symbol, and + // its replacement is a type with the method AssertOriginCall, this system + // will incorrectly add a `nil` as the first argument. + // A full fix requires understanding scope; the Go standard library recommends + // using go/types, which for proper functioning requires an importer + // which can work with Gno. This is deferred for a future PR. + id, ok := fe.X.(*ast.Ident) + if !ok { + break + } + ip, ok := ctx.stdlibImports[id.Name] + if !ok { + break + } + nat := stdlibs.FindNative(ip, gno.Name(fe.Sel.Name)) + if nat != nil && nat.HasMachineParam() { + // Because it's an import, the symbol is always exported, so no need for the + // X_ prefix we add below. + ce.Args = append([]ast.Expr{ast.NewIdent("nil")}, ce.Args...) + } + + case *ast.Ident: + // Is this a native binding? + // Note: this is only useful within packages like `std` and `math`. + // The logic here is not robust to be generic. It does not account for locally + // defined scope. However, because native bindings have a narrowly defined and + // controlled scope (standard libraries) this will work for our usecase. + nat := stdlibs.FindNative(ctx.stdlibPath, gno.Name(fe.Name)) + if ctx.stdlibPath != "" && nat != nil { + if nat.HasMachineParam() { + ce.Args = append([]ast.Expr{ast.NewIdent("nil")}, ce.Args...) + } + if !fe.IsExported() { + // Prefix unexported names with X_, per native binding convention + // (to export the symbol within Go). + fe.Name = "X_" + fe.Name + } + } + } + return true } diff --git a/gnovm/pkg/transpiler/transpiler_test.go b/gnovm/pkg/transpiler/transpiler_test.go index b9e9b218675..2a0707f7f79 100644 --- a/gnovm/pkg/transpiler/transpiler_test.go +++ b/gnovm/pkg/transpiler/transpiler_test.go @@ -2,21 +2,69 @@ package transpiler import ( "go/ast" - goscanner "go/scanner" - "go/token" + "path/filepath" "strings" "testing" + "github.com/gnolang/gno/gnovm/pkg/gnoenv" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) +func TestTranspiledFilenameAndTags(t *testing.T) { + t.Parallel() + + tt := []struct { + name string + changed string + tags string + }{ + { + "hello.gno", + "hello.gno.gen.go", + "gno", + }, + { + "a/b/hello.gno", + "hello.gno.gen.go", + "gno", + }, + { + "hey_test.gno", + ".hey_test.gno.gen_test.go", + "gno && test", + }, + { + "hey_filetest.gno", + ".hey_filetest.gno.gen.go", + "gno && filetest", + }, + { + "badname.go", + "badname.go.gno.gen.go", + "gno", + }, + { + "badname_test.go", + "badname_test.go.gno.gen.go", + "gno", + }, + } + + for _, tc := range tt { + newName, tags := TranspiledFilenameAndTags(tc.name) + assert.Equal(t, tc.changed, newName, "name for %q", tc.name) + assert.Equal(t, tc.tags, tags, "tags for %q", tc.name) + } +} + func TestTranspile(t *testing.T) { t.Parallel() cases := []struct { name string tags string + filename string source string expectedOutput string expectedImports []*ast.ImportSpec @@ -75,7 +123,7 @@ func hello() string { //line foo.gno:1:1 package foo -import "github.com/gnolang/gno/gnovm/stdlibs/stdshim" +import "github.com/gnolang/gno/gnovm/stdlibs/std" func hello() string { _ = std.Foo @@ -87,9 +135,8 @@ func hello() string { Path: &ast.BasicLit{ ValuePos: 21, Kind: 9, - Value: `"github.com/gnolang/gno/gnovm/stdlibs/stdshim"`, + Value: `"github.com/gnolang/gno/gnovm/stdlibs/std"`, }, - EndPos: 26, }, }, }, @@ -119,7 +166,6 @@ func foo() { _ = users.Register } Kind: 9, Value: `"github.com/gnolang/gno/examples/gno.land/r/demo/users"`, }, - EndPos: 44, }, }, }, @@ -130,7 +176,7 @@ package foo import "gno.land/p/demo/avl" -func foo() { _ = avl.Tree } +func foo() { _ = avl.NewTree("hey", 1) } `, expectedOutput: ` // Code generated by github.com/gnolang/gno. DO NOT EDIT. @@ -140,7 +186,7 @@ package foo import "github.com/gnolang/gno/examples/gno.land/p/demo/avl" -func foo() { _ = avl.Tree } +func foo() { _ = avl.NewTree("hey", 1) } `, expectedImports: []*ast.ImportSpec{ { @@ -149,7 +195,6 @@ func foo() { _ = avl.Tree } Kind: 9, Value: `"github.com/gnolang/gno/examples/gno.land/p/demo/avl"`, }, - EndPos: 42, }, }, }, @@ -171,7 +216,7 @@ func hello() string { //line foo.gno:1:1 package foo -import bar "github.com/gnolang/gno/gnovm/stdlibs/stdshim" +import bar "github.com/gnolang/gno/gnovm/stdlibs/std" func hello() string { _ = bar.Foo @@ -187,14 +232,13 @@ func hello() string { Path: &ast.BasicLit{ ValuePos: 25, Kind: 9, - Value: `"github.com/gnolang/gno/gnovm/stdlibs/stdshim"`, + Value: `"github.com/gnolang/gno/gnovm/stdlibs/std"`, }, - EndPos: 30, }, }, }, { - name: "blacklisted-package", + name: "unknown-package", source: ` package foo @@ -202,7 +246,7 @@ import "reflect" func foo() { _ = reflect.ValueOf } `, - expectedError: `transpileAST: foo.gno:3:8: import "reflect" is not in the whitelist`, + expectedError: `transpileAST: foo.gno:3:8: import "reflect" does not exist`, }, { name: "syntax-error", @@ -226,6 +270,27 @@ import "gno.land/p/demo/unknownxyz" //line foo.gno:1:1 package foo +import "github.com/gnolang/gno/examples/gno.land/p/demo/unknownxyz" +`, + expectedError: `transpileAST: foo.gno:3:8: import "gno.land/p/demo/unknownxyz" does not exist`, + }, + { + // Test files should allow unknown imports while + // we still have "native" packages. + + name: "unknown-realm-test", + filename: "foo_test.gno", + source: ` +package foo + +import "gno.land/p/demo/unknownxyz" +`, + expectedOutput: ` +// Code generated by github.com/gnolang/gno. DO NOT EDIT. + +//line foo_test.gno:1:1 +package foo + import "github.com/gnolang/gno/examples/gno.land/p/demo/unknownxyz" `, expectedImports: []*ast.ImportSpec{ @@ -235,12 +300,11 @@ import "github.com/gnolang/gno/examples/gno.land/p/demo/unknownxyz" Kind: 9, Value: `"github.com/gnolang/gno/examples/gno.land/p/demo/unknownxyz"`, }, - EndPos: 49, }, }, }, { - name: "whitelisted-package", + name: "imported-package", source: ` package foo @@ -254,7 +318,7 @@ func foo() { _ = regexp.MatchString } //line foo.gno:1:1 package foo -import "regexp" +import "github.com/gnolang/gno/gnovm/stdlibs/regexp" func foo() { _ = regexp.MatchString } `, @@ -263,11 +327,87 @@ func foo() { _ = regexp.MatchString } Path: &ast.BasicLit{ ValuePos: 21, Kind: 9, - Value: `"regexp"`, + Value: `"github.com/gnolang/gno/gnovm/stdlibs/regexp"`, }, }, }, }, + { + name: "natbind-func", + filename: filepath.Join(gnoenv.RootDir(), "gnovm/stdlibs/math/math.gno"), + source: ` +package math + +import "std" + +func Float32bits(i float32) uint32 + +func testfunc() { + println(Float32bits(3.14159)) + std.AssertOriginCall() +} + +func otherFunc() { + std := 1 + // This is (incorrectly) changed for now. + std.AssertOriginCall() +} +`, + expectedOutput: ` +// Code generated by github.com/gnolang/gno. DO NOT EDIT. + +//line math.gno:1:1 +package math + +import "github.com/gnolang/gno/gnovm/stdlibs/std" + +func testfunc() { + println(Float32bits(3.14159)) + std.AssertOriginCall(nil) +} + +func otherFunc() { + std := 1 + // This is (incorrectly) changed for now. + std.AssertOriginCall(nil) +} +`, + expectedImports: []*ast.ImportSpec{ + { + Path: &ast.BasicLit{ + ValuePos: 22, + Kind: 9, + Value: `"github.com/gnolang/gno/gnovm/stdlibs/std"`, + }, + }, + }, + }, + { + name: "natbind-std", + filename: filepath.Join(gnoenv.RootDir(), "gnovm/stdlibs/std/std.gno"), + source: ` +package std + +func AssertOriginCall() +func origCaller() string + +func testfunc() { + AssertOriginCall() + println(origCaller()) +} +`, + expectedOutput: ` +// Code generated by github.com/gnolang/gno. DO NOT EDIT. + +//line std.gno:1:1 +package std + +func testfunc() { + AssertOriginCall(nil) + println(X_origCaller(nil)) +} +`, + }, } for _, c := range cases { c := c // scopelint @@ -276,7 +416,11 @@ func foo() { _ = regexp.MatchString } // "\n" is added for better test case readability, now trim it source := strings.TrimPrefix(c.source, "\n") - res, err := Transpile(source, c.tags, "foo.gno") + filename := c.filename + if filename == "" { + filename = "foo.gno" + } + res, err := Transpile(source, c.tags, filename) if c.expectedError != "" { require.EqualError(t, err, c.expectedError) @@ -289,53 +433,3 @@ func foo() { _ = regexp.MatchString } }) } } - -func TestParseGoBuildErrors(t *testing.T) { - tests := []struct { - name string - output string - expectedError error - }{ - { - name: "empty output", - output: "", - expectedError: nil, - }, - { - name: "random output", - output: "xxx", - expectedError: nil, - }, - { - name: "some errors", - output: `xxx -main.gno:6:2: nasty error -pkg/file.gno:60:20: ugly error`, - expectedError: goscanner.ErrorList{ - &goscanner.Error{ - Pos: token.Position{ - Filename: "main.gno", - Line: 6, - Column: 2, - }, - Msg: "nasty error", - }, - &goscanner.Error{ - Pos: token.Position{ - Filename: "pkg/file.gno", - Line: 60, - Column: 20, - }, - Msg: "ugly error", - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := parseGoBuildErrors(tt.output) - - assert.Equal(t, tt.expectedError, err) - }) - } -} diff --git a/gnovm/stdlibs/bytes/boundary_test.gno b/gnovm/stdlibs/bytes/boundary_test.gno index 9873b1db987..5d77c576ff8 100644 --- a/gnovm/stdlibs/bytes/boundary_test.gno +++ b/gnovm/stdlibs/bytes/boundary_test.gno @@ -1,8 +1,6 @@ // Copyright 2017 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// -//go:build linux package bytes_test diff --git a/gnovm/stdlibs/crypto/chacha20/chacha/chacha_ref.gno b/gnovm/stdlibs/crypto/chacha20/chacha/chacha_ref.gno index 903e2653572..91e5481fc42 100644 --- a/gnovm/stdlibs/crypto/chacha20/chacha/chacha_ref.gno +++ b/gnovm/stdlibs/crypto/chacha20/chacha/chacha_ref.gno @@ -2,8 +2,6 @@ // Use of this source code is governed by a license that can be // found in the LICENSE file. -//go:build (!amd64 && !386) || gccgo || appengine || nacl - package chacha import "encoding/binary" diff --git a/gnovm/stdlibs/internal/bytealg/compare_generic.gno b/gnovm/stdlibs/internal/bytealg/compare_generic.gno index e1795e47e9a..b56d0a67e02 100644 --- a/gnovm/stdlibs/internal/bytealg/compare_generic.gno +++ b/gnovm/stdlibs/internal/bytealg/compare_generic.gno @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !386 && !amd64 && !s390x && !arm && !arm64 && !ppc64 && !ppc64le && !mips && !mipsle && !wasm && !mips64 && !mips64le - package bytealg // import _ "unsafe" // for go:linkname @@ -35,7 +33,6 @@ samebytes: return 0 } -//go:linkname runtime_cmpstring runtime.cmpstring func runtime_cmpstring(a, b string) int { l := len(a) if len(b) < l { diff --git a/gnovm/stdlibs/internal/bytealg/count_generic.gno b/gnovm/stdlibs/internal/bytealg/count_generic.gno index 932a7c584c1..de08418fcaa 100644 --- a/gnovm/stdlibs/internal/bytealg/count_generic.gno +++ b/gnovm/stdlibs/internal/bytealg/count_generic.gno @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !amd64 && !arm && !arm64 && !ppc64le && !ppc64 && !riscv64 && !s390x - package bytealg func Count(b []byte, c byte) int { diff --git a/gnovm/stdlibs/internal/bytealg/index_generic.gno b/gnovm/stdlibs/internal/bytealg/index_generic.gno index a59e32938e7..d751b1bc940 100644 --- a/gnovm/stdlibs/internal/bytealg/index_generic.gno +++ b/gnovm/stdlibs/internal/bytealg/index_generic.gno @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !amd64 && !arm64 && !s390x && !ppc64le && !ppc64 - package bytealg const MaxBruteForce = 0 diff --git a/gnovm/stdlibs/internal/bytealg/indexbyte_generic.gno b/gnovm/stdlibs/internal/bytealg/indexbyte_generic.gno index 0a45f903843..47aee225df9 100644 --- a/gnovm/stdlibs/internal/bytealg/indexbyte_generic.gno +++ b/gnovm/stdlibs/internal/bytealg/indexbyte_generic.gno @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !386 && !amd64 && !s390x && !arm && !arm64 && !ppc64 && !ppc64le && !mips && !mipsle && !mips64 && !mips64le && !riscv64 && !wasm - package bytealg func IndexByte(b []byte, c byte) int { diff --git a/gnovm/stdlibs/native.go b/gnovm/stdlibs/native.go index 7319e393c35..3dd432c90c0 100644 --- a/gnovm/stdlibs/native.go +++ b/gnovm/stdlibs/native.go @@ -16,15 +16,25 @@ import ( libs_time "github.com/gnolang/gno/gnovm/stdlibs/time" ) -type nativeFunc struct { - gnoPkg string - gnoFunc gno.Name - params []gno.FieldTypeExpr - results []gno.FieldTypeExpr - f func(m *gno.Machine) +// NativeFunc represents a function in the standard library which has a native +// (go-based) implementation, commonly referred to as a "native binding". +type NativeFunc struct { + gnoPkg string + gnoFunc gno.Name + params []gno.FieldTypeExpr + results []gno.FieldTypeExpr + hasMachine bool + f func(m *gno.Machine) } -var nativeFuncs = [...]nativeFunc{ +// HasMachineParam returns whether the given native binding has a machine parameter. +// This means that the Go version of this function expects a *gno.Machine +// as its first parameter. +func (n *NativeFunc) HasMachineParam() bool { + return n.hasMachine +} + +var nativeFuncs = [...]NativeFunc{ { "crypto/ed25519", "verify", @@ -36,6 +46,7 @@ var nativeFuncs = [...]nativeFunc{ []gno.FieldTypeExpr{ {Name: gno.N("r0"), Type: gno.X("bool")}, }, + false, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -69,6 +80,7 @@ var nativeFuncs = [...]nativeFunc{ []gno.FieldTypeExpr{ {Name: gno.N("r0"), Type: gno.X("[32]byte")}, }, + false, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -96,6 +108,7 @@ var nativeFuncs = [...]nativeFunc{ []gno.FieldTypeExpr{ {Name: gno.N("r0"), Type: gno.X("uint32")}, }, + false, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -123,6 +136,7 @@ var nativeFuncs = [...]nativeFunc{ []gno.FieldTypeExpr{ {Name: gno.N("r0"), Type: gno.X("float32")}, }, + false, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -150,6 +164,7 @@ var nativeFuncs = [...]nativeFunc{ []gno.FieldTypeExpr{ {Name: gno.N("r0"), Type: gno.X("uint64")}, }, + false, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -177,6 +192,7 @@ var nativeFuncs = [...]nativeFunc{ []gno.FieldTypeExpr{ {Name: gno.N("r0"), Type: gno.X("float64")}, }, + false, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -206,6 +222,7 @@ var nativeFuncs = [...]nativeFunc{ {Name: gno.N("r0"), Type: gno.X("[]string")}, {Name: gno.N("r1"), Type: gno.X("[]int64")}, }, + true, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -245,6 +262,7 @@ var nativeFuncs = [...]nativeFunc{ {Name: gno.N("p4"), Type: gno.X("[]int64")}, }, []gno.FieldTypeExpr{}, + true, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -281,6 +299,7 @@ var nativeFuncs = [...]nativeFunc{ []gno.FieldTypeExpr{ {Name: gno.N("r0"), Type: gno.X("int64")}, }, + true, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -314,6 +333,7 @@ var nativeFuncs = [...]nativeFunc{ {Name: gno.N("p3"), Type: gno.X("int64")}, }, []gno.FieldTypeExpr{}, + true, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -347,6 +367,7 @@ var nativeFuncs = [...]nativeFunc{ {Name: gno.N("p3"), Type: gno.X("int64")}, }, []gno.FieldTypeExpr{}, + true, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -378,6 +399,7 @@ var nativeFuncs = [...]nativeFunc{ {Name: gno.N("p1"), Type: gno.X("[]string")}, }, []gno.FieldTypeExpr{}, + true, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -400,6 +422,7 @@ var nativeFuncs = [...]nativeFunc{ "AssertOriginCall", []gno.FieldTypeExpr{}, []gno.FieldTypeExpr{}, + true, func(m *gno.Machine) { libs_std.AssertOriginCall( m, @@ -413,6 +436,7 @@ var nativeFuncs = [...]nativeFunc{ []gno.FieldTypeExpr{ {Name: gno.N("r0"), Type: gno.X("bool")}, }, + true, func(m *gno.Machine) { r0 := libs_std.IsOriginCall( m, @@ -432,6 +456,7 @@ var nativeFuncs = [...]nativeFunc{ []gno.FieldTypeExpr{ {Name: gno.N("r0"), Type: gno.X("string")}, }, + true, func(m *gno.Machine) { r0 := libs_std.GetChainID( m, @@ -451,6 +476,7 @@ var nativeFuncs = [...]nativeFunc{ []gno.FieldTypeExpr{ {Name: gno.N("r0"), Type: gno.X("int64")}, }, + true, func(m *gno.Machine) { r0 := libs_std.GetHeight( m, @@ -471,6 +497,7 @@ var nativeFuncs = [...]nativeFunc{ {Name: gno.N("r0"), Type: gno.X("[]string")}, {Name: gno.N("r1"), Type: gno.X("[]int64")}, }, + true, func(m *gno.Machine) { r0, r1 := libs_std.X_origSend( m, @@ -495,6 +522,7 @@ var nativeFuncs = [...]nativeFunc{ []gno.FieldTypeExpr{ {Name: gno.N("r0"), Type: gno.X("string")}, }, + true, func(m *gno.Machine) { r0 := libs_std.X_origCaller( m, @@ -514,6 +542,7 @@ var nativeFuncs = [...]nativeFunc{ []gno.FieldTypeExpr{ {Name: gno.N("r0"), Type: gno.X("string")}, }, + true, func(m *gno.Machine) { r0 := libs_std.X_origPkgAddr( m, @@ -535,6 +564,7 @@ var nativeFuncs = [...]nativeFunc{ []gno.FieldTypeExpr{ {Name: gno.N("r0"), Type: gno.X("string")}, }, + true, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -565,6 +595,7 @@ var nativeFuncs = [...]nativeFunc{ {Name: gno.N("r0"), Type: gno.X("string")}, {Name: gno.N("r1"), Type: gno.X("string")}, }, + true, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -599,6 +630,7 @@ var nativeFuncs = [...]nativeFunc{ []gno.FieldTypeExpr{ {Name: gno.N("r0"), Type: gno.X("string")}, }, + false, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -627,6 +659,7 @@ var nativeFuncs = [...]nativeFunc{ []gno.FieldTypeExpr{ {Name: gno.N("r0"), Type: gno.X("string")}, }, + false, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -659,6 +692,7 @@ var nativeFuncs = [...]nativeFunc{ {Name: gno.N("r1"), Type: gno.X("[20]byte")}, {Name: gno.N("r2"), Type: gno.X("bool")}, }, + false, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -696,6 +730,7 @@ var nativeFuncs = [...]nativeFunc{ []gno.FieldTypeExpr{ {Name: gno.N("r0"), Type: gno.X("string")}, }, + false, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -725,6 +760,7 @@ var nativeFuncs = [...]nativeFunc{ []gno.FieldTypeExpr{ {Name: gno.N("r0"), Type: gno.X("[]byte")}, }, + false, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -759,6 +795,7 @@ var nativeFuncs = [...]nativeFunc{ {Name: gno.N("r0"), Type: gno.X("int")}, {Name: gno.N("r1"), Type: gno.X("error")}, }, + false, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -791,6 +828,7 @@ var nativeFuncs = [...]nativeFunc{ []gno.FieldTypeExpr{ {Name: gno.N("r0"), Type: gno.X("bool")}, }, + false, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -819,6 +857,7 @@ var nativeFuncs = [...]nativeFunc{ []gno.FieldTypeExpr{ {Name: gno.N("r0"), Type: gno.X("string")}, }, + false, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -850,6 +889,7 @@ var nativeFuncs = [...]nativeFunc{ []gno.FieldTypeExpr{ {Name: gno.N("r0"), Type: gno.X("string")}, }, + false, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -880,6 +920,7 @@ var nativeFuncs = [...]nativeFunc{ []gno.FieldTypeExpr{ {Name: gno.N("r0"), Type: gno.X("string")}, }, + false, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -907,6 +948,7 @@ var nativeFuncs = [...]nativeFunc{ []gno.FieldTypeExpr{ {Name: gno.N("r0"), Type: gno.X("string")}, }, + false, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -932,6 +974,7 @@ var nativeFuncs = [...]nativeFunc{ []gno.FieldTypeExpr{ {Name: gno.N("r0"), Type: gno.X("int64")}, }, + false, func(m *gno.Machine) { r0 := libs_testing.X_unixNano() @@ -951,6 +994,7 @@ var nativeFuncs = [...]nativeFunc{ {Name: gno.N("r1"), Type: gno.X("int32")}, {Name: gno.N("r2"), Type: gno.X("int64")}, }, + true, func(m *gno.Machine) { r0, r1, r2 := libs_time.X_now( m, diff --git a/gnovm/stdlibs/stdlibs.go b/gnovm/stdlibs/stdlibs.go index 48e69f78253..c9b16815ab5 100644 --- a/gnovm/stdlibs/stdlibs.go +++ b/gnovm/stdlibs/stdlibs.go @@ -13,11 +13,24 @@ func GetContext(m *gno.Machine) ExecContext { return libsstd.GetContext(m) } -func NativeStore(pkgPath string, name gno.Name) func(*gno.Machine) { - for _, nf := range nativeFuncs { +// FindNative returns the NativeFunc associated with the given pkgPath+name +// combination. If there is none, FindNative returns nil. +func FindNative(pkgPath string, name gno.Name) *NativeFunc { + for i, nf := range nativeFuncs { if nf.gnoPkg == pkgPath && name == nf.gnoFunc { - return nf.f + return &nativeFuncs[i] } } return nil } + +// NativeStore 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) { + nt := FindNative(pkgPath, name) + if nt == nil { + return nil + } + return nt.f +} diff --git a/gnovm/stdlibs/stdshim/addr_set.gno b/gnovm/stdlibs/stdshim/addr_set.gno deleted file mode 100644 index 3f46f48b8b5..00000000000 --- a/gnovm/stdlibs/stdshim/addr_set.gno +++ /dev/null @@ -1,47 +0,0 @@ -package std - -import "errors" - -//---------------------------------------- -// AddressSet - -type AddressSet interface { - Size() int - AddAddress(Address) error - HasAddress(Address) bool -} - -//---------------------------------------- -// AddressList implements AddressSet. -// TODO implement AddressTree with avl. - -type AddressList []Address - -func NewAddressList() *AddressList { - return &AddressList{} -} - -func (alist *AddressList) Size() int { - return len(*alist) -} - -func (alist *AddressList) AddAddress(newAddr Address) error { - // TODO optimize with binary algorithm - for _, addr := range *alist { - if addr == newAddr { - return errors.New("address already exists") - } - } - *alist = append(*alist, newAddr) - return nil -} - -func (alist *AddressList) HasAddress(newAddr Address) bool { - // TODO optimize with binary algorithm - for _, addr := range *alist { - if addr == newAddr { - return true - } - } - return false -} diff --git a/gnovm/stdlibs/stdshim/banker.gno b/gnovm/stdlibs/stdshim/banker.gno deleted file mode 100644 index 44e611b780f..00000000000 --- a/gnovm/stdlibs/stdshim/banker.gno +++ /dev/null @@ -1,70 +0,0 @@ -package std - -// Realm functions can call std.GetBanker(options) to get -// a banker instance. Banker objects cannot be persisted, -// but can be passed onto other functions to be transacted -// on. A banker instance can be passed onto other realm -// functions; this allows other realms to spend coins on -// behalf of the first realm. -// -// Banker panics on errors instead of returning errors. -// This also helps simplify the interface and prevent -// hidden bugs (e.g. ignoring errors) -// -// NOTE: this Gno interface is satisfied by a native go -// type, and those can't return non-primitive objects -// (without confusion). -type Banker interface { - GetCoins(addr Address) (dst Coins) - SendCoins(from, to Address, amt Coins) - TotalCoin(denom string) int64 - IssueCoin(addr Address, denom string, amount int64) - RemoveCoin(addr Address, denom string, amount int64) -} - -// Also available natively in stdlibs/context.go -type BankerType uint8 - -// Also available natively in stdlibs/context.go -const ( - // Can only read state. - BankerTypeReadonly BankerType = iota - // Can only send from tx send. - BankerTypeOrigSend - // Can send from all realm coins. - BankerTypeRealmSend - // Can issue and remove realm coins. - BankerTypeRealmIssue -) - -//---------------------------------------- -// adapter for native banker - -type bankAdapter struct { - nativeBanker Banker -} - -func (ba bankAdapter) GetCoins(addr Address) (dst Coins) { - // convert native -> gno - coins := ba.nativeBanker.GetCoins(addr) - for _, coin := range coins { - dst = append(dst, (Coin)(coin)) - } - return dst -} - -func (ba bankAdapter) SendCoins(from, to Address, amt Coins) { - ba.nativeBanker.SendCoins(from, to, amt) -} - -func (ba bankAdapter) TotalCoin(denom string) int64 { - return ba.nativeBanker.TotalCoin(denom) -} - -func (ba bankAdapter) IssueCoin(addr Address, denom string, amount int64) { - ba.nativeBanker.IssueCoin(addr, denom, amount) -} - -func (ba bankAdapter) RemoveCoin(addr Address, denom string, amount int64) { - ba.nativeBanker.RemoveCoin(addr, denom, amount) -} diff --git a/gnovm/stdlibs/stdshim/coins.gno b/gnovm/stdlibs/stdshim/coins.gno deleted file mode 100644 index 4589113bff4..00000000000 --- a/gnovm/stdlibs/stdshim/coins.gno +++ /dev/null @@ -1,174 +0,0 @@ -package std - -import "strconv" - -// NOTE: this is selectively copied over from tm2/pkgs/std/coin.go - -// Coin hold some amount of one currency. -// A negative amount is invalid. -type Coin struct { - Denom string `json:"denom"` - Amount int64 `json:"amount"` -} - -// NewCoin returns a new coin with a denomination and amount -func NewCoin(denom string, amount int64) Coin { - return Coin{ - Denom: denom, - Amount: amount, - } -} - -// String provides a human-readable representation of a coin -func (c Coin) String() string { - return strconv.Itoa(int(c.Amount)) + c.Denom -} - -// IsGTE returns true if they are the same type and the receiver is -// an equal or greater value -func (c Coin) IsGTE(other Coin) bool { - mustMatchDenominations(c.Denom, other.Denom) - - return c.Amount >= other.Amount -} - -// IsLT returns true if they are the same type and the receiver is -// a smaller value -func (c Coin) IsLT(other Coin) bool { - mustMatchDenominations(c.Denom, other.Denom) - - return c.Amount < other.Amount -} - -// IsEqual returns true if the two sets of Coins have the same value -func (c Coin) IsEqual(other Coin) bool { - mustMatchDenominations(c.Denom, other.Denom) - - return c.Amount == other.Amount -} - -// Add adds amounts of two coins with same denom. -// If the coins differ in denom then it panics. -// An overflow or underflow panics. -// An invalid result panics. -func (c Coin) Add(coinB Coin) Coin { - mustMatchDenominations(c.Denom, coinB.Denom) - - sum := c.Amount + coinB.Amount - - c.Amount = sum - return c -} - -// Sub subtracts amounts of two coins with same denom. -// If the coins differ in denom then it panics. -// An overflow or underflow panics. -// An invalid result panics. -func (c Coin) Sub(coinB Coin) Coin { - mustMatchDenominations(c.Denom, coinB.Denom) - - dff := c.Amount - coinB.Amount - c.Amount = dff - - return c -} - -// IsPositive returns true if coin amount is positive. -func (c Coin) IsPositive() bool { - return c.Amount > 0 -} - -// IsNegative returns true if the coin amount is negative and false otherwise. -func (c Coin) IsNegative() bool { - return c.Amount < 0 -} - -// IsZero returns if this represents no money -func (c Coin) IsZero() bool { - return c.Amount == 0 -} - -func mustMatchDenominations(denomA, denomB string) { - if denomA != denomB { - panic("incompatible coin denominations: " + denomA + ", " + denomB) - } -} - -// Coins is a set of Coin, one per currency -type Coins []Coin - -// NewCoins returns a new set of Coins given one or more Coins -// Consolidates any denom duplicates into one, keeping the properties of a mathematical set -func NewCoins(coins ...Coin) Coins { - coinMap := make(map[string]int64) - - for _, coin := range coins { - coinMap[coin.Denom] = coin.Amount - } - - var setCoins Coins - for denom, amount := range coinMap { - setCoins = append(setCoins, NewCoin(denom, amount)) - } - - return setCoins -} - -// String returns the string representation of Coins -func (cz Coins) String() string { - if len(cz) == 0 { - return "" - } - - res := "" - for i, c := range cz { - if i > 0 { - res += "," - } - res += c.String() - } - - return res -} - -// AmountOf returns the amount of a specific coin from the Coins set -func (cz Coins) AmountOf(denom string) int64 { - for _, c := range cz { - if c.Denom == denom { - return c.Amount - } - } - - return 0 -} - -// Add adds a Coin to the Coins set -func (cz Coins) Add(b Coins) Coins { - c := Coins{} - for _, ac := range cz { - bc := b.AmountOf(ac.Denom) - ac.Amount += bc - c = append(c, ac) - } - - for _, bc := range b { - cc := c.AmountOf(bc.Denom) - if cc == 0 { - c = append(c, bc) - } - } - - return c -} - -// expandNative expands for usage within natively bound functions. -func (cz Coins) expandNative() (denoms []string, amounts []int64) { - denoms = make([]string, len(cz)) - amounts = make([]int64, len(cz)) - for i, coin := range cz { - denoms[i] = coin.Denom - amounts[i] = coin.Amount - } - - return denoms, amounts -} diff --git a/gnovm/stdlibs/stdshim/context.gno b/gnovm/stdlibs/stdshim/context.gno deleted file mode 100644 index 878c963b22b..00000000000 --- a/gnovm/stdlibs/stdshim/context.gno +++ /dev/null @@ -1,4 +0,0 @@ -package std - -// ExecContext is not exposed, -// use native injections std.GetChainID(), std.GetHeight() etc instead. diff --git a/gnovm/stdlibs/stdshim/crypto.gno b/gnovm/stdlibs/stdshim/crypto.gno deleted file mode 100644 index 402a6af3e22..00000000000 --- a/gnovm/stdlibs/stdshim/crypto.gno +++ /dev/null @@ -1,17 +0,0 @@ -package std - -type Address string // NOTE: bech32 - -func (a Address) String() string { - return string(a) -} - -// IsValid checks if the address is valid bech32 encoded string -func (a Address) IsValid() bool { - _, _, ok := DecodeBech32(a) - return ok -} - -const RawAddressSize = 20 - -type RawAddress [RawAddressSize]byte diff --git a/gnovm/stdlibs/stdshim/frame.gno b/gnovm/stdlibs/stdshim/frame.gno deleted file mode 100644 index bc3a000f5a0..00000000000 --- a/gnovm/stdlibs/stdshim/frame.gno +++ /dev/null @@ -1,18 +0,0 @@ -package std - -type Realm struct { - addr Address - pkgPath string -} - -func (r Realm) Addr() Address { - return r.addr -} - -func (r Realm) PkgPath() string { - return r.pkgPath -} - -func (r Realm) IsUser() bool { - return r.pkgPath == "" -} diff --git a/gnovm/stdlibs/time/time.gno b/gnovm/stdlibs/time/time.gno index 521679e48d5..f3395142d1d 100644 --- a/gnovm/stdlibs/time/time.gno +++ b/gnovm/stdlibs/time/time.gno @@ -80,7 +80,6 @@ package time import ( "errors" - // XXX _ "unsafe" // for go:linkname ) // A Time represents an instant in time with nanosecond precision. @@ -1072,8 +1071,6 @@ func daysSinceEpoch(year int) uint64 { func now() (sec int64, nsec int32, mono int64) // injected // runtimeNano returns the current value of the runtime clock in nanoseconds. -// -//go:linkname runtimeNano runtime.nanotime func runtimeNano() int64 { _, _, mono := now() return mono diff --git a/gnovm/tests/file.go b/gnovm/tests/file.go index 852bb74d198..8ab60145bd5 100644 --- a/gnovm/tests/file.go +++ b/gnovm/tests/file.go @@ -263,8 +263,18 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error { 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 { - panic(fmt.Sprintf("fail on %s: got %q, want: %q", path, 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(). @@ -279,12 +289,16 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error { } 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 := fmt.Sprintf( - errstr + - "\n*** CHECK THE ERR MESSAGES ABOVE, MAKE SURE IT'S WHAT YOU EXPECTED, " + - "DELETE THIS LINE AND RUN TEST AGAIN ***", - ) + 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)) } diff --git a/gnovm/tests/files/access0.gno b/gnovm/tests/files/access0_stdlibs.gno similarity index 100% rename from gnovm/tests/files/access0.gno rename to gnovm/tests/files/access0_stdlibs.gno diff --git a/gnovm/tests/files/access1.gno b/gnovm/tests/files/access1_stdlibs.gno similarity index 53% rename from gnovm/tests/files/access1.gno rename to gnovm/tests/files/access1_stdlibs.gno index d9bb9f46203..7a2f2cf3817 100644 --- a/gnovm/tests/files/access1.gno +++ b/gnovm/tests/files/access1_stdlibs.gno @@ -9,4 +9,4 @@ func main() { } // Error: -// main/files/access1.gno:8: cannot access gno.land/p/demo/testutils.testVar2 from main +// main/files/access1_stdlibs.gno:8: cannot access gno.land/p/demo/testutils.testVar2 from main diff --git a/gnovm/tests/files/access2.gno b/gnovm/tests/files/access2_stdlibs.gno similarity index 100% rename from gnovm/tests/files/access2.gno rename to gnovm/tests/files/access2_stdlibs.gno diff --git a/gnovm/tests/files/access3.gno b/gnovm/tests/files/access3_stdlibs.gno similarity index 100% rename from gnovm/tests/files/access3.gno rename to gnovm/tests/files/access3_stdlibs.gno diff --git a/gnovm/tests/files/access4.gno b/gnovm/tests/files/access4_stdlibs.gno similarity index 57% rename from gnovm/tests/files/access4.gno rename to gnovm/tests/files/access4_stdlibs.gno index bb2dcc340f0..6f092efb61c 100644 --- a/gnovm/tests/files/access4.gno +++ b/gnovm/tests/files/access4_stdlibs.gno @@ -10,4 +10,4 @@ func main() { } // Error: -// main/files/access4.gno:9: cannot access gno.land/p/demo/testutils.TestAccessStruct.privateField from main +// main/files/access4_stdlibs.gno:9: cannot access gno.land/p/demo/testutils.TestAccessStruct.privateField from main diff --git a/gnovm/tests/files/access5.gno b/gnovm/tests/files/access5_stdlibs.gno similarity index 100% rename from gnovm/tests/files/access5.gno rename to gnovm/tests/files/access5_stdlibs.gno diff --git a/gnovm/tests/files/access6.gno b/gnovm/tests/files/access6_stdlibs.gno similarity index 67% rename from gnovm/tests/files/access6.gno rename to gnovm/tests/files/access6_stdlibs.gno index 991b4a5457a..b9e019525a7 100644 --- a/gnovm/tests/files/access6.gno +++ b/gnovm/tests/files/access6_stdlibs.gno @@ -16,4 +16,4 @@ func main() { } // Error: -// main/files/access6.gno:15: main.mystruct does not implement gno.land/p/demo/testutils.PrivateInterface +// main/files/access6_stdlibs.gno:15: main.mystruct does not implement gno.land/p/demo/testutils.PrivateInterface diff --git a/gnovm/tests/files/access7.gno b/gnovm/tests/files/access7_stdlibs.gno similarity index 72% rename from gnovm/tests/files/access7.gno rename to gnovm/tests/files/access7_stdlibs.gno index 2b4c32ab818..ab63b3b9639 100644 --- a/gnovm/tests/files/access7.gno +++ b/gnovm/tests/files/access7_stdlibs.gno @@ -20,4 +20,4 @@ func main() { } // Error: -// main/files/access7.gno:19: main.PrivateInterface2 does not implement gno.land/p/demo/testutils.PrivateInterface +// main/files/access7_stdlibs.gno:19: main.PrivateInterface2 does not implement gno.land/p/demo/testutils.PrivateInterface diff --git a/gnovm/tests/files/assign_unnamed_type/method/declaredType6_filetest.gno b/gnovm/tests/files/assign_unnamed_type/method/declaredType6_filetest.gno index 5d994352f35..ba40fb8caa3 100644 --- a/gnovm/tests/files/assign_unnamed_type/method/declaredType6_filetest.gno +++ b/gnovm/tests/files/assign_unnamed_type/method/declaredType6_filetest.gno @@ -17,4 +17,4 @@ func main() { } // Error: -// main/files/assign_unnamed_type/method/declaredType6_filetest.gno:15: cannot use main.c as main.word without explicit conversion +// main/files/assign_unnamed_type/method/declaredType6_filetest.gno:15: cannot use []main.c as []main.word diff --git a/gnovm/tests/files/assign_unnamed_type/method/declaredType6b_filetest.gno b/gnovm/tests/files/assign_unnamed_type/method/declaredType6b_filetest.gno index 88b3203a805..9e46c884252 100644 --- a/gnovm/tests/files/assign_unnamed_type/method/declaredType6b_filetest.gno +++ b/gnovm/tests/files/assign_unnamed_type/method/declaredType6b_filetest.gno @@ -17,4 +17,4 @@ func main() { } // Error: -// main/files/assign_unnamed_type/method/declaredType6b_filetest.gno:15: cannot use uint as main.word without explicit conversion +// main/files/assign_unnamed_type/method/declaredType6b_filetest.gno:15: cannot use []uint as []main.word diff --git a/gnovm/tests/files/assign_unnamed_type/more/cross_realm_compositelit_filetest.gno b/gnovm/tests/files/assign_unnamed_type/more/cross_realm_compositelit_filetest_stdlibs.gno similarity index 100% rename from gnovm/tests/files/assign_unnamed_type/more/cross_realm_compositelit_filetest.gno rename to gnovm/tests/files/assign_unnamed_type/more/cross_realm_compositelit_filetest_stdlibs.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 6549d1824ed..6ef12bd33d4 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 @@ -30,7 +30,7 @@ func main() { // Realm: // switchrealm["gno.land/r/test"] -// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:5]={ +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:6]={ // "Data": null, // "List": [ // { @@ -41,13 +41,13 @@ func main() { // } // ], // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6", // "ModTime": "0", -// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", // "RefCount": "1" // } // } -// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:4]={ +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:5]={ // "Fields": [ // { // "T": { @@ -58,8 +58,8 @@ func main() { // "@type": "/gno.SliceValue", // "Base": { // "@type": "/gno.RefValue", -// "Hash": "9263ebf7e55e2d929e9c190bc4490ee58db148ec", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5" +// "Hash": "e256933ba4dfda233a7edb0902880d554118ba6e", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" // }, // "Length": "1", // "Maxcap": "1", @@ -68,10 +68,29 @@ func main() { // } // ], // "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", +// "RefCount": "1" +// } +// } +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:4]={ +// "ObjectInfo": { // "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", // "ModTime": "0", // "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", // "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/test.Int" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "2b8a024c53e94431e6203115feaf86b36413d7b2", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5" +// } // } // } // u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ @@ -209,19 +228,13 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "3c89d875f7d6daa94113aa4c7e03432ba56202c2", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/test.Int" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "91ebdb8ff6b68e0b93179fae022213185a450649", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" -// } -// } +// "TV": null // } // } // ] diff --git a/gnovm/tests/files/assign_unnamed_type/unnamedtype0b_filetest.gno b/gnovm/tests/files/assign_unnamed_type/unnamedtype0b_filetest.gno index cc1d54cfa2a..8f1eb5399b2 100644 --- a/gnovm/tests/files/assign_unnamed_type/unnamedtype0b_filetest.gno +++ b/gnovm/tests/files/assign_unnamed_type/unnamedtype0b_filetest.gno @@ -14,4 +14,4 @@ func main() { } // Error: -// main/files/assign_unnamed_type/unnamedtype0b_filetest.gno:11: cannot use main.word as int without explicit conversion +// main/files/assign_unnamed_type/unnamedtype0b_filetest.gno:11: cannot use []main.word as []int diff --git a/gnovm/tests/files/bool6.gno b/gnovm/tests/files/bool6.gno index ad4d832036c..3ad789dda32 100644 --- a/gnovm/tests/files/bool6.gno +++ b/gnovm/tests/files/bool6.gno @@ -9,4 +9,4 @@ func X() string { } // Error: -// main/files/bool6.gno:8: operands of boolean operators must evaluate to boolean typed values +// main/files/bool6.gno:8: operator || not defined on: StringKind diff --git a/gnovm/tests/files/comp4.gno b/gnovm/tests/files/comp4.gno new file mode 100644 index 00000000000..501cffeb871 --- /dev/null +++ b/gnovm/tests/files/comp4.gno @@ -0,0 +1,32 @@ +package main + +type S struct { +} + +func main() { + x := (*S)(nil) + println("x == x", x == x) + println("x == nil", x == nil) + println("nil == x", nil == x) + + var y interface{} = (*S)(nil) + println("y == y", y == y) + println("y == nil", y == nil) + println("nil == y", nil == y) + + y = nil + println("y == y", y == y) + println("y == nil", y == nil) + println("nil == y", nil == y) +} + +// Output: +// x == x true +// x == nil true +// nil == x true +// y == y true +// y == nil false +// nil == y false +// y == y true +// y == nil true +// nil == y true diff --git a/gnovm/tests/files/comp5.gno b/gnovm/tests/files/comp5.gno new file mode 100644 index 00000000000..827800f2a67 --- /dev/null +++ b/gnovm/tests/files/comp5.gno @@ -0,0 +1,23 @@ +package main + +type S struct { +} + +func main() { + x := (*S)(nil) + var y interface{} = (*S)(nil) + var znil interface{} = nil + + println("y == znil", y == znil) + println("znil == y", znil == y) + + y = nil + println("y == znil", y == znil) + println("znil == y", znil == y) +} + +// Output: +// y == znil false +// znil == y false +// y == znil true +// znil == y true diff --git a/gnovm/tests/files/for19.gno b/gnovm/tests/files/for19.gno new file mode 100644 index 00000000000..ce02c5fb655 --- /dev/null +++ b/gnovm/tests/files/for19.gno @@ -0,0 +1,25 @@ +package main + +func main() { + v := T + i := 0 + for v { + if i > 2 { + break + } + println(i) + i++ + } +} + +type C bool + +const ( + F C = false + T C = true +) + +// Output: +// 0 +// 1 +// 2 diff --git a/gnovm/tests/files/for7.gno b/gnovm/tests/files/for7.gno index 92ad5f3ce34..0f471956314 100644 --- a/gnovm/tests/files/for7.gno +++ b/gnovm/tests/files/for7.gno @@ -6,4 +6,4 @@ func main() { } // Error: -// main/files/for7.gno:4: cannot use int as bool +// main/files/for7.gno:4: expected typed bool kind, but got IntKind diff --git a/gnovm/tests/files/fun24.gno b/gnovm/tests/files/fun24.gno index 42468ff087e..100d3c26ded 100644 --- a/gnovm/tests/files/fun24.gno +++ b/gnovm/tests/files/fun24.gno @@ -7,4 +7,4 @@ func main() { } // Error: -// main/files/fun24.gno:3: cannot convert StringKind to IntKind +// main/files/fun24.gno:3: cannot use untyped string as IntKind diff --git a/gnovm/tests/files/heap_item_value.gno b/gnovm/tests/files/heap_item_value.gno new file mode 100644 index 00000000000..fe2873f9b60 --- /dev/null +++ b/gnovm/tests/files/heap_item_value.gno @@ -0,0 +1,178 @@ +// PKGPATH: gno.land/r/test +package test + +type S struct { + A int +} + +var a, b *S + +func main() { + a = new(S) + a.A = 4 + b = a +} + +// Realm: +// switchrealm["gno.land/r/test"] +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:5]={ +// "Fields": [ +// { +// "N": "BAAAAAAAAAA=", +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "32" +// } +// } +// ], +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", +// "RefCount": "1" +// } +// } +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:4]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", +// "IsEscaped": true, +// "ModTime": "0", +// "RefCount": "2" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/test.S" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "8c001dde13b1f4dc01fc6d3a5bb4bc0cdfe2a50b", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5" +// } +// } +// } +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ +// "Blank": {}, +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", +// "IsEscaped": true, +// "ModTime": "3", +// "RefCount": "2" +// }, +// "Parent": null, +// "Source": { +// "@type": "/gno.RefNode", +// "BlockNode": null, +// "Location": { +// "File": "", +// "Line": "0", +// "Nonce": "0", +// "PkgPath": "gno.land/r/test" +// } +// }, +// "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/test" +// }, +// "Methods": [], +// "Name": "S", +// "PkgPath": "gno.land/r/test" +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// }, +// "V": { +// "@type": "/gno.FuncValue", +// "Closure": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" +// }, +// "FileName": "main.gno", +// "IsMethod": false, +// "Name": "main", +// "NativeName": "", +// "NativePkg": "", +// "PkgPath": "gno.land/r/test", +// "Source": { +// "@type": "/gno.RefNode", +// "BlockNode": null, +// "Location": { +// "File": "main.gno", +// "Line": "10", +// "Nonce": "0", +// "PkgPath": "gno.land/r/test" +// } +// }, +// "Type": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/test.S" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" +// }, +// "Index": "0", +// "TV": null +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/test.S" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" +// }, +// "Index": "0", +// "TV": null +// } +// } +// ] +// } diff --git a/gnovm/tests/files/heap_item_value_init.gno b/gnovm/tests/files/heap_item_value_init.gno new file mode 100644 index 00000000000..0d1db7bcb76 --- /dev/null +++ b/gnovm/tests/files/heap_item_value_init.gno @@ -0,0 +1,185 @@ +// PKGPATH: gno.land/r/test +package test + +type S struct { + A *int +} + +var a, b *S + +func init() { + a = new(S) + a.A = new(int) + *a.A = 4 +} + +func main() { + b = a +} + +// Realm: +// switchrealm["gno.land/r/test"] +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ +// "Blank": {}, +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", +// "IsEscaped": true, +// "ModTime": "6", +// "RefCount": "2" +// }, +// "Parent": null, +// "Source": { +// "@type": "/gno.RefNode", +// "BlockNode": null, +// "Location": { +// "File": "", +// "Line": "0", +// "Nonce": "0", +// "PkgPath": "gno.land/r/test" +// } +// }, +// "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.PointerType", +// "Elt": { +// "@type": "/gno.PrimitiveType", +// "value": "32" +// } +// } +// } +// ], +// "PkgPath": "gno.land/r/test" +// }, +// "Methods": [], +// "Name": "S", +// "PkgPath": "gno.land/r/test" +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// }, +// "V": { +// "@type": "/gno.FuncValue", +// "Closure": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" +// }, +// "FileName": "main.gno", +// "IsMethod": false, +// "Name": "init.1", +// "NativeName": "", +// "NativePkg": "", +// "PkgPath": "gno.land/r/test", +// "Source": { +// "@type": "/gno.RefNode", +// "BlockNode": null, +// "Location": { +// "File": "main.gno", +// "Line": "10", +// "Nonce": "0", +// "PkgPath": "gno.land/r/test" +// } +// }, +// "Type": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// }, +// "V": { +// "@type": "/gno.FuncValue", +// "Closure": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" +// }, +// "FileName": "main.gno", +// "IsMethod": false, +// "Name": "main", +// "NativeName": "", +// "NativePkg": "", +// "PkgPath": "gno.land/r/test", +// "Source": { +// "@type": "/gno.RefNode", +// "BlockNode": null, +// "Location": { +// "File": "main.gno", +// "Line": "16", +// "Nonce": "0", +// "PkgPath": "gno.land/r/test" +// } +// }, +// "Type": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/test.S" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" +// }, +// "Index": "0", +// "TV": null +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/test.S" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" +// }, +// "Index": "0", +// "TV": null +// } +// } +// ] +// } diff --git a/gnovm/tests/files/if2.gno b/gnovm/tests/files/if2.gno index 88261c520c9..bc417a92fe7 100644 --- a/gnovm/tests/files/if2.gno +++ b/gnovm/tests/files/if2.gno @@ -10,4 +10,4 @@ func main() { } // Error: -// main/files/if2.gno:7: cannot use int as bool +// main/files/if2.gno:7: expected typed bool kind, but got IntKind diff --git a/gnovm/tests/files/if8.gno b/gnovm/tests/files/if8.gno new file mode 100644 index 00000000000..c013896d9d3 --- /dev/null +++ b/gnovm/tests/files/if8.gno @@ -0,0 +1,18 @@ +package main + +func main() { + v := T + if v { + println("true") + } +} + +type C bool + +const ( + F C = false + T C = true +) + +// Output: +// true diff --git a/gnovm/tests/files/issue_1326.gno b/gnovm/tests/files/issue_1326.gno new file mode 100644 index 00000000000..a86058a63e0 --- /dev/null +++ b/gnovm/tests/files/issue_1326.gno @@ -0,0 +1,64 @@ +// PKGPATH: gno.land/r/test +package test + +import ( + "strconv" +) + +func init() { + New() + println(Delta()) +} + +func main() { + println(Delta()) +} + +type Move struct { + N1, N2, N3 byte +} + +type S struct { + Moves []Move +} + +func (s S) clone() S { + mv := s.Moves + return S{Moves: mv} +} + +func (olds S) change() S { + s := olds.clone() + + counter++ + s.Moves = append([]Move{}, s.Moves...) + s.Moves = append(s.Moves, Move{counter, counter, counter}) + return s +} + +var ( + el *S + counter byte +) + +func New() { + el = new(S) +} + +func Delta() string { + n := el.change() + *el = n + return Values() +} + +func Values() string { + s := "" + for _, val := range el.Moves { + s += strconv.Itoa(int(val.N1)) + "," + strconv.Itoa(int(val.N2)) + "," + strconv.Itoa(int(val.N3)) + ";" + } + return s +} + +// Output: +// 1,1,1; +// 1,1,1;2,2,2; diff --git a/gnovm/tests/files/issue_558b.gno b/gnovm/tests/files/issue_558b_stdlibs.gno similarity index 100% rename from gnovm/tests/files/issue_558b.gno rename to gnovm/tests/files/issue_558b_stdlibs.gno diff --git a/gnovm/tests/files/math_native.gno b/gnovm/tests/files/math_native.gno new file mode 100644 index 00000000000..54bddcde1f3 --- /dev/null +++ b/gnovm/tests/files/math_native.gno @@ -0,0 +1,14 @@ +package main + +import ( + "fmt" + "math" +) + +func main() { + var a float64 = 2.0 + fmt.Println(math.Pi * a) +} + +// Output: +// 6.283185307179586 diff --git a/gnovm/tests/files/op7.gno b/gnovm/tests/files/op7.gno index 7167171035b..0c3f540600b 100644 --- a/gnovm/tests/files/op7.gno +++ b/gnovm/tests/files/op7.gno @@ -14,4 +14,4 @@ func main() { } // Error: -// main/files/op7.gno:11: incompatible types in binary expression: .uverse.error GTR main.T +// main/files/op7.gno:11: operator > not defined on: InterfaceKind diff --git a/gnovm/tests/files/star_assign.gno b/gnovm/tests/files/star_assign.gno new file mode 100644 index 00000000000..e40fe2794ac --- /dev/null +++ b/gnovm/tests/files/star_assign.gno @@ -0,0 +1,54 @@ +// PKGPATH: gno.land/r/test +package test + +import ( + "gno.land/p/demo/ufmt" +) + +type A struct { + nums []int +} + +var ( + intPtr *int + strPtr *string + aPtr *A + concretePtr *int + concreteValue int +) + +func init() { + New() +} + +func main() { + Delta() + println(Values()) +} + +func New() { + intPtr = new(int) + strPtr = new(string) + aPtr = &A{} + concretePtr = &concreteValue +} + +func Delta() { + *intPtr++ + *strPtr += "hello" + *aPtr = A{nums: []int{8, 5, 8}} + *concretePtr = 100 +} + +func Values() string { + var results string + results += ufmt.Sprintf("%d, %s, %d, %d", *intPtr, *strPtr, *concretePtr, concreteValue) + for _, n := range aPtr.nums { + results += ufmt.Sprintf(", %d", n) + } + + return results +} + +// Output: +// 1, hello, 100, 100, 8, 5, 8 diff --git a/gnovm/tests/files/std0.gno b/gnovm/tests/files/std0_stdlibs.gno similarity index 100% rename from gnovm/tests/files/std0.gno rename to gnovm/tests/files/std0_stdlibs.gno diff --git a/gnovm/tests/files/std10.gno b/gnovm/tests/files/std10_stdlibs.gno similarity index 100% rename from gnovm/tests/files/std10.gno rename to gnovm/tests/files/std10_stdlibs.gno diff --git a/gnovm/tests/files/std11.gno b/gnovm/tests/files/std11_stdlibs.gno similarity index 100% rename from gnovm/tests/files/std11.gno rename to gnovm/tests/files/std11_stdlibs.gno diff --git a/gnovm/tests/files/std2.gno b/gnovm/tests/files/std2_stdlibs.gno similarity index 100% rename from gnovm/tests/files/std2.gno rename to gnovm/tests/files/std2_stdlibs.gno diff --git a/gnovm/tests/files/std3.gno b/gnovm/tests/files/std3_stdlibs.gno similarity index 100% rename from gnovm/tests/files/std3.gno rename to gnovm/tests/files/std3_stdlibs.gno diff --git a/gnovm/tests/files/std4.gno b/gnovm/tests/files/std4_stdlibs.gno similarity index 100% rename from gnovm/tests/files/std4.gno rename to gnovm/tests/files/std4_stdlibs.gno diff --git a/gnovm/tests/files/std5.gno b/gnovm/tests/files/std5_stdlibs.gno similarity index 100% rename from gnovm/tests/files/std5.gno rename to gnovm/tests/files/std5_stdlibs.gno diff --git a/gnovm/tests/files/std6.gno b/gnovm/tests/files/std6_stdlibs.gno similarity index 100% rename from gnovm/tests/files/std6.gno rename to gnovm/tests/files/std6_stdlibs.gno diff --git a/gnovm/tests/files/std7.gno b/gnovm/tests/files/std7_stdlibs.gno similarity index 100% rename from gnovm/tests/files/std7.gno rename to gnovm/tests/files/std7_stdlibs.gno diff --git a/gnovm/tests/files/std8.gno b/gnovm/tests/files/std8_stdlibs.gno similarity index 100% rename from gnovm/tests/files/std8.gno rename to gnovm/tests/files/std8_stdlibs.gno diff --git a/gnovm/tests/files/std9.gno b/gnovm/tests/files/std9_stdlibs.gno similarity index 100% rename from gnovm/tests/files/std9.gno rename to gnovm/tests/files/std9_stdlibs.gno diff --git a/gnovm/tests/files/time16_native.gno b/gnovm/tests/files/time16_native.gno index 1789fcbef4f..03eca47172f 100644 --- a/gnovm/tests/files/time16_native.gno +++ b/gnovm/tests/files/time16_native.gno @@ -11,4 +11,4 @@ func main() { } // Error: -// main/files/time16_native.gno:10: incompatible types in binary expression: go:time.Duration MUL int64 +// main/files/time16_native.gno:10: incompatible operands in binary expression: go:time.Duration MUL int64 diff --git a/gnovm/tests/files/type31.gno b/gnovm/tests/files/type31.gno index a613c67525c..68c92e9a504 100644 --- a/gnovm/tests/files/type31.gno +++ b/gnovm/tests/files/type31.gno @@ -9,4 +9,4 @@ func main() { } // Error: -// main/files/type31.gno:8: incompatible types in binary expression: string ADD main.String +// main/files/type31.gno:8: invalid operation: mismatched types string and main.String diff --git a/gnovm/tests/files/type32.gno b/gnovm/tests/files/type32.gno index bc943d90186..9565cdba245 100644 --- a/gnovm/tests/files/type32.gno +++ b/gnovm/tests/files/type32.gno @@ -12,4 +12,4 @@ func main() { } // Error: -// main/files/type32.gno:9#1: incompatible types in binary expression: string ADD main.S +// main/files/type32.gno:9#1: invalid operation: mismatched types string and main.S diff --git a/gnovm/tests/files/types/add_a0.gno b/gnovm/tests/files/types/add_a0.gno new file mode 100644 index 00000000000..9b8e08718b7 --- /dev/null +++ b/gnovm/tests/files/types/add_a0.gno @@ -0,0 +1,9 @@ +package main + +// both typed(different) const +func main() { + println(int(1) + int8(1)) +} + +// Error: +// main/files/types/add_a0.gno:5: invalid operation: mismatched types int and int8 diff --git a/gnovm/tests/files/types/add_a1.gno b/gnovm/tests/files/types/add_a1.gno new file mode 100644 index 00000000000..f5bcec883b2 --- /dev/null +++ b/gnovm/tests/files/types/add_a1.gno @@ -0,0 +1,25 @@ +package main + +import ( + "strconv" +) + +type Error1 int64 + +func (e Error1) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +type Error2 int + +func (e Error2) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +// both typed(different) const +func main() { + println(Error1(0) + Error2(0)) +} + +// Error: +// main/files/types/add_a1.gno:21: invalid operation: mismatched types main.Error1 and main.Error2 diff --git a/gnovm/tests/files/types/add_assign_a0.gno b/gnovm/tests/files/types/add_assign_a0.gno new file mode 100644 index 00000000000..181941d1ea9 --- /dev/null +++ b/gnovm/tests/files/types/add_assign_a0.gno @@ -0,0 +1,9 @@ +package main + +// both typed(different) const +func main() { + int(0) += int8(1) +} + +// Error: +// main/files/types/add_assign_a0.gno:5: invalid operation: mismatched types int and int8 diff --git a/gnovm/tests/files/types/add_assign_a1.gno b/gnovm/tests/files/types/add_assign_a1.gno new file mode 100644 index 00000000000..f169e58d5b1 --- /dev/null +++ b/gnovm/tests/files/types/add_assign_a1.gno @@ -0,0 +1,25 @@ +package main + +import ( + "strconv" +) + +type Error1 int64 + +func (e Error1) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +type Error2 int + +func (e Error2) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +// both typed(different) const +func main() { + Error1(0) += Error2(0) +} + +// Error: +// main/files/types/add_assign_a1.gno:21: invalid operation: mismatched types main.Error1 and main.Error2 diff --git a/gnovm/tests/files/types/add_assign_a_01.gno b/gnovm/tests/files/types/add_assign_a_01.gno new file mode 100644 index 00000000000..f09c7ed7344 --- /dev/null +++ b/gnovm/tests/files/types/add_assign_a_01.gno @@ -0,0 +1,12 @@ +package main + +func main() { + a := 1 + b := 1 + a, b += 1, 1 + println(a) + println(b) +} + +// Error: +// main/files/types/add_assign_a_01.gno:6: assignment operator += requires only one expression on lhs and rhs diff --git a/gnovm/tests/files/types/add_assign_b0.gno b/gnovm/tests/files/types/add_assign_b0.gno new file mode 100644 index 00000000000..28b6af83d3a --- /dev/null +++ b/gnovm/tests/files/types/add_assign_b0.gno @@ -0,0 +1,13 @@ +package main + +type Error int8 + +// one untyped const, one typed const +func main() { + r := 1 + r += Error(1) + println(r) +} + +// Error: +// main/files/types/add_assign_b0.gno:8: invalid operation: mismatched types int and main.Error diff --git a/gnovm/tests/files/types/add_assign_b1.gno b/gnovm/tests/files/types/add_assign_b1.gno new file mode 100644 index 00000000000..2e702256f29 --- /dev/null +++ b/gnovm/tests/files/types/add_assign_b1.gno @@ -0,0 +1,13 @@ +package main + +type Error int8 + +// one untyped const, one typed const +func main() { + r := Error(1) + r += 1 + println(r) +} + +// Output: +// (2 main.Error) diff --git a/gnovm/tests/files/types/add_assign_b2.gno b/gnovm/tests/files/types/add_assign_b2.gno new file mode 100644 index 00000000000..87b09754046 --- /dev/null +++ b/gnovm/tests/files/types/add_assign_b2.gno @@ -0,0 +1,10 @@ +package main + +func main() { + r := 1 + r += 'a' + println(r) +} + +// Output: +// 98 diff --git a/gnovm/tests/files/types/add_assign_d0.gno b/gnovm/tests/files/types/add_assign_d0.gno new file mode 100644 index 00000000000..f3da2497a84 --- /dev/null +++ b/gnovm/tests/files/types/add_assign_d0.gno @@ -0,0 +1,12 @@ +package main + +// both untyped const +// TODO: dec value representation, and this should happen in process stage!!! +func main() { + r := 1.0 + r += 1 + println(r) +} + +// Output: +// 2 diff --git a/gnovm/tests/files/types/add_assign_e.gno b/gnovm/tests/files/types/add_assign_e.gno new file mode 100644 index 00000000000..3a72f9711be --- /dev/null +++ b/gnovm/tests/files/types/add_assign_e.gno @@ -0,0 +1,21 @@ +package main + +import ( + "strconv" +) + +type Error1 int64 + +func (e Error1) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +func main() { + var e1 Error1 = Error1(0) + var e2 int64 = 1 + e1 += Error1(e2) + println(e1) +} + +// Output: +// error: 1 diff --git a/gnovm/tests/files/types/add_assign_e0.gno b/gnovm/tests/files/types/add_assign_e0.gno new file mode 100644 index 00000000000..d71bedab118 --- /dev/null +++ b/gnovm/tests/files/types/add_assign_e0.gno @@ -0,0 +1,28 @@ +package main + +import ( + "strconv" +) + +type Error1 int64 + +func (e Error1) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +type Error2 int + +func (e Error2) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +// both not const, typed +func main() { + var e1 Error1 = Error1(0) + var e2 Error2 = Error2(0) + e1 += e2 + println(e1) +} + +// Error: +// main/files/types/add_assign_e0.gno:23: invalid operation: mismatched types main.Error1 and main.Error2 diff --git a/gnovm/tests/files/types/add_assign_e1.gno b/gnovm/tests/files/types/add_assign_e1.gno new file mode 100644 index 00000000000..3657cf2872d --- /dev/null +++ b/gnovm/tests/files/types/add_assign_e1.gno @@ -0,0 +1,20 @@ +package main + +import ( + "strconv" +) + +type Error1 int64 + +func (e Error1) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +func main() { + var e1 Error1 = Error1(0) + e1 += 1 + println(e1) +} + +// Output: +// error: 1 diff --git a/gnovm/tests/files/types/add_assign_f0_stdlibs.gno b/gnovm/tests/files/types/add_assign_f0_stdlibs.gno new file mode 100644 index 00000000000..b91fa58c30d --- /dev/null +++ b/gnovm/tests/files/types/add_assign_f0_stdlibs.gno @@ -0,0 +1,25 @@ +package main + +import ( + "errors" + "strconv" +) + +type Error int64 + +func (e Error) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +var errCmp = errors.New("XXXX") + +// specil case: +// one is interface +func main() { + r := 1 + r += errCmp + println(r) +} + +// Error: +// main/files/types/add_assign_f0_stdlibs.gno:20: 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_stdlibs.gno new file mode 100644 index 00000000000..090f8f51f89 --- /dev/null +++ b/gnovm/tests/files/types/add_assign_f1_stdlibs.gno @@ -0,0 +1,28 @@ +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: +// 1. base type of left is int64, op is legal; +// 2. while RHS is interface kind, and can be converted to left +func main() { + r := Error(0) + r += errCmp // in case of this, should panic mismatch on operand, except RHS is untyped + //println(r) + // println(Error(0) == errCmp) // Note: this is different with += + +} + +// Error: +// main/files/types/add_assign_f1_stdlibs.gno:21: 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_stdlibs.gno new file mode 100644 index 00000000000..5cfd351f574 --- /dev/null +++ b/gnovm/tests/files/types/add_assign_f2_stdlibs.gno @@ -0,0 +1,25 @@ +package main + +import ( + "errors" + "strconv" +) + +type Error int64 + +func (e Error) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +var errCmp = errors.New("XXXX") + +// specil case: +// one is interface +func main() { + r := Error(0) + errCmp += r + println(errCmp) +} + +// Error: +// main/files/types/add_assign_f2_stdlibs.gno:20: operator += not defined on: InterfaceKind diff --git a/gnovm/tests/files/types/add_assign_f3.gno b/gnovm/tests/files/types/add_assign_f3.gno new file mode 100644 index 00000000000..7a48c584dff --- /dev/null +++ b/gnovm/tests/files/types/add_assign_f3.gno @@ -0,0 +1,32 @@ +package main + +import ( + "strconv" +) + +type E interface { + Error() string +} + +type Error1 int64 + +func (e Error1) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +type Error2 int + +func (e Error2) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +// both not const, and both interface +func main() { + var e1 E = Error1(0) + var e2 E = Error2(0) + e1 += e2 + println(e1) +} + +// Error: +// main/files/types/add_assign_f3.gno:27: operator += not defined on: InterfaceKind diff --git a/gnovm/tests/files/types/add_assign_f4.gno b/gnovm/tests/files/types/add_assign_f4.gno new file mode 100644 index 00000000000..bb4083e8935 --- /dev/null +++ b/gnovm/tests/files/types/add_assign_f4.gno @@ -0,0 +1,10 @@ +package main + +func main() { + a := 1 + a += int(1) + println(a) +} + +// Output: +// 2 diff --git a/gnovm/tests/files/types/add_b0.gno b/gnovm/tests/files/types/add_b0.gno new file mode 100644 index 00000000000..3790f9d7134 --- /dev/null +++ b/gnovm/tests/files/types/add_b0.gno @@ -0,0 +1,13 @@ +package main + +type Error int8 + +// one untyped const, one typed const +func main() { + println(1 + Error(1)) + println(Error(1) + 1) +} + +// Output: +// (2 main.Error) +// (2 main.Error) diff --git a/gnovm/tests/files/types/add_b1.gno b/gnovm/tests/files/types/add_b1.gno new file mode 100644 index 00000000000..854ecaa9770 --- /dev/null +++ b/gnovm/tests/files/types/add_b1.gno @@ -0,0 +1,21 @@ +package main + +import ( + "strconv" +) + +type Error int8 + +func (e Error) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +// one untyped const, one typed const +func main() { + println(1 + Error(1)) + println(Error(1) + 1) +} + +// Output: +// error: 2 +// error: 2 diff --git a/gnovm/tests/files/types/add_b2.gno b/gnovm/tests/files/types/add_b2.gno new file mode 100644 index 00000000000..b6463134da4 --- /dev/null +++ b/gnovm/tests/files/types/add_b2.gno @@ -0,0 +1,9 @@ +package main + +// one untyped const, one typed const +func main() { + println(1 + "a") +} + +// Error: +// main/files/types/add_b2.gno:5: cannot use untyped Bigint as StringKind diff --git a/gnovm/tests/files/types/add_b3.gno b/gnovm/tests/files/types/add_b3.gno new file mode 100644 index 00000000000..c8a94c5b6ab --- /dev/null +++ b/gnovm/tests/files/types/add_b3.gno @@ -0,0 +1,9 @@ +package main + +// one untyped const, one typed const +func main() { + println("b" + "a") +} + +// Output: +// ba diff --git a/gnovm/tests/files/types/add_d0.gno b/gnovm/tests/files/types/add_d0.gno new file mode 100644 index 00000000000..3f0d9548fff --- /dev/null +++ b/gnovm/tests/files/types/add_d0.gno @@ -0,0 +1,13 @@ +package main + +// both untyped const +// untyped bigint to untyped bigdec +// TODO: dec value representation +func main() { + println(1.0 + 1) + println(1.0 + 0) +} + +// Output: +// 2 +// 1 diff --git a/gnovm/tests/files/types/add_d1.gno b/gnovm/tests/files/types/add_d1.gno new file mode 100644 index 00000000000..7bf7c26565a --- /dev/null +++ b/gnovm/tests/files/types/add_d1.gno @@ -0,0 +1,10 @@ +package main + +// both untyped const +// TODO: dec value representation +func main() { + println('a' + 'b') +} + +// Output: +// 195 diff --git a/gnovm/tests/files/types/add_d2.gno b/gnovm/tests/files/types/add_d2.gno new file mode 100644 index 00000000000..7ea4924a594 --- /dev/null +++ b/gnovm/tests/files/types/add_d2.gno @@ -0,0 +1,13 @@ +package main + +// both untyped const +// TODO: dec value representation +var r rune + +func main() { + r = 'a' + println(r + 'b') +} + +// Output: +// 195 diff --git a/gnovm/tests/files/types/add_d3.gno b/gnovm/tests/files/types/add_d3.gno new file mode 100644 index 00000000000..b130fc98b66 --- /dev/null +++ b/gnovm/tests/files/types/add_d3.gno @@ -0,0 +1,15 @@ +package main + +// both untyped const +// TODO: dec value representation +var r1 rune +var r2 rune + +func main() { + r1 = 'a' + r2 = 'b' + println(r1 + r2) +} + +// Output: +// 195 diff --git a/gnovm/tests/files/types/add_d4.gno b/gnovm/tests/files/types/add_d4.gno new file mode 100644 index 00000000000..bdfca69ea88 --- /dev/null +++ b/gnovm/tests/files/types/add_d4.gno @@ -0,0 +1,11 @@ +package main + +var a int +var b interface{} + +func main() { + println(b + a) +} + +// Error: +// main/files/types/add_d4.gno:7: operator + not defined on: InterfaceKind diff --git a/gnovm/tests/files/types/add_e0.gno b/gnovm/tests/files/types/add_e0.gno new file mode 100644 index 00000000000..b7fc8e395be --- /dev/null +++ b/gnovm/tests/files/types/add_e0.gno @@ -0,0 +1,27 @@ +package main + +import ( + "strconv" +) + +type Error1 int64 + +func (e Error1) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +type Error2 int + +func (e Error2) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +// both not const, typed +func main() { + var e1 Error1 = Error1(0) + var e2 Error2 = Error2(0) + println(e1 + e2) +} + +// Error: +// main/files/types/add_e0.gno:23: invalid operation: mismatched types main.Error1 and main.Error2 diff --git a/gnovm/tests/files/types/add_f0_stdlibs.gno b/gnovm/tests/files/types/add_f0_stdlibs.gno new file mode 100644 index 00000000000..cf16d0ff084 --- /dev/null +++ b/gnovm/tests/files/types/add_f0_stdlibs.gno @@ -0,0 +1,23 @@ +package main + +import ( + "errors" + "strconv" +) + +type Error int64 + +func (e Error) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +var errCmp = errors.New("XXXX") + +// specil case: +// one is interface +func main() { + println(1 + errCmp) +} + +// Error: +// main/files/types/add_f0_stdlibs.gno:19: operator + not defined on: InterfaceKind diff --git a/gnovm/tests/files/types/add_f1_stdlibs.gno b/gnovm/tests/files/types/add_f1_stdlibs.gno new file mode 100644 index 00000000000..6bf1f814ac5 --- /dev/null +++ b/gnovm/tests/files/types/add_f1_stdlibs.gno @@ -0,0 +1,23 @@ +package main + +import ( + "errors" + "strconv" +) + +type Error int64 + +func (e Error) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +var errCmp = errors.New("XXXX") + +// specil case: +// one is interface +func main() { + println(Error(0) + errCmp) +} + +// Error: +// main/files/types/add_f1_stdlibs.gno:19: operator + not defined on: InterfaceKind diff --git a/gnovm/tests/files/types/add_f2.gno b/gnovm/tests/files/types/add_f2.gno new file mode 100644 index 00000000000..7adfcfdaca6 --- /dev/null +++ b/gnovm/tests/files/types/add_f2.gno @@ -0,0 +1,31 @@ +package main + +import ( + "strconv" +) + +type E interface { + Error() string +} + +type Error1 int64 + +func (e Error1) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +type Error2 int + +func (e Error2) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +// both not const, and both interface +func main() { + var e1 E = Error1(0) + var e2 E = Error2(0) + println(e1 + e2) +} + +// Error: +// main/files/types/add_f2.gno:27: operator + not defined on: InterfaceKind diff --git a/gnovm/tests/files/types/and_a0.gno b/gnovm/tests/files/types/and_a0.gno new file mode 100644 index 00000000000..b1d23b23c91 --- /dev/null +++ b/gnovm/tests/files/types/and_a0.gno @@ -0,0 +1,9 @@ +package main + +// both typed(different) const +func main() { + println(int(0) & int8(1)) +} + +// Error: +// main/files/types/and_a0.gno:5: invalid operation: mismatched types int and int8 diff --git a/gnovm/tests/files/types/and_a1.gno b/gnovm/tests/files/types/and_a1.gno new file mode 100644 index 00000000000..d2fceec80ef --- /dev/null +++ b/gnovm/tests/files/types/and_a1.gno @@ -0,0 +1,25 @@ +package main + +import ( + "strconv" +) + +type Error1 int64 + +func (e Error1) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +type Error2 int + +func (e Error2) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +// both typed(different) const +func main() { + println(Error1(0) & Error2(0)) +} + +// Error: +// main/files/types/and_a1.gno:21: invalid operation: mismatched types main.Error1 and main.Error2 diff --git a/gnovm/tests/files/types/and_b0.gno b/gnovm/tests/files/types/and_b0.gno new file mode 100644 index 00000000000..525796320e6 --- /dev/null +++ b/gnovm/tests/files/types/and_b0.gno @@ -0,0 +1,13 @@ +package main + +type Error int8 + +// one untyped const, one typed const +func main() { + println(1 & Error(1)) + println(Error(1) & 1) +} + +// Output: +// (1 main.Error) +// (1 main.Error) diff --git a/gnovm/tests/files/types/and_b1.gno b/gnovm/tests/files/types/and_b1.gno new file mode 100644 index 00000000000..dc21fc3956b --- /dev/null +++ b/gnovm/tests/files/types/and_b1.gno @@ -0,0 +1,21 @@ +package main + +import ( + "strconv" +) + +type Error int8 + +func (e Error) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +// one untyped const, one typed const +func main() { + println(1 & Error(1)) + println(Error(1) & 1) +} + +// Output: +// error: 1 +// error: 1 diff --git a/gnovm/tests/files/types/and_b2.gno b/gnovm/tests/files/types/and_b2.gno new file mode 100644 index 00000000000..8ff36aaa26b --- /dev/null +++ b/gnovm/tests/files/types/and_b2.gno @@ -0,0 +1,9 @@ +package main + +// one untyped const, one typed const +func main() { + println(1 & "a") +} + +// Error: +// main/files/types/and_b2.gno:5: operator & not defined on: StringKind diff --git a/gnovm/tests/files/types/and_b3.gno b/gnovm/tests/files/types/and_b3.gno new file mode 100644 index 00000000000..69be88d2dc9 --- /dev/null +++ b/gnovm/tests/files/types/and_b3.gno @@ -0,0 +1,9 @@ +package main + +// one untyped const, one typed const +func main() { + println("b" & "a") +} + +// Error: +// main/files/types/and_b3.gno:5: operator & not defined on: StringKind diff --git a/gnovm/tests/files/types/and_b4.gno b/gnovm/tests/files/types/and_b4.gno new file mode 100644 index 00000000000..725714eeafd --- /dev/null +++ b/gnovm/tests/files/types/and_b4.gno @@ -0,0 +1,9 @@ +package main + +// one untyped const, one typed const +func main() { + println(1 & 'a') +} + +// Output: +// 1 diff --git a/gnovm/tests/files/types/and_d0.gno b/gnovm/tests/files/types/and_d0.gno new file mode 100644 index 00000000000..90646e97bc9 --- /dev/null +++ b/gnovm/tests/files/types/and_d0.gno @@ -0,0 +1,10 @@ +package main + +// both untyped const +// TODO: dec value representation, and this should happen in process stage!!! +func main() { + println(1.0 & 1) +} + +// Error: +// main/files/types/and_d0.gno:6: operator & not defined on: BigdecKind diff --git a/gnovm/tests/files/types/and_d1.gno b/gnovm/tests/files/types/and_d1.gno new file mode 100644 index 00000000000..232fff4d385 --- /dev/null +++ b/gnovm/tests/files/types/and_d1.gno @@ -0,0 +1,10 @@ +package main + +// both untyped const +// TODO: dec value representation +func main() { + println('a' & 'b') +} + +// Output: +// 96 diff --git a/gnovm/tests/files/types/and_d2.gno b/gnovm/tests/files/types/and_d2.gno new file mode 100644 index 00000000000..f5c2660bafc --- /dev/null +++ b/gnovm/tests/files/types/and_d2.gno @@ -0,0 +1,13 @@ +package main + +// both untyped const +// TODO: dec value representation +var r rune + +func main() { + r = 'a' + println(r & 'b') +} + +// Output: +// 96 diff --git a/gnovm/tests/files/types/and_d3.gno b/gnovm/tests/files/types/and_d3.gno new file mode 100644 index 00000000000..f5cb8075f24 --- /dev/null +++ b/gnovm/tests/files/types/and_d3.gno @@ -0,0 +1,15 @@ +package main + +// both untyped const +// TODO: dec value representation +var r1 rune +var r2 rune + +func main() { + r1 = 'a' + r2 = 'b' + println(r1 & r2) +} + +// Output: +// 96 diff --git a/gnovm/tests/files/types/and_d4.gno b/gnovm/tests/files/types/and_d4.gno new file mode 100644 index 00000000000..b4f8a70ad3c --- /dev/null +++ b/gnovm/tests/files/types/and_d4.gno @@ -0,0 +1,10 @@ +package main + +// both untyped const +// TODO: dec value representation +func main() { + println(1.0 & 0) +} + +// Error: +// main/files/types/and_d4.gno:6: operator & not defined on: BigdecKind diff --git a/gnovm/tests/files/types/and_e0.gno b/gnovm/tests/files/types/and_e0.gno new file mode 100644 index 00000000000..28d58acf42b --- /dev/null +++ b/gnovm/tests/files/types/and_e0.gno @@ -0,0 +1,27 @@ +package main + +import ( + "strconv" +) + +type Error1 int64 + +func (e Error1) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +type Error2 int + +func (e Error2) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +// both not const, typed +func main() { + var e1 Error1 = Error1(0) + var e2 Error2 = Error2(0) + println(e1 & e2) +} + +// Error: +// main/files/types/and_e0.gno:23: invalid operation: mismatched types main.Error1 and main.Error2 diff --git a/gnovm/tests/files/types/and_f0_stdlibs.gno b/gnovm/tests/files/types/and_f0_stdlibs.gno new file mode 100644 index 00000000000..6b56a8e59c4 --- /dev/null +++ b/gnovm/tests/files/types/and_f0_stdlibs.gno @@ -0,0 +1,23 @@ +package main + +import ( + "errors" + "strconv" +) + +type Error int64 + +func (e Error) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +var errCmp = errors.New("XXXX") + +// specil case: +// one is interface +func main() { + println(1 & errCmp) +} + +// Error: +// main/files/types/and_f0_stdlibs.gno:19: operator & not defined on: InterfaceKind diff --git a/gnovm/tests/files/types/and_f1_stdlibs.gno b/gnovm/tests/files/types/and_f1_stdlibs.gno new file mode 100644 index 00000000000..f1d7589e2f6 --- /dev/null +++ b/gnovm/tests/files/types/and_f1_stdlibs.gno @@ -0,0 +1,23 @@ +package main + +import ( + "errors" + "strconv" +) + +type Error int64 + +func (e Error) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +var errCmp = errors.New("XXXX") + +// specil case: +// one is interface +func main() { + println(Error(0) & errCmp) +} + +// Error: +// main/files/types/and_f1_stdlibs.gno:19: operator & not defined on: InterfaceKind diff --git a/gnovm/tests/files/types/and_f2.gno b/gnovm/tests/files/types/and_f2.gno new file mode 100644 index 00000000000..24368256822 --- /dev/null +++ b/gnovm/tests/files/types/and_f2.gno @@ -0,0 +1,31 @@ +package main + +import ( + "strconv" +) + +type E interface { + Error() string +} + +type Error1 int64 + +func (e Error1) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +type Error2 int + +func (e Error2) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +// both not const, and both interface +func main() { + var e1 E = Error1(0) + var e2 E = Error2(0) + println(e1 & e2) +} + +// Error: +// main/files/types/and_f2.gno:27: operator & not defined on: InterfaceKind diff --git a/gnovm/tests/files/types/assign_call.gno b/gnovm/tests/files/types/assign_call.gno new file mode 100644 index 00000000000..a94b4a0b126 --- /dev/null +++ b/gnovm/tests/files/types/assign_call.gno @@ -0,0 +1,14 @@ +package main + +func foo() (int, float32) { + return 1, 1.0 +} + +func main() { + var s, s1 string + s, s1 = foo() + println(s) +} + +// Error: +// main/files/types/assign_call.gno:9: cannot use int as string diff --git a/gnovm/tests/files/types/assign_index.gno b/gnovm/tests/files/types/assign_index.gno new file mode 100644 index 00000000000..93f4cf0a3c1 --- /dev/null +++ b/gnovm/tests/files/types/assign_index.gno @@ -0,0 +1,14 @@ +package main + +func main() { + m := map[string]int{"a": 1, "b": 2, "c": 3} + + var s string + var ok bool + s, ok = m["a"] + println(s) + println(ok) +} + +// Error: +// main/files/types/assign_index.gno:8: cannot use int as string diff --git a/gnovm/tests/files/types/assign_index_a.gno b/gnovm/tests/files/types/assign_index_a.gno new file mode 100644 index 00000000000..dd92220b1fe --- /dev/null +++ b/gnovm/tests/files/types/assign_index_a.gno @@ -0,0 +1,12 @@ +package main + +func main() { + var s string + var ok bool + s, ok = map[string]int{"a": 1, "b": 2, "c": 3}["a"] + println(s) + println(ok) +} + +// Error: +// main/files/types/assign_index_a.gno:6: cannot use int as string diff --git a/gnovm/tests/files/types/assign_index_b.gno b/gnovm/tests/files/types/assign_index_b.gno new file mode 100644 index 00000000000..d8900759cb2 --- /dev/null +++ b/gnovm/tests/files/types/assign_index_b.gno @@ -0,0 +1,14 @@ +package main + +func main() { + m := map[string]int{"a": 1, "b": 2, "c": 3} + + var s int + var ok int + s, ok = m["a"] + println(s) + println(ok) +} + +// Error: +// main/files/types/assign_index_b.gno:8: want bool type got int diff --git a/gnovm/tests/files/types/assign_index_c.gno b/gnovm/tests/files/types/assign_index_c.gno new file mode 100644 index 00000000000..592191189a2 --- /dev/null +++ b/gnovm/tests/files/types/assign_index_c.gno @@ -0,0 +1,12 @@ +package main + +func main() { + s := []int{1, 2, 3} + + var s1 string + s1 = s[0] + println(s) +} + +// Error: +// main/files/types/assign_index_c.gno:7: cannot use int as string diff --git a/gnovm/tests/files/types/assign_literal.gno b/gnovm/tests/files/types/assign_literal.gno new file mode 100644 index 00000000000..61c128fbe1f --- /dev/null +++ b/gnovm/tests/files/types/assign_literal.gno @@ -0,0 +1,8 @@ +package main + +func main() { + 1 = 6 +} + +// Error: +// main/files/types/assign_literal.gno:4: cannot assign to (const (1 bigint)) diff --git a/gnovm/tests/files/types/assign_literal10.gno b/gnovm/tests/files/types/assign_literal10.gno new file mode 100644 index 00000000000..d7b29f91aec --- /dev/null +++ b/gnovm/tests/files/types/assign_literal10.gno @@ -0,0 +1,21 @@ +package main + +type foo struct { + a int +} + +var n int + +func (f foo) add() *int { return &n } + +func main() { + s := &foo{} + *(s.add()) = 1 + println((*s).a) + + println(n) +} + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/files/types/assign_literal10a.gno b/gnovm/tests/files/types/assign_literal10a.gno new file mode 100644 index 00000000000..f6963d926ee --- /dev/null +++ b/gnovm/tests/files/types/assign_literal10a.gno @@ -0,0 +1,20 @@ +package main + +type foo struct { + a int +} + +var n int + +func (f foo) add() *int { return &n } + +func main() { + s := &foo{} + s.add() = 1 + println((*s).a) + + println(n) +} + +// Error: +// main/files/types/assign_literal10a.gno:13: cannot assign to s.add() diff --git a/gnovm/tests/files/types/assign_literal11.gno b/gnovm/tests/files/types/assign_literal11.gno new file mode 100644 index 00000000000..821f95422a0 --- /dev/null +++ b/gnovm/tests/files/types/assign_literal11.gno @@ -0,0 +1,11 @@ +package main + +const Pi = 3.14 + +func main() { + Pi = 3.14159 + println(Pi) +} + +// Error: +// main/files/types/assign_literal11.gno:6: cannot assign to (const (3.14 bigdec)) diff --git a/gnovm/tests/files/types/assign_literal2.gno b/gnovm/tests/files/types/assign_literal2.gno new file mode 100644 index 00000000000..2e495b6fb78 --- /dev/null +++ b/gnovm/tests/files/types/assign_literal2.gno @@ -0,0 +1,9 @@ +package main + +func main() { + var a int + a, 2 = 6, 6 +} + +// Error: +// main/files/types/assign_literal2.gno:5: cannot assign to (const (2 bigint)) diff --git a/gnovm/tests/files/types/assign_literal2_a.gno b/gnovm/tests/files/types/assign_literal2_a.gno new file mode 100644 index 00000000000..f66868ab842 --- /dev/null +++ b/gnovm/tests/files/types/assign_literal2_a.gno @@ -0,0 +1,9 @@ +package main + +func main() { + var a int + a, 2 := 6, 6 +} + +// Error: +// files/types/assign_literal2_a.gno:5:2: no new variables on left side of := \ No newline at end of file diff --git a/gnovm/tests/files/types/assign_literal3.gno b/gnovm/tests/files/types/assign_literal3.gno new file mode 100644 index 00000000000..67d4877cd5a --- /dev/null +++ b/gnovm/tests/files/types/assign_literal3.gno @@ -0,0 +1,8 @@ +package main + +func main() { + true = false +} + +// Error: +// main/files/types/assign_literal3.gno:4: cannot assign to (const (true bool)) diff --git a/gnovm/tests/files/types/assign_literal4.gno b/gnovm/tests/files/types/assign_literal4.gno new file mode 100644 index 00000000000..bdae4fa39a5 --- /dev/null +++ b/gnovm/tests/files/types/assign_literal4.gno @@ -0,0 +1,8 @@ +package main + +func main() { + []int{1, 2} = []int{3, 4} +} + +// Error: +// main/files/types/assign_literal4.gno:4: cannot assign to [](const-type int){(const (1 int)), (const (2 int))} diff --git a/gnovm/tests/files/types/assign_literal4_a.gno b/gnovm/tests/files/types/assign_literal4_a.gno new file mode 100644 index 00000000000..054ae227b32 --- /dev/null +++ b/gnovm/tests/files/types/assign_literal4_a.gno @@ -0,0 +1,8 @@ +package main + +func main() { + []int{1, 2} := []int{3, 4} +} + +// Error: +// files/types/assign_literal4_a.gno:4:2: no new variables on left side of := \ No newline at end of file diff --git a/gnovm/tests/files/types/assign_literal5.gno b/gnovm/tests/files/types/assign_literal5.gno new file mode 100644 index 00000000000..da382b8ac1c --- /dev/null +++ b/gnovm/tests/files/types/assign_literal5.gno @@ -0,0 +1,8 @@ +package main + +func main() { + map[string]int{"a": 1, "b": 2} = map[string]int{"a": 1, "b": 2} +} + +// Error: +// main/files/types/assign_literal5.gno:4: cannot assign to map[(const-type string)] (const-type int){(const ("a" string)): (const (1 int)), (const ("b" string)): (const (2 int))} diff --git a/gnovm/tests/files/types/assign_literal6.gno b/gnovm/tests/files/types/assign_literal6.gno new file mode 100644 index 00000000000..dfcf9cbb80a --- /dev/null +++ b/gnovm/tests/files/types/assign_literal6.gno @@ -0,0 +1,8 @@ +package main + +func main() { + 1 + 2 = 6 +} + +// Error: +// main/files/types/assign_literal6.gno:4: cannot assign to (const (3 bigint)) diff --git a/gnovm/tests/files/types/assign_literal7.gno b/gnovm/tests/files/types/assign_literal7.gno new file mode 100644 index 00000000000..56308cdbbfc --- /dev/null +++ b/gnovm/tests/files/types/assign_literal7.gno @@ -0,0 +1,10 @@ +package main + +func main() { + s := []int{1, 2, 3} + s[0] = 0 + println(s) +} + +// Output: +// slice[(0 int),(2 int),(3 int)] diff --git a/gnovm/tests/files/types/assign_literal7a.gno b/gnovm/tests/files/types/assign_literal7a.gno new file mode 100644 index 00000000000..8be126c49de --- /dev/null +++ b/gnovm/tests/files/types/assign_literal7a.gno @@ -0,0 +1,10 @@ +package main + +func main() { + m := map[string]int{"a": 1, "b": 2, "c": 3} + m["a"] = 0 + println(m) +} + +// Output: +// map{("a" string):(0 int),("b" string):(2 int),("c" string):(3 int)} diff --git a/gnovm/tests/files/types/assign_literal7b.gno b/gnovm/tests/files/types/assign_literal7b.gno new file mode 100644 index 00000000000..dc61467e07c --- /dev/null +++ b/gnovm/tests/files/types/assign_literal7b.gno @@ -0,0 +1,10 @@ +package main + +func main() { + s := [2]int{} + s[0] = 0 + println(s) +} + +// Output: +// array[(0 int),(0 int)] diff --git a/gnovm/tests/files/types/assign_literal7c.gno b/gnovm/tests/files/types/assign_literal7c.gno new file mode 100644 index 00000000000..989dcca6699 --- /dev/null +++ b/gnovm/tests/files/types/assign_literal7c.gno @@ -0,0 +1,13 @@ +package main + +import "fmt" + +func main() { + str := "hello" + str[0] = 'y' + fmt.Println(str[0]) + fmt.Printf("%c\n", str[0]) +} + +// Error: +// main/files/types/assign_literal7c.gno:7: cannot assign to str[(const (0 int))] diff --git a/gnovm/tests/files/types/assign_literal7d.gno b/gnovm/tests/files/types/assign_literal7d.gno new file mode 100644 index 00000000000..f56c304b3d3 --- /dev/null +++ b/gnovm/tests/files/types/assign_literal7d.gno @@ -0,0 +1,14 @@ +package main + +import "fmt" + +func main() { + bs := []byte("hello") + bs[0] = 'y' + fmt.Println(bs[0]) + fmt.Printf("%c\n", bs[0]) +} + +// Output: +// 121 +// y diff --git a/gnovm/tests/files/types/assign_literal8.gno b/gnovm/tests/files/types/assign_literal8.gno new file mode 100644 index 00000000000..d652efc0874 --- /dev/null +++ b/gnovm/tests/files/types/assign_literal8.gno @@ -0,0 +1,11 @@ +package main + +func main() { + var a int + p := &a + *p = 1 + println(*p) +} + +// Output: +// 1 diff --git a/gnovm/tests/files/types/assign_literal9.gno b/gnovm/tests/files/types/assign_literal9.gno new file mode 100644 index 00000000000..1d672ae46ce --- /dev/null +++ b/gnovm/tests/files/types/assign_literal9.gno @@ -0,0 +1,14 @@ +package main + +type foo struct { + a int +} + +func main() { + s := &foo{} + s.a = 1 + println(s.a) +} + +// Output: +// 1 diff --git a/gnovm/tests/files/types/assign_literal_a.gno b/gnovm/tests/files/types/assign_literal_a.gno new file mode 100644 index 00000000000..dd17e97c466 --- /dev/null +++ b/gnovm/tests/files/types/assign_literal_a.gno @@ -0,0 +1,8 @@ +package main + +func main() { + 1 := 6 +} + +// Error: +// files/types/assign_literal_a.gno:4:2: no new variables on left side of := \ No newline at end of file diff --git a/gnovm/tests/files/types/assign_nil.gno b/gnovm/tests/files/types/assign_nil.gno new file mode 100644 index 00000000000..e4012745d4f --- /dev/null +++ b/gnovm/tests/files/types/assign_nil.gno @@ -0,0 +1,11 @@ +package main + +func main() { + var i interface{} + i = 4 + var j int + j, nil = i.(int) +} + +// Error: +// main/files/types/assign_nil.gno:7: cannot assign to (const (undefined)) diff --git a/gnovm/tests/files/types/assign_nil2.gno b/gnovm/tests/files/types/assign_nil2.gno new file mode 100644 index 00000000000..8af61a1e496 --- /dev/null +++ b/gnovm/tests/files/types/assign_nil2.gno @@ -0,0 +1,11 @@ +package main + +func main() { + var i interface{} + i = 4 + var ok bool + nil, nil = i.(int) +} + +// Error: +// main/files/types/assign_nil2.gno:7: cannot assign to (const (undefined)) diff --git a/gnovm/tests/files/types/assign_range.gno b/gnovm/tests/files/types/assign_range.gno new file mode 100644 index 00000000000..5ee1c49a2e9 --- /dev/null +++ b/gnovm/tests/files/types/assign_range.gno @@ -0,0 +1,42 @@ +package main + +import "fmt" + +func main() { + // Creating a map where keys are strings and values are integers + scores := map[string]int{ + "Alice": 92, + "Bob": 89, + "Charlie": 95, + } + + // Using range to iterate over the map + // k is the key and v is the value for each pair in the map + for k, v := range scores { + fmt.Printf("%s has a score of %d\n", k, v) + } + + // Modifying values during iteration + for k := range scores { + scores[k] += 5 // Adding 5 to each score + } + fmt.Println("Updated scores:", scores) + + // Collecting keys and values into slices + var keys []string + var values []int + for k, v := range scores { + keys = append(keys, k) + values = append(values, v) + } + fmt.Println("Keys:", keys) + fmt.Println("Values:", values) +} + +// Output: +// Alice has a score of 92 +// Bob has a score of 89 +// Charlie has a score of 95 +// Updated scores: map[Alice:97 Bob:94 Charlie:100] +// Keys: [Alice Bob Charlie] +// Values: [97 94 100] diff --git a/gnovm/tests/files/types/assign_range_a.gno b/gnovm/tests/files/types/assign_range_a.gno new file mode 100644 index 00000000000..72115771e52 --- /dev/null +++ b/gnovm/tests/files/types/assign_range_a.gno @@ -0,0 +1,22 @@ +package main + +import "fmt" + +func main() { + // Creating a map where keys are strings and values are integers + scores := map[string]int{ + "Alice": 92, + "Bob": 89, + "Charlie": 95, + } + + // Using range to iterate over the map + // k is the key and v is the value for each pair in the map + var k, v int + for k, v = range scores { + fmt.Printf("%s has a score of %d\n", k, v) + } +} + +// Error: +// main/files/types/assign_range_a.gno:16: cannot use string as int diff --git a/gnovm/tests/files/types/assign_range_a1.gno b/gnovm/tests/files/types/assign_range_a1.gno new file mode 100644 index 00000000000..4e6ede4ffda --- /dev/null +++ b/gnovm/tests/files/types/assign_range_a1.gno @@ -0,0 +1,22 @@ +package main + +import "fmt" + +func main() { + // Creating a map where keys are strings and values are integers + scores := map[string]int{ + "Alice": 92, + "Bob": 89, + "Charlie": 95, + } + + // Using range to iterate over the map + // k is the key and v is the value for each pair in the map + var v int + for 6, v = range scores { + fmt.Printf("%s has a score of %d\n", "a", v) + } +} + +// Error: +// main/files/types/assign_range_a1.gno:16: cannot assign to (const (6 bigint)) diff --git a/gnovm/tests/files/types/assign_range_b.gno b/gnovm/tests/files/types/assign_range_b.gno new file mode 100644 index 00000000000..df7fbeb8158 --- /dev/null +++ b/gnovm/tests/files/types/assign_range_b.gno @@ -0,0 +1,20 @@ +package main + +import "fmt" + +func main() { + // Creating a map where keys are strings and values are integers + scores := []string{ + "a", "b", "c", + } + + // Using range to iterate over the map + // k is the key and v is the value for each pair in the map + var k, v int + for k, v = range scores { + fmt.Printf("%d has a score of %s\n", k, v) + } +} + +// Error: +// main/files/types/assign_range_b.gno:14: cannot use string as int diff --git a/gnovm/tests/files/types/assign_range_b1.gno b/gnovm/tests/files/types/assign_range_b1.gno new file mode 100644 index 00000000000..4632b79b6f0 --- /dev/null +++ b/gnovm/tests/files/types/assign_range_b1.gno @@ -0,0 +1,20 @@ +package main + +import "fmt" + +func main() { + // Creating a map where keys are strings and values are integers + scores := [3]string{ + "a", "b", "c", + } + + // Using range to iterate over the map + // k is the key and v is the value for each pair in the map + var k, v int + for k, v = range scores { + fmt.Printf("%d has a score of %s\n", k, v) + } +} + +// Error: +// main/files/types/assign_range_b1.gno:14: cannot use string as int diff --git a/gnovm/tests/files/types/assign_range_b2.gno b/gnovm/tests/files/types/assign_range_b2.gno new file mode 100644 index 00000000000..a96c3f91ace --- /dev/null +++ b/gnovm/tests/files/types/assign_range_b2.gno @@ -0,0 +1,28 @@ +package main + +func main() { + rangeMap() + rangeSlice() +} + +func rangeMap() { + m := map[int]string{999: "asdf"} + var v int + for v = range m { + println(v) + } +} + +func rangeSlice() { + m := []int{9, 4, 6} + var v int + for v = range m { + println(v) + } +} + +// Output: +// 999 +// 0 +// 1 +// 2 diff --git a/gnovm/tests/files/types/assign_range_c.gno b/gnovm/tests/files/types/assign_range_c.gno new file mode 100644 index 00000000000..783534bebb4 --- /dev/null +++ b/gnovm/tests/files/types/assign_range_c.gno @@ -0,0 +1,20 @@ +package main + +import "fmt" + +func main() { + // Creating a map where keys are strings and values are integers + scores := []string{ + "a", "b", "c", + } + + // Using range to iterate over the map + // k is the key and v is the value for each pair in the map + var k, v float32 + for k, v = range scores { + fmt.Printf("%d has a score of %s\n", k, v) + } +} + +// Error: +// main/files/types/assign_range_c.gno:14: index type should be int, but got float32 diff --git a/gnovm/tests/files/types/assign_range_d.gno b/gnovm/tests/files/types/assign_range_d.gno new file mode 100644 index 00000000000..6903cb0b7bc --- /dev/null +++ b/gnovm/tests/files/types/assign_range_d.gno @@ -0,0 +1,14 @@ +package main + +func main() { + s := "hello" + + var index float32 + var value rune + for index, value = range s { + println(index) + } +} + +// Error: +// main/files/types/assign_range_d.gno:8: index type should be int, but got float32 diff --git a/gnovm/tests/files/types/assign_range_e.gno b/gnovm/tests/files/types/assign_range_e.gno new file mode 100644 index 00000000000..7f7f15c5c23 --- /dev/null +++ b/gnovm/tests/files/types/assign_range_e.gno @@ -0,0 +1,15 @@ +package main + +func main() { + s := "hello" + + var index int + var value int + for index, value = range s { + println(index) + println(value) + } +} + +// Error: +// main/files/types/assign_range_e.gno:8: value type should be int32, but got int diff --git a/gnovm/tests/files/types/assign_rune.gno b/gnovm/tests/files/types/assign_rune.gno new file mode 100644 index 00000000000..7c1e8742fd4 --- /dev/null +++ b/gnovm/tests/files/types/assign_rune.gno @@ -0,0 +1,18 @@ +package main + +var r rune + +// TODO: print right dec, float +func main() { + r = 'a' + println(r) + println(float32(r)) + println(float64(r)) + println(string(r)) +} + +// Output: +// 97 +// 97 +// 97 +// a diff --git a/gnovm/tests/files/types/assign_rune_a.gno b/gnovm/tests/files/types/assign_rune_a.gno new file mode 100644 index 00000000000..13321fd8b08 --- /dev/null +++ b/gnovm/tests/files/types/assign_rune_a.gno @@ -0,0 +1,12 @@ +package main + +var r rune + +// assign +func main() { + r = "a" + println(r) +} + +// Error: +// main/files/types/assign_rune_a.gno:7: cannot use untyped string as Int32Kind diff --git a/gnovm/tests/files/types/assign_type_assertion.gno b/gnovm/tests/files/types/assign_type_assertion.gno new file mode 100644 index 00000000000..6bc2b4b26c8 --- /dev/null +++ b/gnovm/tests/files/types/assign_type_assertion.gno @@ -0,0 +1,22 @@ +package main + +import ( + "fmt" +) + +func main() { + var i interface{} = "Hello, world!" + + // Attempt to assert the type of i to string + var n int + var ok bool + n, ok = i.(string) + if ok { + fmt.Println("i contains a string:", n) + } else { + fmt.Println("i does not contain a string") + } +} + +// Error: +// main/files/types/assign_type_assertion.gno:13: cannot use string as int diff --git a/gnovm/tests/files/types/assign_type_assertion_a.gno b/gnovm/tests/files/types/assign_type_assertion_a.gno new file mode 100644 index 00000000000..1b28408d309 --- /dev/null +++ b/gnovm/tests/files/types/assign_type_assertion_a.gno @@ -0,0 +1,33 @@ +package main + +import "fmt" + +type MyError struct{} + +func (e MyError) Error() string { + return "my error" +} + +func (e MyError) IsSet() bool { + return true +} + +func main() { + var err error = MyError{} + + var assertedErr interface{ IsSet() bool } // Define a variable of the interface type + var ok bool + + // Perform the assertion and assign the result to assertedErr + assertedErr, ok = err.(interface{ IsSet() bool }) + if ok { + fmt.Println("Assertion succeeded:", ok) + fmt.Println("IsSet:", assertedErr.IsSet()) + } else { + fmt.Println("Assertion failed:", ok) + } +} + +// Output: +// Assertion succeeded: true +// IsSet: true diff --git a/gnovm/tests/files/types/assign_type_assertion_b.gno b/gnovm/tests/files/types/assign_type_assertion_b.gno new file mode 100644 index 00000000000..4f68751667d --- /dev/null +++ b/gnovm/tests/files/types/assign_type_assertion_b.gno @@ -0,0 +1,32 @@ +package main + +import "fmt" + +type MyError struct{} + +func (e MyError) Error() string { + return "my error" +} + +func (e MyError) IsSet() bool { + return true +} + +func main() { + var err error = MyError{} + + var assertedErr int // Define a variable of the interface type + var ok bool + + // Perform the assertion and assign the result to assertedErr + assertedErr, ok = err.(interface{ IsSet() bool }) + if ok { + fmt.Println("Assertion succeeded:", ok) + fmt.Println("IsSet:", assertedErr.IsSet()) + } else { + fmt.Println("Assertion failed:", ok) + } +} + +// Error: +// main/files/types/assign_type_assertion_b.gno:22: cannot use interface{IsSet func()(bool)} as int diff --git a/gnovm/tests/files/types/assign_type_assertion_c.gno b/gnovm/tests/files/types/assign_type_assertion_c.gno new file mode 100644 index 00000000000..044154dadfd --- /dev/null +++ b/gnovm/tests/files/types/assign_type_assertion_c.gno @@ -0,0 +1,33 @@ +package main + +import "fmt" + +type MyError struct{} + +func (e MyError) Error() string { + return "my error" +} + +func (e MyError) IsSet() bool { + return true +} + +func main() { + var err error = MyError{} + + var assertedErr interface{ IsNotSet() bool } // Define a variable of the interface type + + var ok bool + + // Perform the assertion and assign the result to assertedErr + assertedErr, ok = err.(interface{ IsSet() bool }) // not impl lhs + if ok { + fmt.Println("Assertion succeeded:", ok) + fmt.Println("IsSet:", assertedErr.IsSet()) + } else { + fmt.Println("Assertion failed:", ok) + } +} + +// Error: +// main/files/types/assign_type_assertion_c.gno:23: interface{IsSet func()(bool)} does not implement interface{IsNotSet func()(bool)} diff --git a/gnovm/tests/files/types/assign_type_assertion_d.gno b/gnovm/tests/files/types/assign_type_assertion_d.gno new file mode 100644 index 00000000000..4ab24f36aac --- /dev/null +++ b/gnovm/tests/files/types/assign_type_assertion_d.gno @@ -0,0 +1,25 @@ +package main + +type Animal interface { + eat() +} + +type Robot struct { +} + +type Dog struct{} + +func (Dog) eat() {} + +func main() { + var animal Animal = Dog{} + + var r Robot + + r = animal.(Dog) + + println(r) +} + +// Error: +// main/files/types/assign_type_assertion_d.gno:19: cannot use main.Dog as main.Robot without explicit conversion diff --git a/gnovm/tests/files/types/assign_type_assertion_e.gno b/gnovm/tests/files/types/assign_type_assertion_e.gno new file mode 100644 index 00000000000..fbe91031667 --- /dev/null +++ b/gnovm/tests/files/types/assign_type_assertion_e.gno @@ -0,0 +1,27 @@ +package main + +type Animal interface { + eat() +} + +type Robot struct { +} + +type Dog struct{} + +func (Dog) eat() {} + +func main() { + var animal Animal = Dog{} + + var r Robot + var ok bool + + r, ok = animal.(Dog) + + println(r) + println(ok) +} + +// Error: +// main/files/types/assign_type_assertion_e.gno:20: cannot use main.Dog as main.Robot without explicit conversion diff --git a/gnovm/tests/files/types/assign_type_assertion_f.gno b/gnovm/tests/files/types/assign_type_assertion_f.gno new file mode 100644 index 00000000000..bc2907eb8af --- /dev/null +++ b/gnovm/tests/files/types/assign_type_assertion_f.gno @@ -0,0 +1,25 @@ +package main + +type Animal interface { + eat() +} + +type Robot struct { +} + +type Dog struct{} + +func (Dog) eat() {} + +func main() { + var animal Animal = Dog{} + + var ok bool + + 1, ok = animal.(Dog) + + println(ok) +} + +// Error: +// main/files/types/assign_type_assertion_f.gno:19: cannot assign to (const (1 bigint)) diff --git a/gnovm/tests/files/types/assign_type_assertion_g.gno b/gnovm/tests/files/types/assign_type_assertion_g.gno new file mode 100644 index 00000000000..2d3d61c561d --- /dev/null +++ b/gnovm/tests/files/types/assign_type_assertion_g.gno @@ -0,0 +1,27 @@ +package main + +type Animal interface { + eat() +} + +type Robot struct { +} + +type Dog struct{} + +func (Dog) eat() {} + +func main() { + var animal Animal = Dog{} + + var a Animal + var ok int + + a, ok = animal.(Dog) + + println(a) + println(ok) +} + +// Error: +// main/files/types/assign_type_assertion_g.gno:20: want bool type got int diff --git a/gnovm/tests/files/types/bigdec.gno b/gnovm/tests/files/types/bigdec.gno new file mode 100644 index 00000000000..baa3d4d6309 --- /dev/null +++ b/gnovm/tests/files/types/bigdec.gno @@ -0,0 +1,11 @@ +package main + +var a uint64 + +func main() { + a = 1 + println(a % 1e9) +} + +// Output: +// 1 diff --git a/gnovm/tests/files/types/bigdec2.gno b/gnovm/tests/files/types/bigdec2.gno new file mode 100644 index 00000000000..60048151a31 --- /dev/null +++ b/gnovm/tests/files/types/bigdec2.gno @@ -0,0 +1,11 @@ +package main + +var a uint64 + +func main() { + a = 1 + println(a % 1.2) +} + +// Error: +// main/files/types/bigdec2.gno:7: cannot convert untyped bigdec to integer -- 1.2 not an exact integer diff --git a/gnovm/tests/files/types/bigdec3.gno b/gnovm/tests/files/types/bigdec3.gno new file mode 100644 index 00000000000..0e0effa7cbb --- /dev/null +++ b/gnovm/tests/files/types/bigdec3.gno @@ -0,0 +1,11 @@ +package main + +var a uint64 + +func main() { + a = 1 + println(1.0 % a) +} + +// Output: +// 0 diff --git a/gnovm/tests/files/types/bigdec4.gno b/gnovm/tests/files/types/bigdec4.gno new file mode 100644 index 00000000000..88cbe894020 --- /dev/null +++ b/gnovm/tests/files/types/bigdec4.gno @@ -0,0 +1,11 @@ +package main + +var a uint64 + +func main() { + a = 1.0 + println(a) +} + +// Output: +// 1 diff --git a/gnovm/tests/files/types/bigdec5.gno b/gnovm/tests/files/types/bigdec5.gno new file mode 100644 index 00000000000..e3f64e0a8ce --- /dev/null +++ b/gnovm/tests/files/types/bigdec5.gno @@ -0,0 +1,11 @@ +package main + +var a uint64 + +func main() { + a = 1.2 + println(a) +} + +// Error: +// main/files/types/bigdec5.gno:6: cannot convert untyped bigdec to integer -- 1.2 not an exact integer diff --git a/gnovm/tests/files/types/bigdec_6.gno b/gnovm/tests/files/types/bigdec_6.gno new file mode 100644 index 00000000000..5ef078619b5 --- /dev/null +++ b/gnovm/tests/files/types/bigdec_6.gno @@ -0,0 +1,8 @@ +package main + +func main() { + println(1 % 1e9) +} + +// Error: +// main/files/types/bigdec_6.gno:4: operator % not defined on: BigdecKind diff --git a/gnovm/tests/files/types/cmp_array.gno b/gnovm/tests/files/types/cmp_array.gno new file mode 100644 index 00000000000..52fea95b034 --- /dev/null +++ b/gnovm/tests/files/types/cmp_array.gno @@ -0,0 +1,16 @@ +package main + +import "fmt" + +func main() { + a := [2][2]int{{1, 2}, {3, 4}} + b := [2][2]int{{1, 2}, {3, 4}} + c := [2][2]int{{5, 6}, {7, 8}} + + fmt.Println("a == b:", a == b) // True because the elements match exactly + fmt.Println("a == c:", a == c) // False because the elements do not match +} + +// Output: +// a == b: true +// a == c: false diff --git a/gnovm/tests/files/types/cmp_array_a.gno b/gnovm/tests/files/types/cmp_array_a.gno new file mode 100644 index 00000000000..1d30f7c51eb --- /dev/null +++ b/gnovm/tests/files/types/cmp_array_a.gno @@ -0,0 +1,21 @@ +package main + +import "fmt" + +// Define a struct that embeds an array +type Matrix struct { + data [2]int +} + +func main() { + a := [2]Matrix{{data: [2]int{1, 2}}, {data: [2]int{3, 4}}} + b := [2]Matrix{{data: [2]int{1, 2}}, {data: [2]int{3, 4}}} + c := [2]Matrix{{data: [2]int{5, 6}}, {data: [2]int{7, 8}}} + + fmt.Println("a == b:", a == b) // True because the elements match exactly + fmt.Println("a == c:", a == c) // False because the elements do not match +} + +// Output: +// a == b: true +// a == c: false diff --git a/gnovm/tests/files/types/cmp_array_b.gno b/gnovm/tests/files/types/cmp_array_b.gno new file mode 100644 index 00000000000..490a3424965 --- /dev/null +++ b/gnovm/tests/files/types/cmp_array_b.gno @@ -0,0 +1,15 @@ +package main + +import "fmt" + +func main() { + a := [2][]int{{1, 2}, {3, 4}} + b := [2][]int{{1, 2}, {3, 4}} + c := [2][]int{{5, 6}, {7, 8}} + + fmt.Println("a == b:", a == b) // True because the elements match exactly + fmt.Println("a == c:", a == c) // False because the elements do not match +} + +// Error: +// main/files/types/cmp_array_b.gno:10: [2][]int is not comparable diff --git a/gnovm/tests/files/types/cmp_array_c.gno b/gnovm/tests/files/types/cmp_array_c.gno new file mode 100644 index 00000000000..f23ef6c9a78 --- /dev/null +++ b/gnovm/tests/files/types/cmp_array_c.gno @@ -0,0 +1,15 @@ +package main + +import "fmt" + +func main() { + a := [2][2][]int{{{1, 2}, {3, 4}}, {{5, 6}, {7, 8}}} + b := [2][2][]int{{{1, 2}, {3, 4}}, {{5, 6}, {7, 8}}} + c := [2][2][]int{{{5, 6}, {7, 8}}, {{1, 2}, {3, 4}}} + + fmt.Println("a == b:", a == b) + fmt.Println("a == c:", a == c) +} + +// Error: +// main/files/types/cmp_array_c.gno:10: [2][]int is not comparable diff --git a/gnovm/tests/files/types/cmp_array_d.gno b/gnovm/tests/files/types/cmp_array_d.gno new file mode 100644 index 00000000000..78f9527f819 --- /dev/null +++ b/gnovm/tests/files/types/cmp_array_d.gno @@ -0,0 +1,13 @@ +package main + +import "fmt" + +func main() { + a := [2]int{1, 2} + b := [2]int{1, 2} + + fmt.Println("a == b:", a == b) +} + +// Output: +// a == b: true diff --git a/gnovm/tests/files/types/cmp_poiner.gno b/gnovm/tests/files/types/cmp_poiner.gno new file mode 100644 index 00000000000..0d7625a878d --- /dev/null +++ b/gnovm/tests/files/types/cmp_poiner.gno @@ -0,0 +1,23 @@ +package main + +import "fmt" + +// Define a Person struct +type Person struct { + Name string + Age int +} + +type Worker string + +func main() { + // Create two pointers to Person instances + p1 := &Person{Name: "Alice", Age: 30} + p2 := Worker("a") + p2Ptr := &p2 + + fmt.Println("p1 == p2:", p1 == p2Ptr) +} + +// Error: +// main/files/types/cmp_poiner.gno:19: cannot use main.Person as main.Worker without explicit conversion diff --git a/gnovm/tests/files/types/cmp_poiner2.gno b/gnovm/tests/files/types/cmp_poiner2.gno new file mode 100644 index 00000000000..6899803075f --- /dev/null +++ b/gnovm/tests/files/types/cmp_poiner2.gno @@ -0,0 +1,10 @@ +package main + +func main() { + a := []int{1, 2} + b := []int{1, 2} + println(&a == &b) +} + +// Output: +// false diff --git a/gnovm/tests/files/types/cmp_struct.gno b/gnovm/tests/files/types/cmp_struct.gno new file mode 100644 index 00000000000..c3ead1510a4 --- /dev/null +++ b/gnovm/tests/files/types/cmp_struct.gno @@ -0,0 +1,23 @@ +package main + +import "fmt" + +// Define a struct that contains an array field +type Matrix struct { + data [2]int +} + +func main() { + // Create two instances of the struct + m1 := Matrix{data: [2]int{1, 2}} + m2 := Matrix{data: [2]int{1, 2}} + m3 := Matrix{data: [2]int{3, 4}} + + // Compare the instances + fmt.Println("m1 == m2:", m1 == m2) // True because the data fields are identical + fmt.Println("m1 == m3:", m1 == m3) // False because the data fields are different +} + +// Output: +// m1 == m2: true +// m1 == m3: false diff --git a/gnovm/tests/files/types/cmp_struct_a.gno b/gnovm/tests/files/types/cmp_struct_a.gno new file mode 100644 index 00000000000..5e8add9f1d6 --- /dev/null +++ b/gnovm/tests/files/types/cmp_struct_a.gno @@ -0,0 +1,20 @@ +package main + +import "fmt" + +// Define a struct that wraps the three-dimensional data structure +type Matrix struct { + data [2][2][]int +} + +func main() { + a := Matrix{data: [2][2][]int{{{1, 2}, {3, 4}}, {{5, 6}, {7, 8}}}} + b := Matrix{data: [2][2][]int{{{1, 2}, {3, 4}}, {{5, 6}, {7, 8}}}} + c := Matrix{data: [2][2][]int{{{5, 6}, {7, 8}}, {{1, 2}, {3, 4}}}} + + fmt.Println("a == b:", a == b) + fmt.Println("a == c:", a == c) +} + +// Error: +// main/files/types/cmp_struct_a.gno:15: [2][]int is not comparable diff --git a/gnovm/tests/files/types/cmp_struct_b.gno b/gnovm/tests/files/types/cmp_struct_b.gno new file mode 100644 index 00000000000..f767fec16d5 --- /dev/null +++ b/gnovm/tests/files/types/cmp_struct_b.gno @@ -0,0 +1,19 @@ +package main + +type foo struct { + a int +} + +type bar struct { + b int +} + +func main() { + fa := foo{} + bb := bar{} + + println(fa == bb) +} + +// Error: +// main/files/types/cmp_struct_b.gno:15: cannot use main.foo as main.bar without explicit conversion diff --git a/gnovm/tests/files/types/cmp_struct_c.gno b/gnovm/tests/files/types/cmp_struct_c.gno new file mode 100644 index 00000000000..4894e32d0e5 --- /dev/null +++ b/gnovm/tests/files/types/cmp_struct_c.gno @@ -0,0 +1,19 @@ +package main + +type foo struct { + a int +} + +type bar struct { + b []int +} + +func main() { + fa := foo{} + bb := bar{} + + println(fa == bb) +} + +// Error: +// main/files/types/cmp_struct_c.gno:15: main.bar is not comparable \ No newline at end of file diff --git a/gnovm/tests/files/types/cmp_struct_c1.gno b/gnovm/tests/files/types/cmp_struct_c1.gno new file mode 100644 index 00000000000..3cb4b835dcd --- /dev/null +++ b/gnovm/tests/files/types/cmp_struct_c1.gno @@ -0,0 +1,19 @@ +package main + +type foo struct { + a int +} + +type bar struct { + b []int +} + +func main() { + fa := foo{} + bb := bar{} + + println(bb == fa) +} + +// Error: +// main/files/types/cmp_struct_c1.gno:15: cannot use main.bar as main.foo without explicit conversion diff --git a/gnovm/tests/files/types/cmp_struct_d.gno b/gnovm/tests/files/types/cmp_struct_d.gno new file mode 100644 index 00000000000..4c2f6eb848f --- /dev/null +++ b/gnovm/tests/files/types/cmp_struct_d.gno @@ -0,0 +1,19 @@ +package main + +type foo struct { + a []int +} + +type bar struct { + b []int +} + +func main() { + fa := foo{} + bb := bar{} + + println(fa == bb) +} + +// Error: +// main/files/types/cmp_struct_d.gno:15: main.bar is not comparable diff --git a/gnovm/tests/files/types/cmp_struct_e.gno b/gnovm/tests/files/types/cmp_struct_e.gno new file mode 100644 index 00000000000..4640c0e4252 --- /dev/null +++ b/gnovm/tests/files/types/cmp_struct_e.gno @@ -0,0 +1,15 @@ +package main + +type foo struct { + a []int +} + +func main() { + fa := foo{} + fb := foo{} + + println(fa == fb) +} + +// Error: +// main/files/types/cmp_struct_e.gno:11: main.foo is not comparable diff --git a/gnovm/tests/files/types/cmp_struct_f.gno b/gnovm/tests/files/types/cmp_struct_f.gno new file mode 100644 index 00000000000..20366b625e9 --- /dev/null +++ b/gnovm/tests/files/types/cmp_struct_f.gno @@ -0,0 +1,43 @@ +package main + +import "fmt" + +// Define a Person struct +type Person struct { + Name string + Age int +} + +// Define an Employee struct that embeds Person +type Employee struct { + Person + Department string + ID int +} + +func main() { + emp1 := Employee{ + Person: Person{Name: "John Doe", Age: 30}, + Department: "Engineering", + ID: 12345, + } + + emp2 := Employee{ + Person: Person{Name: "John Doe", Age: 30}, + Department: "Engineering", + ID: 12345, + } + + emp3 := Employee{ + Person: Person{Name: "Jane Doe", Age: 29}, + Department: "Marketing", + ID: 67890, + } + + fmt.Println("emp1 == emp2:", emp1 == emp2) // True because all fields match + fmt.Println("emp1 == emp3:", emp1 == emp3) // False because some fields differ +} + +// Output: +// emp1 == emp2: true +// emp1 == emp3: false diff --git a/gnovm/tests/files/types/cmp_struct_g.gno b/gnovm/tests/files/types/cmp_struct_g.gno new file mode 100644 index 00000000000..05224000f05 --- /dev/null +++ b/gnovm/tests/files/types/cmp_struct_g.gno @@ -0,0 +1,21 @@ +package main + +import "fmt" + +type Person struct { + age int +} + +type Dog struct { + age int +} + +func main() { + a := Person{} + b := Dog{} + + fmt.Println("a == b:", a == b) +} + +// Error: +// main/files/types/cmp_struct_g.gno:17: cannot use main.Person as main.Dog without explicit conversion diff --git a/gnovm/tests/files/types/eql_0a0.gno b/gnovm/tests/files/types/eql_0a0.gno new file mode 100644 index 00000000000..32e5473b34f --- /dev/null +++ b/gnovm/tests/files/types/eql_0a0.gno @@ -0,0 +1,9 @@ +package main + +// both typed(different) const +func main() { + println(int(1) == int8(1)) +} + +// Error: +// main/files/types/eql_0a0.gno:5: cannot use int as int8 diff --git a/gnovm/tests/files/types/eql_0a01.gno b/gnovm/tests/files/types/eql_0a01.gno new file mode 100644 index 00000000000..e5cdeff4b63 --- /dev/null +++ b/gnovm/tests/files/types/eql_0a01.gno @@ -0,0 +1,8 @@ +package main + +func main() { + println(nil == nil) // xt: , dt: +} + +// Error: +// main/files/types/eql_0a01.gno:4: is not comparable diff --git a/gnovm/tests/files/types/eql_0a02.gno b/gnovm/tests/files/types/eql_0a02.gno new file mode 100644 index 00000000000..67116da6cc8 --- /dev/null +++ b/gnovm/tests/files/types/eql_0a02.gno @@ -0,0 +1,11 @@ +package main + +func main() { + intPtr := new(int) + *intPtr = 5 + s := "hello" + println(intPtr == s) +} + +// Error: +// main/files/types/eql_0a02.gno:7: cannot use *int as string diff --git a/gnovm/tests/files/types/eql_0a03.gno b/gnovm/tests/files/types/eql_0a03.gno new file mode 100644 index 00000000000..677493a9af6 --- /dev/null +++ b/gnovm/tests/files/types/eql_0a03.gno @@ -0,0 +1,12 @@ +package main + +func main() { + intPtr := new(int8) + *intPtr = 5 + + i := 0 + println(intPtr == &i) +} + +// Error: +// main/files/types/eql_0a03.gno:8: cannot use int8 as int diff --git a/gnovm/tests/files/types/eql_0a1.gno b/gnovm/tests/files/types/eql_0a1.gno new file mode 100644 index 00000000000..9641e67c90a --- /dev/null +++ b/gnovm/tests/files/types/eql_0a1.gno @@ -0,0 +1,9 @@ +package main + +// both typed(different) const +func main() { + println(int(1) != int8(1)) +} + +// Error: +// main/files/types/eql_0a1.gno:5: cannot use int as int8 diff --git a/gnovm/tests/files/types/eql_0a1a.gno b/gnovm/tests/files/types/eql_0a1a.gno new file mode 100644 index 00000000000..deb369511fd --- /dev/null +++ b/gnovm/tests/files/types/eql_0a1a.gno @@ -0,0 +1,11 @@ +package main + +// left typed, right untyped +func main() { + println(int(1) == 1) + println(int(1) != 1) +} + +// Output: +// true +// false diff --git a/gnovm/tests/files/types/eql_0a1a0.gno b/gnovm/tests/files/types/eql_0a1a0.gno new file mode 100644 index 00000000000..e251174adef --- /dev/null +++ b/gnovm/tests/files/types/eql_0a1a0.gno @@ -0,0 +1,9 @@ +package main + +func main() { + a := uint(1) + println(uint64(1) == a) +} + +// Error: +// main/files/types/eql_0a1a0.gno:5: cannot use uint64 as uint diff --git a/gnovm/tests/files/types/eql_0a1a1.gno b/gnovm/tests/files/types/eql_0a1a1.gno new file mode 100644 index 00000000000..0af8aa45943 --- /dev/null +++ b/gnovm/tests/files/types/eql_0a1a1.gno @@ -0,0 +1,9 @@ +package main + +func main() { + a := uint(1) + println(a == uint64(1)) +} + +// Error: +// main/files/types/eql_0a1a1.gno:5: cannot use uint as uint64 diff --git a/gnovm/tests/files/types/eql_0a1b.gno b/gnovm/tests/files/types/eql_0a1b.gno new file mode 100644 index 00000000000..1db537a4d8c --- /dev/null +++ b/gnovm/tests/files/types/eql_0a1b.gno @@ -0,0 +1,20 @@ +package main + +type S struct { + expected string +} + +// special case when RHS is result of slice operation, its type is determined in runtime +func main() { + s := S{ + expected: `hello`[:], // this is not converted + } + + a := "hello" + + println(a == s.expected) + +} + +// Output: +// true diff --git a/gnovm/tests/files/types/eql_0a1c.gno b/gnovm/tests/files/types/eql_0a1c.gno new file mode 100644 index 00000000000..76f2db8d7d8 --- /dev/null +++ b/gnovm/tests/files/types/eql_0a1c.gno @@ -0,0 +1,10 @@ +package main + +func main() { + expected := `hello`[:] + a := "hello" + println(a == expected) +} + +// Output: +// true diff --git a/gnovm/tests/files/types/eql_0a1d.gno b/gnovm/tests/files/types/eql_0a1d.gno new file mode 100644 index 00000000000..018d53fa81a --- /dev/null +++ b/gnovm/tests/files/types/eql_0a1d.gno @@ -0,0 +1,14 @@ +package main + +type S struct { + expected string +} + +func main() { + println("hello" == S{ + expected: `hello`[:], + }.expected) +} + +// Output: +// true diff --git a/gnovm/tests/files/types/eql_0a1e.gno b/gnovm/tests/files/types/eql_0a1e.gno new file mode 100644 index 00000000000..2795d618e91 --- /dev/null +++ b/gnovm/tests/files/types/eql_0a1e.gno @@ -0,0 +1,16 @@ +package main + +type S struct { + expected string +} + +func main() { + var s = S{ + expected: `hello`[:], + } + a := "hello" + println(a == s.expected) +} + +// Output: +// true diff --git a/gnovm/tests/files/types/eql_0a1f.gno b/gnovm/tests/files/types/eql_0a1f.gno new file mode 100644 index 00000000000..fda84dcdff8 --- /dev/null +++ b/gnovm/tests/files/types/eql_0a1f.gno @@ -0,0 +1,10 @@ +package main + +func main() { + expected := `hello`[:] + a := 1 + println(a == expected) // both typed +} + +// Error: +// main/files/types/eql_0a1f.gno:6: cannot use int as string diff --git a/gnovm/tests/files/types/eql_0a1g.gno b/gnovm/tests/files/types/eql_0a1g.gno new file mode 100644 index 00000000000..d80b6e0c9ac --- /dev/null +++ b/gnovm/tests/files/types/eql_0a1g.gno @@ -0,0 +1,10 @@ +package main + +func main() { + var a int = 1 + var b float32 = 1.0 + println(a == b) // both typed +} + +// Error: +// main/files/types/eql_0a1g.gno:6: cannot use int as float32 diff --git a/gnovm/tests/files/types/eql_0a2.gno b/gnovm/tests/files/types/eql_0a2.gno new file mode 100644 index 00000000000..281a61e50eb --- /dev/null +++ b/gnovm/tests/files/types/eql_0a2.gno @@ -0,0 +1,25 @@ +package main + +import ( + "strconv" +) + +type Error1 int64 + +func (e Error1) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +type Error2 int + +func (e Error2) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +// both typed(different) const +func main() { + println(Error1(0) == Error2(0)) +} + +// Error: +// main/files/types/eql_0a2.gno:21: cannot use main.Error1 as main.Error2 without explicit conversion diff --git a/gnovm/tests/files/types/eql_0a3.gno b/gnovm/tests/files/types/eql_0a3.gno new file mode 100644 index 00000000000..198a65b0827 --- /dev/null +++ b/gnovm/tests/files/types/eql_0a3.gno @@ -0,0 +1,25 @@ +package main + +import ( + "strconv" +) + +type Error1 int64 + +func (e Error1) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +type Error2 int + +func (e Error2) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +// both typed(different) const +func main() { + println(Error1(0) != Error2(0)) +} + +// Error: +// main/files/types/eql_0a3.gno:21: cannot use main.Error1 as main.Error2 without explicit conversion diff --git a/gnovm/tests/files/types/eql_0a4.gno b/gnovm/tests/files/types/eql_0a4.gno new file mode 100644 index 00000000000..d2c859edd09 --- /dev/null +++ b/gnovm/tests/files/types/eql_0a4.gno @@ -0,0 +1,25 @@ +package main + +import ( + "strconv" +) + +type Error1 int64 + +func (e Error1) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +type Error2 int + +func (e Error2) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +// both typed(different) const +func main() { + println(Error1(0) != Error2(0)) +} + +// Error: +// main/files/types/eql_0a4.gno:21: cannot use main.Error1 as main.Error2 without explicit conversion diff --git a/gnovm/tests/files/types/eql_0b0.gno b/gnovm/tests/files/types/eql_0b0.gno new file mode 100644 index 00000000000..2c968b5158f --- /dev/null +++ b/gnovm/tests/files/types/eql_0b0.gno @@ -0,0 +1,24 @@ +package main + +import ( + "strconv" +) + +type Error int8 + +func (e Error) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +// left is untyped const, right is typed const +// left is assignable to right +func main() { + if 1 == Error(1) { + println("what the firetruck?") + } else { + println("something else") + } +} + +// Output: +// what the firetruck? diff --git a/gnovm/tests/files/types/eql_0b1.gno b/gnovm/tests/files/types/eql_0b1.gno new file mode 100644 index 00000000000..a7bdf5275da --- /dev/null +++ b/gnovm/tests/files/types/eql_0b1.gno @@ -0,0 +1,23 @@ +package main + +import ( + "strconv" +) + +type Error int8 + +func (e Error) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +// left is untyped const, right is typed const +func main() { + if 1 != Error(1) { + println("what the firetruck?") + } else { + println("something else") + } +} + +// Output: +// something else diff --git a/gnovm/tests/files/types/eql_0b2.gno b/gnovm/tests/files/types/eql_0b2.gno new file mode 100644 index 00000000000..51981348a6e --- /dev/null +++ b/gnovm/tests/files/types/eql_0b2.gno @@ -0,0 +1,22 @@ +package main + +type Error string + +func (e Error) Error() string { + return "error: " + string(e) +} + +// left is untyped const, right is typed const +// left is not assignable to right +// a) it's (untyped) bigint +// b) base type of right is string +func main() { + if 1 == Error(1) { + println("what the firetruck?") + } else { + println("something else") + } +} + +// Error: +// main/files/types/eql_0b2.gno:14: cannot use untyped Bigint as StringKind diff --git a/gnovm/tests/files/types/eql_0b3.gno b/gnovm/tests/files/types/eql_0b3.gno new file mode 100644 index 00000000000..34c8a24cba2 --- /dev/null +++ b/gnovm/tests/files/types/eql_0b3.gno @@ -0,0 +1,15 @@ +package main + +var a int8 + +func main() { + a = 1 + if 1 == a { + println("what the firetruck?") + } else { + println("something else") + } +} + +// Output: +// what the firetruck? diff --git a/gnovm/tests/files/types/eql_0b4_native.gno b/gnovm/tests/files/types/eql_0b4_native.gno new file mode 100644 index 00000000000..b78d5f9be73 --- /dev/null +++ b/gnovm/tests/files/types/eql_0b4_native.gno @@ -0,0 +1,13 @@ +package main + +import ( + "errors" +) + +func main() { + errCmp := errors.New("xxx") + println(5 == errCmp) +} + +// Error: +// main/files/types/eql_0b4_native.gno:9: unexpected type pair: cannot use bigint as gonative{error} diff --git a/gnovm/tests/files/types/eql_0b4_stdlibs.gno b/gnovm/tests/files/types/eql_0b4_stdlibs.gno new file mode 100644 index 00000000000..de36f025f34 --- /dev/null +++ b/gnovm/tests/files/types/eql_0b4_stdlibs.gno @@ -0,0 +1,13 @@ +package main + +import ( + "errors" +) + +func main() { + errCmp := errors.New("xxx") + println(5 == errCmp) +} + +// Error: +// main/files/types/eql_0b4_stdlibs.gno:9: bigint does not implement .uverse.error diff --git a/gnovm/tests/files/types/eql_0c2.gno b/gnovm/tests/files/types/eql_0c2.gno new file mode 100644 index 00000000000..6ac4a2799ab --- /dev/null +++ b/gnovm/tests/files/types/eql_0c2.gno @@ -0,0 +1,24 @@ +package main + +import ( + "strconv" +) + +type Error int8 + +func (e Error) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +// left is typed const, right is untyped const +// NOTE: overflow +func main() { + if Error(1) == 128 { // note, this would overflow as expected + println("what the firetruck?") + } else { + println("something else") + } +} + +// Error: +// main/files/types/eql_0c2.gno:16: bigint overflows target kind diff --git a/gnovm/tests/files/types/eql_0d0.gno b/gnovm/tests/files/types/eql_0d0.gno new file mode 100644 index 00000000000..6890d450bab --- /dev/null +++ b/gnovm/tests/files/types/eql_0d0.gno @@ -0,0 +1,13 @@ +package main + +// both untyped const +func main() { + println(1.0 == 1) + println(1.0 == 0) + println(float32(1.0) == 0) +} + +// Output: +// true +// false +// false diff --git a/gnovm/tests/files/types/eql_0e0.gno b/gnovm/tests/files/types/eql_0e0.gno new file mode 100644 index 00000000000..a1ab28d1932 --- /dev/null +++ b/gnovm/tests/files/types/eql_0e0.gno @@ -0,0 +1,27 @@ +package main + +import ( + "strconv" +) + +type Error1 int64 + +func (e Error1) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +type Error2 int + +func (e Error2) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +// both not const, typed +func main() { + var e1 Error1 = Error1(0) + var e2 Error2 = Error2(0) + println(e1 == e2) +} + +// Error: +// main/files/types/eql_0e0.gno:23: cannot use main.Error1 as main.Error2 without explicit conversion diff --git a/gnovm/tests/files/types/eql_0e1.gno b/gnovm/tests/files/types/eql_0e1.gno new file mode 100644 index 00000000000..798d4f04777 --- /dev/null +++ b/gnovm/tests/files/types/eql_0e1.gno @@ -0,0 +1,27 @@ +package main + +import ( + "strconv" +) + +type Error1 int64 + +func (e Error1) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +type Error2 int + +func (e Error2) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +// both not const, typed +func main() { + var e1 Error1 = Error1(0) + var e2 Error2 = Error2(0) + println(e1 != e2) +} + +// Error: +// main/files/types/eql_0e1.gno:23: cannot use main.Error1 as main.Error2 without explicit conversion diff --git a/gnovm/tests/files/types/eql_0f0_native.gno b/gnovm/tests/files/types/eql_0f0_native.gno new file mode 100644 index 00000000000..7db72819774 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f0_native.gno @@ -0,0 +1,28 @@ +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_native.gno:19: unexpected type pair: cannot use bigint as gonative{error} diff --git a/gnovm/tests/files/types/eql_0f0_stdlibs.gno b/gnovm/tests/files/types/eql_0f0_stdlibs.gno new file mode 100644 index 00000000000..39a7ac68490 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f0_stdlibs.gno @@ -0,0 +1,28 @@ +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: bigint does not implement .uverse.error diff --git a/gnovm/tests/files/types/eql_0f12.gno b/gnovm/tests/files/types/eql_0f12.gno new file mode 100644 index 00000000000..751852d0b70 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f12.gno @@ -0,0 +1,13 @@ +package main + +var a [2]string +var b [2]string + +func main() { + a = [2]string{"hello", "world"} + b = [2]string{"hello", "world"} + println(a == b) +} + +// Output: +// true diff --git a/gnovm/tests/files/types/eql_0f14.gno b/gnovm/tests/files/types/eql_0f14.gno new file mode 100644 index 00000000000..500157d7303 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f14.gno @@ -0,0 +1,14 @@ +package main + +var a [2]string +var c [2]int + +// TODO: should stop at comparable check +func main() { + a = [2]string{"hello", "world"} + c = [2]int{1, 2} + println(a == c) +} + +// Error: +// main/files/types/eql_0f14.gno:10: cannot use [2]string as [2]int diff --git a/gnovm/tests/files/types/eql_0f15.gno b/gnovm/tests/files/types/eql_0f15.gno new file mode 100644 index 00000000000..36077127332 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f15.gno @@ -0,0 +1,17 @@ +package main + +var a [2]interface{} +var c [2]interface{} + +func gen() interface{} { + return 1 +} + +func main() { + a = [2]interface{}{gen(), gen()} + c = [2]interface{}{gen(), gen()} + println(a == c) +} + +// Output: +// true diff --git a/gnovm/tests/files/types/eql_0f16.gno b/gnovm/tests/files/types/eql_0f16.gno new file mode 100644 index 00000000000..ae0d8f45ab6 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f16.gno @@ -0,0 +1,20 @@ +package main + +type word []int + +var a [2]word +var c [2]word + +func gen() word { + return []int{1} +} + +// TODO: consider log desc +func main() { + a = [2]word{gen(), gen()} + c = [2]word{gen(), gen()} + println(a == c) +} + +// Error: +// main/files/types/eql_0f16.gno:16: [2]main.word is not comparable diff --git a/gnovm/tests/files/types/eql_0f17.gno b/gnovm/tests/files/types/eql_0f17.gno new file mode 100644 index 00000000000..d60d474f668 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f17.gno @@ -0,0 +1,13 @@ +package main + +type f func() bool + +var a f = func() bool { return true } +var b f = func() bool { return false } + +func main() { + println(a == b) +} + +// Error: +// main/files/types/eql_0f17.gno:9: main.f can only be compared to nil diff --git a/gnovm/tests/files/types/eql_0f18.gno b/gnovm/tests/files/types/eql_0f18.gno new file mode 100644 index 00000000000..ad534066fd1 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f18.gno @@ -0,0 +1,12 @@ +package main + +type f func() bool + +var a f = func() bool { return true } + +func main() { + println(a == nil) +} + +// Output: +// false diff --git a/gnovm/tests/files/types/eql_0f19.gno b/gnovm/tests/files/types/eql_0f19.gno new file mode 100644 index 00000000000..8b08d616a06 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f19.gno @@ -0,0 +1,12 @@ +package main + +type f func() bool + +var a f + +func main() { + println(a == nil) +} + +// Output: +// true diff --git a/gnovm/tests/files/types/eql_0f1_stdlibs.gno b/gnovm/tests/files/types/eql_0f1_stdlibs.gno new file mode 100644 index 00000000000..e0b8ec5c753 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f1_stdlibs.gno @@ -0,0 +1,28 @@ +package main + +import ( + "errors" + "strconv" +) + +type Error int64 + +func (e Error) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +var errCmp = errors.New("XXXX") + +// specil case: +// one is interface +func main() { + if int64(1) == errCmp { + //if errCmp == 1 { + println("what the firetruck?") + } else { + println("something else") + } +} + +// Error: +// main/files/types/eql_0f1_stdlibs.gno:19: int64 does not implement .uverse.error diff --git a/gnovm/tests/files/types/eql_0f20.gno b/gnovm/tests/files/types/eql_0f20.gno new file mode 100644 index 00000000000..f0e2e93ffc9 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f20.gno @@ -0,0 +1,13 @@ +package main + +type f func() bool + +// slice would be comparable +var a [2]f + +func main() { + println(a == nil) // rcx.T == nil +} + +// Error: +// main/files/types/eql_0f20.gno:9: [2]main.f is not comparable diff --git a/gnovm/tests/files/types/eql_0f21.gno b/gnovm/tests/files/types/eql_0f21.gno new file mode 100644 index 00000000000..20b52988a8d --- /dev/null +++ b/gnovm/tests/files/types/eql_0f21.gno @@ -0,0 +1,13 @@ +package main + +type f func() bool + +var a [2]f +var b [2]f + +func main() { + println(a == b) +} + +// Error: +// main/files/types/eql_0f21.gno:9: [2]main.f is not comparable diff --git a/gnovm/tests/files/types/eql_0f21a.gno b/gnovm/tests/files/types/eql_0f21a.gno new file mode 100644 index 00000000000..075149d264f --- /dev/null +++ b/gnovm/tests/files/types/eql_0f21a.gno @@ -0,0 +1,13 @@ +package main + +type f func() bool + +var a [2]*f +var b [2]*f + +func main() { + println(a == b) +} + +// Output: +// true diff --git a/gnovm/tests/files/types/eql_0f22.gno b/gnovm/tests/files/types/eql_0f22.gno new file mode 100644 index 00000000000..75891def307 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f22.gno @@ -0,0 +1,11 @@ +package main + +var a int = 0 +var b int = 1 + +func main() { + println(&a == &b) +} + +// Output: +// false diff --git a/gnovm/tests/files/types/eql_0f23.gno b/gnovm/tests/files/types/eql_0f23.gno new file mode 100644 index 00000000000..55f2ab5189d --- /dev/null +++ b/gnovm/tests/files/types/eql_0f23.gno @@ -0,0 +1,13 @@ +package main + +var a int = 0 +var b int = 1 + +func main() { + c := &a + d := &b + println(*c == *d) +} + +// Output: +// false diff --git a/gnovm/tests/files/types/eql_0f24.gno b/gnovm/tests/files/types/eql_0f24.gno new file mode 100644 index 00000000000..d01a9d4a14a --- /dev/null +++ b/gnovm/tests/files/types/eql_0f24.gno @@ -0,0 +1,13 @@ +package main + +var a int = 0 +var b int = 0 + +func main() { + c := &a + d := &b + println(*c == *d) +} + +// Output: +// true diff --git a/gnovm/tests/files/types/eql_0f25.gno b/gnovm/tests/files/types/eql_0f25.gno new file mode 100644 index 00000000000..8a3343146b0 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f25.gno @@ -0,0 +1,19 @@ +package main + +var a int = 0 +var b int = 0 + +func main() { + c := &a + d := &b + println(*c > *d) + println(*c >= *d) + println(*c < *d) + println(*c <= *d) +} + +// Output: +// false +// true +// false +// true diff --git a/gnovm/tests/files/types/eql_0f27_stdlibs.gno b/gnovm/tests/files/types/eql_0f27_stdlibs.gno new file mode 100644 index 00000000000..2bee791279a --- /dev/null +++ b/gnovm/tests/files/types/eql_0f27_stdlibs.gno @@ -0,0 +1,21 @@ +package main + +import ( + "errors" +) + +var errCmp1 = errors.New("XXXX") +var errCmp2 = errors.New("XXXX") + +// specil case: +// one is interface +func main() { + if errCmp1 > errCmp2 { + println("what the firetruck?") + } else { + println("something else") + } +} + +// Error: +// main/files/types/eql_0f27_stdlibs.gno:13: operator > not defined on: InterfaceKind diff --git a/gnovm/tests/files/types/eql_0f28.gno b/gnovm/tests/files/types/eql_0f28.gno new file mode 100644 index 00000000000..64f973cdde0 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f28.gno @@ -0,0 +1,31 @@ +package main + +import ( + "strconv" +) + +type E interface { + Error() string +} + +type Error1 int64 + +func (e Error1) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +type Error2 int + +func (e Error2) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +// both not const, and both interface +func main() { + var e1 E = Error1(0) + var e2 E = Error2(0) + println(e1 > e2) +} + +// Error: +// main/files/types/eql_0f28.gno:27: operator > not defined on: InterfaceKind diff --git a/gnovm/tests/files/types/eql_0f29.gno b/gnovm/tests/files/types/eql_0f29.gno new file mode 100644 index 00000000000..b5d1e897b89 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f29.gno @@ -0,0 +1,24 @@ +package main + +import ( + "strconv" +) + +type Error int64 + +func (e Error) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +// both not const, and both interface +func main() { + var l interface{} + if l > Error(0) { + println("what the firetruck?") + } else { + println("something else") + } +} + +// Error: +// main/files/types/eql_0f29.gno:16: operator > not defined on: InterfaceKind diff --git a/gnovm/tests/files/types/eql_0f2b_native.gno b/gnovm/tests/files/types/eql_0f2b_native.gno new file mode 100644 index 00000000000..fe961441328 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f2b_native.gno @@ -0,0 +1,28 @@ +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_native.gno:19: 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 new file mode 100644 index 00000000000..20de4b5b104 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f2b_stdlibs.gno @@ -0,0 +1,28 @@ +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: operator <= not defined on: InterfaceKind diff --git a/gnovm/tests/files/types/eql_0f2c_native.gno b/gnovm/tests/files/types/eql_0f2c_native.gno new file mode 100644 index 00000000000..c17c757b760 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f2c_native.gno @@ -0,0 +1,28 @@ +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_native.gno:19: 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 new file mode 100644 index 00000000000..f8da518f4a9 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f2c_stdlibs.gno @@ -0,0 +1,28 @@ +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: operator < not defined on: InterfaceKind diff --git a/gnovm/tests/files/types/eql_0f2d.gno b/gnovm/tests/files/types/eql_0f2d.gno new file mode 100644 index 00000000000..5ad121f515b --- /dev/null +++ b/gnovm/tests/files/types/eql_0f2d.gno @@ -0,0 +1,32 @@ +package main + +import ( + "fmt" + "strconv" +) + +type E interface { + Error() string +} +type Error int64 + +func (e Error) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +// special case: +// one is interface +func main() { + var e0 E + e0 = Error(0) + fmt.Printf("%T \n", e0) + if e0 == Error(0) { + println("what the firetruck?") + } else { + println("something else") + } +} + +// Output: +// int64 +// what the firetruck? diff --git a/gnovm/tests/files/types/eql_0f2e.gno b/gnovm/tests/files/types/eql_0f2e.gno new file mode 100644 index 00000000000..ea03028f5e1 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f2e.gno @@ -0,0 +1,32 @@ +package main + +import ( + "fmt" + "strconv" +) + +type E interface { + Error() string +} +type Error int64 + +func (e Error) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +// special case: +// one is interface +func main() { + var e0 E + e0 = Error(0) + fmt.Printf("%T \n", e0) + if Error(0) == e0 { + println("what the firetruck?") + } else { + println("something else") + } +} + +// Output: +// int64 +// what the firetruck? diff --git a/gnovm/tests/files/types/eql_0f30.gno b/gnovm/tests/files/types/eql_0f30.gno new file mode 100644 index 00000000000..0a9a61a96cf --- /dev/null +++ b/gnovm/tests/files/types/eql_0f30.gno @@ -0,0 +1,9 @@ +package main + +// both not const, and both interface +func main() { + println([]byte("a") == []byte("b")) +} + +// Error: +// main/files/types/eql_0f30.gno:5: []uint8 can only be compared to nil diff --git a/gnovm/tests/files/types/eql_0f30a.gno b/gnovm/tests/files/types/eql_0f30a.gno new file mode 100644 index 00000000000..2dadbd9d6d3 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f30a.gno @@ -0,0 +1,8 @@ +package main + +func main() { + println(map[string]int{"a": 1} == map[string]int{"b": 2}) +} + +// Error: +// main/files/types/eql_0f30a.gno:4: map[string]int can only be compared to nil diff --git a/gnovm/tests/files/types/eql_0f30b.gno b/gnovm/tests/files/types/eql_0f30b.gno new file mode 100644 index 00000000000..2e7a463fddd --- /dev/null +++ b/gnovm/tests/files/types/eql_0f30b.gno @@ -0,0 +1,13 @@ +package main + +type f func() + +var f1 f +var f2 f + +func main() { + println(f1 == f2) +} + +// Error: +// main/files/types/eql_0f30b.gno:9: main.f can only be compared to nil diff --git a/gnovm/tests/files/types/eql_0f30c.gno b/gnovm/tests/files/types/eql_0f30c.gno new file mode 100644 index 00000000000..ffa8c7c96e7 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f30c.gno @@ -0,0 +1,10 @@ +package main + +var f1 func() + +func main() { + println(f1 == nil) +} + +// Output: +// true diff --git a/gnovm/tests/files/types/eql_0f30d.gno b/gnovm/tests/files/types/eql_0f30d.gno new file mode 100644 index 00000000000..8b0612f011b --- /dev/null +++ b/gnovm/tests/files/types/eql_0f30d.gno @@ -0,0 +1,8 @@ +package main + +func main() { + println([]int{1} == []int{1}) +} + +// Error: +// main/files/types/eql_0f30d.gno:4: []int can only be compared to nil diff --git a/gnovm/tests/files/types/eql_0f30e.gno b/gnovm/tests/files/types/eql_0f30e.gno new file mode 100644 index 00000000000..002910072c3 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f30e.gno @@ -0,0 +1,8 @@ +package main + +func main() { + println([]int{1} == nil) +} + +// Output: +// false diff --git a/gnovm/tests/files/types/eql_0f30f.gno b/gnovm/tests/files/types/eql_0f30f.gno new file mode 100644 index 00000000000..880b081b6cd --- /dev/null +++ b/gnovm/tests/files/types/eql_0f30f.gno @@ -0,0 +1,11 @@ +package main + +var a = [2]int{1, 1} +var b = [3]int{1, 1} + +func main() { + println(a == b) +} + +// Error: +// main/files/types/eql_0f30f.gno:7: cannot use [2]int as [3]int diff --git a/gnovm/tests/files/types/eql_0f30g.gno b/gnovm/tests/files/types/eql_0f30g.gno new file mode 100644 index 00000000000..66135a83b62 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f30g.gno @@ -0,0 +1,11 @@ +package main + +var a = [2]int{1, 1} +var b = []int{1, 1} + +func main() { + println(a == b) +} + +// Error: +// main/files/types/eql_0f30g.gno:7: []int can only be compared to nil diff --git a/gnovm/tests/files/types/eql_0f31.gno b/gnovm/tests/files/types/eql_0f31.gno new file mode 100644 index 00000000000..bcb62dc76c3 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f31.gno @@ -0,0 +1,13 @@ +package main + +import "bytes" + +// both not const, and both interface +func main() { + // lv.T: * uint8, rv.T: * uint8 + cmp := bytes.Compare([]byte("a"), []byte("b")) + println(cmp) +} + +// Output: +// -1 diff --git a/gnovm/tests/files/types/eql_0f32.gno b/gnovm/tests/files/types/eql_0f32.gno new file mode 100644 index 00000000000..321824566cb --- /dev/null +++ b/gnovm/tests/files/types/eql_0f32.gno @@ -0,0 +1,8 @@ +package main + +func main() { + println([]byte("a") == nil) // lx: (const (slice[0x61] []uint8)), rx: (const (undefined)), cmp = 0 +} + +// Output: +// false diff --git a/gnovm/tests/files/types/eql_0f33.gno b/gnovm/tests/files/types/eql_0f33.gno new file mode 100644 index 00000000000..e7aac0cf069 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f33.gno @@ -0,0 +1,49 @@ +package main + +import "fmt" + +// var f func() +var a *struct{} +var b interface{} +var c map[string]int +var s []int + +func main() { + if a == nil { + fmt.Println("pointer == nil") + } else { + println("not nil!") + } + fmt.Println("----") + + if b == nil { + fmt.Println("interface == nil") + } else { + println("not nil!") + } + fmt.Println("----") + + if c == nil { + fmt.Println("map == nil") + } else { + println("not nil!") + } + fmt.Println("----") + + if s == nil { + fmt.Println("slice == nil") + } else { + println("not nil!") + } + fmt.Println("----") +} + +// Output: +// pointer == nil +// ---- +// interface == nil +// ---- +// map == nil +// ---- +// slice == nil +// ---- diff --git a/gnovm/tests/files/types/eql_0f34.gno b/gnovm/tests/files/types/eql_0f34.gno new file mode 100644 index 00000000000..8b5d3830091 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f34.gno @@ -0,0 +1,49 @@ +package main + +import "fmt" + +// var f func() +var a *struct{} +var b interface{} +var c map[string]int +var s []int + +func main() { + if nil == a { + fmt.Println("pointer == nil") + } else { + println("not nil!") + } + fmt.Println("----") + + if nil == b { + fmt.Println("interface == nil") + } else { + println("not nil!") + } + fmt.Println("----") + + if nil == c { + fmt.Println("map == nil") + } else { + println("not nil!") + } + fmt.Println("----") + + if nil == s { + fmt.Println("slice == nil") + } else { + println("not nil!") + } + fmt.Println("----") +} + +// Output: +// pointer == nil +// ---- +// interface == nil +// ---- +// map == nil +// ---- +// slice == nil +// ---- diff --git a/gnovm/tests/files/types/eql_0f35.gno b/gnovm/tests/files/types/eql_0f35.gno new file mode 100644 index 00000000000..c57d3e9d375 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f35.gno @@ -0,0 +1,34 @@ +package main + +import ( + "fmt" + "strconv" +) + +type Error0 int64 + +func (e Error0) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +type Error1 int64 + +func (e Error1) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +func main() { + defer func() { + if r := recover(); r != nil { + if r == Error1(0) { + fmt.Println("Recovered. Error:\n", r) + } + } + }() + + panic(Error1(0)) +} + +// Output: +// Recovered. Error: +// 0 diff --git a/gnovm/tests/files/types/eql_0f37.gno b/gnovm/tests/files/types/eql_0f37.gno new file mode 100644 index 00000000000..7fb051961ee --- /dev/null +++ b/gnovm/tests/files/types/eql_0f37.gno @@ -0,0 +1,39 @@ +package main + +import ( + "fmt" + "strconv" +) + +type Error0 int64 + +func (e Error0) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +type Error1 int64 + +func (e Error1) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +func get() interface{} { + return Error1(0) +} + +func main() { + defer func() { + if r := recover(); r != nil { + if r == get() { + fmt.Println("recover Error1") + } else { + fmt.Println("recover Error0") + } + } + }() + + panic(Error1(0)) +} + +// Output: +// recover Error1 diff --git a/gnovm/tests/files/types/eql_0f40_stdlibs.gno b/gnovm/tests/files/types/eql_0f40_stdlibs.gno new file mode 100644 index 00000000000..d826bdbd155 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f40_stdlibs.gno @@ -0,0 +1,41 @@ +package main + +import ( + "errors" +) + +type animal interface { + eat() +} + +type dog struct { +} + +func (d *dog) eat() { + println("dog eating") +} + +func get() animal { + d := &dog{} + return d +} + +var errCmp = errors.New("errCmp") + +// no empty interface, different interface(with different methods) +func main() { + defer func() { + if r := recover(); r != nil { + if r == errCmp { + println("same error") + } else { + println("different error") + } + } + }() + + panic(get()) +} + +// Output: +// different error diff --git a/gnovm/tests/files/types/eql_0f41_stdlibs.gno b/gnovm/tests/files/types/eql_0f41_stdlibs.gno new file mode 100644 index 00000000000..679cb1f9295 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f41_stdlibs.gno @@ -0,0 +1,35 @@ +package main + +import ( + "errors" +) + +type animal interface { + eat() +} + +type dog struct { +} + +func (d *dog) eat() { + println("dog eating") +} + +func get() animal { + d := &dog{} + return d +} + +var errCmp = errors.New("errCmp") + +// no empty interface, different interface(with different methods) +func main() { + if get() == errCmp { + println("same error") + } else { + println("different error") + } +} + +// Error: +// main/files/types/eql_0f41_stdlibs.gno:27: main.animal does not implement .uverse.error diff --git a/gnovm/tests/files/types/eql_0f42.gno b/gnovm/tests/files/types/eql_0f42.gno new file mode 100644 index 00000000000..efb63d40b97 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f42.gno @@ -0,0 +1,10 @@ +package main + +var m map[string]int + +func main() { + println(m == nil) +} + +// Output: +// true diff --git a/gnovm/tests/files/types/eql_0f43_hasNil.gno b/gnovm/tests/files/types/eql_0f43_hasNil.gno new file mode 100644 index 00000000000..c336aa92ef6 --- /dev/null +++ b/gnovm/tests/files/types/eql_0f43_hasNil.gno @@ -0,0 +1,15 @@ +package main + +import "fmt" + +func main() { + var myPointer *int + if myPointer == nil { // rcx.T == nil, <-, after conversion lv: (nil *int), rv: (nil *int) + fmt.Println("Pointer is nil") + } else { + fmt.Println("Pointer is not nil") + } +} + +// Output: +// Pointer is nil diff --git a/gnovm/tests/files/types/eql_0f44.gno b/gnovm/tests/files/types/eql_0f44.gno new file mode 100644 index 00000000000..e23e6c07aba --- /dev/null +++ b/gnovm/tests/files/types/eql_0f44.gno @@ -0,0 +1,16 @@ +package main + +import "fmt" + +type f func() + +func main() { + if f(nil) == nil { // rcx.T == nil + fmt.Println("func is nil") + } else { + fmt.Println("func is not nil") + } +} + +// Output: +// func is nil diff --git a/gnovm/tests/files/types/eql_0f45.gno b/gnovm/tests/files/types/eql_0f45.gno new file mode 100644 index 00000000000..7707136b82e --- /dev/null +++ b/gnovm/tests/files/types/eql_0f45.gno @@ -0,0 +1,14 @@ +package main + +import "fmt" + +func main() { + if func() {} == nil { + fmt.Println("func is nil") + } else { + fmt.Println("func is not nil") + } +} + +// Output: +// func is not nil diff --git a/gnovm/tests/files/types/eql_0f46.gno b/gnovm/tests/files/types/eql_0f46.gno new file mode 100644 index 00000000000..3a65bd18b3b --- /dev/null +++ b/gnovm/tests/files/types/eql_0f46.gno @@ -0,0 +1,18 @@ +package main + +import "fmt" + +type m map[string]int + +func main() { + // m(nil) is a conversion from nil to m, whose underlying type is map[string]int + // lv: (nil main.m), rv: (undefined) + if m(nil) == nil { + fmt.Println("m is nil") + } else { + fmt.Println("m is not nil") + } +} + +// Output: +// m is nil diff --git a/gnovm/tests/files/types/eql_0f46a.gno b/gnovm/tests/files/types/eql_0f46a.gno new file mode 100644 index 00000000000..9476b1f22ef --- /dev/null +++ b/gnovm/tests/files/types/eql_0f46a.gno @@ -0,0 +1,18 @@ +package main + +import "fmt" + +type s []int + +func main() { + // s(nil) is a conversion from nil to s, whose underlying type is []int + // lv: (nil main.s), rv: (undefined) + if s(nil) == nil { + fmt.Println("s is nil") + } else { + fmt.Println("s is not nil") + } +} + +// Output: +// s is nil diff --git a/gnovm/tests/files/types/eql_0f48.gno b/gnovm/tests/files/types/eql_0f48.gno new file mode 100644 index 00000000000..fbf4f164c9d --- /dev/null +++ b/gnovm/tests/files/types/eql_0f48.gno @@ -0,0 +1,22 @@ +package main + +func testEql(want, got interface{}) { + if want != got { + println(false) + } else { + println(true) + } +} + +// return var of error nil +func gen() error { + return nil +} + +func main() { // about untyped nil to (interface{})typed-nil, no support for native for now. + r := gen() + testEql(r, error(nil)) +} + +// Output: +// true diff --git a/gnovm/tests/files/types/eql_0f8_stdlibs.gno b/gnovm/tests/files/types/eql_0f8_stdlibs.gno new file mode 100644 index 00000000000..400ce3c6d1e --- /dev/null +++ b/gnovm/tests/files/types/eql_0f8_stdlibs.gno @@ -0,0 +1,27 @@ +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 errCmp == int64(1) { + println("what the firetruck?") + } else { + println("something else") + } +} + +// Error: +// main/files/types/eql_0f8_stdlibs.gno:19: int64 does not implement .uverse.error diff --git a/gnovm/tests/files/types/eql_iface.gno b/gnovm/tests/files/types/eql_iface.gno new file mode 100644 index 00000000000..d3d41348a62 --- /dev/null +++ b/gnovm/tests/files/types/eql_iface.gno @@ -0,0 +1,13 @@ +package main + +type Foo struct { + n int +} + +func main() { + var l interface{} = 1 + println(1 == l) +} + +// Output: +// true diff --git a/gnovm/tests/files/types/incdec_a0.gno b/gnovm/tests/files/types/incdec_a0.gno new file mode 100644 index 00000000000..8f3f97caaee --- /dev/null +++ b/gnovm/tests/files/types/incdec_a0.gno @@ -0,0 +1,10 @@ +package main + +func main() { + a := 1 + a++ + println(a) +} + +// Output: +// 2 diff --git a/gnovm/tests/files/types/incdec_a1.gno b/gnovm/tests/files/types/incdec_a1.gno new file mode 100644 index 00000000000..ee9d2b18c7e --- /dev/null +++ b/gnovm/tests/files/types/incdec_a1.gno @@ -0,0 +1,10 @@ +package main + +func main() { + a := "hello" + a++ + println(a) +} + +// Error: +// main/files/types/incdec_a1.gno:5: operator ++ not defined on: StringKind diff --git a/gnovm/tests/files/types/incdec_a2.gno b/gnovm/tests/files/types/incdec_a2.gno new file mode 100644 index 00000000000..a91080a0d95 --- /dev/null +++ b/gnovm/tests/files/types/incdec_a2.gno @@ -0,0 +1,29 @@ +package main + +func main() { + a := int(1) + a++ + println(a) + + // TODO: no support for now + //b := 1.0 + //b++ + //println(b) + // + //c := float32(1.0) + //c++ + //println(c) + + d := 'a' + d++ + println(d) + + e := uint(1) + e++ + println(e) +} + +// Output: +// 2 +// 98 +// 2 diff --git a/gnovm/tests/files/types/incdec_a3.gno b/gnovm/tests/files/types/incdec_a3.gno new file mode 100644 index 00000000000..f257b822f8e --- /dev/null +++ b/gnovm/tests/files/types/incdec_a3.gno @@ -0,0 +1,19 @@ +package main + +type Int int8 + +func (i Int) Inc() Int { + i++ + return i +} + +// right is typed const, can not use as its correspondence declared type +func main() { + var a Int + a = Int(int8(0)) + a = a.Inc() + println(a) +} + +// Output: +// (1 main.Int) diff --git a/gnovm/tests/files/types/incdec_a4.gno b/gnovm/tests/files/types/incdec_a4.gno new file mode 100644 index 00000000000..5f87eb5c09c --- /dev/null +++ b/gnovm/tests/files/types/incdec_a4.gno @@ -0,0 +1,18 @@ +package main + +type Int int8 + +func (i Int) Inc() { + i++ +} + +// right is typed const, can not use as its correspondence declared type +func main() { + var a Int + a = int8(0) + a.Inc() + println(a) +} + +// Error: +// main/files/types/incdec_a4.gno:12: cannot use int8 as main.Int without explicit conversion diff --git a/gnovm/tests/files/types/nil.gno b/gnovm/tests/files/types/nil.gno new file mode 100644 index 00000000000..a836d328dfe --- /dev/null +++ b/gnovm/tests/files/types/nil.gno @@ -0,0 +1,9 @@ +package main + +func main() { + + println(nil + nil) +} + +// Error: +// main/files/types/nil.gno:5: operator + not defined on: nil diff --git a/gnovm/tests/files/types/or_a0.gno b/gnovm/tests/files/types/or_a0.gno new file mode 100644 index 00000000000..b2f14c73785 --- /dev/null +++ b/gnovm/tests/files/types/or_a0.gno @@ -0,0 +1,9 @@ +package main + +// both typed(different) const +func main() { + println(int(0) | int8(1)) +} + +// Error: +// main/files/types/or_a0.gno:5: invalid operation: mismatched types int and int8 diff --git a/gnovm/tests/files/types/or_a1.gno b/gnovm/tests/files/types/or_a1.gno new file mode 100644 index 00000000000..c2e7bff0d5d --- /dev/null +++ b/gnovm/tests/files/types/or_a1.gno @@ -0,0 +1,25 @@ +package main + +import ( + "strconv" +) + +type Error1 int64 + +func (e Error1) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +type Error2 int + +func (e Error2) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +// both typed(different) const +func main() { + println(Error1(0) | Error2(0)) +} + +// Error: +// main/files/types/or_a1.gno:21: invalid operation: mismatched types main.Error1 and main.Error2 diff --git a/gnovm/tests/files/types/or_b0.gno b/gnovm/tests/files/types/or_b0.gno new file mode 100644 index 00000000000..655121ee2f2 --- /dev/null +++ b/gnovm/tests/files/types/or_b0.gno @@ -0,0 +1,13 @@ +package main + +type Error int8 + +// one untyped const, one typed const +func main() { + println(1 | Error(1)) + println(Error(1) | 1) +} + +// Output: +// (1 main.Error) +// (1 main.Error) diff --git a/gnovm/tests/files/types/or_b1.gno b/gnovm/tests/files/types/or_b1.gno new file mode 100644 index 00000000000..1616c4db687 --- /dev/null +++ b/gnovm/tests/files/types/or_b1.gno @@ -0,0 +1,21 @@ +package main + +import ( + "strconv" +) + +type Error int8 + +func (e Error) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +// one untyped const, one typed const +func main() { + println(1 | Error(1)) + println(Error(1) | 1) +} + +// Output: +// error: 1 +// error: 1 diff --git a/gnovm/tests/files/types/or_b2.gno b/gnovm/tests/files/types/or_b2.gno new file mode 100644 index 00000000000..0bdf3e0a73a --- /dev/null +++ b/gnovm/tests/files/types/or_b2.gno @@ -0,0 +1,9 @@ +package main + +// one untyped const, one typed const +func main() { + println(1 | "a") +} + +// Error: +// main/files/types/or_b2.gno:5: operator | not defined on: StringKind diff --git a/gnovm/tests/files/types/or_b3.gno b/gnovm/tests/files/types/or_b3.gno new file mode 100644 index 00000000000..4b6f20530cd --- /dev/null +++ b/gnovm/tests/files/types/or_b3.gno @@ -0,0 +1,9 @@ +package main + +// one untyped const, one typed const +func main() { + println("b" | "a") +} + +// Error: +// main/files/types/or_b3.gno:5: operator | not defined on: StringKind diff --git a/gnovm/tests/files/types/or_b4.gno b/gnovm/tests/files/types/or_b4.gno new file mode 100644 index 00000000000..54c50033ad1 --- /dev/null +++ b/gnovm/tests/files/types/or_b4.gno @@ -0,0 +1,9 @@ +package main + +// one untyped const, one typed const +func main() { + println(1 | 'a') +} + +// Output: +// 97 diff --git a/gnovm/tests/files/types/or_d0.gno b/gnovm/tests/files/types/or_d0.gno new file mode 100644 index 00000000000..827e29a8fba --- /dev/null +++ b/gnovm/tests/files/types/or_d0.gno @@ -0,0 +1,10 @@ +package main + +// both untyped const +// TODO: dec value representation, and this should happen in process stage!!! +func main() { + println(1.0 | 1) +} + +// Error: +// main/files/types/or_d0.gno:6: operator | not defined on: BigdecKind diff --git a/gnovm/tests/files/types/or_d1.gno b/gnovm/tests/files/types/or_d1.gno new file mode 100644 index 00000000000..ac2458a96a2 --- /dev/null +++ b/gnovm/tests/files/types/or_d1.gno @@ -0,0 +1,10 @@ +package main + +// both untyped const +// TODO: dec value representation +func main() { + println('a' | 'b') +} + +// Output: +// 99 diff --git a/gnovm/tests/files/types/or_d2.gno b/gnovm/tests/files/types/or_d2.gno new file mode 100644 index 00000000000..0812ff926e7 --- /dev/null +++ b/gnovm/tests/files/types/or_d2.gno @@ -0,0 +1,13 @@ +package main + +// both untyped const +// TODO: dec value representation +var r rune + +func main() { + r = 'a' + println(r | 'b') +} + +// Output: +// 99 diff --git a/gnovm/tests/files/types/or_d3.gno b/gnovm/tests/files/types/or_d3.gno new file mode 100644 index 00000000000..226c9423da5 --- /dev/null +++ b/gnovm/tests/files/types/or_d3.gno @@ -0,0 +1,15 @@ +package main + +// both untyped const +// TODO: dec value representation +var r1 rune +var r2 rune + +func main() { + r1 = 'a' + r2 = 'b' + println(r1 | r2) +} + +// Output: +// 99 diff --git a/gnovm/tests/files/types/or_d4.gno b/gnovm/tests/files/types/or_d4.gno new file mode 100644 index 00000000000..f1240f32c71 --- /dev/null +++ b/gnovm/tests/files/types/or_d4.gno @@ -0,0 +1,10 @@ +package main + +// both untyped const +// TODO: dec value representation +func main() { + println(1.0 | 0) +} + +// Error: +// main/files/types/or_d4.gno:6: operator | not defined on: BigdecKind diff --git a/gnovm/tests/files/types/or_e0.gno b/gnovm/tests/files/types/or_e0.gno new file mode 100644 index 00000000000..555ee5ad37b --- /dev/null +++ b/gnovm/tests/files/types/or_e0.gno @@ -0,0 +1,27 @@ +package main + +import ( + "strconv" +) + +type Error1 int64 + +func (e Error1) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +type Error2 int + +func (e Error2) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +// both not const, typed +func main() { + var e1 Error1 = Error1(0) + var e2 Error2 = Error2(0) + println(e1 | e2) +} + +// Error: +// main/files/types/or_e0.gno:23: invalid operation: mismatched types main.Error1 and main.Error2 diff --git a/gnovm/tests/files/types/or_f0_stdlibs.gno b/gnovm/tests/files/types/or_f0_stdlibs.gno new file mode 100644 index 00000000000..d2a080e0bab --- /dev/null +++ b/gnovm/tests/files/types/or_f0_stdlibs.gno @@ -0,0 +1,23 @@ +package main + +import ( + "errors" + "strconv" +) + +type Error int64 + +func (e Error) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +var errCmp = errors.New("XXXX") + +// specil case: +// one is interface +func main() { + println(1 | errCmp) +} + +// Error: +// main/files/types/or_f0_stdlibs.gno:19: operator | not defined on: InterfaceKind diff --git a/gnovm/tests/files/types/or_f1_stdlibs.gno b/gnovm/tests/files/types/or_f1_stdlibs.gno new file mode 100644 index 00000000000..9cc9eb5157b --- /dev/null +++ b/gnovm/tests/files/types/or_f1_stdlibs.gno @@ -0,0 +1,23 @@ +package main + +import ( + "errors" + "strconv" +) + +type Error int64 + +func (e Error) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +var errCmp = errors.New("XXXX") + +// specil case: +// one is interface +func main() { + println(Error(0) | errCmp) +} + +// Error: +// main/files/types/or_f1_stdlibs.gno:19: operator | not defined on: InterfaceKind diff --git a/gnovm/tests/files/types/or_f2.gno b/gnovm/tests/files/types/or_f2.gno new file mode 100644 index 00000000000..284078f41d4 --- /dev/null +++ b/gnovm/tests/files/types/or_f2.gno @@ -0,0 +1,31 @@ +package main + +import ( + "strconv" +) + +type E interface { + Error() string +} + +type Error1 int64 + +func (e Error1) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +type Error2 int + +func (e Error2) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +// both not const, and both interface +func main() { + var e1 E = Error1(0) + var e2 E = Error2(0) + println(e1 | e2) +} + +// Error: +// main/files/types/or_f2.gno:27: operator | not defined on: InterfaceKind diff --git a/gnovm/tests/files/types/overflow_a0.gno b/gnovm/tests/files/types/overflow_a0.gno new file mode 100644 index 00000000000..e3ecefd621f --- /dev/null +++ b/gnovm/tests/files/types/overflow_a0.gno @@ -0,0 +1,11 @@ +package main + +import "fmt" + +func main() { + const Huge = 1e1000 + fmt.Println(Huge / 1e999) +} + +// Output: +// 10 diff --git a/gnovm/tests/files/types/overflow_a1.gno b/gnovm/tests/files/types/overflow_a1.gno new file mode 100644 index 00000000000..bd893312cf2 --- /dev/null +++ b/gnovm/tests/files/types/overflow_a1.gno @@ -0,0 +1,9 @@ +package main + +func main() { + const Huge = 1e1000 + println(Huge) +} + +// Error: +// cannot convert untyped bigdec to float64: strconv.ParseFloat: parsing "1E+1000": value out of range diff --git a/gnovm/tests/files/types/rem_a0.gno b/gnovm/tests/files/types/rem_a0.gno new file mode 100644 index 00000000000..7ba0f66f533 --- /dev/null +++ b/gnovm/tests/files/types/rem_a0.gno @@ -0,0 +1,9 @@ +package main + +// both typed(different) const +func main() { + println(int(1) % int8(1)) +} + +// Error: +// main/files/types/rem_a0.gno:5: invalid operation: mismatched types int and int8 diff --git a/gnovm/tests/files/types/rem_a1.gno b/gnovm/tests/files/types/rem_a1.gno new file mode 100644 index 00000000000..edea73382ce --- /dev/null +++ b/gnovm/tests/files/types/rem_a1.gno @@ -0,0 +1,25 @@ +package main + +import ( + "strconv" +) + +type Error1 int64 + +func (e Error1) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +type Error2 int + +func (e Error2) Error() string { + return "error: " + strconv.Itoa(int(e)) +} + +// both typed(different) const +func main() { + println(Error1(0) % Error2(0)) +} + +// Error: +// main/files/types/rem_a1.gno:21: invalid operation: mismatched types main.Error1 and main.Error2 diff --git a/gnovm/tests/files/types/rem_a2.gno b/gnovm/tests/files/types/rem_a2.gno new file mode 100644 index 00000000000..3ebba194c0a --- /dev/null +++ b/gnovm/tests/files/types/rem_a2.gno @@ -0,0 +1,8 @@ +package main + +func main() { + println(1 % 0) +} + +// Error: +// main/files/types/rem_a2.gno:4: invalid operation: division by zero diff --git a/gnovm/tests/files/types/rem_b0.gno b/gnovm/tests/files/types/rem_b0.gno new file mode 100644 index 00000000000..9b5f82fb3b7 --- /dev/null +++ b/gnovm/tests/files/types/rem_b0.gno @@ -0,0 +1,13 @@ +package main + +type Error int8 + +// one untyped const, one typed const +func main() { + println(1 % Error(1)) + println(Error(1) % 1) +} + +// Output: +// (0 main.Error) +// (0 main.Error) diff --git a/gnovm/tests/files/types/rem_f3.gno b/gnovm/tests/files/types/rem_f3.gno new file mode 100644 index 00000000000..e34dfab9ae0 --- /dev/null +++ b/gnovm/tests/files/types/rem_f3.gno @@ -0,0 +1,9 @@ +package main + +func main() { + a := 1 + println(a % 0) +} + +// Error: +// main/files/types/rem_f3.gno:5: invalid operation: division by zero diff --git a/gnovm/tests/files/types/runtime_a0.gno b/gnovm/tests/files/types/runtime_a0.gno new file mode 100644 index 00000000000..7d6312e5b9c --- /dev/null +++ b/gnovm/tests/files/types/runtime_a0.gno @@ -0,0 +1,9 @@ +package main + +func main() { + m := map[string]bool{"foo": true} + m["foo"]++ +} + +// Error: +// main/files/types/runtime_a0.gno:5: operator ++ not defined on: BoolKind diff --git a/gnovm/tests/files/types/runtime_a0a.gno b/gnovm/tests/files/types/runtime_a0a.gno new file mode 100644 index 00000000000..fecce01817e --- /dev/null +++ b/gnovm/tests/files/types/runtime_a0a.gno @@ -0,0 +1,8 @@ +package main + +func main() { + println(map[string]bool{"foo": true}["foo"] == true) +} + +// Output: +// true diff --git a/gnovm/tests/files/types/runtime_a2.gno b/gnovm/tests/files/types/runtime_a2.gno new file mode 100644 index 00000000000..d53c9c4d970 --- /dev/null +++ b/gnovm/tests/files/types/runtime_a2.gno @@ -0,0 +1,15 @@ +package main + +import "fmt" + +func gen() interface{} { + return false +} + +func main() { + r := gen() + fmt.Printf("%T \n", r) +} + +// Output: +// bool diff --git a/gnovm/tests/files/types/runtime_a3.gno b/gnovm/tests/files/types/runtime_a3.gno new file mode 100644 index 00000000000..d46a83a1c35 --- /dev/null +++ b/gnovm/tests/files/types/runtime_a3.gno @@ -0,0 +1,12 @@ +package main + +func gen() interface{} { + return false +} + +func main() { + gen()++ +} + +// Error: +// main/files/types/runtime_a3.gno:8: operator ++ not defined on: InterfaceKind diff --git a/gnovm/tests/files/types/shift_a0.gno b/gnovm/tests/files/types/shift_a0.gno new file mode 100644 index 00000000000..a31e7bd8f82 --- /dev/null +++ b/gnovm/tests/files/types/shift_a0.gno @@ -0,0 +1,11 @@ +package main + +// both typed(different) const +func main() { + println(int(1) << int(1)) + println(int(1) >> int(1)) +} + +// Output: +// 2 +// 0 diff --git a/gnovm/tests/files/types/shift_a1.gno b/gnovm/tests/files/types/shift_a1.gno new file mode 100644 index 00000000000..ba92bf0f7d2 --- /dev/null +++ b/gnovm/tests/files/types/shift_a1.gno @@ -0,0 +1,11 @@ +package main + +// both typed(different) const +func main() { + println(int(1) << int8(1)) + println(int(1) >> int8(1)) +} + +// Output: +// 2 +// 0 diff --git a/gnovm/tests/files/types/shift_a10.gno b/gnovm/tests/files/types/shift_a10.gno new file mode 100644 index 00000000000..0eba81f4a9c --- /dev/null +++ b/gnovm/tests/files/types/shift_a10.gno @@ -0,0 +1,10 @@ +package main + +func main() { + a := 1 + r := a << 1.0 // NOTE: go vet would fail, but still process + println(r) +} + +// Output: +// 2 diff --git a/gnovm/tests/files/types/shift_a11.gno b/gnovm/tests/files/types/shift_a11.gno new file mode 100644 index 00000000000..2d3c935e97d --- /dev/null +++ b/gnovm/tests/files/types/shift_a11.gno @@ -0,0 +1,10 @@ +package main + +func main() { + a := 1 + r := a << "hello" // NOTE: go vet would fail, but still process + println(r) +} + +// Error: +// main/files/types/shift_a11.gno:3: cannot convert StringKind to UintKind diff --git a/gnovm/tests/files/types/shift_a12.gno b/gnovm/tests/files/types/shift_a12.gno new file mode 100644 index 00000000000..5735854d684 --- /dev/null +++ b/gnovm/tests/files/types/shift_a12.gno @@ -0,0 +1,14 @@ +package main + +import "fmt" + +func main() { + a := uint(1) + r := uint64(1) << a + println(r) + fmt.Printf("%T \n", r) +} + +// Output: +// 2 +// uint64 diff --git a/gnovm/tests/files/types/shift_a13.gno b/gnovm/tests/files/types/shift_a13.gno new file mode 100644 index 00000000000..7d70cc3589a --- /dev/null +++ b/gnovm/tests/files/types/shift_a13.gno @@ -0,0 +1,13 @@ +package main + +import "fmt" + +func main() { + var r uint64 = 1 << int8(1) + fmt.Printf("%T \n", r) + println(r) +} + +// Output: +// uint64 +// 2 diff --git a/gnovm/tests/files/types/shift_a14.gno b/gnovm/tests/files/types/shift_a14.gno new file mode 100644 index 00000000000..f879da5f3f9 --- /dev/null +++ b/gnovm/tests/files/types/shift_a14.gno @@ -0,0 +1,10 @@ +package main + +func main() { + a := "hello" + r := a << 1 + println(r) +} + +// Error: +// main/files/types/shift_a14.gno:5: operator << not defined on: StringKind diff --git a/gnovm/tests/files/types/shift_a15.gno b/gnovm/tests/files/types/shift_a15.gno new file mode 100644 index 00000000000..b86445f3e75 --- /dev/null +++ b/gnovm/tests/files/types/shift_a15.gno @@ -0,0 +1,10 @@ +package main + +// TODO: fix in assignment +func main() { + a := "hello" + a <<= 1 +} + +// Error: +// main/files/types/shift_a15.gno:6: operator <<= not defined on: StringKind diff --git a/gnovm/tests/files/types/shift_a16.gno b/gnovm/tests/files/types/shift_a16.gno new file mode 100644 index 00000000000..ed41f35f20d --- /dev/null +++ b/gnovm/tests/files/types/shift_a16.gno @@ -0,0 +1,9 @@ +package main + +func main() { + r := "hello" << 1 + println(r) +} + +// Error: +// main/files/types/shift_a16.gno:4: operator << not defined on: StringKind diff --git a/gnovm/tests/files/types/shift_a2.gno b/gnovm/tests/files/types/shift_a2.gno new file mode 100644 index 00000000000..91072929306 --- /dev/null +++ b/gnovm/tests/files/types/shift_a2.gno @@ -0,0 +1,11 @@ +package main + +// both typed(different) const +func main() { + println(1 << int(1)) + println(1 >> int(1)) +} + +// Output: +// 2 +// 0 diff --git a/gnovm/tests/files/types/shift_a3.gno b/gnovm/tests/files/types/shift_a3.gno new file mode 100644 index 00000000000..79d5c566f1d --- /dev/null +++ b/gnovm/tests/files/types/shift_a3.gno @@ -0,0 +1,10 @@ +package main + +// both typed(different) const +func main() { + println(1 << 'a') + println(1 >> 'a') +} + +// Error: +// main/files/types/shift_a3.gno:5: bigint overflows target kind diff --git a/gnovm/tests/files/types/shift_a4.gno b/gnovm/tests/files/types/shift_a4.gno new file mode 100644 index 00000000000..3561929b672 --- /dev/null +++ b/gnovm/tests/files/types/shift_a4.gno @@ -0,0 +1,11 @@ +package main + +// both typed(different) const +func main() { + println(1 << 1.0) + println(1 >> 1.0) +} + +// Output: +// 2 +// 0 diff --git a/gnovm/tests/files/types/shift_a5.gno b/gnovm/tests/files/types/shift_a5.gno new file mode 100644 index 00000000000..0394f8a2f3f --- /dev/null +++ b/gnovm/tests/files/types/shift_a5.gno @@ -0,0 +1,10 @@ +package main + +// TODO: support this? +func main() { + println(1.0 << 1) + println(1.0 >> 1) +} + +// Error: +// main/files/types/shift_a5.gno:5: operator << not defined on: BigdecKind diff --git a/gnovm/tests/files/types/shift_a6.gno b/gnovm/tests/files/types/shift_a6.gno new file mode 100644 index 00000000000..03ad4c0bcea --- /dev/null +++ b/gnovm/tests/files/types/shift_a6.gno @@ -0,0 +1,14 @@ +package main + +func main() { + r := int(1) + println(r << 5) + println(r) + r <<= 5 + println(r) +} + +// Output: +// 32 +// 1 +// 32 diff --git a/gnovm/tests/files/types/shift_a7.gno b/gnovm/tests/files/types/shift_a7.gno new file mode 100644 index 00000000000..5cad6897a21 --- /dev/null +++ b/gnovm/tests/files/types/shift_a7.gno @@ -0,0 +1,9 @@ +package main + +func main() { + r := int(1) + println(r << "a") +} + +// Error: +// main/files/types/shift_a7.gno:3: cannot convert StringKind to UintKind diff --git a/gnovm/tests/files/types/shift_a8.gno b/gnovm/tests/files/types/shift_a8.gno new file mode 100644 index 00000000000..a7a3e1f07d9 --- /dev/null +++ b/gnovm/tests/files/types/shift_a8.gno @@ -0,0 +1,10 @@ +package main + +func main() { + a := 1 + r := a << 1 + println(r) +} + +// Output: +// 2 diff --git a/gnovm/tests/files/types/shift_a9.gno b/gnovm/tests/files/types/shift_a9.gno new file mode 100644 index 00000000000..f998381ef1c --- /dev/null +++ b/gnovm/tests/files/types/shift_a9.gno @@ -0,0 +1,10 @@ +package main + +func main() { + a := 1 + a <<= 1 + println(a) +} + +// Output: +// 2 diff --git a/gnovm/tests/files/types/time_native.gno b/gnovm/tests/files/types/time_native.gno new file mode 100644 index 00000000000..ef1c3daaf44 --- /dev/null +++ b/gnovm/tests/files/types/time_native.gno @@ -0,0 +1,13 @@ +package main + +import ( + "fmt" + "time" +) + +func main() { + fmt.Println(time.Second + time.Second) +} + +// Output: +// 2s diff --git a/gnovm/tests/files/types/unary_a0.gno b/gnovm/tests/files/types/unary_a0.gno new file mode 100644 index 00000000000..2e5a1d289c9 --- /dev/null +++ b/gnovm/tests/files/types/unary_a0.gno @@ -0,0 +1,12 @@ +package main + +import "fmt" + +func main() { + x := 5 + y := +x + fmt.Println(y) // Output: 5 +} + +// Output: +// 5 diff --git a/gnovm/tests/files/types/unary_a0a.gno b/gnovm/tests/files/types/unary_a0a.gno new file mode 100644 index 00000000000..3556bcf2bf6 --- /dev/null +++ b/gnovm/tests/files/types/unary_a0a.gno @@ -0,0 +1,12 @@ +package main + +import "fmt" + +func main() { + x := 1.0 + y := +x + fmt.Println(y) +} + +// Output: +// 1 diff --git a/gnovm/tests/files/types/unary_a0b.gno b/gnovm/tests/files/types/unary_a0b.gno new file mode 100644 index 00000000000..ff4e863e10e --- /dev/null +++ b/gnovm/tests/files/types/unary_a0b.gno @@ -0,0 +1,12 @@ +package main + +import "fmt" + +func main() { + x := int(1) + y := +x + fmt.Println(y) +} + +// Output: +// 1 diff --git a/gnovm/tests/files/types/unary_a0c.gno b/gnovm/tests/files/types/unary_a0c.gno new file mode 100644 index 00000000000..9f1fa057357 --- /dev/null +++ b/gnovm/tests/files/types/unary_a0c.gno @@ -0,0 +1,12 @@ +package main + +import "fmt" + +func main() { + x := "hello" + y := +x + fmt.Println(y) +} + +// Error: +// main/files/types/unary_a0c.gno:7: operator + not defined on: StringKind diff --git a/gnovm/tests/files/types/unary_a1.gno b/gnovm/tests/files/types/unary_a1.gno new file mode 100644 index 00000000000..c3c96d6724e --- /dev/null +++ b/gnovm/tests/files/types/unary_a1.gno @@ -0,0 +1,13 @@ +package main + +import "fmt" + +func main() { + x := 5 + y := -x + fmt.Println(y) // Output: -5 + +} + +// Output: +// -5 diff --git a/gnovm/tests/files/types/unary_a2.gno b/gnovm/tests/files/types/unary_a2.gno new file mode 100644 index 00000000000..1b9edf1b712 --- /dev/null +++ b/gnovm/tests/files/types/unary_a2.gno @@ -0,0 +1,13 @@ +package main + +import "fmt" + +func main() { + a := true + b := !a + fmt.Println(b) // Output: false + +} + +// Output: +// false diff --git a/gnovm/tests/files/types/unary_a2a.gno b/gnovm/tests/files/types/unary_a2a.gno new file mode 100644 index 00000000000..9081ad64ebf --- /dev/null +++ b/gnovm/tests/files/types/unary_a2a.gno @@ -0,0 +1,13 @@ +package main + +import "fmt" + +func main() { + a := 1 + b := !a + fmt.Println(b) // Output: false + +} + +// Error: +// main/files/types/unary_a2a.gno:7: operator ! not defined on: IntKind diff --git a/gnovm/tests/files/types/unary_a3.gno b/gnovm/tests/files/types/unary_a3.gno new file mode 100644 index 00000000000..3f216ccb1c1 --- /dev/null +++ b/gnovm/tests/files/types/unary_a3.gno @@ -0,0 +1,12 @@ +package main + +import "fmt" + +func main() { + x := 10 + p := &x + fmt.Println(*p) // Output: 10 +} + +// Output: +// 10 diff --git a/gnovm/tests/files/types/unary_a4.gno b/gnovm/tests/files/types/unary_a4.gno new file mode 100644 index 00000000000..caaf1ed0438 --- /dev/null +++ b/gnovm/tests/files/types/unary_a4.gno @@ -0,0 +1,10 @@ +package main + +func main() { + v := 42 + p := &v + println(v == *p) +} + +// Output: +// true diff --git a/gnovm/tests/files/types/unary_a5.gno b/gnovm/tests/files/types/unary_a5.gno new file mode 100644 index 00000000000..bb956f85673 --- /dev/null +++ b/gnovm/tests/files/types/unary_a5.gno @@ -0,0 +1,12 @@ +package main + +import "fmt" + +func main() { + x := 1 + y := ^x + fmt.Println(y) // Output will be the bitwise complement of 1 +} + +// Output: +// -2 diff --git a/gnovm/tests/files/types/unary_a6.gno b/gnovm/tests/files/types/unary_a6.gno new file mode 100644 index 00000000000..99e87b66683 --- /dev/null +++ b/gnovm/tests/files/types/unary_a6.gno @@ -0,0 +1,12 @@ +package main + +import "fmt" + +func main() { + x := 1.0 + y := ^x + fmt.Println(y) // Output will be the bitwise complement of 1 +} + +// Error: +// main/files/types/unary_a6.gno:7: operator ^ not defined on: Float64Kind diff --git a/gnovm/tests/files/var18.gno b/gnovm/tests/files/var18.gno index 9fc0cba3053..f2f97f62b15 100644 --- a/gnovm/tests/files/var18.gno +++ b/gnovm/tests/files/var18.gno @@ -5,4 +5,4 @@ func main() { } // Error: -// main/files/var18.gno:4: should not happen +// main/files/var18.gno:4: assignment mismatch: 3 variables but 2 values diff --git a/gnovm/tests/files/zrealm12.gno b/gnovm/tests/files/zrealm12_stdlibs.gno similarity index 90% rename from gnovm/tests/files/zrealm12.gno rename to gnovm/tests/files/zrealm12_stdlibs.gno index ee9e85d827b..d201ecc1470 100644 --- a/gnovm/tests/files/zrealm12.gno +++ b/gnovm/tests/files/zrealm12_stdlibs.gno @@ -2,6 +2,7 @@ package test import ( + "gno.land/r/demo/tests" "std" ) @@ -23,3 +24,6 @@ func main() { panic("should not happen") } } + +// Output: +// second's child diff --git a/gnovm/tests/files/zrealm3.gno b/gnovm/tests/files/zrealm3.gno index da8a581375c..8e2b9827322 100644 --- a/gnovm/tests/files/zrealm3.gno +++ b/gnovm/tests/files/zrealm3.gno @@ -25,7 +25,7 @@ func main() { // Realm: // switchrealm["gno.land/r/test"] -// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:5]={ +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:7]={ // "Fields": [ // { // "T": { @@ -57,10 +57,29 @@ func main() { // } // ], // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6", +// "RefCount": "1" +// } +// } +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:6]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6", // "ModTime": "0", // "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", // "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/test.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "567a18d9c7594ece7956ce54384b0858888bb834", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7" +// } // } // } // u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ @@ -68,7 +87,7 @@ func main() { // "ObjectInfo": { // "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", // "IsEscaped": true, -// "ModTime": "4", +// "ModTime": "5", // "RefCount": "2" // }, // "Parent": null, @@ -238,21 +257,16 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "8197b7c5b4f2c7bf9c12b1c614f6b4dc6e7ce8dd", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/test.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "b1d00c9606ffbb00b2aa3d475c5a390514f6dc14", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5" -// } -// } +// "TV": null // } // } // ] // } // d[a8ada09dee16d791fd406d629fe29bb0ed084a30:4] +// d[a8ada09dee16d791fd406d629fe29bb0ed084a30:5] diff --git a/gnovm/tests/files/zrealm4.gno b/gnovm/tests/files/zrealm4.gno index 4f5254b6951..8a4bac1fe32 100644 --- a/gnovm/tests/files/zrealm4.gno +++ b/gnovm/tests/files/zrealm4.gno @@ -23,7 +23,7 @@ func main() { // Realm: // switchrealm["gno.land/r/test"] -// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:4]={ +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:5]={ // "Fields": [ // { // "T": { @@ -72,9 +72,9 @@ func main() { // } // ], // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", -// "ModTime": "4", -// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", +// "ModTime": "5", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", // "RefCount": "1" // } // } @@ -83,7 +83,7 @@ func main() { // "ObjectInfo": { // "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", // "IsEscaped": true, -// "ModTime": "4", +// "ModTime": "5", // "RefCount": "2" // }, // "Parent": null, @@ -180,19 +180,13 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "7b9d58f40430bbbcbafd47eefb7a6dd342477f71", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "de0c4b2dd935220f7d37d10fc9feb1448bfb011d", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" -// } -// } +// "TV": null // } // } // ] diff --git a/gnovm/tests/files/zrealm5.gno b/gnovm/tests/files/zrealm5.gno index ebe107290e7..108483a435c 100644 --- a/gnovm/tests/files/zrealm5.gno +++ b/gnovm/tests/files/zrealm5.gno @@ -23,7 +23,7 @@ func main() { // Realm: // switchrealm["gno.land/r/test"] -// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:5]={ +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:7]={ // "Fields": [ // { // "T": { @@ -72,13 +72,32 @@ func main() { // } // ], // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", // "ModTime": "0", -// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6", +// "RefCount": "1" +// } +// } +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:6]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", // "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "8a86634afa28ef7d7a1f4272255637f16daae2cd", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7" +// } // } // } -// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:4]={ +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:5]={ // "Fields": [ // { // "T": { @@ -126,26 +145,20 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "7c63a8fd451cd7c470c1851f1ead037246422ded", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "1a4158d473290431f9d4f9c5a85a3b6697640f2a", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5" -// } -// } +// "TV": null // } // } // ], // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", -// "ModTime": "4", -// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", +// "ModTime": "5", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", // "RefCount": "1" // } // } @@ -154,7 +167,7 @@ func main() { // "ObjectInfo": { // "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", // "IsEscaped": true, -// "ModTime": "4", +// "ModTime": "5", // "RefCount": "2" // }, // "Parent": null, @@ -251,19 +264,13 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "7b9d58f40430bbbcbafd47eefb7a6dd342477f71", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "011a2960ff92aedda8acd122b9f4d251902e0cd8", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" -// } -// } +// "TV": null // } // } // ] diff --git a/gnovm/tests/files/zrealm6.gno b/gnovm/tests/files/zrealm6.gno index 9884ab1909c..ff5e7e758a1 100644 --- a/gnovm/tests/files/zrealm6.gno +++ b/gnovm/tests/files/zrealm6.gno @@ -24,7 +24,7 @@ func main() { // Realm: // switchrealm["gno.land/r/test"] -// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:6]={ +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:9]={ // "Fields": [ // { // "T": { @@ -73,13 +73,32 @@ func main() { // } // ], // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9", // "ModTime": "0", -// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8", // "RefCount": "1" // } // } -// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:5]={ +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:8]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", +// "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "34a46349a2bc1b58591d0222a145b585452683be", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9" +// } +// } +// } +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:7]={ // "Fields": [ // { // "T": { @@ -127,30 +146,24 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "81074f5da453299a913435a2ddd05248ee012f8c", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "b1719c55a9b07d432385f020b0bdbc678ba2b9ac", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" -// } -// } +// "TV": null // } // } // ], // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", -// "ModTime": "5", -// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", +// "ModTime": "7", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6", // "RefCount": "1" // } // } -// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:4]={ +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:5]={ // "Fields": [ // { // "T": { @@ -198,26 +211,20 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "7c63a8fd451cd7c470c1851f1ead037246422ded", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "8d00c3fa6c15cb0d78dcbaa23df49f96bbc9591b", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5" -// } -// } +// "TV": null // } // } // ], // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", -// "ModTime": "5", -// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", +// "ModTime": "7", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", // "RefCount": "1" // } // } @@ -226,7 +233,7 @@ func main() { // "ObjectInfo": { // "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", // "IsEscaped": true, -// "ModTime": "5", +// "ModTime": "7", // "RefCount": "2" // }, // "Parent": null, @@ -323,19 +330,13 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "ade9fce2a987ef1924040a1d75c0172410c66952", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "8f2cb2a771ddc55ab5798b791e16df547c94d862", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" -// } -// } +// "TV": null // } // } // ] diff --git a/gnovm/tests/files/zrealm7.gno b/gnovm/tests/files/zrealm7.gno index a706ffcad78..e6fd99503be 100644 --- a/gnovm/tests/files/zrealm7.gno +++ b/gnovm/tests/files/zrealm7.gno @@ -25,7 +25,7 @@ func main() { // Realm: // switchrealm["gno.land/r/test"] -// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:7]={ +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:11]={ // "Fields": [ // { // "T": { @@ -74,13 +74,32 @@ func main() { // } // ], // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:11", // "ModTime": "0", -// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:10", // "RefCount": "1" // } // } -// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:6]={ +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:10]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:10", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9", +// "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "42cd813e173ad23c7873e9605901e8bea1176c96", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:11" +// } +// } +// } +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:9]={ // "Fields": [ // { // "T": { @@ -128,30 +147,43 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "4f88fcdc73a4a94905e8e4044aa50c2ec7bf2227", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:10" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "2ac7cc7e6fdb1ff6dc1f340486011f1449757d85", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7" -// } -// } +// "TV": null // } // } // ], // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6", -// "ModTime": "6", -// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9", +// "ModTime": "9", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8", +// "RefCount": "1" +// } +// } +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:8]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8", +// "ModTime": "9", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", // "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "2c172bbe0183ccc73c59d9acb196c45b0331c39e", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9" +// } // } // } -// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:4]={ +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:7]={ // "Fields": [ // { // "T": { @@ -160,7 +192,7 @@ func main() { // }, // "V": { // "@type": "/gno.StringValue", -// "value": "key0" +// "value": "key1" // } // }, // { @@ -170,11 +202,11 @@ func main() { // }, // "V": { // "@type": "/gno.StringValue", -// "value": "value0" +// "value": "value1" // } // }, // { -// "N": "AQAAAAAAAAA=", +// "N": "AwAAAAAAAAA=", // "T": { // "@type": "/gno.PrimitiveType", // "value": "32" @@ -187,6 +219,16 @@ func main() { // "@type": "/gno.RefType", // "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" // } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "a4fa9bdf45caf8c6b5be7a3752704423817b3ef2", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" +// }, +// "Index": "0", +// "TV": null // } // }, // { @@ -196,13 +238,23 @@ func main() { // "@type": "/gno.RefType", // "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" // } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "43f69f24b7827a331921b4af0f667346d186e0c3", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8" +// }, +// "Index": "0", +// "TV": null // } // } // ], // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", -// "ModTime": "6", -// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", +// "ModTime": "9", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6", // "RefCount": "1" // } // } @@ -215,7 +267,7 @@ func main() { // }, // "V": { // "@type": "/gno.StringValue", -// "value": "key1" +// "value": "key0" // } // }, // { @@ -225,11 +277,11 @@ func main() { // }, // "V": { // "@type": "/gno.StringValue", -// "value": "value1" +// "value": "value0" // } // }, // { -// "N": "AwAAAAAAAAA=", +// "N": "AQAAAAAAAAA=", // "T": { // "@type": "/gno.PrimitiveType", // "value": "32" @@ -242,22 +294,6 @@ func main() { // "@type": "/gno.RefType", // "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" // } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": null, -// "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "a0af92becf7bef8d5d71c94e8f8f044e4cfe526d", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" -// } -// } // } // }, // { @@ -267,38 +303,41 @@ func main() { // "@type": "/gno.RefType", // "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" // } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": null, -// "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "752161efcfe5a3e2ef70c03ff4354097f09ada56", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" -// } -// } // } // } // ], // "ObjectInfo": { // "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", -// "ModTime": "6", +// "ModTime": "9", // "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", // "RefCount": "1" // } // } +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:6]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6", +// "ModTime": "9", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", +// "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "f56fbd9c8db299689cc0cf806fe741b6a6e641e6", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7" +// } +// } +// } // u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ // "Blank": {}, // "ObjectInfo": { // "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", // "IsEscaped": true, -// "ModTime": "6", +// "ModTime": "9", // "RefCount": "2" // }, // "Parent": null, @@ -395,19 +434,13 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "450aef9858564ed4ec1c418f1e8dac828079016b", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "c59d05a21bf190551bb15a8b9d41a9e8da717f3d", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5" -// } -// } +// "TV": null // } // } // ] diff --git a/gnovm/tests/files/zrealm_avl0.gno b/gnovm/tests/files/zrealm_avl0.gno index e91788ac8eb..e3f1363e205 100644 --- a/gnovm/tests/files/zrealm_avl0.gno +++ b/gnovm/tests/files/zrealm_avl0.gno @@ -25,67 +25,25 @@ func main() { // Realm: // switchrealm["gno.land/r/test"] // u[a8ada09dee16d791fd406d629fe29bb0ed084a30:4]={ -// "Fields": [ -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// }, -// "V": { -// "@type": "/gno.StringValue", -// "value": "key0" -// } -// }, -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// }, -// "V": { -// "@type": "/gno.StringValue", -// "value": "value0" -// } -// }, -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "64" -// } -// }, -// { -// "N": "AQAAAAAAAAA=", -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "32" -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// } -// } -// ], // "ObjectInfo": { // "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", -// "ModTime": "5", -// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", +// "ModTime": "7", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", // "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "627e8e517e7ae5db0f3b753e2a32b607989198b6", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5" +// } // } // } -// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:6]={ +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:9]={ // "Fields": [ // { // "T": { @@ -140,13 +98,32 @@ func main() { // } // ], // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9", // "ModTime": "0", -// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8", // "RefCount": "1" // } // } -// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:5]={ +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:8]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", +// "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "b28057ab7be6383785c0a5503e8a531bdbc21851", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9" +// } +// } +// } +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:7]={ // "Fields": [ // { // "T": { @@ -183,19 +160,13 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "6da365f0d6cacbcdf53cd5a4b125803cddce08c2", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "091729e38bda8724bce4c314f9624b91af679459", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" -// } -// } +// "TV": null // } // }, // { @@ -208,27 +179,40 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "f216afe7b5a17f4ebdbb98dceccedbc22e237596", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "0b5493aa4ea42087780bdfcaebab2c3eec351c15", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" -// } -// } +// "TV": null // } // } // ], // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6", +// "RefCount": "1" +// } +// } +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:6]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6", // "ModTime": "0", // "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", // "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "ff1a50d8489090af37a2c7766d659f0d717939b5", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7" +// } // } // } // u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ @@ -236,7 +220,7 @@ func main() { // "ObjectInfo": { // "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", // "IsEscaped": true, -// "ModTime": "4", +// "ModTime": "5", // "RefCount": "2" // }, // "Parent": null, @@ -333,19 +317,13 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "ae86874f9b47fa5e64c30b3e92e9d07f2ec967a4", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "6c9948281d4c60b2d95233b76388d54d8b1a2fad", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5" -// } -// } +// "TV": null // } // } // ] diff --git a/gnovm/tests/files/zrealm_avl1.gno b/gnovm/tests/files/zrealm_avl1.gno index cdd56a5ad89..a6d2205e240 100644 --- a/gnovm/tests/files/zrealm_avl1.gno +++ b/gnovm/tests/files/zrealm_avl1.gno @@ -24,7 +24,7 @@ func main() { // Realm: // switchrealm["gno.land/r/test"] -// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:9]={ +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:15]={ // "Fields": [ // { // "T": { @@ -79,13 +79,32 @@ func main() { // } // ], // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:15", // "ModTime": "0", -// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:14", // "RefCount": "1" // } // } -// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:8]={ +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:14]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:14", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:13", +// "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "143aebc820da33550f7338723fb1e2eec575b196", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:15" +// } +// } +// } +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:13]={ // "Fields": [ // { // "T": { @@ -122,19 +141,13 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "2f3adc5d0f2a3fe0331cfa93572a7abdde14c9aa", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "7a8a63e17a567d7b0891ac89d5cd90072a73787d", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" -// } -// } +// "TV": null // } // }, // { @@ -147,30 +160,43 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "2e733a8e9e74fe14f0a5d10fb0f6728fa53d052d", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:14" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "ab5a297f4eb033d88bdf1677f4dc151ccb9fde9f", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9" -// } -// } +// "TV": null // } // } // ], // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:13", // "ModTime": "0", -// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:12", // "RefCount": "1" // } // } -// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:7]={ +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:12]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:12", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:11", +// "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "fe20a19f956511f274dc77854e9e5468387260f4", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:13" +// } +// } +// } +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:11]={ // "Fields": [ // { // "T": { @@ -207,19 +233,13 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "c89a71bdf045e8bde2059dc9d33839f916e02e5d", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "627e8e517e7ae5db0f3b753e2a32b607989198b6", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5" -// } -// } +// "TV": null // } // }, // { @@ -232,27 +252,40 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "90fa67f8c47db4b9b2a60425dff08d5a3385100f", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:12" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "fe8afd501233fb95375016199f0443b3c6ab1fbc", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8" -// } -// } +// "TV": null // } // } // ], // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:11", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:10", +// "RefCount": "1" +// } +// } +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:10]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:10", // "ModTime": "0", // "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", // "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "83e42caaf53070dd95b5f859053eb51ed900bbda", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:11" +// } // } // } // u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ @@ -260,7 +293,7 @@ func main() { // "ObjectInfo": { // "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", // "IsEscaped": true, -// "ModTime": "6", +// "ModTime": "9", // "RefCount": "2" // }, // "Parent": null, @@ -357,21 +390,16 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "1faa9fa4ba1935121a6d3f0a623772e9d4499b0a", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:10" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "c5eefc40ed065461b4a920c1349ed734ffdead8f", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7" -// } -// } +// "TV": null // } // } // ] // } // d[a8ada09dee16d791fd406d629fe29bb0ed084a30:4] +// d[a8ada09dee16d791fd406d629fe29bb0ed084a30:5] diff --git a/gnovm/tests/files/zrealm_avl2.gno b/gnovm/tests/files/zrealm_avl2.gno index 8ecf6ba52bc..a89f0540f47 100644 --- a/gnovm/tests/files/zrealm_avl2.gno +++ b/gnovm/tests/files/zrealm_avl2.gno @@ -24,67 +24,25 @@ func main() { // Realm: // switchrealm["gno.land/r/test"] // u[a8ada09dee16d791fd406d629fe29bb0ed084a30:5]={ -// "Fields": [ -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// }, -// "V": { -// "@type": "/gno.StringValue", -// "value": "key1" -// } -// }, -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// }, -// "V": { -// "@type": "/gno.StringValue", -// "value": "value1" -// } -// }, -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "64" -// } -// }, -// { -// "N": "AQAAAAAAAAA=", -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "32" -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// } -// } -// ], // "ObjectInfo": { // "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", -// "ModTime": "6", -// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6", +// "ModTime": "8", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8", // "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "0b5493aa4ea42087780bdfcaebab2c3eec351c15", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" +// } // } // } -// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:7]={ +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:10]={ // "Fields": [ // { // "T": { @@ -139,13 +97,32 @@ func main() { // } // ], // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:10", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9", +// "RefCount": "1" +// } +// } +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:9]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9", // "ModTime": "0", -// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8", // "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "be751422ef4c2bc068a456f9467d2daca27db8ca", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:10" +// } // } // } -// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:6]={ +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:8]={ // "Fields": [ // { // "T": { @@ -182,19 +159,13 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "9fa04d8791e205a6de2eedce81bb4dbd0883cac7", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "23c8d928ce614d559719cb47e71a75a456b49a2a", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5" -// } -// } +// "TV": null // } // }, // { @@ -207,27 +178,40 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "a55a6a6b2027d6ec5e322aa32d4269b974fe1a4f", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "fc92b122743503329a416d02fb4fe84cbca6dc57", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7" -// } -// } +// "TV": null // } // } // ], // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", +// "RefCount": "1" +// } +// } +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:7]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", // "ModTime": "0", // "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", // "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "03d901636a4e56d5bd32a75a7b923c7700c8859a", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8" +// } // } // } // u[a8ada09dee16d791fd406d629fe29bb0ed084a30:4]={ @@ -242,25 +226,19 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "2c8281b6c5a347a3d3aeedb74e61f081060ac050", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "e6d40c7e6f2c94668ab964b4c356d7cbd537a2b5", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" -// } -// } +// "TV": null // } // } // ], // "ObjectInfo": { // "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", -// "ModTime": "5", +// "ModTime": "6", // "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", // "RefCount": "1" // } diff --git a/gnovm/tests/files/zrealm_const.gno b/gnovm/tests/files/zrealm_const_stdlibs.gno similarity index 100% rename from gnovm/tests/files/zrealm_const.gno rename to gnovm/tests/files/zrealm_const_stdlibs.gno diff --git a/gnovm/tests/files/zrealm_crossrealm0.gno b/gnovm/tests/files/zrealm_crossrealm0_stdlibs.gno similarity index 100% rename from gnovm/tests/files/zrealm_crossrealm0.gno rename to gnovm/tests/files/zrealm_crossrealm0_stdlibs.gno diff --git a/gnovm/tests/files/zrealm_crossrealm10.gno b/gnovm/tests/files/zrealm_crossrealm10_stdlibs.gno similarity index 100% rename from gnovm/tests/files/zrealm_crossrealm10.gno rename to gnovm/tests/files/zrealm_crossrealm10_stdlibs.gno diff --git a/gnovm/tests/files/zrealm_crossrealm11.gno b/gnovm/tests/files/zrealm_crossrealm11_stdlibs.gno similarity index 99% rename from gnovm/tests/files/zrealm_crossrealm11.gno rename to gnovm/tests/files/zrealm_crossrealm11_stdlibs.gno index b250b07bbac..e6f33c50654 100644 --- a/gnovm/tests/files/zrealm_crossrealm11.gno +++ b/gnovm/tests/files/zrealm_crossrealm11_stdlibs.gno @@ -2,13 +2,10 @@ package crossrealm_test import ( - "std" - "strings" - ptests "gno.land/p/demo/tests" "gno.land/p/demo/ufmt" rtests "gno.land/r/demo/tests" - testfoo "gno.land/r/demo/tests_foo" + "std" ) func getPrevRealm() std.Realm { diff --git a/gnovm/tests/files/zrealm_crossrealm12.gno b/gnovm/tests/files/zrealm_crossrealm12_stdlibs.gno similarity index 86% rename from gnovm/tests/files/zrealm_crossrealm12.gno rename to gnovm/tests/files/zrealm_crossrealm12_stdlibs.gno index ef8ea141ac5..f2f229cd5de 100644 --- a/gnovm/tests/files/zrealm_crossrealm12.gno +++ b/gnovm/tests/files/zrealm_crossrealm12_stdlibs.gno @@ -2,22 +2,22 @@ package crossrealm_test import ( - "std" "fmt" + "std" psubtests "gno.land/p/demo/tests/subtests" rsubtests "gno.land/r/demo/tests/subtests" ) func main() { - tests := []struct{ + tests := []struct { fn func() std.Realm }{ - { std.CurrentRealm }, - { psubtests.GetCurrentRealm }, - { rsubtests.GetCurrentRealm }, + {std.CurrentRealm}, + {psubtests.GetCurrentRealm}, + {rsubtests.GetCurrentRealm}, } - + for _, test := range tests { r := test.fn() diff --git a/gnovm/tests/files/zrealm_crossrealm13.gno b/gnovm/tests/files/zrealm_crossrealm13_stdlibs.gno similarity index 100% rename from gnovm/tests/files/zrealm_crossrealm13.gno rename to gnovm/tests/files/zrealm_crossrealm13_stdlibs.gno diff --git a/gnovm/tests/files/zrealm_crossrealm13a.gno b/gnovm/tests/files/zrealm_crossrealm13a_stdlibs.gno similarity index 100% rename from gnovm/tests/files/zrealm_crossrealm13a.gno rename to gnovm/tests/files/zrealm_crossrealm13a_stdlibs.gno diff --git a/gnovm/tests/files/zrealm_crossrealm1.gno b/gnovm/tests/files/zrealm_crossrealm1_stdlibs.gno similarity index 100% rename from gnovm/tests/files/zrealm_crossrealm1.gno rename to gnovm/tests/files/zrealm_crossrealm1_stdlibs.gno diff --git a/gnovm/tests/files/zrealm_crossrealm2.gno b/gnovm/tests/files/zrealm_crossrealm2_stdlibs.gno similarity index 100% rename from gnovm/tests/files/zrealm_crossrealm2.gno rename to gnovm/tests/files/zrealm_crossrealm2_stdlibs.gno diff --git a/gnovm/tests/files/zrealm_crossrealm3.gno b/gnovm/tests/files/zrealm_crossrealm3_stdlibs.gno similarity index 100% rename from gnovm/tests/files/zrealm_crossrealm3.gno rename to gnovm/tests/files/zrealm_crossrealm3_stdlibs.gno diff --git a/gnovm/tests/files/zrealm_crossrealm4.gno b/gnovm/tests/files/zrealm_crossrealm4_stdlibs.gno similarity index 100% rename from gnovm/tests/files/zrealm_crossrealm4.gno rename to gnovm/tests/files/zrealm_crossrealm4_stdlibs.gno diff --git a/gnovm/tests/files/zrealm_crossrealm5.gno b/gnovm/tests/files/zrealm_crossrealm5_stdlibs.gno similarity index 100% rename from gnovm/tests/files/zrealm_crossrealm5.gno rename to gnovm/tests/files/zrealm_crossrealm5_stdlibs.gno diff --git a/gnovm/tests/files/zrealm_crossrealm6.gno b/gnovm/tests/files/zrealm_crossrealm6_stdlibs.gno similarity index 100% rename from gnovm/tests/files/zrealm_crossrealm6.gno rename to gnovm/tests/files/zrealm_crossrealm6_stdlibs.gno diff --git a/gnovm/tests/files/zrealm_crossrealm7.gno b/gnovm/tests/files/zrealm_crossrealm7_stdlibs.gno similarity index 100% rename from gnovm/tests/files/zrealm_crossrealm7.gno rename to gnovm/tests/files/zrealm_crossrealm7_stdlibs.gno diff --git a/gnovm/tests/files/zrealm_crossrealm8.gno b/gnovm/tests/files/zrealm_crossrealm8_stdlibs.gno similarity index 100% rename from gnovm/tests/files/zrealm_crossrealm8.gno rename to gnovm/tests/files/zrealm_crossrealm8_stdlibs.gno diff --git a/gnovm/tests/files/zrealm_crossrealm9.gno b/gnovm/tests/files/zrealm_crossrealm9_stdlibs.gno similarity index 100% rename from gnovm/tests/files/zrealm_crossrealm9.gno rename to gnovm/tests/files/zrealm_crossrealm9_stdlibs.gno diff --git a/gnovm/tests/files/zrealm_example.gno b/gnovm/tests/files/zrealm_example.gno index 1ce70c04390..45aeb7c5ddb 100644 --- a/gnovm/tests/files/zrealm_example.gno +++ b/gnovm/tests/files/zrealm_example.gno @@ -24,7 +24,7 @@ func main() { // Realm: // switchrealm["gno.land/r/example"] -// c[1ffd45e074aa1b8df562907c95ad97526b7ca187:8]={ +// c[1ffd45e074aa1b8df562907c95ad97526b7ca187:11]={ // "Fields": [ // { // "T": { @@ -37,13 +37,13 @@ func main() { // } // ], // "ObjectInfo": { -// "ID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:8", +// "ID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:11", // "ModTime": "0", -// "OwnerID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:7", +// "OwnerID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:10", // "RefCount": "1" // } // } -// c[1ffd45e074aa1b8df562907c95ad97526b7ca187:7]={ +// c[1ffd45e074aa1b8df562907c95ad97526b7ca187:10]={ // "Fields": [ // { // "T": { @@ -72,19 +72,38 @@ func main() { // }, // "V": { // "@type": "/gno.RefValue", -// "Hash": "c3dc30d2f2a57a0eeb4336dae59355aa7bee0ff5", -// "ObjectID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:8" +// "Hash": "f190df54e397e2006cee3fc525bcc1b4d556e4c4", +// "ObjectID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:11" // } // } // ], // "ObjectInfo": { -// "ID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:7", +// "ID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:10", // "ModTime": "0", -// "OwnerID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:6", +// "OwnerID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:9", // "RefCount": "1" // } // } -// c[1ffd45e074aa1b8df562907c95ad97526b7ca187:6]={ +// c[1ffd45e074aa1b8df562907c95ad97526b7ca187:9]={ +// "ObjectInfo": { +// "ID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:9", +// "ModTime": "0", +// "OwnerID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:8", +// "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/dom.Post" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "a74fad6da10f1cec74ad3a8751490b4dca957761", +// "ObjectID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:10" +// } +// } +// } +// c[1ffd45e074aa1b8df562907c95ad97526b7ca187:8]={ // "Fields": [ // { // "T": { @@ -106,19 +125,13 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "8b11b3d07ddeb034f70a114c9433ec6bd5cbf899", +// "ObjectID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:9" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/dom.Post" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "f5d48c5a050326190d971fabb76835de31f83b20", -// "ObjectID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:7" -// } -// } +// "TV": null // } // }, // { @@ -154,13 +167,32 @@ func main() { // } // ], // "ObjectInfo": { -// "ID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:6", +// "ID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:8", // "ModTime": "0", -// "OwnerID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:5", +// "OwnerID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:7", // "RefCount": "1" // } // } -// u[1ffd45e074aa1b8df562907c95ad97526b7ca187:5]={ +// c[1ffd45e074aa1b8df562907c95ad97526b7ca187:7]={ +// "ObjectInfo": { +// "ID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:7", +// "ModTime": "0", +// "OwnerID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:6", +// "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "869abdac30a3ae78b2191806e1c894c48e399122", +// "ObjectID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:8" +// } +// } +// } +// u[1ffd45e074aa1b8df562907c95ad97526b7ca187:6]={ // "Fields": [ // { // "T": { @@ -172,30 +204,24 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "a919087d0eba652876f9a8df18b30ec5ddc8c26e", +// "ObjectID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:7" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "e373f3e5c834170fe6e8b6cf5a95d185e80b0ad7", -// "ObjectID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:6" -// } -// } +// "TV": null // } // } // ], // "ObjectInfo": { -// "ID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:5", -// "ModTime": "5", -// "OwnerID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:4", +// "ID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:6", +// "ModTime": "6", +// "OwnerID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:5", // "RefCount": "1" // } // } -// u[1ffd45e074aa1b8df562907c95ad97526b7ca187:4]={ +// u[1ffd45e074aa1b8df562907c95ad97526b7ca187:5]={ // "Fields": [ // { // "T": { @@ -214,8 +240,8 @@ func main() { // }, // "V": { // "@type": "/gno.RefValue", -// "Hash": "05c2d0709574f676715a23d0161d2e151c0b21c7", -// "ObjectID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:5" +// "Hash": "dfdeb7ed80c5b030c3a5e9701d00c66203de6f57", +// "ObjectID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:6" // } // }, // { @@ -227,9 +253,9 @@ func main() { // } // ], // "ObjectInfo": { -// "ID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:4", -// "ModTime": "5", -// "OwnerID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:2", +// "ID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:5", +// "ModTime": "6", +// "OwnerID": "1ffd45e074aa1b8df562907c95ad97526b7ca187:4", // "RefCount": "1" // } // } diff --git a/gnovm/tests/files/zrealm_initctx.gno b/gnovm/tests/files/zrealm_initctx_stdlibs.gno similarity index 100% rename from gnovm/tests/files/zrealm_initctx.gno rename to gnovm/tests/files/zrealm_initctx_stdlibs.gno diff --git a/gnovm/tests/files/zrealm_natbind0.gno b/gnovm/tests/files/zrealm_natbind0_stdlibs.gno similarity index 100% rename from gnovm/tests/files/zrealm_natbind0.gno rename to gnovm/tests/files/zrealm_natbind0_stdlibs.gno diff --git a/gnovm/tests/files/zrealm_std0.gno b/gnovm/tests/files/zrealm_std0_stdlibs.gno similarity index 100% rename from gnovm/tests/files/zrealm_std0.gno rename to gnovm/tests/files/zrealm_std0_stdlibs.gno diff --git a/gnovm/tests/files/zrealm_std1.gno b/gnovm/tests/files/zrealm_std1_stdlibs.gno similarity index 88% rename from gnovm/tests/files/zrealm_std1.gno rename to gnovm/tests/files/zrealm_std1_stdlibs.gno index 87f75bcb871..d75a2c60b71 100644 --- a/gnovm/tests/files/zrealm_std1.gno +++ b/gnovm/tests/files/zrealm_std1_stdlibs.gno @@ -25,7 +25,7 @@ func main() { } // Output: -// (slice[ref(1ed29bd278d735e20e296bd4afe927501941392f:4)] std.AddressList) +// (slice[ref(1ed29bd278d735e20e296bd4afe927501941392f:5)] std.AddressList) // error: address already exists // has: true // has: false diff --git a/gnovm/tests/files/zrealm_std2.gno b/gnovm/tests/files/zrealm_std2_stdlibs.gno similarity index 90% rename from gnovm/tests/files/zrealm_std2.gno rename to gnovm/tests/files/zrealm_std2_stdlibs.gno index 1ae1fb4a881..810210c6160 100644 --- a/gnovm/tests/files/zrealm_std2.gno +++ b/gnovm/tests/files/zrealm_std2_stdlibs.gno @@ -26,7 +26,7 @@ func main() { } // Output: -// (slice[ref(1ed29bd278d735e20e296bd4afe927501941392f:4)] std.AddressList) +// (slice[ref(1ed29bd278d735e20e296bd4afe927501941392f:5)] std.AddressList) // error: address already exists // has: true // has: false diff --git a/gnovm/tests/files/zrealm_std3.gno b/gnovm/tests/files/zrealm_std3_stdlibs.gno similarity index 100% rename from gnovm/tests/files/zrealm_std3.gno rename to gnovm/tests/files/zrealm_std3_stdlibs.gno diff --git a/gnovm/tests/files/zrealm_std4.gno b/gnovm/tests/files/zrealm_std4_stdlibs.gno similarity index 100% rename from gnovm/tests/files/zrealm_std4.gno rename to gnovm/tests/files/zrealm_std4_stdlibs.gno diff --git a/gnovm/tests/files/zrealm_std5.gno b/gnovm/tests/files/zrealm_std5_stdlibs.gno similarity index 100% rename from gnovm/tests/files/zrealm_std5.gno rename to gnovm/tests/files/zrealm_std5_stdlibs.gno diff --git a/gnovm/tests/files/zrealm_std6.gno b/gnovm/tests/files/zrealm_std6_stdlibs.gno similarity index 56% rename from gnovm/tests/files/zrealm_std6.gno rename to gnovm/tests/files/zrealm_std6_stdlibs.gno index fb9e0f825a2..17a79c1d43b 100644 --- a/gnovm/tests/files/zrealm_std6.gno +++ b/gnovm/tests/files/zrealm_std6_stdlibs.gno @@ -1,5 +1,5 @@ -// PKGPATH: gno.land/r/test -package test +// PKGPATH: gno.land/r/std_test +package std_test import ( "std" @@ -14,4 +14,4 @@ func main() { } // Output: -// g148esskg7wzxxw6axwv4j5nkgjzdp6v4zmy3pwc +// g157y5v3k529jyzhjjz4fn49tzzhf4gess6v39xg diff --git a/gnovm/tests/files/zrealm_tests0.gno b/gnovm/tests/files/zrealm_tests0_stdlibs.gno similarity index 95% rename from gnovm/tests/files/zrealm_tests0.gno rename to gnovm/tests/files/zrealm_tests0_stdlibs.gno index d68c1f652f7..bfd4ad30a1e 100644 --- a/gnovm/tests/files/zrealm_tests0.gno +++ b/gnovm/tests/files/zrealm_tests0_stdlibs.gno @@ -23,7 +23,7 @@ func main() { // Realm: // switchrealm["gno.land/r/demo/tests"] -// c[0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:14]={ +// c[0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:18]={ // "Fields": [ // { // "T": { @@ -37,13 +37,32 @@ func main() { // } // ], // "ObjectInfo": { -// "ID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:14", +// "ID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:18", // "ModTime": "0", -// "OwnerID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:13", +// "OwnerID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:17", // "RefCount": "1" // } // } -// c[0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:13]={ +// c[0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:17]={ +// "ObjectInfo": { +// "ID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:17", +// "ModTime": "0", +// "OwnerID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:16", +// "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/demo/tests_foo.FooStringer" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "d3d6ffa52602f2bc976051d79294d219750aca64", +// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:18" +// } +// } +// } +// c[0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:16]={ // "Data": null, // "List": [ // { @@ -56,19 +75,13 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "4ea1e08156f3849b74a0f41f92cd4b48fb94926b", +// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:11" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests_foo.FooStringer" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "63ef2b51ca911a9b1727588bacb958ec3cb2a392", -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:10" -// } -// } +// "TV": null // } // }, // { @@ -81,19 +94,13 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "ce86ea1156e75a44cd9d7ba2261819b100aa4ed1", +// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:14" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests_foo.FooStringer" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "54c554e1d1f61e19feb13bb229f43540338c0f8f", -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:12" -// } -// } +// "TV": null // } // }, // { @@ -106,24 +113,18 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "b66192fbd8a8dde79b6f854b5cc3c4cc965cfd92", +// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:17" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests_foo.FooStringer" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "4e50e2cdaeb022a6fcbdb96d9fbd7f3af8df1379", -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:14" -// } -// } +// "TV": null // } // } // ], // "ObjectInfo": { -// "ID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:13", +// "ID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:16", // "ModTime": "0", // "OwnerID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:2", // "RefCount": "1" @@ -134,7 +135,7 @@ func main() { // "ObjectInfo": { // "ID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:2", // "IsEscaped": true, -// "ModTime": "12", +// "ModTime": "15", // "RefCount": "5" // }, // "Parent": null, @@ -1511,8 +1512,8 @@ func main() { // "@type": "/gno.SliceValue", // "Base": { // "@type": "/gno.RefValue", -// "Hash": "5f12a61dd16d828be8584debc0e395e8d2136acb", -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:13" +// "Hash": "ad25f70f66c8c53042afd1377e5ff5ab744bf1a5", +// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:16" // }, // "Length": "3", // "Maxcap": "3", @@ -1529,19 +1530,13 @@ func main() { // }, // "V": { // "@type": "/gno.PointerValue", -// "Base": null, +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "b662d2bfde61831128c908a3d1afff97e05e6227", +// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:7" +// }, // "Index": "0", -// "TV": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests.Int" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "90b77781ec2b1e153ac020b1102354174bde972e", -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:7" -// } -// } +// "TV": null // } // }, // { @@ -1595,7 +1590,7 @@ func main() { // } // ] // } -// d[0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:11] +// d[0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:13] // 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.gno b/gnovm/tests/files/zrealm_testutils0_stdlibs.gno similarity index 100% rename from gnovm/tests/files/zrealm_testutils0.gno rename to gnovm/tests/files/zrealm_testutils0_stdlibs.gno diff --git a/gnovm/tests/integ/invalid_assign/main.gno b/gnovm/tests/integ/invalid_assign/main.gno new file mode 100644 index 00000000000..9f98ef31213 --- /dev/null +++ b/gnovm/tests/integ/invalid_assign/main.gno @@ -0,0 +1,16 @@ +// This should result in a compilation error for assigning a bool to a custom bool type without conversion + +package main + +func main() { + b := true + var v C = b + fmt.Println(v) +} + +type C bool + +const ( + F C = false + T C = true +) diff --git a/gnovm/tests/package_test.go b/gnovm/tests/package_test.go index ded5bedce5f..8e497941c7f 100644 --- a/gnovm/tests/package_test.go +++ b/gnovm/tests/package_test.go @@ -18,7 +18,7 @@ import ( func TestStdlibs(t *testing.T) { // 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`. + // The examples directory is tested directly using `gno test`u // find all packages with *_test.gno files. rootDirs := []string{ diff --git a/gnovm/tests/stdlibs/native.go b/gnovm/tests/stdlibs/native.go index 0f33548054b..d2964a7958c 100644 --- a/gnovm/tests/stdlibs/native.go +++ b/gnovm/tests/stdlibs/native.go @@ -11,20 +11,31 @@ import ( testlibs_testing "github.com/gnolang/gno/gnovm/tests/stdlibs/testing" ) -type nativeFunc struct { - gnoPkg string - gnoFunc gno.Name - params []gno.FieldTypeExpr - results []gno.FieldTypeExpr - f func(m *gno.Machine) +// NativeFunc represents a function in the standard library which has a native +// (go-based) implementation, commonly referred to as a "native binding". +type NativeFunc struct { + gnoPkg string + gnoFunc gno.Name + params []gno.FieldTypeExpr + results []gno.FieldTypeExpr + hasMachine bool + f func(m *gno.Machine) } -var nativeFuncs = [...]nativeFunc{ +// HasMachineParam returns whether the given native binding has a machine parameter. +// This means that the Go version of this function expects a *gno.Machine +// as its first parameter. +func (n *NativeFunc) HasMachineParam() bool { + return n.hasMachine +} + +var nativeFuncs = [...]NativeFunc{ { "std", "AssertOriginCall", []gno.FieldTypeExpr{}, []gno.FieldTypeExpr{}, + true, func(m *gno.Machine) { testlibs_std.AssertOriginCall( m, @@ -38,6 +49,7 @@ var nativeFuncs = [...]nativeFunc{ []gno.FieldTypeExpr{ {Name: gno.N("r0"), Type: gno.X("bool")}, }, + true, func(m *gno.Machine) { r0 := testlibs_std.IsOriginCall( m, @@ -57,6 +69,7 @@ var nativeFuncs = [...]nativeFunc{ {Name: gno.N("p0"), Type: gno.X("int64")}, }, []gno.FieldTypeExpr{}, + true, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -76,6 +89,7 @@ var nativeFuncs = [...]nativeFunc{ "ClearStoreCache", []gno.FieldTypeExpr{}, []gno.FieldTypeExpr{}, + true, func(m *gno.Machine) { testlibs_std.ClearStoreCache( m, @@ -91,6 +105,7 @@ var nativeFuncs = [...]nativeFunc{ []gno.FieldTypeExpr{ {Name: gno.N("r0"), Type: gno.X("string")}, }, + true, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -118,6 +133,7 @@ var nativeFuncs = [...]nativeFunc{ {Name: gno.N("p0"), Type: gno.X("string")}, }, []gno.FieldTypeExpr{}, + true, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -139,6 +155,7 @@ var nativeFuncs = [...]nativeFunc{ {Name: gno.N("p0"), Type: gno.X("string")}, }, []gno.FieldTypeExpr{}, + true, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -161,6 +178,7 @@ var nativeFuncs = [...]nativeFunc{ {Name: gno.N("p1"), Type: gno.X("string")}, }, []gno.FieldTypeExpr{}, + true, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -188,6 +206,7 @@ var nativeFuncs = [...]nativeFunc{ {Name: gno.N("p3"), Type: gno.X("[]int64")}, }, []gno.FieldTypeExpr{}, + true, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -220,6 +239,7 @@ var nativeFuncs = [...]nativeFunc{ {Name: gno.N("p2"), Type: gno.X("[]int64")}, }, []gno.FieldTypeExpr{}, + true, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -250,6 +270,7 @@ var nativeFuncs = [...]nativeFunc{ {Name: gno.N("r0"), Type: gno.X("string")}, {Name: gno.N("r1"), Type: gno.X("string")}, }, + true, func(m *gno.Machine) { b := m.LastBlock() var ( @@ -282,6 +303,7 @@ var nativeFuncs = [...]nativeFunc{ []gno.FieldTypeExpr{ {Name: gno.N("r0"), Type: gno.X("int64")}, }, + false, func(m *gno.Machine) { r0 := testlibs_testing.X_unixNano() diff --git a/gnovm/tests/stdlibs/std/std.gno b/gnovm/tests/stdlibs/std/std.gno index e96be939eb5..f583342ae04 100644 --- a/gnovm/tests/stdlibs/std/std.gno +++ b/gnovm/tests/stdlibs/std/std.gno @@ -9,7 +9,7 @@ func TestSetOrigCaller(addr Address) { testSetOrigCaller(string(addr)) } func TestSetOrigPkgAddr(addr Address) { testSetOrigPkgAddr(string(addr)) } // TestSetRealm sets the realm for the current frame. -// After calling TestSetRealm, calling CurrentRealm() will yield the value of +// After calling TestSetRealm, calling CurrentRealm() in the test function will yield the value of // rlm, while if a realm function is called, using PrevRealm() will yield rlm. func TestSetRealm(rlm Realm) { testSetRealm(string(rlm.addr), rlm.pkgPath) diff --git a/gnovm/tests/stdlibs/std/std.go b/gnovm/tests/stdlibs/std/std.go index ee17ccb2c15..0421f359932 100644 --- a/gnovm/tests/stdlibs/std/std.go +++ b/gnovm/tests/stdlibs/std/std.go @@ -57,6 +57,7 @@ func IsOriginCall(m *gno.Machine) bool { func TestSkipHeights(m *gno.Machine, count int64) { ctx := m.Context.(*TestExecContext) ctx.Height += count + ctx.Timestamp += (count * 5) m.Context = ctx } diff --git a/misc/docs-linter/go.mod b/misc/docs-linter/go.mod new file mode 100644 index 00000000000..2c2840e7a6d --- /dev/null +++ b/misc/docs-linter/go.mod @@ -0,0 +1,19 @@ +module linter + +go 1.21.6 + +require ( + github.com/gnolang/gno v0.0.0-20240516161351-0c9849a8ef0c + github.com/stretchr/testify v1.9.0 + golang.org/x/sync v0.7.0 + mvdan.cc/xurls/v2 v2.5.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/peterbourgon/ff/v3 v3.4.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/term v0.18.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/misc/docs-linter/go.sum b/misc/docs-linter/go.sum new file mode 100644 index 00000000000..ab8c3cf7c48 --- /dev/null +++ b/misc/docs-linter/go.sum @@ -0,0 +1,22 @@ +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= +github.com/gnolang/gno v0.0.0-20240516161351-0c9849a8ef0c h1:jtZ+oN8ZpBM0wYbcFH0B7NjFFzTFqZZmZellSSKtaCE= +github.com/gnolang/gno v0.0.0-20240516161351-0c9849a8ef0c/go.mod h1:YcZbtNIfXVn4jS1pSG8SeG5RVHjyI7FPS3GypZaXxCI= +github.com/peterbourgon/ff/v3 v3.4.0 h1:QBvM/rizZM1cB0p0lGMdmR7HxZeI/ZrBWB4DqLkMUBc= +github.com/peterbourgon/ff/v3 v3.4.0/go.mod h1:zjJVUhx+twciwfDl0zBcFzl4dW8axCRyXE/eKY9RztQ= +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/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +mvdan.cc/xurls/v2 v2.5.0 h1:lyBNOm8Wo71UknhUs4QTFUNNMyxy2JEIaKKo0RWOh+8= +mvdan.cc/xurls/v2 v2.5.0/go.mod h1:yQgaGQ1rFtJUzkmKiHYSSfuQxqfYmd//X6PxvholpeE= diff --git a/misc/docs-linter/main.go b/misc/docs-linter/main.go new file mode 100644 index 00000000000..029b2bf387a --- /dev/null +++ b/misc/docs-linter/main.go @@ -0,0 +1,218 @@ +package main + +import ( + "bufio" + "context" + "errors" + "flag" + "fmt" + "github.com/gnolang/gno/tm2/pkg/commands" + "golang.org/x/sync/errgroup" + "io" + "mvdan.cc/xurls/v2" + "net/http" + "os" + "path/filepath" + "strings" + "sync" +) + +var ( + errEmptyPath = errors.New("you need to pass in a path to scan") + err404Link = errors.New("link returned a 404") + errFound404Links = errors.New("found links resulting in a 404 response status") +) + +type cfg struct { + docsPath string +} + +func main() { + cfg := &cfg{} + + cmd := commands.NewCommand( + commands.Metadata{ + Name: "docs-linter", + ShortUsage: "docs-linter [flags]", + ShortHelp: "Finds broken 404 links in the .md files in the given folder & subfolders", + }, + cfg, + func(ctx context.Context, args []string) error { + return execLint(cfg, ctx) + }) + + cmd.Execute(context.Background(), os.Args[1:]) +} + +func (c *cfg) RegisterFlags(fs *flag.FlagSet) { + fs.StringVar( + &c.docsPath, + "path", + "./", + "path to dir to walk for .md files", + ) +} + +func execLint(cfg *cfg, ctx context.Context) error { + if cfg.docsPath == "" { + return errEmptyPath + } + + fmt.Println("Linting docs/") + + mdFiles, err := findFilePaths(cfg.docsPath) + if err != nil { + return fmt.Errorf("error finding .md files: %w", err) + } + + urlFileMap := make(map[string]string) + for _, filePath := range mdFiles { + // Extract URLs from each file + urls, err := extractUrls(filePath) + if err != nil { + fmt.Printf("Error extracting URLs from file: %s, %v", filePath, err) + continue + } + // For each url, save what file it was found in + for url, file := range urls { + urlFileMap[url] = file + } + } + + // Filter links by prefix & ignore localhost + var validUrls []string + for url := range urlFileMap { + // Look for http & https only + if strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://") { + // Ignore localhost + if !strings.Contains(url, "localhost") && !strings.Contains(url, "127.0.0.1") { + validUrls = append(validUrls, url) + } + } + } + + // Setup parallel checking for links + g, _ := errgroup.WithContext(ctx) + + var ( + lock sync.Mutex + notFoundUrls []string + ) + + for _, url := range validUrls { + url := url + g.Go(func() error { + if err := checkUrl(url); err != nil { + lock.Lock() + notFoundUrls = append(notFoundUrls, fmt.Sprintf(">>> %s (found in file: %s)", url, urlFileMap[url])) + lock.Unlock() + } + + return nil + }) + } + + if err := g.Wait(); err != nil { + return err + } + + // Print out the URLs that returned a 404 along with the file names + if len(notFoundUrls) > 0 { + for _, result := range notFoundUrls { + fmt.Println(result) + } + + return errFound404Links + } + + return nil +} + +// findFilePaths gathers the file paths for specific file types +func findFilePaths(startPath string) ([]string, error) { + filePaths := make([]string, 0) + + walkFn := func(path string, info os.FileInfo, err error) error { + if err != nil { + return fmt.Errorf("error accessing file: %w", err) + } + + // Check if the file is a dir + if info.IsDir() { + return nil + } + + // Check if the file type matches + if !strings.HasSuffix(info.Name(), ".md") { + return nil + } + + // File is not a directory + filePaths = append(filePaths, path) + + return nil + } + + // Walk the directory root recursively + if walkErr := filepath.Walk(startPath, walkFn); walkErr != nil { + return nil, fmt.Errorf("unable to walk directory, %w", walkErr) + } + + return filePaths, nil +} + +// extractUrls extracts URLs from a file and maps them to the file +func extractUrls(filePath string) (map[string]string, error) { + file, err := os.Open(filePath) + if err != nil { + return nil, err + } + + cleanup := func() error { + if closeErr := file.Close(); closeErr != nil { + return fmt.Errorf("unable to gracefully close file, %w", closeErr) + } + return nil + } + + scanner := bufio.NewScanner(file) + urls := make(map[string]string) + + // Scan file line by line + for scanner.Scan() { + line := scanner.Text() + + // Extract links + rxStrict := xurls.Strict() + url := rxStrict.FindString(line) + + // Check for empty links and skip them + if url == " " || len(url) == 0 { + continue + } + + urls[url] = filePath + } + + return urls, cleanup() +} + +// checkUrl checks if a URL is a 404 +func checkUrl(url string) error { + // Attempt to retrieve the HTTP header + resp, err := http.Get(url) + if err != nil || resp.StatusCode == http.StatusNotFound { + return err404Link + } + + // Ensure the response body is closed properly + cleanup := func(Body io.ReadCloser) error { + if err := Body.Close(); err != nil { + return fmt.Errorf("could not close response properly: %w", err) + } + + return nil + } + + return cleanup(resp.Body) +} diff --git a/misc/docs-linter/main_test.go b/misc/docs-linter/main_test.go new file mode 100644 index 00000000000..ceb87fefeda --- /dev/null +++ b/misc/docs-linter/main_test.go @@ -0,0 +1,126 @@ +package main + +import ( + "context" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "os" + "path/filepath" + "sort" + "strconv" + "testing" + "time" +) + +func TestEmptyPathError(t *testing.T) { + t.Parallel() + + cfg := &cfg{ + docsPath: "", + } + + ctx, cancelFn := context.WithTimeout(context.Background(), time.Second*5) + defer cancelFn() + + assert.ErrorIs(t, execLint(cfg, ctx), errEmptyPath) +} + +func TestExtractLinks(t *testing.T) { + t.Parallel() + + // Generate temporary source dir + sourceDir, err := os.MkdirTemp(".", "sourceDir") + require.NoError(t, err) + t.Cleanup(removeDir(t, sourceDir)) + + // Create mock files with random links + mockFiles := map[string]string{ + "file1.md": "This is a test file with a link: https://example.com.\nAnother link: http://example.org.", + "file2.md": "Markdown content with a link: https://example.com/page.", + "file3.md": "Links in a list:\n- https://example.com/item1\n- https://example.org/item2", + } + + for fileName, content := range mockFiles { + filePath := filepath.Join(sourceDir, fileName) + err := os.WriteFile(filePath, []byte(content), 0644) + require.NoError(t, err) + } + + // Expected URLs and their corresponding files + expectedUrls := map[string]string{ + "https://example.com": filepath.Join(sourceDir, "file1.md"), + "http://example.org": filepath.Join(sourceDir, "file1.md"), + "https://example.com/page": filepath.Join(sourceDir, "file2.md"), + "https://example.com/item1": filepath.Join(sourceDir, "file3.md"), + "https://example.org/item2": filepath.Join(sourceDir, "file3.md"), + } + + // Extract URLs from each file in the sourceDir + for fileName := range mockFiles { + filePath := filepath.Join(sourceDir, fileName) + extractedUrls, err := extractUrls(filePath) + require.NoError(t, err) + + // Verify that the extracted URLs match the expected URLs + for url, expectedFile := range expectedUrls { + if expectedFile == filePath { + require.Equal(t, expectedFile, extractedUrls[url], "URL: %s not correctly mapped to file: %s", url, expectedFile) + } + } + } +} + +func TestFindFilePaths(t *testing.T) { + t.Parallel() + + tempDir, err := os.MkdirTemp(".", "test") + require.NoError(t, err) + t.Cleanup(removeDir(t, tempDir)) + + numSourceFiles := 20 + testFiles := make([]string, numSourceFiles) + + for i := 0; i < numSourceFiles; i++ { + testFiles[i] = "sourceFile" + strconv.Itoa(i) + ".md" + } + + for _, file := range testFiles { + filePath := filepath.Join(tempDir, file) + err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm) + require.NoError(t, err) + + _, err = os.Create(filePath) + require.NoError(t, err) + } + + results, err := findFilePaths(tempDir) + require.NoError(t, err) + + expectedResults := make([]string, 0, len(testFiles)) + + for _, testFile := range testFiles { + expectedResults = append(expectedResults, filepath.Join(tempDir, testFile)) + } + + sort.Slice(results, func(i, j int) bool { + return results[i] < results[j] + }) + + sort.Slice(expectedResults, func(i, j int) bool { + return expectedResults[i] < expectedResults[j] + }) + + require.Equal(t, len(results), len(expectedResults)) + + for i, result := range results { + if result != expectedResults[i] { + require.Equal(t, result, expectedResults[i]) + } + } +} + +func removeDir(t *testing.T, dirPath string) func() { + return func() { + require.NoError(t, os.RemoveAll(dirPath)) + } +} diff --git a/misc/genstd/template.tmpl b/misc/genstd/template.tmpl index f2cad0a851b..bfbe252a2d5 100644 --- a/misc/genstd/template.tmpl +++ b/misc/genstd/template.tmpl @@ -12,15 +12,25 @@ import ( {{- end }} ) -type nativeFunc struct { - gnoPkg string - gnoFunc gno.Name - params []gno.FieldTypeExpr - results []gno.FieldTypeExpr - f func(m *gno.Machine) +// NativeFunc represents a function in the standard library which has a native +// (go-based) implementation, commonly referred to as a "native binding". +type NativeFunc struct { + gnoPkg string + gnoFunc gno.Name + params []gno.FieldTypeExpr + results []gno.FieldTypeExpr + hasMachine bool + f func(m *gno.Machine) } -var nativeFuncs = [...]nativeFunc{ +// HasMachineParam returns whether the given native binding has a machine parameter. +// This means that the Go version of this function expects a *gno.Machine +// as its first parameter. +func (n *NativeFunc) HasMachineParam() bool { + return n.hasMachine +} + +var nativeFuncs = [...]NativeFunc{ {{- range $i, $m := .Mappings }} { {{ printf "%q" $m.GnoImportPath }}, @@ -36,6 +46,7 @@ var nativeFuncs = [...]nativeFunc{ {Name: gno.N("r{{ $i }}"), Type: gno.X({{ printf "%q" $r.GnoType }})}, {{- end }} }, + {{ if $m.MachineParam }}true{{ else }}false{{ end }}, func(m *gno.Machine) { {{ if $m.Params -}} b := m.LastBlock() diff --git a/tm2/pkg/bft/rpc/lib/client/http/client.go b/tm2/pkg/bft/rpc/lib/client/http/client.go index 34d301deba2..290310cea31 100644 --- a/tm2/pkg/bft/rpc/lib/client/http/client.go +++ b/tm2/pkg/bft/rpc/lib/client/http/client.go @@ -17,8 +17,6 @@ import ( const ( protoHTTP = "http" protoHTTPS = "https" - protoWSS = "wss" - protoWS = "ws" protoTCP = "tcp" ) @@ -173,12 +171,7 @@ func defaultHTTPClient(remoteAddr string) *http.Client { } func makeHTTPDialer(remoteAddr string) func(string, string) (net.Conn, error) { - protocol, address, err := parseRemoteAddr(remoteAddr) - if err != nil { - return func(_ string, _ string) (net.Conn, error) { - return nil, err - } - } + protocol, address := parseRemoteAddr(remoteAddr) // net.Dial doesn't understand http/https, so change it to TCP switch protocol { @@ -193,17 +186,14 @@ func makeHTTPDialer(remoteAddr string) func(string, string) (net.Conn, error) { // protocol - client's protocol (for example, "http", "https", "wss", "ws", "tcp") // trimmedS - rest of the address (for example, "192.0.2.1:25", "[2001:db8::1]:80") with "/" replaced with "." -func toClientAddrAndParse(remoteAddr string) (string, string, error) { - protocol, address, err := parseRemoteAddr(remoteAddr) - if err != nil { - return "", "", err - } +func toClientAddrAndParse(remoteAddr string) (string, string) { + protocol, address := parseRemoteAddr(remoteAddr) // protocol to use for http operations, to support both http and https var clientProtocol string // default to http for unknown protocols (ex. tcp) switch protocol { - case protoHTTP, protoHTTPS, protoWS, protoWSS: + case protoHTTP, protoHTTPS: clientProtocol = protocol default: clientProtocol = protoHTTP @@ -212,14 +202,11 @@ func toClientAddrAndParse(remoteAddr string) (string, string, error) { // replace / with . for http requests (kvstore domain) trimmedAddress := strings.Replace(address, "/", ".", -1) - return clientProtocol, trimmedAddress, nil + return clientProtocol, trimmedAddress } func toClientAddress(remoteAddr string) (string, error) { - clientProtocol, trimmedAddress, err := toClientAddrAndParse(remoteAddr) - if err != nil { - return "", err - } + clientProtocol, trimmedAddress := toClientAddrAndParse(remoteAddr) return clientProtocol + "://" + trimmedAddress, nil } @@ -227,8 +214,9 @@ func toClientAddress(remoteAddr string) (string, error) { // network - name of the network (for example, "tcp", "unix") // s - rest of the address (for example, "192.0.2.1:25", "[2001:db8::1]:80") // TODO: Deprecate support for IP:PORT or /path/to/socket -func parseRemoteAddr(remoteAddr string) (network string, s string, err error) { +func parseRemoteAddr(remoteAddr string) (string, string) { parts := strings.SplitN(remoteAddr, "://", 2) + var protocol, address string switch len(parts) { case 1: @@ -237,7 +225,19 @@ func parseRemoteAddr(remoteAddr string) (network string, s string, err error) { case 2: protocol, address = parts[0], parts[1] } - return protocol, address, nil + + // Append default ports if not specified + if !strings.Contains(address, ":") { + switch protocol { + case protoHTTPS: + address += ":443" + case protoHTTP, protoTCP: + address += ":80" + default: // noop + } + } + + return protocol, address } // isOKStatus returns a boolean indicating if the response diff --git a/tm2/pkg/bft/rpc/lib/client/http/client_test.go b/tm2/pkg/bft/rpc/lib/client/http/client_test.go index 7c4b1e52ac5..cfe69c3015c 100644 --- a/tm2/pkg/bft/rpc/lib/client/http/client_test.go +++ b/tm2/pkg/bft/rpc/lib/client/http/client_test.go @@ -17,24 +17,39 @@ func TestClient_parseRemoteAddr(t *testing.T) { t.Parallel() testTable := []struct { - remoteAddr string - network string - rest string + remoteAddr string + expectedNetwork string + expectedRest string }{ { "127.0.0.1", "tcp", - "127.0.0.1", + "127.0.0.1:80", + }, + { + "127.0.0.1:5000", + "tcp", + "127.0.0.1:5000", + }, + { + "http://example.com", + "http", + "example.com:80", }, { "https://example.com", "https", - "example.com", + "example.com:443", }, { - "wss://[::1]", - "wss", - "[::1]", + "http://example.com:5000", + "http", + "example.com:5000", + }, + { + "https://example.com:5000", + "https", + "example.com:5000", }, } @@ -44,11 +59,10 @@ func TestClient_parseRemoteAddr(t *testing.T) { t.Run(testCase.remoteAddr, func(t *testing.T) { t.Parallel() - n, r, err := parseRemoteAddr(testCase.remoteAddr) - require.NoError(t, err) + n, r := parseRemoteAddr(testCase.remoteAddr) - assert.Equal(t, n, testCase.network) - assert.Equal(t, r, testCase.rest) + assert.Equal(t, testCase.expectedNetwork, n) + assert.Equal(t, testCase.expectedRest, r) }) } } @@ -66,7 +80,6 @@ func TestClient_makeHTTPDialer(t *testing.T) { require.Error(t, err) assert.Contains(t, err.Error(), "dial tcp:", "should convert https to tcp") - assert.Contains(t, err.Error(), "address .:", "should have parsed the address (as incorrect)") }) t.Run("udp", func(t *testing.T) { @@ -76,7 +89,6 @@ func TestClient_makeHTTPDialer(t *testing.T) { require.Error(t, err) assert.Contains(t, err.Error(), "dial udp:", "udp protocol should remain the same") - assert.Contains(t, err.Error(), "address .:", "should have parsed the address (as incorrect)") }) } diff --git a/tm2/pkg/errors/errors.go b/tm2/pkg/errors/errors.go index c07356f0b06..c72d9c64680 100644 --- a/tm2/pkg/errors/errors.go +++ b/tm2/pkg/errors/errors.go @@ -237,6 +237,9 @@ type FmtError struct { } func (fe FmtError) Error() string { + if len(fe.args) == 0 { + return fe.format + } return fmt.Sprintf(fe.format, fe.args...) } diff --git a/tm2/pkg/sdk/sdk.proto b/tm2/pkg/sdk/sdk.proto index 828b17950cf..62fbfc19758 100644 --- a/tm2/pkg/sdk/sdk.proto +++ b/tm2/pkg/sdk/sdk.proto @@ -12,4 +12,4 @@ message Result { abci.ResponseBase response_base = 1 [json_name = "ResponseBase"]; sint64 gas_wanted = 2 [json_name = "GasWanted"]; sint64 gas_used = 3 [json_name = "GasUsed"]; -} +} \ No newline at end of file