diff --git a/examples/gno.land/r/gnoland/pages/page_contribute.gno b/examples/gno.land/r/gnoland/pages/page_contribute.gno
index 3cdef10d9dc..0855dc327cd 100644
--- a/examples/gno.land/r/gnoland/pages/page_contribute.gno
+++ b/examples/gno.land/r/gnoland/pages/page_contribute.gno
@@ -45,7 +45,7 @@ Don't fear your work being "stolen": if a submission is the result of multiple p
- If you, for instance, cannot complete the entirety of the task or, as a non-developer, can only contribute a part of the specification/implementation, you may still be awarded a bounty for your input in the contribution.
- If Alice makes a PR that aside from implementing what's required, also undertakes creating useful tools among the way, she may qualify for an "outstanding contribution"; and may be awarded up to 25% more of the original bounty's value. Or she may also ask if the team would be willing to offer a different bounty for the implementation of the tools.
-Participants in the gno.land Bounty Program must meet the legal Terms and Conditions referenced [here](https://docs.google.com/document/d/1aXrZ6japdAykB5FLmHCCeBZTo-2tbZQHSQi79ITaTK0).
+Participants in the gno.land Bounty Program must meet the legal Terms and Conditions referenced [here](https://docs.google.com/document/d/e/2PACX-1vSUF-JwIXGscrNsc5QBD7Pa6i83mXUGogAEIf1wkeb_w42UgL3Lj6jFKMlNTdwEMUnhsLkjRlhe25K4/pub).
### Bounty sizes
@@ -80,7 +80,7 @@ _[3XL]_ \* | $ 32000
The gno.land grants program is to encourage and support the growth of the gno.land contributor community, and build out the usability of the platform and smart contract library. The program provides financial resources to contributors to explore the Gno tech stack, and build dApps, tooling, infrastructure, products, and smart contract libraries in gno.land.
-
+For more details on gno.land grants, suggested topics, and how to apply, visit our grants [repository](https://github.com/gnolang/grants).
## Join Game of Realms
diff --git a/examples/gno.land/r/gnoland/pages/page_testnets.gno b/examples/gno.land/r/gnoland/pages/page_testnets.gno
index 05f29a8e0f4..900ee2e3bf7 100644
--- a/examples/gno.land/r/gnoland/pages/page_testnets.gno
+++ b/examples/gno.land/r/gnoland/pages/page_testnets.gno
@@ -6,10 +6,7 @@ func init() {
body := `
- [Portal Loop](https://docs.gno.land/concepts/portal-loop) - a rolling testnet
- [staging.gno.land](https://staging.gno.land) - wiped every commit to monorepo master
-- test4.gno.land (upcoming)
-- _[test3.gno.land](https://test3.gno.land) (latest)_
-- _[test2.gno.land](https://test2.gno.land) (archive)_
-- _[test1.gno.land](https://test1.gno.land) (archive)_
+- _[test4.gno.land](https://test4.gno.land) (latest)_
For a list of RPC endpoints, see the [reference documentation](https://docs.gno.land/reference/rpc-endpoints).
diff --git a/examples/gno.land/r/gnoland/valopers/gno.mod b/examples/gno.land/r/gnoland/valopers/v2/gno.mod
similarity index 56%
rename from examples/gno.land/r/gnoland/valopers/gno.mod
rename to examples/gno.land/r/gnoland/valopers/v2/gno.mod
index 2d24fb27952..099a8406db4 100644
--- a/examples/gno.land/r/gnoland/valopers/gno.mod
+++ b/examples/gno.land/r/gnoland/valopers/v2/gno.mod
@@ -1,11 +1,12 @@
-module gno.land/r/gnoland/valopers
+module gno.land/r/gnoland/valopers/v2
require (
gno.land/p/demo/avl v0.0.0-latest
+ gno.land/p/demo/dao v0.0.0-latest
gno.land/p/demo/testutils v0.0.0-latest
gno.land/p/demo/uassert v0.0.0-latest
gno.land/p/demo/ufmt v0.0.0-latest
gno.land/p/sys/validators v0.0.0-latest
- gno.land/r/gov/dao v0.0.0-latest
- gno.land/r/sys/validators v0.0.0-latest
+ gno.land/r/gov/dao/bridge v0.0.0-latest
+ gno.land/r/sys/validators/v2 v0.0.0-latest
)
diff --git a/examples/gno.land/r/gnoland/valopers/init.gno b/examples/gno.land/r/gnoland/valopers/v2/init.gno
similarity index 100%
rename from examples/gno.land/r/gnoland/valopers/init.gno
rename to examples/gno.land/r/gnoland/valopers/v2/init.gno
diff --git a/examples/gno.land/r/gnoland/valopers/valopers.gno b/examples/gno.land/r/gnoland/valopers/v2/valopers.gno
similarity index 90%
rename from examples/gno.land/r/gnoland/valopers/valopers.gno
rename to examples/gno.land/r/gnoland/valopers/v2/valopers.gno
index 74cec941e0d..d88ea4b872f 100644
--- a/examples/gno.land/r/gnoland/valopers/valopers.gno
+++ b/examples/gno.land/r/gnoland/valopers/v2/valopers.gno
@@ -6,10 +6,11 @@ import (
"std"
"gno.land/p/demo/avl"
+ "gno.land/p/demo/dao"
"gno.land/p/demo/ufmt"
pVals "gno.land/p/sys/validators"
- govdao "gno.land/r/gov/dao"
- "gno.land/r/sys/validators"
+ "gno.land/r/gov/dao/bridge"
+ validators "gno.land/r/sys/validators/v2"
)
const (
@@ -25,6 +26,7 @@ var valopers *avl.Tree // Address -> Valoper
// Valoper represents a validator operator profile
type Valoper struct {
Name string // the display name of the valoper
+ Moniker string // the moniker of the valoper
Description string // the description of the valoper
Address std.Address // The bech32 gno address of the validator
@@ -101,7 +103,7 @@ func Render(_ string) string {
// Render renders a single valoper with their information
func (v Valoper) Render() string {
- output := ufmt.Sprintf("## %s\n", v.Name)
+ output := ufmt.Sprintf("## %s (%s)\n", v.Name, v.Moniker)
output += ufmt.Sprintf("%s\n\n", v.Description)
output += ufmt.Sprintf("- Address: %s\n", v.Address.String())
output += ufmt.Sprintf("- PubKey: %s\n", v.PubKey)
@@ -168,14 +170,19 @@ func GovDAOProposal(address std.Address) {
// Create the executor
executor := validators.NewPropExecutor(changesFn)
- // Craft the proposal comment
- comment := ufmt.Sprintf(
- "Proposal to add valoper %s (Address: %s; PubKey: %s) to the valset",
+ // Craft the proposal description
+ description := ufmt.Sprintf(
+ "Add valoper %s (Address: %s; PubKey: %s) to the valset",
valoper.Name,
valoper.Address.String(),
valoper.PubKey,
)
+ prop := dao.ProposalRequest{
+ Description: description,
+ Executor: executor,
+ }
+
// Create the govdao proposal
- govdao.Propose(comment, executor)
+ bridge.GovDAO().Propose(prop)
}
diff --git a/examples/gno.land/r/gnoland/valopers/valopers_test.gno b/examples/gno.land/r/gnoland/valopers/v2/valopers_test.gno
similarity index 97%
rename from examples/gno.land/r/gnoland/valopers/valopers_test.gno
rename to examples/gno.land/r/gnoland/valopers/v2/valopers_test.gno
index 89544c46ee5..b5940738769 100644
--- a/examples/gno.land/r/gnoland/valopers/valopers_test.gno
+++ b/examples/gno.land/r/gnoland/valopers/v2/valopers_test.gno
@@ -38,6 +38,7 @@ func TestValopers_Register(t *testing.T) {
v := Valoper{
Address: testutils.TestAddress("valoper"),
Name: "new valoper",
+ Moniker: "val-1",
PubKey: "pub key",
}
@@ -50,6 +51,7 @@ func TestValopers_Register(t *testing.T) {
uassert.Equal(t, v.Address, valoper.Address)
uassert.Equal(t, v.Name, valoper.Name)
+ uassert.Equal(t, v.Moniker, valoper.Moniker)
uassert.Equal(t, v.PubKey, valoper.PubKey)
})
})
diff --git a/examples/gno.land/r/gov/dao/bridge/bridge.gno b/examples/gno.land/r/gov/dao/bridge/bridge.gno
new file mode 100644
index 00000000000..ba47978f33f
--- /dev/null
+++ b/examples/gno.land/r/gov/dao/bridge/bridge.gno
@@ -0,0 +1,39 @@
+package bridge
+
+import (
+ "std"
+
+ "gno.land/p/demo/ownable"
+)
+
+const initialOwner = std.Address("g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq") // @moul
+
+var b *Bridge
+
+// Bridge is the active GovDAO
+// implementation bridge
+type Bridge struct {
+ *ownable.Ownable
+
+ dao DAO
+}
+
+// init constructs the initial GovDAO implementation
+func init() {
+ b = &Bridge{
+ Ownable: ownable.NewWithAddress(initialOwner),
+ dao: &govdaoV2{},
+ }
+}
+
+// SetDAO sets the currently active GovDAO implementation
+func SetDAO(dao DAO) {
+ b.AssertCallerIsOwner()
+
+ b.dao = dao
+}
+
+// GovDAO returns the current GovDAO implementation
+func GovDAO() DAO {
+ return b.dao
+}
diff --git a/examples/gno.land/r/gov/dao/bridge/bridge_test.gno b/examples/gno.land/r/gov/dao/bridge/bridge_test.gno
new file mode 100644
index 00000000000..38b5d4be257
--- /dev/null
+++ b/examples/gno.land/r/gov/dao/bridge/bridge_test.gno
@@ -0,0 +1,64 @@
+package bridge
+
+import (
+ "testing"
+
+ "std"
+
+ "gno.land/p/demo/dao"
+ "gno.land/p/demo/ownable"
+ "gno.land/p/demo/testutils"
+ "gno.land/p/demo/uassert"
+ "gno.land/p/demo/urequire"
+)
+
+func TestBridge_DAO(t *testing.T) {
+ var (
+ proposalID = uint64(10)
+ mockDAO = &mockDAO{
+ proposeFn: func(_ dao.ProposalRequest) uint64 {
+ return proposalID
+ },
+ }
+ )
+
+ b.dao = mockDAO
+
+ uassert.Equal(t, proposalID, GovDAO().Propose(dao.ProposalRequest{}))
+}
+
+func TestBridge_SetDAO(t *testing.T) {
+ t.Run("invalid owner", func(t *testing.T) {
+ // Attempt to set a new DAO implementation
+ uassert.PanicsWithMessage(t, ownable.ErrUnauthorized.Error(), func() {
+ SetDAO(&mockDAO{})
+ })
+ })
+
+ t.Run("valid owner", func(t *testing.T) {
+ var (
+ addr = testutils.TestAddress("owner")
+
+ proposalID = uint64(10)
+ mockDAO = &mockDAO{
+ proposeFn: func(_ dao.ProposalRequest) uint64 {
+ return proposalID
+ },
+ }
+ )
+
+ std.TestSetOrigCaller(addr)
+
+ b.Ownable = ownable.NewWithAddress(addr)
+
+ urequire.NotPanics(t, func() {
+ SetDAO(mockDAO)
+ })
+
+ uassert.Equal(
+ t,
+ mockDAO.Propose(dao.ProposalRequest{}),
+ GovDAO().Propose(dao.ProposalRequest{}),
+ )
+ })
+}
diff --git a/examples/gno.land/r/gov/dao/bridge/doc.gno b/examples/gno.land/r/gov/dao/bridge/doc.gno
new file mode 100644
index 00000000000..f812b3c0787
--- /dev/null
+++ b/examples/gno.land/r/gov/dao/bridge/doc.gno
@@ -0,0 +1,4 @@
+// Package bridge represents a GovDAO implementation wrapper, used by other Realms and Packages to
+// always fetch the most active GovDAO implementation, instead of directly referencing it, and having to
+// update it each time the GovDAO implementation changes
+package bridge
diff --git a/examples/gno.land/r/gov/dao/bridge/gno.mod b/examples/gno.land/r/gov/dao/bridge/gno.mod
new file mode 100644
index 00000000000..3382557573a
--- /dev/null
+++ b/examples/gno.land/r/gov/dao/bridge/gno.mod
@@ -0,0 +1,11 @@
+module gno.land/r/gov/dao/bridge
+
+require (
+ gno.land/p/demo/dao v0.0.0-latest
+ gno.land/p/demo/membstore v0.0.0-latest
+ gno.land/p/demo/ownable v0.0.0-latest
+ gno.land/p/demo/testutils v0.0.0-latest
+ gno.land/p/demo/uassert v0.0.0-latest
+ gno.land/p/demo/urequire v0.0.0-latest
+ gno.land/r/gov/dao/v2 v0.0.0-latest
+)
diff --git a/examples/gno.land/r/gov/dao/bridge/mock_test.gno b/examples/gno.land/r/gov/dao/bridge/mock_test.gno
new file mode 100644
index 00000000000..05ac430b4c4
--- /dev/null
+++ b/examples/gno.land/r/gov/dao/bridge/mock_test.gno
@@ -0,0 +1,68 @@
+package bridge
+
+import (
+ "gno.land/p/demo/dao"
+ "gno.land/p/demo/membstore"
+)
+
+type (
+ proposeDelegate func(dao.ProposalRequest) uint64
+ voteOnProposalDelegate func(uint64, dao.VoteOption)
+ executeProposalDelegate func(uint64)
+ getPropStoreDelegate func() dao.PropStore
+ getMembStoreDelegate func() membstore.MemberStore
+ newGovDAOExecutorDelegate func(func() error) dao.Executor
+)
+
+type mockDAO struct {
+ proposeFn proposeDelegate
+ voteOnProposalFn voteOnProposalDelegate
+ executeProposalFn executeProposalDelegate
+ getPropStoreFn getPropStoreDelegate
+ getMembStoreFn getMembStoreDelegate
+ newGovDAOExecutorFn newGovDAOExecutorDelegate
+}
+
+func (m *mockDAO) Propose(request dao.ProposalRequest) uint64 {
+ if m.proposeFn != nil {
+ return m.proposeFn(request)
+ }
+
+ return 0
+}
+
+func (m *mockDAO) VoteOnProposal(id uint64, option dao.VoteOption) {
+ if m.voteOnProposalFn != nil {
+ m.voteOnProposalFn(id, option)
+ }
+}
+
+func (m *mockDAO) ExecuteProposal(id uint64) {
+ if m.executeProposalFn != nil {
+ m.executeProposalFn(id)
+ }
+}
+
+func (m *mockDAO) GetPropStore() dao.PropStore {
+ if m.getPropStoreFn != nil {
+ return m.getPropStoreFn()
+ }
+
+ return nil
+}
+
+func (m *mockDAO) GetMembStore() membstore.MemberStore {
+ if m.getMembStoreFn != nil {
+ return m.getMembStoreFn()
+ }
+
+ return nil
+}
+
+func (m *mockDAO) NewGovDAOExecutor(cb func() error) dao.Executor {
+ if m.newGovDAOExecutorFn != nil {
+ return m.newGovDAOExecutorFn(cb)
+ }
+
+ return nil
+}
diff --git a/examples/gno.land/r/gov/dao/bridge/types.gno b/examples/gno.land/r/gov/dao/bridge/types.gno
new file mode 100644
index 00000000000..27ea8fb62d4
--- /dev/null
+++ b/examples/gno.land/r/gov/dao/bridge/types.gno
@@ -0,0 +1,17 @@
+package bridge
+
+import (
+ "gno.land/p/demo/dao"
+ "gno.land/p/demo/membstore"
+)
+
+// DAO abstracts the commonly used DAO interface
+type DAO interface {
+ Propose(dao.ProposalRequest) uint64
+ VoteOnProposal(uint64, dao.VoteOption)
+ ExecuteProposal(uint64)
+ GetPropStore() dao.PropStore
+ GetMembStore() membstore.MemberStore
+
+ NewGovDAOExecutor(func() error) dao.Executor
+}
diff --git a/examples/gno.land/r/gov/dao/bridge/v2.gno b/examples/gno.land/r/gov/dao/bridge/v2.gno
new file mode 100644
index 00000000000..216419cf31d
--- /dev/null
+++ b/examples/gno.land/r/gov/dao/bridge/v2.gno
@@ -0,0 +1,42 @@
+package bridge
+
+import (
+ "gno.land/p/demo/dao"
+ "gno.land/p/demo/membstore"
+ govdao "gno.land/r/gov/dao/v2"
+)
+
+// govdaoV2 is a wrapper for interacting with the /r/gov/dao/v2 Realm
+type govdaoV2 struct{}
+
+func (g *govdaoV2) Propose(request dao.ProposalRequest) uint64 {
+ return govdao.Propose(request)
+}
+
+func (g *govdaoV2) VoteOnProposal(id uint64, option dao.VoteOption) {
+ govdao.VoteOnProposal(id, option)
+}
+
+func (g *govdaoV2) ExecuteProposal(id uint64) {
+ govdao.ExecuteProposal(id)
+}
+
+func (g *govdaoV2) GetPropStore() dao.PropStore {
+ return govdao.GetPropStore()
+}
+
+func (g *govdaoV2) GetMembStore() membstore.MemberStore {
+ return govdao.GetMembStore()
+}
+
+func (g *govdaoV2) NewGovDAOExecutor(cb func() error) dao.Executor {
+ return govdao.NewGovDAOExecutor(cb)
+}
+
+func (g *govdaoV2) NewMemberPropExecutor(cb func() []membstore.Member) dao.Executor {
+ return govdao.NewMemberPropExecutor(cb)
+}
+
+func (g *govdaoV2) NewMembStoreImplExecutor(cb func() membstore.MemberStore) dao.Executor {
+ return govdao.NewMembStoreImplExecutor(cb)
+}
diff --git a/examples/gno.land/r/gov/dao/dao.gno b/examples/gno.land/r/gov/dao/dao.gno
deleted file mode 100644
index 632935dafed..00000000000
--- a/examples/gno.land/r/gov/dao/dao.gno
+++ /dev/null
@@ -1,207 +0,0 @@
-package govdao
-
-import (
- "std"
- "strconv"
-
- "gno.land/p/demo/ufmt"
- pproposal "gno.land/p/gov/proposal"
-)
-
-var (
- proposals = make([]*proposal, 0)
- members = make([]std.Address, 0) // XXX: these should be pointers to avoid data duplication. Not possible due to VM bugs
-)
-
-const (
- msgMissingExecutor = "missing proposal executor"
- msgPropExecuted = "prop already executed"
- msgPropExpired = "prop is expired"
- msgPropInactive = "prop is not active anymore"
- msgPropActive = "prop is still active"
- msgPropNotAccepted = "prop is not accepted"
-
- msgCallerNotAMember = "caller is not member of govdao"
- msgProposalNotFound = "proposal not found"
-)
-
-type proposal struct {
- author std.Address
- comment string
- executor pproposal.Executor
- voter Voter
- executed bool
- voters []std.Address // XXX: these should be pointers to avoid data duplication. Not possible due to VM bugs.
-}
-
-func (p proposal) Status() Status {
- if p.executor.IsExpired() {
- return Expired
- }
-
- if p.executor.IsDone() {
- return Succeeded
- }
-
- if !p.voter.IsFinished(members) {
- return Active
- }
-
- if p.voter.IsAccepted(members) {
- return Accepted
- }
-
- return NotAccepted
-}
-
-// Propose is designed to be called by another contract or with
-// `maketx run`, not by a `maketx call`.
-func Propose(comment string, executor pproposal.Executor) int {
- // XXX: require payment?
- if executor == nil {
- panic(msgMissingExecutor)
- }
- caller := std.GetOrigCaller() // XXX: CHANGE THIS WHEN MSGRUN PERSIST CODE ESCAPING THE main() SCOPE! IT IS UNSAFE!
- AssertIsMember(caller)
-
- prop := &proposal{
- comment: comment,
- executor: executor,
- author: caller,
- voter: NewPercentageVoter(66), // at least 2/3 must say yes
- }
-
- proposals = append(proposals, prop)
-
- return len(proposals) - 1
-}
-
-func VoteOnProposal(idx int, option string) {
- assertProposalExists(idx)
- caller := std.GetOrigCaller() // XXX: CHANGE THIS WHEN MSGRUN PERSIST CODE ESCAPING THE main() SCOPE! IT IS UNSAFE!
- AssertIsMember(caller)
-
- prop := getProposal(idx)
-
- if prop.executed {
- panic(msgPropExecuted)
- }
-
- if prop.executor.IsExpired() {
- panic(msgPropExpired)
- }
-
- if prop.voter.IsFinished(members) {
- panic(msgPropInactive)
- }
-
- prop.voter.Vote(members, caller, option)
-}
-
-func ExecuteProposal(idx int) {
- assertProposalExists(idx)
- prop := getProposal(idx)
-
- if prop.executed {
- panic(msgPropExecuted)
- }
-
- if prop.executor.IsExpired() {
- panic(msgPropExpired)
- }
-
- if !prop.voter.IsFinished(members) {
- panic(msgPropActive)
- }
-
- if !prop.voter.IsAccepted(members) {
- panic(msgPropNotAccepted)
- }
-
- prop.executor.Execute()
- prop.voters = members
- prop.executed = true
-}
-
-func IsMember(addr std.Address) bool {
- if len(members) == 0 { // special case for initial execution
- return true
- }
-
- for _, v := range members {
- if v == addr {
- return true
- }
- }
-
- return false
-}
-
-func AssertIsMember(addr std.Address) {
- if !IsMember(addr) {
- panic(msgCallerNotAMember)
- }
-}
-
-func Render(path string) string {
- if path == "" {
- if len(proposals) == 0 {
- return "No proposals found :(" // corner case
- }
-
- output := ""
- for idx, prop := range proposals {
- output += ufmt.Sprintf("- [%d](/r/gov/dao:%d) - %s (**%s**)(by %s)\n", idx, idx, prop.comment, string(prop.Status()), 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)
-
- vs := members
- if prop.executed {
- vs = prop.voters
- }
-
- output := ""
- output += ufmt.Sprintf("# Prop #%d", idx)
- output += "\n\n"
- output += prop.comment
- output += "\n\n"
- output += ufmt.Sprintf("Status: %s", string(prop.Status()))
- output += "\n\n"
- output += ufmt.Sprintf("Voting status: %s", prop.voter.Status(vs))
- output += "\n\n"
- output += ufmt.Sprintf("Author: %s", string(prop.author))
- output += "\n\n"
-
- return output
-}
-
-func getProposal(idx int) *proposal {
- if idx > len(proposals)-1 {
- panic(msgProposalNotFound)
- }
-
- return proposals[idx]
-}
-
-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/dao_test.gno b/examples/gno.land/r/gov/dao/dao_test.gno
deleted file mode 100644
index 96eaba7f5e9..00000000000
--- a/examples/gno.land/r/gov/dao/dao_test.gno
+++ /dev/null
@@ -1,192 +0,0 @@
-package govdao
-
-import (
- "std"
- "testing"
-
- "gno.land/p/demo/testutils"
- "gno.land/p/demo/urequire"
- pproposal "gno.land/p/gov/proposal"
-)
-
-func TestPackage(t *testing.T) {
- u1 := testutils.TestAddress("u1")
- u2 := testutils.TestAddress("u2")
- u3 := testutils.TestAddress("u3")
-
- members = append(members, u1)
- members = append(members, u2)
- members = append(members, u3)
-
- nu1 := testutils.TestAddress("random1")
-
- out := Render("")
-
- expected := "No proposals found :("
- urequire.Equal(t, expected, out)
-
- var called bool
- ex := pproposal.NewExecutor(func() error {
- called = true
- return nil
- })
-
- std.TestSetOrigCaller(u1)
- pid := Propose("dummy proposal", ex)
-
- // try to vote not being a member
- std.TestSetOrigCaller(nu1)
-
- urequire.PanicsWithMessage(t, msgCallerNotAMember, func() {
- VoteOnProposal(pid, "YES")
- })
-
- // try to vote several times
- std.TestSetOrigCaller(u1)
- urequire.NotPanics(t, func() {
- VoteOnProposal(pid, "YES")
- })
- urequire.PanicsWithMessage(t, msgAlreadyVoted, func() {
- VoteOnProposal(pid, "YES")
- })
-
- out = Render("0")
- expected = `# Prop #0
-
-dummy proposal
-
-Status: active
-
-Voting status: YES: 1, NO: 0, percent: 33, members: 3
-
-Author: g1w5c47h6lta047h6lta047h6lta047h6ly5kscr
-
-`
-
- urequire.Equal(t, expected, out)
-
- std.TestSetOrigCaller(u2)
- urequire.PanicsWithMessage(t, msgWrongVotingValue, func() {
- VoteOnProposal(pid, "INCORRECT")
- })
- urequire.NotPanics(t, func() {
- VoteOnProposal(pid, "NO")
- })
-
- out = Render("0")
- expected = `# Prop #0
-
-dummy proposal
-
-Status: active
-
-Voting status: YES: 1, NO: 1, percent: 33, members: 3
-
-Author: g1w5c47h6lta047h6lta047h6lta047h6ly5kscr
-
-`
-
- urequire.Equal(t, expected, out)
-
- std.TestSetOrigCaller(u3)
- urequire.NotPanics(t, func() {
- VoteOnProposal(pid, "YES")
- })
-
- out = Render("0")
- expected = `# Prop #0
-
-dummy proposal
-
-Status: accepted
-
-Voting status: YES: 2, NO: 1, percent: 66, members: 3
-
-Author: g1w5c47h6lta047h6lta047h6lta047h6ly5kscr
-
-`
-
- urequire.Equal(t, expected, out)
-
- // Add a new member, so non-executed proposals will change the voting status
- u4 := testutils.TestAddress("u4")
- members = append(members, u4)
-
- out = Render("0")
- expected = `# Prop #0
-
-dummy proposal
-
-Status: active
-
-Voting status: YES: 2, NO: 1, percent: 50, members: 4
-
-Author: g1w5c47h6lta047h6lta047h6lta047h6ly5kscr
-
-`
-
- urequire.Equal(t, expected, out)
-
- std.TestSetOrigCaller(u4)
- urequire.NotPanics(t, func() {
- VoteOnProposal(pid, "YES")
- })
-
- out = Render("0")
- expected = `# Prop #0
-
-dummy proposal
-
-Status: accepted
-
-Voting status: YES: 3, NO: 1, percent: 75, members: 4
-
-Author: g1w5c47h6lta047h6lta047h6lta047h6ly5kscr
-
-`
-
- urequire.Equal(t, expected, out)
-
- ExecuteProposal(pid)
- urequire.True(t, called)
-
- out = Render("0")
- expected = `# Prop #0
-
-dummy proposal
-
-Status: succeeded
-
-Voting status: YES: 3, NO: 1, percent: 75, members: 4
-
-Author: g1w5c47h6lta047h6lta047h6lta047h6ly5kscr
-
-`
-
- urequire.Equal(t, expected, out)
-
- // Add a new member and try to vote an already executed proposal
- u5 := testutils.TestAddress("u5")
- members = append(members, u5)
- std.TestSetOrigCaller(u5)
- urequire.PanicsWithMessage(t, msgPropExecuted, func() {
- ExecuteProposal(pid)
- })
-
- // even if we added a new member the executed proposal is showing correctly the members that voted on it
- out = Render("0")
- expected = `# Prop #0
-
-dummy proposal
-
-Status: succeeded
-
-Voting status: YES: 3, NO: 1, percent: 75, members: 4
-
-Author: g1w5c47h6lta047h6lta047h6lta047h6ly5kscr
-
-`
-
- urequire.Equal(t, expected, out)
-
-}
diff --git a/examples/gno.land/r/gov/dao/memberset.gno b/examples/gno.land/r/gov/dao/memberset.gno
deleted file mode 100644
index 3abd52ae99d..00000000000
--- a/examples/gno.land/r/gov/dao/memberset.gno
+++ /dev/null
@@ -1,40 +0,0 @@
-package govdao
-
-import (
- "std"
-
- pproposal "gno.land/p/gov/proposal"
-)
-
-const daoPkgPath = "gno.land/r/gov/dao"
-
-const (
- errNoChangesProposed = "no set changes proposed"
- errNotGovDAO = "caller not govdao executor"
-)
-
-func NewPropExecutor(changesFn func() []std.Address) pproposal.Executor {
- if changesFn == nil {
- panic(errNoChangesProposed)
- }
-
- callback := func() error {
- // Make sure the GovDAO executor runs the valset changes
- assertGovDAOCaller()
-
- for _, addr := range changesFn() {
- members = append(members, addr)
- }
-
- return nil
- }
-
- return pproposal.NewExecutor(callback)
-}
-
-// assertGovDAOCaller verifies the caller is the GovDAO executor
-func assertGovDAOCaller() {
- if std.CurrentRealm().PkgPath() != daoPkgPath {
- panic(errNotGovDAO)
- }
-}
diff --git a/examples/gno.land/r/gov/dao/prop2_filetest.gno b/examples/gno.land/r/gov/dao/prop2_filetest.gno
deleted file mode 100644
index 047709cc45f..00000000000
--- a/examples/gno.land/r/gov/dao/prop2_filetest.gno
+++ /dev/null
@@ -1,120 +0,0 @@
-package main
-
-import (
- "std"
- "time"
-
- "gno.land/p/demo/context"
- "gno.land/p/gov/proposal"
- gnoblog "gno.land/r/gnoland/blog"
- govdao "gno.land/r/gov/dao"
-)
-
-func init() {
- membersFn := func() []std.Address {
- return []std.Address{
- std.Address("g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm"),
- }
- }
-
- mExec := govdao.NewPropExecutor(membersFn)
-
- comment := "adding someone to vote"
-
- id := govdao.Propose(comment, mExec)
-
- govdao.ExecuteProposal(id)
-
- executor := proposal.NewCtxExecutor(func(ctx context.Context) error {
- gnoblog.DaoAddPost(
- ctx,
- "hello-from-govdao", // slug
- "Hello from GovDAO!", // title
- "This post was published by a GovDAO proposal.", // body
- time.Now().Format(time.RFC3339), // publidation date
- "moul", // authors
- "govdao,example", // tags
- )
- return nil
- })
-
- // Create a proposal.
- // XXX: payment
- comment = "post a new blogpost about govdao"
- 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(gnoblog.Render(""))
- println("--")
- govdao.ExecuteProposal(1)
- println("--")
- println(govdao.Render("1"))
- println("--")
- println(gnoblog.Render(""))
-}
-
-// Output:
-// --
-// - [0](/r/gov/dao:0) - adding someone to vote (**succeeded**)(by g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm)
-// - [1](/r/gov/dao:1) - post a new blogpost about govdao (**active**)(by g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm)
-//
-// --
-// # Prop #1
-//
-// post a new blogpost about govdao
-//
-// Status: active
-//
-// Voting status: YES: 0, NO: 0, percent: 0, members: 1
-//
-// Author: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm
-//
-//
-// --
-// --
-// # Prop #1
-//
-// post a new blogpost about govdao
-//
-// Status: accepted
-//
-// Voting status: YES: 1, NO: 0, percent: 100, members: 1
-//
-// Author: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm
-//
-//
-// --
-// # Gnoland's Blog
-//
-// No posts.
-// --
-// --
-// # Prop #1
-//
-// post a new blogpost about govdao
-//
-// Status: succeeded
-//
-// Voting status: YES: 1, NO: 0, percent: 100, members: 1
-//
-// Author: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm
-//
-//
-// --
-// # Gnoland's Blog
-//
-//
-//
-// ### [Hello from GovDAO!](/r/gnoland/blog:p/hello-from-govdao)
-// 13 Feb 2009
-//
diff --git a/examples/gno.land/r/gov/dao/types.gno b/examples/gno.land/r/gov/dao/types.gno
deleted file mode 100644
index 123fc489075..00000000000
--- a/examples/gno.land/r/gov/dao/types.gno
+++ /dev/null
@@ -1,32 +0,0 @@
-package govdao
-
-import (
- "std"
-)
-
-// Status enum.
-type Status string
-
-var (
- Accepted Status = "accepted"
- Active Status = "active"
- NotAccepted Status = "not accepted"
- Expired Status = "expired"
- Succeeded Status = "succeeded"
-)
-
-// Voter defines the needed methods for a voting system
-type Voter interface {
-
- // IsAccepted indicates if the voting process had been accepted
- IsAccepted(voters []std.Address) bool
-
- // IsFinished indicates if the voting process is finished
- IsFinished(voters []std.Address) bool
-
- // Vote adds a new vote to the voting system
- Vote(voters []std.Address, caller std.Address, flag string)
-
- // Status returns a human friendly string describing how the voting process is going
- Status(voters []std.Address) string
-}
diff --git a/examples/gno.land/r/gov/dao/v2/dao.gno b/examples/gno.land/r/gov/dao/v2/dao.gno
new file mode 100644
index 00000000000..c37eda80bff
--- /dev/null
+++ b/examples/gno.land/r/gov/dao/v2/dao.gno
@@ -0,0 +1,121 @@
+package govdao
+
+import (
+ "std"
+ "strconv"
+
+ "gno.land/p/demo/dao"
+ "gno.land/p/demo/membstore"
+ "gno.land/p/demo/simpledao"
+ "gno.land/p/demo/ufmt"
+)
+
+var (
+ d *simpledao.SimpleDAO // the current active DAO implementation
+ members membstore.MemberStore // the member store
+)
+
+func init() {
+ var (
+ // Example initial member set (just test addresses)
+ set = []membstore.Member{
+ {
+ Address: std.Address("g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm"),
+ VotingPower: 10,
+ },
+ }
+ )
+
+ // Set the member store
+ members = membstore.NewMembStore(membstore.WithInitialMembers(set))
+
+ // Set the DAO implementation
+ d = simpledao.New(members)
+}
+
+// Propose is designed to be called by another contract or with
+// `maketx run`, not by a `maketx call`.
+func Propose(request dao.ProposalRequest) uint64 {
+ idx, err := d.Propose(request)
+ if err != nil {
+ panic(err)
+ }
+
+ return idx
+}
+
+// VoteOnProposal casts a vote for the given proposal
+func VoteOnProposal(id uint64, option dao.VoteOption) {
+ if err := d.VoteOnProposal(id, option); err != nil {
+ panic(err)
+ }
+}
+
+// ExecuteProposal executes the proposal
+func ExecuteProposal(id uint64) {
+ if err := d.ExecuteProposal(id); err != nil {
+ panic(err)
+ }
+}
+
+// GetPropStore returns the active proposal store
+func GetPropStore() dao.PropStore {
+ return d
+}
+
+// GetMembStore returns the active member store
+func GetMembStore() membstore.MemberStore {
+ return members
+}
+
+func Render(path string) string {
+ if path == "" {
+ numProposals := d.Size()
+
+ if numProposals == 0 {
+ return "No proposals found :(" // corner case
+ }
+
+ output := ""
+
+ offset := uint64(0)
+ if numProposals >= 10 {
+ offset = uint64(numProposals) - 10
+ }
+
+ // Fetch the last 10 proposals
+ for idx, prop := range d.Proposals(offset, uint64(10)) {
+ output += ufmt.Sprintf(
+ "- [Proposal #%d](%s:%d) - (**%s**)(by %s)\n",
+ idx,
+ "/r/gov/dao/v2",
+ idx,
+ prop.Status().String(),
+ prop.Author().String(),
+ )
+ }
+
+ return output
+ }
+
+ // Display the detailed proposal
+ idx, err := strconv.Atoi(path)
+ if err != nil {
+ return "404: Invalid proposal ID"
+ }
+
+ // Fetch the proposal
+ prop, err := d.ProposalByID(uint64(idx))
+ if err != nil {
+ return ufmt.Sprintf("unable to fetch proposal, %s", err.Error())
+ }
+
+ // Render the proposal
+ output := ""
+ output += ufmt.Sprintf("# Prop #%d", idx)
+ output += "\n\n"
+ output += prop.Render()
+ output += "\n\n"
+
+ return output
+}
diff --git a/examples/gno.land/r/gov/dao/v2/gno.mod b/examples/gno.land/r/gov/dao/v2/gno.mod
new file mode 100644
index 00000000000..bc379bf18df
--- /dev/null
+++ b/examples/gno.land/r/gov/dao/v2/gno.mod
@@ -0,0 +1,10 @@
+module gno.land/r/gov/dao/v2
+
+require (
+ gno.land/p/demo/combinederr v0.0.0-latest
+ gno.land/p/demo/dao v0.0.0-latest
+ gno.land/p/demo/membstore v0.0.0-latest
+ gno.land/p/demo/simpledao v0.0.0-latest
+ gno.land/p/demo/ufmt v0.0.0-latest
+ gno.land/p/gov/executor v0.0.0-latest
+)
diff --git a/examples/gno.land/r/gov/dao/v2/poc.gno b/examples/gno.land/r/gov/dao/v2/poc.gno
new file mode 100644
index 00000000000..30d8a403f6e
--- /dev/null
+++ b/examples/gno.land/r/gov/dao/v2/poc.gno
@@ -0,0 +1,92 @@
+package govdao
+
+import (
+ "errors"
+ "std"
+
+ "gno.land/p/demo/combinederr"
+ "gno.land/p/demo/dao"
+ "gno.land/p/demo/membstore"
+ "gno.land/p/gov/executor"
+)
+
+var errNoChangesProposed = errors.New("no set changes proposed")
+
+// NewGovDAOExecutor creates the govdao wrapped callback executor
+func NewGovDAOExecutor(cb func() error) dao.Executor {
+ if cb == nil {
+ panic(errNoChangesProposed)
+ }
+
+ return executor.NewCallbackExecutor(
+ cb,
+ std.CurrentRealm().PkgPath(),
+ )
+}
+
+// NewMemberPropExecutor returns the GOVDAO member change executor
+func NewMemberPropExecutor(changesFn func() []membstore.Member) dao.Executor {
+ if changesFn == nil {
+ panic(errNoChangesProposed)
+ }
+
+ callback := func() error {
+ errs := &combinederr.CombinedError{}
+ cbMembers := changesFn()
+
+ for _, member := range cbMembers {
+ switch {
+ case !members.IsMember(member.Address):
+ // Addition request
+ err := members.AddMember(member)
+
+ errs.Add(err)
+ case member.VotingPower == 0:
+ // Remove request
+ err := members.UpdateMember(member.Address, membstore.Member{
+ Address: member.Address,
+ VotingPower: 0, // 0 indicated removal
+ })
+
+ errs.Add(err)
+ default:
+ // Update request
+ err := members.UpdateMember(member.Address, member)
+
+ errs.Add(err)
+ }
+ }
+
+ // Check if there were any execution errors
+ if errs.Size() == 0 {
+ return nil
+ }
+
+ return errs
+ }
+
+ return NewGovDAOExecutor(callback)
+}
+
+func NewMembStoreImplExecutor(changeFn func() membstore.MemberStore) dao.Executor {
+ if changeFn == nil {
+ panic(errNoChangesProposed)
+ }
+
+ callback := func() error {
+ setMembStoreImpl(changeFn())
+
+ return nil
+ }
+
+ return NewGovDAOExecutor(callback)
+}
+
+// setMembStoreImpl sets a new dao.MembStore implementation
+func setMembStoreImpl(impl membstore.MemberStore) {
+ if impl == nil {
+ panic("invalid member store")
+ }
+
+ members = impl
+}
diff --git a/examples/gno.land/r/gov/dao/prop1_filetest.gno b/examples/gno.land/r/gov/dao/v2/prop1_filetest.gno
similarity index 63%
rename from examples/gno.land/r/gov/dao/prop1_filetest.gno
rename to examples/gno.land/r/gov/dao/v2/prop1_filetest.gno
index 49a200fd561..69e55ef1ab6 100644
--- a/examples/gno.land/r/gov/dao/prop1_filetest.gno
+++ b/examples/gno.land/r/gov/dao/v2/prop1_filetest.gno
@@ -10,26 +10,13 @@ package main
import (
"std"
+ "gno.land/p/demo/dao"
pVals "gno.land/p/sys/validators"
- govdao "gno.land/r/gov/dao"
- "gno.land/r/sys/validators"
+ govdao "gno.land/r/gov/dao/v2"
+ validators "gno.land/r/sys/validators/v2"
)
-const daoPkgPath = "gno.land/r/gov/dao"
-
func init() {
- membersFn := func() []std.Address {
- return []std.Address{
- std.Address("g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm"),
- }
- }
-
- mExec := govdao.NewPropExecutor(membersFn)
-
- comment := "adding someone to vote"
- id := govdao.Propose(comment, mExec)
- govdao.ExecuteProposal(id)
-
changesFn := func() []pVals.Validator {
return []pVals.Validator{
{
@@ -54,74 +41,84 @@ func init() {
// complete governance proposal process.
executor := validators.NewPropExecutor(changesFn)
- // Create a proposal.
- // XXX: payment
- comment = "manual valset changes proposal example"
- govdao.Propose(comment, executor)
+ // Create a proposal
+ description := "manual valset changes proposal example"
+
+ prop := dao.ProposalRequest{
+ Description: description,
+ Executor: executor,
+ }
+
+ govdao.Propose(prop)
}
func main() {
println("--")
println(govdao.Render(""))
println("--")
- println(govdao.Render("1"))
+ println(govdao.Render("0"))
println("--")
- govdao.VoteOnProposal(1, "YES")
+ govdao.VoteOnProposal(0, dao.YesVote)
println("--")
- println(govdao.Render("1"))
+ println(govdao.Render("0"))
println("--")
println(validators.Render(""))
println("--")
- govdao.ExecuteProposal(1)
+ govdao.ExecuteProposal(0)
println("--")
- println(govdao.Render("1"))
+ println(govdao.Render("0"))
println("--")
println(validators.Render(""))
}
// Output:
// --
-// - [0](/r/gov/dao:0) - adding someone to vote (**succeeded**)(by g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm)
-// - [1](/r/gov/dao:1) - manual valset changes proposal example (**active**)(by g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm)
+// - [Proposal #0](/r/gov/dao/v2:0) - (**active**)(by g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm)
//
// --
-// # Prop #1
+// # Prop #0
+//
+// Author: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm
//
// manual valset changes proposal example
//
// Status: active
//
-// Voting status: YES: 0, NO: 0, percent: 0, members: 1
+// Voting stats: YAY 0 (0%), NAY 0 (0%), ABSTAIN 0 (0%), HAVEN'T VOTED 10 (100%)
//
-// Author: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm
+// Threshold met: false
//
//
// --
// --
-// # Prop #1
+// # Prop #0
+//
+// Author: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm
//
// manual valset changes proposal example
//
// Status: accepted
//
-// Voting status: YES: 1, NO: 0, percent: 100, members: 1
+// Voting stats: YAY 10 (100%), NAY 0 (0%), ABSTAIN 0 (0%), HAVEN'T VOTED 0 (0%)
//
-// Author: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm
+// Threshold met: true
//
//
// --
// No valset changes to apply.
// --
// --
-// # Prop #1
+// # Prop #0
+//
+// Author: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm
//
// manual valset changes proposal example
//
-// Status: succeeded
+// Status: execution successful
//
-// Voting status: YES: 1, NO: 0, percent: 100, members: 1
+// Voting stats: YAY 10 (100%), NAY 0 (0%), ABSTAIN 0 (0%), HAVEN'T VOTED 0 (0%)
//
-// Author: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm
+// Threshold met: true
//
//
// --
diff --git a/examples/gno.land/r/gov/dao/v2/prop2_filetest.gno b/examples/gno.land/r/gov/dao/v2/prop2_filetest.gno
new file mode 100644
index 00000000000..32ddc11b67c
--- /dev/null
+++ b/examples/gno.land/r/gov/dao/v2/prop2_filetest.gno
@@ -0,0 +1,110 @@
+package main
+
+import (
+ "time"
+
+ "gno.land/p/demo/dao"
+ gnoblog "gno.land/r/gnoland/blog"
+ govdao "gno.land/r/gov/dao/v2"
+)
+
+func init() {
+ ex := gnoblog.NewPostExecutor(
+ "hello-from-govdao", // slug
+ "Hello from GovDAO!", // title
+ "This post was published by a GovDAO proposal.", // body
+ time.Now().Format(time.RFC3339), // publication date
+ "moul", // authors
+ "govdao,example", // tags
+ )
+
+ // Create a proposal
+ description := "post a new blogpost about govdao"
+
+ prop := dao.ProposalRequest{
+ Description: description,
+ Executor: ex,
+ }
+
+ govdao.Propose(prop)
+}
+
+func main() {
+ println("--")
+ println(govdao.Render(""))
+ println("--")
+ println(govdao.Render("0"))
+ println("--")
+ govdao.VoteOnProposal(0, "YES")
+ println("--")
+ println(govdao.Render("0"))
+ println("--")
+ println(gnoblog.Render(""))
+ println("--")
+ govdao.ExecuteProposal(0)
+ println("--")
+ println(govdao.Render("0"))
+ println("--")
+ println(gnoblog.Render(""))
+}
+
+// Output:
+// --
+// - [Proposal #0](/r/gov/dao/v2:0) - (**active**)(by g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm)
+//
+// --
+// # Prop #0
+//
+// Author: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm
+//
+// post a new blogpost about govdao
+//
+// Status: active
+//
+// Voting stats: YAY 0 (0%), NAY 0 (0%), ABSTAIN 0 (0%), HAVEN'T VOTED 10 (100%)
+//
+// Threshold met: false
+//
+//
+// --
+// --
+// # Prop #0
+//
+// Author: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm
+//
+// post a new blogpost about govdao
+//
+// Status: accepted
+//
+// Voting stats: YAY 10 (100%), NAY 0 (0%), ABSTAIN 0 (0%), HAVEN'T VOTED 0 (0%)
+//
+// Threshold met: true
+//
+//
+// --
+// # Gnoland's Blog
+//
+// No posts.
+// --
+// --
+// # Prop #0
+//
+// Author: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm
+//
+// post a new blogpost about govdao
+//
+// Status: execution successful
+//
+// Voting stats: YAY 10 (100%), NAY 0 (0%), ABSTAIN 0 (0%), HAVEN'T VOTED 0 (0%)
+//
+// Threshold met: true
+//
+//
+// --
+// # Gnoland's Blog
+//
+//
+//
+// ### [Hello from GovDAO!](/r/gnoland/blog:p/hello-from-govdao)
+// 13 Feb 2009
+//
diff --git a/examples/gno.land/r/gov/dao/v2/prop3_filetest.gno b/examples/gno.land/r/gov/dao/v2/prop3_filetest.gno
new file mode 100644
index 00000000000..5aa9947c74b
--- /dev/null
+++ b/examples/gno.land/r/gov/dao/v2/prop3_filetest.gno
@@ -0,0 +1,120 @@
+package main
+
+import (
+ "std"
+
+ "gno.land/p/demo/dao"
+ "gno.land/p/demo/membstore"
+ govdao "gno.land/r/gov/dao/v2"
+)
+
+func init() {
+ memberFn := func() []membstore.Member {
+ return []membstore.Member{
+ {
+ Address: std.Address("g123"),
+ VotingPower: 10,
+ },
+ {
+ Address: std.Address("g456"),
+ VotingPower: 10,
+ },
+ {
+ Address: std.Address("g789"),
+ VotingPower: 10,
+ },
+ }
+ }
+
+ // Create a proposal
+ description := "add new members to the govdao"
+
+ prop := dao.ProposalRequest{
+ Description: description,
+ Executor: govdao.NewMemberPropExecutor(memberFn),
+ }
+
+ govdao.Propose(prop)
+}
+
+func main() {
+ println("--")
+ println(govdao.GetMembStore().Size())
+ println("--")
+ println(govdao.Render(""))
+ println("--")
+ println(govdao.Render("0"))
+ println("--")
+ govdao.VoteOnProposal(0, "YES")
+ println("--")
+ println(govdao.Render("0"))
+ println("--")
+ println(govdao.Render(""))
+ println("--")
+ govdao.ExecuteProposal(0)
+ println("--")
+ println(govdao.Render("0"))
+ println("--")
+ println(govdao.Render(""))
+ println("--")
+ println(govdao.GetMembStore().Size())
+}
+
+// Output:
+// --
+// 1
+// --
+// - [Proposal #0](/r/gov/dao/v2:0) - (**active**)(by g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm)
+//
+// --
+// # Prop #0
+//
+// Author: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm
+//
+// add new members to the govdao
+//
+// Status: active
+//
+// Voting stats: YAY 0 (0%), NAY 0 (0%), ABSTAIN 0 (0%), HAVEN'T VOTED 10 (100%)
+//
+// Threshold met: false
+//
+//
+// --
+// --
+// # Prop #0
+//
+// Author: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm
+//
+// add new members to the govdao
+//
+// Status: accepted
+//
+// Voting stats: YAY 10 (100%), NAY 0 (0%), ABSTAIN 0 (0%), HAVEN'T VOTED 0 (0%)
+//
+// Threshold met: true
+//
+//
+// --
+// - [Proposal #0](/r/gov/dao/v2:0) - (**accepted**)(by g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm)
+//
+// --
+// --
+// # Prop #0
+//
+// Author: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm
+//
+// add new members to the govdao
+//
+// Status: execution successful
+//
+// Voting stats: YAY 10 (25%), NAY 0 (0%), ABSTAIN 0 (0%), HAVEN'T VOTED 30 (75%)
+//
+// Threshold met: false
+//
+//
+// --
+// - [Proposal #0](/r/gov/dao/v2:0) - (**execution successful**)(by g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm)
+//
+// --
+// 4
diff --git a/examples/gno.land/r/gov/dao/voter.gno b/examples/gno.land/r/gov/dao/voter.gno
deleted file mode 100644
index 99223210791..00000000000
--- a/examples/gno.land/r/gov/dao/voter.gno
+++ /dev/null
@@ -1,91 +0,0 @@
-package govdao
-
-import (
- "std"
-
- "gno.land/p/demo/ufmt"
-)
-
-const (
- yay = "YES"
- nay = "NO"
-
- msgNoMoreVotesAllowed = "no more votes allowed"
- msgAlreadyVoted = "caller already voted"
- msgWrongVotingValue = "voting values must be YES or NO"
-)
-
-func NewPercentageVoter(percent int) *PercentageVoter {
- if percent < 0 || percent > 100 {
- panic("percent value must be between 0 and 100")
- }
-
- return &PercentageVoter{
- percentage: percent,
- }
-}
-
-// PercentageVoter is a system based on the amount of received votes.
-// When the specified treshold is reached, the voting process finishes.
-type PercentageVoter struct {
- percentage int
-
- voters []std.Address
- yes int
- no int
-}
-
-func (pv *PercentageVoter) IsAccepted(voters []std.Address) bool {
- if len(voters) == 0 {
- return true // special case
- }
-
- return pv.percent(voters) >= pv.percentage
-}
-
-func (pv *PercentageVoter) IsFinished(voters []std.Address) bool {
- return pv.yes+pv.no >= len(voters)
-}
-
-func (pv *PercentageVoter) Status(voters []std.Address) string {
- return ufmt.Sprintf("YES: %d, NO: %d, percent: %d, members: %d", pv.yes, pv.no, pv.percent(voters), len(voters))
-}
-
-func (pv *PercentageVoter) Vote(voters []std.Address, caller std.Address, flag string) {
- if pv.IsFinished(voters) {
- panic(msgNoMoreVotesAllowed)
- }
-
- if pv.alreadyVoted(caller) {
- panic(msgAlreadyVoted)
- }
-
- switch flag {
- case yay:
- pv.yes++
- pv.voters = append(pv.voters, caller)
- case nay:
- pv.no++
- pv.voters = append(pv.voters, caller)
- default:
- panic(msgWrongVotingValue)
- }
-}
-
-func (pv *PercentageVoter) percent(voters []std.Address) int {
- if len(voters) == 0 {
- return 0
- }
-
- return int((float32(pv.yes) / float32(len(voters))) * 100)
-}
-
-func (pv *PercentageVoter) alreadyVoted(addr std.Address) bool {
- for _, v := range pv.voters {
- if v == addr {
- return true
- }
- }
-
- return false
-}
diff --git a/examples/gno.land/r/leon/config/config.gno b/examples/gno.land/r/leon/config/config.gno
index cbc1e537e3f..bc800ec8263 100644
--- a/examples/gno.land/r/leon/config/config.gno
+++ b/examples/gno.land/r/leon/config/config.gno
@@ -8,6 +8,9 @@ import (
var (
main std.Address // leon's main address
backup std.Address // backup address
+
+ ErrInvalidAddr = errors.New("leon's config: invalid address")
+ ErrUnauthorized = errors.New("leon's config: unauthorized")
)
func init() {
@@ -24,7 +27,7 @@ func Backup() std.Address {
func SetAddress(a std.Address) error {
if !a.IsValid() {
- return errors.New("config: invalid address")
+ return ErrInvalidAddr
}
if err := checkAuthorized(); err != nil {
@@ -37,7 +40,7 @@ func SetAddress(a std.Address) error {
func SetBackup(a std.Address) error {
if !a.IsValid() {
- return errors.New("config: invalid address")
+ return ErrInvalidAddr
}
if err := checkAuthorized(); err != nil {
@@ -50,16 +53,11 @@ func SetBackup(a std.Address) error {
func checkAuthorized() error {
caller := std.PrevRealm().Addr()
- if caller != main || caller != backup {
- return errors.New("config: unauthorized")
+ isAuthorized := caller == main || caller == backup
+
+ if !isAuthorized {
+ return ErrUnauthorized
}
return nil
}
-
-func AssertAuthorized() {
- caller := std.PrevRealm().Addr()
- if caller != main || caller != backup {
- panic("config: unauthorized")
- }
-}
diff --git a/examples/gno.land/r/leon/home/home.gno b/examples/gno.land/r/leon/home/home.gno
index 1f6a07e8959..ba688792a4c 100644
--- a/examples/gno.land/r/leon/home/home.gno
+++ b/examples/gno.land/r/leon/home/home.gno
@@ -34,13 +34,19 @@ TODO import r/gh
}
func UpdatePFP(url, caption string) {
- config.AssertAuthorized()
+ if !isAuthorized(std.PrevRealm().Addr()) {
+ panic(config.ErrUnauthorized)
+ }
+
pfp = url
pfpCaption = caption
}
func UpdateAboutMe(col1, col2 string) {
- config.AssertAuthorized()
+ if !isAuthorized(std.PrevRealm().Addr()) {
+ panic(config.ErrUnauthorized)
+ }
+
abtMe[0] = col1
abtMe[1] = col2
}
@@ -119,3 +125,7 @@ func renderMillipede() string {
return out
}
+
+func isAuthorized(addr std.Address) bool {
+ return addr == config.Address() || addr == config.Backup()
+}
diff --git a/examples/gno.land/r/stefann/home/gno.mod b/examples/gno.land/r/stefann/home/gno.mod
new file mode 100644
index 00000000000..dd556e7f817
--- /dev/null
+++ b/examples/gno.land/r/stefann/home/gno.mod
@@ -0,0 +1,9 @@
+module gno.land/r/stefann/home
+
+require (
+ gno.land/p/demo/avl v0.0.0-latest
+ gno.land/p/demo/ownable v0.0.0-latest
+ gno.land/p/demo/testutils v0.0.0-latest
+ gno.land/p/demo/ufmt v0.0.0-latest
+ gno.land/r/stefann/registry v0.0.0-latest
+)
diff --git a/examples/gno.land/r/stefann/home/home.gno b/examples/gno.land/r/stefann/home/home.gno
new file mode 100644
index 00000000000..f40329ebf7e
--- /dev/null
+++ b/examples/gno.land/r/stefann/home/home.gno
@@ -0,0 +1,303 @@
+package home
+
+import (
+ "sort"
+ "std"
+ "strings"
+
+ "gno.land/p/demo/avl"
+ "gno.land/p/demo/ownable"
+ "gno.land/p/demo/ufmt"
+
+ "gno.land/r/stefann/registry"
+)
+
+type City struct {
+ Name string
+ URL string
+}
+
+type Sponsor struct {
+ Address std.Address
+ Amount std.Coins
+}
+
+type Profile struct {
+ pfp string
+ aboutMe []string
+}
+
+type Travel struct {
+ cities []City
+ currentCityIndex int
+ jarLink string
+}
+
+type Sponsorship struct {
+ maxSponsors int
+ sponsors *avl.Tree
+ DonationsCount int
+ sponsorsCount int
+}
+
+var (
+ profile Profile
+ travel Travel
+ sponsorship Sponsorship
+ owner *ownable.Ownable
+)
+
+func init() {
+ owner = ownable.NewWithAddress(registry.MainAddr())
+
+ profile = Profile{
+ pfp: "https://i.ibb.co/Bc5YNCx/DSC-0095a.jpg",
+ aboutMe: []string{
+ `### About Me`,
+ `Hey there! I’m Stefan, a student of Computer Science. I’m all about exploring and adventure — whether it’s diving into the latest tech or discovering a new city, I’m always up for the challenge!`,
+
+ `### Contributions`,
+ `I'm just getting started, but you can follow my journey through Gno.land right [here](https://github.com/gnolang/hackerspace/issues/94) 🔗`,
+ },
+ }
+
+ travel = Travel{
+ cities: []City{
+ {Name: "Venice", URL: "https://i.ibb.co/1mcZ7b1/venice.jpg"},
+ {Name: "Tokyo", URL: "https://i.ibb.co/wNDJv3H/tokyo.jpg"},
+ {Name: "São Paulo", URL: "https://i.ibb.co/yWMq2Sn/sao-paulo.jpg"},
+ {Name: "Toronto", URL: "https://i.ibb.co/pb95HJB/toronto.jpg"},
+ {Name: "Bangkok", URL: "https://i.ibb.co/pQy3w2g/bangkok.jpg"},
+ {Name: "New York", URL: "https://i.ibb.co/6JWLm0h/new-york.jpg"},
+ {Name: "Paris", URL: "https://i.ibb.co/q9vf6Hs/paris.jpg"},
+ {Name: "Kandersteg", URL: "https://i.ibb.co/60DzywD/kandersteg.jpg"},
+ {Name: "Rothenburg", URL: "https://i.ibb.co/cr8d2rQ/rothenburg.jpg"},
+ {Name: "Capetown", URL: "https://i.ibb.co/bPGn0v3/capetown.jpg"},
+ {Name: "Sydney", URL: "https://i.ibb.co/TBNzqfy/sydney.jpg"},
+ {Name: "Oeschinen Lake", URL: "https://i.ibb.co/QJQwp2y/oeschinen-lake.jpg"},
+ {Name: "Barra Grande", URL: "https://i.ibb.co/z4RXKc1/barra-grande.jpg"},
+ {Name: "London", URL: "https://i.ibb.co/CPGtvgr/london.jpg"},
+ },
+ currentCityIndex: 0,
+ jarLink: "https://TODO", // This value should be injected through UpdateJarLink after deployment.
+ }
+
+ sponsorship = Sponsorship{
+ maxSponsors: 5,
+ sponsors: avl.NewTree(),
+ DonationsCount: 0,
+ sponsorsCount: 0,
+ }
+}
+
+func UpdateCities(newCities []City) {
+ owner.AssertCallerIsOwner()
+ travel.cities = newCities
+}
+
+func AddCities(newCities ...City) {
+ owner.AssertCallerIsOwner()
+
+ travel.cities = append(travel.cities, newCities...)
+}
+
+func UpdateJarLink(newLink string) {
+ owner.AssertCallerIsOwner()
+ travel.jarLink = newLink
+}
+
+func UpdatePFP(url string) {
+ owner.AssertCallerIsOwner()
+ profile.pfp = url
+}
+
+func UpdateAboutMe(aboutMeStr string) {
+ owner.AssertCallerIsOwner()
+ profile.aboutMe = strings.Split(aboutMeStr, "|")
+}
+
+func AddAboutMeRows(newRows ...string) {
+ owner.AssertCallerIsOwner()
+
+ profile.aboutMe = append(profile.aboutMe, newRows...)
+}
+
+func UpdateMaxSponsors(newMax int) {
+ owner.AssertCallerIsOwner()
+ if newMax <= 0 {
+ panic("maxSponsors must be greater than zero")
+ }
+ sponsorship.maxSponsors = newMax
+}
+
+func Donate() {
+ address := std.GetOrigCaller()
+ amount := std.GetOrigSend()
+
+ if amount.AmountOf("ugnot") == 0 {
+ panic("Donation must include GNOT")
+ }
+
+ existingAmount, exists := sponsorship.sponsors.Get(address.String())
+ if exists {
+ updatedAmount := existingAmount.(std.Coins).Add(amount)
+ sponsorship.sponsors.Set(address.String(), updatedAmount)
+ } else {
+ sponsorship.sponsors.Set(address.String(), amount)
+ sponsorship.sponsorsCount++
+ }
+
+ travel.currentCityIndex++
+ sponsorship.DonationsCount++
+
+ banker := std.GetBanker(std.BankerTypeRealmSend)
+ ownerAddr := registry.MainAddr()
+ banker.SendCoins(std.CurrentRealm().Addr(), ownerAddr, banker.GetCoins(std.CurrentRealm().Addr()))
+}
+
+type SponsorSlice []Sponsor
+
+func (s SponsorSlice) Len() int {
+ return len(s)
+}
+
+func (s SponsorSlice) Less(i, j int) bool {
+ return s[i].Amount.AmountOf("ugnot") > s[j].Amount.AmountOf("ugnot")
+}
+
+func (s SponsorSlice) Swap(i, j int) {
+ s[i], s[j] = s[j], s[i]
+}
+
+func GetTopSponsors() []Sponsor {
+ var sponsorSlice SponsorSlice
+
+ sponsorship.sponsors.Iterate("", "", func(key string, value interface{}) bool {
+ addr := std.Address(key)
+ amount := value.(std.Coins)
+ sponsorSlice = append(sponsorSlice, Sponsor{Address: addr, Amount: amount})
+ return false
+ })
+
+ sort.Sort(sponsorSlice)
+ return sponsorSlice
+}
+
+func GetTotalDonations() int {
+ total := 0
+ sponsorship.sponsors.Iterate("", "", func(key string, value interface{}) bool {
+ total += int(value.(std.Coins).AmountOf("ugnot"))
+ return false
+ })
+ return total
+}
+
+func Render(path string) string {
+ out := ufmt.Sprintf("# Exploring %s!\n\n", travel.cities[travel.currentCityIndex].Name)
+
+ out += renderAboutMe()
+ out += "\n\n"
+ out += renderTips()
+
+ return out
+}
+
+func renderAboutMe() string {
+ out := "
"
+
+ out += "
\n\n"
+
+ out += ufmt.Sprintf("
\n\n", travel.cities[travel.currentCityIndex%len(travel.cities)].URL)
+
+ out += ufmt.Sprintf("
\n\n", profile.pfp)
+
+ out += "
\n\n"
+
+ for _, rows := range profile.aboutMe {
+ out += "
\n\n"
+ out += rows + "\n\n"
+ out += "
\n\n"
+ }
+
+ out += "
\n\n"
+
+ return out
+}
+
+func renderTips() string {
+ out := `
` + "\n\n"
+
+ out += `
` + "\n"
+
+ out += `
Help Me Travel The World ` + "\n\n"
+
+ out += renderTipsJar() + "\n"
+
+ out += ufmt.Sprintf(`I am currently in %s, tip the jar to send me somewhere else! `, travel.cities[travel.currentCityIndex].Name)
+
+ out += `Click the jar, tip in GNOT coins, and watch my background change as I head to a new adventure! ` + "\n\n"
+
+ out += renderSponsors()
+
+ out += `` + "\n\n"
+
+ out += `
` + "\n"
+
+ return out
+}
+
+func formatAddress(address string) string {
+ if len(address) <= 8 {
+ return address
+ }
+ return address[:4] + "..." + address[len(address)-4:]
+}
+
+func renderSponsors() string {
+ out := `
Sponsor Leaderboard ` + "\n"
+
+ if sponsorship.sponsorsCount == 0 {
+ return out + `
No sponsors yet. Be the first to tip the jar!
` + "\n"
+ }
+
+ topSponsors := GetTopSponsors()
+ numSponsors := len(topSponsors)
+ if numSponsors > sponsorship.maxSponsors {
+ numSponsors = sponsorship.maxSponsors
+ }
+
+ out += `
` + "\n"
+
+ for i := 0; i < numSponsors; i++ {
+ sponsor := topSponsors[i]
+ isLastItem := (i == numSponsors-1)
+
+ padding := "10px 5px"
+ border := "border-bottom: 1px solid #ddd;"
+
+ if isLastItem {
+ padding = "8px 5px"
+ border = ""
+ }
+
+ out += ufmt.Sprintf(
+ `
+ %d. %s
+ %s
+ `,
+ padding, border, i+1, formatAddress(sponsor.Address.String()), sponsor.Amount.String(),
+ )
+ }
+
+ return out
+}
+
+func renderTipsJar() string {
+ out := ufmt.Sprintf(``, travel.jarLink) + "\n"
+
+ out += ` ` + "\n"
+
+ out += ` ` + "\n"
+
+ return out
+}
diff --git a/examples/gno.land/r/stefann/home/home_test.gno b/examples/gno.land/r/stefann/home/home_test.gno
new file mode 100644
index 00000000000..ca146b9eb13
--- /dev/null
+++ b/examples/gno.land/r/stefann/home/home_test.gno
@@ -0,0 +1,291 @@
+package home
+
+import (
+ "std"
+ "strings"
+ "testing"
+
+ "gno.land/p/demo/avl"
+ "gno.land/p/demo/testutils"
+)
+
+func TestUpdatePFP(t *testing.T) {
+ var owner = std.Address("g1sd5ezmxt4rwpy52u6wl3l3y085n8x0p6nllxm8")
+ std.TestSetOrigCaller(owner)
+
+ profile.pfp = ""
+
+ UpdatePFP("https://example.com/pic.png")
+
+ if profile.pfp != "https://example.com/pic.png" {
+ t.Fatalf("expected pfp to be https://example.com/pic.png, got %s", profile.pfp)
+ }
+}
+
+func TestUpdateAboutMe(t *testing.T) {
+ var owner = std.Address("g1sd5ezmxt4rwpy52u6wl3l3y085n8x0p6nllxm8")
+ std.TestSetOrigCaller(owner)
+
+ profile.aboutMe = []string{}
+
+ UpdateAboutMe("This is my new bio.|I love coding!")
+
+ expected := []string{"This is my new bio.", "I love coding!"}
+
+ if len(profile.aboutMe) != len(expected) {
+ t.Fatalf("expected aboutMe to have length %d, got %d", len(expected), len(profile.aboutMe))
+ }
+
+ for i := range profile.aboutMe {
+ if profile.aboutMe[i] != expected[i] {
+ t.Fatalf("expected aboutMe[%d] to be %s, got %s", i, expected[i], profile.aboutMe[i])
+ }
+ }
+}
+
+func TestUpdateCities(t *testing.T) {
+ var owner = std.Address("g1sd5ezmxt4rwpy52u6wl3l3y085n8x0p6nllxm8")
+ std.TestSetOrigCaller(owner)
+
+ travel.cities = []City{}
+
+ newCities := []City{
+ {Name: "Berlin", URL: "https://example.com/berlin.jpg"},
+ {Name: "Vienna", URL: "https://example.com/vienna.jpg"},
+ }
+
+ UpdateCities(newCities)
+
+ if len(travel.cities) != 2 {
+ t.Fatalf("expected 2 cities, got %d", len(travel.cities))
+ }
+
+ if travel.cities[0].Name != "Berlin" || travel.cities[1].Name != "Vienna" {
+ t.Fatalf("expected cities to be updated to Berlin and Vienna, got %+v", travel.cities)
+ }
+}
+
+func TestUpdateJarLink(t *testing.T) {
+ var owner = std.Address("g1sd5ezmxt4rwpy52u6wl3l3y085n8x0p6nllxm8")
+ std.TestSetOrigCaller(owner)
+
+ travel.jarLink = ""
+
+ UpdateJarLink("https://example.com/jar")
+
+ if travel.jarLink != "https://example.com/jar" {
+ t.Fatalf("expected jarLink to be https://example.com/jar, got %s", travel.jarLink)
+ }
+}
+
+func TestUpdateMaxSponsors(t *testing.T) {
+ var owner = std.Address("g1sd5ezmxt4rwpy52u6wl3l3y085n8x0p6nllxm8")
+ std.TestSetOrigCaller(owner)
+
+ sponsorship.maxSponsors = 0
+
+ UpdateMaxSponsors(10)
+
+ if sponsorship.maxSponsors != 10 {
+ t.Fatalf("expected maxSponsors to be 10, got %d", sponsorship.maxSponsors)
+ }
+
+ defer func() {
+ if r := recover(); r == nil {
+ t.Fatalf("expected panic for setting maxSponsors to 0")
+ }
+ }()
+ UpdateMaxSponsors(0)
+}
+
+func TestAddCities(t *testing.T) {
+ var owner = std.Address("g1sd5ezmxt4rwpy52u6wl3l3y085n8x0p6nllxm8")
+ std.TestSetOrigCaller(owner)
+
+ travel.cities = []City{}
+
+ AddCities(City{Name: "Berlin", URL: "https://example.com/berlin.jpg"})
+
+ if len(travel.cities) != 1 {
+ t.Fatalf("expected 1 city, got %d", len(travel.cities))
+ }
+ if travel.cities[0].Name != "Berlin" || travel.cities[0].URL != "https://example.com/berlin.jpg" {
+ t.Fatalf("expected city to be Berlin, got %+v", travel.cities[0])
+ }
+
+ AddCities(
+ City{Name: "Paris", URL: "https://example.com/paris.jpg"},
+ City{Name: "Tokyo", URL: "https://example.com/tokyo.jpg"},
+ )
+
+ if len(travel.cities) != 3 {
+ t.Fatalf("expected 3 cities, got %d", len(travel.cities))
+ }
+ if travel.cities[1].Name != "Paris" || travel.cities[2].Name != "Tokyo" {
+ t.Fatalf("expected cities to be Paris and Tokyo, got %+v", travel.cities[1:])
+ }
+}
+
+func TestAddAboutMeRows(t *testing.T) {
+ var owner = std.Address("g1sd5ezmxt4rwpy52u6wl3l3y085n8x0p6nllxm8")
+ std.TestSetOrigCaller(owner)
+
+ profile.aboutMe = []string{}
+
+ AddAboutMeRows("I love exploring new technologies!")
+
+ if len(profile.aboutMe) != 1 {
+ t.Fatalf("expected 1 aboutMe row, got %d", len(profile.aboutMe))
+ }
+ if profile.aboutMe[0] != "I love exploring new technologies!" {
+ t.Fatalf("expected first aboutMe row to be 'I love exploring new technologies!', got %s", profile.aboutMe[0])
+ }
+
+ AddAboutMeRows("Travel is my passion!", "Always learning.")
+
+ if len(profile.aboutMe) != 3 {
+ t.Fatalf("expected 3 aboutMe rows, got %d", len(profile.aboutMe))
+ }
+ if profile.aboutMe[1] != "Travel is my passion!" || profile.aboutMe[2] != "Always learning." {
+ t.Fatalf("expected aboutMe rows to be 'Travel is my passion!' and 'Always learning.', got %+v", profile.aboutMe[1:])
+ }
+}
+
+func TestDonate(t *testing.T) {
+ var user = testutils.TestAddress("user")
+ std.TestSetOrigCaller(user)
+
+ sponsorship.sponsors = avl.NewTree()
+ sponsorship.DonationsCount = 0
+ sponsorship.sponsorsCount = 0
+ travel.currentCityIndex = 0
+
+ coinsSent := std.NewCoins(std.NewCoin("ugnot", 500))
+ std.TestSetOrigSend(coinsSent, std.NewCoins())
+ Donate()
+
+ existingAmount, exists := sponsorship.sponsors.Get(string(user))
+ if !exists {
+ t.Fatalf("expected sponsor to be added, but it was not found")
+ }
+
+ if existingAmount.(std.Coins).AmountOf("ugnot") != 500 {
+ t.Fatalf("expected donation amount to be 500ugnot, got %d", existingAmount.(std.Coins).AmountOf("ugnot"))
+ }
+
+ if sponsorship.DonationsCount != 1 {
+ t.Fatalf("expected DonationsCount to be 1, got %d", sponsorship.DonationsCount)
+ }
+
+ if sponsorship.sponsorsCount != 1 {
+ t.Fatalf("expected sponsorsCount to be 1, got %d", sponsorship.sponsorsCount)
+ }
+
+ if travel.currentCityIndex != 1 {
+ t.Fatalf("expected currentCityIndex to be 1, got %d", travel.currentCityIndex)
+ }
+
+ coinsSent = std.NewCoins(std.NewCoin("ugnot", 300))
+ std.TestSetOrigSend(coinsSent, std.NewCoins())
+ Donate()
+
+ existingAmount, exists = sponsorship.sponsors.Get(string(user))
+ if !exists {
+ t.Fatalf("expected sponsor to exist after second donation, but it was not found")
+ }
+
+ if existingAmount.(std.Coins).AmountOf("ugnot") != 800 {
+ t.Fatalf("expected total donation amount to be 800ugnot, got %d", existingAmount.(std.Coins).AmountOf("ugnot"))
+ }
+
+ if sponsorship.DonationsCount != 2 {
+ t.Fatalf("expected DonationsCount to be 2 after second donation, got %d", sponsorship.DonationsCount)
+ }
+
+ if travel.currentCityIndex != 2 {
+ t.Fatalf("expected currentCityIndex to be 2 after second donation, got %d", travel.currentCityIndex)
+ }
+}
+
+func TestGetTopSponsors(t *testing.T) {
+ var user = testutils.TestAddress("user")
+ std.TestSetOrigCaller(user)
+
+ sponsorship.sponsors = avl.NewTree()
+ sponsorship.sponsorsCount = 0
+
+ sponsorship.sponsors.Set("g1address1", std.NewCoins(std.NewCoin("ugnot", 300)))
+ sponsorship.sponsors.Set("g1address2", std.NewCoins(std.NewCoin("ugnot", 500)))
+ sponsorship.sponsors.Set("g1address3", std.NewCoins(std.NewCoin("ugnot", 200)))
+ sponsorship.sponsorsCount = 3
+
+ topSponsors := GetTopSponsors()
+
+ if len(topSponsors) != 3 {
+ t.Fatalf("expected 3 sponsors, got %d", len(topSponsors))
+ }
+
+ if topSponsors[0].Address.String() != "g1address2" || topSponsors[0].Amount.AmountOf("ugnot") != 500 {
+ t.Fatalf("expected top sponsor to be g1address2 with 500ugnot, got %s with %dugnot", topSponsors[0].Address.String(), topSponsors[0].Amount.AmountOf("ugnot"))
+ }
+
+ if topSponsors[1].Address.String() != "g1address1" || topSponsors[1].Amount.AmountOf("ugnot") != 300 {
+ t.Fatalf("expected second sponsor to be g1address1 with 300ugnot, got %s with %dugnot", topSponsors[1].Address.String(), topSponsors[1].Amount.AmountOf("ugnot"))
+ }
+
+ if topSponsors[2].Address.String() != "g1address3" || topSponsors[2].Amount.AmountOf("ugnot") != 200 {
+ t.Fatalf("expected third sponsor to be g1address3 with 200ugnot, got %s with %dugnot", topSponsors[2].Address.String(), topSponsors[2].Amount.AmountOf("ugnot"))
+ }
+}
+
+func TestGetTotalDonations(t *testing.T) {
+ var user = testutils.TestAddress("user")
+ std.TestSetOrigCaller(user)
+
+ sponsorship.sponsors = avl.NewTree()
+ sponsorship.sponsorsCount = 0
+
+ sponsorship.sponsors.Set("g1address1", std.NewCoins(std.NewCoin("ugnot", 300)))
+ sponsorship.sponsors.Set("g1address2", std.NewCoins(std.NewCoin("ugnot", 500)))
+ sponsorship.sponsors.Set("g1address3", std.NewCoins(std.NewCoin("ugnot", 200)))
+ sponsorship.sponsorsCount = 3
+
+ totalDonations := GetTotalDonations()
+
+ if totalDonations != 1000 {
+ t.Fatalf("expected total donations to be 1000ugnot, got %dugnot", totalDonations)
+ }
+}
+
+func TestRender(t *testing.T) {
+ travel.currentCityIndex = 0
+ travel.cities = []City{
+ {Name: "Venice", URL: "https://example.com/venice.jpg"},
+ {Name: "Paris", URL: "https://example.com/paris.jpg"},
+ }
+
+ output := Render("")
+
+ expectedCity := "Venice"
+ if !strings.Contains(output, expectedCity) {
+ t.Fatalf("expected output to contain city name '%s', got %s", expectedCity, output)
+ }
+
+ expectedURL := "https://example.com/venice.jpg"
+ if !strings.Contains(output, expectedURL) {
+ t.Fatalf("expected output to contain city URL '%s', got %s", expectedURL, output)
+ }
+
+ travel.currentCityIndex = 1
+ output = Render("")
+
+ expectedCity = "Paris"
+ if !strings.Contains(output, expectedCity) {
+ t.Fatalf("expected output to contain city name '%s', got %s", expectedCity, output)
+ }
+
+ expectedURL = "https://example.com/paris.jpg"
+ if !strings.Contains(output, expectedURL) {
+ t.Fatalf("expected output to contain city URL '%s', got %s", expectedURL, output)
+ }
+}
diff --git a/examples/gno.land/r/stefann/registry/gno.mod b/examples/gno.land/r/stefann/registry/gno.mod
new file mode 100644
index 00000000000..5ed3e4916e2
--- /dev/null
+++ b/examples/gno.land/r/stefann/registry/gno.mod
@@ -0,0 +1,3 @@
+module gno.land/r/stefann/registry
+
+require gno.land/p/demo/ownable v0.0.0-latest
diff --git a/examples/gno.land/r/stefann/registry/registry.gno b/examples/gno.land/r/stefann/registry/registry.gno
new file mode 100644
index 00000000000..6f56d105e4b
--- /dev/null
+++ b/examples/gno.land/r/stefann/registry/registry.gno
@@ -0,0 +1,51 @@
+package registry
+
+import (
+ "errors"
+ "std"
+
+ "gno.land/p/demo/ownable"
+)
+
+var (
+ mainAddr std.Address
+ backupAddr std.Address
+ owner *ownable.Ownable
+)
+
+func init() {
+ mainAddr = "g1sd5ezmxt4rwpy52u6wl3l3y085n8x0p6nllxm8"
+ backupAddr = "g13awn2575t8s2vf3svlprc4dg0e9z5wchejdxk8"
+
+ owner = ownable.NewWithAddress(mainAddr)
+}
+
+func MainAddr() std.Address {
+ return mainAddr
+}
+
+func BackupAddr() std.Address {
+ return backupAddr
+}
+
+func SetMainAddr(addr std.Address) error {
+ if !addr.IsValid() {
+ return errors.New("config: invalid address")
+ }
+
+ owner.AssertCallerIsOwner()
+
+ mainAddr = addr
+ return nil
+}
+
+func SetBackupAddr(addr std.Address) error {
+ if !addr.IsValid() {
+ return errors.New("config: invalid address")
+ }
+
+ owner.AssertCallerIsOwner()
+
+ backupAddr = addr
+ return nil
+}
diff --git a/examples/gno.land/r/sys/validators/doc.gno b/examples/gno.land/r/sys/validators/v2/doc.gno
similarity index 100%
rename from examples/gno.land/r/sys/validators/doc.gno
rename to examples/gno.land/r/sys/validators/v2/doc.gno
diff --git a/examples/gno.land/r/sys/validators/gno.mod b/examples/gno.land/r/sys/validators/v2/gno.mod
similarity index 71%
rename from examples/gno.land/r/sys/validators/gno.mod
rename to examples/gno.land/r/sys/validators/v2/gno.mod
index d9d129dd543..db94a208902 100644
--- a/examples/gno.land/r/sys/validators/gno.mod
+++ b/examples/gno.land/r/sys/validators/v2/gno.mod
@@ -1,12 +1,13 @@
-module gno.land/r/sys/validators
+module gno.land/r/sys/validators/v2
require (
gno.land/p/demo/avl v0.0.0-latest
+ gno.land/p/demo/dao v0.0.0-latest
gno.land/p/demo/seqid v0.0.0-latest
gno.land/p/demo/testutils v0.0.0-latest
gno.land/p/demo/uassert v0.0.0-latest
gno.land/p/demo/ufmt v0.0.0-latest
- gno.land/p/gov/proposal v0.0.0-latest
gno.land/p/nt/poa v0.0.0-latest
gno.land/p/sys/validators v0.0.0-latest
+ gno.land/r/gov/dao/bridge v0.0.0-latest
)
diff --git a/examples/gno.land/r/sys/validators/gnosdk.gno b/examples/gno.land/r/sys/validators/v2/gnosdk.gno
similarity index 100%
rename from examples/gno.land/r/sys/validators/gnosdk.gno
rename to examples/gno.land/r/sys/validators/v2/gnosdk.gno
diff --git a/examples/gno.land/r/sys/validators/init.gno b/examples/gno.land/r/sys/validators/v2/init.gno
similarity index 100%
rename from examples/gno.land/r/sys/validators/init.gno
rename to examples/gno.land/r/sys/validators/v2/init.gno
diff --git a/examples/gno.land/r/sys/validators/poc.gno b/examples/gno.land/r/sys/validators/v2/poc.gno
similarity index 63%
rename from examples/gno.land/r/sys/validators/poc.gno
rename to examples/gno.land/r/sys/validators/v2/poc.gno
index e088b3b4293..760edc39d1e 100644
--- a/examples/gno.land/r/sys/validators/poc.gno
+++ b/examples/gno.land/r/sys/validators/v2/poc.gno
@@ -3,16 +3,12 @@ package validators
import (
"std"
- "gno.land/p/gov/proposal"
+ "gno.land/p/demo/dao"
"gno.land/p/sys/validators"
+ "gno.land/r/gov/dao/bridge"
)
-const daoPkgPath = "gno.land/r/gov/dao"
-
-const (
- errNoChangesProposed = "no set changes proposed"
- errNotGovDAO = "caller not govdao executor"
-)
+const errNoChangesProposed = "no set changes proposed"
// NewPropExecutor creates a new executor that wraps a changes closure
// proposal. This wrapper is required to ensure the GovDAO Realm actually
@@ -20,15 +16,12 @@ const (
//
// Concept adapted from:
// https://github.com/gnolang/gno/pull/1945
-func NewPropExecutor(changesFn func() []validators.Validator) proposal.Executor {
+func NewPropExecutor(changesFn func() []validators.Validator) dao.Executor {
if changesFn == nil {
panic(errNoChangesProposed)
}
callback := func() error {
- // Make sure the GovDAO executor runs the valset changes
- assertGovDAOCaller()
-
for _, change := range changesFn() {
if change.VotingPower == 0 {
// This change request is to remove the validator
@@ -44,14 +37,7 @@ func NewPropExecutor(changesFn func() []validators.Validator) proposal.Executor
return nil
}
- return proposal.NewExecutor(callback)
-}
-
-// assertGovDAOCaller verifies the caller is the GovDAO executor
-func assertGovDAOCaller() {
- if std.PrevRealm().PkgPath() != daoPkgPath {
- panic(errNotGovDAO)
- }
+ return bridge.GovDAO().NewGovDAOExecutor(callback)
}
// IsValidator returns a flag indicating if the given bech32 address
diff --git a/examples/gno.land/r/sys/validators/validators.gno b/examples/gno.land/r/sys/validators/v2/validators.gno
similarity index 100%
rename from examples/gno.land/r/sys/validators/validators.gno
rename to examples/gno.land/r/sys/validators/v2/validators.gno
diff --git a/examples/gno.land/r/sys/validators/validators_test.gno b/examples/gno.land/r/sys/validators/v2/validators_test.gno
similarity index 100%
rename from examples/gno.land/r/sys/validators/validators_test.gno
rename to examples/gno.land/r/sys/validators/v2/validators_test.gno
diff --git a/gno.land/cmd/gnoland/genesis.go b/gno.land/cmd/gnoland/genesis.go
deleted file mode 100644
index 37c0f8f2926..00000000000
--- a/gno.land/cmd/gnoland/genesis.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package main
-
-import (
- "flag"
-
- "github.com/gnolang/gno/tm2/pkg/commands"
-)
-
-func newGenesisCmd(io commands.IO) *commands.Command {
- cmd := commands.NewCommand(
- commands.Metadata{
- Name: "genesis",
- ShortUsage: "genesis [flags] [...]",
- ShortHelp: "gno genesis manipulation suite",
- LongHelp: "Gno genesis.json manipulation suite, for managing genesis parameters",
- },
- commands.NewEmptyConfig(),
- commands.HelpExec,
- )
-
- cmd.AddSubCommands(
- newGenerateCmd(io),
- newValidatorCmd(io),
- newVerifyCmd(io),
- newBalancesCmd(io),
- newTxsCmd(io),
- )
-
- return cmd
-}
-
-// commonCfg is the common
-// configuration for genesis commands
-// that require a genesis.json
-type commonCfg struct {
- genesisPath string
-}
-
-func (c *commonCfg) RegisterFlags(fs *flag.FlagSet) {
- fs.StringVar(
- &c.genesisPath,
- "genesis-path",
- "./genesis.json",
- "the path to the genesis.json",
- )
-}
diff --git a/gno.land/cmd/gnoland/root.go b/gno.land/cmd/gnoland/root.go
index 8df716b1fed..c6143ab9cd3 100644
--- a/gno.land/cmd/gnoland/root.go
+++ b/gno.land/cmd/gnoland/root.go
@@ -5,12 +5,8 @@ import (
"os"
"github.com/gnolang/gno/tm2/pkg/commands"
- "github.com/peterbourgon/ff/v3"
- "github.com/peterbourgon/ff/v3/fftoml"
)
-const flagConfigFlag = "flag-config-path"
-
func main() {
cmd := newRootCmd(commands.NewDefaultIO())
@@ -21,11 +17,7 @@ func newRootCmd(io commands.IO) *commands.Command {
cmd := commands.NewCommand(
commands.Metadata{
ShortUsage: " [flags] [...]",
- ShortHelp: "starts the gnoland blockchain node",
- Options: []ff.Option{
- ff.WithConfigFileFlag(flagConfigFlag),
- ff.WithConfigFileParser(fftoml.Parser),
- },
+ ShortHelp: "manages the gnoland blockchain node",
},
commands.NewEmptyConfig(),
commands.HelpExec,
@@ -33,7 +25,6 @@ func newRootCmd(io commands.IO) *commands.Command {
cmd.AddSubCommands(
newStartCmd(io),
- newGenesisCmd(io),
newSecretsCmd(io),
newConfigCmd(io),
)
diff --git a/gno.land/cmd/gnoland/start.go b/gno.land/cmd/gnoland/start.go
index 21f0cb4b1a6..77d7e20b8ef 100644
--- a/gno.land/cmd/gnoland/start.go
+++ b/gno.land/cmd/gnoland/start.go
@@ -14,6 +14,7 @@ import (
"time"
"github.com/gnolang/gno/gno.land/pkg/gnoland"
+ "github.com/gnolang/gno/gno.land/pkg/gnoland/ugnot"
"github.com/gnolang/gno/gno.land/pkg/log"
"github.com/gnolang/gno/gnovm/pkg/gnoenv"
abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types"
@@ -25,6 +26,7 @@ import (
"github.com/gnolang/gno/tm2/pkg/crypto"
"github.com/gnolang/gno/tm2/pkg/events"
osm "github.com/gnolang/gno/tm2/pkg/os"
+ "github.com/gnolang/gno/tm2/pkg/std"
"github.com/gnolang/gno/tm2/pkg/telemetry"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
@@ -42,6 +44,12 @@ var startGraphic = strings.ReplaceAll(`
/___/
`, "'", "`")
+var (
+ // Keep in sync with contribs/gnogenesis/internal/txs/txs_add_packages.go
+ genesisDeployAddress = crypto.MustAddressFromString("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") // test1
+ genesisDeployFee = std.NewFee(50000, std.MustParseCoin(ugnot.ValueString(1000000)))
+)
+
type startCfg struct {
gnoRootDir string // TODO: remove as part of https://github.com/gnolang/gno/issues/1952
skipFailingGenesisTxs bool // TODO: remove as part of https://github.com/gnolang/gno/issues/1952
@@ -51,8 +59,6 @@ type startCfg struct {
genesisFile string
chainID string
dataDir string
- genesisMaxVMCycles int64
- config string
lazyInit bool
logLevel string
@@ -137,20 +143,6 @@ func (c *startCfg) RegisterFlags(fs *flag.FlagSet) {
"replacement for '%%REMOTE%%' in genesis",
)
- fs.Int64Var(
- &c.genesisMaxVMCycles,
- "genesis-max-vm-cycles",
- 100_000_000,
- "set maximum allowed vm cycles per operation. Zero means no limit.",
- )
-
- fs.StringVar(
- &c.config,
- flagConfigFlag,
- "",
- "the flag config file (optional)",
- )
-
fs.StringVar(
&c.logLevel,
"log-level",
diff --git a/gno.land/cmd/gnoland/testdata/append.txtar b/gno.land/cmd/gnoland/testdata/append.txtar
index 46b66f9524b..3450b3e9b32 100644
--- a/gno.land/cmd/gnoland/testdata/append.txtar
+++ b/gno.land/cmd/gnoland/testdata/append.txtar
@@ -3,69 +3,69 @@ loadpkg gno.land/p/demo/ufmt
# start a new node
gnoland start
-gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/append -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1
+gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/append -gas-fee 1000000ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1
stdout OK!
# Call Append 1
-gnokey maketx call -pkgpath gno.land/r/append -func Append -gas-fee 1000000ugnot -gas-wanted 2000000 -args '1' -broadcast -chainid=tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/append -func Append -gas-fee 1000000ugnot -gas-wanted 4000000 -args '1' -broadcast -chainid=tendermint_test test1
stdout OK!
-gnokey maketx call -pkgpath gno.land/r/append -func AppendNil -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/append -func AppendNil -gas-fee 1000000ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1
stdout OK!
# Call Append 2
-gnokey maketx call -pkgpath gno.land/r/append -func Append -gas-fee 1000000ugnot -gas-wanted 2000000 -args '2' -broadcast -chainid=tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/append -func Append -gas-fee 1000000ugnot -gas-wanted 4000000 -args '2' -broadcast -chainid=tendermint_test test1
stdout OK!
# Call Append 3
-gnokey maketx call -pkgpath gno.land/r/append -func Append -gas-fee 1000000ugnot -gas-wanted 2000000 -args '3' -broadcast -chainid=tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/append -func Append -gas-fee 1000000ugnot -gas-wanted 4000000 -args '3' -broadcast -chainid=tendermint_test test1
stdout OK!
# Call render
-gnokey maketx call -pkgpath gno.land/r/append -func Render -gas-fee 1000000ugnot -gas-wanted 2000000 -args '' -broadcast -chainid=tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/append -func Render -gas-fee 1000000ugnot -gas-wanted 4000000 -args '' -broadcast -chainid=tendermint_test test1
stdout '("1-2-3-" string)'
stdout OK!
# Call Pop
-gnokey maketx call -pkgpath gno.land/r/append -func Pop -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/append -func Pop -gas-fee 1000000ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1
stdout OK!
# Call render
-gnokey maketx call -pkgpath gno.land/r/append -func Render -gas-fee 1000000ugnot -gas-wanted 2000000 -args '' -broadcast -chainid=tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/append -func Render -gas-fee 1000000ugnot -gas-wanted 4000000 -args '' -broadcast -chainid=tendermint_test test1
stdout '("2-3-" string)'
stdout OK!
# Call Append 42
-gnokey maketx call -pkgpath gno.land/r/append -func Append -gas-fee 1000000ugnot -gas-wanted 2000000 -args '42' -broadcast -chainid=tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/append -func Append -gas-fee 1000000ugnot -gas-wanted 4000000 -args '42' -broadcast -chainid=tendermint_test test1
stdout OK!
# Call render
-gnokey maketx call -pkgpath gno.land/r/append -func Render -gas-fee 1000000ugnot -gas-wanted 2000000 -args '' -broadcast -chainid=tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/append -func Render -gas-fee 1000000ugnot -gas-wanted 4000000 -args '' -broadcast -chainid=tendermint_test test1
stdout '("2-3-42-" string)'
stdout OK!
-gnokey maketx call -pkgpath gno.land/r/append -func CopyAppend -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/append -func CopyAppend -gas-fee 1000000ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1
stdout OK!
-gnokey maketx call -pkgpath gno.land/r/append -func PopB -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/append -func PopB -gas-fee 1000000ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1
stdout OK!
# Call render
-gnokey maketx call -pkgpath gno.land/r/append -func Render -gas-fee 1000000ugnot -gas-wanted 2000000 -args '' -broadcast -chainid=tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/append -func Render -gas-fee 1000000ugnot -gas-wanted 4000000 -args '' -broadcast -chainid=tendermint_test test1
stdout '("2-3-42-" string)'
stdout OK!
-gnokey maketx call -pkgpath gno.land/r/append -func AppendMoreAndC -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/append -func AppendMoreAndC -gas-fee 1000000ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1
stdout OK!
-gnokey maketx call -pkgpath gno.land/r/append -func ReassignC -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/append -func ReassignC -gas-fee 1000000ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1
stdout OK!
-gnokey maketx call -pkgpath gno.land/r/append -func Render -gas-fee 1000000ugnot -gas-wanted 2000000 -args '' -broadcast -chainid=tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/append -func Render -gas-fee 1000000ugnot -gas-wanted 4000000 -args '' -broadcast -chainid=tendermint_test test1
stdout '("2-3-42-70-100-" string)'
stdout OK!
-gnokey maketx call -pkgpath gno.land/r/append -func Render -gas-fee 1000000ugnot -gas-wanted 2000000 -args 'd' -broadcast -chainid=tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/append -func Render -gas-fee 1000000ugnot -gas-wanted 4000000 -args 'd' -broadcast -chainid=tendermint_test test1
stdout '("1-" string)'
stdout OK!
diff --git a/gno.land/cmd/gnoland/testdata/assertorigincall.txtar b/gno.land/cmd/gnoland/testdata/assertorigincall.txtar
index e3cd1be744a..1315f23cc95 100644
--- a/gno.land/cmd/gnoland/testdata/assertorigincall.txtar
+++ b/gno.land/cmd/gnoland/testdata/assertorigincall.txtar
@@ -33,85 +33,85 @@ gnoland start
# Test cases
## 1. MsgCall -> myrlm.A: PANIC
-! gnokey maketx call -pkgpath gno.land/r/myrlm -func A -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1
+! gnokey maketx call -pkgpath gno.land/r/myrlm -func A -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
stderr 'invalid non-origin call'
## 2. MsgCall -> myrlm.B: PASS
-gnokey maketx call -pkgpath gno.land/r/myrlm -func B -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/myrlm -func B -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
stdout 'OK!'
## 3. MsgCall -> myrlm.C: PASS
-gnokey maketx call -pkgpath gno.land/r/myrlm -func C -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/myrlm -func C -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
stdout 'OK!'
## 4. MsgCall -> r/foo.A -> myrlm.A: PANIC
-! gnokey maketx call -pkgpath gno.land/r/foo -func A -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1
+! gnokey maketx call -pkgpath gno.land/r/foo -func A -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
stderr 'invalid non-origin call'
## 5. MsgCall -> r/foo.B -> myrlm.B: PASS
-gnokey maketx call -pkgpath gno.land/r/foo -func B -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/foo -func B -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
stdout 'OK!'
## 6. MsgCall -> r/foo.C -> myrlm.C: PANIC
-! gnokey maketx call -pkgpath gno.land/r/foo -func C -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1
+! gnokey maketx call -pkgpath gno.land/r/foo -func C -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
stderr 'invalid non-origin call'
## remove due to update to maketx call can only call realm (case 7,8,9)
## 7. MsgCall -> p/demo/bar.A -> myrlm.A: PANIC
-## ! gnokey maketx call -pkgpath gno.land/p/demo/bar -func A -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1
+## ! gnokey maketx call -pkgpath gno.land/p/demo/bar -func A -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
## stderr 'invalid non-origin call'
## 8. MsgCall -> p/demo/bar.B -> myrlm.B: PASS
-## gnokey maketx call -pkgpath gno.land/p/demo/bar -func B -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1
+## gnokey maketx call -pkgpath gno.land/p/demo/bar -func B -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
## stdout 'OK!'
## 9. MsgCall -> p/demo/bar.C -> myrlm.C: PANIC
-## ! gnokey maketx call -pkgpath gno.land/p/demo/bar -func C -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1
+## ! gnokey maketx call -pkgpath gno.land/p/demo/bar -func C -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
## stderr 'invalid non-origin call'
## 10. MsgRun -> run.main -> myrlm.A: PANIC
-! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1 $WORK/run/myrlmA.gno
+! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/myrlmA.gno
stderr 'invalid non-origin call'
## 11. MsgRun -> run.main -> myrlm.B: PASS
-gnokey maketx run -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1 $WORK/run/myrlmB.gno
+gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/myrlmB.gno
stdout 'OK!'
## 12. MsgRun -> run.main -> myrlm.C: PANIC
-! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1 $WORK/run/myrlmC.gno
+! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/myrlmC.gno
stderr 'invalid non-origin call'
## 13. MsgRun -> run.main -> foo.A: PANIC
-! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1 $WORK/run/fooA.gno
+! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/fooA.gno
stderr 'invalid non-origin call'
## 14. MsgRun -> run.main -> foo.B: PASS
-gnokey maketx run -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1 $WORK/run/fooB.gno
+gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/fooB.gno
stdout 'OK!'
## 15. MsgRun -> run.main -> foo.C: PANIC
-! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1 $WORK/run/fooC.gno
+! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/fooC.gno
stderr 'invalid non-origin call'
## 16. MsgRun -> run.main -> bar.A: PANIC
-! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1 $WORK/run/barA.gno
+! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/barA.gno
stderr 'invalid non-origin call'
## 17. MsgRun -> run.main -> bar.B: PASS
-gnokey maketx run -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1 $WORK/run/barB.gno
+gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/barB.gno
stdout 'OK!'
## 18. MsgRun -> run.main -> bar.C: PANIC
-! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1 $WORK/run/barC.gno
+! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/barC.gno
stderr 'invalid non-origin call'
## remove testcase 19 due to maketx call forced to call a realm
## 19. MsgCall -> std.AssertOriginCall: pass
-## gnokey maketx call -pkgpath std -func AssertOriginCall -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1
+## gnokey maketx call -pkgpath std -func AssertOriginCall -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
## stdout 'OK!'
## 20. MsgRun -> std.AssertOriginCall: PANIC
-! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1 $WORK/run/baz.gno
+! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/baz.gno
stderr 'invalid non-origin call'
diff --git a/gno.land/cmd/gnoland/testdata/grc20_registry.txtar b/gno.land/cmd/gnoland/testdata/grc20_registry.txtar
index 20e78f7ba6e..a5f7ad5eee3 100644
--- a/gno.land/cmd/gnoland/testdata/grc20_registry.txtar
+++ b/gno.land/cmd/gnoland/testdata/grc20_registry.txtar
@@ -6,15 +6,15 @@ loadpkg gno.land/r/registry $WORK/registry
gnoland start
# we call Transfer with foo20, before it's registered
-gnokey maketx call -pkgpath gno.land/r/registry -func TransferByName -args 'foo20' -args 'g123456789' -args '42' -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/registry -func TransferByName -args 'foo20' -args 'g123456789' -args '42' -gas-fee 1000000ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1
stdout 'not found'
# add foo20, and foo20wrapper
-gnokey maketx addpkg -pkgdir $WORK/foo20 -pkgpath gno.land/r/foo20 -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1
-gnokey maketx addpkg -pkgdir $WORK/foo20wrapper -pkgpath gno.land/r/foo20wrapper -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1
+gnokey maketx addpkg -pkgdir $WORK/foo20 -pkgpath gno.land/r/foo20 -gas-fee 1000000ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1
+gnokey maketx addpkg -pkgdir $WORK/foo20wrapper -pkgpath gno.land/r/foo20wrapper -gas-fee 1000000ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1
# we call Transfer with foo20, after it's registered
-gnokey maketx call -pkgpath gno.land/r/registry -func TransferByName -args 'foo20' -args 'g123456789' -args '42' -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/registry -func TransferByName -args 'foo20' -args 'g123456789' -args '42' -gas-fee 1000000ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1
stdout 'same address, success!'
-- registry/registry.gno --
@@ -49,7 +49,7 @@ import "gno.land/r/registry"
import "gno.land/r/foo20"
func init() {
- registry.Register("foo20", foo20.Transfer)
+ registry.Register("foo20", foo20.Transfer)
}
-- foo20/foo20.gno --
diff --git a/gno.land/cmd/gnoland/testdata/issue_1167.txtar b/gno.land/cmd/gnoland/testdata/issue_1167.txtar
index c43f7a45bd5..73febb0235a 100644
--- a/gno.land/cmd/gnoland/testdata/issue_1167.txtar
+++ b/gno.land/cmd/gnoland/testdata/issue_1167.txtar
@@ -4,30 +4,30 @@ loadpkg gno.land/p/demo/avl
gnoland start
# add contract
-gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/demo/xx -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1
+gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/demo/xx -gas-fee 1000000ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1
stdout OK!
# execute New
-gnokey maketx call -pkgpath gno.land/r/demo/xx -func New -args X -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/demo/xx -func New -args X -gas-fee 1000000ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1
stdout OK!
# execute Delta for the first time
-gnokey maketx call -pkgpath gno.land/r/demo/xx -func Delta -args X -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/demo/xx -func Delta -args X -gas-fee 1000000ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1
stdout OK!
stdout '"1,1,1;" string'
# execute Delta for the second time
-gnokey maketx call -pkgpath gno.land/r/demo/xx -func Delta -args X -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/demo/xx -func Delta -args X -gas-fee 1000000ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1
stdout OK!
stdout '1,1,1;2,2,2;" string'
# execute Delta for the third time
-gnokey maketx call -pkgpath gno.land/r/demo/xx -func Delta -args X -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/demo/xx -func Delta -args X -gas-fee 1000000ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1
stdout OK!
stdout '1,1,1;2,2,2;3,3,3;" string'
# execute Render
-gnokey maketx call -pkgpath gno.land/r/demo/xx -func Render -args X -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/demo/xx -func Render -args X -gas-fee 1000000ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1
stdout OK!
stdout '1,1,1;2,2,2;3,3,3;" string'
diff --git a/gno.land/cmd/gnoland/testdata/params.txtar b/gno.land/cmd/gnoland/testdata/params.txtar
new file mode 100644
index 00000000000..30363aa6369
--- /dev/null
+++ b/gno.land/cmd/gnoland/testdata/params.txtar
@@ -0,0 +1,65 @@
+# test for https://github.com/gnolang/gno/pull/2920
+
+gnoland start
+
+# query before adding the package
+gnokey query params/vm/gno.land/r/sys/setter.foo.string
+stdout 'data: $'
+gnokey query params/vm/gno.land/r/sys/setter.bar.bool
+stdout 'data: $'
+gnokey query params/vm/gno.land/r/sys/setter.baz.int64
+stdout 'data: $'
+
+gnokey maketx addpkg -pkgdir $WORK/setter -pkgpath gno.land/r/sys/setter -gas-fee 1000000ugnot -gas-wanted 100000000 -broadcast -chainid=tendermint_test test1
+
+# query after adding the package, but before setting values
+gnokey query params/vm/gno.land/r/sys/setter.foo.string
+stdout 'data: $'
+gnokey query params/vm/gno.land/r/sys/setter.bar.bool
+stdout 'data: $'
+gnokey query params/vm/gno.land/r/sys/setter.baz.int64
+stdout 'data: $'
+
+
+# set foo (string)
+gnokey maketx call -pkgpath gno.land/r/sys/setter -func SetFoo -args foo1 -gas-fee 1000000ugnot -gas-wanted 10000000 -broadcast -chainid=tendermint_test test1
+gnokey query params/vm/gno.land/r/sys/setter.foo.string
+stdout 'data: "foo1"'
+
+# override foo
+gnokey maketx call -pkgpath gno.land/r/sys/setter -func SetFoo -args foo2 -gas-fee 1000000ugnot -gas-wanted 10000000 -broadcast -chainid=tendermint_test test1
+gnokey query params/vm/gno.land/r/sys/setter.foo.string
+stdout 'data: "foo2"'
+
+
+# set bar (bool)
+gnokey maketx call -pkgpath gno.land/r/sys/setter -func SetBar -args true -gas-fee 1000000ugnot -gas-wanted 10000000 -broadcast -chainid=tendermint_test test1
+gnokey query params/vm/gno.land/r/sys/setter.bar.bool
+stdout 'data: true'
+
+# override bar
+gnokey maketx call -pkgpath gno.land/r/sys/setter -func SetBar -args false -gas-fee 1000000ugnot -gas-wanted 10000000 -broadcast -chainid=tendermint_test test1
+gnokey query params/vm/gno.land/r/sys/setter.bar.bool
+stdout 'data: false'
+
+
+# set baz (bool)
+gnokey maketx call -pkgpath gno.land/r/sys/setter -func SetBaz -args 1337 -gas-fee 1000000ugnot -gas-wanted 10000000 -broadcast -chainid=tendermint_test test1
+gnokey query params/vm/gno.land/r/sys/setter.baz.int64
+stdout 'data: "1337"'
+
+# override baz
+gnokey maketx call -pkgpath gno.land/r/sys/setter -func SetBaz -args 31337 -gas-fee 1000000ugnot -gas-wanted 10000000 -broadcast -chainid=tendermint_test test1
+gnokey query params/vm/gno.land/r/sys/setter.baz.int64
+stdout 'data: "31337"'
+
+-- setter/setter.gno --
+package setter
+
+import (
+ "std"
+)
+
+func SetFoo(newFoo string) { std.SetParamString("foo.string", newFoo) }
+func SetBar(newBar bool) { std.SetParamBool("bar.bool", newBar) }
+func SetBaz(newBaz int64) { std.SetParamInt64("baz.int64", newBaz) }
diff --git a/gno.land/cmd/gnoland/testdata/prevrealm.txtar b/gno.land/cmd/gnoland/testdata/prevrealm.txtar
index 72a207fae22..7a0d994a686 100644
--- a/gno.land/cmd/gnoland/testdata/prevrealm.txtar
+++ b/gno.land/cmd/gnoland/testdata/prevrealm.txtar
@@ -34,60 +34,60 @@ env RFOO_ADDR=g1evezrh92xaucffmtgsaa3rvmz5s8kedffsg469
# Test cases
## 1. MsgCall -> myrlm.A: user address
-gnokey maketx call -pkgpath gno.land/r/myrlm -func A -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/myrlm -func A -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
stdout ${USER_ADDR_test1}
## 2. MsgCall -> myrealm.B -> myrlm.A: user address
-gnokey maketx call -pkgpath gno.land/r/myrlm -func B -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/myrlm -func B -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
stdout ${USER_ADDR_test1}
## 3. MsgCall -> r/foo.A -> myrlm.A: r/foo
-gnokey maketx call -pkgpath gno.land/r/foo -func A -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/foo -func A -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
stdout ${RFOO_ADDR}
## 4. MsgCall -> r/foo.B -> myrlm.B -> r/foo.A: r/foo
-gnokey maketx call -pkgpath gno.land/r/foo -func B -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/foo -func B -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
stdout ${RFOO_ADDR}
## remove due to update to maketx call can only call realm (case 5, 6, 13)
## 5. MsgCall -> p/demo/bar.A -> myrlm.A: user address
-## gnokey maketx call -pkgpath gno.land/p/demo/bar -func A -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1
+## gnokey maketx call -pkgpath gno.land/p/demo/bar -func A -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
## stdout ${USER_ADDR_test1}
## 6. MsgCall -> p/demo/bar.B -> myrlm.B -> r/foo.A: user address
-## gnokey maketx call -pkgpath gno.land/p/demo/bar -func B -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1
+## gnokey maketx call -pkgpath gno.land/p/demo/bar -func B -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
## stdout ${USER_ADDR_test1}
## 7. MsgRun -> myrlm.A: user address
-gnokey maketx run -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1 $WORK/run/myrlmA.gno
+gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/myrlmA.gno
stdout ${USER_ADDR_test1}
## 8. MsgRun -> myrealm.B -> myrlm.A: user address
-gnokey maketx run -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1 $WORK/run/myrlmB.gno
+gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/myrlmB.gno
stdout ${USER_ADDR_test1}
## 9. MsgRun -> r/foo.A -> myrlm.A: r/foo
-gnokey maketx run -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1 $WORK/run/fooA.gno
+gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/fooA.gno
stdout ${RFOO_ADDR}
## 10. MsgRun -> r/foo.B -> myrlm.B -> r/foo.A: r/foo
-gnokey maketx run -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1 $WORK/run/fooB.gno
+gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/fooB.gno
stdout ${RFOO_ADDR}
## 11. MsgRun -> p/demo/bar.A -> myrlm.A: user address
-gnokey maketx run -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1 $WORK/run/barA.gno
+gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/barA.gno
stdout ${USER_ADDR_test1}
## 12. MsgRun -> p/demo/bar.B -> myrlm.B -> r/foo.A: user address
-gnokey maketx run -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1 $WORK/run/barB.gno
+gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/barB.gno
stdout ${USER_ADDR_test1}
## 13. MsgCall -> std.PrevRealm(): user address
-## gnokey maketx call -pkgpath std -func PrevRealm -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1
+## gnokey maketx call -pkgpath std -func PrevRealm -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1
## stdout ${USER_ADDR_test1}
## 14. MsgRun -> std.PrevRealm(): user address
-gnokey maketx run -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1 $WORK/run/baz.gno
+gnokey maketx run -gas-fee 100000ugnot -gas-wanted 4000000 -broadcast -chainid tendermint_test test1 $WORK/run/baz.gno
stdout ${USER_ADDR_test1}
-- r/myrlm/myrlm.gno --
diff --git a/gno.land/cmd/gnoland/testdata/restart_missing_type.txtar b/gno.land/cmd/gnoland/testdata/restart_missing_type.txtar
index 7eb91096437..c492f1c6646 100644
--- a/gno.land/cmd/gnoland/testdata/restart_missing_type.txtar
+++ b/gno.land/cmd/gnoland/testdata/restart_missing_type.txtar
@@ -162,7 +162,7 @@ gnoland restart
}
],
"fee": {
- "gas_wanted": "16000000",
+ "gas_wanted": "20000000",
"gas_fee": "1000000ugnot"
},
"signatures": [],
@@ -193,10 +193,9 @@ gnoland restart
}
],
"fee": {
- "gas_wanted": "15000000",
+ "gas_wanted": "16000000",
"gas_fee": "1000000ugnot"
},
"signatures": [],
"memo": ""
}
-
diff --git a/gno.land/cmd/gnoland/types.go b/gno.land/cmd/gnoland/types.go
deleted file mode 100644
index a48bfaf7b31..00000000000
--- a/gno.land/cmd/gnoland/types.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package main
-
-import (
- "github.com/gnolang/gno/tm2/pkg/std"
-)
-
-// txStore is a wrapper for TM2 transactions
-type txStore []std.Tx
-
-// leftMerge merges the two tx stores, with
-// preference to the left
-func (i *txStore) leftMerge(b txStore) error {
- // Build out the tx hash map
- txHashMap := make(map[string]struct{}, len(*i))
-
- for _, tx := range *i {
- txHash, err := getTxHash(tx)
- if err != nil {
- return err
- }
-
- txHashMap[txHash] = struct{}{}
- }
-
- for _, tx := range b {
- txHash, err := getTxHash(tx)
- if err != nil {
- return err
- }
-
- if _, exists := txHashMap[txHash]; !exists {
- *i = append(*i, tx)
- }
- }
-
- return nil
-}
diff --git a/gno.land/cmd/gnoweb/README.md b/gno.land/cmd/gnoweb/README.md
index 941d5e4f67e..6379d3f6c43 100644
--- a/gno.land/cmd/gnoweb/README.md
+++ b/gno.land/cmd/gnoweb/README.md
@@ -2,7 +2,7 @@
The gno.land web interface.
-Live demo: https://test3.gno.land/
+Live demo: https://gno.land/
## Install `gnoweb`
diff --git a/gno.land/cmd/gnoweb/main.go b/gno.land/cmd/gnoweb/main.go
index 547134548ff..5cec7257ebe 100644
--- a/gno.land/cmd/gnoweb/main.go
+++ b/gno.land/cmd/gnoweb/main.go
@@ -37,6 +37,7 @@ func runMain(args []string) error {
fs.StringVar(&cfg.HelpRemote, "help-remote", cfg.HelpRemote, "help page's remote addr")
fs.BoolVar(&cfg.WithAnalytics, "with-analytics", cfg.WithAnalytics, "enable privacy-first analytics")
fs.StringVar(&bindAddress, "bind", "127.0.0.1:8888", "server listening address")
+ fs.BoolVar(&cfg.WithHTML, "with-html", cfg.WithHTML, "Enable HTML parsing in markdown rendering")
if err := fs.Parse(args); err != nil {
return err
diff --git a/gno.land/pkg/gnoclient/client_test.go b/gno.land/pkg/gnoclient/client_test.go
index b7eb21837a7..1f8563d34fe 100644
--- a/gno.land/pkg/gnoclient/client_test.go
+++ b/gno.land/pkg/gnoclient/client_test.go
@@ -8,6 +8,7 @@ import (
"github.com/gnolang/gno/gno.land/pkg/gnoland/ugnot"
"github.com/gnolang/gno/gno.land/pkg/sdk/vm"
+ "github.com/gnolang/gno/gnovm"
abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types"
ctypes "github.com/gnolang/gno/tm2/pkg/bft/rpc/core/types"
"github.com/gnolang/gno/tm2/pkg/bft/types"
@@ -17,7 +18,7 @@ import (
"github.com/gnolang/gno/tm2/pkg/std"
)
-var testGasFee = ugnot.ValueString(10000)
+var testGasFee = ugnot.ValueString(10_000)
func TestRender(t *testing.T) {
t.Parallel()
@@ -652,8 +653,8 @@ func main() {
msg := vm.MsgRun{
Caller: caller.GetAddress(),
- Package: &std.MemPackage{
- Files: []*std.MemFile{
+ Package: &gnovm.MemPackage{
+ Files: []*gnovm.MemFile{
{
Name: "main.gno",
Body: fileBody,
@@ -729,8 +730,8 @@ func main() {
msg1 := vm.MsgRun{
Caller: caller.GetAddress(),
- Package: &std.MemPackage{
- Files: []*std.MemFile{
+ Package: &gnovm.MemPackage{
+ Files: []*gnovm.MemFile{
{
Name: "main1.gno",
Body: fileBody,
@@ -742,8 +743,8 @@ func main() {
msg2 := vm.MsgRun{
Caller: caller.GetAddress(),
- Package: &std.MemPackage{
- Files: []*std.MemFile{
+ Package: &gnovm.MemPackage{
+ Files: []*gnovm.MemFile{
{
Name: "main2.gno",
Body: fileBody,
@@ -794,10 +795,10 @@ func TestRunErrors(t *testing.T) {
msgs: []vm.MsgRun{
{
Caller: mockAddress,
- Package: &std.MemPackage{
+ Package: &gnovm.MemPackage{
Name: "",
Path: "",
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{
Name: "file1.gno",
Body: "",
@@ -841,10 +842,10 @@ func TestRunErrors(t *testing.T) {
msgs: []vm.MsgRun{
{
Caller: mockAddress,
- Package: &std.MemPackage{
+ Package: &gnovm.MemPackage{
Name: "",
Path: "",
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{
Name: "file1.gno",
Body: "",
@@ -872,10 +873,10 @@ func TestRunErrors(t *testing.T) {
msgs: []vm.MsgRun{
{
Caller: mockAddress,
- Package: &std.MemPackage{
+ Package: &gnovm.MemPackage{
Name: "",
Path: "",
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{
Name: "file1.gno",
Body: "",
@@ -903,10 +904,10 @@ func TestRunErrors(t *testing.T) {
msgs: []vm.MsgRun{
{
Caller: mockAddress,
- Package: &std.MemPackage{
+ Package: &gnovm.MemPackage{
Name: "",
Path: "",
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{
Name: "file1.gno",
Body: "",
@@ -943,7 +944,7 @@ func TestRunErrors(t *testing.T) {
msgs: []vm.MsgRun{
{
Caller: mockAddress,
- Package: &std.MemPackage{Name: "", Path: " "},
+ Package: &gnovm.MemPackage{Name: "", Path: " "},
Send: nil,
},
},
@@ -993,10 +994,10 @@ func TestAddPackageErrors(t *testing.T) {
msgs: []vm.MsgAddPackage{
{
Creator: mockAddress,
- Package: &std.MemPackage{
+ Package: &gnovm.MemPackage{
Name: "",
Path: "",
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{
Name: "file1.gno",
Body: "",
@@ -1040,10 +1041,10 @@ func TestAddPackageErrors(t *testing.T) {
msgs: []vm.MsgAddPackage{
{
Creator: mockAddress,
- Package: &std.MemPackage{
+ Package: &gnovm.MemPackage{
Name: "",
Path: "",
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{
Name: "file1.gno",
Body: "",
@@ -1071,10 +1072,10 @@ func TestAddPackageErrors(t *testing.T) {
msgs: []vm.MsgAddPackage{
{
Creator: mockAddress,
- Package: &std.MemPackage{
+ Package: &gnovm.MemPackage{
Name: "",
Path: "",
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{
Name: "file1.gno",
Body: "",
@@ -1102,10 +1103,10 @@ func TestAddPackageErrors(t *testing.T) {
msgs: []vm.MsgAddPackage{
{
Creator: mockAddress,
- Package: &std.MemPackage{
+ Package: &gnovm.MemPackage{
Name: "",
Path: "",
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{
Name: "file1.gno",
Body: "",
@@ -1142,7 +1143,7 @@ func TestAddPackageErrors(t *testing.T) {
msgs: []vm.MsgAddPackage{
{
Creator: mockAddress,
- Package: &std.MemPackage{Name: "", Path: ""},
+ Package: &gnovm.MemPackage{Name: "", Path: ""},
Deposit: nil,
},
},
diff --git a/gno.land/pkg/gnoclient/integration_test.go b/gno.land/pkg/gnoclient/integration_test.go
index ea068e0680b..0a06eb4756a 100644
--- a/gno.land/pkg/gnoclient/integration_test.go
+++ b/gno.land/pkg/gnoclient/integration_test.go
@@ -5,17 +5,17 @@ import (
"github.com/gnolang/gno/gnovm/pkg/gnolang"
- "github.com/gnolang/gno/tm2/pkg/sdk/bank"
- "github.com/gnolang/gno/tm2/pkg/std"
-
"github.com/gnolang/gno/gno.land/pkg/gnoland/ugnot"
"github.com/gnolang/gno/gno.land/pkg/integration"
"github.com/gnolang/gno/gno.land/pkg/sdk/vm"
+ "github.com/gnolang/gno/gnovm"
"github.com/gnolang/gno/gnovm/pkg/gnoenv"
rpcclient "github.com/gnolang/gno/tm2/pkg/bft/rpc/client"
"github.com/gnolang/gno/tm2/pkg/crypto"
"github.com/gnolang/gno/tm2/pkg/crypto/keys"
"github.com/gnolang/gno/tm2/pkg/log"
+ "github.com/gnolang/gno/tm2/pkg/sdk/bank"
+ "github.com/gnolang/gno/tm2/pkg/std"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -316,9 +316,9 @@ func main() {
// Make Msg configs
msg := vm.MsgRun{
Caller: caller.GetAddress(),
- Package: &std.MemPackage{
+ Package: &gnovm.MemPackage{
Name: "main",
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{
Name: "main.gno",
Body: fileBody,
@@ -359,7 +359,7 @@ func TestRunMultiple_Integration(t *testing.T) {
// Make Tx config
baseCfg := BaseTxCfg{
GasFee: ugnot.ValueString(10000),
- GasWanted: 8000000,
+ GasWanted: 13000000,
AccountNumber: 0,
SequenceNumber: 0,
Memo: "",
@@ -393,9 +393,9 @@ func main() {
// Make Msg configs
msg1 := vm.MsgRun{
Caller: caller.GetAddress(),
- Package: &std.MemPackage{
+ Package: &gnovm.MemPackage{
Name: "main",
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{
Name: "main.gno",
Body: fileBody1,
@@ -406,9 +406,9 @@ func main() {
}
msg2 := vm.MsgRun{
Caller: caller.GetAddress(),
- Package: &std.MemPackage{
+ Package: &gnovm.MemPackage{
Name: "main",
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{
Name: "main.gno",
Body: fileBody2,
@@ -474,10 +474,10 @@ func Echo(str string) string {
// Make Msg config
msg := vm.MsgAddPackage{
Creator: caller.GetAddress(),
- Package: &std.MemPackage{
+ Package: &gnovm.MemPackage{
Name: "echo",
Path: deploymentPath,
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{
Name: fileName,
Body: body,
@@ -556,7 +556,7 @@ func Echo(str string) string {
body2 := `package hello
func Hello(str string) string {
- return "Hello " + str + "!"
+ return "Hello " + str + "!"
}`
caller, err := client.Signer.Info()
@@ -564,10 +564,10 @@ func Hello(str string) string {
msg1 := vm.MsgAddPackage{
Creator: caller.GetAddress(),
- Package: &std.MemPackage{
+ Package: &gnovm.MemPackage{
Name: "echo",
Path: deploymentPath1,
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{
Name: "echo.gno",
Body: body1,
@@ -579,10 +579,10 @@ func Hello(str string) string {
msg2 := vm.MsgAddPackage{
Creator: caller.GetAddress(),
- Package: &std.MemPackage{
+ Package: &gnovm.MemPackage{
Name: "hello",
Path: deploymentPath2,
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{
Name: "gno.mod",
Body: "module gno.land/p/demo/integration/test/hello",
diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go
index 2380658c6e9..e784f2148aa 100644
--- a/gno.land/pkg/gnoland/app.go
+++ b/gno.land/pkg/gnoland/app.go
@@ -19,6 +19,7 @@ import (
"github.com/gnolang/gno/tm2/pkg/sdk"
"github.com/gnolang/gno/tm2/pkg/sdk/auth"
"github.com/gnolang/gno/tm2/pkg/sdk/bank"
+ "github.com/gnolang/gno/tm2/pkg/sdk/params"
"github.com/gnolang/gno/tm2/pkg/std"
"github.com/gnolang/gno/tm2/pkg/store"
"github.com/gnolang/gno/tm2/pkg/store/dbadapter"
@@ -34,7 +35,6 @@ type AppOptions struct {
DB dbm.DB // required
Logger *slog.Logger // required
EventSwitch events.EventSwitch // required
- MaxCycles int64 // hard limit for cycles in GnoVM
InitChainerConfig // options related to InitChainer
}
@@ -88,12 +88,13 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) {
// Construct keepers.
acctKpr := auth.NewAccountKeeper(mainKey, ProtoGnoAccount)
bankKpr := bank.NewBankKeeper(acctKpr)
- vmk := vm.NewVMKeeper(baseKey, mainKey, acctKpr, bankKpr, cfg.MaxCycles)
+ paramsKpr := params.NewParamsKeeper(mainKey, "vm")
+ vmk := vm.NewVMKeeper(baseKey, mainKey, acctKpr, bankKpr, paramsKpr)
// Set InitChainer
icc := cfg.InitChainerConfig
icc.baseApp = baseApp
- icc.acctKpr, icc.bankKpr, icc.vmKpr = acctKpr, bankKpr, vmk
+ icc.acctKpr, icc.bankKpr, icc.vmKpr, icc.paramsKpr = acctKpr, bankKpr, vmk, paramsKpr
baseApp.SetInitChainer(icc.InitChainer)
// Set AnteHandler
@@ -148,6 +149,7 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) {
// Set a handler Route.
baseApp.Router().AddRoute("auth", auth.NewHandler(acctKpr))
baseApp.Router().AddRoute("bank", bank.NewHandler(bankKpr))
+ baseApp.Router().AddRoute("params", params.NewHandler(paramsKpr))
baseApp.Router().AddRoute("vm", vm.NewHandler(vmk))
// Load latest version.
@@ -225,10 +227,11 @@ type InitChainerConfig struct {
// These fields are passed directly by NewAppWithOptions, and should not be
// configurable by end-users.
- baseApp *sdk.BaseApp
- vmKpr vm.VMKeeperI
- acctKpr auth.AccountKeeperI
- bankKpr bank.BankKeeperI
+ baseApp *sdk.BaseApp
+ vmKpr vm.VMKeeperI
+ acctKpr auth.AccountKeeperI
+ bankKpr bank.BankKeeperI
+ paramsKpr params.ParamsKeeperI
}
// InitChainer is the function that can be used as a [sdk.InitChainer].
diff --git a/gno.land/pkg/gnoland/app_test.go b/gno.land/pkg/gnoland/app_test.go
index 193ff0b0b14..87b624280c6 100644
--- a/gno.land/pkg/gnoland/app_test.go
+++ b/gno.land/pkg/gnoland/app_test.go
@@ -9,7 +9,8 @@ import (
"time"
"github.com/gnolang/gno/gno.land/pkg/sdk/vm"
- gnostd "github.com/gnolang/gno/gnovm/stdlibs/std"
+ "github.com/gnolang/gno/gnovm"
+ gnostdlibs "github.com/gnolang/gno/gnovm/stdlibs/std"
"github.com/gnolang/gno/tm2/pkg/amino"
abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types"
bft "github.com/gnolang/gno/tm2/pkg/bft/types"
@@ -53,7 +54,7 @@ func TestNewAppWithOptions(t *testing.T) {
},
Txs: []std.Tx{
{
- Msgs: []std.Msg{vm.NewMsgAddPackage(addr, "gno.land/r/demo", []*std.MemFile{
+ Msgs: []std.Msg{vm.NewMsgAddPackage(addr, "gno.land/r/demo", []*gnovm.MemFile{
{
Name: "demo.gno",
Body: "package demo; func Hello() string { return `hello`; }",
@@ -308,7 +309,7 @@ func TestEndBlocker(t *testing.T) {
c := newCollector[validatorUpdate](mockEventSwitch, noFilter)
// Fire a GnoVM event
- mockEventSwitch.FireEvent(gnostd.GnoEvent{})
+ mockEventSwitch.FireEvent(gnostdlibs.GnoEvent{})
// Create the EndBlocker
eb := EndBlocker(c, mockVMKeeper, &mockEndBlockerApp{})
@@ -351,7 +352,7 @@ func TestEndBlocker(t *testing.T) {
c := newCollector[validatorUpdate](mockEventSwitch, noFilter)
// Fire a GnoVM event
- mockEventSwitch.FireEvent(gnostd.GnoEvent{})
+ mockEventSwitch.FireEvent(gnostdlibs.GnoEvent{})
// Create the EndBlocker
eb := EndBlocker(c, mockVMKeeper, &mockEndBlockerApp{})
@@ -390,7 +391,7 @@ func TestEndBlocker(t *testing.T) {
// Construct the GnoVM events
vmEvents := make([]abci.Event, 0, len(changes))
for index := range changes {
- event := gnostd.GnoEvent{
+ event := gnostdlibs.GnoEvent{
Type: validatorAddedEvent,
PkgPath: valRealm,
}
@@ -399,7 +400,7 @@ func TestEndBlocker(t *testing.T) {
if index%2 == 0 {
changes[index].Power = 0
- event = gnostd.GnoEvent{
+ event = gnostdlibs.GnoEvent{
Type: validatorRemovedEvent,
PkgPath: valRealm,
}
diff --git a/gno.land/pkg/gnoland/node_inmemory.go b/gno.land/pkg/gnoland/node_inmemory.go
index d168c955607..f81838e1eb3 100644
--- a/gno.land/pkg/gnoland/node_inmemory.go
+++ b/gno.land/pkg/gnoland/node_inmemory.go
@@ -20,11 +20,10 @@ import (
)
type InMemoryNodeConfig struct {
- PrivValidator bft.PrivValidator // identity of the validator
- Genesis *bft.GenesisDoc
- TMConfig *tmcfg.Config
- GenesisMaxVMCycles int64
- DB *memdb.MemDB // will be initialized if nil
+ PrivValidator bft.PrivValidator // identity of the validator
+ Genesis *bft.GenesisDoc
+ TMConfig *tmcfg.Config
+ DB *memdb.MemDB // will be initialized if nil
// If StdlibDir not set, then it's filepath.Join(TMConfig.RootDir, "gnovm", "stdlibs")
InitChainerConfig
@@ -106,7 +105,6 @@ func NewInMemoryNode(logger *slog.Logger, cfg *InMemoryNodeConfig) (*node.Node,
// Initialize the application with the provided options
gnoApp, err := NewAppWithOptions(&AppOptions{
Logger: logger,
- MaxCycles: cfg.GenesisMaxVMCycles,
DB: cfg.DB,
EventSwitch: evsw,
InitChainerConfig: cfg.InitChainerConfig,
diff --git a/gno.land/pkg/gnoland/vals.go b/gno.land/pkg/gnoland/validators.go
similarity index 94%
rename from gno.land/pkg/gnoland/vals.go
rename to gno.land/pkg/gnoland/validators.go
index 1843dff3984..339ebd9dcad 100644
--- a/gno.land/pkg/gnoland/vals.go
+++ b/gno.land/pkg/gnoland/validators.go
@@ -9,7 +9,7 @@ import (
)
const (
- valRealm = "gno.land/r/sys/validators"
+ valRealm = "gno.land/r/sys/validators/v2" // XXX: make it configurable from GovDAO
valChangesFn = "GetChanges"
validatorAddedEvent = "ValidatorAdded"
diff --git a/gno.land/pkg/gnoweb/gnoweb.go b/gno.land/pkg/gnoweb/gnoweb.go
index 5377ae6a420..ed6271f5afe 100644
--- a/gno.land/pkg/gnoweb/gnoweb.go
+++ b/gno.land/pkg/gnoweb/gnoweb.go
@@ -13,14 +13,15 @@ import (
"net/url"
"os"
"path/filepath"
+ "regexp"
"runtime"
"strings"
"time"
+ "github.com/gnolang/gno/gnovm"
"github.com/gnolang/gno/tm2/pkg/amino"
abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types"
"github.com/gnolang/gno/tm2/pkg/bft/rpc/client"
- "github.com/gnolang/gno/tm2/pkg/std"
"github.com/gorilla/mux"
"github.com/gotuna/gotuna"
@@ -45,6 +46,7 @@ type Config struct {
HelpChainID string
HelpRemote string
WithAnalytics bool
+ WithHTML bool
}
func NewDefaultConfig() Config {
@@ -56,6 +58,7 @@ func NewDefaultConfig() Config {
HelpChainID: "dev",
HelpRemote: "127.0.0.1:26657",
WithAnalytics: false,
+ WithHTML: false,
}
}
@@ -109,6 +112,34 @@ func MakeApp(logger *slog.Logger, cfg Config) gotuna.App {
return app
}
+var (
+ inlineCodePattern = regexp.MustCompile("`[^`]*`")
+ htmlTagPattern = regexp.MustCompile(`<\/?\w+[^>]*?>`)
+)
+
+func sanitizeContent(cfg *Config, content string) string {
+ if cfg.WithHTML {
+ return content
+ }
+
+ placeholders := map[string]string{}
+ contentWithPlaceholders := inlineCodePattern.ReplaceAllStringFunc(content, func(match string) string {
+ placeholder := fmt.Sprintf("__GNOMDCODE_%d__", len(placeholders))
+ placeholders[placeholder] = match
+ return placeholder
+ })
+
+ sanitizedContent := htmlTagPattern.ReplaceAllString(contentWithPlaceholders, "")
+
+ if len(placeholders) > 0 {
+ for placeholder, code := range placeholders {
+ sanitizedContent = strings.ReplaceAll(sanitizedContent, placeholder, code)
+ }
+ }
+
+ return sanitizedContent
+}
+
// handlerRealmAlias is used to render official pages from realms.
// url is intended to be shorter.
// UX is intended to be more minimalistic.
@@ -151,7 +182,7 @@ func handlerRealmAlias(logger *slog.Logger, app gotuna.App, cfg *Config, rlmpath
tmpl.Set("RealmPath", rlmpath)
tmpl.Set("Query", querystr)
tmpl.Set("PathLinks", pathLinks)
- tmpl.Set("Contents", string(res.Data))
+ tmpl.Set("Contents", sanitizeContent(cfg, string(res.Data)))
tmpl.Set("Config", cfg)
tmpl.Set("IsAlias", true)
tmpl.Render(w, r, "realm_render.html", "funcs.html")
@@ -339,7 +370,7 @@ func handleRealmRender(logger *slog.Logger, app gotuna.App, cfg *Config, w http.
tmpl.Set("RealmPath", rlmpath)
tmpl.Set("Query", querystr)
tmpl.Set("PathLinks", pathLinks)
- tmpl.Set("Contents", string(res.Data))
+ tmpl.Set("Contents", sanitizeContent(cfg, string(res.Data)))
tmpl.Set("Config", cfg)
tmpl.Set("HasReadme", hasReadme)
tmpl.Render(w, r, "realm_render.html", "funcs.html")
@@ -358,7 +389,7 @@ func handlerPackageFile(logger *slog.Logger, app gotuna.App, cfg *Config) http.H
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
pkgpath := "gno.land/p/" + vars["filepath"]
- diruri, filename := std.SplitFilepath(pkgpath)
+ diruri, filename := gnovm.SplitFilepath(pkgpath)
if filename == "" && diruri == pkgpath {
// redirect to diruri + "/"
http.Redirect(w, r, "/p/"+vars["filepath"]+"/", http.StatusFound)
@@ -476,7 +507,7 @@ func handleNotFound(logger *slog.Logger, app gotuna.App, cfg *Config, path strin
// decode path for non-ascii characters
decodedPath, err := url.PathUnescape(path)
if err != nil {
- logger.Error("failed to decode path", err)
+ logger.Error("failed to decode path", "error", err)
decodedPath = path
}
w.WriteHeader(http.StatusNotFound)
@@ -491,7 +522,7 @@ func writeError(logger *slog.Logger, w http.ResponseWriter, err error) {
if details := errors.Unwrap(err); details != nil {
logger.Error("handler", "error", err, "details", details)
} else {
- logger.Error("handler", "error:", err)
+ logger.Error("handler", "error", err)
}
// XXX: writeError should return an error page template.
diff --git a/gno.land/pkg/gnoweb/views/realm_help.html b/gno.land/pkg/gnoweb/views/realm_help.html
index b9c8e119e7a..0a93f786c0d 100644
--- a/gno.land/pkg/gnoweb/views/realm_help.html
+++ b/gno.land/pkg/gnoweb/views/realm_help.html
@@ -17,7 +17,7 @@
These are the realm's exposed functions ("public smart contracts").
- My address: (see `gnokey list` )
+ My address: (see `gnokey list` )
{{ template "func_specs" . }}
diff --git a/gno.land/pkg/integration/testdata/loadpkg_example.txtar b/gno.land/pkg/integration/testdata/loadpkg_example.txtar
index d0c95331ff5..9dccd72c8a6 100644
--- a/gno.land/pkg/integration/testdata/loadpkg_example.txtar
+++ b/gno.land/pkg/integration/testdata/loadpkg_example.txtar
@@ -4,11 +4,11 @@ loadpkg gno.land/p/demo/ufmt
## start a new node
gnoland start
-gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/importtest -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1
+gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/importtest -gas-fee 1000000ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1
stdout OK!
## execute Render
-gnokey maketx call -pkgpath gno.land/r/importtest -func Render -gas-fee 1000000ugnot -gas-wanted 2000000 -args '' -broadcast -chainid=tendermint_test test1
+gnokey maketx call -pkgpath gno.land/r/importtest -func Render -gas-fee 1000000ugnot -gas-wanted 4000000 -args '' -broadcast -chainid=tendermint_test test1
stdout '("92054" string)'
stdout OK!
@@ -25,4 +25,3 @@ import (
func Render(_ string) string {
return ufmt.Sprintf("%d", 92054)
}
-
diff --git a/gno.land/pkg/keyscli/root.go b/gno.land/pkg/keyscli/root.go
index 19513fc0de6..c910e01b82c 100644
--- a/gno.land/pkg/keyscli/root.go
+++ b/gno.land/pkg/keyscli/root.go
@@ -30,6 +30,7 @@ func NewRootCmd(io commands.IO, base client.BaseOptions) *commands.Command {
cmd.AddSubCommands(
client.NewAddCmd(cfg, io),
client.NewDeleteCmd(cfg, io),
+ client.NewRotateCmd(cfg, io),
client.NewGenerateCmd(cfg, io),
client.NewExportCmd(cfg, io),
client.NewImportCmd(cfg, io),
diff --git a/gno.land/pkg/keyscli/run.go b/gno.land/pkg/keyscli/run.go
index aa0ee298201..b0e05fe5a84 100644
--- a/gno.land/pkg/keyscli/run.go
+++ b/gno.land/pkg/keyscli/run.go
@@ -8,6 +8,7 @@ import (
"os"
"github.com/gnolang/gno/gno.land/pkg/sdk/vm"
+ "github.com/gnolang/gno/gnovm"
gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
"github.com/gnolang/gno/tm2/pkg/amino"
"github.com/gnolang/gno/tm2/pkg/commands"
@@ -73,13 +74,13 @@ func execMakeRun(cfg *MakeRunCfg, args []string, cmdio commands.IO) error {
return errors.Wrap(err, "parsing gas fee coin")
}
- memPkg := &std.MemPackage{}
+ memPkg := &gnovm.MemPackage{}
if sourcePath == "-" { // stdin
data, err := io.ReadAll(cmdio.In())
if err != nil {
return fmt.Errorf("could not read stdin: %w", err)
}
- memPkg.Files = []*std.MemFile{
+ memPkg.Files = []*gnovm.MemFile{
{
Name: "stdin.gno",
Body: string(data),
@@ -97,7 +98,7 @@ func execMakeRun(cfg *MakeRunCfg, args []string, cmdio commands.IO) error {
if err != nil {
return fmt.Errorf("could not read %q: %w", sourcePath, err)
}
- memPkg.Files = []*std.MemFile{
+ memPkg.Files = []*gnovm.MemFile{
{
Name: info.Name(),
Body: string(b),
diff --git a/gno.land/pkg/sdk/vm/builtins.go b/gno.land/pkg/sdk/vm/builtins.go
index de58cd3e8ae..161e459873d 100644
--- a/gno.land/pkg/sdk/vm/builtins.go
+++ b/gno.land/pkg/sdk/vm/builtins.go
@@ -42,7 +42,7 @@ func (bnk *SDKBanker) TotalCoin(denom string) int64 {
func (bnk *SDKBanker) IssueCoin(b32addr crypto.Bech32Address, denom string, amount int64) {
addr := crypto.MustAddressFromString(string(b32addr))
- _, err := bnk.vmk.bank.AddCoins(bnk.ctx, addr, std.Coins{std.Coin{denom, amount}})
+ _, err := bnk.vmk.bank.AddCoins(bnk.ctx, addr, std.Coins{std.Coin{Denom: denom, Amount: amount}})
if err != nil {
panic(err)
}
@@ -50,8 +50,31 @@ func (bnk *SDKBanker) IssueCoin(b32addr crypto.Bech32Address, denom string, amou
func (bnk *SDKBanker) RemoveCoin(b32addr crypto.Bech32Address, denom string, amount int64) {
addr := crypto.MustAddressFromString(string(b32addr))
- _, err := bnk.vmk.bank.SubtractCoins(bnk.ctx, addr, std.Coins{std.Coin{denom, amount}})
+ _, err := bnk.vmk.bank.SubtractCoins(bnk.ctx, addr, std.Coins{std.Coin{Denom: denom, Amount: amount}})
if err != nil {
panic(err)
}
}
+
+// ----------------------------------------
+// SDKParams
+
+type SDKParams struct {
+ vmk *VMKeeper
+ ctx sdk.Context
+}
+
+func NewSDKParams(vmk *VMKeeper, ctx sdk.Context) *SDKParams {
+ return &SDKParams{
+ vmk: vmk,
+ ctx: ctx,
+ }
+}
+
+func (prm *SDKParams) SetString(key, value string) { prm.vmk.prmk.SetString(prm.ctx, key, value) }
+func (prm *SDKParams) SetBool(key string, value bool) { prm.vmk.prmk.SetBool(prm.ctx, key, value) }
+func (prm *SDKParams) SetInt64(key string, value int64) { prm.vmk.prmk.SetInt64(prm.ctx, key, value) }
+func (prm *SDKParams) SetUint64(key string, value uint64) {
+ prm.vmk.prmk.SetUint64(prm.ctx, key, value)
+}
+func (prm *SDKParams) SetBytes(key string, value []byte) { prm.vmk.prmk.SetBytes(prm.ctx, key, value) }
diff --git a/gno.land/pkg/sdk/vm/common_test.go b/gno.land/pkg/sdk/vm/common_test.go
index 43a8fe1fbec..8b1b7d909c1 100644
--- a/gno.land/pkg/sdk/vm/common_test.go
+++ b/gno.land/pkg/sdk/vm/common_test.go
@@ -11,6 +11,7 @@ import (
"github.com/gnolang/gno/tm2/pkg/sdk"
authm "github.com/gnolang/gno/tm2/pkg/sdk/auth"
bankm "github.com/gnolang/gno/tm2/pkg/sdk/bank"
+ paramsm "github.com/gnolang/gno/tm2/pkg/sdk/params"
"github.com/gnolang/gno/tm2/pkg/std"
"github.com/gnolang/gno/tm2/pkg/store"
"github.com/gnolang/gno/tm2/pkg/store/dbadapter"
@@ -22,6 +23,7 @@ type testEnv struct {
vmk *VMKeeper
bank bankm.BankKeeper
acck authm.AccountKeeper
+ vmh vmHandler
}
func setupTestEnv() testEnv {
@@ -47,7 +49,8 @@ func _setupTestEnv(cacheStdlibs bool) testEnv {
ctx := sdk.NewContext(sdk.RunTxModeDeliver, ms, &bft.Header{ChainID: "test-chain-id"}, log.NewNoopLogger())
acck := authm.NewAccountKeeper(iavlCapKey, std.ProtoBaseAccount)
bank := bankm.NewBankKeeper(acck)
- vmk := NewVMKeeper(baseCapKey, iavlCapKey, acck, bank, 100_000_000)
+ prmk := paramsm.NewParamsKeeper(iavlCapKey, "params")
+ vmk := NewVMKeeper(baseCapKey, iavlCapKey, acck, bank, prmk)
mcw := ms.MultiCacheWrap()
vmk.Initialize(log.NewNoopLogger(), mcw)
@@ -60,6 +63,7 @@ func _setupTestEnv(cacheStdlibs bool) testEnv {
}
vmk.CommitGnoTransactionStore(stdlibCtx)
mcw.MultiWrite()
+ vmh := NewHandler(vmk)
- return testEnv{ctx: ctx, vmk: vmk, bank: bank, acck: acck}
+ return testEnv{ctx: ctx, vmk: vmk, bank: bank, acck: acck, vmh: vmh}
}
diff --git a/gno.land/pkg/sdk/vm/gas_test.go b/gno.land/pkg/sdk/vm/gas_test.go
index 4171b1cdbc3..ff924610627 100644
--- a/gno.land/pkg/sdk/vm/gas_test.go
+++ b/gno.land/pkg/sdk/vm/gas_test.go
@@ -4,6 +4,7 @@ import (
"testing"
"github.com/gnolang/gno/gno.land/pkg/gnoland/ugnot"
+ "github.com/gnolang/gno/gnovm"
bft "github.com/gnolang/gno/tm2/pkg/bft/types"
"github.com/gnolang/gno/tm2/pkg/crypto"
"github.com/gnolang/gno/tm2/pkg/sdk"
@@ -74,7 +75,7 @@ func TestAddPkgDeliverTx(t *testing.T) {
assert.True(t, res.IsOK())
// NOTE: let's try to keep this bellow 100_000 :)
- assert.Equal(t, int64(92825), gasDeliver)
+ assert.Equal(t, int64(93825), gasDeliver)
}
// Enough gas for a failed transaction.
@@ -142,16 +143,15 @@ func setupAddPkg(success bool) (sdk.Context, sdk.Tx, vmHandler) {
ctx := env.ctx
// conduct base gas meter tests from a non-genesis block since genesis block use infinite gas meter instead.
ctx = ctx.WithBlockHeader(&bft.Header{Height: int64(1)})
- vmHandler := NewHandler(env.vmk)
// Create an account with 10M ugnot (10gnot)
addr := crypto.AddressFromPreimage([]byte("test1"))
acc := env.acck.NewAccountWithAddress(ctx, addr)
env.acck.SetAccount(ctx, acc)
env.bank.SetCoins(ctx, addr, std.MustParseCoins(ugnot.ValueString(10000000)))
// success message
- var files []*std.MemFile
+ var files []*gnovm.MemFile
if success {
- files = []*std.MemFile{
+ files = []*gnovm.MemFile{
{
Name: "hello.gno",
Body: `package hello
@@ -163,7 +163,7 @@ func Echo() string {
}
} else {
// failed message
- files = []*std.MemFile{
+ files = []*gnovm.MemFile{
{
Name: "hello.gno",
Body: `package hello
@@ -182,5 +182,5 @@ func Echo() UnknowType {
fee := std.NewFee(500000, std.MustParseCoin(ugnot.ValueString(1)))
tx := std.NewTx(msgs, fee, []std.Signature{}, "")
- return ctx, tx, vmHandler
+ return ctx, tx, env.vmh
}
diff --git a/gno.land/pkg/sdk/vm/handler_test.go b/gno.land/pkg/sdk/vm/handler_test.go
index 38ac8fa61b9..7e029f4cacb 100644
--- a/gno.land/pkg/sdk/vm/handler_test.go
+++ b/gno.land/pkg/sdk/vm/handler_test.go
@@ -1,8 +1,13 @@
package vm
import (
+ "fmt"
"testing"
+ "github.com/gnolang/gno/gnovm"
+ abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types"
+ "github.com/gnolang/gno/tm2/pkg/crypto"
+ "github.com/gnolang/gno/tm2/pkg/std"
"github.com/stretchr/testify/assert"
)
@@ -48,3 +53,272 @@ func Test_parseQueryEval_panic(t *testing.T) {
parseQueryEvalData("gno.land/r/demo/users")
})
}
+
+func TestVmHandlerQuery_Eval(t *testing.T) {
+ tt := []struct {
+ input []byte
+ expectedResult string
+ expectedResultMatch string
+ expectedErrorMatch string
+ expectedPanicMatch string
+ // XXX: expectedEvents
+ }{
+ // valid queries
+ {input: []byte(`gno.land/r/hello.Echo("hello")`), expectedResult: `("echo:hello" string)`},
+ {input: []byte(`gno.land/r/hello.caller()`), expectedResult: `("" std.Address)`}, // FIXME?
+ {input: []byte(`gno.land/r/hello.GetHeight()`), expectedResult: `(0 int64)`},
+ // {input: []byte(`gno.land/r/hello.time.RFC3339`), expectedResult: `test`}, // not working, but should we care?
+ {input: []byte(`gno.land/r/hello.PubString`), expectedResult: `("public string" string)`},
+ {input: []byte(`gno.land/r/hello.ConstString`), expectedResult: `("const string" string)`},
+ {input: []byte(`gno.land/r/hello.pvString`), expectedResult: `("private string" string)`},
+ {input: []byte(`gno.land/r/hello.counter`), expectedResult: `(42 int)`},
+ {input: []byte(`gno.land/r/hello.GetCounter()`), expectedResult: `(42 int)`},
+ {input: []byte(`gno.land/r/hello.Inc()`), expectedResult: `(43 int)`},
+ {input: []byte(`gno.land/r/hello.pvEcho("hello")`), expectedResult: `("pvecho:hello" string)`},
+ {input: []byte(`gno.land/r/hello.1337`), expectedResult: `(1337 int)`},
+ {input: []byte(`gno.land/r/hello.13.37`), expectedResult: `(13.37 float64)`},
+ {input: []byte(`gno.land/r/hello.float64(1337)`), expectedResult: `(1337 float64)`},
+ {input: []byte(`gno.land/r/hello.myStructInst`), expectedResult: `(struct{(1000 int)} gno.land/r/hello.myStruct)`},
+ {input: []byte(`gno.land/r/hello.myStructInst.Foo()`), expectedResult: `("myStruct.Foo" string)`},
+ {input: []byte(`gno.land/r/hello.myStruct`), expectedResultMatch: `\(typeval{gno.land/r/hello.myStruct \(0x.*\)} type{}\)`},
+ {input: []byte(`gno.land/r/hello.Inc`), expectedResult: `(Inc func()( int))`},
+ {input: []byte(`gno.land/r/hello.fn()("hi")`), expectedResult: `("echo:hi" string)`},
+ {input: []byte(`gno.land/r/hello.sl`), expectedResultMatch: `(slice[ref(.*)] []int)`}, // XXX: should return the actual value
+ {input: []byte(`gno.land/r/hello.sl[1]`), expectedResultMatch: `(slice[ref(.*)] []int)`}, // XXX: should return the actual value
+ {input: []byte(`gno.land/r/hello.println(1234)`), expectedResultMatch: `^$`}, // XXX: compare stdout?
+
+ // panics
+ {input: []byte(`gno.land/r/hello`), expectedPanicMatch: `expected . syntax in query input data`},
+
+ // errors
+ {input: []byte(`gno.land/r/hello.doesnotexist`), expectedErrorMatch: `^/:0:0: name doesnotexist not declared:`}, // multiline error
+ {input: []byte(`gno.land/r/doesnotexist.Foo`), expectedErrorMatch: `^invalid package path$`},
+ {input: []byte(`gno.land/r/hello.Panic()`), expectedErrorMatch: `^foo$`},
+ {input: []byte(`gno.land/r/hello.sl[6]`), expectedErrorMatch: `^slice index out of bounds: 6 \(len=5\)$`},
+ }
+
+ for _, tc := range tt {
+ name := string(tc.input)
+ t.Run(name, func(t *testing.T) {
+ env := setupTestEnv()
+ ctx := env.vmk.MakeGnoTransactionStore(env.ctx)
+ vmHandler := env.vmh
+
+ // Give "addr1" some gnots.
+ addr := crypto.AddressFromPreimage([]byte("addr1"))
+ acc := env.acck.NewAccountWithAddress(ctx, addr)
+ env.acck.SetAccount(ctx, acc)
+ env.bank.SetCoins(ctx, addr, std.MustParseCoins("10000000ugnot"))
+ assert.True(t, env.bank.GetCoins(ctx, addr).IsEqual(std.MustParseCoins("10000000ugnot")))
+
+ // Create test package.
+ files := []*gnovm.MemFile{
+ {"hello.gno", `
+package hello
+
+import "std"
+import "time"
+
+var _ = time.RFC3339
+func caller() std.Address { return std.GetOrigCaller() }
+var GetHeight = std.GetHeight
+var sl = []int{1,2,3,4,5}
+func fn() func(string) string { return Echo }
+type myStruct struct{a int}
+var myStructInst = myStruct{a: 1000}
+func (ms myStruct) Foo() string { return "myStruct.Foo" }
+func Panic() { panic("foo") }
+var counter int = 42
+var pvString = "private string"
+var PubString = "public string"
+const ConstString = "const string"
+func Echo(msg string) string { return "echo:"+msg }
+func GetCounter() int { return counter }
+func Inc() int { counter += 1; return counter }
+func pvEcho(msg string) string { return "pvecho:"+msg }
+`},
+ }
+ pkgPath := "gno.land/r/hello"
+ msg1 := NewMsgAddPackage(addr, pkgPath, files)
+ err := env.vmk.AddPackage(ctx, msg1)
+ assert.NoError(t, err)
+ env.vmk.CommitGnoTransactionStore(ctx)
+
+ req := abci.RequestQuery{
+ Path: "vm/qeval",
+ Data: tc.input,
+ }
+
+ defer func() {
+ if r := recover(); r != nil {
+ output := fmt.Sprintf("%v", r)
+ assert.Regexp(t, tc.expectedPanicMatch, output)
+ } else {
+ assert.Equal(t, tc.expectedPanicMatch, "", "should not panic")
+ }
+ }()
+ res := vmHandler.Query(env.ctx, req)
+ if tc.expectedPanicMatch == "" {
+ if tc.expectedErrorMatch == "" {
+ assert.True(t, res.IsOK(), "should not have error")
+ if tc.expectedResult != "" {
+ assert.Equal(t, string(res.Data), tc.expectedResult)
+ }
+ if tc.expectedResultMatch != "" {
+ assert.Regexp(t, tc.expectedResultMatch, string(res.Data))
+ }
+ } else {
+ assert.False(t, res.IsOK(), "should have an error")
+ errmsg := res.Error.Error()
+ assert.Regexp(t, tc.expectedErrorMatch, errmsg)
+ }
+ }
+ })
+ }
+}
+
+func TestVmHandlerQuery_Funcs(t *testing.T) {
+ tt := []struct {
+ input []byte
+ expectedResult string
+ expectedErrorMatch string
+ }{
+ // valid queries
+ {input: []byte(`gno.land/r/hello`), expectedResult: `[{"FuncName":"Panic","Params":null,"Results":null},{"FuncName":"Echo","Params":[{"Name":"msg","Type":"string","Value":""}],"Results":[{"Name":"_","Type":"string","Value":""}]},{"FuncName":"GetCounter","Params":null,"Results":[{"Name":"_","Type":"int","Value":""}]},{"FuncName":"Inc","Params":null,"Results":[{"Name":"_","Type":"int","Value":""}]}]`},
+ {input: []byte(`gno.land/r/doesnotexist`), expectedErrorMatch: `invalid package path`},
+ {input: []byte(`std`), expectedErrorMatch: `invalid package path`},
+ {input: []byte(`strings`), expectedErrorMatch: `invalid package path`},
+ }
+
+ for _, tc := range tt {
+ name := string(tc.input)
+ t.Run(name, func(t *testing.T) {
+ env := setupTestEnv()
+ ctx := env.vmk.MakeGnoTransactionStore(env.ctx)
+ vmHandler := env.vmh
+
+ // Give "addr1" some gnots.
+ addr := crypto.AddressFromPreimage([]byte("addr1"))
+ acc := env.acck.NewAccountWithAddress(ctx, addr)
+ env.acck.SetAccount(ctx, acc)
+ env.bank.SetCoins(ctx, addr, std.MustParseCoins("10000000ugnot"))
+ assert.True(t, env.bank.GetCoins(ctx, addr).IsEqual(std.MustParseCoins("10000000ugnot")))
+
+ // Create test package.
+ files := []*gnovm.MemFile{
+ {"hello.gno", `
+package hello
+
+var sl = []int{1,2,3,4,5}
+func fn() func(string) string { return Echo }
+type myStruct struct{a int}
+var myStructInst = myStruct{a: 1000}
+func (ms myStruct) Foo() string { return "myStruct.Foo" }
+func Panic() { panic("foo") }
+var counter int = 42
+var pvString = "private string"
+var PubString = "public string"
+const ConstString = "const string"
+func Echo(msg string) string { return "echo:"+msg }
+func GetCounter() int { return counter }
+func Inc() int { counter += 1; return counter }
+func pvEcho(msg string) string { return "pvecho:"+msg }
+`},
+ }
+ pkgPath := "gno.land/r/hello"
+ msg1 := NewMsgAddPackage(addr, pkgPath, files)
+ err := env.vmk.AddPackage(ctx, msg1)
+ assert.NoError(t, err)
+
+ req := abci.RequestQuery{
+ Path: "vm/qfuncs",
+ Data: tc.input,
+ }
+
+ res := vmHandler.Query(env.ctx, req)
+ if tc.expectedErrorMatch == "" {
+ assert.True(t, res.IsOK(), "should not have error")
+ if tc.expectedResult != "" {
+ assert.Equal(t, string(res.Data), tc.expectedResult)
+ }
+ } else {
+ assert.False(t, res.IsOK(), "should have an error")
+ errmsg := res.Error.Error()
+ assert.Regexp(t, tc.expectedErrorMatch, errmsg)
+ }
+ })
+ }
+}
+
+func TestVmHandlerQuery_File(t *testing.T) {
+ tt := []struct {
+ input []byte
+ expectedResult string
+ expectedResultMatch string
+ expectedErrorMatch string
+ expectedPanicMatch string
+ // XXX: expectedEvents
+ }{
+ // valid queries
+ {input: []byte(`gno.land/r/hello/hello.gno`), expectedResult: "package hello\n\nfunc Hello() string { return \"hello\" }\n"},
+ {input: []byte(`gno.land/r/hello/README.md`), expectedResult: "# Hello"},
+ {input: []byte(`gno.land/r/hello/doesnotexist.gno`), expectedErrorMatch: `file "gno.land/r/hello/doesnotexist.gno" is not available`},
+ {input: []byte(`gno.land/r/hello`), expectedResult: "README.md\nhello.gno"},
+ {input: []byte(`gno.land/r/doesnotexist`), expectedErrorMatch: `package "gno.land/r/doesnotexist" is not available`},
+ {input: []byte(`gno.land/r/doesnotexist/hello.gno`), expectedErrorMatch: `file "gno.land/r/doesnotexist/hello.gno" is not available`},
+ }
+
+ for _, tc := range tt {
+ name := string(tc.input)
+ t.Run(name, func(t *testing.T) {
+ env := setupTestEnv()
+ ctx := env.vmk.MakeGnoTransactionStore(env.ctx)
+ vmHandler := env.vmh
+
+ // Give "addr1" some gnots.
+ addr := crypto.AddressFromPreimage([]byte("addr1"))
+ acc := env.acck.NewAccountWithAddress(ctx, addr)
+ env.acck.SetAccount(ctx, acc)
+ env.bank.SetCoins(ctx, addr, std.MustParseCoins("10000000ugnot"))
+ assert.True(t, env.bank.GetCoins(ctx, addr).IsEqual(std.MustParseCoins("10000000ugnot")))
+
+ // Create test package.
+ files := []*gnovm.MemFile{
+ {"README.md", "# Hello"},
+ {"hello.gno", "package hello\n\nfunc Hello() string { return \"hello\" }\n"},
+ }
+ pkgPath := "gno.land/r/hello"
+ msg1 := NewMsgAddPackage(addr, pkgPath, files)
+ err := env.vmk.AddPackage(ctx, msg1)
+ assert.NoError(t, err)
+
+ req := abci.RequestQuery{
+ Path: "vm/qfile",
+ Data: tc.input,
+ }
+
+ defer func() {
+ if r := recover(); r != nil {
+ output := fmt.Sprintf("%v", r)
+ assert.Regexp(t, tc.expectedPanicMatch, output)
+ } else {
+ assert.Equal(t, "", tc.expectedPanicMatch, "should not panic")
+ }
+ }()
+ res := vmHandler.Query(env.ctx, req)
+ if tc.expectedErrorMatch == "" {
+ assert.True(t, res.IsOK(), "should not have error")
+ if tc.expectedResult != "" {
+ assert.Equal(t, string(res.Data), tc.expectedResult)
+ }
+ if tc.expectedResultMatch != "" {
+ assert.Regexp(t, tc.expectedResultMatch, string(res.Data))
+ }
+ } else {
+ assert.False(t, res.IsOK(), "should have an error")
+ errmsg := res.Error.Error()
+ assert.Regexp(t, tc.expectedErrorMatch, errmsg)
+ }
+ })
+ }
+}
diff --git a/gno.land/pkg/sdk/vm/keeper.go b/gno.land/pkg/sdk/vm/keeper.go
index 365473b3e7a..7f5216a4f03 100644
--- a/gno.land/pkg/sdk/vm/keeper.go
+++ b/gno.land/pkg/sdk/vm/keeper.go
@@ -14,6 +14,7 @@ import (
"sync"
"time"
+ "github.com/gnolang/gno/gnovm"
gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
"github.com/gnolang/gno/gnovm/stdlibs"
"github.com/gnolang/gno/tm2/pkg/crypto"
@@ -23,6 +24,7 @@ import (
"github.com/gnolang/gno/tm2/pkg/sdk"
"github.com/gnolang/gno/tm2/pkg/sdk/auth"
"github.com/gnolang/gno/tm2/pkg/sdk/bank"
+ "github.com/gnolang/gno/tm2/pkg/sdk/params"
"github.com/gnolang/gno/tm2/pkg/std"
"github.com/gnolang/gno/tm2/pkg/store"
"github.com/gnolang/gno/tm2/pkg/store/dbadapter"
@@ -59,11 +61,10 @@ type VMKeeper struct {
iavlKey store.StoreKey
acck auth.AccountKeeper
bank bank.BankKeeper
+ prmk params.ParamsKeeper
// cached, the DeliverTx persistent state.
gnoStore gno.Store
-
- maxCycles int64 // max allowed cylces on VM executions
}
// NewVMKeeper returns a new VMKeeper.
@@ -72,15 +73,14 @@ func NewVMKeeper(
iavlKey store.StoreKey,
acck auth.AccountKeeper,
bank bank.BankKeeper,
- maxCycles int64,
+ prmk params.ParamsKeeper,
) *VMKeeper {
- // TODO: create an Options struct to avoid too many constructor parameters
vmk := &VMKeeper{
- baseKey: baseKey,
- iavlKey: iavlKey,
- acck: acck,
- bank: bank,
- maxCycles: maxCycles,
+ baseKey: baseKey,
+ iavlKey: iavlKey,
+ acck: acck,
+ bank: bank,
+ prmk: prmk,
}
return vmk
}
@@ -226,9 +226,15 @@ func (vm *VMKeeper) getGnoTransactionStore(ctx sdk.Context) gno.TransactionStore
// Namespace can be either a user or crypto address.
var reNamespace = regexp.MustCompile(`^gno.land/(?:r|p)/([\.~_a-zA-Z0-9]+)`)
+const (
+ sysUsersPkgParamKey = "vm/gno.land/r/sys/params.string"
+ sysUsersPkgDefault = "gno.land/r/sys/users"
+)
+
// checkNamespacePermission check if the user as given has correct permssion to on the given pkg path
func (vm *VMKeeper) checkNamespacePermission(ctx sdk.Context, creator crypto.Address, pkgPath string) error {
- const sysUsersPkg = "gno.land/r/sys/users"
+ sysUsersPkg := sysUsersPkgDefault
+ vm.prmk.GetString(ctx, sysUsersPkgParamKey, &sysUsersPkg)
store := vm.getGnoTransactionStore(ctx)
@@ -262,18 +268,18 @@ func (vm *VMKeeper) checkNamespacePermission(ctx sdk.Context, creator crypto.Add
OrigPkgAddr: pkgAddr.Bech32(),
// XXX: should we remove the banker ?
Banker: NewSDKBanker(vm, ctx),
+ Params: NewSDKParams(vm, ctx),
EventLogger: ctx.EventLogger(),
}
m := gno.NewMachineWithOptions(
gno.MachineOptions{
- PkgPath: "",
- Output: os.Stdout, // XXX
- Store: store,
- Context: msgCtx,
- Alloc: store.GetAllocator(),
- MaxCycles: vm.maxCycles,
- GasMeter: ctx.GasMeter(),
+ PkgPath: "",
+ Output: os.Stdout, // XXX
+ Store: store,
+ Context: msgCtx,
+ Alloc: store.GetAllocator(),
+ GasMeter: ctx.GasMeter(),
})
defer m.Release()
@@ -363,18 +369,18 @@ func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) (err error) {
OrigSendSpent: new(std.Coins),
OrigPkgAddr: pkgAddr.Bech32(),
Banker: NewSDKBanker(vm, ctx),
+ Params: NewSDKParams(vm, ctx),
EventLogger: ctx.EventLogger(),
}
// Parse and run the files, construct *PV.
m2 := gno.NewMachineWithOptions(
gno.MachineOptions{
- PkgPath: "",
- Output: os.Stdout, // XXX
- Store: gnostore,
- Alloc: gnostore.GetAllocator(),
- Context: msgCtx,
- MaxCycles: vm.maxCycles,
- GasMeter: ctx.GasMeter(),
+ PkgPath: "",
+ Output: os.Stdout, // XXX
+ Store: gnostore,
+ Alloc: gnostore.GetAllocator(),
+ Context: msgCtx,
+ GasMeter: ctx.GasMeter(),
})
defer m2.Release()
defer func() {
@@ -464,18 +470,18 @@ func (vm *VMKeeper) Call(ctx sdk.Context, msg MsgCall) (res string, err error) {
OrigSendSpent: new(std.Coins),
OrigPkgAddr: pkgAddr.Bech32(),
Banker: NewSDKBanker(vm, ctx),
+ Params: NewSDKParams(vm, ctx),
EventLogger: ctx.EventLogger(),
}
// Construct machine and evaluate.
m := gno.NewMachineWithOptions(
gno.MachineOptions{
- PkgPath: "",
- Output: os.Stdout, // XXX
- Store: gnostore,
- Context: msgCtx,
- Alloc: gnostore.GetAllocator(),
- MaxCycles: vm.maxCycles,
- GasMeter: ctx.GasMeter(),
+ PkgPath: "",
+ Output: os.Stdout, // XXX
+ Store: gnostore,
+ Context: msgCtx,
+ Alloc: gnostore.GetAllocator(),
+ GasMeter: ctx.GasMeter(),
})
defer m.Release()
m.SetActivePackage(mpv)
@@ -563,19 +569,19 @@ func (vm *VMKeeper) Run(ctx sdk.Context, msg MsgRun) (res string, err error) {
OrigSendSpent: new(std.Coins),
OrigPkgAddr: pkgAddr.Bech32(),
Banker: NewSDKBanker(vm, ctx),
+ Params: NewSDKParams(vm, ctx),
EventLogger: ctx.EventLogger(),
}
// Parse and run the files, construct *PV.
buf := new(bytes.Buffer)
m := gno.NewMachineWithOptions(
gno.MachineOptions{
- PkgPath: "",
- Output: buf,
- Store: gnostore,
- Alloc: gnostore.GetAllocator(),
- Context: msgCtx,
- MaxCycles: vm.maxCycles,
- GasMeter: ctx.GasMeter(),
+ PkgPath: "",
+ Output: buf,
+ Store: gnostore,
+ Alloc: gnostore.GetAllocator(),
+ Context: msgCtx,
+ GasMeter: ctx.GasMeter(),
})
// XXX MsgRun does not have pkgPath. How do we find it on chain?
defer m.Release()
@@ -596,13 +602,12 @@ func (vm *VMKeeper) Run(ctx sdk.Context, msg MsgRun) (res string, err error) {
m2 := gno.NewMachineWithOptions(
gno.MachineOptions{
- PkgPath: "",
- Output: buf,
- Store: gnostore,
- Alloc: gnostore.GetAllocator(),
- Context: msgCtx,
- MaxCycles: vm.maxCycles,
- GasMeter: ctx.GasMeter(),
+ PkgPath: "",
+ Output: buf,
+ Store: gnostore,
+ Alloc: gnostore.GetAllocator(),
+ Context: msgCtx,
+ GasMeter: ctx.GasMeter(),
})
defer m2.Release()
m2.SetActivePackage(pv)
@@ -724,17 +729,17 @@ func (vm *VMKeeper) QueryEval(ctx sdk.Context, pkgPath string, expr string) (res
// OrigSendSpent: nil,
OrigPkgAddr: pkgAddr.Bech32(),
Banker: NewSDKBanker(vm, ctx), // safe as long as ctx is a fork to be discarded.
+ Params: NewSDKParams(vm, ctx),
EventLogger: ctx.EventLogger(),
}
m := gno.NewMachineWithOptions(
gno.MachineOptions{
- PkgPath: pkgPath,
- Output: os.Stdout, // XXX
- Store: gnostore,
- Context: msgCtx,
- Alloc: alloc,
- MaxCycles: vm.maxCycles,
- GasMeter: ctx.GasMeter(),
+ PkgPath: pkgPath,
+ Output: os.Stdout, // XXX
+ Store: gnostore,
+ Context: msgCtx,
+ Alloc: alloc,
+ GasMeter: ctx.GasMeter(),
})
defer m.Release()
defer func() {
@@ -791,17 +796,17 @@ func (vm *VMKeeper) QueryEvalString(ctx sdk.Context, pkgPath string, expr string
// OrigSendSpent: nil,
OrigPkgAddr: pkgAddr.Bech32(),
Banker: NewSDKBanker(vm, ctx), // safe as long as ctx is a fork to be discarded.
+ Params: NewSDKParams(vm, ctx),
EventLogger: ctx.EventLogger(),
}
m := gno.NewMachineWithOptions(
gno.MachineOptions{
- PkgPath: pkgPath,
- Output: os.Stdout, // XXX
- Store: gnostore,
- Context: msgCtx,
- Alloc: alloc,
- MaxCycles: vm.maxCycles,
- GasMeter: ctx.GasMeter(),
+ PkgPath: pkgPath,
+ Output: os.Stdout, // XXX
+ Store: gnostore,
+ Context: msgCtx,
+ Alloc: alloc,
+ GasMeter: ctx.GasMeter(),
})
defer m.Release()
defer func() {
@@ -828,7 +833,7 @@ func (vm *VMKeeper) QueryEvalString(ctx sdk.Context, pkgPath string, expr string
func (vm *VMKeeper) QueryFile(ctx sdk.Context, filepath string) (res string, err error) {
store := vm.newGnoTransactionStore(ctx) // throwaway (never committed)
- dirpath, filename := std.SplitFilepath(filepath)
+ dirpath, filename := gnovm.SplitFilepath(filepath)
if filename != "" {
memFile := store.GetMemFile(dirpath, filename)
if memFile == nil {
diff --git a/gno.land/pkg/sdk/vm/keeper_test.go b/gno.land/pkg/sdk/vm/keeper_test.go
index 9257da2ddaf..3aba53d4490 100644
--- a/gno.land/pkg/sdk/vm/keeper_test.go
+++ b/gno.land/pkg/sdk/vm/keeper_test.go
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/gnolang/gno/gno.land/pkg/gnoland/ugnot"
+ "github.com/gnolang/gno/gnovm"
"github.com/gnolang/gno/gnovm/pkg/gnolang"
"github.com/gnolang/gno/tm2/pkg/crypto"
"github.com/gnolang/gno/tm2/pkg/db/memdb"
@@ -35,7 +36,7 @@ func TestVMKeeperAddPackage(t *testing.T) {
assert.True(t, env.bank.GetCoins(ctx, addr).IsEqual(std.MustParseCoins(coinsString)))
// Create test package.
- files := []*std.MemFile{
+ files := []*gnovm.MemFile{
{
Name: "test.gno",
Body: `package test
@@ -80,8 +81,8 @@ func TestVMKeeperOrigSend1(t *testing.T) {
assert.True(t, env.bank.GetCoins(ctx, addr).IsEqual(std.MustParseCoins(coinsString)))
// Create test package.
- files := []*std.MemFile{
- {"init.gno", `
+ files := []*gnovm.MemFile{
+ {Name: "init.gno", Body: `
package test
import "std"
@@ -125,8 +126,8 @@ func TestVMKeeperOrigSend2(t *testing.T) {
assert.True(t, env.bank.GetCoins(ctx, addr).IsEqual(std.MustParseCoins(coinsString)))
// Create test package.
- files := []*std.MemFile{
- {"init.gno", `
+ files := []*gnovm.MemFile{
+ {Name: "init.gno", Body: `
package test
import "std"
@@ -179,8 +180,8 @@ func TestVMKeeperOrigSend3(t *testing.T) {
assert.True(t, env.bank.GetCoins(ctx, addr).IsEqual(std.MustParseCoins(coinsString)))
// Create test package.
- files := []*std.MemFile{
- {"init.gno", `
+ files := []*gnovm.MemFile{
+ {Name: "init.gno", Body: `
package test
import "std"
@@ -223,8 +224,8 @@ func TestVMKeeperRealmSend1(t *testing.T) {
assert.True(t, env.bank.GetCoins(ctx, addr).IsEqual(std.MustParseCoins(coinsString)))
// Create test package.
- files := []*std.MemFile{
- {"init.gno", `
+ files := []*gnovm.MemFile{
+ {Name: "init.gno", Body: `
package test
import "std"
@@ -267,8 +268,8 @@ func TestVMKeeperRealmSend2(t *testing.T) {
assert.True(t, env.bank.GetCoins(ctx, addr).IsEqual(std.MustParseCoins(coinsString)))
// Create test package.
- files := []*std.MemFile{
- {"init.gno", `
+ files := []*gnovm.MemFile{
+ {Name: "init.gno", Body: `
package test
import "std"
@@ -298,6 +299,60 @@ func Echo(msg string) string {
assert.Error(t, err)
}
+// Using x/params from a realm.
+func TestVMKeeperParams(t *testing.T) {
+ env := setupTestEnv()
+ ctx := env.vmk.MakeGnoTransactionStore(env.ctx)
+
+ // Give "addr1" some gnots.
+ addr := crypto.AddressFromPreimage([]byte("addr1"))
+ acc := env.acck.NewAccountWithAddress(ctx, addr)
+ env.acck.SetAccount(ctx, acc)
+ env.bank.SetCoins(ctx, addr, std.MustParseCoins(coinsString))
+ // env.prmk.
+ assert.True(t, env.bank.GetCoins(ctx, addr).IsEqual(std.MustParseCoins(coinsString)))
+
+ // Create test package.
+ files := []*gnovm.MemFile{
+ {"init.gno", `
+package test
+
+import "std"
+
+func init() {
+ std.SetParamString("foo.string", "foo1")
+}
+
+func Do() string {
+ std.SetParamInt64("bar.int64", int64(1337))
+ std.SetParamString("foo.string", "foo2") // override init
+
+ return "XXX" // return std.GetConfig("gno.land/r/test.foo"), if we want to expose std.GetConfig, maybe as a std.TestGetConfig
+}`},
+ }
+ pkgPath := "gno.land/r/test"
+ msg1 := NewMsgAddPackage(addr, pkgPath, files)
+ err := env.vmk.AddPackage(ctx, msg1)
+ assert.NoError(t, err)
+
+ // Run Echo function.
+ coins := std.MustParseCoins(ugnot.ValueString(9_000_000))
+ msg2 := NewMsgCall(addr, coins, pkgPath, "Do", []string{})
+
+ res, err := env.vmk.Call(ctx, msg2)
+ assert.NoError(t, err)
+ _ = res
+ expected := fmt.Sprintf("(\"%s\" string)\n\n", "XXX") // XXX: return something more useful
+ assert.Equal(t, expected, res)
+
+ var foo string
+ var bar int64
+ env.vmk.prmk.GetString(ctx, "gno.land/r/test.foo.string", &foo)
+ env.vmk.prmk.GetInt64(ctx, "gno.land/r/test.bar.int64", &bar)
+ assert.Equal(t, "foo2", foo)
+ assert.Equal(t, int64(1337), bar)
+}
+
// Assign admin as OrigCaller on deploying the package.
func TestVMKeeperOrigCallerInit(t *testing.T) {
env := setupTestEnv()
@@ -311,8 +366,8 @@ func TestVMKeeperOrigCallerInit(t *testing.T) {
assert.True(t, env.bank.GetCoins(ctx, addr).IsEqual(std.MustParseCoins(coinsString)))
// Create test package.
- files := []*std.MemFile{
- {"init.gno", `
+ files := []*gnovm.MemFile{
+ {Name: "init.gno", Body: `
package test
import "std"
@@ -320,7 +375,7 @@ import "std"
var admin std.Address
func init() {
- admin = std.GetOrigCaller()
+ admin = std.GetOrigCaller()
}
func Echo(msg string) string {
@@ -362,8 +417,8 @@ func TestVMKeeperRunSimple(t *testing.T) {
acc := env.acck.NewAccountWithAddress(ctx, addr)
env.acck.SetAccount(ctx, acc)
- files := []*std.MemFile{
- {"script.gno", `
+ files := []*gnovm.MemFile{
+ {Name: "script.gno", Body: `
package main
func main() {
@@ -401,8 +456,8 @@ func testVMKeeperRunImportStdlibs(t *testing.T, env testEnv) {
acc := env.acck.NewAccountWithAddress(ctx, addr)
env.acck.SetAccount(ctx, acc)
- files := []*std.MemFile{
- {"script.gno", `
+ files := []*gnovm.MemFile{
+ {Name: "script.gno", Body: `
package main
import "std"
@@ -434,7 +489,7 @@ func TestNumberOfArgsError(t *testing.T) {
assert.True(t, env.bank.GetCoins(ctx, addr).IsEqual(std.MustParseCoins(coinsString)))
// Create test package.
- files := []*std.MemFile{
+ files := []*gnovm.MemFile{
{
Name: "test.gno",
Body: `package test
@@ -473,8 +528,8 @@ func TestVMKeeperReinitialize(t *testing.T) {
assert.True(t, env.bank.GetCoins(ctx, addr).IsEqual(std.MustParseCoins(coinsString)))
// Create test package.
- files := []*std.MemFile{
- {"init.gno", `
+ files := []*gnovm.MemFile{
+ {Name: "init.gno", Body: `
package test
func Echo(msg string) string {
diff --git a/gno.land/pkg/sdk/vm/msg_test.go b/gno.land/pkg/sdk/vm/msg_test.go
index eaaaa0f0ab2..684dc21e9f2 100644
--- a/gno.land/pkg/sdk/vm/msg_test.go
+++ b/gno.land/pkg/sdk/vm/msg_test.go
@@ -3,6 +3,7 @@ package vm
import (
"testing"
+ "github.com/gnolang/gno/gnovm"
"github.com/gnolang/gno/tm2/pkg/crypto"
"github.com/gnolang/gno/tm2/pkg/std"
"github.com/stretchr/testify/assert"
@@ -14,7 +15,7 @@ func TestMsgAddPackage_ValidateBasic(t *testing.T) {
creator := crypto.AddressFromPreimage([]byte("addr1"))
pkgName := "test"
pkgPath := "gno.land/r/namespace/test"
- files := []*std.MemFile{
+ files := []*gnovm.MemFile{
{
Name: "test.gno",
Body: `package test
@@ -40,7 +41,7 @@ func TestMsgAddPackage_ValidateBasic(t *testing.T) {
name: "missing creator address",
msg: MsgAddPackage{
Creator: crypto.Address{},
- Package: &std.MemPackage{
+ Package: &gnovm.MemPackage{
Name: pkgName,
Path: pkgPath,
Files: files,
@@ -56,7 +57,7 @@ func TestMsgAddPackage_ValidateBasic(t *testing.T) {
name: "missing package path",
msg: MsgAddPackage{
Creator: creator,
- Package: &std.MemPackage{
+ Package: &gnovm.MemPackage{
Name: pkgName,
Path: "",
Files: files,
@@ -72,7 +73,7 @@ func TestMsgAddPackage_ValidateBasic(t *testing.T) {
name: "invalid deposit coins",
msg: MsgAddPackage{
Creator: creator,
- Package: &std.MemPackage{
+ Package: &gnovm.MemPackage{
Name: pkgName,
Path: pkgPath,
Files: files,
@@ -199,7 +200,7 @@ func TestMsgRun_ValidateBasic(t *testing.T) {
caller := crypto.AddressFromPreimage([]byte("addr1"))
pkgName := "main"
pkgPath := "gno.land/r/" + caller.String() + "/run"
- pkgFiles := []*std.MemFile{
+ pkgFiles := []*gnovm.MemFile{
{
Name: "main.gno",
Body: `package main
@@ -226,7 +227,7 @@ func TestMsgRun_ValidateBasic(t *testing.T) {
name: "invalid caller address",
msg: MsgRun{
Caller: crypto.Address{},
- Package: &std.MemPackage{
+ Package: &gnovm.MemPackage{
Name: pkgName,
Path: pkgPath,
Files: pkgFiles,
@@ -242,7 +243,7 @@ func TestMsgRun_ValidateBasic(t *testing.T) {
name: "invalid package path",
msg: MsgRun{
Caller: caller,
- Package: &std.MemPackage{
+ Package: &gnovm.MemPackage{
Name: pkgName,
Path: "gno.land/r/namespace/test", // this is not a valid run path
Files: pkgFiles,
diff --git a/gno.land/pkg/sdk/vm/msgs.go b/gno.land/pkg/sdk/vm/msgs.go
index d650c23f382..d5b82067a98 100644
--- a/gno.land/pkg/sdk/vm/msgs.go
+++ b/gno.land/pkg/sdk/vm/msgs.go
@@ -4,6 +4,7 @@ import (
"fmt"
"strings"
+ "github.com/gnolang/gno/gnovm"
gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
"github.com/gnolang/gno/tm2/pkg/amino"
"github.com/gnolang/gno/tm2/pkg/crypto"
@@ -16,15 +17,15 @@ import (
// MsgAddPackage - create and initialize new package
type MsgAddPackage struct {
- Creator crypto.Address `json:"creator" yaml:"creator"`
- Package *std.MemPackage `json:"package" yaml:"package"`
- Deposit std.Coins `json:"deposit" yaml:"deposit"`
+ Creator crypto.Address `json:"creator" yaml:"creator"`
+ Package *gnovm.MemPackage `json:"package" yaml:"package"`
+ Deposit std.Coins `json:"deposit" yaml:"deposit"`
}
var _ std.Msg = MsgAddPackage{}
// NewMsgAddPackage - upload a package with files.
-func NewMsgAddPackage(creator crypto.Address, pkgPath string, files []*std.MemFile) MsgAddPackage {
+func NewMsgAddPackage(creator crypto.Address, pkgPath string, files []*gnovm.MemFile) MsgAddPackage {
var pkgName string
for _, file := range files {
if strings.HasSuffix(file.Name, ".gno") {
@@ -34,7 +35,7 @@ func NewMsgAddPackage(creator crypto.Address, pkgPath string, files []*std.MemFi
}
return MsgAddPackage{
Creator: creator,
- Package: &std.MemPackage{
+ Package: &gnovm.MemPackage{
Name: pkgName,
Path: pkgPath,
Files: files,
@@ -145,14 +146,14 @@ func (msg MsgCall) GetReceived() std.Coins {
// MsgRun - executes arbitrary Gno code.
type MsgRun struct {
- Caller crypto.Address `json:"caller" yaml:"caller"`
- Send std.Coins `json:"send" yaml:"send"`
- Package *std.MemPackage `json:"package" yaml:"package"`
+ Caller crypto.Address `json:"caller" yaml:"caller"`
+ Send std.Coins `json:"send" yaml:"send"`
+ Package *gnovm.MemPackage `json:"package" yaml:"package"`
}
var _ std.Msg = MsgRun{}
-func NewMsgRun(caller crypto.Address, send std.Coins, files []*std.MemFile) MsgRun {
+func NewMsgRun(caller crypto.Address, send std.Coins, files []*gnovm.MemFile) MsgRun {
for _, file := range files {
if strings.HasSuffix(file.Name, ".gno") {
pkgName := string(gno.PackageNameFromFileBody(file.Name, file.Body))
@@ -164,7 +165,7 @@ func NewMsgRun(caller crypto.Address, send std.Coins, files []*std.MemFile) MsgR
return MsgRun{
Caller: caller,
Send: send,
- Package: &std.MemPackage{
+ Package: &gnovm.MemPackage{
Name: "main",
Path: "", // auto set by the handler
Files: files,
diff --git a/gno.land/pkg/sdk/vm/package.go b/gno.land/pkg/sdk/vm/package.go
index 30dd116d4e3..0359061ccea 100644
--- a/gno.land/pkg/sdk/vm/package.go
+++ b/gno.land/pkg/sdk/vm/package.go
@@ -1,6 +1,7 @@
package vm
import (
+ "github.com/gnolang/gno/gnovm"
"github.com/gnolang/gno/tm2/pkg/amino"
"github.com/gnolang/gno/tm2/pkg/std"
)
@@ -11,6 +12,7 @@ var Package = amino.RegisterPackage(amino.NewPackage(
amino.GetCallersDirname(),
).WithDependencies(
std.Package,
+ gnovm.Package,
).WithTypes(
MsgCall{}, "m_call",
MsgRun{}, "m_run",
diff --git a/gno.land/pkg/sdk/vm/vm.proto b/gno.land/pkg/sdk/vm/vm.proto
index e02226e1ae4..efaf025e431 100644
--- a/gno.land/pkg/sdk/vm/vm.proto
+++ b/gno.land/pkg/sdk/vm/vm.proto
@@ -5,6 +5,7 @@ option go_package = "github.com/gnolang/gno/gno.land/pkg/sdk/vm/pb";
// imports
import "github.com/gnolang/gno/tm2/pkg/std/std.proto";
+import "github.com/gnolang/gno/gnovm/gnovm.proto";
// messages
message m_call {
@@ -18,12 +19,12 @@ message m_call {
message m_run {
string caller = 1;
string send = 2;
- std.MemPackage package = 3;
+ gnovm.MemPackage package = 3;
}
message m_addpkg {
string creator = 1;
- std.MemPackage package = 2;
+ gnovm.MemPackage package = 2;
string deposit = 3;
}
@@ -40,5 +41,8 @@ message InvalidExprError {
}
message TypeCheckError {
- repeated string errors = 1 [json_name = "Errors"];
+ repeated string errors = 1;
+}
+
+message UnauthorizedUserError {
}
\ No newline at end of file
diff --git a/gnovm/Makefile b/gnovm/Makefile
index 5ff3af9c253..d27395d9cd1 100644
--- a/gnovm/Makefile
+++ b/gnovm/Makefile
@@ -54,7 +54,7 @@ lint:
.PHONY: fmt
fmt:
- go run ./cmd/gno fmt $(GNOFMT_FLAGS) ./stdlibs/...
+ go run ./cmd/gno fmt $(GNOFMT_FLAGS) ./stdlibs/... ./tests/stdlibs/...
$(rundep) mvdan.cc/gofumpt $(GOFMT_FLAGS) .
.PHONY: imports
diff --git a/gnovm/README.md b/gnovm/README.md
index 91419746cfa..2fe4345c367 100644
--- a/gnovm/README.md
+++ b/gnovm/README.md
@@ -4,7 +4,7 @@ GnoVM is a virtual machine that interprets Gnolang, a custom version of Golang o
It works with Tendermint2 and enables smarter, more modular, and transparent appchains with embedded smart-contracts.
It can be used in TendermintCore, forks, and non-Cosmos blockchains.
-Read the ["Intro to Gnoland"](https://test3.gno.land/r/gnoland/blog:p/intro) blogpost.
+Read the ["Intro to Gnoland"](https://gno.land/r/gnoland/blog:p/intro) blogpost.
This folder focuses on the VM, language, stdlibs, tests, and tools, independent of the blockchain.
This enables non-web3 developers to contribute without requiring an understanding of the broader context.
diff --git a/gnovm/cmd/gno/lint.go b/gnovm/cmd/gno/lint.go
index 6c497c7e2c0..c6008117f13 100644
--- a/gnovm/cmd/gno/lint.go
+++ b/gnovm/cmd/gno/lint.go
@@ -17,6 +17,7 @@ import (
"github.com/gnolang/gno/gnovm/tests"
"github.com/gnolang/gno/tm2/pkg/commands"
osm "github.com/gnolang/gno/tm2/pkg/os"
+ "go.uber.org/multierr"
)
type lintCfg struct {
@@ -174,12 +175,18 @@ func catchRuntimeError(pkgPath string, stderr io.WriteCloser, action func()) (ha
case *gno.PreprocessError:
err := verr.Unwrap()
fmt.Fprint(stderr, issueFromError(pkgPath, err).String()+"\n")
- case scanner.ErrorList:
- for _, err := range verr {
- fmt.Fprint(stderr, issueFromError(pkgPath, err).String()+"\n")
- }
case error:
- fmt.Fprint(stderr, issueFromError(pkgPath, verr).String()+"\n")
+ errors := multierr.Errors(verr)
+ for _, err := range errors {
+ errList, ok := err.(scanner.ErrorList)
+ if ok {
+ for _, errorInList := range errList {
+ fmt.Fprint(stderr, issueFromError(pkgPath, errorInList).String()+"\n")
+ }
+ } else {
+ fmt.Fprint(stderr, issueFromError(pkgPath, err).String()+"\n")
+ }
+ }
case string:
fmt.Fprint(stderr, issueFromError(pkgPath, errors.New(verr)).String()+"\n")
default:
diff --git a/gnovm/cmd/gno/lint_test.go b/gnovm/cmd/gno/lint_test.go
index a5c0319cd00..20d21c05d05 100644
--- a/gnovm/cmd/gno/lint_test.go
+++ b/gnovm/cmd/gno/lint_test.go
@@ -23,6 +23,10 @@ func TestLintApp(t *testing.T) {
args: []string{"lint", "../../tests/integ/several-lint-errors/main.gno"},
stderrShouldContain: "../../tests/integ/several-lint-errors/main.gno:5: expected ';', found example (code=2).\n../../tests/integ/several-lint-errors/main.gno:6",
errShouldBe: "exit code: 1",
+ }, {
+ args: []string{"lint", "../../tests/integ/several-files-multiple-errors/main.gno"},
+ stderrShouldContain: "../../tests/integ/several-files-multiple-errors/file2.gno:3: expected 'IDENT', found '{' (code=2).\n../../tests/integ/several-files-multiple-errors/file2.gno:5: expected type, found '}' (code=2).\n../../tests/integ/several-files-multiple-errors/main.gno:5: expected ';', found example (code=2).\n../../tests/integ/several-files-multiple-errors/main.gno:6: expected '}', found 'EOF' (code=2).\n",
+ errShouldBe: "exit code: 1",
}, {
args: []string{"lint", "../../tests/integ/run_main/"},
stderrShouldContain: "./../../tests/integ/run_main: missing 'gno.mod' file (code=1).",
diff --git a/gnovm/cmd/gno/mod.go b/gnovm/cmd/gno/mod.go
index fec1b0ab2c1..03b2bb348a8 100644
--- a/gnovm/cmd/gno/mod.go
+++ b/gnovm/cmd/gno/mod.go
@@ -131,7 +131,7 @@ func (c *modDownloadCfg) RegisterFlags(fs *flag.FlagSet) {
fs.StringVar(
&c.remote,
"remote",
- "test3.gno.land:26657",
+ "gno.land:26657",
"remote for fetching gno modules",
)
diff --git a/gnovm/cmd/gno/run_test.go b/gnovm/cmd/gno/run_test.go
index 975868b7daf..e5aa1bd6279 100644
--- a/gnovm/cmd/gno/run_test.go
+++ b/gnovm/cmd/gno/run_test.go
@@ -83,6 +83,11 @@ func TestRunApp(t *testing.T) {
args: []string{"run", "-expr", "Context()", "../../tests/integ/context/context.gno"},
stdoutShouldContain: "Context worked",
},
+ {
+ args: []string{"run", "../../tests/integ/several-files-multiple-errors/"},
+ stderrShouldContain: "../../tests/integ/several-files-multiple-errors/file2.gno:3: expected 'IDENT', found '{' (code=2).\n../../tests/integ/several-files-multiple-errors/file2.gno:5: expected type, found '}' (code=2).\n../../tests/integ/several-files-multiple-errors/main.gno:5: expected ';', found example (code=2).\n../../tests/integ/several-files-multiple-errors/main.gno:6: expected '}', found 'EOF' (code=2).",
+ errShouldBe: "exit code: 1",
+ },
// 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 5884463a552..d54b12f6a4f 100644
--- a/gnovm/cmd/gno/test.go
+++ b/gnovm/cmd/gno/test.go
@@ -7,6 +7,7 @@ import (
"flag"
"fmt"
"log"
+ "math"
"os"
"path/filepath"
"runtime/debug"
@@ -17,14 +18,15 @@ import (
"go.uber.org/multierr"
+ "github.com/gnolang/gno/gnovm"
"github.com/gnolang/gno/gnovm/pkg/gnoenv"
gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
"github.com/gnolang/gno/gnovm/pkg/gnomod"
"github.com/gnolang/gno/gnovm/tests"
+ teststd "github.com/gnolang/gno/gnovm/tests/stdlibs/std"
"github.com/gnolang/gno/tm2/pkg/commands"
"github.com/gnolang/gno/tm2/pkg/errors"
"github.com/gnolang/gno/tm2/pkg/random"
- "github.com/gnolang/gno/tm2/pkg/std"
"github.com/gnolang/gno/tm2/pkg/testutils"
)
@@ -35,6 +37,7 @@ type testCfg struct {
timeout time.Duration
updateGoldenTests bool
printRuntimeMetrics bool
+ printEvents bool
withNativeFallback bool
}
@@ -149,6 +152,13 @@ func (c *testCfg) RegisterFlags(fs *flag.FlagSet) {
false,
"print runtime metrics (gas, memory, cpu cycles)",
)
+
+ fs.BoolVar(
+ &c.printEvents,
+ "print-events",
+ false,
+ "print emitted events",
+ )
}
func execTest(cfg *testCfg, args []string, io commands.IO) error {
@@ -228,6 +238,7 @@ func gnoTestPkg(
rootDir = cfg.rootDir
runFlag = cfg.run
printRuntimeMetrics = cfg.printRuntimeMetrics
+ printEvents = cfg.printEvents
stdin = io.In()
stdout = io.Out()
@@ -290,12 +301,12 @@ func gnoTestPkg(
if printRuntimeMetrics {
// from tm2/pkg/sdk/vm/keeper.go
// XXX: make maxAllocTx configurable.
- maxAllocTx := int64(500 * 1000 * 1000)
+ maxAllocTx := int64(math.MaxInt64)
m.Alloc = gno.NewAllocator(maxAllocTx)
}
m.RunMemPackage(memPkg, true)
- err := runTestFiles(m, tfiles, memPkg.Name, verbose, printRuntimeMetrics, runFlag, io)
+ err := runTestFiles(m, tfiles, memPkg.Name, verbose, printRuntimeMetrics, printEvents, runFlag, io)
if err != nil {
errs = multierr.Append(errs, err)
}
@@ -314,7 +325,7 @@ func gnoTestPkg(
m := tests.TestMachine(testStore, stdout, testPkgName)
- memFiles := make([]*std.MemFile, 0, len(ifiles.FileNames())+1)
+ memFiles := make([]*gnovm.MemFile, 0, len(ifiles.FileNames())+1)
for _, f := range memPkg.Files {
for _, ifileName := range ifiles.FileNames() {
if f.Name == "gno.mod" || f.Name == ifileName {
@@ -329,7 +340,7 @@ func gnoTestPkg(
memPkg.Path = memPkg.Path + "_test"
m.RunMemPackage(memPkg, true)
- err := runTestFiles(m, ifiles, testPkgName, verbose, printRuntimeMetrics, runFlag, io)
+ err := runTestFiles(m, ifiles, testPkgName, verbose, printRuntimeMetrics, printEvents, runFlag, io)
if err != nil {
errs = multierr.Append(errs, err)
}
@@ -419,6 +430,7 @@ func runTestFiles(
pkgName string,
verbose bool,
printRuntimeMetrics bool,
+ printEvents bool,
runFlag string,
io commands.IO,
) (errs error) {
@@ -448,10 +460,24 @@ func runTestFiles(
m.RunFiles(n)
for _, test := range testFuncs.Tests {
+ // cleanup machine between tests
+ tests.CleanupMachine(m)
+
testFuncStr := fmt.Sprintf("%q", test.Name)
eval := m.Eval(gno.Call("runtest", testFuncStr))
+ if printEvents {
+ events := m.Context.(*teststd.TestExecContext).EventLogger.Events()
+ if events != nil {
+ res, err := json.Marshal(events)
+ if err != nil {
+ panic(err)
+ }
+ io.ErrPrintfln("EVENTS: %s", string(res))
+ }
+ }
+
ret := eval[0].GetString()
if ret == "" {
err := errors.New("failed to execute unit test: %q", test.Name)
@@ -574,9 +600,10 @@ func loadTestFuncs(pkgName string, t *testFuncs, tfiles *gno.FileSet) *testFuncs
// parseMemPackageTests is copied from gno.ParseMemPackageTests
// for except to _filetest.gno
-func parseMemPackageTests(memPkg *std.MemPackage) (tset, itset *gno.FileSet) {
+func parseMemPackageTests(memPkg *gnovm.MemPackage) (tset, itset *gno.FileSet) {
tset = &gno.FileSet{}
itset = &gno.FileSet{}
+ var errs error
for _, mfile := range memPkg.Files {
if !strings.HasSuffix(mfile.Name, ".gno") {
continue // skip this file.
@@ -586,7 +613,8 @@ func parseMemPackageTests(memPkg *std.MemPackage) (tset, itset *gno.FileSet) {
}
n, err := gno.ParseFile(mfile.Name, mfile.Body)
if err != nil {
- panic(err)
+ errs = multierr.Append(errs, err)
+ continue
}
if n == nil {
panic("should not happen")
@@ -606,6 +634,9 @@ func parseMemPackageTests(memPkg *std.MemPackage) (tset, itset *gno.FileSet) {
memPkg.Name, memPkg.Name, n.PkgName, mfile))
}
}
+ if errs != nil {
+ panic(errs)
+ }
return tset, itset
}
diff --git a/gnovm/cmd/gno/testdata/gno_test/filetest_events.txtar b/gnovm/cmd/gno/testdata/gno_test/filetest_events.txtar
new file mode 100644
index 00000000000..0236872e78a
--- /dev/null
+++ b/gnovm/cmd/gno/testdata/gno_test/filetest_events.txtar
@@ -0,0 +1,51 @@
+# Test with a valid _filetest.gno file
+
+gno test -print-events .
+
+! stdout .+
+stderr 'ok \. \d\.\d\ds'
+
+gno test -print-events -v .
+
+! stdout .+
+stderr '=== RUN file/valid_filetest.gno'
+stderr '--- PASS: file/valid_filetest.gno \(\d\.\d\ds\)'
+stderr 'ok \. \d\.\d\ds'
+
+-- valid.gno --
+package valid
+
+-- valid_filetest.gno --
+package main
+
+import "std"
+
+func main() {
+ println("test")
+ std.Emit("EventA")
+ std.Emit("EventB", "keyA", "valA")
+}
+
+// Output:
+// test
+
+// Events:
+// [
+// {
+// "type": "EventA",
+// "attrs": [],
+// "pkg_path": "",
+// "func": "main"
+// },
+// {
+// "type": "EventB",
+// "attrs": [
+// {
+// "key": "keyA",
+// "value": "valA"
+// }
+// ],
+// "pkg_path": "",
+// "func": "main"
+// }
+// ]
diff --git a/gnovm/cmd/gno/testdata/gno_test/multitest_events.txtar b/gnovm/cmd/gno/testdata/gno_test/multitest_events.txtar
new file mode 100644
index 00000000000..321c790561a
--- /dev/null
+++ b/gnovm/cmd/gno/testdata/gno_test/multitest_events.txtar
@@ -0,0 +1,26 @@
+# Test with a valid _test.gno file
+
+gno test -print-events .
+
+! stdout .+
+stderr 'EVENTS: \[{\"type\":\"EventA\",\"attrs\":\[\],\"pkg_path\":\"gno.land/r/.*\",\"func\":\"TestA\"}\]'
+stderr 'EVENTS: \[{\"type\":\"EventB\",\"attrs\":\[{\"key\":\"keyA\",\"value\":\"valA\"}\],\"pkg_path\":\"gno.land/r/.*\",\"func\":\"TestB\"},{\"type\":\"EventC\",\"attrs\":\[{\"key\":\"keyD\",\"value\":\"valD\"}\],\"pkg_path\":\"gno.land/r/.*\",\"func\":\"TestB\"}\]'
+stderr 'ok \. \d\.\d\ds'
+
+-- valid.gno --
+package valid
+
+-- valid_test.gno --
+package valid
+
+import "testing"
+import "std"
+
+func TestA(t *testing.T) {
+ std.Emit("EventA")
+}
+
+func TestB(t *testing.T) {
+ std.Emit("EventB", "keyA", "valA")
+ std.Emit("EventC", "keyD", "valD")
+}
diff --git a/gnovm/cmd/gno/testdata/gno_test/test_with-native-fallback.txtar b/gnovm/cmd/gno/testdata/gno_test/test_with-native-fallback.txtar
index 0954d1dd932..6099788a9a1 100644
--- a/gnovm/cmd/gno/testdata/gno_test/test_with-native-fallback.txtar
+++ b/gnovm/cmd/gno/testdata/gno_test/test_with-native-fallback.txtar
@@ -4,7 +4,7 @@
! stdout .+
stderr 'panic: unknown import path net \[recovered\]'
-stderr ' panic: gno.land/r/\w{8}/contract.gno:3:1: unknown import path net'
+stderr ' panic: gno.land/r/\w{8}/contract.gno:3:8: unknown import path net'
gno test -v --with-native-fallback .
diff --git a/gnovm/cmd/gno/testdata/gno_test/unknow_lib.txtar b/gnovm/cmd/gno/testdata/gno_test/unknow_lib.txtar
index 15125f695f5..37ef68f3d91 100644
--- a/gnovm/cmd/gno/testdata/gno_test/unknow_lib.txtar
+++ b/gnovm/cmd/gno/testdata/gno_test/unknow_lib.txtar
@@ -4,13 +4,13 @@
! stdout .+
stderr 'panic: unknown import path foobarbaz \[recovered\]'
-stderr ' panic: gno.land/r/\w{8}/contract.gno:3:1: unknown import path foobarbaz'
+stderr ' panic: gno.land/r/\w{8}/contract.gno:3:8: unknown import path foobarbaz'
! gno test -v --with-native-fallback .
! stdout .+
stderr 'panic: unknown import path foobarbaz \[recovered\]'
-stderr ' panic: gno.land/r/\w{8}/contract.gno:3:1: unknown import path foobarbaz'
+stderr ' panic: gno.land/r/\w{8}/contract.gno:3:8: unknown import path foobarbaz'
-- contract.gno --
package contract
diff --git a/gnovm/gno.proto b/gnovm/gno.proto
index 5f53c363b73..8a15ca96e14 100644
--- a/gnovm/gno.proto
+++ b/gnovm/gno.proto
@@ -1,7 +1,7 @@
syntax = "proto3";
package gno;
-option go_package = "github.com/gnolang/gno/pb";
+option go_package = "github.com/gnolang/gno/gnovm/pb";
// imports
import "google/protobuf/any.proto";
@@ -601,3 +601,15 @@ message tupleType {
message RefType {
string ID = 1;
}
+
+// messages
+message MemFile {
+ string name = 1;
+ string body = 2;
+}
+
+message MemPackage {
+ string name = 1;
+ string path = 2;
+ repeated MemFile files = 3;
+}
diff --git a/gnovm/gnovm.proto b/gnovm/gnovm.proto
new file mode 100644
index 00000000000..c9f0b23ae80
--- /dev/null
+++ b/gnovm/gnovm.proto
@@ -0,0 +1,16 @@
+syntax = "proto3";
+package gnovm;
+
+option go_package = "github.com/gnolang/gno/gnovm/pb";
+
+// messages
+message MemFile {
+ string name = 1;
+ string body = 2;
+}
+
+message MemPackage {
+ string name = 1;
+ string path = 2;
+ repeated MemFile files = 3;
+}
\ No newline at end of file
diff --git a/tm2/pkg/std/memfile.go b/gnovm/memfile.go
similarity index 99%
rename from tm2/pkg/std/memfile.go
rename to gnovm/memfile.go
index 01bc18c1487..a37bba6ef3d 100644
--- a/tm2/pkg/std/memfile.go
+++ b/gnovm/memfile.go
@@ -1,4 +1,4 @@
-package std
+package gnovm
import (
"fmt"
diff --git a/tm2/pkg/std/memfile_test.go b/gnovm/memfile_test.go
similarity index 99%
rename from tm2/pkg/std/memfile_test.go
rename to gnovm/memfile_test.go
index 3e1fb49e131..c93c251b0e7 100644
--- a/tm2/pkg/std/memfile_test.go
+++ b/gnovm/memfile_test.go
@@ -1,4 +1,4 @@
-package std
+package gnovm
import (
"testing"
diff --git a/gnovm/package.go b/gnovm/package.go
new file mode 100644
index 00000000000..d6332b05709
--- /dev/null
+++ b/gnovm/package.go
@@ -0,0 +1,15 @@
+package gnovm
+
+import (
+ "github.com/gnolang/gno/tm2/pkg/amino"
+)
+
+var Package = amino.RegisterPackage(amino.NewPackage(
+ "github.com/gnolang/gno/gnovm",
+ "gnovm",
+ amino.GetCallersDirname(),
+).WithDependencies().WithTypes(
+ // MemFile/MemPackage
+ MemFile{}, "MemFile",
+ MemPackage{}, "MemPackage",
+))
diff --git a/gnovm/pkg/gnoenv/gnohome.go b/gnovm/pkg/gnoenv/gnohome.go
index 52dd5e6adb4..9e0f1bab689 100644
--- a/gnovm/pkg/gnoenv/gnohome.go
+++ b/gnovm/pkg/gnoenv/gnohome.go
@@ -29,7 +29,5 @@ func HomeDir() string {
}
gnoHome := filepath.Join(dir, "gno")
- // XXX: added april 2023 as a transitory measure - remove after test4
- fixOldDefaultGnoHome(gnoHome)
return gnoHome
}
diff --git a/gnovm/pkg/gnoenv/migration.go b/gnovm/pkg/gnoenv/migration.go
deleted file mode 100644
index 5b1d1fd1fa0..00000000000
--- a/gnovm/pkg/gnoenv/migration.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package gnoenv
-
-import (
- "log"
- "os"
- "path/filepath"
-)
-
-// XXX: added april 2023 as a transitory measure - remove after test4
-func fixOldDefaultGnoHome(newDir string) {
- dir, err := os.UserHomeDir()
- if err != nil {
- return
- }
- oldDir := filepath.Join(dir, ".gno")
- s, err := os.Stat(oldDir)
- if err != nil || !s.IsDir() {
- return
- }
- if err = os.Rename(oldDir, newDir); err != nil {
- if os.IsExist(err) {
- log.Printf("WARNING: attempted moving old default GNO_HOME (%q) to new (%q) but failed because directory exists.", oldDir, newDir)
- log.Printf("You may need to move files from the old directory manually, or set the env var GNO_HOME to %q to retain the old directory.", oldDir)
- } else {
- log.Printf("WARNING: attempted moving old default GNO_HOME (%q) to new (%q) but failed with error: %v", oldDir, newDir, err)
- }
- }
-}
diff --git a/gnovm/pkg/gnoenv/migration_test.go b/gnovm/pkg/gnoenv/migration_test.go
deleted file mode 100644
index 86edd8502a1..00000000000
--- a/gnovm/pkg/gnoenv/migration_test.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package gnoenv
-
-import (
- "os"
- "path/filepath"
- "testing"
-
- "github.com/stretchr/testify/require"
-)
-
-func TestFixOldDefaultGnoHome(t *testing.T) {
- tempHomeDir := t.TempDir()
- t.Setenv("HOME", tempHomeDir)
-
- oldGnoHome := filepath.Join(tempHomeDir, ".gno")
- newGnoHome := filepath.Join(tempHomeDir, "gno")
-
- // Create a dummy old GNO_HOME
- os.Mkdir(oldGnoHome, 0o755)
-
- // Test migration
- fixOldDefaultGnoHome(newGnoHome)
-
- _, errOld := os.Stat(oldGnoHome)
- require.NotNil(t, errOld)
- _, errNew := os.Stat(newGnoHome)
- require.True(t, os.IsNotExist(errOld), "invalid errors", errOld)
- require.NoError(t, errNew)
-}
diff --git a/gnovm/pkg/gnolang/debugger.go b/gnovm/pkg/gnolang/debugger.go
index 839b6a691de..f047a176af7 100644
--- a/gnovm/pkg/gnolang/debugger.go
+++ b/gnovm/pkg/gnolang/debugger.go
@@ -257,8 +257,10 @@ func debugUpdateLocation(m *Machine) {
for i := nx - 1; i >= 0; i-- {
expr := m.Exprs[i]
if l := expr.GetLine(); l > 0 {
- m.Debugger.loc.Line = l
- m.Debugger.loc.Column = expr.GetColumn()
+ if col := expr.GetColumn(); col > 0 {
+ m.Debugger.loc.Line = l
+ m.Debugger.loc.Column = expr.GetColumn()
+ }
return
}
}
@@ -266,8 +268,10 @@ func debugUpdateLocation(m *Machine) {
if len(m.Stmts) > 0 {
if stmt := m.PeekStmt1(); stmt != nil {
if l := stmt.GetLine(); l > 0 {
- m.Debugger.loc.Line = l
- m.Debugger.loc.Column = stmt.GetColumn()
+ if col := stmt.GetColumn(); col > 0 {
+ m.Debugger.loc.Line = l
+ m.Debugger.loc.Column = stmt.GetColumn()
+ }
return
}
}
@@ -648,7 +652,7 @@ func debugEvalExpr(m *Machine, node ast.Node) (tv TypedValue, err error) {
return tv, fmt.Errorf("invalid selector: %s", n.Sel.Name)
}
for _, vp := range tr {
- x = x.GetPointerTo(m.Alloc, m.Store, vp).Deref()
+ x = x.GetPointerToFromTV(m.Alloc, m.Store, vp).Deref()
}
return x, nil
case *ast.IndexExpr:
diff --git a/gnovm/pkg/gnolang/debugger_test.go b/gnovm/pkg/gnolang/debugger_test.go
index fe059ba9f56..44786257d67 100644
--- a/gnovm/pkg/gnolang/debugger_test.go
+++ b/gnovm/pkg/gnolang/debugger_test.go
@@ -131,7 +131,7 @@ func TestDebug(t *testing.T) {
{in: "p \"xxxx\"\n", out: `("xxxx" string)`},
{in: "si\n", out: "sample.gno:14"},
{in: "s\ns\n", out: `=> 14: var global = "test"`},
- {in: "s\n\n", out: "=> 33: num := 5"},
+ {in: "s\n\n\n", out: "=> 33: num := 5"},
{in: "foo", out: "command not available: foo"},
{in: "\n\n", out: "dbg> "},
{in: "#\n", out: "dbg> "},
diff --git a/gnovm/pkg/gnolang/eval_test.go b/gnovm/pkg/gnolang/eval_test.go
index 9acf4cc89f0..9b83d673767 100644
--- a/gnovm/pkg/gnolang/eval_test.go
+++ b/gnovm/pkg/gnolang/eval_test.go
@@ -40,8 +40,8 @@ func TestEvalFiles(t *testing.T) {
if wantStacktrace != "" && !strings.Contains(stacktrace, wantStacktrace) {
t.Fatalf("unexpected stacktrace\nWant: %s\n Got: %s", wantStacktrace, stacktrace)
}
- if wantOut != "" && out != wantOut {
- t.Fatalf("unexpected output\nWant: %s\n Got: %s", wantOut, out)
+ if wantOut != "" && strings.TrimSpace(out) != strings.TrimSpace(wantOut) {
+ t.Fatalf("unexpected output\nWant: \"%s\"\n Got: \"%s\"", wantOut, out)
}
})
diff --git a/gnovm/pkg/gnolang/gno_test.go b/gnovm/pkg/gnolang/gno_test.go
index 1f83303023c..3b15c018505 100644
--- a/gnovm/pkg/gnolang/gno_test.go
+++ b/gnovm/pkg/gnolang/gno_test.go
@@ -404,18 +404,6 @@ func BenchmarkBenchdata(b *testing.B) {
name += "_param:" + param
}
b.Run(name, func(b *testing.B) {
- if strings.HasPrefix(name, "matrix.gno_param") {
- // CGO_ENABLED=0 go test -bench . -benchmem ./... -short -run=^$ -cpu 1,2 -count=1 ./...
- // That is not just exposing test and benchmark traces as output, but these benchmarks are failing
- // making the output unparseable:
- /*
- BenchmarkBenchdata/matrix.gno_param:3 panic: runtime error: index out of range [31] with length 25 [recovered]
- panic: runtime error: index out of range [31] with length 25:
- ...
- */
- b.Skip("it panics causing an error when parsing benchmark results")
- }
-
// Gen template with N and param.
var buf bytes.Buffer
require.NoError(b, tpl.Execute(&buf, bdataParams{
diff --git a/gnovm/pkg/gnolang/gnolang.proto b/gnovm/pkg/gnolang/gnolang.proto
index eee9a0375e6..27c26534a5f 100644
--- a/gnovm/pkg/gnolang/gnolang.proto
+++ b/gnovm/pkg/gnolang/gnolang.proto
@@ -56,10 +56,11 @@ message FuncValue {
google.protobuf.Any source = 3 [json_name = "Source"];
string name = 4 [json_name = "Name"];
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"];
+ repeated TypedValue captures = 6 [json_name = "Captures"];
+ string file_name = 7 [json_name = "FileName"];
+ string pkg_path = 8 [json_name = "PkgPath"];
+ string native_pkg = 9 [json_name = "NativePkg"];
+ string native_name = 10 [json_name = "NativeName"];
}
message MapValue {
@@ -110,6 +111,11 @@ message RefValue {
string hash = 4 [json_name = "Hash"];
}
+message HeapItemValue {
+ ObjectInfo object_info = 1 [json_name = "ObjectInfo"];
+ TypedValue value = 2 [json_name = "Value"];
+}
+
message ObjectID {
string value = 1;
}
@@ -147,13 +153,15 @@ message Location {
message Attributes {
sint64 line = 1 [json_name = "Line"];
- string label = 2 [json_name = "Label"];
+ sint64 column = 2 [json_name = "Column"];
+ string label = 3 [json_name = "Label"];
}
message NameExpr {
Attributes attributes = 1 [json_name = "Attributes"];
ValuePath path = 2 [json_name = "Path"];
string name = 3 [json_name = "Name"];
+ sint64 type = 4 [json_name = "Type"];
}
message BasicLitExpr {
@@ -239,6 +247,7 @@ message FuncLitExpr {
StaticBlock static_block = 2 [json_name = "StaticBlock"];
FuncTypeExpr type = 3 [json_name = "Type"];
repeated google.protobuf.Any body = 4 [json_name = "Body"];
+ repeated NameExpr heap_captures = 5 [json_name = "HeapCaptures"];
}
message ConstExpr {
@@ -602,4 +611,4 @@ message tupleType {
message RefType {
string id = 1 [json_name = "ID"];
-}
+}
\ No newline at end of file
diff --git a/gnovm/pkg/gnolang/go2gno.go b/gnovm/pkg/gnolang/go2gno.go
index efdfecf0289..99e051f7913 100644
--- a/gnovm/pkg/gnolang/go2gno.go
+++ b/gnovm/pkg/gnolang/go2gno.go
@@ -44,8 +44,8 @@ import (
"strings"
"github.com/davecgh/go-spew/spew"
+ "github.com/gnolang/gno/gnovm"
"github.com/gnolang/gno/tm2/pkg/errors"
- "github.com/gnolang/gno/tm2/pkg/std"
"go.uber.org/multierr"
)
@@ -486,7 +486,7 @@ func Go2Gno(fs *token.FileSet, gon ast.Node) (n Node) {
// MemPackageGetter implements the GetMemPackage() method. It is a subset of
// [Store], separated for ease of testing.
type MemPackageGetter interface {
- GetMemPackage(path string) *std.MemPackage
+ GetMemPackage(path string) *gnovm.MemPackage
}
// TypeCheckMemPackage performs type validation and checking on the given
@@ -496,7 +496,7 @@ type MemPackageGetter interface {
//
// If format is true, the code will be automatically updated with the
// formatted source code.
-func TypeCheckMemPackage(mempkg *std.MemPackage, getter MemPackageGetter, format bool) error {
+func TypeCheckMemPackage(mempkg *gnovm.MemPackage, getter MemPackageGetter, format bool) error {
var errs error
imp := &gnoImporter{
getter: getter,
@@ -556,7 +556,7 @@ func (g *gnoImporter) ImportFrom(path, _ string, _ types.ImportMode) (*types.Pac
return result, err
}
-func (g *gnoImporter) parseCheckMemPackage(mpkg *std.MemPackage, fmt bool) (*types.Package, error) {
+func (g *gnoImporter) parseCheckMemPackage(mpkg *gnovm.MemPackage, fmt bool) (*types.Package, error) {
fset := token.NewFileSet()
files := make([]*ast.File, 0, len(mpkg.Files))
var errs error
@@ -754,11 +754,13 @@ func toDecls(fs *token.FileSet, gd *ast.GenDecl) (ds Decls) {
name := toName(s.Name)
tipe := toExpr(fs, s.Type)
alias := s.Assign != 0
- ds = append(ds, &TypeDecl{
+ td := &TypeDecl{
NameExpr: NameExpr{Name: name},
Type: tipe,
IsAlias: alias,
- })
+ }
+ setLoc(fs, s.Pos(), td)
+ ds = append(ds, td)
case *ast.ValueSpec:
if gd.Tok == token.CONST {
var names []NameExpr
diff --git a/gnovm/pkg/gnolang/go2gno_test.go b/gnovm/pkg/gnolang/go2gno_test.go
index d85c142ca52..8aba5d7f293 100644
--- a/gnovm/pkg/gnolang/go2gno_test.go
+++ b/gnovm/pkg/gnolang/go2gno_test.go
@@ -4,7 +4,7 @@ import (
"fmt"
"testing"
- "github.com/gnolang/gno/tm2/pkg/std"
+ "github.com/gnolang/gno/gnovm"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/multierr"
@@ -29,9 +29,9 @@ func main(){
fmt.Printf("AST.String():\n%s\n", n.String())
}
-type mockPackageGetter []*std.MemPackage
+type mockPackageGetter []*gnovm.MemPackage
-func (mi mockPackageGetter) GetMemPackage(path string) *std.MemPackage {
+func (mi mockPackageGetter) GetMemPackage(path string) *gnovm.MemPackage {
for _, pkg := range mi {
if pkg.Path == path {
return pkg
@@ -45,7 +45,7 @@ type mockPackageGetterCounts struct {
counts map[string]int
}
-func (mpg mockPackageGetterCounts) GetMemPackage(path string) *std.MemPackage {
+func (mpg mockPackageGetterCounts) GetMemPackage(path string) *gnovm.MemPackage {
mpg.counts[path]++
return mpg.mockPackageGetter.GetMemPackage(path)
}
@@ -77,17 +77,17 @@ func TestTypeCheckMemPackage(t *testing.T) {
type testCase struct {
name string
- pkg *std.MemPackage
+ pkg *gnovm.MemPackage
getter MemPackageGetter
check func(*testing.T, error)
}
tt := []testCase{
{
"Simple",
- &std.MemPackage{
+ &gnovm.MemPackage{
Name: "hello",
Path: "gno.land/p/demo/hello",
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{
Name: "hello.gno",
Body: `
@@ -103,10 +103,10 @@ func TestTypeCheckMemPackage(t *testing.T) {
},
{
"WrongReturn",
- &std.MemPackage{
+ &gnovm.MemPackage{
Name: "hello",
Path: "gno.land/p/demo/hello",
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{
Name: "hello.gno",
Body: `
@@ -122,10 +122,10 @@ func TestTypeCheckMemPackage(t *testing.T) {
},
{
"ParseError",
- &std.MemPackage{
+ &gnovm.MemPackage{
Name: "hello",
Path: "gno.land/p/demo/hello",
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{
Name: "hello.gno",
Body: `
@@ -139,10 +139,10 @@ func TestTypeCheckMemPackage(t *testing.T) {
},
{
"MultiError",
- &std.MemPackage{
+ &gnovm.MemPackage{
Name: "main",
Path: "gno.land/p/demo/main",
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{
Name: "hello.gno",
Body: `
@@ -159,10 +159,10 @@ func TestTypeCheckMemPackage(t *testing.T) {
},
{
"TestsIgnored",
- &std.MemPackage{
+ &gnovm.MemPackage{
Name: "hello",
Path: "gno.land/p/demo/hello",
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{
Name: "hello.gno",
Body: `
@@ -181,10 +181,10 @@ func TestTypeCheckMemPackage(t *testing.T) {
},
{
"ImportFailed",
- &std.MemPackage{
+ &gnovm.MemPackage{
Name: "hello",
Path: "gno.land/p/demo/hello",
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{
Name: "hello.gno",
Body: `
@@ -199,10 +199,10 @@ func TestTypeCheckMemPackage(t *testing.T) {
},
{
"ImportSucceeded",
- &std.MemPackage{
+ &gnovm.MemPackage{
Name: "hello",
Path: "gno.land/p/demo/hello",
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{
Name: "hello.gno",
Body: `
@@ -213,12 +213,12 @@ func TestTypeCheckMemPackage(t *testing.T) {
},
},
mockPackageGetter{
- &std.MemPackage{
+ &gnovm.MemPackage{
Name: "std",
Path: "std",
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{
- Name: "std.gno",
+ Name: "gnovm.gno",
Body: `
package std
type Address string`,
@@ -230,10 +230,10 @@ func TestTypeCheckMemPackage(t *testing.T) {
},
{
"ImportBadIdent",
- &std.MemPackage{
+ &gnovm.MemPackage{
Name: "hello",
Path: "gno.land/p/demo/hello",
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{
Name: "hello.gno",
Body: `
@@ -244,12 +244,12 @@ func TestTypeCheckMemPackage(t *testing.T) {
},
},
mockPackageGetter{
- &std.MemPackage{
+ &gnovm.MemPackage{
Name: "a_completely_different_identifier",
Path: "std",
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{
- Name: "std.gno",
+ Name: "gnovm.gno",
Body: `
package a_completely_different_identifier
type Address string`,
@@ -263,10 +263,10 @@ func TestTypeCheckMemPackage(t *testing.T) {
cacheMpg := mockPackageGetterCounts{
mockPackageGetter{
- &std.MemPackage{
+ &gnovm.MemPackage{
Name: "bye",
Path: "bye",
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{
Name: "bye.gno",
Body: `
@@ -276,12 +276,12 @@ func TestTypeCheckMemPackage(t *testing.T) {
},
},
},
- &std.MemPackage{
+ &gnovm.MemPackage{
Name: "std",
Path: "std",
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{
- Name: "std.gno",
+ Name: "gnovm.gno",
Body: `
package std
type Address string`,
@@ -295,10 +295,10 @@ func TestTypeCheckMemPackage(t *testing.T) {
tt = append(tt, testCase{
"ImportWithCache",
// This test will make use of the importer's internal cache for package `std`.
- &std.MemPackage{
+ &gnovm.MemPackage{
Name: "hello",
Path: "gno.land/p/demo/hello",
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{
Name: "hello.gno",
Body: `
@@ -347,10 +347,10 @@ func TestTypeCheckMemPackage_format(t *testing.T) {
`
- pkg := &std.MemPackage{
+ pkg := &gnovm.MemPackage{
Name: "hello",
Path: "gno.land/p/demo/hello",
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{
Name: "hello.gno",
Body: input,
diff --git a/gnovm/pkg/gnolang/gonative.go b/gnovm/pkg/gnolang/gonative.go
index 6127fa42b07..fe92f5bcd23 100644
--- a/gnovm/pkg/gnolang/gonative.go
+++ b/gnovm/pkg/gnolang/gonative.go
@@ -866,7 +866,7 @@ func gno2GoType(t Type) reflect.Type {
return rt
} else {
// NOTE: can this be implemented in go1.15? i think not.
- panic("not yet supported")
+ panic("gno2go conversion of type not yet supported: " + ct.String())
}
case *TypeType:
panic("should not happen")
@@ -1258,16 +1258,25 @@ func (x *PackageNode) DefineGoNativeType(rt reflect.Type) {
x.Define(Name(name), asValue(nt))
}
-func (x *PackageNode) DefineGoNativeValue(n Name, nv interface{}) {
+func (x *PackageNode) DefineGoNativeValue(name Name, nv interface{}) {
+ x.defineGoNativeValue(false, name, nv)
+}
+
+func (x *PackageNode) DefineGoNativeConstValue(name Name, nv interface{}) {
+ x.defineGoNativeValue(true, name, nv)
+}
+
+func (x *PackageNode) defineGoNativeValue(isConst bool, n Name, nv interface{}) {
if debug {
- debug.Printf("*PackageNode.DefineGoNativeValue(%s)\n", reflect.ValueOf(nv).String())
+ debug.Printf("*PackageNode.defineGoNativeValue(%s)\n", reflect.ValueOf(nv).String())
}
rv := reflect.ValueOf(nv)
// rv is not settable, so create something that is.
rt := rv.Type()
rv2 := reflect.New(rt).Elem()
rv2.Set(rv)
- x.Define(n, go2GnoValue(nilAllocator, rv2))
+ tv := go2GnoValue(nilAllocator, rv2)
+ x.Define2(isConst, n, tv.T, tv)
}
// ----------------------------------------
diff --git a/gnovm/pkg/gnolang/kind_string.go b/gnovm/pkg/gnolang/kind_string.go
index cbe6bfa8e33..12e95829b20 100644
--- a/gnovm/pkg/gnolang/kind_string.go
+++ b/gnovm/pkg/gnolang/kind_string.go
@@ -36,13 +36,14 @@ func _() {
_ = x[MapKind-25]
_ = x[TypeKind-26]
_ = x[BlockKind-27]
- _ = x[TupleKind-28]
- _ = x[RefTypeKind-29]
+ _ = x[HeapItemKind-28]
+ _ = x[TupleKind-29]
+ _ = x[RefTypeKind-30]
}
-const _Kind_name = "InvalidKindBoolKindStringKindIntKindInt8KindInt16KindInt32KindInt64KindUintKindUint8KindUint16KindUint32KindUint64KindFloat32KindFloat64KindBigintKindBigdecKindArrayKindSliceKindPointerKindStructKindPackageKindInterfaceKindChanKindFuncKindMapKindTypeKindBlockKindTupleKindRefTypeKind"
+const _Kind_name = "InvalidKindBoolKindStringKindIntKindInt8KindInt16KindInt32KindInt64KindUintKindUint8KindUint16KindUint32KindUint64KindFloat32KindFloat64KindBigintKindBigdecKindArrayKindSliceKindPointerKindStructKindPackageKindInterfaceKindChanKindFuncKindMapKindTypeKindBlockKindHeapItemKindTupleKindRefTypeKind"
-var _Kind_index = [...]uint16{0, 11, 19, 29, 36, 44, 53, 62, 71, 79, 88, 98, 108, 118, 129, 140, 150, 160, 169, 178, 189, 199, 210, 223, 231, 239, 246, 254, 263, 272, 283}
+var _Kind_index = [...]uint16{0, 11, 19, 29, 36, 44, 53, 62, 71, 79, 88, 98, 108, 118, 129, 140, 150, 160, 169, 178, 189, 199, 210, 223, 231, 239, 246, 254, 263, 275, 284, 295}
func (i Kind) String() string {
if i >= Kind(len(_Kind_index)-1) {
diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go
index a0542bf9713..09be71682a5 100644
--- a/gnovm/pkg/gnolang/machine.go
+++ b/gnovm/pkg/gnolang/machine.go
@@ -8,14 +8,16 @@ import (
"io"
"os"
"reflect"
+ "slices"
+ "strconv"
"strings"
"sync"
"testing"
"github.com/gnolang/overflow"
+ "github.com/gnolang/gno/gnovm"
"github.com/gnolang/gno/tm2/pkg/errors"
- "github.com/gnolang/gno/tm2/pkg/std"
"github.com/gnolang/gno/tm2/pkg/store"
)
@@ -263,7 +265,7 @@ func (m *Machine) PreprocessAllFilesAndSaveBlockNodes() {
// Parses files, sets the package if doesn't exist, runs files, saves mempkg
// and corresponding package node, package value, and types to store. Save
// is set to false for tests where package values may be native.
-func (m *Machine) RunMemPackage(memPkg *std.MemPackage, save bool) (*PackageNode, *PackageValue) {
+func (m *Machine) RunMemPackage(memPkg *gnovm.MemPackage, save bool) (*PackageNode, *PackageValue) {
return m.runMemPackage(memPkg, save, false)
}
@@ -271,15 +273,17 @@ func (m *Machine) RunMemPackage(memPkg *std.MemPackage, save bool) (*PackageNode
// declarations are filtered removing duplicate declarations.
// To control which declaration overrides which, use [ReadMemPackageFromList],
// putting the overrides at the top of the list.
-func (m *Machine) RunMemPackageWithOverrides(memPkg *std.MemPackage, save bool) (*PackageNode, *PackageValue) {
+func (m *Machine) RunMemPackageWithOverrides(memPkg *gnovm.MemPackage, save bool) (*PackageNode, *PackageValue) {
return m.runMemPackage(memPkg, save, true)
}
-func (m *Machine) runMemPackage(memPkg *std.MemPackage, save, overrides bool) (*PackageNode, *PackageValue) {
+func (m *Machine) runMemPackage(memPkg *gnovm.MemPackage, save, overrides bool) (*PackageNode, *PackageValue) {
// parse files.
files := ParseMemPackage(memPkg)
- if !overrides && checkDuplicates(files) {
- panic(fmt.Errorf("running package %q: duplicate declarations not allowed", memPkg.Path))
+ if !overrides {
+ if err := checkDuplicates(files); err != nil {
+ panic(fmt.Errorf("running package %q: %w", memPkg.Path, err))
+ }
}
// make and set package if doesn't exist.
pn := (*PackageNode)(nil)
@@ -322,9 +326,31 @@ func (m *Machine) runMemPackage(memPkg *std.MemPackage, save, overrides bool) (*
return pn, pv
}
-// checkDuplicates returns true if there duplicate declarations in the fset.
-func checkDuplicates(fset *FileSet) bool {
+type redeclarationErrors []Name
+
+func (r redeclarationErrors) Error() string {
+ var b strings.Builder
+ b.WriteString("redeclarations for identifiers: ")
+ for idx, s := range r {
+ b.WriteString(strconv.Quote(string(s)))
+ if idx != len(r)-1 {
+ b.WriteString(", ")
+ }
+ }
+ return b.String()
+}
+
+func (r redeclarationErrors) add(newI Name) redeclarationErrors {
+ if slices.Contains(r, newI) {
+ return r
+ }
+ return append(r, newI)
+}
+
+// checkDuplicates returns an error if there are duplicate declarations in the fset.
+func checkDuplicates(fset *FileSet) error {
defined := make(map[Name]struct{}, 128)
+ var duplicated redeclarationErrors
for _, f := range fset.Files {
for _, d := range f.Decls {
var name Name
@@ -345,7 +371,7 @@ func checkDuplicates(fset *FileSet) bool {
continue
}
if _, ok := defined[nx.Name]; ok {
- return true
+ duplicated = duplicated.add(nx.Name)
}
defined[nx.Name] = struct{}{}
}
@@ -357,12 +383,15 @@ func checkDuplicates(fset *FileSet) bool {
continue
}
if _, ok := defined[name]; ok {
- return true
+ duplicated = duplicated.add(name)
}
defined[name] = struct{}{}
}
}
- return false
+ if len(duplicated) > 0 {
+ return duplicated
+ }
+ return nil
}
func destar(x Expr) Expr {
@@ -377,7 +406,7 @@ func destar(x Expr) Expr {
// The resulting package value and node become injected with TestMethods and
// other declarations, so it is expected that non-test code will not be run
// afterwards from the same store.
-func (m *Machine) TestMemPackage(t *testing.T, memPkg *std.MemPackage) {
+func (m *Machine) TestMemPackage(t *testing.T, memPkg *gnovm.MemPackage) {
defer m.injectLocOnPanic()
DisableDebug()
fmt.Println("DEBUG DISABLED (FOR TEST DEPENDENCIES INIT)")
@@ -670,7 +699,7 @@ func (m *Machine) runFileDecls(fns ...*FileNode) []TypedValue {
}
}
// if dep already in loopfindr, abort.
- if hasName(dep, loopfindr) {
+ if slices.Contains(loopfindr, dep) {
if _, ok := (*depdecl).(*FuncDecl); ok {
// recursive function dependencies
// are OK with func decls.
@@ -2083,15 +2112,25 @@ func (m *Machine) PushForPointer(lx Expr) {
func (m *Machine) PopAsPointer(lx Expr) PointerValue {
switch lx := lx.(type) {
case *NameExpr:
- lb := m.LastBlock()
- return lb.GetPointerTo(m.Store, lx.Path)
+ switch lx.Type {
+ case NameExprTypeNormal:
+ lb := m.LastBlock()
+ return lb.GetPointerTo(m.Store, lx.Path)
+ case NameExprTypeHeapUse:
+ lb := m.LastBlock()
+ return lb.GetPointerToHeapUse(m.Store, lx.Path)
+ case NameExprTypeHeapClosure:
+ panic("should not happen")
+ default:
+ panic("unexpected NameExpr in PopAsPointer")
+ }
case *IndexExpr:
iv := m.PopValue()
xv := m.PopValue()
return xv.GetPointerAtIndex(m.Alloc, m.Store, iv)
case *SelectorExpr:
xv := m.PopValue()
- return xv.GetPointerTo(m.Alloc, m.Store, lx.Path)
+ return xv.GetPointerToFromTV(m.Alloc, m.Store, lx.Path)
case *StarExpr:
ptr := m.PopValue().V.(PointerValue)
return ptr
@@ -2319,15 +2358,3 @@ func (m *Machine) ExceptionsStacktrace() string {
return builder.String()
}
-
-//----------------------------------------
-// utility
-
-func hasName(n Name, ns []Name) bool {
- for _, n2 := range ns {
- if n == n2 {
- return true
- }
- }
- return false
-}
diff --git a/gnovm/pkg/gnolang/machine_test.go b/gnovm/pkg/gnolang/machine_test.go
index 8e27b127fbb..c3b2118f099 100644
--- a/gnovm/pkg/gnolang/machine_test.go
+++ b/gnovm/pkg/gnolang/machine_test.go
@@ -4,8 +4,8 @@ import (
"fmt"
"testing"
+ "github.com/gnolang/gno/gnovm"
"github.com/gnolang/gno/tm2/pkg/db/memdb"
- "github.com/gnolang/gno/tm2/pkg/std"
"github.com/gnolang/gno/tm2/pkg/store/dbadapter"
"github.com/gnolang/gno/tm2/pkg/store/iavl"
stypes "github.com/gnolang/gno/tm2/pkg/store/types"
@@ -27,10 +27,10 @@ func TestRunMemPackageWithOverrides_revertToOld(t *testing.T) {
iavlStore := iavl.StoreConstructor(db, stypes.StoreOptions{})
store := NewStore(nil, baseStore, iavlStore)
m := NewMachine("std", store)
- m.RunMemPackageWithOverrides(&std.MemPackage{
+ m.RunMemPackageWithOverrides(&gnovm.MemPackage{
Name: "std",
Path: "std",
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{Name: "a.gno", Body: `package std; func Redecl(x int) string { return "1" }`},
},
}, true)
@@ -38,10 +38,10 @@ func TestRunMemPackageWithOverrides_revertToOld(t *testing.T) {
defer func() {
p = fmt.Sprint(recover())
}()
- m.RunMemPackageWithOverrides(&std.MemPackage{
+ m.RunMemPackageWithOverrides(&gnovm.MemPackage{
Name: "std",
Path: "std",
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{Name: "b.gno", Body: `package std; func Redecl(x int) string { var y string; _, _ = y; return "2" }`},
},
}, true)
diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go
index b18ed157ca6..c282b619fdc 100644
--- a/gnovm/pkg/gnolang/nodes.go
+++ b/gnovm/pkg/gnolang/nodes.go
@@ -12,8 +12,9 @@ import (
"strconv"
"strings"
+ "github.com/gnolang/gno/gnovm"
"github.com/gnolang/gno/tm2/pkg/errors"
- "github.com/gnolang/gno/tm2/pkg/std"
+ "go.uber.org/multierr"
)
// ----------------------------------------
@@ -145,11 +146,26 @@ func (loc Location) IsZero() bool {
// even after preprocessing. Temporary attributes (e.g. those
// for preprocessing) are stored in .data.
+type GnoAttribute string
+
+const (
+ ATTR_PREPROCESSED GnoAttribute = "ATTR_PREPROCESSED"
+ ATTR_PREDEFINED GnoAttribute = "ATTR_PREDEFINED"
+ ATTR_TYPE_VALUE GnoAttribute = "ATTR_TYPE_VALUE"
+ ATTR_TYPEOF_VALUE GnoAttribute = "ATTR_TYPEOF_VALUE"
+ ATTR_IOTA GnoAttribute = "ATTR_IOTA"
+ ATTR_LOCATIONED GnoAttribute = "ATTR_LOCATIONE" // XXX DELETE
+ ATTR_GOTOLOOP_STMT GnoAttribute = "ATTR_GOTOLOOP_STMT" // XXX delete?
+ ATTR_LOOP_DEFINES GnoAttribute = "ATTR_LOOP_DEFINES" // []Name defined within loops.
+ ATTR_LOOP_USES GnoAttribute = "ATTR_LOOP_USES" // []Name loop defines actually used.
+ ATTR_SHIFT_RHS GnoAttribute = "ATTR_SHIFT_RHS"
+)
+
type Attributes struct {
Line int
Column int
Label Name
- data map[interface{}]interface{} // not persisted
+ data map[GnoAttribute]interface{} // not persisted
}
func (attr *Attributes) GetLine() int {
@@ -176,22 +192,31 @@ func (attr *Attributes) SetLabel(label Name) {
attr.Label = label
}
-func (attr *Attributes) HasAttribute(key interface{}) bool {
+func (attr *Attributes) HasAttribute(key GnoAttribute) bool {
_, ok := attr.data[key]
return ok
}
-func (attr *Attributes) GetAttribute(key interface{}) interface{} {
+// GnoAttribute must not be user provided / arbitrary,
+// otherwise will create potential exploits.
+func (attr *Attributes) GetAttribute(key GnoAttribute) interface{} {
return attr.data[key]
}
-func (attr *Attributes) SetAttribute(key interface{}, value interface{}) {
+func (attr *Attributes) SetAttribute(key GnoAttribute, value interface{}) {
if attr.data == nil {
- attr.data = make(map[interface{}]interface{})
+ attr.data = make(map[GnoAttribute]interface{})
}
attr.data[key] = value
}
+func (attr *Attributes) DelAttribute(key GnoAttribute) {
+ if debug && attr.data == nil {
+ panic("should not happen, attribute is expected to be non-empty.")
+ }
+ delete(attr.data, key)
+}
+
// ----------------------------------------
// Node
@@ -205,9 +230,10 @@ type Node interface {
SetColumn(int)
GetLabel() Name
SetLabel(Name)
- HasAttribute(key interface{}) bool
- GetAttribute(key interface{}) interface{}
- SetAttribute(key interface{}, value interface{})
+ HasAttribute(key GnoAttribute) bool
+ GetAttribute(key GnoAttribute) interface{}
+ SetAttribute(key GnoAttribute, value interface{})
+ DelAttribute(key GnoAttribute)
}
// non-pointer receiver to help make immutable.
@@ -367,11 +393,22 @@ var (
_ Expr = &ConstExpr{}
)
+type NameExprType int
+
+const (
+ NameExprTypeNormal NameExprType = iota // default
+ NameExprTypeDefine // when defining normally
+ NameExprTypeHeapDefine // when defining escaped name in loop
+ NameExprTypeHeapUse // when above used in non-define lhs/rhs
+ NameExprTypeHeapClosure // when closure captures name
+)
+
type NameExpr struct {
Attributes
// TODO rename .Path's to .ValuePaths.
Path ValuePath // set by preprocessor.
Name
+ Type NameExprType
}
type NameExprs []NameExpr
@@ -498,8 +535,9 @@ type KeyValueExprs []KeyValueExpr
type FuncLitExpr struct {
Attributes
StaticBlock
- Type FuncTypeExpr // function type
- Body // function body
+ Type FuncTypeExpr // function type
+ Body // function body
+ HeapCaptures NameExprs // filled in findLoopUses1
}
// The preprocessor replaces const expressions
@@ -580,11 +618,15 @@ func (ftxz FieldTypeExprs) IsNamed() bool {
named := false
for i, ftx := range ftxz {
if i == 0 {
- named = ftx.Name != ""
+ if ftx.Name == "" || isHiddenResultVariable(string(ftx.Name)) {
+ named = false
+ } else {
+ named = true
+ }
} else {
if named && ftx.Name == "" {
panic("[]FieldTypeExpr has inconsistent namedness (starts named)")
- } else if !named && ftx.Name != "" {
+ } else if !named && (ftx.Name != "" || !isHiddenResultVariable(string(ftx.Name))) {
panic("[]FieldTypeExpr has inconsistent namedness (starts unnamed)")
}
}
@@ -1110,7 +1152,7 @@ func PackageNameFromFileBody(name, body string) Name {
//
// NOTE: panics if package name is invalid (characters must be alphanumeric or _,
// lowercase, and must start with a letter).
-func ReadMemPackage(dir string, pkgPath string) *std.MemPackage {
+func ReadMemPackage(dir string, pkgPath string) *gnovm.MemPackage {
files, err := os.ReadDir(dir)
if err != nil {
panic(err)
@@ -1144,14 +1186,14 @@ func ReadMemPackage(dir string, pkgPath string) *std.MemPackage {
return ReadMemPackageFromList(list, pkgPath)
}
-// ReadMemPackageFromList creates a new [std.MemPackage] with the specified pkgPath,
+// ReadMemPackageFromList creates a new [gnovm.MemPackage] with the specified pkgPath,
// containing the contents of all the files provided in the list slice.
// No parsing or validation is done on the filenames.
//
// NOTE: panics if package name is invalid (characters must be alphanumeric or _,
// lowercase, and must start with a letter).
-func ReadMemPackageFromList(list []string, pkgPath string) *std.MemPackage {
- memPkg := &std.MemPackage{Path: pkgPath}
+func ReadMemPackageFromList(list []string, pkgPath string) *gnovm.MemPackage {
+ memPkg := &gnovm.MemPackage{Path: pkgPath}
var pkgName Name
for _, fpath := range list {
fname := filepath.Base(fpath)
@@ -1167,7 +1209,7 @@ func ReadMemPackageFromList(list []string, pkgPath string) *std.MemPackage {
}
}
memPkg.Files = append(memPkg.Files,
- &std.MemFile{
+ &gnovm.MemFile{
Name: fname,
Body: string(bz),
})
@@ -1187,8 +1229,9 @@ func ReadMemPackageFromList(list []string, pkgPath string) *std.MemPackage {
//
// If one of the files has a different package name than memPkg.Name,
// or [ParseFile] returns an error, ParseMemPackage panics.
-func ParseMemPackage(memPkg *std.MemPackage) (fset *FileSet) {
+func ParseMemPackage(memPkg *gnovm.MemPackage) (fset *FileSet) {
fset = &FileSet{}
+ var errs error
for _, mfile := range memPkg.Files {
if !strings.HasSuffix(mfile.Name, ".gno") ||
endsWith(mfile.Name, []string{"_test.gno", "_filetest.gno"}) {
@@ -1196,7 +1239,8 @@ func ParseMemPackage(memPkg *std.MemPackage) (fset *FileSet) {
}
n, err := ParseFile(mfile.Name, mfile.Body)
if err != nil {
- panic(err)
+ errs = multierr.Append(errs, err)
+ continue
}
if memPkg.Name != string(n.PkgName) {
panic(fmt.Sprintf(
@@ -1206,10 +1250,13 @@ func ParseMemPackage(memPkg *std.MemPackage) (fset *FileSet) {
// add package file.
fset.AddFiles(n)
}
+ if errs != nil {
+ panic(errs)
+ }
return fset
}
-func ParseMemPackageTests(memPkg *std.MemPackage) (tset, itset *FileSet) {
+func ParseMemPackageTests(memPkg *gnovm.MemPackage) (tset, itset *FileSet) {
tset = &FileSet{}
itset = &FileSet{}
for _, mfile := range memPkg.Files {
@@ -1483,6 +1530,7 @@ type BlockNode interface {
GetNumNames() uint16
GetParentNode(Store) BlockNode
GetPathForName(Store, Name) ValuePath
+ GetBlockNodeForPath(Store, ValuePath) BlockNode
GetIsConst(Store, Name) bool
GetLocalIndex(Name) (uint16, bool)
GetValueRef(Store, Name, bool) *TypedValue
@@ -1582,6 +1630,8 @@ func (sb *StaticBlock) GetBlockNames() (ns []Name) {
}
// Implements BlockNode.
+// NOTE: Extern names may also be local, if declared after usage as an extern
+// (thus shadowing the extern name).
func (sb *StaticBlock) GetExternNames() (ns []Name) {
return sb.Externs // copy?
}
@@ -1623,6 +1673,9 @@ func (sb *StaticBlock) GetPathForName(store Store, n Name) ValuePath {
}
// Register as extern.
// NOTE: uverse names are externs too.
+ // NOTE: externs may also be shadowed later in the block. Thus, usages
+ // before the declaration will have depth > 1; following it, depth == 1,
+ // matching the two different identifiers they refer to.
if !isFile(sb.GetSource(store)) {
sb.GetStaticBlock().addExternName(n)
}
@@ -1651,6 +1704,21 @@ func (sb *StaticBlock) GetPathForName(store Store, n Name) ValuePath {
panic(fmt.Sprintf("name %s not declared", n))
}
+// Get the containing block node for node with path relative to this containing block.
+func (sb *StaticBlock) GetBlockNodeForPath(store Store, path ValuePath) BlockNode {
+ if path.Type != VPBlock {
+ panic("expected block type value path but got " + path.Type.String())
+ }
+
+ // NOTE: path.Depth == 1 means it's in bn.
+ bn := sb.GetSource(store)
+ for i := 1; i < int(path.Depth); i++ {
+ bn = bn.GetParentNode(store)
+ }
+
+ return bn
+}
+
// Returns whether a name defined here in in ancestry is a const.
// This is not the same as whether a name's static type is
// untyped -- as in c := a == b, a name may be an untyped non-const.
@@ -1707,21 +1775,12 @@ func (sb *StaticBlock) GetStaticTypeOf(store Store, n Name) Type {
// Implements BlockNode.
func (sb *StaticBlock) GetStaticTypeOfAt(store Store, path ValuePath) Type {
if debug {
- if path.Type != VPBlock {
- panic("should not happen")
- }
if path.Depth == 0 {
panic("should not happen")
}
}
- for {
- if path.Depth == 1 {
- return sb.Types[path.Index]
- } else {
- sb = sb.GetParentNode(store).GetStaticBlock()
- path.Depth -= 1
- }
- }
+ bn := sb.GetBlockNodeForPath(store, path)
+ return bn.GetStaticBlock().Types[path.Index]
}
// Implements BlockNode.
@@ -2109,18 +2168,6 @@ func (x *BasicLitExpr) GetInt() int {
return i
}
-type GnoAttribute string
-
-const (
- ATTR_PREPROCESSED GnoAttribute = "ATTR_PREPROCESSED"
- ATTR_PREDEFINED GnoAttribute = "ATTR_PREDEFINED"
- ATTR_TYPE_VALUE GnoAttribute = "ATTR_TYPE_VALUE"
- ATTR_TYPEOF_VALUE GnoAttribute = "ATTR_TYPEOF_VALUE"
- ATTR_IOTA GnoAttribute = "ATTR_IOTA"
- ATTR_LOCATIONED GnoAttribute = "ATTR_LOCATIONED"
- ATTR_INJECTED GnoAttribute = "ATTR_INJECTED"
-)
-
var rePkgName = regexp.MustCompile(`^[a-z][a-z0-9_]+$`)
// TODO: consider length restrictions.
@@ -2130,3 +2177,12 @@ func validatePkgName(name string) {
panic(fmt.Sprintf("cannot create package with invalid name %q", name))
}
}
+
+const hiddenResultVariable = ".res_"
+
+func isHiddenResultVariable(name string) bool {
+ if strings.HasPrefix(name, hiddenResultVariable) {
+ return true
+ }
+ return false
+}
diff --git a/gnovm/pkg/gnolang/nodes_string.go b/gnovm/pkg/gnolang/nodes_string.go
index 547ad83294d..e16e2f182a5 100644
--- a/gnovm/pkg/gnolang/nodes_string.go
+++ b/gnovm/pkg/gnolang/nodes_string.go
@@ -96,7 +96,20 @@ func (vp ValuePath) String() string {
}
func (x NameExpr) String() string {
- return fmt.Sprintf("%s<%s>", x.Name, x.Path.String())
+ switch x.Type {
+ case NameExprTypeNormal:
+ return fmt.Sprintf("%s<%s>", x.Name, x.Path.String())
+ case NameExprTypeDefine:
+ return fmt.Sprintf("%s", x.Name, x.Path.String())
+ case NameExprTypeHeapDefine:
+ return fmt.Sprintf("%s", x.Name, x.Path.String())
+ case NameExprTypeHeapUse:
+ return fmt.Sprintf("%s<~%s>", x.Name, x.Path.String())
+ case NameExprTypeHeapClosure:
+ return fmt.Sprintf("%s<()~%s>", x.Name, x.Path.String())
+ default:
+ panic("unexpected NameExpr type")
+ }
}
func (x BasicLitExpr) String() string {
@@ -172,7 +185,11 @@ func (x CompositeLitExpr) String() string {
}
func (x FuncLitExpr) String() string {
- return fmt.Sprintf("func %s{ %s }", x.Type, x.Body.String())
+ heapCaptures := ""
+ if len(x.HeapCaptures) > 0 {
+ heapCaptures = "<" + x.HeapCaptures.String() + ">"
+ }
+ return fmt.Sprintf("func %s{ %s }%s", x.Type, x.Body.String(), heapCaptures)
}
func (x KeyValueExpr) String() string {
diff --git a/gnovm/pkg/gnolang/op_assign.go b/gnovm/pkg/gnolang/op_assign.go
index eb67ffcc351..8caacbfd1e6 100644
--- a/gnovm/pkg/gnolang/op_assign.go
+++ b/gnovm/pkg/gnolang/op_assign.go
@@ -11,7 +11,7 @@ func (m *Machine) doOpDefine() {
// Get name and value of i'th term.
nx := s.Lhs[i].(*NameExpr)
// Finally, define (or assign if loop block).
- ptr := lb.GetPointerTo(m.Store, nx.Path)
+ ptr := lb.GetPointerToMaybeHeapDefine(m.Store, nx)
// XXX HACK (until value persistence impl'd)
if m.ReadOnly {
if oo, ok := ptr.Base.(Object); ok {
diff --git a/gnovm/pkg/gnolang/op_binary.go b/gnovm/pkg/gnolang/op_binary.go
index db3c1e5695c..24123d285ad 100644
--- a/gnovm/pkg/gnolang/op_binary.go
+++ b/gnovm/pkg/gnolang/op_binary.go
@@ -1097,6 +1097,7 @@ func xorAssign(lv, rv *TypedValue) {
// for doOpShl and doOpShlAssign.
func shlAssign(lv, rv *TypedValue) {
+ rv.AssertNonNegative("runtime error: negative shift amount")
// set the result in lv.
// NOTE: baseOf(rv.T) is always UintType.
switch baseOf(lv.T) {
@@ -1136,6 +1137,7 @@ func shlAssign(lv, rv *TypedValue) {
// for doOpShr and doOpShrAssign.
func shrAssign(lv, rv *TypedValue) {
+ rv.AssertNonNegative("runtime error: negative shift amount")
// set the result in lv.
// NOTE: baseOf(rv.T) is always UintType.
switch baseOf(lv.T) {
diff --git a/gnovm/pkg/gnolang/op_call.go b/gnovm/pkg/gnolang/op_call.go
index 15531ec610d..ba5b7507cff 100644
--- a/gnovm/pkg/gnolang/op_call.go
+++ b/gnovm/pkg/gnolang/op_call.go
@@ -27,6 +27,11 @@ func (m *Machine) doOpPrecall() {
case TypeValue:
// Do not pop type yet.
// No need for frames.
+ xv := m.PeekValue(1)
+ if cx.GetAttribute(ATTR_SHIFT_RHS) == true {
+ xv.AssertNonNegative("runtime error: negative shift amount")
+ }
+
m.PushOp(OpConvert)
if debug {
if len(cx.Args) != 1 {
@@ -57,6 +62,18 @@ func (m *Machine) doOpCall() {
// Create new block scope.
clo := fr.Func.GetClosure(m.Store)
b := m.Alloc.NewBlock(fr.Func.GetSource(m.Store), clo)
+
+ // Copy *FuncValue.Captures into block
+ // NOTE: addHeapCapture in preprocess ensures order.
+ if len(fv.Captures) != 0 {
+ if len(fv.Captures) > len(b.Values) {
+ panic("should not happen, length of captured variables must not exceed the number of values")
+ }
+ for i := 0; i < len(fv.Captures); i++ {
+ b.Values[len(b.Values)-len(fv.Captures)+i] = fv.Captures[i].Copy(m.Alloc)
+ }
+ }
+
m.PushBlock(b)
if fv.nativeBody == nil && fv.NativePkg != "" {
// native function, unmarshaled so doesn't have nativeBody yet
@@ -78,6 +95,7 @@ func (m *Machine) doOpCall() {
// Initialize return variables with default value.
numParams := len(ft.Params)
for i, rt := range ft.Results {
+ // results/parameters never are heap use/closure.
ptr := b.GetPointerToInt(nil, numParams+i)
dtv := defaultTypedValue(m.Alloc, rt.Type)
ptr.Assign2(m.Alloc, nil, nil, dtv, false)
@@ -287,6 +305,15 @@ func (m *Machine) doOpReturnCallDefers() {
// Create new block scope for defer.
clo := dfr.Func.GetClosure(m.Store)
b := m.Alloc.NewBlock(fv.GetSource(m.Store), clo)
+ // copy values from captures
+ if len(fv.Captures) != 0 {
+ if len(fv.Captures) > len(b.Values) {
+ panic("should not happen, length of captured variables must not exceed the number of values")
+ }
+ for i := 0; i < len(fv.Captures); i++ {
+ b.Values[len(b.Values)-len(fv.Captures)+i] = fv.Captures[i].Copy(m.Alloc)
+ }
+ }
m.PushBlock(b)
if fv.nativeBody == nil {
fbody := fv.GetBodyFromSource(m.Store)
diff --git a/gnovm/pkg/gnolang/op_decl.go b/gnovm/pkg/gnolang/op_decl.go
index 2c20c43ae2f..c9c04ccf76d 100644
--- a/gnovm/pkg/gnolang/op_decl.go
+++ b/gnovm/pkg/gnolang/op_decl.go
@@ -58,8 +58,8 @@ func (m *Machine) doOpValueDecl() {
} else if isUntyped(tv.T) {
ConvertUntypedTo(&tv, nil)
}
- nx := s.NameExprs[i]
- ptr := lb.GetPointerTo(m.Store, nx.Path)
+ nx := &s.NameExprs[i]
+ ptr := lb.GetPointerToMaybeHeapDefine(m.Store, nx)
ptr.Assign2(m.Alloc, m.Store, m.Realm, tv, false)
}
}
diff --git a/gnovm/pkg/gnolang/op_eval.go b/gnovm/pkg/gnolang/op_eval.go
index 701615fff13..1beba1d6e3f 100644
--- a/gnovm/pkg/gnolang/op_eval.go
+++ b/gnovm/pkg/gnolang/op_eval.go
@@ -36,7 +36,7 @@ func (m *Machine) doOpEval() {
// Get value from scope.
lb := m.LastBlock()
// Push value, done.
- ptr := lb.GetPointerTo(m.Store, nx.Path)
+ ptr := lb.GetPointerToMaybeHeapUse(m.Store, nx)
m.PushValue(ptr.Deref())
return
}
diff --git a/gnovm/pkg/gnolang/op_exec.go b/gnovm/pkg/gnolang/op_exec.go
index c7e8ffd600c..a61349b0806 100644
--- a/gnovm/pkg/gnolang/op_exec.go
+++ b/gnovm/pkg/gnolang/op_exec.go
@@ -171,8 +171,8 @@ func (m *Machine) doOpExec(op Op) {
case ASSIGN:
m.PopAsPointer(bs.Key).Assign2(m.Alloc, m.Store, m.Realm, iv, false)
case DEFINE:
- knxp := bs.Key.(*NameExpr).Path
- ptr := m.LastBlock().GetPointerTo(m.Store, knxp)
+ knx := bs.Key.(*NameExpr)
+ ptr := m.LastBlock().GetPointerToMaybeHeapDefine(m.Store, knx)
ptr.TV.Assign(m.Alloc, iv, false)
default:
panic("should not happen")
@@ -186,8 +186,8 @@ func (m *Machine) doOpExec(op Op) {
case ASSIGN:
m.PopAsPointer(bs.Value).Assign2(m.Alloc, m.Store, m.Realm, ev, false)
case DEFINE:
- vnxp := bs.Value.(*NameExpr).Path
- ptr := m.LastBlock().GetPointerTo(m.Store, vnxp)
+ vnx := bs.Value.(*NameExpr)
+ ptr := m.LastBlock().GetPointerToMaybeHeapDefine(m.Store, vnx)
ptr.TV.Assign(m.Alloc, ev, false)
default:
panic("should not happen")
@@ -267,8 +267,8 @@ func (m *Machine) doOpExec(op Op) {
case ASSIGN:
m.PopAsPointer(bs.Key).Assign2(m.Alloc, m.Store, m.Realm, iv, false)
case DEFINE:
- knxp := bs.Key.(*NameExpr).Path
- ptr := m.LastBlock().GetPointerTo(m.Store, knxp)
+ knx := bs.Key.(*NameExpr)
+ ptr := m.LastBlock().GetPointerToMaybeHeapDefine(m.Store, knx)
ptr.TV.Assign(m.Alloc, iv, false)
default:
panic("should not happen")
@@ -280,8 +280,8 @@ func (m *Machine) doOpExec(op Op) {
case ASSIGN:
m.PopAsPointer(bs.Value).Assign2(m.Alloc, m.Store, m.Realm, ev, false)
case DEFINE:
- vnxp := bs.Value.(*NameExpr).Path
- ptr := m.LastBlock().GetPointerTo(m.Store, vnxp)
+ vnx := bs.Value.(*NameExpr)
+ ptr := m.LastBlock().GetPointerToMaybeHeapDefine(m.Store, vnx)
ptr.TV.Assign(m.Alloc, ev, false)
default:
panic("should not happen")
@@ -360,8 +360,8 @@ func (m *Machine) doOpExec(op Op) {
case ASSIGN:
m.PopAsPointer(bs.Key).Assign2(m.Alloc, m.Store, m.Realm, kv, false)
case DEFINE:
- knxp := bs.Key.(*NameExpr).Path
- ptr := m.LastBlock().GetPointerTo(m.Store, knxp)
+ knx := bs.Key.(*NameExpr)
+ ptr := m.LastBlock().GetPointerToMaybeHeapDefine(m.Store, knx)
ptr.TV.Assign(m.Alloc, kv, false)
default:
panic("should not happen")
@@ -373,8 +373,8 @@ func (m *Machine) doOpExec(op Op) {
case ASSIGN:
m.PopAsPointer(bs.Value).Assign2(m.Alloc, m.Store, m.Realm, vv, false)
case DEFINE:
- vnxp := bs.Value.(*NameExpr).Path
- ptr := m.LastBlock().GetPointerTo(m.Store, vnxp)
+ vnx := bs.Value.(*NameExpr)
+ ptr := m.LastBlock().GetPointerToMaybeHeapDefine(m.Store, vnx)
ptr.TV.Assign(m.Alloc, vv, false)
default:
panic("should not happen")
@@ -884,6 +884,8 @@ func (m *Machine) doOpTypeSwitch() {
// NOTE: assumes the var is first in block.
vp := NewValuePath(
VPBlock, 1, 0, ss.VarName)
+ // NOTE: GetPointerToMaybeHeapDefine not needed,
+ // because this type is in new type switch clause block.
ptr := b.GetPointerTo(m.Store, vp)
ptr.TV.Assign(m.Alloc, *xv, false)
}
diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go
index 8ff0b5bd538..a1d677ca878 100644
--- a/gnovm/pkg/gnolang/op_expressions.go
+++ b/gnovm/pkg/gnolang/op_expressions.go
@@ -78,7 +78,7 @@ func (m *Machine) doOpIndex2() {
func (m *Machine) doOpSelector() {
sx := m.PopExpr().(*SelectorExpr)
xv := m.PeekValue(1)
- res := xv.GetPointerTo(m.Alloc, m.Store, sx.Path).Deref()
+ res := xv.GetPointerToFromTV(m.Alloc, m.Store, sx.Path).Deref()
if debug {
m.Printf("-v[S] %v\n", xv)
m.Printf("+v[S] %v\n", res)
@@ -758,6 +758,25 @@ func (m *Machine) doOpFuncLit() {
ft := m.PopValue().V.(TypeValue).Type.(*FuncType)
lb := m.LastBlock()
m.Alloc.AllocateFunc()
+
+ // First copy closure captured heap values
+ // to *FuncValue. Later during doOpCall a block
+ // will be created that copies these values for
+ // every invocation of the function.
+ captures := []TypedValue(nil)
+ for _, nx := range x.HeapCaptures {
+ ptr := lb.GetPointerTo(m.Store, nx.Path)
+ // check that ptr.TV is a heap item value.
+ // it must be in the form of:
+ // {T:heapItemType{},V:HeapItemValue{...}}
+ if _, ok := ptr.TV.T.(heapItemType); !ok {
+ panic("should not happen, should be heapItemType")
+ }
+ if _, ok := ptr.TV.V.(*HeapItemValue); !ok {
+ panic("should not happen, should be heapItemValue")
+ }
+ captures = append(captures, *ptr.TV)
+ }
m.PushValue(TypedValue{
T: ft,
V: &FuncValue{
@@ -766,6 +785,7 @@ func (m *Machine) doOpFuncLit() {
Source: x,
Name: "",
Closure: lb,
+ Captures: captures,
PkgPath: m.Package.PkgPath,
body: x.Body,
nativeBody: nil,
diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go
index fb17260a438..78e4488b2a0 100644
--- a/gnovm/pkg/gnolang/preprocess.go
+++ b/gnovm/pkg/gnolang/preprocess.go
@@ -2,8 +2,10 @@ package gnolang
import (
"fmt"
+ "math"
"math/big"
"reflect"
+ "slices"
"strings"
"sync/atomic"
@@ -23,7 +25,8 @@ func PredefineFileSet(store Store, pn *PackageNode, fset *FileSet) {
// This will also reserve names on BlockNode.StaticBlock by
// calling StaticBlock.Predefine().
for _, fn := range fset.Files {
- SetNodeLocations(pn.PkgPath, string(fn.Name), fn)
+ setNodeLines(fn)
+ setNodeLocations(pn.PkgPath, string(fn.Name), fn)
initStaticBlocks(store, pn, fn)
}
// NOTE: The calls to .Predefine() above is more of a name reservation,
@@ -92,7 +95,7 @@ func PredefineFileSet(store Store, pn *PackageNode, fset *FileSet) {
}
}
}
- // Finally, predefine other decls and
+ // Then, predefine other decls and
// preprocess ValueDecls..
for _, fn := range fset.Files {
for i := 0; i < len(fn.Decls); i++ {
@@ -143,32 +146,7 @@ func initStaticBlocks(store Store, ctx BlockNode, bn BlockNode) {
// iterate over all nodes recursively.
_ = Transcribe(bn, func(ns []Node, ftype TransField, index int, n Node, stage TransStage) (Node, TransCtrl) {
- defer func() {
- if r := recover(); r != nil {
- // before re-throwing the error, append location information to message.
- loc := last.GetLocation()
- if nline := n.GetLine(); nline > 0 {
- loc.Line = nline
- }
-
- var err error
- rerr, ok := r.(error)
- if ok {
- // NOTE: gotuna/gorilla expects error exceptions.
- err = errors.Wrap(rerr, loc.String())
- } else {
- // NOTE: gotuna/gorilla expects error exceptions.
- err = fmt.Errorf("%s: %v", loc.String(), r)
- }
-
- // Re-throw the error after wrapping it with the preprocessing stack information.
- panic(&PreprocessError{
- err: err,
- stack: stack,
- })
- }
- }()
-
+ defer doRecover(stack, n)
if debug {
debug.Printf("initStaticBlocks %s (%v) stage:%v\n", n.String(), reflect.TypeOf(n), stage)
}
@@ -179,47 +157,54 @@ func initStaticBlocks(store Store, ctx BlockNode, bn BlockNode) {
switch n := n.(type) {
case *AssignStmt:
if n.Op == DEFINE {
- var defined bool
for _, lx := range n.Lhs {
- ln := lx.(*NameExpr).Name
+ nx := lx.(*NameExpr)
+ ln := nx.Name
if ln == blankIdentifier {
continue
}
- last.Predefine(false, ln)
- defined = true
- }
- if !defined {
- panic(fmt.Sprintf("nothing defined in assignment %s", n.String()))
+ if !isLocallyDefined2(last, ln) {
+ // if loop extern, will promote to
+ // NameExprTypeHeapDefine later.
+ nx.Type = NameExprTypeDefine
+ last.Predefine(false, ln)
+ }
}
}
case *ImportDecl:
- name := n.Name
- if name == "." {
+ nx := &n.NameExpr
+ nn := nx.Name
+ if nn == "." {
panic("dot imports not allowed in gno")
}
- if name == "" { // use default
+ if nn == "" { // use default
pv := store.GetPackage(n.PkgPath, true)
if pv == nil {
panic(fmt.Sprintf(
"unknown import path %s",
n.PkgPath))
}
- name = pv.PkgName
+ nn = pv.PkgName
}
- if name != blankIdentifier {
- last.Predefine(false, name)
+ if nn != blankIdentifier {
+ nx.Type = NameExprTypeDefine
+ last.Predefine(false, nn)
}
case *ValueDecl:
last2 := skipFile(last)
for i := 0; i < len(n.NameExprs); i++ {
nx := &n.NameExprs[i]
- if nx.Name == blankIdentifier {
+ nn := nx.Name
+ if nn == blankIdentifier {
continue
}
- last2.Predefine(n.Const, nx.Name)
+ nx.Type = NameExprTypeDefine
+ last2.Predefine(n.Const, nn)
}
case *TypeDecl:
last2 := skipFile(last)
+ nx := &n.NameExpr
+ nx.Type = NameExprTypeDefine
last2.Predefine(false, n.Name)
case *FuncDecl:
if n.IsMethod {
@@ -238,7 +223,8 @@ func initStaticBlocks(store Store, ctx BlockNode, bn BlockNode) {
dname := Name(fmt.Sprintf("init.%d", idx))
n.Name = dname
}
-
+ nx := &n.NameExpr
+ nx.Type = NameExprTypeDefine
pkg.Predefine(false, n.Name)
}
case *FuncTypeExpr:
@@ -256,7 +242,7 @@ func initStaticBlocks(store Store, ctx BlockNode, bn BlockNode) {
if r.Name == blankIdentifier {
// create a hidden var with leading dot.
// NOTE: document somewhere.
- rn := fmt.Sprintf(".res_%d", i)
+ rn := fmt.Sprintf("%s%d", hiddenResultVariable, i)
r.Name = Name(rn)
}
}
@@ -265,15 +251,9 @@ func initStaticBlocks(store Store, ctx BlockNode, bn BlockNode) {
// ----------------------------------------
case TRANS_BLOCK:
+ pushInitBlock(n.(BlockNode), &last, &stack)
switch n := n.(type) {
- case *BlockStmt:
- pushInitBlock(n, &last, &stack)
- case *ForStmt:
- pushInitBlock(n, &last, &stack)
- case *IfStmt:
- pushInitBlock(n, &last, &stack)
case *IfCaseStmt:
- pushInitRealBlock(n, &last, &stack)
// parent if statement.
ifs := ns[len(ns)-1].(*IfStmt)
// anything declared in ifs are copied.
@@ -281,17 +261,27 @@ func initStaticBlocks(store Store, ctx BlockNode, bn BlockNode) {
last.Predefine(false, n)
}
case *RangeStmt:
- pushInitBlock(n, &last, &stack)
if n.Op == DEFINE {
if n.Key != nil {
- last.Predefine(false, n.Key.(*NameExpr).Name)
+ nx := n.Key.(*NameExpr)
+ if nx.Name != blankIdentifier {
+ // XXX, this should be uncommented when fully
+ // support Go1.22 loopvar, to make it consistent
+ // with for i := 0; i < 10; i++ {...}.
+ // nx.Type = NameExprTypeDefine
+
+ last.Predefine(false, nx.Name)
+ }
}
if n.Value != nil {
- last.Predefine(false, n.Value.(*NameExpr).Name)
+ nx := n.Value.(*NameExpr)
+ if nx.Name != blankIdentifier {
+ // nx.Type = NameExprTypeDefine // XXX,ditto
+ last.Predefine(false, nx.Name)
+ }
}
}
case *FuncLitExpr:
- pushInitBlock(n, &last, &stack)
for _, p := range n.Type.Params {
last.Predefine(false, p.Name)
}
@@ -300,10 +290,7 @@ func initStaticBlocks(store Store, ctx BlockNode, bn BlockNode) {
last.Predefine(false, rf.Name)
}
}
- case *SelectCaseStmt:
- pushInitBlock(n, &last, &stack)
case *SwitchStmt:
- pushInitBlock(n, &last, &stack)
if n.VarName != "" {
// NOTE: this defines for default clauses too,
// see comment on block copying @
@@ -311,7 +298,6 @@ func initStaticBlocks(store Store, ctx BlockNode, bn BlockNode) {
last.Predefine(false, n.VarName)
}
case *SwitchClauseStmt:
- pushInitRealBlock(n, &last, &stack)
// parent switch statement.
ss := ns[len(ns)-1].(*SwitchStmt)
// anything declared in ss are copied,
@@ -329,7 +315,6 @@ func initStaticBlocks(store Store, ctx BlockNode, bn BlockNode) {
}
}
case *FuncDecl:
- pushInitBlock(n, &last, &stack)
if n.IsMethod {
n.Predefine(false, n.Recv.Name)
}
@@ -339,23 +324,24 @@ func initStaticBlocks(store Store, ctx BlockNode, bn BlockNode) {
}
n.Predefine(false, pte.Name)
}
- for _, rte := range n.Type.Results {
- if rte.Name != "" {
- n.Predefine(false, rte.Name)
+ for i, rte := range n.Type.Results {
+ if rte.Name == "" {
+ rn := fmt.Sprintf("%s%d", hiddenResultVariable, i)
+ rte.Name = Name(rn)
}
+ n.Predefine(false, rte.Name)
}
- case *FileNode:
- pushInitBlock(n, &last, &stack)
- default:
- panic("should not happen")
}
return n, TRANS_CONTINUE
// ----------------------------------------
case TRANS_LEAVE:
- // finalization.
- if _, ok := n.(BlockNode); ok {
- // Pop block.
+ // Pop block from stack.
+ // NOTE: DO NOT USE TRANS_SKIP WITHIN BLOCK
+ // NODES, AS TRANS_LEAVE WILL BE SKIPPED; OR
+ // POP BLOCK YOURSELF.
+ switch n.(type) {
+ case BlockNode:
stack = stack[:len(stack)-1]
last = stack[len(stack)-1]
}
@@ -365,6 +351,34 @@ func initStaticBlocks(store Store, ctx BlockNode, bn BlockNode) {
})
}
+func doRecover(stack []BlockNode, n Node) {
+ if r := recover(); r != nil {
+ // before re-throwing the error, append location information to message.
+ last := stack[len(stack)-1]
+ loc := last.GetLocation()
+ if nline := n.GetLine(); nline > 0 {
+ loc.Line = nline
+ loc.Column = n.GetColumn()
+ }
+
+ var err error
+ rerr, ok := r.(error)
+ if ok {
+ // NOTE: gotuna/gorilla expects error exceptions.
+ err = errors.Wrap(rerr, loc.String())
+ } else {
+ // NOTE: gotuna/gorilla expects error exceptions.
+ err = fmt.Errorf("%s: %v", loc.String(), r)
+ }
+
+ // Re-throw the error after wrapping it with the preprocessing stack information.
+ panic(&PreprocessError{
+ err: err,
+ stack: stack,
+ })
+ }
+}
+
// This counter ensures (during testing) that certain functions
// (like ConvertUntypedTo() for bigints and strings)
// are only called during the preprocessing stage.
@@ -390,6 +404,27 @@ var preprocessing atomic.Int32
// - Assigns BlockValuePath to NameExprs.
// - TODO document what it does.
func Preprocess(store Store, ctx BlockNode, n Node) Node {
+ // If initStaticBlocks doesn't happen here,
+ // it means Preprocess on blocks might fail.
+ // it works for now because preprocess also does pushInitBlock,
+ // but it's kinda weird.
+ // maybe consider moving initStaticBlocks here and ensure idempotency of it.
+ n = preprocess1(store, ctx, n)
+ // XXX check node lines and locations
+ checkNodeLinesLocations("XXXpkgPath", "XXXfileName", n)
+ // XXX what about the fact that preprocess1 sets the PREPROCESSED attr on all nodes?
+ // XXX do any of the following need the attr, or similar attrs?
+ // XXX well the following may be isn't idempotent,
+ // XXX so it is currently strange.
+ if bn, ok := n.(BlockNode); ok {
+ findGotoLoopDefines(ctx, bn)
+ findLoopUses1(ctx, bn)
+ findLoopUses2(ctx, bn)
+ }
+ return n
+}
+
+func preprocess1(store Store, ctx BlockNode, n Node) Node {
// Increment preprocessing counter while preprocessing.
preprocessing.Add(1)
defer preprocessing.Add(-1)
@@ -403,7 +438,8 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node {
if fn, ok := n.(*FileNode); ok {
pkgPath := ctx.(*PackageNode).PkgPath
fileName := string(fn.Name)
- SetNodeLocations(pkgPath, fileName, fn)
+ setNodeLines(fn)
+ setNodeLocations(pkgPath, fileName, fn)
}
// create stack of BlockNodes.
@@ -419,32 +455,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node {
return n, TRANS_SKIP
}
- defer func() {
- if r := recover(); r != nil {
- // before re-throwing the error, append location information to message.
- loc := last.GetLocation()
- if nline := n.GetLine(); nline > 0 {
- loc.Line = nline
- loc.Column = n.GetColumn()
- }
-
- var err error
- rerr, ok := r.(error)
- if ok {
- // NOTE: gotuna/gorilla expects error exceptions.
- err = errors.Wrap(rerr, loc.String())
- } else {
- // NOTE: gotuna/gorilla expects error exceptions.
- err = fmt.Errorf("%s: %v", loc.String(), r)
- }
-
- // Re-throw the error after wrapping it with the preprocessing stack information.
- panic(&PreprocessError{
- err: err,
- stack: stack,
- })
- }
- }()
+ defer doRecover(stack, n)
if debug {
debug.Printf("Preprocess %s (%v) stage:%v\n", n.String(), reflect.TypeOf(n), stage)
}
@@ -540,7 +551,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node {
// TRANS_BLOCK -----------------------
case *IfCaseStmt:
- pushInitRealBlockAndCopy(n, &last, &stack)
+ pushInitBlockAndCopy(n, &last, &stack)
// parent if statement.
ifs := ns[len(ns)-1].(*IfStmt)
// anything declared in ifs are copied.
@@ -624,7 +635,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node {
} else {
// create a hidden var with leading dot.
// NOTE: document somewhere.
- rn := fmt.Sprintf(".res_%d", i)
+ rn := fmt.Sprintf("%s%d", hiddenResultVariable, i)
last.Define(Name(rn), anyValue(rf.Type))
}
}
@@ -656,7 +667,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node {
// TRANS_BLOCK -----------------------
case *SwitchClauseStmt:
- pushInitRealBlockAndCopy(n, &last, &stack)
+ pushInitBlockAndCopy(n, &last, &stack)
// parent switch statement.
ss := ns[len(ns)-1].(*SwitchStmt)
// anything declared in ss are copied,
@@ -723,12 +734,8 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node {
// TRANS_BLOCK -----------------------
case *FuncDecl:
// retrieve cached function type.
+ // the type and receiver are already set in predefineNow.
ft := getType(&n.Type).(*FuncType)
- if n.IsMethod {
- // recv/type set @ predefineNow().
- } else {
- // type set @ predefineNow().
- }
// push func body block.
pushInitBlock(n, &last, &stack)
@@ -750,7 +757,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node {
last.Define(rf.Name, anyValue(rf.Type))
} else {
// create a hidden var with leading dot.
- rn := fmt.Sprintf(".res_%d", i)
+ rn := fmt.Sprintf("%s%d", hiddenResultVariable, i)
last.Define(Name(rn), anyValue(rf.Type))
}
}
@@ -869,15 +876,23 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node {
// in evalStaticType(store,).
n.SetAttribute(ATTR_PREPROCESSED, true)
- // -There is still work to be done while leaving, but
- // once the logic of that is done, we will have to
- // perform additionally deferred logic that is best
- // handled with orthogonal switch conditions.
- // -For example, while leaving nodes w/
- // TRANS_COMPOSITE_TYPE, (regardless of whether name or
- // literal), any elided type names are inserted. (This
- // works because the transcriber leaves the composite
- // type before entering the kv elements.)
+ // Defer pop block from stack.
+ // NOTE: DO NOT USE TRANS_SKIP WITHIN BLOCK
+ // NODES, AS TRANS_LEAVE WILL BE SKIPPED; OR
+ // POP BLOCK YOURSELF.
+ defer func() {
+ switch n.(type) {
+ case BlockNode:
+ stack = stack[:len(stack)-1]
+ last = stack[len(stack)-1]
+ }
+ }()
+
+ // While leaving nodes w/ TRANS_COMPOSITE_TYPE,
+ // (regardless of whether name or literal), any elided
+ // type names are inserted. (This works because the
+ // transcriber leaves the composite type before
+ // entering the kv elements.)
defer func() {
switch ftype {
// TRANS_LEAVE (deferred)---------
@@ -963,6 +978,10 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node {
if ftype == TRANS_ASSIGN_LHS {
as := ns[len(ns)-1].(*AssignStmt)
fillNameExprPath(last, n, as.Op == DEFINE)
+ return n, TRANS_CONTINUE
+ } else if ftype == TRANS_VAR_NAME {
+ fillNameExprPath(last, n, true)
+ return n, TRANS_CONTINUE
} else {
fillNameExprPath(last, n, false)
}
@@ -1025,7 +1044,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node {
isShift := n.Op == SHL || n.Op == SHR
if isShift {
// check LHS type compatibility
- n.checkShiftLhs(lt)
+ n.assertShiftExprCompatible1(store, last, lt, rt)
// checkOrConvert RHS
if baseOf(rt) != UintType {
// convert n.Right to (gno) uint type,
@@ -1036,6 +1055,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node {
Op: n.Op,
Right: rn,
}
+ n2.Right.SetAttribute(ATTR_SHIFT_RHS, true)
resn := Preprocess(store, last, n2)
return resn, TRANS_CONTINUE
}
@@ -1097,12 +1117,34 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node {
// NOTE: binary operations are always computed in
// gno, never with reflect.
} else {
- // convert n.Left to right type.
- checkOrConvertType(store, last, &n.Left, rt, false)
+ // right is untyped const, left is not const, typed/untyped
+ checkUntypedShiftExpr := func(x Expr) {
+ if bx, ok := x.(*BinaryExpr); ok {
+ slt := evalStaticTypeOf(store, last, bx.Left)
+ if bx.Op == SHL || bx.Op == SHR {
+ srt := evalStaticTypeOf(store, last, bx.Right)
+ bx.assertShiftExprCompatible1(store, last, slt, srt)
+ }
+ }
+ }
+
+ if !isUntyped(rt) { // right is typed
+ checkOrConvertType(store, last, &n.Left, rt, false)
+ } else {
+ if shouldSwapOnSpecificity(lt, rt) {
+ checkUntypedShiftExpr(n.Right)
+ } else {
+ checkUntypedShiftExpr(n.Left)
+ }
+ }
}
} else if lcx.T == nil { // LHS is nil.
// convert n.Left to typed-nil type.
checkOrConvertType(store, last, &n.Left, rt, false)
+ } else {
+ if isUntyped(rt) {
+ checkOrConvertType(store, last, &n.Right, lt, false)
+ }
}
} else if ric { // right is const, left is not
if isUntyped(rcx.T) {
@@ -1134,12 +1176,33 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node {
// 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)
+ // right is untyped const, left is not const, typed or untyped
+ checkUntypedShiftExpr := func(x Expr) {
+ if bx, ok := x.(*BinaryExpr); ok {
+ if bx.Op == SHL || bx.Op == SHR {
+ srt := evalStaticTypeOf(store, last, bx.Right)
+ bx.assertShiftExprCompatible1(store, last, rt, srt)
+ }
+ }
+ }
+ // both untyped, e.g. 1<>=.
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 {
@@ -2032,6 +2121,10 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node {
// General case: a, b = x, y.
for i, lx := range n.Lhs {
lt := evalStaticTypeOf(store, last, lx)
+ if nt, ok := lt.(*NativeType); ok && nt.Kind() == FuncKind {
+ panic(fmt.Sprintf("cannot assign to %s (neither addressable nor a map index expression)", lx))
+ }
+
// if lt is interface, nothing will happen
checkOrConvertType(store, last, &n.Rhs[i], lt, true)
}
@@ -2043,14 +2136,28 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node {
case *BranchStmt:
switch n.Op {
case BREAK:
- if !isSwitchLabel(ns, n.Label) {
- findBranchLabel(last, n.Label)
+ if n.Label == "" {
+ if !findBreakableNode(ns) {
+ panic("cannot break with no parent loop or switch")
+ }
+ } else {
+ // Make sure that the label exists, either for a switch or a
+ // BranchStmt.
+ if !isSwitchLabel(ns, n.Label) {
+ findBranchLabel(last, n.Label)
+ }
}
case CONTINUE:
- if isSwitchLabel(ns, n.Label) {
- panic(fmt.Sprintf("invalid continue label %q\n", n.Label))
+ if n.Label == "" {
+ if !findContinuableNode(ns) {
+ panic("cannot continue with no parent loop")
+ }
+ } else {
+ if isSwitchLabel(ns, n.Label) {
+ panic(fmt.Sprintf("invalid continue label %q\n", n.Label))
+ }
+ findBranchLabel(last, n.Label)
}
- findBranchLabel(last, n.Label)
case GOTO:
_, depth, index := findGotoLabel(last, n.Label)
n.Depth = depth
@@ -2073,6 +2180,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node {
panic("should not happen")
}
+ // TRANS_LEAVE -----------------------
case *IncDecStmt:
xt := evalStaticTypeOf(store, last, n.X)
n.AssertCompatible(xt)
@@ -2284,10 +2392,15 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node {
vt := evalStaticTypeOf(store, last, vx)
sts[i] = vt
}
- } else {
+ } else { // T is nil, n not const
// convert n.Value to default type.
for i, vx := range n.Values {
- convertIfConst(store, last, vx)
+ if cx, ok := vx.(*ConstExpr); ok {
+ convertConst(store, last, cx, nil)
+ // convertIfConst(store, last, vx)
+ } else {
+ checkOrConvertType(store, last, &vx, nil, false)
+ }
vt := evalStaticTypeOf(store, last, vx)
sts[i] = vt
}
@@ -2397,13 +2510,6 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node {
}
// end type switch statement
// END TRANS_LEAVE -----------------------
-
- // finalization (during leave).
- if _, ok := n.(BlockNode); ok {
- // Pop block.
- stack = stack[:len(stack)-1]
- last = stack[len(stack)-1]
- }
return n, TRANS_CONTINUE
}
@@ -2414,6 +2520,458 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node {
return nn
}
+// Identifies NameExprTypeHeapDefines.
+// Also finds GotoLoopStmts, XXX but probably remove, not needed.
+func findGotoLoopDefines(ctx BlockNode, bn BlockNode) {
+ // create stack of BlockNodes.
+ var stack []BlockNode = make([]BlockNode, 0, 32)
+ var last BlockNode = ctx
+ stack = append(stack, last)
+
+ // iterate over all nodes recursively.
+ _ = Transcribe(bn, func(ns []Node, ftype TransField, index int, n Node, stage TransStage) (Node, TransCtrl) {
+ defer doRecover(stack, n)
+
+ if debug {
+ debug.Printf("findGotoLoopDefines %s (%v) stage:%v\n", n.String(), reflect.TypeOf(n), stage)
+ }
+
+ switch stage {
+ // ----------------------------------------
+ case TRANS_ENTER:
+ return n, TRANS_CONTINUE
+
+ // ----------------------------------------
+ case TRANS_BLOCK:
+ pushInitBlock(n.(BlockNode), &last, &stack)
+ return n, TRANS_CONTINUE
+
+ // ----------------------------------------
+ case TRANS_LEAVE:
+
+ // Defer pop block from stack.
+ // NOTE: DO NOT USE TRANS_SKIP WITHIN BLOCK
+ // NODES, AS TRANS_LEAVE WILL BE SKIPPED; OR
+ // POP BLOCK YOURSELF.
+ defer func() {
+ switch n.(type) {
+ case BlockNode:
+ stack = stack[:len(stack)-1]
+ last = stack[len(stack)-1]
+ }
+ }()
+
+ switch n := n.(type) {
+ case *ForStmt, *RangeStmt:
+ Transcribe(n,
+ func(ns []Node, ftype TransField, index int, n Node, stage TransStage) (Node, TransCtrl) {
+ switch stage {
+ case TRANS_ENTER:
+ switch n := n.(type) {
+ case *FuncLitExpr:
+ // inner funcs.
+ return n, TRANS_SKIP
+ case *FuncDecl:
+ panic("unexpected inner func decl")
+ case *NameExpr:
+ if n.Type == NameExprTypeDefine {
+ n.Type = NameExprTypeHeapDefine
+ }
+ }
+ }
+ return n, TRANS_CONTINUE
+ })
+ case *BranchStmt:
+ switch n.Op {
+ case GOTO:
+ bn, _, _ := findGotoLabel(last, n.Label)
+ // already done in Preprocess:
+ // n.Depth = depth
+ // n.BodyIndex = index
+
+ // NOTE: we must not use line numbers
+ // for logic, as line numbers are not
+ // guaranteed (see checkNodeLinesLocations).
+ // Instead we rely on the transcribe order
+ // and keep track of whether we've seen
+ // the label and goto stmts respectively.
+ //
+ // DOES NOT WORK:
+ // gotoLine := n.GetLine()
+ // if labelLine >= gotoLine {
+ // return n, TRANS_SKIP
+ // }
+ var (
+ label = n.Label
+ labelReached bool
+ origGoto = n
+ )
+
+ // Recurse and mark stmts as ATTR_GOTOLOOP_STMT.
+ // NOTE: ATTR_GOTOLOOP_STMT is not used.
+ Transcribe(bn,
+ func(ns []Node, ftype TransField, index int, n Node, stage TransStage) (Node, TransCtrl) {
+ switch stage {
+ case TRANS_ENTER:
+ // Check to see if label reached.
+ if _, ok := n.(Stmt); ok {
+ // XXX HasLabel
+ if n.GetLabel() == label {
+ labelReached = true
+ }
+ // If goto < label,
+ // then not a goto loop.
+ if n == origGoto && !labelReached {
+ return n, TRANS_EXIT
+ }
+ }
+
+ // If label not reached, continue.
+ if !labelReached {
+ return n, TRANS_CONTINUE
+ }
+
+ // NOTE: called redundantly
+ // for many goto stmts,
+ // idempotenct updates only.
+ switch n := n.(type) {
+ // Skip the body of these:
+ case *FuncLitExpr:
+ if len(ns) > 0 {
+ // inner funcs.
+ return n, TRANS_SKIP
+ }
+ return n, TRANS_CONTINUE
+ case *FuncDecl:
+ if len(ns) > 0 {
+ panic("unexpected inner func decl")
+ }
+ return n, TRANS_CONTINUE
+ // Otherwise mark stmt as gotoloop.
+ case Stmt:
+ // we're done if we
+ // re-encounter origGotoStmtm.
+ if n == origGoto {
+ n.SetAttribute(
+ ATTR_GOTOLOOP_STMT,
+ true)
+ return n, TRANS_EXIT // done
+ }
+ // otherwise set attribute.
+ n.SetAttribute(
+ ATTR_GOTOLOOP_STMT,
+ true)
+ return n, TRANS_CONTINUE
+ // Special case, maybe convert
+ // NameExprTypeDefine to
+ // NameExprTypeHeapDefine.
+ case *NameExpr:
+ if n.Type == NameExprTypeDefine {
+ n.Type = NameExprTypeHeapDefine
+ }
+ }
+ return n, TRANS_CONTINUE
+ }
+ return n, TRANS_CONTINUE
+ })
+ }
+ }
+ return n, TRANS_CONTINUE
+ }
+ return n, TRANS_CONTINUE
+ })
+}
+
+// Find uses of loop names; those names that are defined as loop defines;
+// defines within loops that are used as reference or captured in a closure
+// later. Also happens to adjust the type (but not paths) of such usage.
+// If there is no usage of the &name or as closure capture, a
+// NameExprTypeHeapDefine gets demoted to NameExprTypeDefine in demoteHeapDefines().
+func findLoopUses1(ctx BlockNode, bn BlockNode) {
+ // create stack of BlockNodes.
+ var stack []BlockNode = make([]BlockNode, 0, 32)
+ var last BlockNode = ctx
+ stack = append(stack, last)
+
+ // Iterate over all nodes recursively.
+ _ = Transcribe(bn, func(ns []Node, ftype TransField, index int, n Node, stage TransStage) (Node, TransCtrl) {
+ defer doRecover(stack, n)
+
+ if debug {
+ debug.Printf("findLoopUses1 %s (%v) stage:%v\n", n.String(), reflect.TypeOf(n), stage)
+ }
+
+ switch stage {
+ // ----------------------------------------
+ case TRANS_BLOCK:
+ pushInitBlock(n.(BlockNode), &last, &stack)
+
+ // ----------------------------------------
+ case TRANS_ENTER:
+ switch n := n.(type) {
+ case *NameExpr:
+ // Ignore non-block type paths
+ if n.Path.Type != VPBlock {
+ return n, TRANS_CONTINUE
+ }
+ switch n.Type {
+ case NameExprTypeNormal:
+ // Find the block where name is defined
+ dbn := last.GetBlockNodeForPath(nil, n.Path)
+ // if the name is loop defined,
+ lds, _ := dbn.GetAttribute(ATTR_LOOP_DEFINES).([]Name)
+ if slices.Contains(lds, n.Name) {
+ fle, depth, found := findFirstClosure(stack, dbn)
+ if found {
+ // If across a closure,
+ // mark name as loop used.
+ addAttrHeapUse(dbn, n.Name)
+ // The path must stay same for now,
+ // used later in findLoopUses2.
+ idx := addHeapCapture(dbn, fle, n.Name)
+ // adjust NameExpr type.
+ n.Type = NameExprTypeHeapUse
+ n.Path.Depth = uint8(depth)
+ n.Path.Index = idx
+ } else {
+ if ftype == TRANS_REF_X {
+ // if used as a reference,
+ // mark name as loop used.
+ addAttrHeapUse(dbn, n.Name)
+ // Also adjust NameExpr type.
+ // We could do this later too.
+ n.Type = NameExprTypeHeapUse
+ }
+ }
+ } else {
+ // if the name is not loop defined,
+ // do nothing.
+ }
+ case NameExprTypeDefine:
+ // nothing to do.
+ case NameExprTypeHeapDefine:
+ // Set name in attribute, so later matches
+ // on NameExpr can know that this was loop defined
+ // on this block.
+ setAttrHeapDefine(last, n.Name)
+ case NameExprTypeHeapUse, NameExprTypeHeapClosure:
+ panic("unexpected node type")
+ }
+ }
+ return n, TRANS_CONTINUE
+
+ // ----------------------------------------
+ case TRANS_LEAVE:
+ // Pop block from stack.
+ // NOTE: DO NOT USE TRANS_SKIP WITHIN BLOCK
+ // NODES, AS TRANS_LEAVE WILL BE SKIPPED; OR
+ // POP BLOCK YOURSELF.
+ switch n.(type) {
+ case BlockNode:
+ stack = stack[:len(stack)-1]
+ last = stack[len(stack)-1]
+ }
+
+ return n, TRANS_CONTINUE
+ }
+ return n, TRANS_CONTINUE
+ })
+}
+
+func assertNotHasName(names []Name, name Name) {
+ if slices.Contains(names, name) {
+ panic(fmt.Sprintf("name: %s already contained in names: %v", name, names))
+ }
+}
+
+func setAttrHeapDefine(bn BlockNode, name Name) {
+ bnLDs, _ := bn.GetAttribute(ATTR_LOOP_DEFINES).([]Name)
+ assertNotHasName(bnLDs, name)
+ bnLDs = append(bnLDs, name)
+ bn.SetAttribute(ATTR_LOOP_DEFINES, bnLDs)
+}
+
+func addAttrHeapUse(bn BlockNode, name Name) {
+ bnLUs, _ := bn.GetAttribute(ATTR_LOOP_USES).([]Name)
+ if slices.Contains(bnLUs, name) {
+ return
+ } else {
+ bnLUs = append(bnLUs, name)
+ bn.SetAttribute(ATTR_LOOP_USES, bnLUs)
+ return
+ }
+}
+
+// adds ~name to fle static block and to heap captures atomically.
+func addHeapCapture(dbn BlockNode, fle *FuncLitExpr, name Name) (idx uint16) {
+ for _, ne := range fle.HeapCaptures {
+ if ne.Name == name {
+ // assert ~name also already defined.
+ var ok bool
+ idx, ok = fle.GetLocalIndex("~" + name)
+ if !ok {
+ panic("~name not added to fle atomically")
+ }
+ return // already exists
+ }
+ }
+
+ // define ~name to fle.
+ _, ok := fle.GetLocalIndex("~" + name)
+ if ok {
+ panic("~name already defined in fle")
+ }
+
+ tv := dbn.GetValueRef(nil, name, true)
+ fle.Define("~"+name, tv.Copy(nil))
+
+ // add name to fle.HeapCaptures.
+ vp := fle.GetPathForName(nil, name)
+ vp.Depth -= 1 // minus 1 for fle itself.
+ ne := NameExpr{
+ Path: vp,
+ Name: name,
+ Type: NameExprTypeHeapClosure,
+ }
+ fle.HeapCaptures = append(fle.HeapCaptures, ne)
+
+ // find index after define
+ for i, n := range fle.GetBlockNames() {
+ if n == "~"+name {
+ idx = uint16(i)
+ return
+ }
+ }
+
+ panic("should not happen, idx not found")
+}
+
+// finds the first FuncLitExpr in the stack at or after stop.
+// returns the depth of first closure, 1 if stop itself is a closure,
+// or 0 if not found.
+func findFirstClosure(stack []BlockNode, stop BlockNode) (fle *FuncLitExpr, depth int, found bool) {
+ redundant := 0 // count faux block
+ for i := len(stack) - 1; i >= 0; i-- {
+ stbn := stack[i]
+ switch stbn := stbn.(type) {
+ case *FuncLitExpr:
+ if stbn == stop { // if fle is stopBn, does not count, use last fle
+ return
+ }
+ fle = stbn
+ depth = len(stack) - 1 - redundant - i + 1 // +1 since 1 is lowest.
+ found = true
+ // even if found, continue iteration in case
+ // an earlier *FuncLitExpr is found.
+ case *IfCaseStmt, *SwitchClauseStmt:
+ if stbn == stop {
+ return
+ }
+ redundant++
+ default:
+ if stbn == stop {
+ return
+ }
+ }
+ }
+ panic("stop not found in stack")
+}
+
+// Convert non-loop uses of loop names to NameExprTypeHeapUse.
+// Also, NameExprTypeHeapDefine gets demoted to NameExprTypeDefine if no actual
+// usage was found that warrants a NameExprTypeHeapDefine.
+func findLoopUses2(ctx BlockNode, bn BlockNode) {
+ // create stack of BlockNodes.
+ var stack []BlockNode = make([]BlockNode, 0, 32)
+ var last BlockNode = ctx
+ stack = append(stack, last)
+
+ // Iterate over all nodes recursively.
+ _ = Transcribe(bn, func(ns []Node, ftype TransField, index int, n Node, stage TransStage) (Node, TransCtrl) {
+ defer doRecover(stack, n)
+
+ if debug {
+ debug.Printf("findLoopUses2 %s (%v) stage:%v\n", n.String(), reflect.TypeOf(n), stage)
+ }
+
+ switch stage {
+ // ----------------------------------------
+ case TRANS_BLOCK:
+ pushInitBlock(n.(BlockNode), &last, &stack)
+
+ // ----------------------------------------
+ case TRANS_ENTER:
+ switch n := n.(type) {
+ case *NameExpr:
+ // Ignore non-block type paths
+ if n.Path.Type != VPBlock {
+ return n, TRANS_CONTINUE
+ }
+ switch n.Type {
+ case NameExprTypeNormal:
+ // Find the block where name is defined
+ dbn := last.GetBlockNodeForPath(nil, n.Path)
+ // if the name is loop defined,
+ lds, _ := dbn.GetAttribute(ATTR_LOOP_DEFINES).([]Name)
+ if slices.Contains(lds, n.Name) {
+ // if the name is actually loop used,
+ lus, _ := dbn.GetAttribute(ATTR_LOOP_USES).([]Name)
+ if slices.Contains(lus, n.Name) {
+ // change type finally to HeapUse.
+ n.Type = NameExprTypeHeapUse
+ } else {
+ // else, will be demoted in later clause.
+ }
+ }
+ case NameExprTypeHeapDefine:
+ // Find the block where name is defined
+ dbn := last.GetBlockNodeForPath(nil, n.Path)
+ // if the name is loop defined
+ lds, _ := dbn.GetAttribute(ATTR_LOOP_DEFINES).([]Name)
+ if slices.Contains(lds, n.Name) {
+ // if the name is actually loop used
+ lus, _ := dbn.GetAttribute(ATTR_LOOP_USES).([]Name)
+ if !slices.Contains(lus, n.Name) {
+ // demote type finally to Define.
+ n.Type = NameExprTypeDefine
+ }
+ }
+ }
+ }
+ return n, TRANS_CONTINUE
+
+ // ----------------------------------------
+ case TRANS_LEAVE:
+
+ // Defer pop block from stack.
+ // NOTE: DO NOT USE TRANS_SKIP WITHIN BLOCK
+ // NODES, AS TRANS_LEAVE WILL BE SKIPPED; OR
+ // POP BLOCK YOURSELF.
+ defer func() {
+ switch n.(type) {
+ case BlockNode:
+ stack = stack[:len(stack)-1]
+ last = stack[len(stack)-1]
+ }
+ }()
+
+ switch n := n.(type) {
+ case BlockNode:
+ lds, _ := n.GetAttribute(ATTR_LOOP_DEFINES).([]Name)
+ lus, _ := n.GetAttribute(ATTR_LOOP_USES).([]Name)
+ if len(lds) < len(lus) {
+ panic("defines should be a superset of used-defines")
+ }
+ // no need anymore
+ n.DelAttribute(ATTR_LOOP_USES)
+ n.DelAttribute(ATTR_LOOP_DEFINES)
+ }
+ return n, TRANS_CONTINUE
+ }
+ return n, TRANS_CONTINUE
+ })
+}
+
func isSwitchLabel(ns []Node, label Name) bool {
for {
swch := lastSwitch(ns)
@@ -2432,37 +2990,38 @@ func isSwitchLabel(ns []Node, label Name) bool {
}
// Idempotent.
+// Also makes sure the stack doesn't reach MaxUint8 in length.
func pushInitBlock(bn BlockNode, last *BlockNode, stack *[]BlockNode) {
if !bn.IsInitialized() {
- bn.InitStaticBlock(bn, *last)
+ switch bn.(type) {
+ case *IfCaseStmt, *SwitchClauseStmt:
+ // skip faux block
+ bn.InitStaticBlock(bn, (*last).GetParentNode(nil))
+ default:
+ bn.InitStaticBlock(bn, *last)
+ }
}
if bn.GetStaticBlock().Source != bn {
panic("expected the source of a block node to be itself")
}
*last = bn
*stack = append(*stack, bn)
-}
-
-// like pushInitBlock(), but when the last block is a faux block,
-// namely after SwitchStmt and IfStmt.
-// Idempotent.
-func pushInitRealBlock(bn BlockNode, last *BlockNode, stack *[]BlockNode) {
- if !bn.IsInitialized() {
- bn.InitStaticBlock(bn, (*last).GetParentNode(nil))
- }
- if bn.GetStaticBlock().Source != bn {
- panic("expected the source of a block node to be itself")
+ if len(*stack) >= math.MaxUint8 {
+ panic("block node depth reached maximum MaxUint8")
}
- *last = bn
- *stack = append(*stack, bn)
}
// like pushInitBlock(), but when the last block is a faux block,
// namely after SwitchStmt and IfStmt.
// Not idempotent, as it calls bn.Define with reference to last's TV value slot.
-func pushInitRealBlockAndCopy(bn BlockNode, last *BlockNode, stack *[]BlockNode) {
+func pushInitBlockAndCopy(bn BlockNode, last *BlockNode, stack *[]BlockNode) {
+ if _, ok := bn.(*IfCaseStmt); !ok {
+ if _, ok := bn.(*SwitchClauseStmt); !ok {
+ panic("should not happen")
+ }
+ }
orig := *last
- pushInitRealBlock(bn, last, stack)
+ pushInitBlock(bn, last, stack)
copyFromFauxBlock(bn, orig)
}
@@ -2668,6 +3227,7 @@ func evalConst(store Store, last BlockNode, x Expr) *ConstExpr {
Source: x,
TypedValue: cv,
}
+ cx.SetLine(x.GetLine())
cx.SetAttribute(ATTR_PREPROCESSED, true)
setConstAttrs(cx)
return cx
@@ -2676,6 +3236,7 @@ func evalConst(store Store, last BlockNode, x Expr) *ConstExpr {
func constType(source Expr, t Type) *constTypeExpr {
cx := &constTypeExpr{Source: source}
cx.Type = t
+ cx.SetLine(source.GetLine())
cx.SetAttribute(ATTR_PREPROCESSED, true)
return cx
}
@@ -2711,6 +3272,26 @@ func funcOf(last BlockNode) (BlockNode, *FuncTypeExpr) {
}
}
+func findBreakableNode(ns []Node) bool {
+ for _, n := range ns {
+ switch n.(type) {
+ case *ForStmt, *RangeStmt, *SwitchClauseStmt:
+ return true
+ }
+ }
+ return false
+}
+
+func findContinuableNode(ns []Node) bool {
+ for _, n := range ns {
+ switch n.(type) {
+ case *ForStmt, *RangeStmt:
+ return true
+ }
+ }
+ return false
+}
+
func findBranchLabel(last BlockNode, label Name) (
bn BlockNode, depth uint8, bodyIdx int,
) {
@@ -2843,9 +3424,25 @@ func checkOrConvertType(store Store, last BlockNode, x *Expr, t Type, autoNative
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. recursively.
- checkOrConvertType(store, last, &bx.Left, t, autoNative)
- } else if *x != nil { // XXX if x != nil && t != nil {
+ xt := evalStaticTypeOf(store, last, *x)
+ if debug {
+ debug.Printf("shift, xt: %v, Op: %v, t: %v \n", xt, bx.Op, t)
+ }
+ if isUntyped(xt) {
+ // check assignable first, see: types/shift_b6.gno
+ assertAssignableTo(xt, t, autoNative)
+
+ if t == nil || t.Kind() == InterfaceKind {
+ t = defaultTypeOf(xt)
+ }
+
+ bx.assertShiftExprCompatible2(t)
+ checkOrConvertType(store, last, &bx.Left, t, autoNative)
+ } else {
+ assertAssignableTo(xt, t, autoNative)
+ }
+ return
+ } else if *x != nil {
xt := evalStaticTypeOf(store, last, *x)
if t != nil {
assertAssignableTo(xt, t, autoNative)
@@ -2856,19 +3453,53 @@ func checkOrConvertType(store Store, last BlockNode, x *Expr, t Type, autoNative
switch bx.Op {
case ADD, SUB, MUL, QUO, REM, BAND, BOR, XOR,
BAND_NOT, LAND, LOR:
- // push t into bx.Left and bx.Right
- checkOrConvertType(store, last, &bx.Left, t, autoNative)
- checkOrConvertType(store, last, &bx.Right, t, autoNative)
- return
- case SHL, SHR:
- // push t into bx.Left
- checkOrConvertType(store, last, &bx.Left, t, autoNative)
+ lt := evalStaticTypeOf(store, last, bx.Left)
+ rt := evalStaticTypeOf(store, last, bx.Right)
+ if t != nil {
+ // push t into bx.Left and bx.Right
+ checkOrConvertType(store, last, &bx.Left, t, autoNative)
+ checkOrConvertType(store, last, &bx.Right, t, autoNative)
+ return
+ } else {
+ if shouldSwapOnSpecificity(lt, rt) {
+ // e.g. 1.0<gno type conversion must be registered.
@@ -124,7 +119,6 @@ func NewStore(alloc *Allocator, baseStore, iavlStore store.Store) *defaultStore
// store configuration
pkgGetter: nil,
cacheNativeTypes: make(map[reflect.Type]Type),
- pkgInjector: nil,
nativeStore: nil,
go2gnoStrict: true,
}
@@ -154,7 +148,6 @@ func (ds *defaultStore) BeginTransaction(baseStore, iavlStore store.Store) Trans
// store configuration
pkgGetter: ds.pkgGetter,
cacheNativeTypes: ds.cacheNativeTypes,
- pkgInjector: ds.pkgInjector,
nativeStore: ds.nativeStore,
go2gnoStrict: ds.go2gnoStrict,
@@ -190,10 +183,6 @@ func (transactionStore) ClearCache() {
// panic("Go2GnoType may not be called in a transaction store")
// }
-func (transactionStore) SetPackageInjector(inj PackageInjector) {
- panic("SetPackageInjector may not be called in a transaction store")
-}
-
func (transactionStore) SetNativeStore(ns NativeStore) {
panic("SetNativeStore may not be called in a transaction store")
}
@@ -263,26 +252,6 @@ func (ds *defaultStore) GetPackage(pkgPath string, isImport bool) *PackageValue
rlm := ds.GetPackageRealm(pkgPath)
pv.Realm = rlm
}
- // get package node.
- pl := PackageNodeLocation(pkgPath)
- pn, ok := ds.GetBlockNodeSafe(pl).(*PackageNode)
- if !ok {
- // Do not inject packages from packageGetter
- // that don't have corresponding *PackageNodes.
- } else {
- // Inject natives after load.
- if ds.pkgInjector != nil {
- if pn.HasAttribute(ATTR_INJECTED) {
- // e.g. in checktx or simulate or query.
- pn.PrepareNewValues(pv)
- } else {
- // pv.GetBlock(ds) // preload pv.Block
- ds.pkgInjector(ds, pn)
- pn.SetAttribute(ATTR_INJECTED, true)
- pn.PrepareNewValues(pv)
- }
- }
- }
// Rederive pv.fBlocksMap.
pv.deriveFBlocksMap(ds)
return pv
@@ -303,18 +272,6 @@ func (ds *defaultStore) GetPackage(pkgPath string, isImport bool) *PackageValue
// will get written elsewhere
// later.
ds.cacheObjects[oid] = pv
- // inject natives after init.
- if ds.pkgInjector != nil {
- if pn.HasAttribute(ATTR_INJECTED) {
- // not sure why this would happen.
- panic("should not happen")
- // pn.PrepareNewValues(pv)
- } else {
- ds.pkgInjector(ds, pn)
- pn.SetAttribute(ATTR_INJECTED, true)
- pn.PrepareNewValues(pv)
- }
- }
// cache all types. usually preprocess() sets types,
// but packages gotten from the pkgGetter may skip this step,
// so fill in store.CacheTypes here.
@@ -654,7 +611,7 @@ func (ds *defaultStore) incGetPackageIndexCounter() uint64 {
}
}
-func (ds *defaultStore) AddMemPackage(memPkg *std.MemPackage) {
+func (ds *defaultStore) AddMemPackage(memPkg *gnovm.MemPackage) {
memPkg.Validate() // NOTE: duplicate validation.
ctr := ds.incGetPackageIndexCounter()
idxkey := []byte(backendPackageIndexKey(ctr))
@@ -666,11 +623,11 @@ func (ds *defaultStore) AddMemPackage(memPkg *std.MemPackage) {
// GetMemPackage retrieves the MemPackage at the given path.
// It returns nil if the package could not be found.
-func (ds *defaultStore) GetMemPackage(path string) *std.MemPackage {
+func (ds *defaultStore) GetMemPackage(path string) *gnovm.MemPackage {
return ds.getMemPackage(path, false)
}
-func (ds *defaultStore) getMemPackage(path string, isRetry bool) *std.MemPackage {
+func (ds *defaultStore) getMemPackage(path string, isRetry bool) *gnovm.MemPackage {
pathkey := []byte(backendPackagePathKey(path))
bz := ds.iavlStore.Get(pathkey)
if bz == nil {
@@ -687,7 +644,7 @@ func (ds *defaultStore) getMemPackage(path string, isRetry bool) *std.MemPackage
return nil
}
- var memPkg *std.MemPackage
+ var memPkg *gnovm.MemPackage
amino.MustUnmarshal(bz, &memPkg)
return memPkg
}
@@ -695,7 +652,7 @@ func (ds *defaultStore) getMemPackage(path string, isRetry bool) *std.MemPackage
// GetMemFile retrieves the MemFile with the given name, contained in the
// MemPackage at the given path. It returns nil if the file or the package
// do not exist.
-func (ds *defaultStore) GetMemFile(path string, name string) *std.MemFile {
+func (ds *defaultStore) GetMemFile(path string, name string) *gnovm.MemFile {
memPkg := ds.GetMemPackage(path)
if memPkg == nil {
return nil
@@ -704,7 +661,7 @@ func (ds *defaultStore) GetMemFile(path string, name string) *std.MemFile {
return memFile
}
-func (ds *defaultStore) IterMemPackage() <-chan *std.MemPackage {
+func (ds *defaultStore) IterMemPackage() <-chan *gnovm.MemPackage {
ctrkey := []byte(backendPackageIndexCtrKey())
ctrbz := ds.baseStore.Get(ctrkey)
if ctrbz == nil {
@@ -714,7 +671,7 @@ func (ds *defaultStore) IterMemPackage() <-chan *std.MemPackage {
if err != nil {
panic(err)
}
- ch := make(chan *std.MemPackage, 0)
+ ch := make(chan *gnovm.MemPackage, 0)
go func() {
for i := uint64(1); i <= uint64(ctr); i++ {
idxkey := []byte(backendPackageIndexKey(i))
@@ -742,10 +699,6 @@ func (ds *defaultStore) ClearObjectCache() {
ds.SetCachePackage(Uverse())
}
-func (ds *defaultStore) SetPackageInjector(inj PackageInjector) {
- ds.pkgInjector = inj
-}
-
func (ds *defaultStore) SetNativeStore(ns NativeStore) {
ds.nativeStore = ns
}
diff --git a/gnovm/pkg/gnolang/store_test.go b/gnovm/pkg/gnolang/store_test.go
index 8114291d1b6..40f84b65375 100644
--- a/gnovm/pkg/gnolang/store_test.go
+++ b/gnovm/pkg/gnolang/store_test.go
@@ -4,8 +4,8 @@ import (
"io"
"testing"
+ "github.com/gnolang/gno/gnovm"
"github.com/gnolang/gno/tm2/pkg/db/memdb"
- "github.com/gnolang/gno/tm2/pkg/std"
"github.com/gnolang/gno/tm2/pkg/store/dbadapter"
storetypes "github.com/gnolang/gno/tm2/pkg/store/types"
"github.com/stretchr/testify/assert"
@@ -23,10 +23,10 @@ func TestTransactionStore(t *testing.T) {
Store: txSt,
Output: io.Discard,
})
- _, pv := m.RunMemPackage(&std.MemPackage{
+ _, pv := m.RunMemPackage(&gnovm.MemPackage{
Name: "hello",
Path: "hello",
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{Name: "hello.gno", Body: "package hello; func main() { println(A(11)); }; type A int"},
},
}, true)
@@ -59,7 +59,6 @@ func TestTransactionStore_blockedMethods(t *testing.T) {
// only be changed in the root store.
assert.Panics(t, func() { transactionStore{}.SetPackageGetter(nil) })
assert.Panics(t, func() { transactionStore{}.ClearCache() })
- assert.Panics(t, func() { transactionStore{}.SetPackageInjector(nil) })
assert.Panics(t, func() { transactionStore{}.SetNativeStore(nil) })
assert.Panics(t, func() { transactionStore{}.SetStrictGo2GnoMapping(false) })
}
@@ -76,10 +75,10 @@ func TestCopyFromCachedStore(t *testing.T) {
Name: "Reader",
Base: BoolType,
})
- cachedStore.AddMemPackage(&std.MemPackage{
+ cachedStore.AddMemPackage(&gnovm.MemPackage{
Name: "math",
Path: "math",
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{Name: "math.gno", Body: "package math"},
},
})
diff --git a/gnovm/pkg/gnolang/transcribe.go b/gnovm/pkg/gnolang/transcribe.go
index 28e97ff2b5b..dab539a8707 100644
--- a/gnovm/pkg/gnolang/transcribe.go
+++ b/gnovm/pkg/gnolang/transcribe.go
@@ -47,6 +47,7 @@ const (
TRANS_COMPOSITE_KEY
TRANS_COMPOSITE_VALUE
TRANS_FUNCLIT_TYPE
+ TRANS_FUNCLIT_HEAP_CAPTURE
TRANS_FUNCLIT_BODY
TRANS_FIELDTYPE_TYPE
TRANS_FIELDTYPE_TAG
@@ -100,6 +101,7 @@ const (
TRANS_IMPORT_PATH
TRANS_CONST_TYPE
TRANS_CONST_VALUE
+ TRANS_VAR_NAME // XXX stringer
TRANS_VAR_TYPE
TRANS_VAR_VALUE
TRANS_TYPE_TYPE
@@ -112,6 +114,7 @@ const (
// ENTER,CHILDS1,[BLOCK,CHILDS2]?,LEAVE sequence for that node,
// i.e. skipping (the rest of) it;
// - TRANS_BREAK to break out of looping in CHILDS1 or CHILDS2,
+// but still perform TRANS_LEAVE.
// - TRANS_EXIT to stop traversing altogether.
//
// Do not mutate ns.
@@ -264,6 +267,14 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc
if isStopOrSkip(nc, c) {
return
}
+ for idx := range cnn.HeapCaptures {
+ cnn.HeapCaptures[idx] = *(transcribe(t, nns, TRANS_FUNCLIT_HEAP_CAPTURE, idx, &cnn.HeapCaptures[idx], &c).(*NameExpr))
+ if isBreak(c) {
+ break
+ } else if isStopOrSkip(nc, c) {
+ return
+ }
+ }
cnn2, c2 := t(ns, ftype, index, cnn, TRANS_BLOCK)
if isStopOrSkip(nc, c2) {
nn = cnn2
@@ -692,6 +703,10 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc
return
}
}
+ // XXX consider RHS, LHS, RHS, LHS, ... order.
+ for idx := range cnn.NameExprs {
+ cnn.NameExprs[idx] = *(transcribe(t, nns, TRANS_VAR_NAME, idx, &cnn.NameExprs[idx], &c).(*NameExpr))
+ }
for idx := range cnn.Values {
cnn.Values[idx] = transcribe(t, nns, TRANS_VAR_VALUE, idx, cnn.Values[idx], &c).(Expr)
if isBreak(c) {
diff --git a/gnovm/pkg/gnolang/transfield_string.go b/gnovm/pkg/gnolang/transfield_string.go
index 587ca7a7654..31afcf2be0d 100644
--- a/gnovm/pkg/gnolang/transfield_string.go
+++ b/gnovm/pkg/gnolang/transfield_string.go
@@ -29,68 +29,70 @@ func _() {
_ = x[TRANS_COMPOSITE_KEY-18]
_ = x[TRANS_COMPOSITE_VALUE-19]
_ = x[TRANS_FUNCLIT_TYPE-20]
- _ = x[TRANS_FUNCLIT_BODY-21]
- _ = x[TRANS_FIELDTYPE_TYPE-22]
- _ = x[TRANS_FIELDTYPE_TAG-23]
- _ = x[TRANS_ARRAYTYPE_LEN-24]
- _ = x[TRANS_ARRAYTYPE_ELT-25]
- _ = x[TRANS_SLICETYPE_ELT-26]
- _ = x[TRANS_INTERFACETYPE_METHOD-27]
- _ = x[TRANS_CHANTYPE_VALUE-28]
- _ = x[TRANS_FUNCTYPE_PARAM-29]
- _ = x[TRANS_FUNCTYPE_RESULT-30]
- _ = x[TRANS_MAPTYPE_KEY-31]
- _ = x[TRANS_MAPTYPE_VALUE-32]
- _ = x[TRANS_STRUCTTYPE_FIELD-33]
- _ = x[TRANS_MAYBENATIVETYPE_TYPE-34]
- _ = x[TRANS_ASSIGN_LHS-35]
- _ = x[TRANS_ASSIGN_RHS-36]
- _ = x[TRANS_BLOCK_BODY-37]
- _ = x[TRANS_DECL_BODY-38]
- _ = x[TRANS_DEFER_CALL-39]
- _ = x[TRANS_EXPR_X-40]
- _ = x[TRANS_FOR_INIT-41]
- _ = x[TRANS_FOR_COND-42]
- _ = x[TRANS_FOR_POST-43]
- _ = x[TRANS_FOR_BODY-44]
- _ = x[TRANS_GO_CALL-45]
- _ = x[TRANS_IF_INIT-46]
- _ = x[TRANS_IF_COND-47]
- _ = x[TRANS_IF_BODY-48]
- _ = x[TRANS_IF_ELSE-49]
- _ = x[TRANS_IF_CASE_BODY-50]
- _ = x[TRANS_INCDEC_X-51]
- _ = x[TRANS_RANGE_X-52]
- _ = x[TRANS_RANGE_KEY-53]
- _ = x[TRANS_RANGE_VALUE-54]
- _ = x[TRANS_RANGE_BODY-55]
- _ = x[TRANS_RETURN_RESULT-56]
- _ = x[TRANS_PANIC_EXCEPTION-57]
- _ = x[TRANS_SELECT_CASE-58]
- _ = x[TRANS_SELECTCASE_COMM-59]
- _ = x[TRANS_SELECTCASE_BODY-60]
- _ = x[TRANS_SEND_CHAN-61]
- _ = x[TRANS_SEND_VALUE-62]
- _ = x[TRANS_SWITCH_INIT-63]
- _ = x[TRANS_SWITCH_X-64]
- _ = x[TRANS_SWITCH_CASE-65]
- _ = x[TRANS_SWITCHCASE_CASE-66]
- _ = x[TRANS_SWITCHCASE_BODY-67]
- _ = x[TRANS_FUNC_RECV-68]
- _ = x[TRANS_FUNC_TYPE-69]
- _ = x[TRANS_FUNC_BODY-70]
- _ = x[TRANS_IMPORT_PATH-71]
- _ = x[TRANS_CONST_TYPE-72]
- _ = x[TRANS_CONST_VALUE-73]
- _ = x[TRANS_VAR_TYPE-74]
- _ = x[TRANS_VAR_VALUE-75]
- _ = x[TRANS_TYPE_TYPE-76]
- _ = x[TRANS_FILE_BODY-77]
+ _ = x[TRANS_FUNCLIT_HEAP_CAPTURE-21]
+ _ = x[TRANS_FUNCLIT_BODY-22]
+ _ = x[TRANS_FIELDTYPE_TYPE-23]
+ _ = x[TRANS_FIELDTYPE_TAG-24]
+ _ = x[TRANS_ARRAYTYPE_LEN-25]
+ _ = x[TRANS_ARRAYTYPE_ELT-26]
+ _ = x[TRANS_SLICETYPE_ELT-27]
+ _ = x[TRANS_INTERFACETYPE_METHOD-28]
+ _ = x[TRANS_CHANTYPE_VALUE-29]
+ _ = x[TRANS_FUNCTYPE_PARAM-30]
+ _ = x[TRANS_FUNCTYPE_RESULT-31]
+ _ = x[TRANS_MAPTYPE_KEY-32]
+ _ = x[TRANS_MAPTYPE_VALUE-33]
+ _ = x[TRANS_STRUCTTYPE_FIELD-34]
+ _ = x[TRANS_MAYBENATIVETYPE_TYPE-35]
+ _ = x[TRANS_ASSIGN_LHS-36]
+ _ = x[TRANS_ASSIGN_RHS-37]
+ _ = x[TRANS_BLOCK_BODY-38]
+ _ = x[TRANS_DECL_BODY-39]
+ _ = x[TRANS_DEFER_CALL-40]
+ _ = x[TRANS_EXPR_X-41]
+ _ = x[TRANS_FOR_INIT-42]
+ _ = x[TRANS_FOR_COND-43]
+ _ = x[TRANS_FOR_POST-44]
+ _ = x[TRANS_FOR_BODY-45]
+ _ = x[TRANS_GO_CALL-46]
+ _ = x[TRANS_IF_INIT-47]
+ _ = x[TRANS_IF_COND-48]
+ _ = x[TRANS_IF_BODY-49]
+ _ = x[TRANS_IF_ELSE-50]
+ _ = x[TRANS_IF_CASE_BODY-51]
+ _ = x[TRANS_INCDEC_X-52]
+ _ = x[TRANS_RANGE_X-53]
+ _ = x[TRANS_RANGE_KEY-54]
+ _ = x[TRANS_RANGE_VALUE-55]
+ _ = x[TRANS_RANGE_BODY-56]
+ _ = x[TRANS_RETURN_RESULT-57]
+ _ = x[TRANS_PANIC_EXCEPTION-58]
+ _ = x[TRANS_SELECT_CASE-59]
+ _ = x[TRANS_SELECTCASE_COMM-60]
+ _ = x[TRANS_SELECTCASE_BODY-61]
+ _ = x[TRANS_SEND_CHAN-62]
+ _ = x[TRANS_SEND_VALUE-63]
+ _ = x[TRANS_SWITCH_INIT-64]
+ _ = x[TRANS_SWITCH_X-65]
+ _ = x[TRANS_SWITCH_CASE-66]
+ _ = x[TRANS_SWITCHCASE_CASE-67]
+ _ = x[TRANS_SWITCHCASE_BODY-68]
+ _ = x[TRANS_FUNC_RECV-69]
+ _ = x[TRANS_FUNC_TYPE-70]
+ _ = x[TRANS_FUNC_BODY-71]
+ _ = x[TRANS_IMPORT_PATH-72]
+ _ = x[TRANS_CONST_TYPE-73]
+ _ = x[TRANS_CONST_VALUE-74]
+ _ = x[TRANS_VAR_NAME-75]
+ _ = x[TRANS_VAR_TYPE-76]
+ _ = x[TRANS_VAR_VALUE-77]
+ _ = x[TRANS_TYPE_TYPE-78]
+ _ = x[TRANS_FILE_BODY-79]
}
-const _TransField_name = "TRANS_ROOTTRANS_BINARY_LEFTTRANS_BINARY_RIGHTTRANS_CALL_FUNCTRANS_CALL_ARGTRANS_INDEX_XTRANS_INDEX_INDEXTRANS_SELECTOR_XTRANS_SLICE_XTRANS_SLICE_LOWTRANS_SLICE_HIGHTRANS_SLICE_MAXTRANS_STAR_XTRANS_REF_XTRANS_TYPEASSERT_XTRANS_TYPEASSERT_TYPETRANS_UNARY_XTRANS_COMPOSITE_TYPETRANS_COMPOSITE_KEYTRANS_COMPOSITE_VALUETRANS_FUNCLIT_TYPETRANS_FUNCLIT_BODYTRANS_FIELDTYPE_TYPETRANS_FIELDTYPE_TAGTRANS_ARRAYTYPE_LENTRANS_ARRAYTYPE_ELTTRANS_SLICETYPE_ELTTRANS_INTERFACETYPE_METHODTRANS_CHANTYPE_VALUETRANS_FUNCTYPE_PARAMTRANS_FUNCTYPE_RESULTTRANS_MAPTYPE_KEYTRANS_MAPTYPE_VALUETRANS_STRUCTTYPE_FIELDTRANS_MAYBENATIVETYPE_TYPETRANS_ASSIGN_LHSTRANS_ASSIGN_RHSTRANS_BLOCK_BODYTRANS_DECL_BODYTRANS_DEFER_CALLTRANS_EXPR_XTRANS_FOR_INITTRANS_FOR_CONDTRANS_FOR_POSTTRANS_FOR_BODYTRANS_GO_CALLTRANS_IF_INITTRANS_IF_CONDTRANS_IF_BODYTRANS_IF_ELSETRANS_IF_CASE_BODYTRANS_INCDEC_XTRANS_RANGE_XTRANS_RANGE_KEYTRANS_RANGE_VALUETRANS_RANGE_BODYTRANS_RETURN_RESULTTRANS_PANIC_EXCEPTIONTRANS_SELECT_CASETRANS_SELECTCASE_COMMTRANS_SELECTCASE_BODYTRANS_SEND_CHANTRANS_SEND_VALUETRANS_SWITCH_INITTRANS_SWITCH_XTRANS_SWITCH_CASETRANS_SWITCHCASE_CASETRANS_SWITCHCASE_BODYTRANS_FUNC_RECVTRANS_FUNC_TYPETRANS_FUNC_BODYTRANS_IMPORT_PATHTRANS_CONST_TYPETRANS_CONST_VALUETRANS_VAR_TYPETRANS_VAR_VALUETRANS_TYPE_TYPETRANS_FILE_BODY"
+const _TransField_name = "TRANS_ROOTTRANS_BINARY_LEFTTRANS_BINARY_RIGHTTRANS_CALL_FUNCTRANS_CALL_ARGTRANS_INDEX_XTRANS_INDEX_INDEXTRANS_SELECTOR_XTRANS_SLICE_XTRANS_SLICE_LOWTRANS_SLICE_HIGHTRANS_SLICE_MAXTRANS_STAR_XTRANS_REF_XTRANS_TYPEASSERT_XTRANS_TYPEASSERT_TYPETRANS_UNARY_XTRANS_COMPOSITE_TYPETRANS_COMPOSITE_KEYTRANS_COMPOSITE_VALUETRANS_FUNCLIT_TYPETRANS_FUNCLIT_HEAP_CAPTURETRANS_FUNCLIT_BODYTRANS_FIELDTYPE_TYPETRANS_FIELDTYPE_TAGTRANS_ARRAYTYPE_LENTRANS_ARRAYTYPE_ELTTRANS_SLICETYPE_ELTTRANS_INTERFACETYPE_METHODTRANS_CHANTYPE_VALUETRANS_FUNCTYPE_PARAMTRANS_FUNCTYPE_RESULTTRANS_MAPTYPE_KEYTRANS_MAPTYPE_VALUETRANS_STRUCTTYPE_FIELDTRANS_MAYBENATIVETYPE_TYPETRANS_ASSIGN_LHSTRANS_ASSIGN_RHSTRANS_BLOCK_BODYTRANS_DECL_BODYTRANS_DEFER_CALLTRANS_EXPR_XTRANS_FOR_INITTRANS_FOR_CONDTRANS_FOR_POSTTRANS_FOR_BODYTRANS_GO_CALLTRANS_IF_INITTRANS_IF_CONDTRANS_IF_BODYTRANS_IF_ELSETRANS_IF_CASE_BODYTRANS_INCDEC_XTRANS_RANGE_XTRANS_RANGE_KEYTRANS_RANGE_VALUETRANS_RANGE_BODYTRANS_RETURN_RESULTTRANS_PANIC_EXCEPTIONTRANS_SELECT_CASETRANS_SELECTCASE_COMMTRANS_SELECTCASE_BODYTRANS_SEND_CHANTRANS_SEND_VALUETRANS_SWITCH_INITTRANS_SWITCH_XTRANS_SWITCH_CASETRANS_SWITCHCASE_CASETRANS_SWITCHCASE_BODYTRANS_FUNC_RECVTRANS_FUNC_TYPETRANS_FUNC_BODYTRANS_IMPORT_PATHTRANS_CONST_TYPETRANS_CONST_VALUETRANS_VAR_NAMETRANS_VAR_TYPETRANS_VAR_VALUETRANS_TYPE_TYPETRANS_FILE_BODY"
-var _TransField_index = [...]uint16{0, 10, 27, 45, 60, 74, 87, 104, 120, 133, 148, 164, 179, 191, 202, 220, 241, 254, 274, 293, 314, 332, 350, 370, 389, 408, 427, 446, 472, 492, 512, 533, 550, 569, 591, 617, 633, 649, 665, 680, 696, 708, 722, 736, 750, 764, 777, 790, 803, 816, 829, 847, 861, 874, 889, 906, 922, 941, 962, 979, 1000, 1021, 1036, 1052, 1069, 1083, 1100, 1121, 1142, 1157, 1172, 1187, 1204, 1220, 1237, 1251, 1266, 1281, 1296}
+var _TransField_index = [...]uint16{0, 10, 27, 45, 60, 74, 87, 104, 120, 133, 148, 164, 179, 191, 202, 220, 241, 254, 274, 293, 314, 332, 358, 376, 396, 415, 434, 453, 472, 498, 518, 538, 559, 576, 595, 617, 643, 659, 675, 691, 706, 722, 734, 748, 762, 776, 790, 803, 816, 829, 842, 855, 873, 887, 900, 915, 932, 948, 967, 988, 1005, 1026, 1047, 1062, 1078, 1095, 1109, 1126, 1147, 1168, 1183, 1198, 1213, 1230, 1246, 1263, 1277, 1291, 1306, 1321, 1336}
func (i TransField) String() string {
if i >= TransField(len(_TransField_index)-1) {
diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go
index c3c638d1363..c62d67375ee 100644
--- a/gnovm/pkg/gnolang/type_check.go
+++ b/gnovm/pkg/gnolang/type_check.go
@@ -47,8 +47,8 @@ var (
MUL_ASSIGN: isNumeric,
QUO_ASSIGN: isNumeric,
REM_ASSIGN: isIntNum,
- SHL_ASSIGN: isNumeric,
- SHR_ASSIGN: isNumeric,
+ SHL_ASSIGN: isIntNum,
+ SHR_ASSIGN: isIntNum,
BAND_ASSIGN: isIntNum,
XOR_ASSIGN: isIntNum,
BOR_ASSIGN: isIntNum,
@@ -583,10 +583,73 @@ func checkAssignableTo(xt, dt Type, autoNative bool) error {
}
// ===========================================================
-func (x *BinaryExpr) checkShiftLhs(dt Type) {
+// this check happens while trans_leave binary expression.
+// it checks if type of RHS is valid.
+// Check part of the LHS to ensure it is of a numeric type.
+// More stringent checks will be performed in the subsequent stage in
+// assertShiftExprCompatible2.
+func (x *BinaryExpr) assertShiftExprCompatible1(store Store, last BlockNode, lt, rt Type) {
+ // check rhs type
+ if rt == nil {
+ panic(fmt.Sprintf("cannot convert %v to type uint", x.Right))
+ }
+
+ lcx, lic := x.Left.(*ConstExpr)
+ _, ric := x.Right.(*ConstExpr)
+ // step1, check RHS type
+ // special case when rhs is not integer
+ if !isNumeric(rt) {
+ panic(fmt.Sprintf("cannot convert %v to type uint", x.Right))
+ }
+ if !isIntNum(rt) {
+ var isIntValue bool
+ // special case for num like 1.0, it will be converted to uint later
+ if ric {
+ rv := evalConst(store, last, x.Right)
+ if bd, ok := rv.V.(BigdecValue); ok {
+ if isInteger(bd.V) {
+ isIntValue = true
+ }
+ }
+ }
+ if !isIntValue {
+ panic(fmt.Sprintf("invalid operation: invalid shift count: %v", x.Right))
+ }
+ } else if ric { // is integer, check negative
+ rv := evalConst(store, last, x.Right)
+ if rv.Sign() < 0 {
+ panic(fmt.Sprintf("invalid operation: negative shift count: %v", x.Right))
+ }
+ }
+
+ // step2, check lhs 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)))
+ if checker(lt) { // check pass
+ return
+ }
+ // special case for 1.0
+ if lt == UntypedBigdecType {
+ // 1.0 << 1
+ if lic && ric {
+ convertConst(store, last, lcx, UntypedBigintType)
+ return
+ }
+ }
+ // not const, e.g. 1.0 << x, see types/shift_d5.gno
+ if isNumeric(lt) {
+ return
+ }
+ panic(fmt.Sprintf("operator %s not defined on: %v", x.Op.TokenString(), kindString(lt)))
+ }
+ panic(fmt.Sprintf("checker for %s does not exist", x.Op))
+}
+
+// used in checkOrConvertType, only check lhs type
+func (x *BinaryExpr) assertShiftExprCompatible2(t Type) {
+ // check lhs type
+ if checker, ok := binaryChecker[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))
@@ -654,7 +717,7 @@ func (x *BinaryExpr) AssertCompatible(lt, rt Type) {
// special case of zero divisor
if isQuoOrRem(x.Op) {
if rcx, ok := x.Right.(*ConstExpr); ok {
- if rcx.TypedValue.isZero() {
+ if rcx.TypedValue.Sign() == 0 {
panic("invalid operation: division by zero")
}
}
@@ -793,8 +856,8 @@ func (x *AssignStmt) AssertCompatible(store Store, last BlockNode) {
if x.Op == ASSIGN {
// check assignable
for i, lx := range x.Lhs {
+ assertValidAssignLhs(store, last, lx)
if !isBlankIdentifier(lx) {
- assertValidAssignLhs(store, last, lx)
lxt := evalStaticTypeOf(store, last, lx)
assertAssignableTo(cft.Results[i].Type, lxt, false)
}
@@ -806,15 +869,16 @@ func (x *AssignStmt) AssertCompatible(store Store, last BlockNode) {
panic("should not happen")
}
if x.Op == ASSIGN {
- // check assignable to first value
+ // check first value
+ assertValidAssignLhs(store, last, x.Lhs[0])
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)
}
+ // check second value
+ assertValidAssignLhs(store, last, x.Lhs[1])
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))
@@ -827,8 +891,8 @@ func (x *AssignStmt) AssertCompatible(store Store, last BlockNode) {
panic("should not happen")
}
if x.Op == ASSIGN {
+ assertValidAssignLhs(store, last, x.Lhs[0])
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)
@@ -844,8 +908,9 @@ func (x *AssignStmt) AssertCompatible(store Store, last BlockNode) {
}
}
}
+
+ assertValidAssignLhs(store, last, x.Lhs[1])
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))
@@ -871,30 +936,38 @@ func (x *AssignStmt) AssertCompatible(store Store, last BlockNode) {
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])
+ lt := evalStaticTypeOf(store, last, x.Lhs[0])
+ rt := evalStaticTypeOf(store, last, x.Rhs[0])
- 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))
- }
+ 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))
+ case SHL_ASSIGN, SHR_ASSIGN:
+ if !isIntNum(rt) {
+ panic(fmt.Sprintf("invalid operation: invalid shift count: %v", x.Rhs[0]))
+ }
+ _, ric := x.Rhs[0].(*ConstExpr)
+ // check negative
+ if ric {
+ rv := evalConst(store, last, x.Rhs[0])
+ rv.AssertNonNegative("invalid operation: negative shift count")
+ }
+ default:
+ // do nothing
}
+ } else {
+ panic(fmt.Sprintf("checker for %s does not exist", x.Op))
}
}
}
@@ -903,7 +976,17 @@ func (x *AssignStmt) AssertCompatible(store Store, last BlockNode) {
func assertValidAssignLhs(store Store, last BlockNode, lx Expr) {
shouldPanic := true
switch clx := lx.(type) {
- case *NameExpr, *StarExpr, *SelectorExpr:
+ case *NameExpr:
+ if clx.Name == blankIdentifier {
+ shouldPanic = false
+ } else if clx.Path.Type == VPUverse {
+ panic(fmt.Sprintf("cannot assign to uverse %v", clx.Name))
+ } else if last.GetIsConst(store, clx.Name) {
+ panic(fmt.Sprintf("cannot assign to const %v", clx.Name))
+ } else {
+ shouldPanic = false
+ }
+ case *StarExpr, *SelectorExpr:
shouldPanic = false
case *IndexExpr:
xt := evalStaticTypeOf(store, last, clx.X)
@@ -981,7 +1064,7 @@ func isComparison(op Word) bool {
// it returns true, indicating a swap is needed.
func shouldSwapOnSpecificity(t1, t2 Type) bool {
// check nil
- if t1 == nil { // see test file 0f46
+ if t1 == nil {
return false // also with both nil
} else if t2 == nil {
return true
@@ -1020,7 +1103,7 @@ func shouldSwapOnSpecificity(t1, t2 Type) bool {
func isBlankIdentifier(x Expr) bool {
if nx, ok := x.(*NameExpr); ok {
- return nx.Name == "_"
+ return nx.Name == blankIdentifier
}
return false
}
diff --git a/gnovm/pkg/gnolang/types.go b/gnovm/pkg/gnolang/types.go
index b43f623ea99..eedb71ffa73 100644
--- a/gnovm/pkg/gnolang/types.go
+++ b/gnovm/pkg/gnolang/types.go
@@ -69,6 +69,7 @@ func (*PackageType) assertType() {}
func (*ChanType) assertType() {}
func (*NativeType) assertType() {}
func (blockType) assertType() {}
+func (heapItemType) assertType() {}
func (*tupleType) assertType() {}
func (RefType) assertType() {}
func (MaybeNativeType) assertType() {}
@@ -1964,6 +1965,35 @@ func (bt blockType) IsNamed() bool {
panic("blockType has no property called named")
}
+// ----------------------------------------
+// heapItemType
+
+type heapItemType struct{} // no data
+
+func (bt heapItemType) Kind() Kind {
+ return HeapItemKind
+}
+
+func (bt heapItemType) TypeID() TypeID {
+ return typeid("heapitem")
+}
+
+func (bt heapItemType) String() string {
+ return "heapitem"
+}
+
+func (bt heapItemType) Elem() Type {
+ panic("heapItemType has no elem type")
+}
+
+func (bt heapItemType) GetPkgPath() string {
+ panic("heapItemType has no package path")
+}
+
+func (bt heapItemType) IsNamed() bool {
+ panic("heapItemType has no property called named")
+}
+
// ----------------------------------------
// tupleType
@@ -2118,9 +2148,10 @@ const (
MapKind
TypeKind // not in go.
// UnsafePointerKind
- BlockKind // not in go.
- TupleKind // not in go.
- RefTypeKind // not in go.
+ BlockKind // not in go.
+ HeapItemKind // not in go.
+ TupleKind // not in go.
+ RefTypeKind // not in go.
)
// This is generally slower than switching on baseOf(t).
@@ -2193,6 +2224,8 @@ func KindOf(t Type) Kind {
return t.Kind()
case blockType:
return BlockKind
+ case heapItemType:
+ return HeapItemKind
case *tupleType:
return TupleKind
case RefType:
diff --git a/gnovm/pkg/gnolang/uverse.go b/gnovm/pkg/gnolang/uverse.go
index d62d4228d68..ba66b27499f 100644
--- a/gnovm/pkg/gnolang/uverse.go
+++ b/gnovm/pkg/gnolang/uverse.go
@@ -61,26 +61,55 @@ var gStringerType = &DeclaredType{
var (
uverseNode *PackageNode
uverseValue *PackageValue
+ uverseInit = uverseUninitialized
)
+const (
+ uverseUninitialized = iota
+ uverseInitializing
+ uverseInitialized
+)
+
+func init() {
+ // Call Uverse() so we initialize the Uverse node ahead of any calls to the package.
+ Uverse()
+}
+
const uversePkgPath = ".uverse"
-// Always returns a new copy from the latest state of source.
+// UverseNode returns the uverse PackageValue.
+// If called while initializing the UverseNode itself, it will return an empty
+// PackageValue.
func Uverse() *PackageValue {
- if uverseValue == nil {
- pn := UverseNode()
- uverseValue = pn.NewPackage()
+ switch uverseInit {
+ case uverseUninitialized:
+ uverseInit = uverseInitializing
+ makeUverseNode()
+ uverseInit = uverseInitialized
+ case uverseInitializing:
+ return &PackageValue{}
}
+
return uverseValue
}
-// Always returns the same instance with possibly differing completeness.
+// UverseNode returns the uverse PackageNode.
+// If called while initializing the UverseNode itself, it will return an empty
+// PackageNode.
func UverseNode() *PackageNode {
- // Global is singleton.
- if uverseNode != nil {
- return uverseNode
+ switch uverseInit {
+ case uverseUninitialized:
+ uverseInit = uverseInitializing
+ makeUverseNode()
+ uverseInit = uverseInitialized
+ case uverseInitializing:
+ return &PackageNode{}
}
+ return uverseNode
+}
+
+func makeUverseNode() {
// NOTE: uverse node is hidden, thus the leading dot in pkgPath=".uverse".
uverseNode = NewPackageNode("uverse", uversePkgPath, nil)
@@ -1017,7 +1046,7 @@ func UverseNode() *PackageNode {
m.Exceptions = nil
},
)
- return uverseNode
+ uverseValue = uverseNode.NewPackage()
}
func copyDataToList(dst []TypedValue, data []byte, et Type) {
diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go
index bbf77bf19c7..68c2967811f 100644
--- a/gnovm/pkg/gnolang/values.go
+++ b/gnovm/pkg/gnolang/values.go
@@ -534,12 +534,13 @@ func (sv *StructValue) Copy(alloc *Allocator) *StructValue {
// makes construction TypedValue{T:*FuncType{},V:*FuncValue{}}
// faster.
type FuncValue struct {
- Type Type // includes unbound receiver(s)
- IsMethod bool // is an (unbound) method
- Source BlockNode // for block mem allocation
- Name Name // name of function/method
- Closure Value // *Block or RefValue to closure (may be nil for file blocks; lazy)
- FileName Name // file name where declared
+ Type Type // includes unbound receiver(s)
+ IsMethod bool // is an (unbound) method
+ Source BlockNode // for block mem allocation
+ Name Name // name of function/method
+ Closure Value // *Block or RefValue to closure (may be nil for file blocks; lazy)
+ Captures []TypedValue `json:",omitempty"` // HeapItemValues captured from closure.
+ FileName Name // file name where declared
PkgPath string
NativePkg string // for native bindings through NativeStore
NativeName Name // not redundant with Name; this cannot be changed in userspace
@@ -1509,86 +1510,31 @@ func (tv *TypedValue) GetBigDec() *apd.Decimal {
return tv.V.(BigdecValue).V
}
-// returns true if tv is zero
-func (tv *TypedValue) isZero() bool {
+func (tv *TypedValue) Sign() int {
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 UintKind, Uint8Kind, Uint16Kind, Uint32Kind, Uint64Kind:
+ return signOfUnsignedBytes(tv.N)
+ case IntKind, Int8Kind, Int16Kind, Int32Kind, Int64Kind, Float32Kind, Float64Kind:
+ return signOfSignedBytes(tv.N)
case BigintKind:
v := tv.GetBigInt()
- if v.Sign() == 0 {
- return true
- }
+ return v.Sign()
case BigdecKind:
v := tv.GetBigDec()
- if v.Sign() == 0 {
- return true
- }
+ return v.Sign()
default:
- panic("not numeric")
+ panic("type should be numeric")
+ }
+}
+
+func (tv *TypedValue) AssertNonNegative(msg string) {
+ if tv.Sign() < 0 {
+ panic(fmt.Sprintf("%s: %v", msg, tv))
}
- return false
}
func (tv *TypedValue) ComputeMapKey(store Store, omitType bool) MapKey {
@@ -1700,10 +1646,10 @@ func (tv *TypedValue) Assign(alloc *Allocator, tv2 TypedValue, cu bool) {
// or binary operations. When a pointer is to be
// allocated, *Allocator.AllocatePointer() is called separately,
// as in OpRef.
-func (tv *TypedValue) GetPointerTo(alloc *Allocator, store Store, path ValuePath) PointerValue {
+func (tv *TypedValue) GetPointerToFromTV(alloc *Allocator, store Store, path ValuePath) PointerValue {
if debug {
if tv.IsUndefined() {
- panic("GetPointerTo() on undefined value")
+ panic("GetPointerToFromTV() on undefined value")
}
}
@@ -1922,7 +1868,7 @@ func (tv *TypedValue) GetPointerTo(alloc *Allocator, store Store, path ValuePath
}
bv := *dtv
for i, path := range tr {
- ptr := bv.GetPointerTo(alloc, store, path)
+ ptr := bv.GetPointerToFromTV(alloc, store, path)
if i == len(tr)-1 {
return ptr // done
}
@@ -2491,6 +2437,70 @@ func (b *Block) GetPointerTo(store Store, path ValuePath) PointerValue {
return b.GetPointerToInt(store, int(path.Index))
}
+// Convenience
+func (b *Block) GetPointerToMaybeHeapUse(store Store, nx *NameExpr) PointerValue {
+ switch nx.Type {
+ case NameExprTypeNormal:
+ return b.GetPointerTo(store, nx.Path)
+ case NameExprTypeHeapUse:
+ return b.GetPointerToHeapUse(store, nx.Path)
+ case NameExprTypeHeapClosure:
+ panic("should not happen with type heap closure")
+ default:
+ panic("unexpected NameExpr type for GetPointerToMaybeHeapUse")
+ }
+}
+
+// Convenience
+func (b *Block) GetPointerToMaybeHeapDefine(store Store, nx *NameExpr) PointerValue {
+ switch nx.Type {
+ case NameExprTypeNormal:
+ return b.GetPointerTo(store, nx.Path)
+ case NameExprTypeDefine:
+ return b.GetPointerTo(store, nx.Path)
+ case NameExprTypeHeapDefine:
+ return b.GetPointerToHeapDefine(store, nx.Path)
+ default:
+ panic("unexpected NameExpr type for GetPointerToMaybeHeapDefine")
+ }
+}
+
+// First defines a new HeapItemValue.
+// This gets called from NameExprTypeHeapDefine name expressions.
+func (b *Block) GetPointerToHeapDefine(store Store, path ValuePath) PointerValue {
+ ptr := b.GetPointerTo(store, path)
+ hiv := &HeapItemValue{}
+ // point to a heapItem
+ *ptr.TV = TypedValue{
+ T: heapItemType{},
+ V: hiv,
+ }
+
+ return PointerValue{
+ TV: &hiv.Value,
+ Base: hiv,
+ Index: 0,
+ }
+}
+
+// Assumes a HeapItemValue, and gets inner pointer.
+// This gets called from NameExprTypeHeapUse name expressions.
+func (b *Block) GetPointerToHeapUse(store Store, path ValuePath) PointerValue {
+ ptr := b.GetPointerTo(store, path)
+ if _, ok := ptr.TV.T.(heapItemType); !ok {
+ panic("should not happen, should be heapItemType")
+ }
+ if _, ok := ptr.TV.V.(*HeapItemValue); !ok {
+ panic("should not happen, should be HeapItemValue")
+ }
+
+ return PointerValue{
+ TV: &ptr.TV.V.(*HeapItemValue).Value,
+ Base: ptr.TV.V,
+ Index: 0,
+ }
+}
+
// Result is used has lhs for any assignments to "_".
func (b *Block) GetBlankRef() *TypedValue {
return &b.Blank
@@ -2693,3 +2703,24 @@ func fillValueTV(store Store, tv *TypedValue) *TypedValue {
}
return tv
}
+
+// ----------------------------------------
+// Utility
+func signOfSignedBytes(n [8]byte) int {
+ si := *(*int64)(unsafe.Pointer(&n[0]))
+ switch {
+ case si == 0:
+ return 0
+ case si < 0:
+ return -1
+ default:
+ return 1
+ }
+}
+
+func signOfUnsignedBytes(n [8]byte) int {
+ if *(*uint64)(unsafe.Pointer(&n[0])) == 0 {
+ return 0
+ }
+ return 1
+}
diff --git a/gnovm/pkg/gnolang/values_conversions.go b/gnovm/pkg/gnolang/values_conversions.go
index c5ddc232fcb..9ec3427ed8f 100644
--- a/gnovm/pkg/gnolang/values_conversions.go
+++ b/gnovm/pkg/gnolang/values_conversions.go
@@ -44,6 +44,7 @@ func ConvertTo(alloc *Allocator, store Store, tv *TypedValue, t Type) {
tv.T = t
return
} else {
+ // both NativeType, use reflect to assert.
// convert go-native to gno type (shallow).
*tv = go2GnoValue2(alloc, store, tv.V.(*NativeValue).Value, false)
ConvertTo(alloc, store, tv, t)
diff --git a/gnovm/pkg/gnomod/file_test.go b/gnovm/pkg/gnomod/file_test.go
index 7abfe16f340..a64c2794a65 100644
--- a/gnovm/pkg/gnomod/file_test.go
+++ b/gnovm/pkg/gnomod/file_test.go
@@ -14,7 +14,7 @@ import (
"golang.org/x/mod/module"
)
-const testRemote string = "test3.gno.land:26657"
+const testRemote string = "gno.land:26657" // XXX(race condition): test with a local node so that this test is consistent with git and not with a deploy
func TestFetchDeps(t *testing.T) {
for _, tc := range []struct {
@@ -68,7 +68,7 @@ func TestFetchDeps(t *testing.T) {
"cached gno.land/p/demo/avl",
},
}, {
- desc: "fetch_gno.land/p/demo/blog",
+ desc: "fetch_gno.land/p/demo/blog6",
modFile: File{
Module: &modfile.Module{
Mod: module.Version{
@@ -84,7 +84,7 @@ func TestFetchDeps(t *testing.T) {
},
},
},
- requirements: []string{"avl", "blog", "ufmt"},
+ requirements: []string{"avl", "blog", "ufmt", "mux"},
stdOutContains: []string{
"fetching gno.land/p/demo/blog",
"fetching gno.land/p/demo/avl // indirect",
diff --git a/gnovm/pkg/gnomod/gnomod.go b/gnovm/pkg/gnomod/gnomod.go
index 0effa532107..553bb32f4b5 100644
--- a/gnovm/pkg/gnomod/gnomod.go
+++ b/gnovm/pkg/gnomod/gnomod.go
@@ -9,10 +9,10 @@ import (
"path/filepath"
"strings"
+ "github.com/gnolang/gno/gnovm"
"github.com/gnolang/gno/gnovm/pkg/gnoenv"
"github.com/gnolang/gno/gnovm/pkg/gnolang"
"github.com/gnolang/gno/gnovm/pkg/transpiler"
- "github.com/gnolang/gno/tm2/pkg/std"
"golang.org/x/mod/modfile"
"golang.org/x/mod/module"
)
@@ -43,7 +43,7 @@ func writePackage(remote, basePath, pkgPath string) (requirements []string, err
return nil, fmt.Errorf("querychain (%s): %w", pkgPath, err)
}
- dirPath, fileName := std.SplitFilepath(pkgPath)
+ dirPath, fileName := gnovm.SplitFilepath(pkgPath)
if fileName == "" {
// Is Dir
// Create Dir if not exists
diff --git a/gnovm/pkg/gnomod/read.go b/gnovm/pkg/gnomod/read.go
index a7a600f8826..d6d771429d3 100644
--- a/gnovm/pkg/gnomod/read.go
+++ b/gnovm/pkg/gnomod/read.go
@@ -249,7 +249,7 @@ func (in *input) readToken() {
// Otherwise, save comment for later attachment to syntax tree.
in.endToken(_EOLCOMMENT)
- in.comments = append(in.comments, modfile.Comment{in.token.pos, in.token.text, suffix})
+ in.comments = append(in.comments, modfile.Comment{Start: in.token.pos, Token: in.token.text, Suffix: suffix})
return
}
diff --git a/gnovm/stdlibs/generated.go b/gnovm/stdlibs/generated.go
index 4c460e220b7..a2d82b0bc60 100644
--- a/gnovm/stdlibs/generated.go
+++ b/gnovm/stdlibs/generated.go
@@ -11,7 +11,6 @@ import (
libs_crypto_sha256 "github.com/gnolang/gno/gnovm/stdlibs/crypto/sha256"
libs_math "github.com/gnolang/gno/gnovm/stdlibs/math"
libs_std "github.com/gnolang/gno/gnovm/stdlibs/std"
- libs_strconv "github.com/gnolang/gno/gnovm/stdlibs/strconv"
libs_testing "github.com/gnolang/gno/gnovm/stdlibs/testing"
libs_time "github.com/gnolang/gno/gnovm/stdlibs/time"
)
@@ -722,249 +721,145 @@ var nativeFuncs = [...]NativeFunc{
},
},
{
- "strconv",
- "Itoa",
- []gno.FieldTypeExpr{
- {Name: gno.N("p0"), Type: gno.X("int")},
- },
- []gno.FieldTypeExpr{
- {Name: gno.N("r0"), Type: gno.X("string")},
- },
- false,
- func(m *gno.Machine) {
- b := m.LastBlock()
- var (
- p0 int
- rp0 = reflect.ValueOf(&p0).Elem()
- )
-
- gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0)
-
- r0 := libs_strconv.Itoa(p0)
-
- m.PushValue(gno.Go2GnoValue(
- m.Alloc,
- m.Store,
- reflect.ValueOf(&r0).Elem(),
- ))
- },
- },
- {
- "strconv",
- "AppendUint",
- []gno.FieldTypeExpr{
- {Name: gno.N("p0"), Type: gno.X("[]byte")},
- {Name: gno.N("p1"), Type: gno.X("uint64")},
- {Name: gno.N("p2"), Type: gno.X("int")},
- },
- []gno.FieldTypeExpr{
- {Name: gno.N("r0"), Type: gno.X("[]byte")},
- },
- false,
+ "std",
+ "assertCallerIsRealm",
+ []gno.FieldTypeExpr{},
+ []gno.FieldTypeExpr{},
+ true,
func(m *gno.Machine) {
- b := m.LastBlock()
- var (
- p0 []byte
- rp0 = reflect.ValueOf(&p0).Elem()
- p1 uint64
- rp1 = reflect.ValueOf(&p1).Elem()
- p2 int
- rp2 = reflect.ValueOf(&p2).Elem()
+ libs_std.X_assertCallerIsRealm(
+ m,
)
-
- gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0)
- gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1)
- gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 2, "")).TV, rp2)
-
- r0 := libs_strconv.AppendUint(p0, p1, p2)
-
- m.PushValue(gno.Go2GnoValue(
- m.Alloc,
- m.Store,
- reflect.ValueOf(&r0).Elem(),
- ))
},
},
{
- "strconv",
- "Atoi",
+ "std",
+ "setParamString",
[]gno.FieldTypeExpr{
{Name: gno.N("p0"), Type: gno.X("string")},
+ {Name: gno.N("p1"), Type: gno.X("string")},
},
- []gno.FieldTypeExpr{
- {Name: gno.N("r0"), Type: gno.X("int")},
- {Name: gno.N("r1"), Type: gno.X("error")},
- },
- false,
+ []gno.FieldTypeExpr{},
+ true,
func(m *gno.Machine) {
b := m.LastBlock()
var (
p0 string
rp0 = reflect.ValueOf(&p0).Elem()
+ p1 string
+ rp1 = reflect.ValueOf(&p1).Elem()
)
gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0)
+ gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1)
- r0, r1 := libs_strconv.Atoi(p0)
-
- m.PushValue(gno.Go2GnoValue(
- m.Alloc,
- m.Store,
- reflect.ValueOf(&r0).Elem(),
- ))
- m.PushValue(gno.Go2GnoValue(
- m.Alloc,
- m.Store,
- reflect.ValueOf(&r1).Elem(),
- ))
+ libs_std.X_setParamString(
+ m,
+ p0, p1)
},
},
{
- "strconv",
- "CanBackquote",
+ "std",
+ "setParamBool",
[]gno.FieldTypeExpr{
{Name: gno.N("p0"), Type: gno.X("string")},
+ {Name: gno.N("p1"), Type: gno.X("bool")},
},
- []gno.FieldTypeExpr{
- {Name: gno.N("r0"), Type: gno.X("bool")},
- },
- false,
+ []gno.FieldTypeExpr{},
+ true,
func(m *gno.Machine) {
b := m.LastBlock()
var (
p0 string
rp0 = reflect.ValueOf(&p0).Elem()
- )
-
- gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0)
-
- r0 := libs_strconv.CanBackquote(p0)
-
- m.PushValue(gno.Go2GnoValue(
- m.Alloc,
- m.Store,
- reflect.ValueOf(&r0).Elem(),
- ))
- },
- },
- {
- "strconv",
- "FormatInt",
- []gno.FieldTypeExpr{
- {Name: gno.N("p0"), Type: gno.X("int64")},
- {Name: gno.N("p1"), Type: gno.X("int")},
- },
- []gno.FieldTypeExpr{
- {Name: gno.N("r0"), Type: gno.X("string")},
- },
- false,
- func(m *gno.Machine) {
- b := m.LastBlock()
- var (
- p0 int64
- rp0 = reflect.ValueOf(&p0).Elem()
- p1 int
+ p1 bool
rp1 = reflect.ValueOf(&p1).Elem()
)
gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0)
gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1)
- r0 := libs_strconv.FormatInt(p0, p1)
-
- m.PushValue(gno.Go2GnoValue(
- m.Alloc,
- m.Store,
- reflect.ValueOf(&r0).Elem(),
- ))
+ libs_std.X_setParamBool(
+ m,
+ p0, p1)
},
},
{
- "strconv",
- "FormatUint",
- []gno.FieldTypeExpr{
- {Name: gno.N("p0"), Type: gno.X("uint64")},
- {Name: gno.N("p1"), Type: gno.X("int")},
- },
+ "std",
+ "setParamInt64",
[]gno.FieldTypeExpr{
- {Name: gno.N("r0"), Type: gno.X("string")},
+ {Name: gno.N("p0"), Type: gno.X("string")},
+ {Name: gno.N("p1"), Type: gno.X("int64")},
},
- false,
+ []gno.FieldTypeExpr{},
+ true,
func(m *gno.Machine) {
b := m.LastBlock()
var (
- p0 uint64
+ p0 string
rp0 = reflect.ValueOf(&p0).Elem()
- p1 int
+ p1 int64
rp1 = reflect.ValueOf(&p1).Elem()
)
gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0)
gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1)
- r0 := libs_strconv.FormatUint(p0, p1)
-
- m.PushValue(gno.Go2GnoValue(
- m.Alloc,
- m.Store,
- reflect.ValueOf(&r0).Elem(),
- ))
+ libs_std.X_setParamInt64(
+ m,
+ p0, p1)
},
},
{
- "strconv",
- "Quote",
+ "std",
+ "setParamUint64",
[]gno.FieldTypeExpr{
{Name: gno.N("p0"), Type: gno.X("string")},
+ {Name: gno.N("p1"), Type: gno.X("uint64")},
},
- []gno.FieldTypeExpr{
- {Name: gno.N("r0"), Type: gno.X("string")},
- },
- false,
+ []gno.FieldTypeExpr{},
+ true,
func(m *gno.Machine) {
b := m.LastBlock()
var (
p0 string
rp0 = reflect.ValueOf(&p0).Elem()
+ p1 uint64
+ rp1 = reflect.ValueOf(&p1).Elem()
)
gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0)
+ gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1)
- r0 := libs_strconv.Quote(p0)
-
- m.PushValue(gno.Go2GnoValue(
- m.Alloc,
- m.Store,
- reflect.ValueOf(&r0).Elem(),
- ))
+ libs_std.X_setParamUint64(
+ m,
+ p0, p1)
},
},
{
- "strconv",
- "QuoteToASCII",
+ "std",
+ "setParamBytes",
[]gno.FieldTypeExpr{
{Name: gno.N("p0"), Type: gno.X("string")},
+ {Name: gno.N("p1"), Type: gno.X("[]byte")},
},
- []gno.FieldTypeExpr{
- {Name: gno.N("r0"), Type: gno.X("string")},
- },
- false,
+ []gno.FieldTypeExpr{},
+ true,
func(m *gno.Machine) {
b := m.LastBlock()
var (
p0 string
rp0 = reflect.ValueOf(&p0).Elem()
+ p1 []byte
+ rp1 = reflect.ValueOf(&p1).Elem()
)
gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0)
+ gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1)
- r0 := libs_strconv.QuoteToASCII(p0)
-
- m.PushValue(gno.Go2GnoValue(
- m.Alloc,
- m.Store,
- reflect.ValueOf(&r0).Elem(),
- ))
+ libs_std.X_setParamBytes(
+ m,
+ p0, p1)
},
},
{
diff --git a/gnovm/stdlibs/io/export_test.gno b/gnovm/stdlibs/io/export_test.gno
deleted file mode 100644
index 6204ffc4591..00000000000
--- a/gnovm/stdlibs/io/export_test.gno
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2020 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.
-
-package io
-
-// exported for test
-var (
- ErrInvalidWrite = errInvalidWrite
- ErrWhence = errWhence
- ErrOffset = errOffset
-)
diff --git a/gnovm/stdlibs/io/io_test.gno b/gnovm/stdlibs/io/io_test.gno
index a7533a87799..4915982057b 100644
--- a/gnovm/stdlibs/io/io_test.gno
+++ b/gnovm/stdlibs/io/io_test.gno
@@ -1,4 +1,4 @@
-package io_test
+package io
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
@@ -8,7 +8,6 @@ import (
"bytes"
"errors"
"fmt"
- "io"
"strings"
"testing"
)
@@ -16,8 +15,8 @@ import (
// A version of bytes.Buffer without ReadFrom and WriteTo
type Buffer struct {
bytes.Buffer
- io.ReaderFrom // conflicts with and hides bytes.Buffer's ReaderFrom.
- io.WriterTo // conflicts with and hides bytes.Buffer's WriterTo.
+ ReaderFrom // conflicts with and hides bytes.Buffer's ReaderFrom.
+ WriterTo // conflicts with and hides bytes.Buffer's WriterTo.
}
// Simple tests, primarily to verify the ReadFrom and WriteTo callouts inside Copy, CopyBuffer and CopyN.
@@ -26,7 +25,7 @@ func TestCopy(t *testing.T) {
rb := new(Buffer)
wb := new(Buffer)
rb.WriteString("hello, world.")
- io.Copy(wb, rb)
+ Copy(wb, rb)
if wb.String() != "hello, world." {
t.Errorf("Copy did not work properly")
}
@@ -36,12 +35,12 @@ func TestCopyNegative(t *testing.T) {
rb := new(Buffer)
wb := new(Buffer)
rb.WriteString("hello")
- io.Copy(wb, &io.LimitedReader{R: rb, N: -1})
+ Copy(wb, &LimitedReader{R: rb, N: -1})
if wb.String() != "" {
t.Errorf("Copy on LimitedReader with N<0 copied data")
}
- io.CopyN(wb, rb, -1)
+ CopyN(wb, rb, -1)
if wb.String() != "" {
t.Errorf("CopyN with N<0 copied data")
}
@@ -51,7 +50,7 @@ func TestCopyBuffer(t *testing.T) {
rb := new(Buffer)
wb := new(Buffer)
rb.WriteString("hello, world.")
- io.CopyBuffer(wb, rb, make([]byte, 1)) // Tiny buffer to keep it honest.
+ CopyBuffer(wb, rb, make([]byte, 1)) // Tiny buffer to keep it honest.
if wb.String() != "hello, world." {
t.Errorf("CopyBuffer did not work properly")
}
@@ -61,7 +60,7 @@ func TestCopyBufferNil(t *testing.T) {
rb := new(Buffer)
wb := new(Buffer)
rb.WriteString("hello, world.")
- io.CopyBuffer(wb, rb, nil) // Should allocate a buffer.
+ CopyBuffer(wb, rb, nil) // Should allocate a buffer.
if wb.String() != "hello, world." {
t.Errorf("CopyBuffer did not work properly")
}
@@ -71,7 +70,7 @@ func TestCopyReadFrom(t *testing.T) {
rb := new(Buffer)
wb := new(bytes.Buffer) // implements ReadFrom.
rb.WriteString("hello, world.")
- io.Copy(wb, rb)
+ Copy(wb, rb)
if wb.String() != "hello, world." {
t.Errorf("Copy did not work properly")
}
@@ -81,7 +80,7 @@ func TestCopyWriteTo(t *testing.T) {
rb := new(bytes.Buffer) // implements WriteTo.
wb := new(Buffer)
rb.WriteString("hello, world.")
- io.Copy(wb, rb)
+ Copy(wb, rb)
if wb.String() != "hello, world." {
t.Errorf("Copy did not work properly")
}
@@ -93,7 +92,7 @@ type writeToChecker struct {
writeToCalled bool
}
-func (wt *writeToChecker) WriteTo(w io.Writer) (int64, error) {
+func (wt *writeToChecker) WriteTo(w Writer) (int64, error) {
wt.writeToCalled = true
return wt.Buffer.WriteTo(w)
}
@@ -105,7 +104,7 @@ func TestCopyPriority(t *testing.T) {
rb := new(writeToChecker)
wb := new(bytes.Buffer)
rb.WriteString("hello, world.")
- io.Copy(wb, rb)
+ Copy(wb, rb)
if wb.String() != "hello, world." {
t.Errorf("Copy did not work properly")
} else if !rb.writeToCalled {
@@ -135,7 +134,7 @@ func (w errWriter) Write([]byte) (int, error) {
func TestCopyReadErrWriteErr(t *testing.T) {
er, ew := errors.New("readError"), errors.New("writeError")
r, w := zeroErrReader{err: er}, errWriter{err: ew}
- n, err := io.Copy(w, r)
+ n, err := Copy(w, r)
if n != 0 || err != ew {
t.Errorf("Copy(zeroErrReader, errWriter) = %d, %v; want 0, writeError", n, err)
}
@@ -145,7 +144,7 @@ func TestCopyN(t *testing.T) {
rb := new(Buffer)
wb := new(Buffer)
rb.WriteString("hello, world.")
- io.CopyN(wb, rb, 5)
+ CopyN(wb, rb, 5)
if wb.String() != "hello" {
t.Errorf("CopyN did not work properly")
}
@@ -155,7 +154,7 @@ func TestCopyNReadFrom(t *testing.T) {
rb := new(Buffer)
wb := new(bytes.Buffer) // implements ReadFrom.
rb.WriteString("hello")
- io.CopyN(wb, rb, 5)
+ CopyN(wb, rb, 5)
if wb.String() != "hello" {
t.Errorf("CopyN did not work properly")
}
@@ -165,7 +164,7 @@ func TestCopyNWriteTo(t *testing.T) {
rb := new(bytes.Buffer) // implements WriteTo.
wb := new(Buffer)
rb.WriteString("hello, world.")
- io.CopyN(wb, rb, 5)
+ CopyN(wb, rb, 5)
if wb.String() != "hello" {
t.Errorf("CopyN did not work properly")
}
@@ -178,7 +177,7 @@ func BenchmarkCopyNSmall(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
- io.CopyN(buf, rd, 512)
+ CopyN(buf, rd, 512)
rd.Reset(bs)
}
}
@@ -190,13 +189,13 @@ func BenchmarkCopyNLarge(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
- io.CopyN(buf, rd, 32*1024)
+ CopyN(buf, rd, 32*1024)
rd.Reset(bs)
}
}
type noReadFrom struct {
- w io.Writer
+ w Writer
}
func (w *noReadFrom) Write(p []byte) (n int, err error) {
@@ -215,32 +214,32 @@ func TestCopyNEOF(t *testing.T) {
b := new(bytes.Buffer)
- n, err := io.CopyN(&noReadFrom{b}, strings.NewReader("foo"), 3)
+ n, err := CopyN(&noReadFrom{b}, strings.NewReader("foo"), 3)
if n != 3 || err != nil {
t.Errorf("CopyN(noReadFrom, foo, 3) = %d, %v; want 3, nil", n, err)
}
- n, err = io.CopyN(&noReadFrom{b}, strings.NewReader("foo"), 4)
- if n != 3 || err != io.EOF {
+ n, err = CopyN(&noReadFrom{b}, strings.NewReader("foo"), 4)
+ if n != 3 || err != EOF {
t.Errorf("CopyN(noReadFrom, foo, 4) = %d, %v; want 3, EOF", n, err)
}
- n, err = io.CopyN(b, strings.NewReader("foo"), 3) // b has read from
+ n, err = CopyN(b, strings.NewReader("foo"), 3) // b has read from
if n != 3 || err != nil {
t.Errorf("CopyN(bytes.Buffer, foo, 3) = %d, %v; want 3, nil", n, err)
}
- n, err = io.CopyN(b, strings.NewReader("foo"), 4) // b has read from
- if n != 3 || err != io.EOF {
+ n, err = CopyN(b, strings.NewReader("foo"), 4) // b has read from
+ if n != 3 || err != EOF {
t.Errorf("CopyN(bytes.Buffer, foo, 4) = %d, %v; want 3, EOF", n, err)
}
- n, err = io.CopyN(b, wantedAndErrReader{}, 5)
+ n, err = CopyN(b, wantedAndErrReader{}, 5)
if n != 5 || err != nil {
t.Errorf("CopyN(bytes.Buffer, wantedAndErrReader, 5) = %d, %v; want 5, nil", n, err)
}
- n, err = io.CopyN(&noReadFrom{b}, wantedAndErrReader{}, 5)
+ n, err = CopyN(&noReadFrom{b}, wantedAndErrReader{}, 5)
if n != 5 || err != nil {
t.Errorf("CopyN(noReadFrom, wantedAndErrReader, 5) = %d, %v; want 5, nil", n, err)
}
@@ -268,7 +267,7 @@ func (r *dataAndErrorBuffer) Read(p []byte) (n int, err error) {
func TestReadAtLeastWithDataAndEOF(t *testing.T) {
var rb dataAndErrorBuffer
- rb.err = io.EOF
+ rb.err = EOF
testReadAtLeast(t, &rb)
}
@@ -278,41 +277,41 @@ func TestReadAtLeastWithDataAndError(t *testing.T) {
testReadAtLeast(t, &rb)
}
-func testReadAtLeast(t *testing.T, rb io.ReadWriter) {
+func testReadAtLeast(t *testing.T, rb ReadWriter) {
rb.Write([]byte("0123"))
buf := make([]byte, 2)
- n, err := io.ReadAtLeast(rb, buf, 2)
+ n, err := ReadAtLeast(rb, buf, 2)
if err != nil {
t.Error(err)
}
if n != 2 {
t.Errorf("expected to have read 2 bytes, got %v", n)
}
- n, err = io.ReadAtLeast(rb, buf, 4)
- if err != io.ErrShortBuffer {
- t.Errorf("expected ErrShortBuffer got %v", err)
+ n, err = ReadAtLeast(rb, buf, 4)
+ if err != ErrShortBuffer {
+ t.Errorf("expected `ErrShortBuffer` got %v", err)
}
if n != 0 {
t.Errorf("expected to have read 0 bytes, got %v", n)
}
- n, err = io.ReadAtLeast(rb, buf, 1)
+ n, err = ReadAtLeast(rb, buf, 1)
if err != nil {
t.Error(err)
}
if n != 2 {
t.Errorf("expected to have read 2 bytes, got %v", n)
}
- n, err = io.ReadAtLeast(rb, buf, 2)
- if err != io.EOF {
+ n, err = ReadAtLeast(rb, buf, 2)
+ if err != EOF {
t.Errorf("expected EOF, got %v", err)
}
if n != 0 {
t.Errorf("expected to have read 0 bytes, got %v", n)
}
rb.Write([]byte("4"))
- n, err = io.ReadAtLeast(rb, buf, 2)
- want := io.ErrUnexpectedEOF
- if rb, ok := rb.(*dataAndErrorBuffer); ok && rb.err != io.EOF {
+ n, err = ReadAtLeast(rb, buf, 2)
+ want := ErrUnexpectedEOF
+ if rb, ok := rb.(*dataAndErrorBuffer); ok && rb.err != EOF {
want = rb.err
}
if err != want {
@@ -323,14 +322,14 @@ func testReadAtLeast(t *testing.T, rb io.ReadWriter) {
}
}
-/* XXX no io.Pipe() no chan
+/* XXX no Pipe() no chan
func TestTeeReader(t *testing.T) {
src := []byte("hello, world")
dst := make([]byte, len(src))
rb := bytes.NewBuffer(src)
wb := new(bytes.Buffer)
- r := io.TeeReader(rb, wb)
- if n, err := io.ReadFull(r, dst); err != nil || n != len(src) {
+ r := TeeReader(rb, wb)
+ if n, err := ReadFull(r, dst); err != nil || n != len(src) {
t.Fatalf("ReadFull(r, dst) = %d, %v; want %d, nil", n, err, len(src))
}
if !bytes.Equal(dst, src) {
@@ -339,14 +338,14 @@ func TestTeeReader(t *testing.T) {
if !bytes.Equal(wb.Bytes(), src) {
t.Errorf("bytes written = %q want %q", wb.Bytes(), src)
}
- if n, err := r.Read(dst); n != 0 || err != io.EOF {
+ if n, err := r.Read(dst); n != 0 || err != EOF {
t.Errorf("r.Read at EOF = %d, %v want 0, EOF", n, err)
}
rb = bytes.NewBuffer(src)
- pr, pw := io.Pipe()
+ pr, pw := Pipe()
pr.Close()
- r = io.TeeReader(rb, pw)
- if n, err := io.ReadFull(r, dst); n != 0 || err != io.ErrClosedPipe {
+ r = TeeReader(rb, pw)
+ if n, err := ReadFull(r, dst); n != 0 || err != ErrClosedPipe {
t.Errorf("closed tee: ReadFull(r, dst) = %d, %v; want 0, EPIPE", n, err)
}
}
@@ -363,22 +362,22 @@ func TestSectionReader_ReadAt(t *testing.T) {
exp string
err error
}{
- {data: "", off: 0, n: 10, bufLen: 2, at: 0, exp: "", err: io.EOF},
+ {data: "", off: 0, n: 10, bufLen: 2, at: 0, exp: "", err: EOF},
{data: dat, off: 0, n: len(dat), bufLen: 0, at: 0, exp: "", err: nil},
- {data: dat, off: len(dat), n: 1, bufLen: 1, at: 0, exp: "", err: io.EOF},
+ {data: dat, off: len(dat), n: 1, bufLen: 1, at: 0, exp: "", err: EOF},
{data: dat, off: 0, n: len(dat) + 2, bufLen: len(dat), at: 0, exp: dat, err: nil},
{data: dat, off: 0, n: len(dat), bufLen: len(dat) / 2, at: 0, exp: dat[:len(dat)/2], err: nil},
{data: dat, off: 0, n: len(dat), bufLen: len(dat), at: 0, exp: dat, err: nil},
{data: dat, off: 0, n: len(dat), bufLen: len(dat) / 2, at: 2, exp: dat[2 : 2+len(dat)/2], err: nil},
{data: dat, off: 3, n: len(dat), bufLen: len(dat) / 2, at: 2, exp: dat[5 : 5+len(dat)/2], err: nil},
{data: dat, off: 3, n: len(dat) / 2, bufLen: len(dat)/2 - 2, at: 2, exp: dat[5 : 5+len(dat)/2-2], err: nil},
- {data: dat, off: 3, n: len(dat) / 2, bufLen: len(dat)/2 + 2, at: 2, exp: dat[5 : 5+len(dat)/2-2], err: io.EOF},
- {data: dat, off: 0, n: 0, bufLen: 0, at: -1, exp: "", err: io.EOF},
- {data: dat, off: 0, n: 0, bufLen: 0, at: 1, exp: "", err: io.EOF},
+ {data: dat, off: 3, n: len(dat) / 2, bufLen: len(dat)/2 + 2, at: 2, exp: dat[5 : 5+len(dat)/2-2], err: EOF},
+ {data: dat, off: 0, n: 0, bufLen: 0, at: -1, exp: "", err: EOF},
+ {data: dat, off: 0, n: 0, bufLen: 0, at: 1, exp: "", err: EOF},
}
for i, tt := range tests {
r := strings.NewReader(tt.data)
- s := io.NewSectionReader(r, int64(tt.off), int64(tt.n))
+ s := NewSectionReader(r, int64(tt.off), int64(tt.n))
buf := make([]byte, tt.bufLen)
if n, err := s.ReadAt(buf, int64(tt.at)); n != len(tt.exp) || string(buf[:n]) != tt.exp || err != tt.err {
t.Fatalf("%d: ReadAt(%d) = %q, %v; expected %q, %v", i, tt.at, buf[:n], err, tt.exp, tt.err)
@@ -389,9 +388,9 @@ func TestSectionReader_ReadAt(t *testing.T) {
func TestSectionReader_Seek(t *testing.T) {
// Verifies that NewSectionReader's Seeker behaves like bytes.NewReader (which is like strings.NewReader)
br := bytes.NewReader([]byte("foo"))
- sr := io.NewSectionReader(br, 0, int64(len("foo")))
+ sr := NewSectionReader(br, 0, int64(len("foo")))
- for _, whence := range []int{io.SeekStart, io.SeekCurrent, io.SeekEnd} {
+ for _, whence := range []int{SeekStart, SeekCurrent, SeekEnd} {
for offset := int64(-3); offset <= 4; offset++ {
brOff, brErr := br.Seek(offset, whence)
srOff, srErr := sr.Seek(offset, whence)
@@ -403,13 +402,13 @@ func TestSectionReader_Seek(t *testing.T) {
}
// And verify we can just seek past the end and get an EOF
- got, err := sr.Seek(100, io.SeekStart)
+ got, err := sr.Seek(100, SeekStart)
if err != nil || got != 100 {
t.Errorf("Seek = %v, %v; want 100, nil", got, err)
}
n, err := sr.Read(make([]byte, 10))
- if n != 0 || err != io.EOF {
+ if n != 0 || err != EOF {
t.Errorf("Read = %v, %v; want 0, EOF", n, err)
}
}
@@ -425,7 +424,7 @@ func TestSectionReader_Size(t *testing.T) {
for _, tt := range tests {
r := strings.NewReader(tt.data)
- sr := io.NewSectionReader(r, 0, int64(len(tt.data)))
+ sr := NewSectionReader(r, 0, int64(len(tt.data)))
if got := sr.Size(); got != tt.want {
t.Errorf("Size = %v; want %v", got, tt.want)
}
@@ -443,11 +442,11 @@ func (w largeWriter) Write(p []byte) (int, error) {
}
func TestCopyLargeWriter(t *testing.T) {
- want := io.ErrInvalidWrite
+ want := errInvalidWrite
rb := new(Buffer)
wb := largeWriter{}
rb.WriteString("hello, world.")
- if _, err := io.Copy(wb, rb); err != want {
+ if _, err := Copy(wb, rb); err != want {
t.Errorf("Copy error: got %v, want %v", err, want)
}
@@ -455,7 +454,7 @@ func TestCopyLargeWriter(t *testing.T) {
rb = new(Buffer)
wb = largeWriter{err: want}
rb.WriteString("hello, world.")
- if _, err := io.Copy(wb, rb); err != want {
+ if _, err := Copy(wb, rb); err != want {
t.Errorf("Copy error: got %v, want %v", err, want)
}
}
@@ -463,18 +462,18 @@ func TestCopyLargeWriter(t *testing.T) {
func TestNopCloserWriterToForwarding(t *testing.T) {
for _, tc := range [...]struct {
Name string
- r io.Reader
+ r Reader
}{
- {"not a WriterTo", io.Reader(nil)},
+ {"not a WriterTo", Reader(nil)},
{"a WriterTo", struct {
- io.Reader
- io.WriterTo
+ Reader
+ WriterTo
}{}},
} {
- nc := io.NopCloser(tc.r)
+ nc := NopCloser(tc.r)
- _, expected := tc.r.(io.WriterTo)
- _, got := nc.(io.WriterTo)
+ _, expected := tc.r.(WriterTo)
+ _, got := nc.(WriterTo)
if expected != got {
t.Errorf("NopCloser incorrectly forwards WriterTo for %s, got %t want %t", tc.Name, got, expected)
}
@@ -496,9 +495,9 @@ func TestNopCloserWriterToForwarding(t *testing.T) {
// for _, whence := range []int{-3, -2, -1, 3, 4, 5} {
// var offset int64 = 0
// gotOff, gotErr := w.Seek(offset, whence)
-// if gotOff != 0 || gotErr != ErrWhence {
+// if gotOff != 0 || gotErr != errWhence {
// t.Errorf("For whence %d, offset %d, OffsetWriter.Seek got: (%d, %v), want: (%d, %v)",
-// whence, offset, gotOff, gotErr, 0, ErrWhence)
+// whence, offset, gotOff, gotErr, 0, errWhence)
// }
// }
// })
@@ -508,7 +507,7 @@ func TestNopCloserWriterToForwarding(t *testing.T) {
// for _, whence := range []int{SeekStart, SeekCurrent} {
// for offset := int64(-3); offset < 0; offset++ {
// gotOff, gotErr := w.Seek(offset, whence)
-// if gotOff != 0 || gotErr != ErrOffset {
+// if gotOff != 0 || gotErr != errOffset {
// t.Errorf("For whence %d, offset %d, OffsetWriter.Seek got: (%d, %v), want: (%d, %v)",
// whence, offset, gotOff, gotErr, 0, ErrOffset)
// }
@@ -540,3 +539,70 @@ func TestNopCloserWriterToForwarding(t *testing.T) {
// }
// })
// }
+
+// TODO: The original test uses `os.CreateTemp`, but here
+// to work around it by using `bytes.Buffer`.
+// When `os.CreateTemp` is available in the future, we should change the test
+// to use the original approach instead of this method. (just un-comment the test above)
+func TestOffsetWriter_Seek(t *testing.T) {
+ buf := new(bytes.Buffer)
+ w := NewOffsetWriter(testWriterAt{buf}, 0)
+
+ // Should throw error errWhence if whence is not valid
+ t.Run("errWhence", func(t *testing.T) {
+ for _, whence := range []int{-3, -2, -1, 3, 4, 5} {
+ var offset int64 = 0
+ gotOff, gotErr := w.Seek(offset, whence)
+ if gotOff != 0 || gotErr != errWhence {
+ t.Errorf("For whence %d, offset %d, OffsetWriter.Seek got: (%d, %v), want: (%d, %v)",
+ whence, offset, gotOff, gotErr, 0, errWhence)
+ }
+ }
+ })
+
+ // Should throw error errOffset if offset is negative
+ t.Run("errOffset", func(t *testing.T) {
+ for _, whence := range []int{SeekStart, SeekCurrent} {
+ for offset := int64(-3); offset < 0; offset++ {
+ gotOff, gotErr := w.Seek(offset, whence)
+ if gotOff != 0 || gotErr != errOffset {
+ t.Errorf("For whence %d, offset %d, OffsetWriter.Seek got: (%d, %v), want: (%d, %v)",
+ whence, offset, gotOff, gotErr, 0, errOffset)
+ }
+ }
+ }
+ })
+
+ t.Run("normal", func(t *testing.T) {
+ tests := []struct {
+ offset int64
+ whence int
+ returnOff int64
+ }{
+ {whence: SeekStart, offset: 1, returnOff: 1},
+ {whence: SeekStart, offset: 2, returnOff: 2},
+ {whence: SeekStart, offset: 3, returnOff: 3},
+ {whence: SeekCurrent, offset: 1, returnOff: 4},
+ {whence: SeekCurrent, offset: 2, returnOff: 6},
+ {whence: SeekCurrent, offset: 3, returnOff: 9},
+ }
+ for idx, tt := range tests {
+ gotOff, gotErr := w.Seek(tt.offset, tt.whence)
+ if gotOff != tt.returnOff || gotErr != nil {
+ t.Errorf("%d:: For whence %d, offset %d, OffsetWriter.Seek got: (%d, %v), want: (%d, )",
+ idx+1, tt.whence, tt.offset, gotOff, gotErr, tt.returnOff)
+ }
+ }
+ })
+}
+
+type testWriterAt struct {
+ buf *bytes.Buffer
+}
+
+func (w testWriterAt) WriteAt(p []byte, off int64) (n int, err error) {
+ if int64(w.buf.Len()) < off+int64(len(p)) {
+ w.buf.Grow(int(off + int64(len(p)) - int64(w.buf.Len())))
+ }
+ return copy(w.buf.Bytes()[off:], p), nil
+}
diff --git a/gnovm/stdlibs/io/multi_test.gno b/gnovm/stdlibs/io/multi_test.gno
index 31345279318..8932ace2e59 100644
--- a/gnovm/stdlibs/io/multi_test.gno
+++ b/gnovm/stdlibs/io/multi_test.gno
@@ -1,4 +1,4 @@
-package io_test
+package io
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
@@ -8,7 +8,6 @@ import (
"bytes"
"crypto/sha1"
"fmt"
- "io"
"strings"
"testing"
)
@@ -18,14 +17,14 @@ type Stringer interface {
}
func TestMultiReader(t *testing.T) {
- var mr io.Reader
+ var mr Reader
var buf []byte
nread := 0
withFooBar := func(tests func()) {
r1 := strings.NewReader("foo ")
r2 := strings.NewReader("")
r3 := strings.NewReader("bar")
- mr = io.MultiReader(r1, r2, r3)
+ mr = MultiReader(r1, r2, r3)
buf = make([]byte, 20)
tests()
}
@@ -51,13 +50,13 @@ func TestMultiReader(t *testing.T) {
expectRead(2, "fo", nil)
expectRead(5, "o ", nil)
expectRead(5, "bar", nil)
- expectRead(5, "", io.EOF)
+ expectRead(5, "", EOF)
})
withFooBar(func() {
expectRead(4, "foo ", nil)
expectRead(1, "b", nil)
expectRead(3, "ar", nil)
- expectRead(1, "", io.EOF)
+ expectRead(1, "", EOF)
})
withFooBar(func() {
expectRead(5, "foo ", nil)
@@ -68,7 +67,7 @@ func TestMultiWriter(t *testing.T) {
sink := new(bytes.Buffer)
// Hide bytes.Buffer's WriteString method:
testMultiWriter(t, struct {
- io.Writer
+ Writer
Stringer
}{sink, sink})
}
@@ -83,11 +82,11 @@ func TestMultiWriter_String(t *testing.T) {
func TestMultiWriter_WriteStringSingleAlloc(t *testing.T) {
var sink1, sink2 bytes.Buffer
type simpleWriter struct { // hide bytes.Buffer's WriteString
- io.Writer
+ Writer
}
- mw := io.MultiWriter(simpleWriter{&sink1}, simpleWriter{&sink2})
+ mw := MultiWriter(simpleWriter{&sink1}, simpleWriter{&sink2})
allocs := int(testing.AllocsPerRun2(1000, func() {
- io.WriteString(mw, "foo")
+ WriteString(mw, "foo")
}))
if allocs != 1 {
t.Errorf("num allocations = %d; want 1", allocs)
@@ -108,24 +107,24 @@ func (c *writeStringChecker) Write(p []byte) (n int, err error) {
func TestMultiWriter_StringCheckCall(t *testing.T) {
var c writeStringChecker
- mw := io.MultiWriter(&c)
- io.WriteString(mw, "foo")
+ mw := MultiWriter(&c)
+ WriteString(mw, "foo")
if !c.called {
t.Error("did not see WriteString call to writeStringChecker")
}
}
func testMultiWriter(t *testing.T, sink interface {
- io.Writer
+ Writer
Stringer
},
) {
sha1 := sha1.New()
- mw := io.MultiWriter(sha1, sink)
+ mw := MultiWriter(sha1, sink)
sourceString := "My input text."
source := strings.NewReader(sourceString)
- written, err := io.Copy(mw, source)
+ written, err := Copy(mw, source)
if written != int64(len(sourceString)) {
t.Errorf("short write of %d, not %d", written, len(sourceString))
@@ -183,25 +182,25 @@ func TestMultiWriterSingleChainFlatten(t *testing.T) {
func TestMultiWriterError(t *testing.T) {
f1 := writerFunc(func(p []byte) (int, error) {
- return len(p) / 2, io.ErrShortWrite
+ return len(p) / 2, ErrShortWrite
})
f2 := writerFunc(func(p []byte) (int, error) {
t.Errorf("MultiWriter called f2.Write")
return len(p), nil
})
- w := io.MultiWriter(f1, f2)
+ w := MultiWriter(f1, f2)
n, err := w.Write(make([]byte, 100))
- if n != 50 || err != io.ErrShortWrite {
+ if n != 50 || err != ErrShortWrite {
t.Errorf("Write = %d, %v, want 50, ErrShortWrite", n, err)
}
}
// Test that MultiReader copies the input slice and is insulated from future modification.
func TestMultiReaderCopy(t *testing.T) {
- slice := []io.Reader{strings.NewReader("hello world")}
- r := io.MultiReader(slice...)
+ slice := []Reader{strings.NewReader("hello world")}
+ r := MultiReader(slice...)
slice[0] = nil
- data, err := io.ReadAll(r)
+ data, err := ReadAll(r)
if err != nil || string(data) != "hello world" {
t.Errorf("ReadAll() = %q, %v, want %q, nil", data, err, "hello world")
}
@@ -210,8 +209,8 @@ func TestMultiReaderCopy(t *testing.T) {
// Test that MultiWriter copies the input slice and is insulated from future modification.
func TestMultiWriterCopy(t *testing.T) {
var buf bytes.Buffer
- slice := []io.Writer{&buf}
- w := io.MultiWriter(slice...)
+ slice := []Writer{&buf}
+ w := MultiWriter(slice...)
slice[0] = nil
n, err := w.Write([]byte("hello world"))
if err != nil || n != 11 {
@@ -278,12 +277,12 @@ func (b byteAndEOFReader) Read(p []byte) (n int, err error) {
panic("unexpected call")
}
p[0] = byte(b)
- return 1, io.EOF
+ return 1, EOF
}
// This used to yield bytes forever; issue 16795.
func TestMultiReaderSingleByteWithEOF(t *testing.T) {
- got, err := io.ReadAll(io.LimitReader(io.MultiReader(byteAndEOFReader('a'), byteAndEOFReader('b')), 10))
+ got, err := ReadAll(LimitReader(MultiReader(byteAndEOFReader('a'), byteAndEOFReader('b')), 10))
if err != nil {
t.Fatal(err)
}
@@ -297,10 +296,10 @@ func TestMultiReaderSingleByteWithEOF(t *testing.T) {
// chain continues to return EOF on its final read, rather than
// yielding a (0, EOF).
func TestMultiReaderFinalEOF(t *testing.T) {
- r := io.MultiReader(bytes.NewReader(nil), byteAndEOFReader('a'))
+ r := MultiReader(bytes.NewReader(nil), byteAndEOFReader('a'))
buf := make([]byte, 2)
n, err := r.Read(buf)
- if n != 1 || err != io.EOF {
+ if n != 1 || err != EOF {
t.Errorf("got %v, %v; want 1, EOF", n, err)
}
}
@@ -343,21 +342,21 @@ func TestInterleavedMultiReader(t *testing.T) {
r1 := strings.NewReader("123")
r2 := strings.NewReader("45678")
- mr1 := io.MultiReader(r1, r2)
- mr2 := io.MultiReader(mr1)
+ mr1 := MultiReader(r1, r2)
+ mr2 := MultiReader(mr1)
buf := make([]byte, 4)
// Have mr2 use mr1's []Readers.
// Consume r1 (and clear it for GC to handle) and consume part of r2.
- n, err := io.ReadFull(mr2, buf)
+ n, err := ReadFull(mr2, buf)
if got := string(buf[:n]); got != "1234" || err != nil {
t.Errorf(`ReadFull(mr2) = (%q, %v), want ("1234", nil)`, got, err)
}
// Consume the rest of r2 via mr1.
// This should not panic even though mr2 cleared r1.
- n, err = io.ReadFull(mr1, buf)
+ n, err = ReadFull(mr1, buf)
if got := string(buf[:n]); got != "5678" || err != nil {
t.Errorf(`ReadFull(mr1) = (%q, %v), want ("5678", nil)`, got, err)
}
diff --git a/gnovm/stdlibs/std/banker.gno b/gnovm/stdlibs/std/banker.gno
index 2b94292bd7e..5412b73281c 100644
--- a/gnovm/stdlibs/std/banker.gno
+++ b/gnovm/stdlibs/std/banker.gno
@@ -65,6 +65,7 @@ func (b BankerType) String() string {
// GetBanker returns a new Banker, with its capabilities matching the given
// [BankerType].
func GetBanker(bt BankerType) Banker {
+ assertCallerIsRealm()
if bt >= maxBanker {
panic("invalid banker type")
}
diff --git a/gnovm/stdlibs/std/context.go b/gnovm/stdlibs/std/context.go
index ff5c91a14eb..a0dafe5dc44 100644
--- a/gnovm/stdlibs/std/context.go
+++ b/gnovm/stdlibs/std/context.go
@@ -18,6 +18,7 @@ type ExecContext struct {
OrigSend std.Coins
OrigSendSpent *std.Coins // mutable
Banker BankerInterface
+ Params ParamsInterface
EventLogger *sdk.EventLogger
}
diff --git a/gnovm/stdlibs/std/native.gno b/gnovm/stdlibs/std/native.gno
index 22a16fc18d1..5421e231de2 100644
--- a/gnovm/stdlibs/std/native.gno
+++ b/gnovm/stdlibs/std/native.gno
@@ -65,3 +65,4 @@ func getRealm(height int) (address string, pkgPath string)
func derivePkgAddr(pkgPath string) string
func encodeBech32(prefix string, bz [20]byte) string
func decodeBech32(addr string) (prefix string, bz [20]byte, ok bool)
+func assertCallerIsRealm()
diff --git a/gnovm/stdlibs/std/native.go b/gnovm/stdlibs/std/native.go
index f185a52f249..3fe5fbb9889 100644
--- a/gnovm/stdlibs/std/native.go
+++ b/gnovm/stdlibs/std/native.go
@@ -158,6 +158,13 @@ func X_decodeBech32(addr string) (prefix string, bytes [20]byte, ok bool) {
return prefix, [20]byte(bz), true
}
+func X_assertCallerIsRealm(m *gno.Machine) {
+ frame := m.Frames[m.NumFrames()-2]
+ if path := frame.LastPackage.PkgPath; !gno.IsRealmPath(path) {
+ m.Panic(typedString("caller is not a realm"))
+ }
+}
+
func typedString(s string) gno.TypedValue {
tv := gno.TypedValue{T: gno.StringType}
tv.SetString(gno.StringValue(s))
diff --git a/gnovm/stdlibs/std/params.gno b/gnovm/stdlibs/std/params.gno
new file mode 100644
index 00000000000..ce400270cda
--- /dev/null
+++ b/gnovm/stdlibs/std/params.gno
@@ -0,0 +1,13 @@
+package std
+
+func setParamString(key string, val string)
+func setParamBool(key string, val bool)
+func setParamInt64(key string, val int64)
+func setParamUint64(key string, val uint64)
+func setParamBytes(key string, val []byte)
+
+func SetParamString(key string, val string) { setParamString(key, val) }
+func SetParamBool(key string, val bool) { setParamBool(key, val) }
+func SetParamInt64(key string, val int64) { setParamInt64(key, val) }
+func SetParamUint64(key string, val uint64) { setParamUint64(key, val) }
+func SetParamBytes(key string, val []byte) { setParamBytes(key, val) }
diff --git a/gnovm/stdlibs/std/params.go b/gnovm/stdlibs/std/params.go
new file mode 100644
index 00000000000..e21bd9912dd
--- /dev/null
+++ b/gnovm/stdlibs/std/params.go
@@ -0,0 +1,72 @@
+package std
+
+import (
+ "fmt"
+ "strings"
+ "unicode"
+
+ gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
+)
+
+// ParamsInterface is the interface through which Gno is capable of accessing
+// the blockchain's params.
+//
+// The name is what it is to avoid a collision with Gno's Params, when
+// transpiling.
+type ParamsInterface interface {
+ SetString(key, val string)
+ SetBool(key string, val bool)
+ SetInt64(key string, val int64)
+ SetUint64(key string, val uint64)
+ SetBytes(key string, val []byte)
+}
+
+func X_setParamString(m *gno.Machine, key, val string) {
+ pk := pkey(m, key, "string")
+ GetContext(m).Params.SetString(pk, val)
+}
+
+func X_setParamBool(m *gno.Machine, key string, val bool) {
+ pk := pkey(m, key, "bool")
+ GetContext(m).Params.SetBool(pk, val)
+}
+
+func X_setParamInt64(m *gno.Machine, key string, val int64) {
+ pk := pkey(m, key, "int64")
+ GetContext(m).Params.SetInt64(pk, val)
+}
+
+func X_setParamUint64(m *gno.Machine, key string, val uint64) {
+ pk := pkey(m, key, "uint64")
+ GetContext(m).Params.SetUint64(pk, val)
+}
+
+func X_setParamBytes(m *gno.Machine, key string, val []byte) {
+ pk := pkey(m, key, "bytes")
+ GetContext(m).Params.SetBytes(pk, val)
+}
+
+func pkey(m *gno.Machine, key string, kind string) string {
+ // validate key.
+ untypedKey := strings.TrimSuffix(key, "."+kind)
+ if key == untypedKey {
+ m.Panic(typedString("invalid param key: " + key))
+ }
+
+ if len(key) == 0 {
+ m.Panic(typedString("empty param key"))
+ }
+ first := rune(key[0])
+ if !unicode.IsLetter(first) && first != '_' {
+ m.Panic(typedString("invalid param key: " + key))
+ }
+ for _, char := range untypedKey[1:] {
+ if !unicode.IsLetter(char) && !unicode.IsDigit(char) && char != '_' {
+ m.Panic(typedString("invalid param key: " + key))
+ }
+ }
+
+ // decorate key with realm and type.
+ _, rlmPath := currentRealm(m)
+ return fmt.Sprintf("%s.%s", rlmPath, key)
+}
diff --git a/gnovm/stdlibs/strconv/atob.gno b/gnovm/stdlibs/strconv/atob.gno
new file mode 100644
index 00000000000..0a495008d77
--- /dev/null
+++ b/gnovm/stdlibs/strconv/atob.gno
@@ -0,0 +1,35 @@
+// Copyright 2009 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.
+
+package strconv
+
+// ParseBool returns the boolean value represented by the string.
+// It accepts 1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False.
+// Any other value returns an error.
+func ParseBool(str string) (bool, error) {
+ switch str {
+ case "1", "t", "T", "true", "TRUE", "True":
+ return true, nil
+ case "0", "f", "F", "false", "FALSE", "False":
+ return false, nil
+ }
+ return false, syntaxError("ParseBool", str)
+}
+
+// FormatBool returns "true" or "false" according to the value of b.
+func FormatBool(b bool) string {
+ if b {
+ return "true"
+ }
+ return "false"
+}
+
+// AppendBool appends "true" or "false", according to the value of b,
+// to dst and returns the extended buffer.
+func AppendBool(dst []byte, b bool) []byte {
+ if b {
+ return append(dst, "true"...)
+ }
+ return append(dst, "false"...)
+}
diff --git a/gnovm/stdlibs/strconv/atob_test.gno b/gnovm/stdlibs/strconv/atob_test.gno
new file mode 100644
index 00000000000..39746f8953d
--- /dev/null
+++ b/gnovm/stdlibs/strconv/atob_test.gno
@@ -0,0 +1,90 @@
+// Copyright 2009 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.
+
+package strconv
+
+import (
+ "bytes"
+ "testing"
+)
+
+type atobTest struct {
+ in string
+ out bool
+ err error
+}
+
+var atobtests = []atobTest{
+ {"", false, ErrSyntax},
+ {"asdf", false, ErrSyntax},
+ {"0", false, nil},
+ {"f", false, nil},
+ {"F", false, nil},
+ {"FALSE", false, nil},
+ {"false", false, nil},
+ {"False", false, nil},
+ {"1", true, nil},
+ {"t", true, nil},
+ {"T", true, nil},
+ {"TRUE", true, nil},
+ {"true", true, nil},
+ {"True", true, nil},
+}
+
+func TestParseBool(t *testing.T) {
+ for _, test := range atobtests {
+ b, e := ParseBool(test.in)
+ if test.err != nil {
+ // expect an error
+ if e == nil {
+ t.Errorf("ParseBool(%s) = nil; want %s", test.in, test.err)
+ } else {
+ // NumError assertion must succeed; it's the only thing we return.
+ if e.(*NumError).Err != test.err {
+ t.Errorf("ParseBool(%s) = %s; want %s", test.in, e, test.err)
+ }
+ }
+ } else {
+ if e != nil {
+ t.Errorf("ParseBool(%s) = %s; want nil", test.in, e)
+ }
+ if b != test.out {
+ t.Errorf("ParseBool(%s) = %t; want %t", test.in, b, test.out)
+ }
+ }
+ }
+}
+
+var boolString = map[bool]string{
+ true: "true",
+ false: "false",
+}
+
+func TestFormatBool(t *testing.T) {
+ for b, s := range boolString {
+ if f := FormatBool(b); f != s {
+ t.Errorf("FormatBool(%v) = %q; want %q", b, f, s)
+ }
+ }
+}
+
+type appendBoolTest struct {
+ b bool
+ in []byte
+ out []byte
+}
+
+var appendBoolTests = []appendBoolTest{
+ {true, []byte("foo "), []byte("foo true")},
+ {false, []byte("foo "), []byte("foo false")},
+}
+
+func TestAppendBool(t *testing.T) {
+ for _, test := range appendBoolTests {
+ b := AppendBool(test.in, test.b)
+ if !bytes.Equal(b, test.out) {
+ t.Errorf("AppendBool(%q, %v) = %q; want %q", test.in, test.b, b, test.out)
+ }
+ }
+}
diff --git a/gnovm/stdlibs/strconv/atof.gno b/gnovm/stdlibs/strconv/atof.gno
new file mode 100644
index 00000000000..8fc90425f69
--- /dev/null
+++ b/gnovm/stdlibs/strconv/atof.gno
@@ -0,0 +1,709 @@
+// Copyright 2009 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.
+
+package strconv
+
+// decimal to binary floating point conversion.
+// Algorithm:
+// 1) Store input in multiprecision decimal.
+// 2) Multiply/divide decimal by powers of two until in range [0.5, 1)
+// 3) Multiply by 2^precision and round to get mantissa.
+
+import "math"
+
+var optimize = true // set to false to force slow-path conversions for testing
+
+// commonPrefixLenIgnoreCase returns the length of the common
+// prefix of s and prefix, with the character case of s ignored.
+// The prefix argument must be all lower-case.
+func commonPrefixLenIgnoreCase(s, prefix string) int {
+ n := len(prefix)
+ if n > len(s) {
+ n = len(s)
+ }
+ for i := 0; i < n; i++ {
+ c := s[i]
+ if 'A' <= c && c <= 'Z' {
+ c += 'a' - 'A'
+ }
+ if c != prefix[i] {
+ return i
+ }
+ }
+ return n
+}
+
+// special returns the floating-point value for the special,
+// possibly signed floating-point representations inf, infinity,
+// and NaN. The result is ok if a prefix of s contains one
+// of these representations and n is the length of that prefix.
+// The character case is ignored.
+func special(s string) (f float64, n int, ok bool) {
+ if len(s) == 0 {
+ return 0, 0, false
+ }
+ sign := 1
+ nsign := 0
+ switch s[0] {
+ case '+', '-':
+ if s[0] == '-' {
+ sign = -1
+ }
+ nsign = 1
+ s = s[1:]
+ fallthrough
+ case 'i', 'I':
+ n := commonPrefixLenIgnoreCase(s, "infinity")
+ // Anything longer than "inf" is ok, but if we
+ // don't have "infinity", only consume "inf".
+ if 3 < n && n < 8 {
+ n = 3
+ }
+ if n == 3 || n == 8 {
+ return math.Inf(sign), nsign + n, true
+ }
+ case 'n', 'N':
+ if commonPrefixLenIgnoreCase(s, "nan") == 3 {
+ return math.NaN(), 3, true
+ }
+ }
+ return 0, 0, false
+}
+
+func (b *decimal) set(s string) (ok bool) {
+ i := 0
+ b.neg = false
+ b.trunc = false
+
+ // optional sign
+ if i >= len(s) {
+ return
+ }
+ switch {
+ case s[i] == '+':
+ i++
+ case s[i] == '-':
+ b.neg = true
+ i++
+ }
+
+ // digits
+ sawdot := false
+ sawdigits := false
+ for ; i < len(s); i++ {
+ switch {
+ case s[i] == '_':
+ // readFloat already checked underscores
+ continue
+ case s[i] == '.':
+ if sawdot {
+ return
+ }
+ sawdot = true
+ b.dp = b.nd
+ continue
+
+ case '0' <= s[i] && s[i] <= '9':
+ sawdigits = true
+ if s[i] == '0' && b.nd == 0 { // ignore leading zeros
+ b.dp--
+ continue
+ }
+ if b.nd < len(b.d) {
+ b.d[b.nd] = s[i]
+ b.nd++
+ } else if s[i] != '0' {
+ b.trunc = true
+ }
+ continue
+ }
+ break
+ }
+ if !sawdigits {
+ return
+ }
+ if !sawdot {
+ b.dp = b.nd
+ }
+
+ // optional exponent moves decimal point.
+ // if we read a very large, very long number,
+ // just be sure to move the decimal point by
+ // a lot (say, 100000). it doesn't matter if it's
+ // not the exact number.
+ if i < len(s) && lower(s[i]) == 'e' {
+ i++
+ if i >= len(s) {
+ return
+ }
+ esign := 1
+ if s[i] == '+' {
+ i++
+ } else if s[i] == '-' {
+ i++
+ esign = -1
+ }
+ if i >= len(s) || s[i] < '0' || s[i] > '9' {
+ return
+ }
+ e := 0
+ for ; i < len(s) && ('0' <= s[i] && s[i] <= '9' || s[i] == '_'); i++ {
+ if s[i] == '_' {
+ // readFloat already checked underscores
+ continue
+ }
+ if e < 10000 {
+ e = e*10 + int(s[i]) - '0'
+ }
+ }
+ b.dp += e * esign
+ }
+
+ if i != len(s) {
+ return
+ }
+
+ ok = true
+ return
+}
+
+// readFloat reads a decimal or hexadecimal mantissa and exponent from a float
+// string representation in s; the number may be followed by other characters.
+// readFloat reports the number of bytes consumed (i), and whether the number
+// is valid (ok).
+func readFloat(s string) (mantissa uint64, exp int, neg, trunc, hex bool, i int, ok bool) {
+ underscores := false
+
+ // optional sign
+ if i >= len(s) {
+ return
+ }
+ switch {
+ case s[i] == '+':
+ i++
+ case s[i] == '-':
+ neg = true
+ i++
+ }
+
+ // digits
+ base := uint64(10)
+ maxMantDigits := 19 // 10^19 fits in uint64
+ expChar := byte('e')
+ if i+2 < len(s) && s[i] == '0' && lower(s[i+1]) == 'x' {
+ base = 16
+ maxMantDigits = 16 // 16^16 fits in uint64
+ i += 2
+ expChar = 'p'
+ hex = true
+ }
+ sawdot := false
+ sawdigits := false
+ nd := 0
+ ndMant := 0
+ dp := 0
+loop:
+ for ; i < len(s); i++ {
+ switch c := s[i]; true {
+ case c == '_':
+ underscores = true
+ continue
+
+ case c == '.':
+ if sawdot {
+ break loop
+ }
+ sawdot = true
+ dp = nd
+ continue
+
+ case '0' <= c && c <= '9':
+ sawdigits = true
+ if c == '0' && nd == 0 { // ignore leading zeros
+ dp--
+ continue
+ }
+ nd++
+ if ndMant < maxMantDigits {
+ mantissa *= base
+ mantissa += uint64(c - '0')
+ ndMant++
+ } else if c != '0' {
+ trunc = true
+ }
+ continue
+
+ case base == 16 && 'a' <= lower(c) && lower(c) <= 'f':
+ sawdigits = true
+ nd++
+ if ndMant < maxMantDigits {
+ mantissa *= 16
+ mantissa += uint64(lower(c) - 'a' + 10)
+ ndMant++
+ } else {
+ trunc = true
+ }
+ continue
+ }
+ break
+ }
+ if !sawdigits {
+ return
+ }
+ if !sawdot {
+ dp = nd
+ }
+
+ if base == 16 {
+ dp *= 4
+ ndMant *= 4
+ }
+
+ // optional exponent moves decimal point.
+ // if we read a very large, very long number,
+ // just be sure to move the decimal point by
+ // a lot (say, 100000). it doesn't matter if it's
+ // not the exact number.
+ if i < len(s) && lower(s[i]) == expChar {
+ i++
+ if i >= len(s) {
+ return
+ }
+ esign := 1
+ if s[i] == '+' {
+ i++
+ } else if s[i] == '-' {
+ i++
+ esign = -1
+ }
+ if i >= len(s) || s[i] < '0' || s[i] > '9' {
+ return
+ }
+ e := 0
+ for ; i < len(s) && ('0' <= s[i] && s[i] <= '9' || s[i] == '_'); i++ {
+ if s[i] == '_' {
+ underscores = true
+ continue
+ }
+ if e < 10000 {
+ e = e*10 + int(s[i]) - '0'
+ }
+ }
+ dp += e * esign
+ } else if base == 16 {
+ // Must have exponent.
+ return
+ }
+
+ if mantissa != 0 {
+ exp = dp - ndMant
+ }
+
+ if underscores && !underscoreOK(s[:i]) {
+ return
+ }
+
+ ok = true
+ return
+}
+
+// decimal power of ten to binary power of two.
+var powtab = []int{1, 3, 6, 9, 13, 16, 19, 23, 26}
+
+func (d *decimal) floatBits(flt *floatInfo) (b uint64, overflow bool) {
+ var exp int
+ var mant uint64
+
+ // Zero is always a special case.
+ if d.nd == 0 {
+ mant = 0
+ exp = flt.bias
+ goto out
+ }
+
+ // Obvious overflow/underflow.
+ // These bounds are for 64-bit floats.
+ // Will have to change if we want to support 80-bit floats in the future.
+ if d.dp > 310 {
+ goto overflow
+ }
+ if d.dp < -330 {
+ // zero
+ mant = 0
+ exp = flt.bias
+ goto out
+ }
+
+ // Scale by powers of two until in range [0.5, 1.0)
+ exp = 0
+ for d.dp > 0 {
+ var n int
+ if d.dp >= len(powtab) {
+ n = 27
+ } else {
+ n = powtab[d.dp]
+ }
+ d.Shift(-n)
+ exp += n
+ }
+ for d.dp < 0 || d.dp == 0 && d.d[0] < '5' {
+ var n int
+ if -d.dp >= len(powtab) {
+ n = 27
+ } else {
+ n = powtab[-d.dp]
+ }
+ d.Shift(n)
+ exp -= n
+ }
+
+ // Our range is [0.5,1) but floating point range is [1,2).
+ exp--
+
+ // Minimum representable exponent is flt.bias+1.
+ // If the exponent is smaller, move it up and
+ // adjust d accordingly.
+ if exp < flt.bias+1 {
+ n := flt.bias + 1 - exp
+ d.Shift(-n)
+ exp += n
+ }
+
+ if exp-flt.bias >= 1<>= 1
+ exp++
+ if exp-flt.bias >= 1<>float64info.mantbits != 0 {
+ return
+ }
+ f = float64(mantissa)
+ if neg {
+ f = -f
+ }
+ switch {
+ case exp == 0:
+ // an integer.
+ return f, true
+ // Exact integers are <= 10^15.
+ // Exact powers of ten are <= 10^22.
+ case exp > 0 && exp <= 15+22: // int * 10^k
+ // If exponent is big but number of digits is not,
+ // can move a few zeros into the integer part.
+ if exp > 22 {
+ f *= float64pow10[exp-22]
+ exp = 22
+ }
+ if f > 1e15 || f < -1e15 {
+ // the exponent was really too large.
+ return
+ }
+ return f * float64pow10[exp], true
+ case exp < 0 && exp >= -22: // int / 10^k
+ return f / float64pow10[-exp], true
+ }
+ return
+}
+
+// If possible to compute mantissa*10^exp to 32-bit float f exactly,
+// entirely in floating-point math, do so, avoiding the machinery above.
+func atof32exact(mantissa uint64, exp int, neg bool) (f float32, ok bool) {
+ if mantissa>>float32info.mantbits != 0 {
+ return
+ }
+ f = float32(mantissa)
+ if neg {
+ f = -f
+ }
+ switch {
+ case exp == 0:
+ return f, true
+ // Exact integers are <= 10^7.
+ // Exact powers of ten are <= 10^10.
+ case exp > 0 && exp <= 7+10: // int * 10^k
+ // If exponent is big but number of digits is not,
+ // can move a few zeros into the integer part.
+ if exp > 10 {
+ f *= float32pow10[exp-10]
+ exp = 10
+ }
+ if f > 1e7 || f < -1e7 {
+ // the exponent was really too large.
+ return
+ }
+ return f * float32pow10[exp], true
+ case exp < 0 && exp >= -10: // int / 10^k
+ return f / float32pow10[-exp], true
+ }
+ return
+}
+
+// atofHex converts the hex floating-point string s
+// to a rounded float32 or float64 value (depending on flt==&float32info or flt==&float64info)
+// and returns it as a float64.
+// The string s has already been parsed into a mantissa, exponent, and sign (neg==true for negative).
+// If trunc is true, trailing non-zero bits have been omitted from the mantissa.
+func atofHex(s string, flt *floatInfo, mantissa uint64, exp int, neg, trunc bool) (float64, error) {
+ maxExp := 1<>(flt.mantbits+2) == 0 {
+ mantissa <<= 1
+ exp--
+ }
+ if trunc {
+ mantissa |= 1
+ }
+ for mantissa>>(1+flt.mantbits+2) != 0 {
+ mantissa = mantissa>>1 | mantissa&1
+ exp++
+ }
+
+ // If exponent is too negative,
+ // denormalize in hopes of making it representable.
+ // (The -2 is for the rounding bits.)
+ for mantissa > 1 && exp < minExp-2 {
+ mantissa = mantissa>>1 | mantissa&1
+ exp++
+ }
+
+ // Round using two bottom bits.
+ round := mantissa & 3
+ mantissa >>= 2
+ round |= mantissa & 1 // round to even (round up if mantissa is odd)
+ exp += 2
+ if round == 3 {
+ mantissa++
+ if mantissa == 1<<(1+flt.mantbits) {
+ mantissa >>= 1
+ exp++
+ }
+ }
+
+ if mantissa>>flt.mantbits == 0 { // Denormal or zero.
+ exp = flt.bias
+ }
+ var err error
+ if exp > maxExp { // infinity and range error
+ mantissa = 1 << flt.mantbits
+ exp = maxExp + 1
+ err = rangeError(fnParseFloat, s)
+ }
+
+ bits := mantissa & (1<", "(", ")", "i", "init"} {
+ in := test.in + suffix
+ _, n, err := ParseFloatPrefix(in, 64)
+ if err != nil {
+ t.Errorf("ParseFloatPrefix(%q, 64): err = %v; want no error", in, err)
+ }
+ if n != len(test.in) {
+ t.Errorf("ParseFloatPrefix(%q, 64): n = %d; want %d", in, n, len(test.in))
+ }
+ }
+ }
+}
+
+func errEqual(e1, e2 error) bool {
+ // XXX: used in place of reflect.DeepEqual
+ if e1 == nil || e2 == nil {
+ return e1 == e2
+ }
+ return e1.Error() == e2.Error()
+}
+
+func printError(err error) string {
+ // XXX: gonative fns (like fmt.Printf, t.Errorf...) do not support printing errors
+ // and it would be very complicated to add. hence we're simplifying them to strings here.
+ if err == nil {
+ return ""
+ }
+ return err.Error()
+}
+
+func testAtof(t *testing.T, opt bool) {
+ initAtof()
+ oldopt := SetOptimize(opt)
+ for i := 0; i < len(atoftests); i++ {
+ test := &atoftests[i]
+ out, err := ParseFloat(test.in, 64)
+ outs := FormatFloat(out, 'g', -1, 64)
+ if outs != test.out || !errEqual(err, test.err) {
+ t.Errorf("ParseFloat(%v, 64) = %v, %v want %v, %v",
+ test.in, out, printError(err), test.out, printError(test.err))
+ }
+
+ if float64(float32(out)) == out {
+ out, err := ParseFloat(test.in, 32)
+ out32 := float32(out)
+ if float64(out32) != out {
+ t.Errorf("ParseFloat(%v, 32) = %v, not a float32 (closest is %v)", test.in, out, float64(out32))
+ continue
+ }
+ outs := FormatFloat(float64(out32), 'g', -1, 32)
+ if outs != test.out || !errEqual(err, test.err) {
+ t.Errorf("ParseFloat(%v, 32) = %v, %v want %v, %v # %v",
+ test.in, out32, printError(err), test.out, printError(test.err), out)
+ }
+ }
+ }
+ for _, test := range atof32tests {
+ out, err := ParseFloat(test.in, 32)
+ out32 := float32(out)
+ if float64(out32) != out {
+ t.Errorf("ParseFloat(%v, 32) = %v, not a float32 (closest is %v)", test.in, out, float64(out32))
+ continue
+ }
+ outs := FormatFloat(float64(out32), 'g', -1, 32)
+ if outs != test.out || !errEqual(err, test.err) {
+ t.Errorf("ParseFloat(%v, 32) = %v, %v want %v, %v # %v",
+ test.in, out32, printError(err), test.out, printError(test.err), out)
+ }
+ }
+ SetOptimize(oldopt)
+}
+
+func TestAtof(t *testing.T) { testAtof(t, true) }
+
+func TestAtofSlow(t *testing.T) { testAtof(t, false) }
+
+func TestAtofRandom(t *testing.T) {
+ initAtof()
+ for _, test := range atofRandomTests {
+ x, _ := ParseFloat(test.s, 64)
+ switch {
+ default:
+ t.Errorf("number %s badly parsed as %b (expected %b)", test.s, x, test.x)
+ case x == test.x:
+ case math.IsNaN(test.x) && math.IsNaN(x):
+ }
+ }
+ t.Logf("tested %d random numbers", len(atofRandomTests))
+}
+
+var roundTripCases = []struct {
+ f float64
+ s string
+}{
+ // Issue 2917.
+ // This test will break the optimized conversion if the
+ // FPU is using 80-bit registers instead of 64-bit registers,
+ // usually because the operating system initialized the
+ // thread with 80-bit precision and the Go runtime didn't
+ // fix the FP control word.
+ {8865794286000691 << 39, "4.87402195346389e+27"},
+ {8865794286000692 << 39, "4.8740219534638903e+27"},
+}
+
+func TestRoundTrip(t *testing.T) {
+ for _, tt := range roundTripCases {
+ old := SetOptimize(false)
+ s := FormatFloat(tt.f, 'g', -1, 64)
+ if s != tt.s {
+ t.Errorf("no-opt FormatFloat(%b) = %s, want %s", tt.f, s, tt.s)
+ }
+ f, err := ParseFloat(tt.s, 64)
+ if f != tt.f || err != nil {
+ t.Errorf("no-opt ParseFloat(%s) = %b, %v want %b, nil", tt.s, f, err, tt.f)
+ }
+ SetOptimize(true)
+ s = FormatFloat(tt.f, 'g', -1, 64)
+ if s != tt.s {
+ t.Errorf("opt FormatFloat(%b) = %s, want %s", tt.f, s, tt.s)
+ }
+ f, err = ParseFloat(tt.s, 64)
+ if f != tt.f || err != nil {
+ t.Errorf("opt ParseFloat(%s) = %b, %v want %b, nil", tt.s, f, err, tt.f)
+ }
+ SetOptimize(old)
+ }
+}
+
+// TestRoundTrip32 tries a fraction of all finite positive float32 values.
+func TestRoundTrip32(t *testing.T) {
+ step := uint32(997)
+ if testing.Short() {
+ step = 99991
+ }
+ count := 0
+ for i := uint32(0); i < 0xff<<23; i += step {
+ f := math.Float32frombits(i)
+ if i&1 == 1 {
+ f = -f // negative
+ }
+ s := FormatFloat(float64(f), 'g', -1, 32)
+
+ parsed, err := ParseFloat(s, 32)
+ parsed32 := float32(parsed)
+ switch {
+ case err != nil:
+ t.Errorf("ParseFloat(%q, 32) gave error %s", s, err)
+ case float64(parsed32) != parsed:
+ t.Errorf("ParseFloat(%q, 32) = %v, not a float32 (nearest is %v)", s, parsed, parsed32)
+ case parsed32 != f:
+ t.Errorf("ParseFloat(%q, 32) = %b (expected %b)", s, parsed32, f)
+ }
+ count++
+ }
+ t.Logf("tested %d float32's", count)
+}
+
+// Issue 42297: a lot of code in the wild accidentally calls ParseFloat(s, 10)
+// or ParseFloat(s, 0), so allow bitSize values other than 32 and 64.
+func TestParseFloatIncorrectBitSize(t *testing.T) {
+ const s = "1.5e308"
+ const want = 1.5e308
+
+ for _, bitSize := range []int{0, 10, 100, 128} {
+ f, err := ParseFloat(s, bitSize)
+ if err != nil {
+ t.Fatalf("ParseFloat(%q, %d) gave error %s", s, bitSize, err)
+ }
+ if f != want {
+ t.Fatalf("ParseFloat(%q, %d) = %g (expected %g)", s, bitSize, f, want)
+ }
+ }
+}
+
+func BenchmarkAtof64Decimal(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ ParseFloat("33909", 64)
+ }
+}
+
+func BenchmarkAtof64Float(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ ParseFloat("339.7784", 64)
+ }
+}
+
+func BenchmarkAtof64FloatExp(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ ParseFloat("-5.09e75", 64)
+ }
+}
+
+func BenchmarkAtof64Big(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ ParseFloat("123456789123456789123456789", 64)
+ }
+}
+
+func BenchmarkAtof64RandomBits(b *testing.B) {
+ initAtof()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ ParseFloat(benchmarksRandomBits[i%1024], 64)
+ }
+}
+
+func BenchmarkAtof64RandomFloats(b *testing.B) {
+ initAtof()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ ParseFloat(benchmarksRandomNormal[i%1024], 64)
+ }
+}
+
+func BenchmarkAtof64RandomLongFloats(b *testing.B) {
+ initAtof()
+ samples := make([]string, len(atofRandomTests))
+ for i, t := range atofRandomTests {
+ samples[i] = FormatFloat(t.x, 'g', 20, 64)
+ }
+ b.ResetTimer()
+ idx := 0
+ for i := 0; i < b.N; i++ {
+ ParseFloat(samples[idx], 64)
+ idx++
+ if idx == len(samples) {
+ idx = 0
+ }
+ }
+}
+
+func BenchmarkAtof32Decimal(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ ParseFloat("33909", 32)
+ }
+}
+
+func BenchmarkAtof32Float(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ ParseFloat("339.778", 32)
+ }
+}
+
+func BenchmarkAtof32FloatExp(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ ParseFloat("12.3456e32", 32)
+ }
+}
+
+func BenchmarkAtof32Random(b *testing.B) {
+ n := uint32(997)
+ var float32strings [4096]string
+ for i := range float32strings {
+ n = (99991*n + 42) % (0xff << 23)
+ float32strings[i] = FormatFloat(float64(math.Float32frombits(n)), 'g', -1, 32)
+ }
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ ParseFloat(float32strings[i%4096], 32)
+ }
+}
+
+func BenchmarkAtof32RandomLong(b *testing.B) {
+ n := uint32(997)
+ var float32strings [4096]string
+ for i := range float32strings {
+ n = (99991*n + 42) % (0xff << 23)
+ float32strings[i] = FormatFloat(float64(math.Float32frombits(n)), 'g', 20, 32)
+ }
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ ParseFloat(float32strings[i%4096], 32)
+ }
+}
diff --git a/gnovm/stdlibs/strconv/atoi.gno b/gnovm/stdlibs/strconv/atoi.gno
new file mode 100644
index 00000000000..28b8e7f0b14
--- /dev/null
+++ b/gnovm/stdlibs/strconv/atoi.gno
@@ -0,0 +1,332 @@
+// Copyright 2009 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.
+
+package strconv
+
+import "errors"
+
+// lower(c) is a lower-case letter if and only if
+// c is either that lower-case letter or the equivalent upper-case letter.
+// Instead of writing c == 'x' || c == 'X' one can write lower(c) == 'x'.
+// Note that lower of non-letters can produce other non-letters.
+func lower(c byte) byte {
+ return c | ('x' - 'X')
+}
+
+// ErrRange indicates that a value is out of range for the target type.
+var ErrRange = errors.New("value out of range")
+
+// ErrSyntax indicates that a value does not have the right syntax for the target type.
+var ErrSyntax = errors.New("invalid syntax")
+
+// A NumError records a failed conversion.
+type NumError struct {
+ Func string // the failing function (ParseBool, ParseInt, ParseUint, ParseFloat)
+ Num string // the input
+ Err error // the reason the conversion failed (e.g. ErrRange, ErrSyntax, etc.)
+}
+
+func (e *NumError) Error() string {
+ return "strconv." + e.Func + ": " + "parsing " + Quote(e.Num) + ": " + e.Err.Error()
+}
+
+func (e *NumError) Unwrap() error { return e.Err }
+
+// cloneString returns a string copy of x.
+//
+// All ParseXXX functions allow the input string to escape to the error value.
+// This hurts strconv.ParseXXX(string(b)) calls where b is []byte since
+// the conversion from []byte must allocate a string on the heap.
+// If we assume errors are infrequent, then we can avoid escaping the input
+// back to the output by copying it first. This allows the compiler to call
+// strconv.ParseXXX without a heap allocation for most []byte to string
+// conversions, since it can now prove that the string cannot escape Parse.
+//
+// TODO: Use strings.Clone instead? However, we cannot depend on "strings"
+// since it incurs a transitive dependency on "unicode".
+// Either move strings.Clone to an internal/bytealg or make the
+// "strings" to "unicode" dependency lighter (see https://go.dev/issue/54098).
+func cloneString(x string) string { return string([]byte(x)) }
+
+func syntaxError(fn, str string) *NumError {
+ return &NumError{fn, cloneString(str), ErrSyntax}
+}
+
+func rangeError(fn, str string) *NumError {
+ return &NumError{fn, cloneString(str), ErrRange}
+}
+
+func baseError(fn, str string, base int) *NumError {
+ return &NumError{fn, cloneString(str), errors.New("invalid base " + Itoa(base))}
+}
+
+func bitSizeError(fn, str string, bitSize int) *NumError {
+ return &NumError{fn, cloneString(str), errors.New("invalid bit size " + Itoa(bitSize))}
+}
+
+const intSize = 32 << (^uint(0) >> 63)
+
+// IntSize is the size in bits of an int or uint value.
+const IntSize = intSize
+
+const maxUint64 = 1<<64 - 1
+
+// ParseUint is like ParseInt but for unsigned numbers.
+//
+// A sign prefix is not permitted.
+func ParseUint(s string, base int, bitSize int) (uint64, error) {
+ const fnParseUint = "ParseUint"
+
+ if s == "" {
+ return 0, syntaxError(fnParseUint, s)
+ }
+
+ base0 := base == 0
+
+ s0 := s
+ switch {
+ case 2 <= base && base <= 36:
+ // valid base; nothing to do
+
+ case base == 0:
+ // Look for octal, hex prefix.
+ base = 10
+ if s[0] == '0' {
+ switch {
+ case len(s) >= 3 && lower(s[1]) == 'b':
+ base = 2
+ s = s[2:]
+ case len(s) >= 3 && lower(s[1]) == 'o':
+ base = 8
+ s = s[2:]
+ case len(s) >= 3 && lower(s[1]) == 'x':
+ base = 16
+ s = s[2:]
+ default:
+ base = 8
+ s = s[1:]
+ }
+ }
+
+ default:
+ return 0, baseError(fnParseUint, s0, base)
+ }
+
+ if bitSize == 0 {
+ bitSize = IntSize
+ } else if bitSize < 0 || bitSize > 64 {
+ return 0, bitSizeError(fnParseUint, s0, bitSize)
+ }
+
+ // Cutoff is the smallest number such that cutoff*base > maxUint64.
+ // Use compile-time constants for common cases.
+ var cutoff uint64
+ switch base {
+ case 10:
+ cutoff = maxUint64/10 + 1
+ case 16:
+ cutoff = maxUint64/16 + 1
+ default:
+ cutoff = maxUint64/uint64(base) + 1
+ }
+
+ maxVal := uint64(1)<= byte(base) {
+ return 0, syntaxError(fnParseUint, s0)
+ }
+
+ if n >= cutoff {
+ // n*base overflows
+ return maxVal, rangeError(fnParseUint, s0)
+ }
+ n *= uint64(base)
+
+ n1 := n + uint64(d)
+ if n1 < n || n1 > maxVal {
+ // n+d overflows
+ return maxVal, rangeError(fnParseUint, s0)
+ }
+ n = n1
+ }
+
+ if underscores && !underscoreOK(s0) {
+ return 0, syntaxError(fnParseUint, s0)
+ }
+
+ return n, nil
+}
+
+// ParseInt interprets a string s in the given base (0, 2 to 36) and
+// bit size (0 to 64) and returns the corresponding value i.
+//
+// The string may begin with a leading sign: "+" or "-".
+//
+// If the base argument is 0, the true base is implied by the string's
+// prefix following the sign (if present): 2 for "0b", 8 for "0" or "0o",
+// 16 for "0x", and 10 otherwise. Also, for argument base 0 only,
+// underscore characters are permitted as defined by the Go syntax for
+// [integer literals].
+//
+// The bitSize argument specifies the integer type
+// that the result must fit into. Bit sizes 0, 8, 16, 32, and 64
+// correspond to int, int8, int16, int32, and int64.
+// If bitSize is below 0 or above 64, an error is returned.
+//
+// The errors that ParseInt returns have concrete type *NumError
+// and include err.Num = s. If s is empty or contains invalid
+// digits, err.Err = ErrSyntax and the returned value is 0;
+// if the value corresponding to s cannot be represented by a
+// signed integer of the given size, err.Err = ErrRange and the
+// returned value is the maximum magnitude integer of the
+// appropriate bitSize and sign.
+//
+// [integer literals]: https://go.dev/ref/spec#Integer_literals
+func ParseInt(s string, base int, bitSize int) (i int64, err error) {
+ const fnParseInt = "ParseInt"
+
+ if s == "" {
+ return 0, syntaxError(fnParseInt, s)
+ }
+
+ // Pick off leading sign.
+ s0 := s
+ neg := false
+ if s[0] == '+' {
+ s = s[1:]
+ } else if s[0] == '-' {
+ neg = true
+ s = s[1:]
+ }
+
+ // Convert unsigned and check range.
+ var un uint64
+ un, err = ParseUint(s, base, bitSize)
+ if err != nil && err.(*NumError).Err != ErrRange {
+ err.(*NumError).Func = fnParseInt
+ err.(*NumError).Num = cloneString(s0)
+ return 0, err
+ }
+
+ if bitSize == 0 {
+ bitSize = IntSize
+ }
+
+ cutoff := uint64(1 << uint(bitSize-1))
+ if !neg && un >= cutoff {
+ return int64(cutoff - 1), rangeError(fnParseInt, s0)
+ }
+ if neg && un > cutoff {
+ return -int64(cutoff), rangeError(fnParseInt, s0)
+ }
+ n := int64(un)
+ if neg {
+ n = -n
+ }
+ return n, nil
+}
+
+// Atoi is equivalent to ParseInt(s, 10, 0), converted to type int.
+func Atoi(s string) (int, error) {
+ const fnAtoi = "Atoi"
+
+ sLen := len(s)
+ if intSize == 32 && (0 < sLen && sLen < 10) ||
+ intSize == 64 && (0 < sLen && sLen < 19) {
+ // Fast path for small integers that fit int type.
+ s0 := s
+ if s[0] == '-' || s[0] == '+' {
+ s = s[1:]
+ if len(s) < 1 {
+ return 0, syntaxError(fnAtoi, s0)
+ }
+ }
+
+ n := 0
+ for _, ch := range []byte(s) {
+ ch -= '0'
+ if ch > 9 {
+ return 0, syntaxError(fnAtoi, s0)
+ }
+ n = n*10 + int(ch)
+ }
+ if s0[0] == '-' {
+ n = -n
+ }
+ return n, nil
+ }
+
+ // Slow path for invalid, big, or underscored integers.
+ i64, err := ParseInt(s, 10, 0)
+ if nerr, ok := err.(*NumError); ok {
+ nerr.Func = fnAtoi
+ }
+ return int(i64), err
+}
+
+// underscoreOK reports whether the underscores in s are allowed.
+// Checking them in this one function lets all the parsers skip over them simply.
+// Underscore must appear only between digits or between a base prefix and a digit.
+func underscoreOK(s string) bool {
+ // saw tracks the last character (class) we saw:
+ // ^ for beginning of number,
+ // 0 for a digit or base prefix,
+ // _ for an underscore,
+ // ! for none of the above.
+ saw := '^'
+ i := 0
+
+ // Optional sign.
+ if len(s) >= 1 && (s[0] == '-' || s[0] == '+') {
+ s = s[1:]
+ }
+
+ // Optional base prefix.
+ hex := false
+ if len(s) >= 2 && s[0] == '0' && (lower(s[1]) == 'b' || lower(s[1]) == 'o' || lower(s[1]) == 'x') {
+ i = 2
+ saw = '0' // base prefix counts as a digit for "underscore as digit separator"
+ hex = lower(s[1]) == 'x'
+ }
+
+ // Number proper.
+ for ; i < len(s); i++ {
+ // Digits are always okay.
+ if '0' <= s[i] && s[i] <= '9' || hex && 'a' <= lower(s[i]) && lower(s[i]) <= 'f' {
+ saw = '0'
+ continue
+ }
+ // Underscore must follow digit.
+ if s[i] == '_' {
+ if saw != '0' {
+ return false
+ }
+ saw = '_'
+ continue
+ }
+ // Underscore must also be followed by digit.
+ if saw == '_' {
+ return false
+ }
+ // Saw non-digit, non-underscore.
+ saw = '!'
+ }
+ return saw != '_'
+}
diff --git a/gnovm/stdlibs/strconv/atoi_test.gno b/gnovm/stdlibs/strconv/atoi_test.gno
new file mode 100644
index 00000000000..cb150628f5c
--- /dev/null
+++ b/gnovm/stdlibs/strconv/atoi_test.gno
@@ -0,0 +1,677 @@
+// Copyright 2009 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.
+
+package strconv
+
+import (
+ "errors"
+ "fmt"
+ "testing"
+)
+
+type parseUint64Test struct {
+ in string
+ out uint64
+ err error
+}
+
+var parseUint64Tests = []parseUint64Test{
+ {"", 0, ErrSyntax},
+ {"0", 0, nil},
+ {"1", 1, nil},
+ {"12345", 12345, nil},
+ {"012345", 12345, nil},
+ {"12345x", 0, ErrSyntax},
+ {"98765432100", 98765432100, nil},
+ {"18446744073709551615", 1<<64 - 1, nil},
+ {"18446744073709551616", 1<<64 - 1, ErrRange},
+ {"18446744073709551620", 1<<64 - 1, ErrRange},
+ {"1_2_3_4_5", 0, ErrSyntax}, // base=10 so no underscores allowed
+ {"_12345", 0, ErrSyntax},
+ {"1__2345", 0, ErrSyntax},
+ {"12345_", 0, ErrSyntax},
+ {"-0", 0, ErrSyntax},
+ {"-1", 0, ErrSyntax},
+ {"+1", 0, ErrSyntax},
+}
+
+type parseUint64BaseTest struct {
+ in string
+ base int
+ out uint64
+ err error
+}
+
+var parseUint64BaseTests = []parseUint64BaseTest{
+ {"", 0, 0, ErrSyntax},
+ {"0", 0, 0, nil},
+ {"0x", 0, 0, ErrSyntax},
+ {"0X", 0, 0, ErrSyntax},
+ {"1", 0, 1, nil},
+ {"12345", 0, 12345, nil},
+ {"012345", 0, 012345, nil},
+ {"0x12345", 0, 0x12345, nil},
+ {"0X12345", 0, 0x12345, nil},
+ {"12345x", 0, 0, ErrSyntax},
+ {"0xabcdefg123", 0, 0, ErrSyntax},
+ {"123456789abc", 0, 0, ErrSyntax},
+ {"98765432100", 0, 98765432100, nil},
+ {"18446744073709551615", 0, 1<<64 - 1, nil},
+ {"18446744073709551616", 0, 1<<64 - 1, ErrRange},
+ {"18446744073709551620", 0, 1<<64 - 1, ErrRange},
+ {"0xFFFFFFFFFFFFFFFF", 0, 1<<64 - 1, nil},
+ {"0x10000000000000000", 0, 1<<64 - 1, ErrRange},
+ {"01777777777777777777777", 0, 1<<64 - 1, nil},
+ {"01777777777777777777778", 0, 0, ErrSyntax},
+ {"02000000000000000000000", 0, 1<<64 - 1, ErrRange},
+ {"0200000000000000000000", 0, 1 << 61, nil},
+ {"0b", 0, 0, ErrSyntax},
+ {"0B", 0, 0, ErrSyntax},
+ {"0b101", 0, 5, nil},
+ {"0B101", 0, 5, nil},
+ {"0o", 0, 0, ErrSyntax},
+ {"0O", 0, 0, ErrSyntax},
+ {"0o377", 0, 255, nil},
+ {"0O377", 0, 255, nil},
+
+ // underscores allowed with base == 0 only
+ {"1_2_3_4_5", 0, 12345, nil}, // base 0 => 10
+ {"_12345", 0, 0, ErrSyntax},
+ {"1__2345", 0, 0, ErrSyntax},
+ {"12345_", 0, 0, ErrSyntax},
+
+ {"1_2_3_4_5", 10, 0, ErrSyntax}, // base 10
+ {"_12345", 10, 0, ErrSyntax},
+ {"1__2345", 10, 0, ErrSyntax},
+ {"12345_", 10, 0, ErrSyntax},
+
+ {"0x_1_2_3_4_5", 0, 0x12345, nil}, // base 0 => 16
+ {"_0x12345", 0, 0, ErrSyntax},
+ {"0x__12345", 0, 0, ErrSyntax},
+ {"0x1__2345", 0, 0, ErrSyntax},
+ {"0x1234__5", 0, 0, ErrSyntax},
+ {"0x12345_", 0, 0, ErrSyntax},
+
+ {"1_2_3_4_5", 16, 0, ErrSyntax}, // base 16
+ {"_12345", 16, 0, ErrSyntax},
+ {"1__2345", 16, 0, ErrSyntax},
+ {"1234__5", 16, 0, ErrSyntax},
+ {"12345_", 16, 0, ErrSyntax},
+
+ {"0_1_2_3_4_5", 0, 012345, nil}, // base 0 => 8 (0377)
+ {"_012345", 0, 0, ErrSyntax},
+ {"0__12345", 0, 0, ErrSyntax},
+ {"01234__5", 0, 0, ErrSyntax},
+ {"012345_", 0, 0, ErrSyntax},
+
+ {"0o_1_2_3_4_5", 0, 012345, nil}, // base 0 => 8 (0o377)
+ {"_0o12345", 0, 0, ErrSyntax},
+ {"0o__12345", 0, 0, ErrSyntax},
+ {"0o1234__5", 0, 0, ErrSyntax},
+ {"0o12345_", 0, 0, ErrSyntax},
+
+ {"0_1_2_3_4_5", 8, 0, ErrSyntax}, // base 8
+ {"_012345", 8, 0, ErrSyntax},
+ {"0__12345", 8, 0, ErrSyntax},
+ {"01234__5", 8, 0, ErrSyntax},
+ {"012345_", 8, 0, ErrSyntax},
+
+ {"0b_1_0_1", 0, 5, nil}, // base 0 => 2 (0b101)
+ {"_0b101", 0, 0, ErrSyntax},
+ {"0b__101", 0, 0, ErrSyntax},
+ {"0b1__01", 0, 0, ErrSyntax},
+ {"0b10__1", 0, 0, ErrSyntax},
+ {"0b101_", 0, 0, ErrSyntax},
+
+ {"1_0_1", 2, 0, ErrSyntax}, // base 2
+ {"_101", 2, 0, ErrSyntax},
+ {"1_01", 2, 0, ErrSyntax},
+ {"10_1", 2, 0, ErrSyntax},
+ {"101_", 2, 0, ErrSyntax},
+}
+
+type parseInt64Test struct {
+ in string
+ out int64
+ err error
+}
+
+var parseInt64Tests = []parseInt64Test{
+ {"", 0, ErrSyntax},
+ {"0", 0, nil},
+ {"-0", 0, nil},
+ {"+0", 0, nil},
+ {"1", 1, nil},
+ {"-1", -1, nil},
+ {"+1", 1, nil},
+ {"12345", 12345, nil},
+ {"-12345", -12345, nil},
+ {"012345", 12345, nil},
+ {"-012345", -12345, nil},
+ {"98765432100", 98765432100, nil},
+ {"-98765432100", -98765432100, nil},
+ {"9223372036854775807", 1<<63 - 1, nil},
+ {"-9223372036854775807", -(1<<63 - 1), nil},
+ {"9223372036854775808", 1<<63 - 1, ErrRange},
+ {"-9223372036854775808", -1 << 63, nil},
+ {"9223372036854775809", 1<<63 - 1, ErrRange},
+ {"-9223372036854775809", -1 << 63, ErrRange},
+ {"-1_2_3_4_5", 0, ErrSyntax}, // base=10 so no underscores allowed
+ {"-_12345", 0, ErrSyntax},
+ {"_12345", 0, ErrSyntax},
+ {"1__2345", 0, ErrSyntax},
+ {"12345_", 0, ErrSyntax},
+ {"123%45", 0, ErrSyntax},
+}
+
+type parseInt64BaseTest struct {
+ in string
+ base int
+ out int64
+ err error
+}
+
+var parseInt64BaseTests = []parseInt64BaseTest{
+ {"", 0, 0, ErrSyntax},
+ {"0", 0, 0, nil},
+ {"-0", 0, 0, nil},
+ {"1", 0, 1, nil},
+ {"-1", 0, -1, nil},
+ {"12345", 0, 12345, nil},
+ {"-12345", 0, -12345, nil},
+ {"012345", 0, 012345, nil},
+ {"-012345", 0, -012345, nil},
+ {"0x12345", 0, 0x12345, nil},
+ {"-0X12345", 0, -0x12345, nil},
+ {"12345x", 0, 0, ErrSyntax},
+ {"-12345x", 0, 0, ErrSyntax},
+ {"98765432100", 0, 98765432100, nil},
+ {"-98765432100", 0, -98765432100, nil},
+ {"9223372036854775807", 0, 1<<63 - 1, nil},
+ {"-9223372036854775807", 0, -(1<<63 - 1), nil},
+ {"9223372036854775808", 0, 1<<63 - 1, ErrRange},
+ {"-9223372036854775808", 0, -1 << 63, nil},
+ {"9223372036854775809", 0, 1<<63 - 1, ErrRange},
+ {"-9223372036854775809", 0, -1 << 63, ErrRange},
+
+ // other bases
+ {"g", 17, 16, nil},
+ {"10", 25, 25, nil},
+ {"holycow", 35, (((((17*35+24)*35+21)*35+34)*35+12)*35+24)*35 + 32, nil},
+ {"holycow", 36, (((((17*36+24)*36+21)*36+34)*36+12)*36+24)*36 + 32, nil},
+
+ // base 2
+ {"0", 2, 0, nil},
+ {"-1", 2, -1, nil},
+ {"1010", 2, 10, nil},
+ {"1000000000000000", 2, 1 << 15, nil},
+ {"111111111111111111111111111111111111111111111111111111111111111", 2, 1<<63 - 1, nil},
+ {"1000000000000000000000000000000000000000000000000000000000000000", 2, 1<<63 - 1, ErrRange},
+ {"-1000000000000000000000000000000000000000000000000000000000000000", 2, -1 << 63, nil},
+ {"-1000000000000000000000000000000000000000000000000000000000000001", 2, -1 << 63, ErrRange},
+
+ // base 8
+ {"-10", 8, -8, nil},
+ {"57635436545", 8, 057635436545, nil},
+ {"100000000", 8, 1 << 24, nil},
+
+ // base 16
+ {"10", 16, 16, nil},
+ {"-123456789abcdef", 16, -0x123456789abcdef, nil},
+ {"7fffffffffffffff", 16, 1<<63 - 1, nil},
+
+ // underscores
+ {"-0x_1_2_3_4_5", 0, -0x12345, nil},
+ {"0x_1_2_3_4_5", 0, 0x12345, nil},
+ {"-_0x12345", 0, 0, ErrSyntax},
+ {"_-0x12345", 0, 0, ErrSyntax},
+ {"_0x12345", 0, 0, ErrSyntax},
+ {"0x__12345", 0, 0, ErrSyntax},
+ {"0x1__2345", 0, 0, ErrSyntax},
+ {"0x1234__5", 0, 0, ErrSyntax},
+ {"0x12345_", 0, 0, ErrSyntax},
+
+ {"-0_1_2_3_4_5", 0, -012345, nil}, // octal
+ {"0_1_2_3_4_5", 0, 012345, nil}, // octal
+ {"-_012345", 0, 0, ErrSyntax},
+ {"_-012345", 0, 0, ErrSyntax},
+ {"_012345", 0, 0, ErrSyntax},
+ {"0__12345", 0, 0, ErrSyntax},
+ {"01234__5", 0, 0, ErrSyntax},
+ {"012345_", 0, 0, ErrSyntax},
+
+ {"+0xf", 0, 0xf, nil},
+ {"-0xf", 0, -0xf, nil},
+ {"0x+f", 0, 0, ErrSyntax},
+ {"0x-f", 0, 0, ErrSyntax},
+}
+
+type parseUint32Test struct {
+ in string
+ out uint32
+ err error
+}
+
+var parseUint32Tests = []parseUint32Test{
+ {"", 0, ErrSyntax},
+ {"0", 0, nil},
+ {"1", 1, nil},
+ {"12345", 12345, nil},
+ {"012345", 12345, nil},
+ {"12345x", 0, ErrSyntax},
+ {"987654321", 987654321, nil},
+ {"4294967295", 1<<32 - 1, nil},
+ {"4294967296", 1<<32 - 1, ErrRange},
+ {"1_2_3_4_5", 0, ErrSyntax}, // base=10 so no underscores allowed
+ {"_12345", 0, ErrSyntax},
+ {"_12345", 0, ErrSyntax},
+ {"1__2345", 0, ErrSyntax},
+ {"12345_", 0, ErrSyntax},
+}
+
+type parseInt32Test struct {
+ in string
+ out int32
+ err error
+}
+
+var parseInt32Tests = []parseInt32Test{
+ {"", 0, ErrSyntax},
+ {"0", 0, nil},
+ {"-0", 0, nil},
+ {"1", 1, nil},
+ {"-1", -1, nil},
+ {"12345", 12345, nil},
+ {"-12345", -12345, nil},
+ {"012345", 12345, nil},
+ {"-012345", -12345, nil},
+ {"12345x", 0, ErrSyntax},
+ {"-12345x", 0, ErrSyntax},
+ {"987654321", 987654321, nil},
+ {"-987654321", -987654321, nil},
+ {"2147483647", 1<<31 - 1, nil},
+ {"-2147483647", -(1<<31 - 1), nil},
+ {"2147483648", 1<<31 - 1, ErrRange},
+ {"-2147483648", -1 << 31, nil},
+ {"2147483649", 1<<31 - 1, ErrRange},
+ {"-2147483649", -1 << 31, ErrRange},
+ {"-1_2_3_4_5", 0, ErrSyntax}, // base=10 so no underscores allowed
+ {"-_12345", 0, ErrSyntax},
+ {"_12345", 0, ErrSyntax},
+ {"1__2345", 0, ErrSyntax},
+ {"12345_", 0, ErrSyntax},
+ {"123%45", 0, ErrSyntax},
+}
+
+type numErrorTest struct {
+ num, want string
+}
+
+var numErrorTests = []numErrorTest{
+ {"0", `strconv.ParseFloat: parsing "0": failed`},
+ {"`", "strconv.ParseFloat: parsing \"`\": failed"},
+ {"1\x00.2", `strconv.ParseFloat: parsing "1\x00.2": failed`},
+}
+
+func init() {
+ // The parse routines return NumErrors wrapping
+ // the error and the string. Convert the tables above.
+ for i := range parseUint64Tests {
+ test := &parseUint64Tests[i]
+ if test.err != nil {
+ test.err = &NumError{"ParseUint", test.in, test.err}
+ }
+ }
+ for i := range parseUint64BaseTests {
+ test := &parseUint64BaseTests[i]
+ if test.err != nil {
+ test.err = &NumError{"ParseUint", test.in, test.err}
+ }
+ }
+ for i := range parseInt64Tests {
+ test := &parseInt64Tests[i]
+ if test.err != nil {
+ test.err = &NumError{"ParseInt", test.in, test.err}
+ }
+ }
+ for i := range parseInt64BaseTests {
+ test := &parseInt64BaseTests[i]
+ if test.err != nil {
+ test.err = &NumError{"ParseInt", test.in, test.err}
+ }
+ }
+ for i := range parseUint32Tests {
+ test := &parseUint32Tests[i]
+ if test.err != nil {
+ test.err = &NumError{"ParseUint", test.in, test.err}
+ }
+ }
+ for i := range parseInt32Tests {
+ test := &parseInt32Tests[i]
+ if test.err != nil {
+ test.err = &NumError{"ParseInt", test.in, test.err}
+ }
+ }
+}
+
+func TestParseUint32(t *testing.T) {
+ for i := range parseUint32Tests {
+ test := &parseUint32Tests[i]
+ out, err := ParseUint(test.in, 10, 32)
+ if uint64(test.out) != out || !errEqual(test.err, err) {
+ t.Errorf("ParseUint(%q, 10, 32) = %v, %v want %v, %v",
+ test.in, out, err, test.out, test.err)
+ }
+ }
+}
+
+func TestParseUint64(t *testing.T) {
+ for i := range parseUint64Tests {
+ test := &parseUint64Tests[i]
+ out, err := ParseUint(test.in, 10, 64)
+ if test.out != out || !errEqual(test.err, err) {
+ t.Errorf("ParseUint(%q, 10, 64) = %v, %v want %v, %v",
+ test.in, out, err, test.out, test.err)
+ }
+ }
+}
+
+func TestParseUint64Base(t *testing.T) {
+ for i := range parseUint64BaseTests {
+ test := &parseUint64BaseTests[i]
+ out, err := ParseUint(test.in, test.base, 64)
+ if test.out != out || !errEqual(test.err, err) {
+ t.Errorf("ParseUint(%q, %v, 64) = %v, %v want %v, %v",
+ test.in, test.base, out, err, test.out, test.err)
+ }
+ }
+}
+
+func TestParseInt32(t *testing.T) {
+ for i := range parseInt32Tests {
+ test := &parseInt32Tests[i]
+ out, err := ParseInt(test.in, 10, 32)
+ if int64(test.out) != out || !errEqual(test.err, err) {
+ t.Errorf("ParseInt(%q, 10 ,32) = %v, %v want %v, %v",
+ test.in, out, err, test.out, test.err)
+ }
+ }
+}
+
+func TestParseInt64(t *testing.T) {
+ for i := range parseInt64Tests {
+ test := &parseInt64Tests[i]
+ out, err := ParseInt(test.in, 10, 64)
+ if test.out != out || !errEqual(test.err, err) {
+ t.Errorf("ParseInt(%q, 10, 64) = %v, %v want %v, %v",
+ test.in, out, err, test.out, test.err)
+ }
+ }
+}
+
+func TestParseInt64Base(t *testing.T) {
+ for i := range parseInt64BaseTests {
+ test := &parseInt64BaseTests[i]
+ out, err := ParseInt(test.in, test.base, 64)
+ if test.out != out || !errEqual(test.err, err) {
+ t.Errorf("ParseInt(%q, %v, 64) = %v, %v want %v, %v",
+ test.in, test.base, out, err, test.out, test.err)
+ }
+ }
+}
+
+func TestParseUint(t *testing.T) {
+ switch IntSize {
+ case 32:
+ for i := range parseUint32Tests {
+ test := &parseUint32Tests[i]
+ out, err := ParseUint(test.in, 10, 0)
+ if uint64(test.out) != out || !errEqual(test.err, err) {
+ t.Errorf("ParseUint(%q, 10, 0) = %v, %v want %v, %v",
+ test.in, out, err, test.out, test.err)
+ }
+ }
+ case 64:
+ for i := range parseUint64Tests {
+ test := &parseUint64Tests[i]
+ out, err := ParseUint(test.in, 10, 0)
+ if test.out != out || !errEqual(test.err, err) {
+ t.Errorf("ParseUint(%q, 10, 0) = %v, %v want %v, %v",
+ test.in, out, err, test.out, test.err)
+ }
+ }
+ }
+}
+
+func TestParseInt(t *testing.T) {
+ switch IntSize {
+ case 32:
+ for i := range parseInt32Tests {
+ test := &parseInt32Tests[i]
+ out, err := ParseInt(test.in, 10, 0)
+ if int64(test.out) != out || !errEqual(test.err, err) {
+ t.Errorf("ParseInt(%q, 10, 0) = %v, %v want %v, %v",
+ test.in, out, err, test.out, test.err)
+ }
+ }
+ case 64:
+ for i := range parseInt64Tests {
+ test := &parseInt64Tests[i]
+ out, err := ParseInt(test.in, 10, 0)
+ if test.out != out || !errEqual(test.err, err) {
+ t.Errorf("ParseInt(%q, 10, 0) = %v, %v want %v, %v",
+ test.in, out, err, test.out, test.err)
+ }
+ }
+ }
+}
+
+func TestAtoi(t *testing.T) {
+ switch IntSize {
+ case 32:
+ for i := range parseInt32Tests {
+ test := &parseInt32Tests[i]
+ out, err := Atoi(test.in)
+ var testErr error
+ if test.err != nil {
+ testErr = &NumError{"Atoi", test.in, test.err.(*NumError).Err}
+ }
+ if int(test.out) != out || !errEqual(testErr, err) {
+ t.Errorf("Atoi(%q) = %v, %v want %v, %v",
+ test.in, out, err, test.out, testErr)
+ }
+ }
+ case 64:
+ for i := range parseInt64Tests {
+ test := &parseInt64Tests[i]
+ out, err := Atoi(test.in)
+ var testErr error
+ if test.err != nil {
+ testErr = &NumError{"Atoi", test.in, test.err.(*NumError).Err}
+ }
+ if test.out != int64(out) || !errEqual(testErr, err) {
+ t.Errorf("Atoi(%q) = %v, %v want %v, %v",
+ test.in, out, err, test.out, testErr)
+ }
+ }
+ }
+}
+
+func bitSizeErrStub(name string, bitSize int) error {
+ return BitSizeError(name, "0", bitSize)
+}
+
+func baseErrStub(name string, base int) error {
+ return BaseError(name, "0", base)
+}
+
+func noErrStub(name string, arg int) error {
+ return nil
+}
+
+type parseErrorTest struct {
+ arg int
+ errStub func(name string, arg int) error
+}
+
+var parseBitSizeTests = []parseErrorTest{
+ {-1, bitSizeErrStub},
+ {0, noErrStub},
+ {64, noErrStub},
+ {65, bitSizeErrStub},
+}
+
+var parseBaseTests = []parseErrorTest{
+ {-1, baseErrStub},
+ {0, noErrStub},
+ {1, baseErrStub},
+ {2, noErrStub},
+ {36, noErrStub},
+ {37, baseErrStub},
+}
+
+func equalError(a, b error) bool {
+ if a == nil {
+ return b == nil
+ }
+ if b == nil {
+ return a == nil
+ }
+ return a.Error() == b.Error()
+}
+
+func TestParseIntBitSize(t *testing.T) {
+ for i := range parseBitSizeTests {
+ test := &parseBitSizeTests[i]
+ testErr := test.errStub("ParseInt", test.arg)
+ _, err := ParseInt("0", 0, test.arg)
+ if !equalError(testErr, err) {
+ t.Errorf("ParseInt(\"0\", 0, %v) = 0, %v want 0, %v",
+ test.arg, err, testErr)
+ }
+ }
+}
+
+func TestParseUintBitSize(t *testing.T) {
+ for i := range parseBitSizeTests {
+ test := &parseBitSizeTests[i]
+ testErr := test.errStub("ParseUint", test.arg)
+ _, err := ParseUint("0", 0, test.arg)
+ if !equalError(testErr, err) {
+ t.Errorf("ParseUint(\"0\", 0, %v) = 0, %v want 0, %v",
+ test.arg, err, testErr)
+ }
+ }
+}
+
+func TestParseIntBase(t *testing.T) {
+ for i := range parseBaseTests {
+ test := &parseBaseTests[i]
+ testErr := test.errStub("ParseInt", test.arg)
+ _, err := ParseInt("0", test.arg, 0)
+ if !equalError(testErr, err) {
+ t.Errorf("ParseInt(\"0\", %v, 0) = 0, %v want 0, %v",
+ test.arg, err, testErr)
+ }
+ }
+}
+
+func TestParseUintBase(t *testing.T) {
+ for i := range parseBaseTests {
+ test := &parseBaseTests[i]
+ testErr := test.errStub("ParseUint", test.arg)
+ _, err := ParseUint("0", test.arg, 0)
+ if !equalError(testErr, err) {
+ t.Errorf("ParseUint(\"0\", %v, 0) = 0, %v want 0, %v",
+ test.arg, err, testErr)
+ }
+ }
+}
+
+func TestNumError(t *testing.T) {
+ for _, test := range numErrorTests {
+ err := &NumError{
+ Func: "ParseFloat",
+ Num: test.num,
+ Err: errors.New("failed"),
+ }
+ if got := err.Error(); got != test.want {
+ t.Errorf(`(&NumError{"ParseFloat", %q, "failed"}).Error() = %v, want %v`, test.num, got, test.want)
+ }
+ }
+}
+
+/* XXX: add when we support reflection / error un/wrapping.
+func TestNumErrorUnwrap(t *testing.T) {
+ err := &NumError{Err: ErrSyntax}
+ if !errEqual(err, ErrSyntax) {
+ t.Error("errors.Is failed, wanted success")
+ }
+}
+*/
+
+func BenchmarkParseInt(b *testing.B) {
+ b.Run("Pos", func(b *testing.B) {
+ benchmarkParseInt(b, 1)
+ })
+ b.Run("Neg", func(b *testing.B) {
+ benchmarkParseInt(b, -1)
+ })
+}
+
+type benchCase struct {
+ name string
+ num int64
+}
+
+func benchmarkParseInt(b *testing.B, neg int) {
+ cases := []benchCase{
+ {"7bit", 1<<7 - 1},
+ {"26bit", 1<<26 - 1},
+ {"31bit", 1<<31 - 1},
+ {"56bit", 1<<56 - 1},
+ {"63bit", 1<<63 - 1},
+ }
+ for _, cs := range cases {
+ b.Run(cs.name, func(b *testing.B) {
+ s := fmt.Sprintf("%d", cs.num*int64(neg))
+ for i := 0; i < b.N; i++ {
+ out, _ := ParseInt(s, 10, 64)
+ BenchSink += int(out)
+ }
+ })
+ }
+}
+
+func BenchmarkAtoi(b *testing.B) {
+ b.Run("Pos", func(b *testing.B) {
+ benchmarkAtoi(b, 1)
+ })
+ b.Run("Neg", func(b *testing.B) {
+ benchmarkAtoi(b, -1)
+ })
+}
+
+func benchmarkAtoi(b *testing.B, neg int) {
+ cases := []benchCase{
+ {"7bit", 1<<7 - 1},
+ {"26bit", 1<<26 - 1},
+ {"31bit", 1<<31 - 1},
+ }
+ if IntSize == 64 {
+ cases = append(cases, []benchCase{
+ {"56bit", 1<<56 - 1},
+ {"63bit", 1<<63 - 1},
+ }...)
+ }
+ for _, cs := range cases {
+ b.Run(cs.name, func(b *testing.B) {
+ s := fmt.Sprintf("%d", cs.num*int64(neg))
+ for i := 0; i < b.N; i++ {
+ out, _ := Atoi(s)
+ BenchSink += out
+ }
+ })
+ }
+}
diff --git a/gnovm/stdlibs/strconv/bytealg.gno b/gnovm/stdlibs/strconv/bytealg.gno
new file mode 100644
index 00000000000..2c813885f53
--- /dev/null
+++ b/gnovm/stdlibs/strconv/bytealg.gno
@@ -0,0 +1,12 @@
+// Copyright 2009 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.
+
+package strconv
+
+import "internal/bytealg"
+
+// index returns the index of the first instance of c in s, or -1 if missing.
+func index(s string, c byte) int {
+ return bytealg.IndexByteString(s, c)
+}
diff --git a/gnovm/stdlibs/strconv/decimal.gno b/gnovm/stdlibs/strconv/decimal.gno
new file mode 100644
index 00000000000..b58001888e8
--- /dev/null
+++ b/gnovm/stdlibs/strconv/decimal.gno
@@ -0,0 +1,415 @@
+// Copyright 2009 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.
+
+// Multiprecision decimal numbers.
+// For floating-point formatting only; not general purpose.
+// Only operations are assign and (binary) left/right shift.
+// Can do binary floating point in multiprecision decimal precisely
+// because 2 divides 10; cannot do decimal floating point
+// in multiprecision binary precisely.
+
+package strconv
+
+type decimal struct {
+ d [800]byte // digits, big-endian representation
+ nd int // number of digits used
+ dp int // decimal point
+ neg bool // negative flag
+ trunc bool // discarded nonzero digits beyond d[:nd]
+}
+
+func (a *decimal) String() string {
+ n := 10 + a.nd
+ if a.dp > 0 {
+ n += a.dp
+ }
+ if a.dp < 0 {
+ n += -a.dp
+ }
+
+ buf := make([]byte, n)
+ w := 0
+ switch {
+ case a.nd == 0:
+ return "0"
+
+ case a.dp <= 0:
+ // zeros fill space between decimal point and digits
+ buf[w] = '0'
+ w++
+ buf[w] = '.'
+ w++
+ w += digitZero(buf[w : w+-a.dp])
+ w += copy(buf[w:], a.d[0:a.nd])
+
+ case a.dp < a.nd:
+ // decimal point in middle of digits
+ w += copy(buf[w:], a.d[0:a.dp])
+ buf[w] = '.'
+ w++
+ w += copy(buf[w:], a.d[a.dp:a.nd])
+
+ default:
+ // zeros fill space between digits and decimal point
+ w += copy(buf[w:], a.d[0:a.nd])
+ w += digitZero(buf[w : w+a.dp-a.nd])
+ }
+ return string(buf[0:w])
+}
+
+func digitZero(dst []byte) int {
+ for i := range dst {
+ dst[i] = '0'
+ }
+ return len(dst)
+}
+
+// trim trailing zeros from number.
+// (They are meaningless; the decimal point is tracked
+// independent of the number of digits.)
+func trim(a *decimal) {
+ for a.nd > 0 && a.d[a.nd-1] == '0' {
+ a.nd--
+ }
+ if a.nd == 0 {
+ a.dp = 0
+ }
+}
+
+// Assign v to a.
+func (a *decimal) Assign(v uint64) {
+ var buf [24]byte
+
+ // Write reversed decimal in buf.
+ n := 0
+ for v > 0 {
+ v1 := v / 10
+ v -= 10 * v1
+ buf[n] = byte(v + '0')
+ n++
+ v = v1
+ }
+
+ // Reverse again to produce forward decimal in a.d.
+ a.nd = 0
+ for n--; n >= 0; n-- {
+ a.d[a.nd] = buf[n]
+ a.nd++
+ }
+ a.dp = a.nd
+ trim(a)
+}
+
+// Maximum shift that we can do in one pass without overflow.
+// A uint has 32 or 64 bits, and we have to be able to accommodate 9<> 63)
+const maxShift = uintSize - 4
+
+// Binary shift right (/ 2) by k bits. k <= maxShift to avoid overflow.
+func rightShift(a *decimal, k uint) {
+ r := 0 // read pointer
+ w := 0 // write pointer
+
+ // Pick up enough leading digits to cover first shift.
+ var n uint
+ for ; n>>k == 0; r++ {
+ if r >= a.nd {
+ if n == 0 {
+ // a == 0; shouldn't get here, but handle anyway.
+ a.nd = 0
+ return
+ }
+ for n>>k == 0 {
+ n = n * 10
+ r++
+ }
+ break
+ }
+ c := uint(a.d[r])
+ n = n*10 + c - '0'
+ }
+ a.dp -= r - 1
+
+ var mask uint = (1 << k) - 1
+
+ // Pick up a digit, put down a digit.
+ for ; r < a.nd; r++ {
+ c := uint(a.d[r])
+ dig := n >> k
+ n &= mask
+ a.d[w] = byte(dig + '0')
+ w++
+ n = n*10 + c - '0'
+ }
+
+ // Put down extra digits.
+ for n > 0 {
+ dig := n >> k
+ n &= mask
+ if w < len(a.d) {
+ a.d[w] = byte(dig + '0')
+ w++
+ } else if dig > 0 {
+ a.trunc = true
+ }
+ n = n * 10
+ }
+
+ a.nd = w
+ trim(a)
+}
+
+// Cheat sheet for left shift: table indexed by shift count giving
+// number of new digits that will be introduced by that shift.
+//
+// For example, leftcheats[4] = {2, "625"}. That means that
+// if we are shifting by 4 (multiplying by 16), it will add 2 digits
+// when the string prefix is "625" through "999", and one fewer digit
+// if the string prefix is "000" through "624".
+//
+// Credit for this trick goes to Ken.
+
+type leftCheat struct {
+ delta int // number of new digits
+ cutoff string // minus one digit if original < a.
+}
+
+var leftcheats = []leftCheat{
+ // Leading digits of 1/2^i = 5^i.
+ // 5^23 is not an exact 64-bit floating point number,
+ // so have to use bc for the math.
+ // Go up to 60 to be large enough for 32bit and 64bit platforms.
+ /*
+ seq 60 | sed 's/^/5^/' | bc |
+ awk 'BEGIN{ print "\t{ 0, \"\" }," }
+ {
+ log2 = log(2)/log(10)
+ printf("\t{ %d, \"%s\" },\t// * %d\n",
+ int(log2*NR+1), $0, 2**NR)
+ }'
+ */
+ {0, ""},
+ {1, "5"}, // * 2
+ {1, "25"}, // * 4
+ {1, "125"}, // * 8
+ {2, "625"}, // * 16
+ {2, "3125"}, // * 32
+ {2, "15625"}, // * 64
+ {3, "78125"}, // * 128
+ {3, "390625"}, // * 256
+ {3, "1953125"}, // * 512
+ {4, "9765625"}, // * 1024
+ {4, "48828125"}, // * 2048
+ {4, "244140625"}, // * 4096
+ {4, "1220703125"}, // * 8192
+ {5, "6103515625"}, // * 16384
+ {5, "30517578125"}, // * 32768
+ {5, "152587890625"}, // * 65536
+ {6, "762939453125"}, // * 131072
+ {6, "3814697265625"}, // * 262144
+ {6, "19073486328125"}, // * 524288
+ {7, "95367431640625"}, // * 1048576
+ {7, "476837158203125"}, // * 2097152
+ {7, "2384185791015625"}, // * 4194304
+ {7, "11920928955078125"}, // * 8388608
+ {8, "59604644775390625"}, // * 16777216
+ {8, "298023223876953125"}, // * 33554432
+ {8, "1490116119384765625"}, // * 67108864
+ {9, "7450580596923828125"}, // * 134217728
+ {9, "37252902984619140625"}, // * 268435456
+ {9, "186264514923095703125"}, // * 536870912
+ {10, "931322574615478515625"}, // * 1073741824
+ {10, "4656612873077392578125"}, // * 2147483648
+ {10, "23283064365386962890625"}, // * 4294967296
+ {10, "116415321826934814453125"}, // * 8589934592
+ {11, "582076609134674072265625"}, // * 17179869184
+ {11, "2910383045673370361328125"}, // * 34359738368
+ {11, "14551915228366851806640625"}, // * 68719476736
+ {12, "72759576141834259033203125"}, // * 137438953472
+ {12, "363797880709171295166015625"}, // * 274877906944
+ {12, "1818989403545856475830078125"}, // * 549755813888
+ {13, "9094947017729282379150390625"}, // * 1099511627776
+ {13, "45474735088646411895751953125"}, // * 2199023255552
+ {13, "227373675443232059478759765625"}, // * 4398046511104
+ {13, "1136868377216160297393798828125"}, // * 8796093022208
+ {14, "5684341886080801486968994140625"}, // * 17592186044416
+ {14, "28421709430404007434844970703125"}, // * 35184372088832
+ {14, "142108547152020037174224853515625"}, // * 70368744177664
+ {15, "710542735760100185871124267578125"}, // * 140737488355328
+ {15, "3552713678800500929355621337890625"}, // * 281474976710656
+ {15, "17763568394002504646778106689453125"}, // * 562949953421312
+ {16, "88817841970012523233890533447265625"}, // * 1125899906842624
+ {16, "444089209850062616169452667236328125"}, // * 2251799813685248
+ {16, "2220446049250313080847263336181640625"}, // * 4503599627370496
+ {16, "11102230246251565404236316680908203125"}, // * 9007199254740992
+ {17, "55511151231257827021181583404541015625"}, // * 18014398509481984
+ {17, "277555756156289135105907917022705078125"}, // * 36028797018963968
+ {17, "1387778780781445675529539585113525390625"}, // * 72057594037927936
+ {18, "6938893903907228377647697925567626953125"}, // * 144115188075855872
+ {18, "34694469519536141888238489627838134765625"}, // * 288230376151711744
+ {18, "173472347597680709441192448139190673828125"}, // * 576460752303423488
+ {19, "867361737988403547205962240695953369140625"}, // * 1152921504606846976
+}
+
+// Is the leading prefix of b lexicographically less than s?
+func prefixIsLessThan(b []byte, s string) bool {
+ for i := 0; i < len(s); i++ {
+ if i >= len(b) {
+ return true
+ }
+ if b[i] != s[i] {
+ return b[i] < s[i]
+ }
+ }
+ return false
+}
+
+// Binary shift left (* 2) by k bits. k <= maxShift to avoid overflow.
+func leftShift(a *decimal, k uint) {
+ delta := leftcheats[k].delta
+ if prefixIsLessThan(a.d[0:a.nd], leftcheats[k].cutoff) {
+ delta--
+ }
+
+ r := a.nd // read index
+ w := a.nd + delta // write index
+
+ // Pick up a digit, put down a digit.
+ var n uint
+ for r--; r >= 0; r-- {
+ n += (uint(a.d[r]) - '0') << k
+ quo := n / 10
+ rem := n - 10*quo
+ w--
+ if w < len(a.d) {
+ a.d[w] = byte(rem + '0')
+ } else if rem != 0 {
+ a.trunc = true
+ }
+ n = quo
+ }
+
+ // Put down extra digits.
+ for n > 0 {
+ quo := n / 10
+ rem := n - 10*quo
+ w--
+ if w < len(a.d) {
+ a.d[w] = byte(rem + '0')
+ } else if rem != 0 {
+ a.trunc = true
+ }
+ n = quo
+ }
+
+ a.nd += delta
+ if a.nd >= len(a.d) {
+ a.nd = len(a.d)
+ }
+ a.dp += delta
+ trim(a)
+}
+
+// Binary shift left (k > 0) or right (k < 0).
+func (a *decimal) Shift(k int) {
+ switch {
+ case a.nd == 0:
+ // nothing to do: a == 0
+ case k > 0:
+ for k > maxShift {
+ leftShift(a, maxShift)
+ k -= maxShift
+ }
+ leftShift(a, uint(k))
+ case k < 0:
+ for k < -maxShift {
+ rightShift(a, maxShift)
+ k += maxShift
+ }
+ rightShift(a, uint(-k))
+ }
+}
+
+// If we chop a at nd digits, should we round up?
+func shouldRoundUp(a *decimal, nd int) bool {
+ if nd < 0 || nd >= a.nd {
+ return false
+ }
+ if a.d[nd] == '5' && nd+1 == a.nd { // exactly halfway - round to even
+ // if we truncated, a little higher than what's recorded - always round up
+ if a.trunc {
+ return true
+ }
+ return nd > 0 && (a.d[nd-1]-'0')%2 != 0
+ }
+ // not halfway - digit tells all
+ return a.d[nd] >= '5'
+}
+
+// Round a to nd digits (or fewer).
+// If nd is zero, it means we're rounding
+// just to the left of the digits, as in
+// 0.09 -> 0.1.
+func (a *decimal) Round(nd int) {
+ if nd < 0 || nd >= a.nd {
+ return
+ }
+ if shouldRoundUp(a, nd) {
+ a.RoundUp(nd)
+ } else {
+ a.RoundDown(nd)
+ }
+}
+
+// Round a down to nd digits (or fewer).
+func (a *decimal) RoundDown(nd int) {
+ if nd < 0 || nd >= a.nd {
+ return
+ }
+ a.nd = nd
+ trim(a)
+}
+
+// Round a up to nd digits (or fewer).
+func (a *decimal) RoundUp(nd int) {
+ if nd < 0 || nd >= a.nd {
+ return
+ }
+
+ // round up
+ for i := nd - 1; i >= 0; i-- {
+ c := a.d[i]
+ if c < '9' { // can stop after this digit
+ a.d[i]++
+ a.nd = i + 1
+ return
+ }
+ }
+
+ // Number is all 9s.
+ // Change to single 1 with adjusted decimal point.
+ a.d[0] = '1'
+ a.nd = 1
+ a.dp++
+}
+
+// Extract integer part, rounded appropriately.
+// No guarantees about overflow.
+func (a *decimal) RoundedInteger() uint64 {
+ if a.dp > 20 {
+ return 0xFFFFFFFFFFFFFFFF
+ }
+ var i int
+ n := uint64(0)
+ for i = 0; i < a.dp && i < a.nd; i++ {
+ n = n*10 + uint64(a.d[i]-'0')
+ }
+ for ; i < a.dp; i++ {
+ n *= 10
+ }
+ if shouldRoundUp(a, a.dp) {
+ n++
+ }
+ return n
+}
diff --git a/gnovm/stdlibs/strconv/decimal_test.gno b/gnovm/stdlibs/strconv/decimal_test.gno
new file mode 100644
index 00000000000..9dc8c997b9c
--- /dev/null
+++ b/gnovm/stdlibs/strconv/decimal_test.gno
@@ -0,0 +1,126 @@
+// Copyright 2009 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.
+
+package strconv
+
+import (
+ "testing"
+)
+
+type shiftTest struct {
+ i uint64
+ shift int
+ out string
+}
+
+var shifttests = []shiftTest{
+ {0, -100, "0"},
+ {0, 100, "0"},
+ {1, 100, "1267650600228229401496703205376"},
+ {1, -100,
+ "0.00000000000000000000000000000078886090522101180541" +
+ "17285652827862296732064351090230047702789306640625",
+ },
+ {12345678, 8, "3160493568"},
+ {12345678, -8, "48225.3046875"},
+ {195312, 9, "99999744"},
+ {1953125, 9, "1000000000"},
+}
+
+func TestDecimalShift(t *testing.T) {
+ for i := 0; i < len(shifttests); i++ {
+ test := &shifttests[i]
+ d := NewDecimal(test.i)
+ d.Shift(test.shift)
+ s := d.String()
+ if s != test.out {
+ t.Errorf("Decimal %v << %v = %v, want %v",
+ test.i, test.shift, s, test.out)
+ }
+ }
+}
+
+type roundTest struct {
+ i uint64
+ nd int
+ down, round, up string
+ int uint64
+}
+
+var roundtests = []roundTest{
+ {0, 4, "0", "0", "0", 0},
+ {12344999, 4, "12340000", "12340000", "12350000", 12340000},
+ {12345000, 4, "12340000", "12340000", "12350000", 12340000},
+ {12345001, 4, "12340000", "12350000", "12350000", 12350000},
+ {23454999, 4, "23450000", "23450000", "23460000", 23450000},
+ {23455000, 4, "23450000", "23460000", "23460000", 23460000},
+ {23455001, 4, "23450000", "23460000", "23460000", 23460000},
+
+ {99994999, 4, "99990000", "99990000", "100000000", 99990000},
+ {99995000, 4, "99990000", "100000000", "100000000", 100000000},
+ {99999999, 4, "99990000", "100000000", "100000000", 100000000},
+
+ {12994999, 4, "12990000", "12990000", "13000000", 12990000},
+ {12995000, 4, "12990000", "13000000", "13000000", 13000000},
+ {12999999, 4, "12990000", "13000000", "13000000", 13000000},
+}
+
+func TestDecimalRound(t *testing.T) {
+ for i := 0; i < len(roundtests); i++ {
+ test := &roundtests[i]
+ d := NewDecimal(test.i)
+ d.RoundDown(test.nd)
+ s := d.String()
+ if s != test.down {
+ t.Errorf("Decimal %v RoundDown %d = %v, want %v",
+ test.i, test.nd, s, test.down)
+ }
+ d = NewDecimal(test.i)
+ d.Round(test.nd)
+ s = d.String()
+ if s != test.round {
+ t.Errorf("Decimal %v Round %d = %v, want %v",
+ test.i, test.nd, s, test.down)
+ }
+ d = NewDecimal(test.i)
+ d.RoundUp(test.nd)
+ s = d.String()
+ if s != test.up {
+ t.Errorf("Decimal %v RoundUp %d = %v, want %v",
+ test.i, test.nd, s, test.up)
+ }
+ }
+}
+
+type roundIntTest struct {
+ i uint64
+ shift int
+ int uint64
+}
+
+var roundinttests = []roundIntTest{
+ {0, 100, 0},
+ {512, -8, 2},
+ {513, -8, 2},
+ {640, -8, 2},
+ {641, -8, 3},
+ {384, -8, 2},
+ {385, -8, 2},
+ {383, -8, 1},
+ {1, 100, 1<<64 - 1},
+ {1000, 0, 1000},
+}
+
+func TestDecimalRoundedInteger(t *testing.T) {
+ for i := 0; i < len(roundinttests); i++ {
+ test := roundinttests[i]
+ d := NewDecimal(test.i)
+ d.Shift(test.shift)
+ num := d.RoundedInteger()
+ if num != test.int {
+ t.Errorf("Decimal %v >> %v RoundedInteger = %v, want %v",
+ test.i, test.shift, num, test.int)
+ }
+ }
+}
diff --git a/gnovm/stdlibs/strconv/doc.gno b/gnovm/stdlibs/strconv/doc.gno
new file mode 100644
index 00000000000..9a22d77a0cd
--- /dev/null
+++ b/gnovm/stdlibs/strconv/doc.gno
@@ -0,0 +1,59 @@
+// Copyright 2015 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.
+
+// Package strconv implements conversions to and from string representations
+// of basic data types.
+//
+// # Numeric Conversions
+//
+// The most common numeric conversions are Atoi (string to int) and Itoa (int to string).
+//
+// i, err := strconv.Atoi("-42")
+// s := strconv.Itoa(-42)
+//
+// These assume decimal and the Go int type.
+//
+// [ParseBool], [ParseFloat], [ParseInt], and [ParseUint] convert strings to values:
+//
+// b, err := strconv.ParseBool("true")
+// f, err := strconv.ParseFloat("3.1415", 64)
+// i, err := strconv.ParseInt("-42", 10, 64)
+// u, err := strconv.ParseUint("42", 10, 64)
+//
+// The parse functions return the widest type (float64, int64, and uint64),
+// but if the size argument specifies a narrower width the result can be
+// converted to that narrower type without data loss:
+//
+// s := "2147483647" // biggest int32
+// i64, err := strconv.ParseInt(s, 10, 32)
+// ...
+// i := int32(i64)
+//
+// [FormatBool], [FormatFloat], [FormatInt], and [FormatUint] convert values to strings:
+//
+// s := strconv.FormatBool(true)
+// s := strconv.FormatFloat(3.1415, 'E', -1, 64)
+// s := strconv.FormatInt(-42, 16)
+// s := strconv.FormatUint(42, 16)
+//
+// [AppendBool], [AppendFloat], [AppendInt], and [AppendUint] are similar but
+// append the formatted value to a destination slice.
+//
+// # String Conversions
+//
+// [Quote] and [QuoteToASCII] convert strings to quoted Go string literals.
+// The latter guarantees that the result is an ASCII string, by escaping
+// any non-ASCII Unicode with \u:
+//
+// q := strconv.Quote("Hello, 世界")
+// q := strconv.QuoteToASCII("Hello, 世界")
+//
+// [QuoteRune] and [QuoteRuneToASCII] are similar but accept runes and
+// return quoted Go rune literals.
+//
+// [Unquote] and [UnquoteChar] unquote Go string and rune literals.
+//
+// XXX: Gno does not implement any of the functions from Go's strconv which
+// pertain to complex numbers, such as FormatComplex and ParseComplex.
+package strconv
diff --git a/examples/gno.land/p/demo/json/eisel_lemire/eisel_lemire.gno b/gnovm/stdlibs/strconv/eisel_lemire.gno
similarity index 93%
rename from examples/gno.land/p/demo/json/eisel_lemire/eisel_lemire.gno
rename to gnovm/stdlibs/strconv/eisel_lemire.gno
index 6a29f7f1350..03842e50797 100644
--- a/examples/gno.land/p/demo/json/eisel_lemire/eisel_lemire.gno
+++ b/gnovm/stdlibs/strconv/eisel_lemire.gno
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package eisel_lemire
+package strconv
// This file implements the Eisel-Lemire ParseFloat algorithm, published in
// 2020 and discussed extensively at
@@ -22,43 +22,17 @@ import (
"math/bits"
)
-const (
- float32ExponentBias = 127
- float64ExponentBias = 1023
-)
-
-// eiselLemire64 parses a floating-point number from its mantissa and exponent representation.
-// This implementation is based on the Eisel-Lemire ParseFloat algorithm, which is efficient
-// and precise for converting strings to floating-point numbers.
-//
-// Arguments:
-// man (uint64): The mantissa part of the floating-point number.
-// exp10 (int): The exponent part, representing the power of 10.
-// neg (bool): Indicates if the number is negative.
-//
-// Returns:
-// f (float64): The parsed floating-point number.
-// ok (bool): Indicates whether the parsing was successful.
-//
-// The function starts by handling special cases, such as zero mantissa.
-// It then checks if the exponent is within the allowed range.
-// After that, it normalizes the mantissa by left-shifting it to fill
-// the leading zeros. This is followed by the main algorithm logic that
-// converts the normalized mantissa and exponent into a 64-bit floating-point number.
-// The function returns this number along with a boolean indicating the success of the operation.
-func EiselLemire64(man uint64, exp10 int, neg bool) (f float64, ok bool) {
+func eiselLemire64(man uint64, exp10 int, neg bool) (f float64, ok bool) {
// The terse comments in this function body refer to sections of the
// https://nigeltao.github.io/blog/2020/eisel-lemire.html blog post.
// Exp10 Range.
if man == 0 {
if neg {
- f = math.Float64frombits(0x80000000_00000000) // Negative zero.
+ f = math.Float64frombits(0x8000000000000000) // Negative zero.
}
-
return f, true
}
-
if exp10 < detailedPowersOfTenMinExp10 || detailedPowersOfTenMaxExp10 < exp10 {
return 0, false
}
@@ -66,6 +40,7 @@ func EiselLemire64(man uint64, exp10 int, neg bool) (f float64, ok bool) {
// Normalization.
clz := bits.LeadingZeros64(man)
man <<= uint(clz)
+ const float64ExponentBias = 1023
retExp2 := uint64(217706*exp10>>16+64+float64ExponentBias) - uint64(clz)
// Multiplication.
@@ -78,11 +53,9 @@ func EiselLemire64(man uint64, exp10 int, neg bool) (f float64, ok bool) {
if mergedLo < xLo {
mergedHi++
}
-
if mergedHi&0x1FF == 0x1FF && mergedLo+1 == 0 && yLo+man < man {
return 0, false
}
-
xHi, xLo = mergedHi, mergedLo
}
@@ -103,7 +76,6 @@ func EiselLemire64(man uint64, exp10 int, neg bool) (f float64, ok bool) {
retMantissa >>= 1
retExp2 += 1
}
-
// retExp2 is a uint64. Zero or underflow means that we're in subnormal
// float64 space. 0x7FF or above means that we're in Inf/NaN float64 space.
//
@@ -112,15 +84,88 @@ func EiselLemire64(man uint64, exp10 int, neg bool) (f float64, ok bool) {
if retExp2-1 >= 0x7FF-1 {
return 0, false
}
-
- retBits := retExp2<<52 | retMantissa&0x000FFFFF_FFFFFFFF
+ retBits := retExp2<<52 | retMantissa&0x000FFFFFFFFFFFFF
if neg {
- retBits |= 0x80000000_00000000
+ retBits |= 0x8000000000000000
}
-
return math.Float64frombits(retBits), true
}
+func eiselLemire32(man uint64, exp10 int, neg bool) (f float32, ok bool) {
+ // The terse comments in this function body refer to sections of the
+ // https://nigeltao.github.io/blog/2020/eisel-lemire.html blog post.
+ //
+ // That blog post discusses the float64 flavor (11 exponent bits with a
+ // -1023 bias, 52 mantissa bits) of the algorithm, but the same approach
+ // applies to the float32 flavor (8 exponent bits with a -127 bias, 23
+ // mantissa bits). The computation here happens with 64-bit values (e.g.
+ // man, xHi, retMantissa) before finally converting to a 32-bit float.
+
+ // Exp10 Range.
+ if man == 0 {
+ if neg {
+ f = math.Float32frombits(0x80000000) // Negative zero.
+ }
+ return f, true
+ }
+ if exp10 < detailedPowersOfTenMinExp10 || detailedPowersOfTenMaxExp10 < exp10 {
+ return 0, false
+ }
+
+ // Normalization.
+ clz := bits.LeadingZeros64(man)
+ man <<= uint(clz)
+ const float32ExponentBias = 127
+ retExp2 := uint64(217706*exp10>>16+64+float32ExponentBias) - uint64(clz)
+
+ // Multiplication.
+ xHi, xLo := bits.Mul64(man, detailedPowersOfTen[exp10-detailedPowersOfTenMinExp10][1])
+
+ // Wider Approximation.
+ if xHi&0x3FFFFFFFFF == 0x3FFFFFFFFF && xLo+man < man {
+ yHi, yLo := bits.Mul64(man, detailedPowersOfTen[exp10-detailedPowersOfTenMinExp10][0])
+ mergedHi, mergedLo := xHi, xLo+yHi
+ if mergedLo < xLo {
+ mergedHi++
+ }
+ if mergedHi&0x3FFFFFFFFF == 0x3FFFFFFFFF && mergedLo+1 == 0 && yLo+man < man {
+ return 0, false
+ }
+ xHi, xLo = mergedHi, mergedLo
+ }
+
+ // Shifting to 54 Bits (and for float32, it's shifting to 25 bits).
+ msb := xHi >> 63
+ retMantissa := xHi >> (msb + 38)
+ retExp2 -= 1 ^ msb
+
+ // Half-way Ambiguity.
+ if xLo == 0 && xHi&0x3FFFFFFFFF == 0 && retMantissa&3 == 1 {
+ return 0, false
+ }
+
+ // From 54 to 53 Bits (and for float32, it's from 25 to 24 bits).
+ retMantissa += retMantissa & 1
+ retMantissa >>= 1
+ if retMantissa>>24 > 0 {
+ retMantissa >>= 1
+ retExp2 += 1
+ }
+ // retExp2 is a uint64. Zero or underflow means that we're in subnormal
+ // float32 space. 0xFF or above means that we're in Inf/NaN float32 space.
+ //
+ // The if block is equivalent to (but has fewer branches than):
+ // if retExp2 <= 0 || retExp2 >= 0xFF { etc }
+ if retExp2-1 >= 0xFF-1 {
+ return 0, false
+ }
+ retBits := retExp2<<23 | retMantissa&0x007FFFFF
+ if neg {
+ retBits |= 0x80000000
+ }
+ return math.Float32frombits(uint32(retBits)), true
+}
+
// detailedPowersOfTen{Min,Max}Exp10 is the power of 10 represented by the
// first and last rows of detailedPowersOfTen. Both bounds are inclusive.
const (
diff --git a/gnovm/stdlibs/strconv/example_test.gno b/gnovm/stdlibs/strconv/example_test.gno
new file mode 100644
index 00000000000..428fde4e660
--- /dev/null
+++ b/gnovm/stdlibs/strconv/example_test.gno
@@ -0,0 +1,440 @@
+// Copyright 2015 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.
+
+package strconv_test
+
+import (
+ "fmt"
+ "log"
+ "strconv"
+)
+
+func ExampleAppendBool() {
+ b := []byte("bool:")
+ b = strconv.AppendBool(b, true)
+ fmt.Println(string(b))
+
+ // Output:
+ // bool:true
+}
+
+func ExampleAppendFloat() {
+ b32 := []byte("float32:")
+ b32 = strconv.AppendFloat(b32, 3.1415926535, 'E', -1, 32)
+ fmt.Println(string(b32))
+
+ b64 := []byte("float64:")
+ b64 = strconv.AppendFloat(b64, 3.1415926535, 'E', -1, 64)
+ fmt.Println(string(b64))
+
+ // Output:
+ // float32:3.1415927E+00
+ // float64:3.1415926535E+00
+}
+
+func ExampleAppendInt() {
+ b10 := []byte("int (base 10):")
+ b10 = strconv.AppendInt(b10, -42, 10)
+ fmt.Println(string(b10))
+
+ b16 := []byte("int (base 16):")
+ b16 = strconv.AppendInt(b16, -42, 16)
+ fmt.Println(string(b16))
+
+ // Output:
+ // int (base 10):-42
+ // int (base 16):-2a
+}
+
+func ExampleAppendQuote() {
+ b := []byte("quote:")
+ b = strconv.AppendQuote(b, `"Fran & Freddie's Diner"`)
+ fmt.Println(string(b))
+
+ // Output:
+ // quote:"\"Fran & Freddie's Diner\""
+}
+
+func ExampleAppendQuoteRune() {
+ b := []byte("rune:")
+ b = strconv.AppendQuoteRune(b, '☺')
+ fmt.Println(string(b))
+
+ // Output:
+ // rune:'☺'
+}
+
+func ExampleAppendQuoteRuneToASCII() {
+ b := []byte("rune (ascii):")
+ b = strconv.AppendQuoteRuneToASCII(b, '☺')
+ fmt.Println(string(b))
+
+ // Output:
+ // rune (ascii):'\u263a'
+}
+
+func ExampleAppendQuoteToASCII() {
+ b := []byte("quote (ascii):")
+ b = strconv.AppendQuoteToASCII(b, `"Fran & Freddie's Diner"`)
+ fmt.Println(string(b))
+
+ // Output:
+ // quote (ascii):"\"Fran & Freddie's Diner\""
+}
+
+func ExampleAppendUint() {
+ b10 := []byte("uint (base 10):")
+ b10 = strconv.AppendUint(b10, 42, 10)
+ fmt.Println(string(b10))
+
+ b16 := []byte("uint (base 16):")
+ b16 = strconv.AppendUint(b16, 42, 16)
+ fmt.Println(string(b16))
+
+ // Output:
+ // uint (base 10):42
+ // uint (base 16):2a
+}
+
+func ExampleAtoi() {
+ v := "10"
+ if s, err := strconv.Atoi(v); err == nil {
+ fmt.Printf("%T, %v", s, s)
+ }
+
+ // Output:
+ // int, 10
+}
+
+func ExampleCanBackquote() {
+ fmt.Println(strconv.CanBackquote("Fran & Freddie's Diner ☺"))
+ fmt.Println(strconv.CanBackquote("`can't backquote this`"))
+
+ // Output:
+ // true
+ // false
+}
+
+func ExampleFormatBool() {
+ v := true
+ s := strconv.FormatBool(v)
+ fmt.Printf("%T, %v\n", s, s)
+
+ // Output:
+ // string, true
+}
+
+func ExampleFormatFloat() {
+ v := 3.1415926535
+
+ s32 := strconv.FormatFloat(v, 'E', -1, 32)
+ fmt.Printf("%T, %v\n", s32, s32)
+
+ s64 := strconv.FormatFloat(v, 'E', -1, 64)
+ fmt.Printf("%T, %v\n", s64, s64)
+
+ // fmt.Println uses these arguments to print floats
+ fmt64 := strconv.FormatFloat(v, 'g', -1, 64)
+ fmt.Printf("%T, %v\n", fmt64, fmt64)
+
+ // Output:
+ // string, 3.1415927E+00
+ // string, 3.1415926535E+00
+ // string, 3.1415926535
+}
+
+func ExampleFormatInt() {
+ v := int64(-42)
+
+ s10 := strconv.FormatInt(v, 10)
+ fmt.Printf("%T, %v\n", s10, s10)
+
+ s16 := strconv.FormatInt(v, 16)
+ fmt.Printf("%T, %v\n", s16, s16)
+
+ // Output:
+ // string, -42
+ // string, -2a
+}
+
+func ExampleFormatUint() {
+ v := uint64(42)
+
+ s10 := strconv.FormatUint(v, 10)
+ fmt.Printf("%T, %v\n", s10, s10)
+
+ s16 := strconv.FormatUint(v, 16)
+ fmt.Printf("%T, %v\n", s16, s16)
+
+ // Output:
+ // string, 42
+ // string, 2a
+}
+
+func ExampleIsGraphic() {
+ shamrock := strconv.IsGraphic('☘')
+ fmt.Println(shamrock)
+
+ a := strconv.IsGraphic('a')
+ fmt.Println(a)
+
+ bel := strconv.IsGraphic('\007')
+ fmt.Println(bel)
+
+ // Output:
+ // true
+ // true
+ // false
+}
+
+func ExampleIsPrint() {
+ c := strconv.IsPrint('\u263a')
+ fmt.Println(c)
+
+ bel := strconv.IsPrint('\007')
+ fmt.Println(bel)
+
+ // Output:
+ // true
+ // false
+}
+
+func ExampleItoa() {
+ i := 10
+ s := strconv.Itoa(i)
+ fmt.Printf("%T, %v\n", s, s)
+
+ // Output:
+ // string, 10
+}
+
+func ExampleParseBool() {
+ v := "true"
+ if s, err := strconv.ParseBool(v); err == nil {
+ fmt.Printf("%T, %v\n", s, s)
+ }
+
+ // Output:
+ // bool, true
+}
+
+func ExampleParseFloat() {
+ v := "3.1415926535"
+ if s, err := strconv.ParseFloat(v, 32); err == nil {
+ fmt.Printf("%T, %v\n", s, s)
+ }
+ if s, err := strconv.ParseFloat(v, 64); err == nil {
+ fmt.Printf("%T, %v\n", s, s)
+ }
+ if s, err := strconv.ParseFloat("NaN", 32); err == nil {
+ fmt.Printf("%T, %v\n", s, s)
+ }
+ // ParseFloat is case insensitive
+ if s, err := strconv.ParseFloat("nan", 32); err == nil {
+ fmt.Printf("%T, %v\n", s, s)
+ }
+ if s, err := strconv.ParseFloat("inf", 32); err == nil {
+ fmt.Printf("%T, %v\n", s, s)
+ }
+ if s, err := strconv.ParseFloat("+Inf", 32); err == nil {
+ fmt.Printf("%T, %v\n", s, s)
+ }
+ if s, err := strconv.ParseFloat("-Inf", 32); err == nil {
+ fmt.Printf("%T, %v\n", s, s)
+ }
+ if s, err := strconv.ParseFloat("-0", 32); err == nil {
+ fmt.Printf("%T, %v\n", s, s)
+ }
+ if s, err := strconv.ParseFloat("+0", 32); err == nil {
+ fmt.Printf("%T, %v\n", s, s)
+ }
+
+ // Output:
+ // float64, 3.1415927410125732
+ // float64, 3.1415926535
+ // float64, NaN
+ // float64, NaN
+ // float64, +Inf
+ // float64, +Inf
+ // float64, -Inf
+ // float64, -0
+ // float64, 0
+}
+
+func ExampleParseInt() {
+ v32 := "-354634382"
+ if s, err := strconv.ParseInt(v32, 10, 32); err == nil {
+ fmt.Printf("%T, %v\n", s, s)
+ }
+ if s, err := strconv.ParseInt(v32, 16, 32); err == nil {
+ fmt.Printf("%T, %v\n", s, s)
+ }
+
+ v64 := "-3546343826724305832"
+ if s, err := strconv.ParseInt(v64, 10, 64); err == nil {
+ fmt.Printf("%T, %v\n", s, s)
+ }
+ if s, err := strconv.ParseInt(v64, 16, 64); err == nil {
+ fmt.Printf("%T, %v\n", s, s)
+ }
+
+ // Output:
+ // int64, -354634382
+ // int64, -3546343826724305832
+}
+
+func ExampleParseUint() {
+ v := "42"
+ if s, err := strconv.ParseUint(v, 10, 32); err == nil {
+ fmt.Printf("%T, %v\n", s, s)
+ }
+ if s, err := strconv.ParseUint(v, 10, 64); err == nil {
+ fmt.Printf("%T, %v\n", s, s)
+ }
+
+ // Output:
+ // uint64, 42
+ // uint64, 42
+}
+
+func ExampleQuote() {
+ // This string literal contains a tab character.
+ s := strconv.Quote(`"Fran & Freddie's Diner ☺"`)
+ fmt.Println(s)
+
+ // Output:
+ // "\"Fran & Freddie's Diner\t☺\""
+}
+
+func ExampleQuoteRune() {
+ s := strconv.QuoteRune('☺')
+ fmt.Println(s)
+
+ // Output:
+ // '☺'
+}
+
+func ExampleQuoteRuneToASCII() {
+ s := strconv.QuoteRuneToASCII('☺')
+ fmt.Println(s)
+
+ // Output:
+ // '\u263a'
+}
+
+func ExampleQuoteRuneToGraphic() {
+ s := strconv.QuoteRuneToGraphic('☺')
+ fmt.Println(s)
+
+ s = strconv.QuoteRuneToGraphic('\u263a')
+ fmt.Println(s)
+
+ s = strconv.QuoteRuneToGraphic('\u000a')
+ fmt.Println(s)
+
+ s = strconv.QuoteRuneToGraphic(' ') // tab character
+ fmt.Println(s)
+
+ // Output:
+ // '☺'
+ // '☺'
+ // '\n'
+ // '\t'
+}
+
+func ExampleQuoteToASCII() {
+ // This string literal contains a tab character.
+ s := strconv.QuoteToASCII(`"Fran & Freddie's Diner ☺"`)
+ fmt.Println(s)
+
+ // Output:
+ // "\"Fran & Freddie's Diner\t\u263a\""
+}
+
+func ExampleQuoteToGraphic() {
+ s := strconv.QuoteToGraphic("☺")
+ fmt.Println(s)
+
+ // This string literal contains a tab character.
+ s = strconv.QuoteToGraphic("This is a \u263a \u000a")
+ fmt.Println(s)
+
+ s = strconv.QuoteToGraphic(`" This is a ☺ \n "`)
+ fmt.Println(s)
+
+ // Output:
+ // "☺"
+ // "This is a ☺\t\n"
+ // "\" This is a ☺ \\n \""
+}
+
+func ExampleQuotedPrefix() {
+ s, err := strconv.QuotedPrefix("not a quoted string")
+ fmt.Printf("%q, %v\n", s, err)
+ s, err = strconv.QuotedPrefix("\"double-quoted string\" with trailing text")
+ fmt.Printf("%q, %v\n", s, err)
+ s, err = strconv.QuotedPrefix("`or backquoted` with more trailing text")
+ fmt.Printf("%q, %v\n", s, err)
+ s, err = strconv.QuotedPrefix("'\u263a' is also okay")
+ fmt.Printf("%q, %v\n", s, err)
+
+ // Output:
+ // "", invalid syntax
+ // "\"double-quoted string\"",
+ // "`or backquoted`",
+ // "'☺'",
+}
+
+func ExampleUnquote() {
+ s, err := strconv.Unquote("You can't unquote a string without quotes")
+ fmt.Printf("%q, %v\n", s, err)
+ s, err = strconv.Unquote("\"The string must be either double-quoted\"")
+ fmt.Printf("%q, %v\n", s, err)
+ s, err = strconv.Unquote("`or backquoted.`")
+ fmt.Printf("%q, %v\n", s, err)
+ s, err = strconv.Unquote("'\u263a'") // single character only allowed in single quotes
+ fmt.Printf("%q, %v\n", s, err)
+ s, err = strconv.Unquote("'\u2639\u2639'")
+ fmt.Printf("%q, %v\n", s, err)
+
+ // Output:
+ // "", invalid syntax
+ // "The string must be either double-quoted",
+ // "or backquoted.",
+ // "☺",
+ // "", invalid syntax
+}
+
+func ExampleUnquoteChar() {
+ v, mb, t, err := strconv.UnquoteChar(`\"Fran & Freddie's Diner\"`, '"')
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ fmt.Println("value:", string(v))
+ fmt.Println("multibyte:", mb)
+ fmt.Println("tail:", t)
+
+ // Output:
+ // value: "
+ // multibyte: false
+ // tail: Fran & Freddie's Diner\"
+}
+
+func ExampleNumError() {
+ str := "Not a number"
+ if _, err := strconv.ParseFloat(str, 64); err != nil {
+ e := err.(*strconv.NumError)
+ fmt.Println("Func:", e.Func)
+ fmt.Println("Num:", e.Num)
+ fmt.Println("Err:", e.Err)
+ fmt.Println(err)
+ }
+
+ // Output:
+ // Func: ParseFloat
+ // Num: Not a number
+ // Err: invalid syntax
+ // strconv.ParseFloat: parsing "Not a number": invalid syntax
+}
diff --git a/gnovm/stdlibs/strconv/export_test.gno b/gnovm/stdlibs/strconv/export_test.gno
new file mode 100644
index 00000000000..8c03a7ffb4f
--- /dev/null
+++ b/gnovm/stdlibs/strconv/export_test.gno
@@ -0,0 +1,10 @@
+// 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.
+
+package strconv
+
+var (
+ BitSizeError = bitSizeError
+ BaseError = baseError
+)
diff --git a/gnovm/stdlibs/strconv/fp_test.gno b/gnovm/stdlibs/strconv/fp_test.gno
new file mode 100644
index 00000000000..76cc95663c4
--- /dev/null
+++ b/gnovm/stdlibs/strconv/fp_test.gno
@@ -0,0 +1,320 @@
+// Copyright 2009 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.
+
+package strconv_test
+
+import (
+ "bufio"
+ "fmt"
+ "strconv"
+ "strings"
+ "testing"
+)
+
+func pow2(i int) float64 {
+ switch {
+ case i < 0:
+ return 1 / pow2(-i)
+ case i == 0:
+ return 1
+ case i == 1:
+ return 2
+ }
+ return pow2(i/2) * pow2(i-i/2)
+}
+
+// Wrapper around strconv.ParseFloat(x, 64). Handles dddddp+ddd (binary exponent)
+// itself, passes the rest on to strconv.ParseFloat.
+func myatof64(s string) (f float64, ok bool) {
+ if mant, exp, ok := strings.Cut(s, "p"); ok {
+ n, err := strconv.ParseInt(mant, 10, 64)
+ if err != nil {
+ return 0, false
+ }
+ e, err1 := strconv.Atoi(exp)
+ if err1 != nil {
+ println("bad e", exp)
+ return 0, false
+ }
+ v := float64(n)
+ // We expect that v*pow2(e) fits in a float64,
+ // but pow2(e) by itself may not. Be careful.
+ if e <= -1000 {
+ v *= pow2(-1000)
+ e += 1000
+ for e < 0 {
+ v /= 2
+ e++
+ }
+ return v, true
+ }
+ if e >= 1000 {
+ v *= pow2(1000)
+ e -= 1000
+ for e > 0 {
+ v *= 2
+ e--
+ }
+ return v, true
+ }
+ return v * pow2(e), true
+ }
+ f1, err := strconv.ParseFloat(s, 64)
+ if err != nil {
+ return 0, false
+ }
+ return f1, true
+}
+
+// Wrapper around strconv.ParseFloat(x, 32). Handles dddddp+ddd (binary exponent)
+// itself, passes the rest on to strconv.ParseFloat.
+func myatof32(s string) (f float32, ok bool) {
+ if mant, exp, ok := strings.Cut(s, "p"); ok {
+ n, err := strconv.Atoi(mant)
+ if err != nil {
+ println("bad n", mant)
+ return 0, false
+ }
+ e, err1 := strconv.Atoi(exp)
+ if err1 != nil {
+ println("bad p", exp)
+ return 0, false
+ }
+ return float32(float64(n) * pow2(e)), true
+ }
+ f64, err1 := strconv.ParseFloat(s, 32)
+ f1 := float32(f64)
+ if err1 != nil {
+ return 0, false
+ }
+ return f1, true
+}
+
+// XXX: copied from go source src/strconv/testdata/testfp.txt
+// to avoid using os.Open in our tests
+const testfp = `# Floating-point conversion test cases.
+# Empty lines and lines beginning with # are ignored.
+# The rest have four fields per line: type, format, input, and output.
+# The input is given either in decimal or binary scientific notation.
+# The output is the string that should be produced by formatting the
+# input with the given format.
+#
+# The formats are as in C's printf, except that %b means print
+# binary scientific notation: NpE = N x 2^E.
+
+# TODO:
+# Powers of 10.
+# Powers of 2.
+# %.20g versions.
+# random sources
+# random targets
+# random targets ± half a ULP
+
+# Difficult boundary cases, derived from tables given in
+# Vern Paxson, A Program for Testing IEEE Decimal-Binary Conversion
+# ftp://ftp.ee.lbl.gov/testbase-report.ps.Z
+
+# Table 1: Stress Inputs for Conversion to 53-bit Binary, < 1/2 ULP
+float64 %b 5e+125 6653062250012735p+365
+float64 %b 69e+267 4705683757438170p+841
+float64 %b 999e-026 6798841691080350p-129
+float64 %b 7861e-034 8975675289889240p-153
+float64 %b 75569e-254 6091718967192243p-880
+float64 %b 928609e-261 7849264900213743p-900
+float64 %b 9210917e+080 8341110837370930p+236
+float64 %b 84863171e+114 4625202867375927p+353
+float64 %b 653777767e+273 5068902999763073p+884
+float64 %b 5232604057e-298 5741343011915040p-1010
+float64 %b 27235667517e-109 6707124626673586p-380
+float64 %b 653532977297e-123 7078246407265384p-422
+float64 %b 3142213164987e-294 8219991337640559p-988
+float64 %b 46202199371337e-072 5224462102115359p-246
+float64 %b 231010996856685e-073 5224462102115359p-247
+float64 %b 9324754620109615e+212 5539753864394442p+705
+float64 %b 78459735791271921e+049 8388176519442766p+166
+float64 %b 272104041512242479e+200 5554409530847367p+670
+float64 %b 6802601037806061975e+198 5554409530847367p+668
+float64 %b 20505426358836677347e-221 4524032052079546p-722
+float64 %b 836168422905420598437e-234 5070963299887562p-760
+float64 %b 4891559871276714924261e+222 6452687840519111p+757
+
+# Table 2: Stress Inputs for Conversion to 53-bit Binary, > 1/2 ULP
+float64 %b 9e-265 8168427841980010p-930
+float64 %b 85e-037 6360455125664090p-169
+float64 %b 623e+100 6263531988747231p+289
+float64 %b 3571e+263 6234526311072170p+833
+float64 %b 81661e+153 6696636728760206p+472
+float64 %b 920657e-023 5975405561110124p-109
+float64 %b 4603285e-024 5975405561110124p-110
+float64 %b 87575437e-309 8452160731874668p-1053
+float64 %b 245540327e+122 4985336549131723p+381
+float64 %b 6138508175e+120 4985336549131723p+379
+float64 %b 83356057653e+193 5986732817132056p+625
+float64 %b 619534293513e+124 4798406992060657p+399
+float64 %b 2335141086879e+218 5419088166961646p+713
+float64 %b 36167929443327e-159 8135819834632444p-536
+float64 %b 609610927149051e-255 4576664294594737p-850
+float64 %b 3743626360493413e-165 6898586531774201p-549
+float64 %b 94080055902682397e-242 6273271706052298p-800
+float64 %b 899810892172646163e+283 7563892574477827p+947
+float64 %b 7120190517612959703e+120 5385467232557565p+409
+float64 %b 25188282901709339043e-252 5635662608542340p-825
+float64 %b 308984926168550152811e-052 5644774693823803p-157
+float64 %b 6372891218502368041059e+064 4616868614322430p+233
+
+# Table 3: Stress Inputs for Converting 53-bit Binary to Decimal, < 1/2 ULP
+float64 %.0e 8511030020275656p-342 9e-88
+float64 %.1e 5201988407066741p-824 4.6e-233
+float64 %.2e 6406892948269899p+237 1.41e+87
+float64 %.3e 8431154198732492p+72 3.981e+37
+float64 %.4e 6475049196144587p+99 4.1040e+45
+float64 %.5e 8274307542972842p+726 2.92084e+234
+float64 %.6e 5381065484265332p-456 2.891946e-122
+float64 %.7e 6761728585499734p-1057 4.3787718e-303
+float64 %.8e 7976538478610756p+376 1.22770163e+129
+float64 %.9e 5982403858958067p+377 1.841552452e+129
+float64 %.10e 5536995190630837p+93 5.4835744350e+43
+float64 %.11e 7225450889282194p+710 3.89190181146e+229
+float64 %.12e 7225450889282194p+709 1.945950905732e+229
+float64 %.13e 8703372741147379p+117 1.4460958381605e+51
+float64 %.14e 8944262675275217p-1001 4.17367747458531e-286
+float64 %.15e 7459803696087692p-707 1.107950772878888e-197
+float64 %.16e 6080469016670379p-381 1.2345501366327440e-99
+float64 %.17e 8385515147034757p+721 9.25031711960365024e+232
+float64 %.18e 7514216811389786p-828 4.198047150284889840e-234
+float64 %.19e 8397297803260511p-345 1.1716315319786511046e-88
+float64 %.20e 6733459239310543p+202 4.32810072844612493629e+76
+float64 %.21e 8091450587292794p-473 3.317710118160031081518e-127
+
+# Table 4: Stress Inputs for Converting 53-bit Binary to Decimal, > 1/2 ULP
+float64 %.0e 6567258882077402p+952 3e+302
+float64 %.1e 6712731423444934p+535 7.6e+176
+float64 %.2e 6712731423444934p+534 3.78e+176
+float64 %.3e 5298405411573037p-957 4.350e-273
+float64 %.4e 5137311167659507p-144 2.3037e-28
+float64 %.5e 6722280709661868p+363 1.26301e+125
+float64 %.6e 5344436398034927p-169 7.142211e-36
+float64 %.7e 8369123604277281p-853 1.3934574e-241
+float64 %.8e 8995822108487663p-780 1.41463449e-219
+float64 %.9e 8942832835564782p-383 4.539277920e-100
+float64 %.10e 8942832835564782p-384 2.2696389598e-100
+float64 %.11e 8942832835564782p-385 1.13481947988e-100
+float64 %.12e 6965949469487146p-249 7.700366561890e-60
+float64 %.13e 6965949469487146p-250 3.8501832809448e-60
+float64 %.14e 6965949469487146p-251 1.92509164047238e-60
+float64 %.15e 7487252720986826p+548 6.898586531774201e+180
+float64 %.16e 5592117679628511p+164 1.3076622631878654e+65
+float64 %.17e 8887055249355788p+665 1.36052020756121240e+216
+float64 %.18e 6994187472632449p+690 3.592810217475959676e+223
+float64 %.19e 8797576579012143p+588 8.9125197712484551899e+192
+float64 %.20e 7363326733505337p+272 5.58769757362301140950e+97
+float64 %.21e 8549497411294502p-448 1.176257830728540379990e-119
+
+# Table 14: Stress Inputs for Conversion to 24-bit Binary, <1/2 ULP
+# NOTE: The lines with exponent p-149 have been changed from the
+# paper. Those entries originally read p-150 and had a mantissa
+# twice as large (and even), but IEEE single-precision has no p-150:
+# that's the start of the denormals.
+float32 %b 5e-20 15474250p-88
+float32 %b 67e+14 12479722p+29
+float32 %b 985e+15 14333636p+36
+# float32 %b 7693e-42 10979816p-150
+float32 %b 7693e-42 5489908p-149
+float32 %b 55895e-16 12888509p-61
+# float32 %b 996622e-44 14224264p-150
+float32 %b 996622e-44 7112132p-149
+float32 %b 7038531e-32 11420669p-107
+# float32 %b 60419369e-46 8623340p-150
+float32 %b 60419369e-46 4311670p-149
+float32 %b 702990899e-20 16209866p-61
+# float32 %b 6930161142e-48 9891056p-150
+float32 %b 6930161142e-48 4945528p-149
+float32 %b 25933168707e+13 14395800p+54
+float32 %b 596428896559e+20 12333860p+82
+
+# Table 15: Stress Inputs for Conversion to 24-bit Binary, >1/2 ULP
+float32 %b 3e-23 9507380p-98
+float32 %b 57e+18 12960300p+42
+float32 %b 789e-35 10739312p-130
+float32 %b 2539e-18 11990089p-72
+float32 %b 76173e+28 9845130p+86
+float32 %b 887745e-11 9760860p-40
+float32 %b 5382571e-37 11447463p-124
+float32 %b 82381273e-35 8554961p-113
+float32 %b 750486563e-38 9975678p-120
+float32 %b 3752432815e-39 9975678p-121
+float32 %b 75224575729e-45 13105970p-137
+float32 %b 459926601011e+15 12466336p+65
+
+# Table 16: Stress Inputs for Converting 24-bit Binary to Decimal, < 1/2 ULP
+float32 %.0e 12676506p-102 2e-24
+float32 %.1e 12676506p-103 1.2e-24
+float32 %.2e 15445013p+86 1.19e+33
+float32 %.3e 13734123p-138 3.941e-35
+float32 %.4e 12428269p-130 9.1308e-33
+float32 %.5e 15334037p-146 1.71900e-37
+float32 %.6e 11518287p-41 5.237910e-06
+float32 %.7e 12584953p-145 2.8216440e-37
+float32 %.8e 15961084p-125 3.75243281e-31
+float32 %.9e 14915817p-146 1.672120916e-37
+float32 %.10e 10845484p-102 2.1388945814e-24
+float32 %.11e 16431059p-61 7.12583594561e-12
+
+# Table 17: Stress Inputs for Converting 24-bit Binary to Decimal, > 1/2 ULP
+float32 %.0e 16093626p+69 1e+28
+float32 %.1e 9983778p+25 3.4e+14
+float32 %.2e 12745034p+104 2.59e+38
+float32 %.3e 12706553p+72 6.001e+28
+float32 %.4e 11005028p+45 3.8721e+20
+float32 %.5e 15059547p+71 3.55584e+28
+float32 %.6e 16015691p-99 2.526831e-23
+float32 %.7e 8667859p+56 6.2458507e+23
+float32 %.8e 14855922p-82 3.07213267e-18
+float32 %.9e 14855922p-83 1.536066333e-18
+float32 %.10e 10144164p-110 7.8147796834e-27
+float32 %.11e 13248074p+95 5.24810279937e+35
+`
+
+func TestFp(t *testing.T) {
+ s := bufio.NewScanner(strings.NewReader(testfp))
+
+ for lineno := 1; s.Scan(); lineno++ {
+ line := s.Text()
+ if len(line) == 0 || line[0] == '#' {
+ continue
+ }
+ a := strings.Split(line, " ")
+ if len(a) != 4 {
+ t.Error("testdata/testfp.txt:", lineno, ": wrong field count")
+ continue
+ }
+ var s string
+ var v float64
+ switch a[0] {
+ case "float64":
+ var ok bool
+ v, ok = myatof64(a[2])
+ if !ok {
+ t.Error("testdata/testfp.txt:", lineno, ": cannot atof64 ", a[2])
+ continue
+ }
+ s = fmt.Sprintf(a[1], v)
+ case "float32":
+ v1, ok := myatof32(a[2])
+ if !ok {
+ t.Error("testdata/testfp.txt:", lineno, ": cannot atof32 ", a[2])
+ continue
+ }
+ s = fmt.Sprintf(a[1], v1)
+ v = float64(v1)
+ }
+ if s != a[3] {
+ t.Error("testdata/testfp.txt:", lineno, ": ", a[0], " ", a[1], " ", a[2], " (", v, ") ",
+ "want ", a[3], " got ", s)
+ }
+ }
+ if s.Err() != nil {
+ t.Fatal("testfp: read testdata/testfp.txt: ", s.Err())
+ }
+}
diff --git a/gnovm/stdlibs/strconv/ftoa.gno b/gnovm/stdlibs/strconv/ftoa.gno
new file mode 100644
index 00000000000..fcbf4df13b6
--- /dev/null
+++ b/gnovm/stdlibs/strconv/ftoa.gno
@@ -0,0 +1,584 @@
+// Copyright 2009 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.
+
+// Binary to decimal floating point conversion.
+// Algorithm:
+// 1) store mantissa in multiprecision decimal
+// 2) shift decimal by exponent
+// 3) read digits out & format
+
+package strconv
+
+import "math"
+
+// TODO: move elsewhere?
+type floatInfo struct {
+ mantbits uint
+ expbits uint
+ bias int
+}
+
+var float32info = floatInfo{23, 8, -127}
+var float64info = floatInfo{52, 11, -1023}
+
+// FormatFloat converts the floating-point number f to a string,
+// according to the format fmt and precision prec. It rounds the
+// result assuming that the original was obtained from a floating-point
+// value of bitSize bits (32 for float32, 64 for float64).
+//
+// The format fmt is one of
+// 'b' (-ddddp±ddd, a binary exponent),
+// 'e' (-d.dddde±dd, a decimal exponent),
+// 'E' (-d.ddddE±dd, a decimal exponent),
+// 'f' (-ddd.dddd, no exponent),
+// 'g' ('e' for large exponents, 'f' otherwise),
+// 'G' ('E' for large exponents, 'f' otherwise),
+// 'x' (-0xd.ddddp±ddd, a hexadecimal fraction and binary exponent), or
+// 'X' (-0Xd.ddddP±ddd, a hexadecimal fraction and binary exponent).
+//
+// The precision prec controls the number of digits (excluding the exponent)
+// printed by the 'e', 'E', 'f', 'g', 'G', 'x', and 'X' formats.
+// For 'e', 'E', 'f', 'x', and 'X', it is the number of digits after the decimal point.
+// For 'g' and 'G' it is the maximum number of significant digits (trailing
+// zeros are removed).
+// The special precision -1 uses the smallest number of digits
+// necessary such that ParseFloat will return f exactly.
+func FormatFloat(f float64, fmt byte, prec, bitSize int) string {
+ return string(genericFtoa(make([]byte, 0, max(prec+4, 24)), f, fmt, prec, bitSize))
+}
+
+// AppendFloat appends the string form of the floating-point number f,
+// as generated by FormatFloat, to dst and returns the extended buffer.
+func AppendFloat(dst []byte, f float64, fmt byte, prec, bitSize int) []byte {
+ return genericFtoa(dst, f, fmt, prec, bitSize)
+}
+
+func genericFtoa(dst []byte, val float64, fmt byte, prec, bitSize int) []byte {
+ var bits uint64
+ var flt *floatInfo
+ switch bitSize {
+ case 32:
+ bits = uint64(math.Float32bits(float32(val)))
+ flt = &float32info
+ case 64:
+ bits = math.Float64bits(val)
+ flt = &float64info
+ default:
+ panic("strconv: illegal AppendFloat/FormatFloat bitSize")
+ }
+
+ neg := bits>>(flt.expbits+flt.mantbits) != 0
+ exp := int(bits>>flt.mantbits) & (1< digs.nd && digs.nd >= digs.dp {
+ eprec = digs.nd
+ }
+ // %e is used if the exponent from the conversion
+ // is less than -4 or greater than or equal to the precision.
+ // if precision was the shortest possible, use precision 6 for this decision.
+ if shortest {
+ eprec = 6
+ }
+ exp := digs.dp - 1
+ if exp < -4 || exp >= eprec {
+ if prec > digs.nd {
+ prec = digs.nd
+ }
+ return fmtE(dst, neg, digs, prec-1, fmt+'e'-'g')
+ }
+ if prec > digs.dp {
+ prec = digs.nd
+ }
+ return fmtF(dst, neg, digs, max(prec-digs.dp, 0))
+ }
+
+ // unknown format
+ return append(dst, '%', fmt)
+}
+
+// roundShortest rounds d (= mant * 2^exp) to the shortest number of digits
+// that will let the original floating point value be precisely reconstructed.
+func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) {
+ // If mantissa is zero, the number is zero; stop now.
+ if mant == 0 {
+ d.nd = 0
+ return
+ }
+
+ // Compute upper and lower such that any decimal number
+ // between upper and lower (possibly inclusive)
+ // will round to the original floating point number.
+
+ // We may see at once that the number is already shortest.
+ //
+ // Suppose d is not denormal, so that 2^exp <= d < 10^dp.
+ // The closest shorter number is at least 10^(dp-nd) away.
+ // The lower/upper bounds computed below are at distance
+ // at most 2^(exp-mantbits).
+ //
+ // So the number is already shortest if 10^(dp-nd) > 2^(exp-mantbits),
+ // or equivalently log2(10)*(dp-nd) > exp-mantbits.
+ // It is true if 332/100*(dp-nd) >= exp-mantbits (log2(10) > 3.32).
+ minexp := flt.bias + 1 // minimum possible exponent
+ if exp > minexp && 332*(d.dp-d.nd) >= 100*(exp-int(flt.mantbits)) {
+ // The number is already shortest.
+ return
+ }
+
+ // d = mant << (exp - mantbits)
+ // Next highest floating point number is mant+1 << exp-mantbits.
+ // Our upper bound is halfway between, mant*2+1 << exp-mantbits-1.
+ upper := new(decimal)
+ upper.Assign(mant*2 + 1)
+ upper.Shift(exp - int(flt.mantbits) - 1)
+
+ // d = mant << (exp - mantbits)
+ // Next lowest floating point number is mant-1 << exp-mantbits,
+ // unless mant-1 drops the significant bit and exp is not the minimum exp,
+ // in which case the next lowest is mant*2-1 << exp-mantbits-1.
+ // Either way, call it mantlo << explo-mantbits.
+ // Our lower bound is halfway between, mantlo*2+1 << explo-mantbits-1.
+ var mantlo uint64
+ var explo int
+ if mant > 1<= d.nd {
+ break
+ }
+ li := ui - upper.dp + lower.dp
+ l := byte('0') // lower digit
+ if li >= 0 && li < lower.nd {
+ l = lower.d[li]
+ }
+ m := byte('0') // middle digit
+ if mi >= 0 {
+ m = d.d[mi]
+ }
+ u := byte('0') // upper digit
+ if ui < upper.nd {
+ u = upper.d[ui]
+ }
+
+ // Okay to round down (truncate) if lower has a different digit
+ // or if lower is inclusive and is exactly the result of rounding
+ // down (i.e., and we have reached the final digit of lower).
+ okdown := l != m || inclusive && li+1 == lower.nd
+
+ switch {
+ case upperdelta == 0 && m+1 < u:
+ // Example:
+ // m = 12345xxx
+ // u = 12347xxx
+ upperdelta = 2
+ case upperdelta == 0 && m != u:
+ // Example:
+ // m = 12345xxx
+ // u = 12346xxx
+ upperdelta = 1
+ case upperdelta == 1 && (m != '9' || u != '0'):
+ // Example:
+ // m = 1234598x
+ // u = 1234600x
+ upperdelta = 2
+ }
+ // Okay to round up if upper has a different digit and either upper
+ // is inclusive or upper is bigger than the result of rounding up.
+ okup := upperdelta > 0 && (inclusive || upperdelta > 1 || ui+1 < upper.nd)
+
+ // If it's okay to do either, then round to the nearest one.
+ // If it's okay to do only one, do it.
+ switch {
+ case okdown && okup:
+ d.Round(mi + 1)
+ return
+ case okdown:
+ d.RoundDown(mi + 1)
+ return
+ case okup:
+ d.RoundUp(mi + 1)
+ return
+ }
+ }
+}
+
+type decimalSlice struct {
+ d []byte
+ nd, dp int
+}
+
+// %e: -d.ddddde±dd
+func fmtE(dst []byte, neg bool, d decimalSlice, prec int, fmt byte) []byte {
+ // sign
+ if neg {
+ dst = append(dst, '-')
+ }
+
+ // first digit
+ ch := byte('0')
+ if d.nd != 0 {
+ ch = d.d[0]
+ }
+ dst = append(dst, ch)
+
+ // .moredigits
+ if prec > 0 {
+ dst = append(dst, '.')
+ i := 1
+ m := min(d.nd, prec+1)
+ if i < m {
+ dst = append(dst, d.d[i:m]...)
+ i = m
+ }
+ for ; i <= prec; i++ {
+ dst = append(dst, '0')
+ }
+ }
+
+ // e±
+ dst = append(dst, fmt)
+ exp := d.dp - 1
+ if d.nd == 0 { // special case: 0 has exponent 0
+ exp = 0
+ }
+ if exp < 0 {
+ ch = '-'
+ exp = -exp
+ } else {
+ ch = '+'
+ }
+ dst = append(dst, ch)
+
+ // dd or ddd
+ switch {
+ case exp < 10:
+ dst = append(dst, '0', byte(exp)+'0')
+ case exp < 100:
+ dst = append(dst, byte(exp/10)+'0', byte(exp%10)+'0')
+ default:
+ dst = append(dst, byte(exp/100)+'0', byte(exp/10)%10+'0', byte(exp%10)+'0')
+ }
+
+ return dst
+}
+
+// %f: -ddddddd.ddddd
+func fmtF(dst []byte, neg bool, d decimalSlice, prec int) []byte {
+ // sign
+ if neg {
+ dst = append(dst, '-')
+ }
+
+ // integer, padded with zeros as needed.
+ if d.dp > 0 {
+ m := min(d.nd, d.dp)
+ dst = append(dst, d.d[:m]...)
+ for ; m < d.dp; m++ {
+ dst = append(dst, '0')
+ }
+ } else {
+ dst = append(dst, '0')
+ }
+
+ // fraction
+ if prec > 0 {
+ dst = append(dst, '.')
+ for i := 0; i < prec; i++ {
+ ch := byte('0')
+ if j := d.dp + i; 0 <= j && j < d.nd {
+ ch = d.d[j]
+ }
+ dst = append(dst, ch)
+ }
+ }
+
+ return dst
+}
+
+// %b: -ddddddddp±ddd
+func fmtB(dst []byte, neg bool, mant uint64, exp int, flt *floatInfo) []byte {
+ // sign
+ if neg {
+ dst = append(dst, '-')
+ }
+
+ // mantissa
+ dst, _ = formatBits(dst, mant, 10, false, true)
+
+ // p
+ dst = append(dst, 'p')
+
+ // ±exponent
+ exp -= int(flt.mantbits)
+ if exp >= 0 {
+ dst = append(dst, '+')
+ }
+ dst, _ = formatBits(dst, uint64(exp), 10, exp < 0, true)
+
+ return dst
+}
+
+// %x: -0x1.yyyyyyyyp±ddd or -0x0p+0. (y is hex digit, d is decimal digit)
+func fmtX(dst []byte, prec int, fmt byte, neg bool, mant uint64, exp int, flt *floatInfo) []byte {
+ if mant == 0 {
+ exp = 0
+ }
+
+ // Shift digits so leading 1 (if any) is at bit 1<<60.
+ mant <<= 60 - flt.mantbits
+ for mant != 0 && mant&(1<<60) == 0 {
+ mant <<= 1
+ exp--
+ }
+
+ // Round if requested.
+ if prec >= 0 && prec < 15 {
+ shift := uint(prec * 4)
+ extra := (mant << shift) & (1<<60 - 1)
+ mant >>= 60 - shift
+ if extra|(mant&1) > 1<<59 {
+ mant++
+ }
+ mant <<= 60 - shift
+ if mant&(1<<61) != 0 {
+ // Wrapped around.
+ mant >>= 1
+ exp++
+ }
+ }
+
+ hex := lowerhex
+ if fmt == 'X' {
+ hex = upperhex
+ }
+
+ // sign, 0x, leading digit
+ if neg {
+ dst = append(dst, '-')
+ }
+ dst = append(dst, '0', fmt, '0'+byte((mant>>60)&1))
+
+ // .fraction
+ mant <<= 4 // remove leading 0 or 1
+ if prec < 0 && mant != 0 {
+ dst = append(dst, '.')
+ for mant != 0 {
+ dst = append(dst, hex[(mant>>60)&15])
+ mant <<= 4
+ }
+ } else if prec > 0 {
+ dst = append(dst, '.')
+ for i := 0; i < prec; i++ {
+ dst = append(dst, hex[(mant>>60)&15])
+ mant <<= 4
+ }
+ }
+
+ // p±
+ ch := byte('P')
+ if fmt == lower(fmt) {
+ ch = 'p'
+ }
+ dst = append(dst, ch)
+ if exp < 0 {
+ ch = '-'
+ exp = -exp
+ } else {
+ ch = '+'
+ }
+ dst = append(dst, ch)
+
+ // dd or ddd or dddd
+ switch {
+ case exp < 100:
+ dst = append(dst, byte(exp/10)+'0', byte(exp%10)+'0')
+ case exp < 1000:
+ dst = append(dst, byte(exp/100)+'0', byte((exp/10)%10)+'0', byte(exp%10)+'0')
+ default:
+ dst = append(dst, byte(exp/1000)+'0', byte(exp/100)%10+'0', byte((exp/10)%10)+'0', byte(exp%10)+'0')
+ }
+
+ return dst
+}
+
+func min(a, b int) int {
+ if a < b {
+ return a
+ }
+ return b
+}
+
+func max(a, b int) int {
+ if a > b {
+ return a
+ }
+ return b
+}
diff --git a/gnovm/stdlibs/strconv/ftoa_test.gno b/gnovm/stdlibs/strconv/ftoa_test.gno
new file mode 100644
index 00000000000..df1cc733827
--- /dev/null
+++ b/gnovm/stdlibs/strconv/ftoa_test.gno
@@ -0,0 +1,323 @@
+// Copyright 2009 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.
+
+package strconv
+
+import (
+ "math"
+ "math/rand"
+ "testing"
+)
+
+type ftoaTest struct {
+ f float64
+ fmt byte
+ prec int
+ s string
+}
+
+func fdiv(a, b float64) float64 { return a / b }
+
+const (
+ below1e23 = 99999999999999974834176
+ above1e23 = 100000000000000008388608
+)
+
+var ftoatests = []ftoaTest{
+ {1, 'e', 5, "1.00000e+00"},
+ {1, 'f', 5, "1.00000"},
+ {1, 'g', 5, "1"},
+ {1, 'g', -1, "1"},
+ {1, 'x', -1, "0x1p+00"},
+ {1, 'x', 5, "0x1.00000p+00"},
+ {20, 'g', -1, "20"},
+ {20, 'x', -1, "0x1.4p+04"},
+ {1234567.8, 'g', -1, "1.2345678e+06"},
+ {1234567.8, 'x', -1, "0x1.2d687cccccccdp+20"},
+ {200000, 'g', -1, "200000"},
+ {200000, 'x', -1, "0x1.86ap+17"},
+ {200000, 'X', -1, "0X1.86AP+17"},
+ {2000000, 'g', -1, "2e+06"},
+ {1e10, 'g', -1, "1e+10"},
+
+ // g conversion and zero suppression
+ {400, 'g', 2, "4e+02"},
+ {40, 'g', 2, "40"},
+ {4, 'g', 2, "4"},
+ {.4, 'g', 2, "0.4"},
+ {.04, 'g', 2, "0.04"},
+ {.004, 'g', 2, "0.004"},
+ {.0004, 'g', 2, "0.0004"},
+ {.00004, 'g', 2, "4e-05"},
+ {.000004, 'g', 2, "4e-06"},
+
+ {0, 'e', 5, "0.00000e+00"},
+ {0, 'f', 5, "0.00000"},
+ {0, 'g', 5, "0"},
+ {0, 'g', -1, "0"},
+ {0, 'x', 5, "0x0.00000p+00"},
+
+ {-1, 'e', 5, "-1.00000e+00"},
+ {-1, 'f', 5, "-1.00000"},
+ {-1, 'g', 5, "-1"},
+ {-1, 'g', -1, "-1"},
+
+ {12, 'e', 5, "1.20000e+01"},
+ {12, 'f', 5, "12.00000"},
+ {12, 'g', 5, "12"},
+ {12, 'g', -1, "12"},
+
+ {123456700, 'e', 5, "1.23457e+08"},
+ {123456700, 'f', 5, "123456700.00000"},
+ {123456700, 'g', 5, "1.2346e+08"},
+ {123456700, 'g', -1, "1.234567e+08"},
+
+ {1.2345e6, 'e', 5, "1.23450e+06"},
+ {1.2345e6, 'f', 5, "1234500.00000"},
+ {1.2345e6, 'g', 5, "1.2345e+06"},
+
+ // Round to even
+ {1.2345e6, 'e', 3, "1.234e+06"},
+ {1.2355e6, 'e', 3, "1.236e+06"},
+ {1.2345, 'f', 3, "1.234"},
+ {1.2355, 'f', 3, "1.236"},
+ {1234567890123456.5, 'e', 15, "1.234567890123456e+15"},
+ {1234567890123457.5, 'e', 15, "1.234567890123458e+15"},
+ {108678236358137.625, 'g', -1, "1.0867823635813762e+14"},
+
+ {1e23, 'e', 17, "9.99999999999999916e+22"},
+ {1e23, 'f', 17, "99999999999999991611392.00000000000000000"},
+ {1e23, 'g', 17, "9.9999999999999992e+22"},
+
+ {1e23, 'e', -1, "1e+23"},
+ {1e23, 'f', -1, "100000000000000000000000"},
+ {1e23, 'g', -1, "1e+23"},
+
+ {below1e23, 'e', 17, "9.99999999999999748e+22"},
+ {below1e23, 'f', 17, "99999999999999974834176.00000000000000000"},
+ {below1e23, 'g', 17, "9.9999999999999975e+22"},
+
+ {below1e23, 'e', -1, "9.999999999999997e+22"},
+ {below1e23, 'f', -1, "99999999999999970000000"},
+ {below1e23, 'g', -1, "9.999999999999997e+22"},
+
+ {above1e23, 'e', 17, "1.00000000000000008e+23"},
+ {above1e23, 'f', 17, "100000000000000008388608.00000000000000000"},
+ {above1e23, 'g', 17, "1.0000000000000001e+23"},
+
+ {above1e23, 'e', -1, "1.0000000000000001e+23"},
+ {above1e23, 'f', -1, "100000000000000010000000"},
+ {above1e23, 'g', -1, "1.0000000000000001e+23"},
+
+ {fdiv(5e-304, 1e20), 'g', -1, "5e-324"}, // avoid constant arithmetic
+ {fdiv(-5e-304, 1e20), 'g', -1, "-5e-324"}, // avoid constant arithmetic
+
+ {32, 'g', -1, "32"},
+ {32, 'g', 0, "3e+01"},
+
+ {100, 'x', -1, "0x1.9p+06"},
+ {100, 'y', -1, "%y"},
+
+ {math.NaN(), 'g', -1, "NaN"},
+ {-math.NaN(), 'g', -1, "NaN"},
+ {math.Inf(0), 'g', -1, "+Inf"},
+ {math.Inf(-1), 'g', -1, "-Inf"},
+ {-math.Inf(0), 'g', -1, "-Inf"},
+
+ {-1, 'b', -1, "-4503599627370496p-52"},
+
+ // fixed bugs
+ {0.9, 'f', 1, "0.9"},
+ {0.09, 'f', 1, "0.1"},
+ {0.0999, 'f', 1, "0.1"},
+ {0.05, 'f', 1, "0.1"},
+ {0.05, 'f', 0, "0"},
+ {0.5, 'f', 1, "0.5"},
+ {0.5, 'f', 0, "0"},
+ {1.5, 'f', 0, "2"},
+
+ // https://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/
+ {2.2250738585072012e-308, 'g', -1, "2.2250738585072014e-308"},
+ // https://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/
+ {2.2250738585072011e-308, 'g', -1, "2.225073858507201e-308"},
+
+ // Issue 2625.
+ {383260575764816448, 'f', 0, "383260575764816448"},
+ {383260575764816448, 'g', -1, "3.8326057576481645e+17"},
+
+ // Issue 29491.
+ {498484681984085570, 'f', -1, "498484681984085570"},
+ {-5.8339553793802237e+23, 'g', -1, "-5.8339553793802237e+23"},
+
+ // Issue 52187
+ {123.45, '?', 0, "%?"},
+ {123.45, '?', 1, "%?"},
+ {123.45, '?', -1, "%?"},
+
+ // rounding
+ {2.275555555555555, 'x', -1, "0x1.23456789abcdep+01"},
+ {2.275555555555555, 'x', 0, "0x1p+01"},
+ {2.275555555555555, 'x', 2, "0x1.23p+01"},
+ {2.275555555555555, 'x', 16, "0x1.23456789abcde000p+01"},
+ {2.275555555555555, 'x', 21, "0x1.23456789abcde00000000p+01"},
+ {2.2755555510520935, 'x', -1, "0x1.2345678p+01"},
+ {2.2755555510520935, 'x', 6, "0x1.234568p+01"},
+ {2.275555431842804, 'x', -1, "0x1.2345668p+01"},
+ {2.275555431842804, 'x', 6, "0x1.234566p+01"},
+ {3.999969482421875, 'x', -1, "0x1.ffffp+01"},
+ {3.999969482421875, 'x', 4, "0x1.ffffp+01"},
+ {3.999969482421875, 'x', 3, "0x1.000p+02"},
+ {3.999969482421875, 'x', 2, "0x1.00p+02"},
+ {3.999969482421875, 'x', 1, "0x1.0p+02"},
+ {3.999969482421875, 'x', 0, "0x1p+02"},
+}
+
+func TestFtoa(t *testing.T) {
+ for i := 0; i < len(ftoatests); i++ {
+ test := &ftoatests[i]
+ s := FormatFloat(test.f, test.fmt, test.prec, 64)
+ if s != test.s {
+ t.Error("testN=64", test.f, string(test.fmt), test.prec, "want", test.s, "got", s)
+ }
+ x := AppendFloat([]byte("abc"), test.f, test.fmt, test.prec, 64)
+ if string(x) != "abc"+test.s {
+ t.Error("AppendFloat testN=64", test.f, string(test.fmt), test.prec, "want", "abc"+test.s, "got", string(x))
+ }
+ if float64(float32(test.f)) == test.f && test.fmt != 'b' {
+ s := FormatFloat(test.f, test.fmt, test.prec, 32)
+ if s != test.s {
+ t.Error("testN=32", test.f, string(test.fmt), test.prec, "want", test.s, "got", s)
+ }
+ x := AppendFloat([]byte("abc"), test.f, test.fmt, test.prec, 32)
+ if string(x) != "abc"+test.s {
+ t.Error("AppendFloat testN=32", test.f, string(test.fmt), test.prec, "want", "abc"+test.s, "got", string(x))
+ }
+ }
+ }
+}
+
+func TestFtoaPowersOfTwo(t *testing.T) {
+ for exp := -2048; exp <= 2048; exp++ {
+ f := math.Ldexp(1, exp)
+ if !math.IsInf(f, 0) {
+ s := FormatFloat(f, 'e', -1, 64)
+ if x, _ := ParseFloat(s, 64); x != f {
+ t.Errorf("failed roundtrip %v => %s => %v", f, s, x)
+ }
+ }
+ f32 := float32(f)
+ if !math.IsInf(float64(f32), 0) {
+ s := FormatFloat(float64(f32), 'e', -1, 32)
+ if x, _ := ParseFloat(s, 32); float32(x) != f32 {
+ t.Errorf("failed roundtrip %v => %s => %v", f32, s, float32(x))
+ }
+ }
+ }
+}
+
+func TestFtoaRandom(t *testing.T) {
+ N := int(1e4)
+ if testing.Short() {
+ N = 100
+ }
+ t.Logf("testing %d random numbers with fast and slow FormatFloat", N)
+ for i := 0; i < N; i++ {
+ bits := uint64(rand.Uint32())<<32 | uint64(rand.Uint32())
+ x := math.Float64frombits(bits)
+
+ shortFast := FormatFloat(x, 'g', -1, 64)
+ SetOptimize(false)
+ shortSlow := FormatFloat(x, 'g', -1, 64)
+ SetOptimize(true)
+ if shortSlow != shortFast {
+ t.Errorf("%b printed as %s, want %s", x, shortFast, shortSlow)
+ }
+
+ prec := rand.IntN(12) + 5
+ shortFast = FormatFloat(x, 'e', prec, 64)
+ SetOptimize(false)
+ shortSlow = FormatFloat(x, 'e', prec, 64)
+ SetOptimize(true)
+ if shortSlow != shortFast {
+ t.Errorf("%b printed as %s, want %s", x, shortFast, shortSlow)
+ }
+ }
+}
+
+func TestFormatFloatInvalidBitSize(t *testing.T) {
+ defer func() {
+ if r := recover(); r == nil {
+ t.Fatalf("expected panic due to invalid bitSize")
+ }
+ }()
+ _ = FormatFloat(3.14, 'g', -1, 100)
+}
+
+var ftoaBenches = []struct {
+ name string
+ float float64
+ fmt byte
+ prec int
+ bitSize int
+}{
+ {"Decimal", 33909, 'g', -1, 64},
+ {"Float", 339.7784, 'g', -1, 64},
+ {"Exp", -5.09e75, 'g', -1, 64},
+ {"NegExp", -5.11e-95, 'g', -1, 64},
+ {"LongExp", 1.234567890123456e-78, 'g', -1, 64},
+
+ {"Big", 123456789123456789123456789, 'g', -1, 64},
+ {"BinaryExp", -1, 'b', -1, 64},
+
+ {"32Integer", 33909, 'g', -1, 32},
+ {"32ExactFraction", 3.375, 'g', -1, 32},
+ {"32Point", 339.7784, 'g', -1, 32},
+ {"32Exp", -5.09e25, 'g', -1, 32},
+ {"32NegExp", -5.11e-25, 'g', -1, 32},
+ {"32Shortest", 1.234567e-8, 'g', -1, 32},
+ {"32Fixed8Hard", math.Ldexp(15961084, -125), 'e', 8, 32},
+ {"32Fixed9Hard", math.Ldexp(14855922, -83), 'e', 9, 32},
+
+ {"64Fixed1", 123456, 'e', 3, 64},
+ {"64Fixed2", 123.456, 'e', 3, 64},
+ {"64Fixed3", 1.23456e+78, 'e', 3, 64},
+ {"64Fixed4", 1.23456e-78, 'e', 3, 64},
+ {"64Fixed12", 1.23456e-78, 'e', 12, 64},
+ {"64Fixed16", 1.23456e-78, 'e', 16, 64},
+ // From testdata/testfp.txt
+ {"64Fixed12Hard", math.Ldexp(6965949469487146, -249), 'e', 12, 64},
+ {"64Fixed17Hard", math.Ldexp(8887055249355788, 665), 'e', 17, 64},
+ {"64Fixed18Hard", math.Ldexp(6994187472632449, 690), 'e', 18, 64},
+
+ // Trigger slow path (see issue #15672).
+ // The shortest is: 8.034137530808823e+43
+ {"Slowpath64", 8.03413753080882349e+43, 'e', -1, 64},
+ // This denormal is pathological because the lower/upper
+ // halfways to neighboring floats are:
+ // 622666234635.321003e-320 ~= 622666234635.321e-320
+ // 622666234635.321497e-320 ~= 622666234635.3215e-320
+ // making it hard to find the 3rd digit
+ {"SlowpathDenormal64", 622666234635.3213e-320, 'e', -1, 64},
+}
+
+func BenchmarkFormatFloat(b *testing.B) {
+ for _, c := range ftoaBenches {
+ b.Run(c.name, func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ FormatFloat(c.float, c.fmt, c.prec, c.bitSize)
+ }
+ })
+ }
+}
+
+func BenchmarkAppendFloat(b *testing.B) {
+ dst := make([]byte, 30)
+ for _, c := range ftoaBenches {
+ b.Run(c.name, func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ AppendFloat(dst[:0], c.float, c.fmt, c.prec, c.bitSize)
+ }
+ })
+ }
+}
diff --git a/gnovm/stdlibs/strconv/ftoaryu.gno b/gnovm/stdlibs/strconv/ftoaryu.gno
new file mode 100644
index 00000000000..2e7bf71df0b
--- /dev/null
+++ b/gnovm/stdlibs/strconv/ftoaryu.gno
@@ -0,0 +1,569 @@
+// Copyright 2021 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.
+
+package strconv
+
+import (
+ "math/bits"
+)
+
+// binary to decimal conversion using the Ryū algorithm.
+//
+// See Ulf Adams, "Ryū: Fast Float-to-String Conversion" (doi:10.1145/3192366.3192369)
+//
+// Fixed precision formatting is a variant of the original paper's
+// algorithm, where a single multiplication by 10^k is required,
+// sharing the same rounding guarantees.
+
+// ryuFtoaFixed32 formats mant*(2^exp) with prec decimal digits.
+func ryuFtoaFixed32(d *decimalSlice, mant uint32, exp int, prec int) {
+ if prec < 0 {
+ panic("ryuFtoaFixed32 called with negative prec")
+ }
+ if prec > 9 {
+ panic("ryuFtoaFixed32 called with prec > 9")
+ }
+ // Zero input.
+ if mant == 0 {
+ d.nd, d.dp = 0, 0
+ return
+ }
+ // Renormalize to a 25-bit mantissa.
+ e2 := exp
+ if b := bits.Len32(mant); b < 25 {
+ mant <<= uint(25 - b)
+ e2 += b - 25
+ }
+ // Choose an exponent such that rounded mant*(2^e2)*(10^q) has
+ // at least prec decimal digits, i.e
+ // mant*(2^e2)*(10^q) >= 10^(prec-1)
+ // Because mant >= 2^24, it is enough to choose:
+ // 2^(e2+24) >= 10^(-q+prec-1)
+ // or q = -mulByLog2Log10(e2+24) + prec - 1
+ q := -mulByLog2Log10(e2+24) + prec - 1
+
+ // Now compute mant*(2^e2)*(10^q).
+ // Is it an exact computation?
+ // Only small positive powers of 10 are exact (5^28 has 66 bits).
+ exact := q <= 27 && q >= 0
+
+ di, dexp2, d0 := mult64bitPow10(mant, e2, q)
+ if dexp2 >= 0 {
+ panic("not enough significant bits after mult64bitPow10")
+ }
+ // As a special case, computation might still be exact, if exponent
+ // was negative and if it amounts to computing an exact division.
+ // In that case, we ignore all lower bits.
+ // Note that division by 10^11 cannot be exact as 5^11 has 26 bits.
+ if q < 0 && q >= -10 && divisibleByPower5(uint64(mant), -q) {
+ exact = true
+ d0 = true
+ }
+ // Remove extra lower bits and keep rounding info.
+ extra := uint(-dexp2)
+ extraMask := uint32(1<>extra, di&extraMask
+ roundUp := false
+ if exact {
+ // If we computed an exact product, d + 1/2
+ // should round to d+1 if 'd' is odd.
+ roundUp = dfrac > 1<<(extra-1) ||
+ (dfrac == 1<<(extra-1) && !d0) ||
+ (dfrac == 1<<(extra-1) && d0 && di&1 == 1)
+ } else {
+ // otherwise, d+1/2 always rounds up because
+ // we truncated below.
+ roundUp = dfrac>>(extra-1) == 1
+ }
+ if dfrac != 0 {
+ d0 = false
+ }
+ // Proceed to the requested number of digits
+ formatDecimal(d, uint64(di), !d0, roundUp, prec)
+ // Adjust exponent
+ d.dp -= q
+}
+
+// ryuFtoaFixed64 formats mant*(2^exp) with prec decimal digits.
+func ryuFtoaFixed64(d *decimalSlice, mant uint64, exp int, prec int) {
+ if prec > 18 {
+ panic("ryuFtoaFixed64 called with prec > 18")
+ }
+ // Zero input.
+ if mant == 0 {
+ d.nd, d.dp = 0, 0
+ return
+ }
+ // Renormalize to a 55-bit mantissa.
+ e2 := exp
+ if b := bits.Len64(mant); b < 55 {
+ mant = mant << uint(55-b)
+ e2 += b - 55
+ }
+ // Choose an exponent such that rounded mant*(2^e2)*(10^q) has
+ // at least prec decimal digits, i.e
+ // mant*(2^e2)*(10^q) >= 10^(prec-1)
+ // Because mant >= 2^54, it is enough to choose:
+ // 2^(e2+54) >= 10^(-q+prec-1)
+ // or q = -mulByLog2Log10(e2+54) + prec - 1
+ //
+ // The minimal required exponent is -mulByLog2Log10(1025)+18 = -291
+ // The maximal required exponent is mulByLog2Log10(1074)+18 = 342
+ q := -mulByLog2Log10(e2+54) + prec - 1
+
+ // Now compute mant*(2^e2)*(10^q).
+ // Is it an exact computation?
+ // Only small positive powers of 10 are exact (5^55 has 128 bits).
+ exact := q <= 55 && q >= 0
+
+ di, dexp2, d0 := mult128bitPow10(mant, e2, q)
+ if dexp2 >= 0 {
+ panic("not enough significant bits after mult128bitPow10")
+ }
+ // As a special case, computation might still be exact, if exponent
+ // was negative and if it amounts to computing an exact division.
+ // In that case, we ignore all lower bits.
+ // Note that division by 10^23 cannot be exact as 5^23 has 54 bits.
+ if q < 0 && q >= -22 && divisibleByPower5(mant, -q) {
+ exact = true
+ d0 = true
+ }
+ // Remove extra lower bits and keep rounding info.
+ extra := uint(-dexp2)
+ extraMask := uint64(1<>extra, di&extraMask
+ roundUp := false
+ if exact {
+ // If we computed an exact product, d + 1/2
+ // should round to d+1 if 'd' is odd.
+ roundUp = dfrac > 1<<(extra-1) ||
+ (dfrac == 1<<(extra-1) && !d0) ||
+ (dfrac == 1<<(extra-1) && d0 && di&1 == 1)
+ } else {
+ // otherwise, d+1/2 always rounds up because
+ // we truncated below.
+ roundUp = dfrac>>(extra-1) == 1
+ }
+ if dfrac != 0 {
+ d0 = false
+ }
+ // Proceed to the requested number of digits
+ formatDecimal(d, di, !d0, roundUp, prec)
+ // Adjust exponent
+ d.dp -= q
+}
+
+var uint64pow10 = [...]uint64{
+ 1, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
+ 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
+}
+
+// formatDecimal fills d with at most prec decimal digits
+// of mantissa m. The boolean trunc indicates whether m
+// is truncated compared to the original number being formatted.
+func formatDecimal(d *decimalSlice, m uint64, trunc bool, roundUp bool, prec int) {
+ max := uint64pow10[prec]
+ trimmed := 0
+ for m >= max {
+ a, b := m/10, m%10
+ m = a
+ trimmed++
+ if b > 5 {
+ roundUp = true
+ } else if b < 5 {
+ roundUp = false
+ } else { // b == 5
+ // round up if there are trailing digits,
+ // or if the new value of m is odd (round-to-even convention)
+ roundUp = trunc || m&1 == 1
+ }
+ if b != 0 {
+ trunc = true
+ }
+ }
+ if roundUp {
+ m++
+ }
+ if m >= max {
+ // Happens if di was originally 99999....xx
+ m /= 10
+ trimmed++
+ }
+ // render digits (similar to formatBits)
+ n := uint(prec)
+ d.nd = prec
+ v := m
+ for v >= 100 {
+ var v1, v2 uint64
+ if v>>32 == 0 {
+ v1, v2 = uint64(uint32(v)/100), uint64(uint32(v)%100)
+ } else {
+ v1, v2 = v/100, v%100
+ }
+ n -= 2
+ d.d[n+1] = smallsString[2*v2+1]
+ d.d[n+0] = smallsString[2*v2+0]
+ v = v1
+ }
+ if v > 0 {
+ n--
+ d.d[n] = smallsString[2*v+1]
+ }
+ if v >= 10 {
+ n--
+ d.d[n] = smallsString[2*v]
+ }
+ for d.d[d.nd-1] == '0' {
+ d.nd--
+ trimmed++
+ }
+ d.dp = d.nd + trimmed
+}
+
+// ryuFtoaShortest formats mant*2^exp with prec decimal digits.
+func ryuFtoaShortest(d *decimalSlice, mant uint64, exp int, flt *floatInfo) {
+ if mant == 0 {
+ d.nd, d.dp = 0, 0
+ return
+ }
+ // If input is an exact integer with fewer bits than the mantissa,
+ // the previous and next integer are not admissible representations.
+ if exp <= 0 && bits.TrailingZeros64(mant) >= -exp {
+ mant >>= uint(-exp)
+ ryuDigits(d, mant, mant, mant, true, false)
+ return
+ }
+ ml, mc, mu, e2 := computeBounds(mant, exp, flt)
+ if e2 == 0 {
+ ryuDigits(d, ml, mc, mu, true, false)
+ return
+ }
+ // Find 10^q *larger* than 2^-e2
+ q := mulByLog2Log10(-e2) + 1
+
+ // We are going to multiply by 10^q using 128-bit arithmetic.
+ // The exponent is the same for all 3 numbers.
+ var dl, dc, du uint64
+ var dl0, dc0, du0 bool
+ if flt == &float32info {
+ var dl32, dc32, du32 uint32
+ dl32, _, dl0 = mult64bitPow10(uint32(ml), e2, q)
+ dc32, _, dc0 = mult64bitPow10(uint32(mc), e2, q)
+ du32, e2, du0 = mult64bitPow10(uint32(mu), e2, q)
+ dl, dc, du = uint64(dl32), uint64(dc32), uint64(du32)
+ } else {
+ dl, _, dl0 = mult128bitPow10(ml, e2, q)
+ dc, _, dc0 = mult128bitPow10(mc, e2, q)
+ du, e2, du0 = mult128bitPow10(mu, e2, q)
+ }
+ if e2 >= 0 {
+ panic("not enough significant bits after mult128bitPow10")
+ }
+ // Is it an exact computation?
+ if q > 55 {
+ // Large positive powers of ten are not exact
+ dl0, dc0, du0 = false, false, false
+ }
+ if q < 0 && q >= -24 {
+ // Division by a power of ten may be exact.
+ // (note that 5^25 is a 59-bit number so division by 5^25 is never exact).
+ if divisibleByPower5(ml, -q) {
+ dl0 = true
+ }
+ if divisibleByPower5(mc, -q) {
+ dc0 = true
+ }
+ if divisibleByPower5(mu, -q) {
+ du0 = true
+ }
+ }
+ // Express the results (dl, dc, du)*2^e2 as integers.
+ // Extra bits must be removed and rounding hints computed.
+ extra := uint(-e2)
+ extraMask := uint64(1<>extra, dl&extraMask
+ dc, fracc := dc>>extra, dc&extraMask
+ du, fracu := du>>extra, du&extraMask
+ // Is it allowed to use 'du' as a result?
+ // It is always allowed when it is truncated, but also
+ // if it is exact and the original binary mantissa is even
+ // When disallowed, we can subtract 1.
+ uok := !du0 || fracu > 0
+ if du0 && fracu == 0 {
+ uok = mant&1 == 0
+ }
+ if !uok {
+ du--
+ }
+ // Is 'dc' the correctly rounded base 10 mantissa?
+ // The correct rounding might be dc+1
+ cup := false // don't round up.
+ if dc0 {
+ // If we computed an exact product, the half integer
+ // should round to next (even) integer if 'dc' is odd.
+ cup = fracc > 1<<(extra-1) ||
+ (fracc == 1<<(extra-1) && dc&1 == 1)
+ } else {
+ // otherwise, the result is a lower truncation of the ideal
+ // result.
+ cup = fracc>>(extra-1) == 1
+ }
+ // Is 'dl' an allowed representation?
+ // Only if it is an exact value, and if the original binary mantissa
+ // was even.
+ lok := dl0 && fracl == 0 && (mant&1 == 0)
+ if !lok {
+ dl++
+ }
+ // We need to remember whether the trimmed digits of 'dc' are zero.
+ c0 := dc0 && fracc == 0
+ // render digits
+ ryuDigits(d, dl, dc, du, c0, cup)
+ d.dp -= q
+}
+
+// mulByLog2Log10 returns math.Floor(x * log(2)/log(10)) for an integer x in
+// the range -1600 <= x && x <= +1600.
+//
+// The range restriction lets us work in faster integer arithmetic instead of
+// slower floating point arithmetic. Correctness is verified by unit tests.
+func mulByLog2Log10(x int) int {
+ // log(2)/log(10) ≈ 0.30102999566 ≈ 78913 / 2^18
+ return (x * 78913) >> 18
+}
+
+// mulByLog10Log2 returns math.Floor(x * log(10)/log(2)) for an integer x in
+// the range -500 <= x && x <= +500.
+//
+// The range restriction lets us work in faster integer arithmetic instead of
+// slower floating point arithmetic. Correctness is verified by unit tests.
+func mulByLog10Log2(x int) int {
+ // log(10)/log(2) ≈ 3.32192809489 ≈ 108853 / 2^15
+ return (x * 108853) >> 15
+}
+
+// computeBounds returns a floating-point vector (l, c, u)×2^e2
+// where the mantissas are 55-bit (or 26-bit) integers, describing the interval
+// represented by the input float64 or float32.
+func computeBounds(mant uint64, exp int, flt *floatInfo) (lower, central, upper uint64, e2 int) {
+ if mant != 1< 5e8) || (clo == 5e8 && cup)
+ ryuDigits32(d, lhi, chi, uhi, c0, cup, 8)
+ d.dp += 9
+ } else {
+ d.nd = 0
+ // emit high part
+ n := uint(9)
+ for v := chi; v > 0; {
+ v1, v2 := v/10, v%10
+ v = v1
+ n--
+ d.d[n] = byte(v2 + '0')
+ }
+ d.d = d.d[n:]
+ d.nd = int(9 - n)
+ // emit low part
+ ryuDigits32(d, llo, clo, ulo,
+ c0, cup, d.nd+8)
+ }
+ // trim trailing zeros
+ for d.nd > 0 && d.d[d.nd-1] == '0' {
+ d.nd--
+ }
+ // trim initial zeros
+ for d.nd > 0 && d.d[0] == '0' {
+ d.nd--
+ d.dp--
+ d.d = d.d[1:]
+ }
+}
+
+// ryuDigits32 emits decimal digits for a number less than 1e9.
+func ryuDigits32(d *decimalSlice, lower, central, upper uint32,
+ c0, cup bool, endindex int) {
+ if upper == 0 {
+ d.dp = endindex + 1
+ return
+ }
+ trimmed := 0
+ // Remember last trimmed digit to check for round-up.
+ // c0 will be used to remember zeroness of following digits.
+ cNextDigit := 0
+ for upper > 0 {
+ // Repeatedly compute:
+ // l = Ceil(lower / 10^k)
+ // c = Round(central / 10^k)
+ // u = Floor(upper / 10^k)
+ // and stop when c goes out of the (l, u) interval.
+ l := (lower + 9) / 10
+ c, cdigit := central/10, central%10
+ u := upper / 10
+ if l > u {
+ // don't trim the last digit as it is forbidden to go below l
+ // other, trim and exit now.
+ break
+ }
+ // Check that we didn't cross the lower boundary.
+ // The case where l < u but c == l-1 is essentially impossible,
+ // but may happen if:
+ // lower = ..11
+ // central = ..19
+ // upper = ..31
+ // and means that 'central' is very close but less than
+ // an integer ending with many zeros, and usually
+ // the "round-up" logic hides the problem.
+ if l == c+1 && c < u {
+ c++
+ cdigit = 0
+ cup = false
+ }
+ trimmed++
+ // Remember trimmed digits of c
+ c0 = c0 && cNextDigit == 0
+ cNextDigit = int(cdigit)
+ lower, central, upper = l, c, u
+ }
+ // should we round up?
+ if trimmed > 0 {
+ cup = cNextDigit > 5 ||
+ (cNextDigit == 5 && !c0) ||
+ (cNextDigit == 5 && c0 && central&1 == 1)
+ }
+ if central < upper && cup {
+ central++
+ }
+ // We know where the number ends, fill directly
+ endindex -= trimmed
+ v := central
+ n := endindex
+ for n > d.nd {
+ v1, v2 := v/100, v%100
+ d.d[n] = smallsString[2*v2+1]
+ d.d[n-1] = smallsString[2*v2+0]
+ n -= 2
+ v = v1
+ }
+ if n == d.nd {
+ d.d[n] = byte(v + '0')
+ }
+ d.nd = endindex + 1
+ d.dp = d.nd + trimmed
+}
+
+// mult64bitPow10 takes a floating-point input with a 25-bit
+// mantissa and multiplies it with 10^q. The resulting mantissa
+// is m*P >> 57 where P is a 64-bit element of the detailedPowersOfTen tables.
+// It is typically 31 or 32-bit wide.
+// The returned boolean is true if all trimmed bits were zero.
+//
+// That is:
+//
+// m*2^e2 * round(10^q) = resM * 2^resE + ε
+// exact = ε == 0
+func mult64bitPow10(m uint32, e2, q int) (resM uint32, resE int, exact bool) {
+ if q == 0 {
+ // P == 1<<63
+ return m << 6, e2 - 6, true
+ }
+ if q < detailedPowersOfTenMinExp10 || detailedPowersOfTenMaxExp10 < q {
+ // This never happens due to the range of float32/float64 exponent
+ panic("mult64bitPow10: power of 10 is out of range")
+ }
+ pow := detailedPowersOfTen[q-detailedPowersOfTenMinExp10][1]
+ if q < 0 {
+ // Inverse powers of ten must be rounded up.
+ pow += 1
+ }
+ hi, lo := bits.Mul64(uint64(m), pow)
+ e2 += mulByLog10Log2(q) - 63 + 57
+ return uint32(hi<<7 | lo>>57), e2, lo<<7 == 0
+}
+
+// mult128bitPow10 takes a floating-point input with a 55-bit
+// mantissa and multiplies it with 10^q. The resulting mantissa
+// is m*P >> 119 where P is a 128-bit element of the detailedPowersOfTen tables.
+// It is typically 63 or 64-bit wide.
+// The returned boolean is true is all trimmed bits were zero.
+//
+// That is:
+//
+// m*2^e2 * round(10^q) = resM * 2^resE + ε
+// exact = ε == 0
+func mult128bitPow10(m uint64, e2, q int) (resM uint64, resE int, exact bool) {
+ if q == 0 {
+ // P == 1<<127
+ return m << 8, e2 - 8, true
+ }
+ if q < detailedPowersOfTenMinExp10 || detailedPowersOfTenMaxExp10 < q {
+ // This never happens due to the range of float32/float64 exponent
+ panic("mult128bitPow10: power of 10 is out of range")
+ }
+ pow := detailedPowersOfTen[q-detailedPowersOfTenMinExp10]
+ if q < 0 {
+ // Inverse powers of ten must be rounded up.
+ pow[0] += 1
+ }
+ e2 += mulByLog10Log2(q) - 127 + 119
+
+ // long multiplication
+ l1, l0 := bits.Mul64(m, pow[0])
+ h1, h0 := bits.Mul64(m, pow[1])
+ mid, carry := bits.Add64(l1, h0, 0)
+ h1 += carry
+ return h1<<9 | mid>>55, e2, mid<<9 == 0 && l0 == 0
+}
+
+func divisibleByPower5(m uint64, k int) bool {
+ if m == 0 {
+ return true
+ }
+ for i := 0; i < k; i++ {
+ if m%5 != 0 {
+ return false
+ }
+ m /= 5
+ }
+ return true
+}
+
+// divmod1e9 computes quotient and remainder of division by 1e9,
+// avoiding runtime uint64 division on 32-bit platforms.
+func divmod1e9(x uint64) (uint32, uint32) {
+ if !host32bit {
+ return uint32(x / 1e9), uint32(x % 1e9)
+ }
+ // Use the same sequence of operations as the amd64 compiler.
+ hi, _ := bits.Mul64(x>>1, 0x89705f4136b4a598) // binary digits of 1e-9
+ q := hi >> 28
+ return uint32(q), uint32(x - q*1e9)
+}
diff --git a/gnovm/stdlibs/strconv/ftoaryu_test.gno b/gnovm/stdlibs/strconv/ftoaryu_test.gno
new file mode 100644
index 00000000000..bd969e8e997
--- /dev/null
+++ b/gnovm/stdlibs/strconv/ftoaryu_test.gno
@@ -0,0 +1,30 @@
+// Copyright 2021 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.
+
+package strconv
+
+import (
+ "math"
+ "testing"
+)
+
+func TestMulByLog2Log10(t *testing.T) {
+ for x := -1600; x <= +1600; x++ {
+ iMath := MulByLog2Log10(x)
+ fMath := int(math.Floor(float64(x) * math.Ln2 / math.Ln10))
+ if iMath != fMath {
+ t.Errorf("mulByLog2Log10(%d) failed: %d vs %d\n", x, iMath, fMath)
+ }
+ }
+}
+
+func TestMulByLog10Log2(t *testing.T) {
+ for x := -500; x <= +500; x++ {
+ iMath := MulByLog10Log2(x)
+ fMath := int(math.Floor(float64(x) * math.Ln10 / math.Ln2))
+ if iMath != fMath {
+ t.Errorf("mulByLog10Log2(%d) failed: %d vs %d\n", x, iMath, fMath)
+ }
+ }
+}
diff --git a/gnovm/stdlibs/strconv/internal_test.gno b/gnovm/stdlibs/strconv/internal_test.gno
new file mode 100644
index 00000000000..f2cceff20eb
--- /dev/null
+++ b/gnovm/stdlibs/strconv/internal_test.gno
@@ -0,0 +1,31 @@
+// Copyright 2009 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.
+
+// export access to strconv internals for tests
+
+package strconv
+
+func NewDecimal(i uint64) *decimal {
+ d := new(decimal)
+ d.Assign(i)
+ return d
+}
+
+func SetOptimize(b bool) bool {
+ old := optimize
+ optimize = b
+ return old
+}
+
+func ParseFloatPrefix(s string, bitSize int) (float64, int, error) {
+ return parseFloatPrefix(s, bitSize)
+}
+
+func MulByLog2Log10(x int) int {
+ return mulByLog2Log10(x)
+}
+
+func MulByLog10Log2(x int) int {
+ return mulByLog10Log2(x)
+}
diff --git a/gnovm/stdlibs/strconv/isprint.gno b/gnovm/stdlibs/strconv/isprint.gno
new file mode 100644
index 00000000000..baa14a65bd6
--- /dev/null
+++ b/gnovm/stdlibs/strconv/isprint.gno
@@ -0,0 +1,752 @@
+// Copyright 2013 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.
+
+// Code generated by go run makeisprint.go -output isprint.go; DO NOT EDIT.
+
+package strconv
+
+// (424+133+112)*2 + (508)*4 = 3370 bytes
+
+var isPrint16 = []uint16{
+ 0x0020, 0x007e,
+ 0x00a1, 0x0377,
+ 0x037a, 0x037f,
+ 0x0384, 0x0556,
+ 0x0559, 0x058a,
+ 0x058d, 0x05c7,
+ 0x05d0, 0x05ea,
+ 0x05ef, 0x05f4,
+ 0x0606, 0x070d,
+ 0x0710, 0x074a,
+ 0x074d, 0x07b1,
+ 0x07c0, 0x07fa,
+ 0x07fd, 0x082d,
+ 0x0830, 0x085b,
+ 0x085e, 0x086a,
+ 0x0870, 0x088e,
+ 0x0898, 0x098c,
+ 0x098f, 0x0990,
+ 0x0993, 0x09b2,
+ 0x09b6, 0x09b9,
+ 0x09bc, 0x09c4,
+ 0x09c7, 0x09c8,
+ 0x09cb, 0x09ce,
+ 0x09d7, 0x09d7,
+ 0x09dc, 0x09e3,
+ 0x09e6, 0x09fe,
+ 0x0a01, 0x0a0a,
+ 0x0a0f, 0x0a10,
+ 0x0a13, 0x0a39,
+ 0x0a3c, 0x0a42,
+ 0x0a47, 0x0a48,
+ 0x0a4b, 0x0a4d,
+ 0x0a51, 0x0a51,
+ 0x0a59, 0x0a5e,
+ 0x0a66, 0x0a76,
+ 0x0a81, 0x0ab9,
+ 0x0abc, 0x0acd,
+ 0x0ad0, 0x0ad0,
+ 0x0ae0, 0x0ae3,
+ 0x0ae6, 0x0af1,
+ 0x0af9, 0x0b0c,
+ 0x0b0f, 0x0b10,
+ 0x0b13, 0x0b39,
+ 0x0b3c, 0x0b44,
+ 0x0b47, 0x0b48,
+ 0x0b4b, 0x0b4d,
+ 0x0b55, 0x0b57,
+ 0x0b5c, 0x0b63,
+ 0x0b66, 0x0b77,
+ 0x0b82, 0x0b8a,
+ 0x0b8e, 0x0b95,
+ 0x0b99, 0x0b9f,
+ 0x0ba3, 0x0ba4,
+ 0x0ba8, 0x0baa,
+ 0x0bae, 0x0bb9,
+ 0x0bbe, 0x0bc2,
+ 0x0bc6, 0x0bcd,
+ 0x0bd0, 0x0bd0,
+ 0x0bd7, 0x0bd7,
+ 0x0be6, 0x0bfa,
+ 0x0c00, 0x0c39,
+ 0x0c3c, 0x0c4d,
+ 0x0c55, 0x0c5a,
+ 0x0c5d, 0x0c5d,
+ 0x0c60, 0x0c63,
+ 0x0c66, 0x0c6f,
+ 0x0c77, 0x0cb9,
+ 0x0cbc, 0x0ccd,
+ 0x0cd5, 0x0cd6,
+ 0x0cdd, 0x0ce3,
+ 0x0ce6, 0x0cf3,
+ 0x0d00, 0x0d4f,
+ 0x0d54, 0x0d63,
+ 0x0d66, 0x0d96,
+ 0x0d9a, 0x0dbd,
+ 0x0dc0, 0x0dc6,
+ 0x0dca, 0x0dca,
+ 0x0dcf, 0x0ddf,
+ 0x0de6, 0x0def,
+ 0x0df2, 0x0df4,
+ 0x0e01, 0x0e3a,
+ 0x0e3f, 0x0e5b,
+ 0x0e81, 0x0ebd,
+ 0x0ec0, 0x0ed9,
+ 0x0edc, 0x0edf,
+ 0x0f00, 0x0f6c,
+ 0x0f71, 0x0fda,
+ 0x1000, 0x10c7,
+ 0x10cd, 0x10cd,
+ 0x10d0, 0x124d,
+ 0x1250, 0x125d,
+ 0x1260, 0x128d,
+ 0x1290, 0x12b5,
+ 0x12b8, 0x12c5,
+ 0x12c8, 0x1315,
+ 0x1318, 0x135a,
+ 0x135d, 0x137c,
+ 0x1380, 0x1399,
+ 0x13a0, 0x13f5,
+ 0x13f8, 0x13fd,
+ 0x1400, 0x169c,
+ 0x16a0, 0x16f8,
+ 0x1700, 0x1715,
+ 0x171f, 0x1736,
+ 0x1740, 0x1753,
+ 0x1760, 0x1773,
+ 0x1780, 0x17dd,
+ 0x17e0, 0x17e9,
+ 0x17f0, 0x17f9,
+ 0x1800, 0x1819,
+ 0x1820, 0x1878,
+ 0x1880, 0x18aa,
+ 0x18b0, 0x18f5,
+ 0x1900, 0x192b,
+ 0x1930, 0x193b,
+ 0x1940, 0x1940,
+ 0x1944, 0x196d,
+ 0x1970, 0x1974,
+ 0x1980, 0x19ab,
+ 0x19b0, 0x19c9,
+ 0x19d0, 0x19da,
+ 0x19de, 0x1a1b,
+ 0x1a1e, 0x1a7c,
+ 0x1a7f, 0x1a89,
+ 0x1a90, 0x1a99,
+ 0x1aa0, 0x1aad,
+ 0x1ab0, 0x1ace,
+ 0x1b00, 0x1b4c,
+ 0x1b50, 0x1bf3,
+ 0x1bfc, 0x1c37,
+ 0x1c3b, 0x1c49,
+ 0x1c4d, 0x1c88,
+ 0x1c90, 0x1cba,
+ 0x1cbd, 0x1cc7,
+ 0x1cd0, 0x1cfa,
+ 0x1d00, 0x1f15,
+ 0x1f18, 0x1f1d,
+ 0x1f20, 0x1f45,
+ 0x1f48, 0x1f4d,
+ 0x1f50, 0x1f7d,
+ 0x1f80, 0x1fd3,
+ 0x1fd6, 0x1fef,
+ 0x1ff2, 0x1ffe,
+ 0x2010, 0x2027,
+ 0x2030, 0x205e,
+ 0x2070, 0x2071,
+ 0x2074, 0x209c,
+ 0x20a0, 0x20c0,
+ 0x20d0, 0x20f0,
+ 0x2100, 0x218b,
+ 0x2190, 0x2426,
+ 0x2440, 0x244a,
+ 0x2460, 0x2b73,
+ 0x2b76, 0x2cf3,
+ 0x2cf9, 0x2d27,
+ 0x2d2d, 0x2d2d,
+ 0x2d30, 0x2d67,
+ 0x2d6f, 0x2d70,
+ 0x2d7f, 0x2d96,
+ 0x2da0, 0x2e5d,
+ 0x2e80, 0x2ef3,
+ 0x2f00, 0x2fd5,
+ 0x2ff0, 0x2ffb,
+ 0x3001, 0x3096,
+ 0x3099, 0x30ff,
+ 0x3105, 0x31e3,
+ 0x31f0, 0xa48c,
+ 0xa490, 0xa4c6,
+ 0xa4d0, 0xa62b,
+ 0xa640, 0xa6f7,
+ 0xa700, 0xa7ca,
+ 0xa7d0, 0xa7d9,
+ 0xa7f2, 0xa82c,
+ 0xa830, 0xa839,
+ 0xa840, 0xa877,
+ 0xa880, 0xa8c5,
+ 0xa8ce, 0xa8d9,
+ 0xa8e0, 0xa953,
+ 0xa95f, 0xa97c,
+ 0xa980, 0xa9d9,
+ 0xa9de, 0xaa36,
+ 0xaa40, 0xaa4d,
+ 0xaa50, 0xaa59,
+ 0xaa5c, 0xaac2,
+ 0xaadb, 0xaaf6,
+ 0xab01, 0xab06,
+ 0xab09, 0xab0e,
+ 0xab11, 0xab16,
+ 0xab20, 0xab6b,
+ 0xab70, 0xabed,
+ 0xabf0, 0xabf9,
+ 0xac00, 0xd7a3,
+ 0xd7b0, 0xd7c6,
+ 0xd7cb, 0xd7fb,
+ 0xf900, 0xfa6d,
+ 0xfa70, 0xfad9,
+ 0xfb00, 0xfb06,
+ 0xfb13, 0xfb17,
+ 0xfb1d, 0xfbc2,
+ 0xfbd3, 0xfd8f,
+ 0xfd92, 0xfdc7,
+ 0xfdcf, 0xfdcf,
+ 0xfdf0, 0xfe19,
+ 0xfe20, 0xfe6b,
+ 0xfe70, 0xfefc,
+ 0xff01, 0xffbe,
+ 0xffc2, 0xffc7,
+ 0xffca, 0xffcf,
+ 0xffd2, 0xffd7,
+ 0xffda, 0xffdc,
+ 0xffe0, 0xffee,
+ 0xfffc, 0xfffd,
+}
+
+var isNotPrint16 = []uint16{
+ 0x00ad,
+ 0x038b,
+ 0x038d,
+ 0x03a2,
+ 0x0530,
+ 0x0590,
+ 0x061c,
+ 0x06dd,
+ 0x083f,
+ 0x085f,
+ 0x08e2,
+ 0x0984,
+ 0x09a9,
+ 0x09b1,
+ 0x09de,
+ 0x0a04,
+ 0x0a29,
+ 0x0a31,
+ 0x0a34,
+ 0x0a37,
+ 0x0a3d,
+ 0x0a5d,
+ 0x0a84,
+ 0x0a8e,
+ 0x0a92,
+ 0x0aa9,
+ 0x0ab1,
+ 0x0ab4,
+ 0x0ac6,
+ 0x0aca,
+ 0x0b00,
+ 0x0b04,
+ 0x0b29,
+ 0x0b31,
+ 0x0b34,
+ 0x0b5e,
+ 0x0b84,
+ 0x0b91,
+ 0x0b9b,
+ 0x0b9d,
+ 0x0bc9,
+ 0x0c0d,
+ 0x0c11,
+ 0x0c29,
+ 0x0c45,
+ 0x0c49,
+ 0x0c57,
+ 0x0c8d,
+ 0x0c91,
+ 0x0ca9,
+ 0x0cb4,
+ 0x0cc5,
+ 0x0cc9,
+ 0x0cdf,
+ 0x0cf0,
+ 0x0d0d,
+ 0x0d11,
+ 0x0d45,
+ 0x0d49,
+ 0x0d80,
+ 0x0d84,
+ 0x0db2,
+ 0x0dbc,
+ 0x0dd5,
+ 0x0dd7,
+ 0x0e83,
+ 0x0e85,
+ 0x0e8b,
+ 0x0ea4,
+ 0x0ea6,
+ 0x0ec5,
+ 0x0ec7,
+ 0x0ecf,
+ 0x0f48,
+ 0x0f98,
+ 0x0fbd,
+ 0x0fcd,
+ 0x10c6,
+ 0x1249,
+ 0x1257,
+ 0x1259,
+ 0x1289,
+ 0x12b1,
+ 0x12bf,
+ 0x12c1,
+ 0x12d7,
+ 0x1311,
+ 0x1680,
+ 0x176d,
+ 0x1771,
+ 0x180e,
+ 0x191f,
+ 0x1a5f,
+ 0x1b7f,
+ 0x1f58,
+ 0x1f5a,
+ 0x1f5c,
+ 0x1f5e,
+ 0x1fb5,
+ 0x1fc5,
+ 0x1fdc,
+ 0x1ff5,
+ 0x208f,
+ 0x2b96,
+ 0x2d26,
+ 0x2da7,
+ 0x2daf,
+ 0x2db7,
+ 0x2dbf,
+ 0x2dc7,
+ 0x2dcf,
+ 0x2dd7,
+ 0x2ddf,
+ 0x2e9a,
+ 0x3040,
+ 0x3130,
+ 0x318f,
+ 0x321f,
+ 0xa7d2,
+ 0xa7d4,
+ 0xa9ce,
+ 0xa9ff,
+ 0xab27,
+ 0xab2f,
+ 0xfb37,
+ 0xfb3d,
+ 0xfb3f,
+ 0xfb42,
+ 0xfb45,
+ 0xfe53,
+ 0xfe67,
+ 0xfe75,
+ 0xffe7,
+}
+
+var isPrint32 = []uint32{
+ 0x010000, 0x01004d,
+ 0x010050, 0x01005d,
+ 0x010080, 0x0100fa,
+ 0x010100, 0x010102,
+ 0x010107, 0x010133,
+ 0x010137, 0x01019c,
+ 0x0101a0, 0x0101a0,
+ 0x0101d0, 0x0101fd,
+ 0x010280, 0x01029c,
+ 0x0102a0, 0x0102d0,
+ 0x0102e0, 0x0102fb,
+ 0x010300, 0x010323,
+ 0x01032d, 0x01034a,
+ 0x010350, 0x01037a,
+ 0x010380, 0x0103c3,
+ 0x0103c8, 0x0103d5,
+ 0x010400, 0x01049d,
+ 0x0104a0, 0x0104a9,
+ 0x0104b0, 0x0104d3,
+ 0x0104d8, 0x0104fb,
+ 0x010500, 0x010527,
+ 0x010530, 0x010563,
+ 0x01056f, 0x0105bc,
+ 0x010600, 0x010736,
+ 0x010740, 0x010755,
+ 0x010760, 0x010767,
+ 0x010780, 0x0107ba,
+ 0x010800, 0x010805,
+ 0x010808, 0x010838,
+ 0x01083c, 0x01083c,
+ 0x01083f, 0x01089e,
+ 0x0108a7, 0x0108af,
+ 0x0108e0, 0x0108f5,
+ 0x0108fb, 0x01091b,
+ 0x01091f, 0x010939,
+ 0x01093f, 0x01093f,
+ 0x010980, 0x0109b7,
+ 0x0109bc, 0x0109cf,
+ 0x0109d2, 0x010a06,
+ 0x010a0c, 0x010a35,
+ 0x010a38, 0x010a3a,
+ 0x010a3f, 0x010a48,
+ 0x010a50, 0x010a58,
+ 0x010a60, 0x010a9f,
+ 0x010ac0, 0x010ae6,
+ 0x010aeb, 0x010af6,
+ 0x010b00, 0x010b35,
+ 0x010b39, 0x010b55,
+ 0x010b58, 0x010b72,
+ 0x010b78, 0x010b91,
+ 0x010b99, 0x010b9c,
+ 0x010ba9, 0x010baf,
+ 0x010c00, 0x010c48,
+ 0x010c80, 0x010cb2,
+ 0x010cc0, 0x010cf2,
+ 0x010cfa, 0x010d27,
+ 0x010d30, 0x010d39,
+ 0x010e60, 0x010ead,
+ 0x010eb0, 0x010eb1,
+ 0x010efd, 0x010f27,
+ 0x010f30, 0x010f59,
+ 0x010f70, 0x010f89,
+ 0x010fb0, 0x010fcb,
+ 0x010fe0, 0x010ff6,
+ 0x011000, 0x01104d,
+ 0x011052, 0x011075,
+ 0x01107f, 0x0110c2,
+ 0x0110d0, 0x0110e8,
+ 0x0110f0, 0x0110f9,
+ 0x011100, 0x011147,
+ 0x011150, 0x011176,
+ 0x011180, 0x0111f4,
+ 0x011200, 0x011241,
+ 0x011280, 0x0112a9,
+ 0x0112b0, 0x0112ea,
+ 0x0112f0, 0x0112f9,
+ 0x011300, 0x01130c,
+ 0x01130f, 0x011310,
+ 0x011313, 0x011344,
+ 0x011347, 0x011348,
+ 0x01134b, 0x01134d,
+ 0x011350, 0x011350,
+ 0x011357, 0x011357,
+ 0x01135d, 0x011363,
+ 0x011366, 0x01136c,
+ 0x011370, 0x011374,
+ 0x011400, 0x011461,
+ 0x011480, 0x0114c7,
+ 0x0114d0, 0x0114d9,
+ 0x011580, 0x0115b5,
+ 0x0115b8, 0x0115dd,
+ 0x011600, 0x011644,
+ 0x011650, 0x011659,
+ 0x011660, 0x01166c,
+ 0x011680, 0x0116b9,
+ 0x0116c0, 0x0116c9,
+ 0x011700, 0x01171a,
+ 0x01171d, 0x01172b,
+ 0x011730, 0x011746,
+ 0x011800, 0x01183b,
+ 0x0118a0, 0x0118f2,
+ 0x0118ff, 0x011906,
+ 0x011909, 0x011909,
+ 0x01190c, 0x011938,
+ 0x01193b, 0x011946,
+ 0x011950, 0x011959,
+ 0x0119a0, 0x0119a7,
+ 0x0119aa, 0x0119d7,
+ 0x0119da, 0x0119e4,
+ 0x011a00, 0x011a47,
+ 0x011a50, 0x011aa2,
+ 0x011ab0, 0x011af8,
+ 0x011b00, 0x011b09,
+ 0x011c00, 0x011c45,
+ 0x011c50, 0x011c6c,
+ 0x011c70, 0x011c8f,
+ 0x011c92, 0x011cb6,
+ 0x011d00, 0x011d36,
+ 0x011d3a, 0x011d47,
+ 0x011d50, 0x011d59,
+ 0x011d60, 0x011d98,
+ 0x011da0, 0x011da9,
+ 0x011ee0, 0x011ef8,
+ 0x011f00, 0x011f3a,
+ 0x011f3e, 0x011f59,
+ 0x011fb0, 0x011fb0,
+ 0x011fc0, 0x011ff1,
+ 0x011fff, 0x012399,
+ 0x012400, 0x012474,
+ 0x012480, 0x012543,
+ 0x012f90, 0x012ff2,
+ 0x013000, 0x01342f,
+ 0x013440, 0x013455,
+ 0x014400, 0x014646,
+ 0x016800, 0x016a38,
+ 0x016a40, 0x016a69,
+ 0x016a6e, 0x016ac9,
+ 0x016ad0, 0x016aed,
+ 0x016af0, 0x016af5,
+ 0x016b00, 0x016b45,
+ 0x016b50, 0x016b77,
+ 0x016b7d, 0x016b8f,
+ 0x016e40, 0x016e9a,
+ 0x016f00, 0x016f4a,
+ 0x016f4f, 0x016f87,
+ 0x016f8f, 0x016f9f,
+ 0x016fe0, 0x016fe4,
+ 0x016ff0, 0x016ff1,
+ 0x017000, 0x0187f7,
+ 0x018800, 0x018cd5,
+ 0x018d00, 0x018d08,
+ 0x01aff0, 0x01b122,
+ 0x01b132, 0x01b132,
+ 0x01b150, 0x01b152,
+ 0x01b155, 0x01b155,
+ 0x01b164, 0x01b167,
+ 0x01b170, 0x01b2fb,
+ 0x01bc00, 0x01bc6a,
+ 0x01bc70, 0x01bc7c,
+ 0x01bc80, 0x01bc88,
+ 0x01bc90, 0x01bc99,
+ 0x01bc9c, 0x01bc9f,
+ 0x01cf00, 0x01cf2d,
+ 0x01cf30, 0x01cf46,
+ 0x01cf50, 0x01cfc3,
+ 0x01d000, 0x01d0f5,
+ 0x01d100, 0x01d126,
+ 0x01d129, 0x01d172,
+ 0x01d17b, 0x01d1ea,
+ 0x01d200, 0x01d245,
+ 0x01d2c0, 0x01d2d3,
+ 0x01d2e0, 0x01d2f3,
+ 0x01d300, 0x01d356,
+ 0x01d360, 0x01d378,
+ 0x01d400, 0x01d49f,
+ 0x01d4a2, 0x01d4a2,
+ 0x01d4a5, 0x01d4a6,
+ 0x01d4a9, 0x01d50a,
+ 0x01d50d, 0x01d546,
+ 0x01d54a, 0x01d6a5,
+ 0x01d6a8, 0x01d7cb,
+ 0x01d7ce, 0x01da8b,
+ 0x01da9b, 0x01daaf,
+ 0x01df00, 0x01df1e,
+ 0x01df25, 0x01df2a,
+ 0x01e000, 0x01e018,
+ 0x01e01b, 0x01e02a,
+ 0x01e030, 0x01e06d,
+ 0x01e08f, 0x01e08f,
+ 0x01e100, 0x01e12c,
+ 0x01e130, 0x01e13d,
+ 0x01e140, 0x01e149,
+ 0x01e14e, 0x01e14f,
+ 0x01e290, 0x01e2ae,
+ 0x01e2c0, 0x01e2f9,
+ 0x01e2ff, 0x01e2ff,
+ 0x01e4d0, 0x01e4f9,
+ 0x01e7e0, 0x01e8c4,
+ 0x01e8c7, 0x01e8d6,
+ 0x01e900, 0x01e94b,
+ 0x01e950, 0x01e959,
+ 0x01e95e, 0x01e95f,
+ 0x01ec71, 0x01ecb4,
+ 0x01ed01, 0x01ed3d,
+ 0x01ee00, 0x01ee24,
+ 0x01ee27, 0x01ee3b,
+ 0x01ee42, 0x01ee42,
+ 0x01ee47, 0x01ee54,
+ 0x01ee57, 0x01ee64,
+ 0x01ee67, 0x01ee9b,
+ 0x01eea1, 0x01eebb,
+ 0x01eef0, 0x01eef1,
+ 0x01f000, 0x01f02b,
+ 0x01f030, 0x01f093,
+ 0x01f0a0, 0x01f0ae,
+ 0x01f0b1, 0x01f0f5,
+ 0x01f100, 0x01f1ad,
+ 0x01f1e6, 0x01f202,
+ 0x01f210, 0x01f23b,
+ 0x01f240, 0x01f248,
+ 0x01f250, 0x01f251,
+ 0x01f260, 0x01f265,
+ 0x01f300, 0x01f6d7,
+ 0x01f6dc, 0x01f6ec,
+ 0x01f6f0, 0x01f6fc,
+ 0x01f700, 0x01f776,
+ 0x01f77b, 0x01f7d9,
+ 0x01f7e0, 0x01f7eb,
+ 0x01f7f0, 0x01f7f0,
+ 0x01f800, 0x01f80b,
+ 0x01f810, 0x01f847,
+ 0x01f850, 0x01f859,
+ 0x01f860, 0x01f887,
+ 0x01f890, 0x01f8ad,
+ 0x01f8b0, 0x01f8b1,
+ 0x01f900, 0x01fa53,
+ 0x01fa60, 0x01fa6d,
+ 0x01fa70, 0x01fa7c,
+ 0x01fa80, 0x01fa88,
+ 0x01fa90, 0x01fac5,
+ 0x01face, 0x01fadb,
+ 0x01fae0, 0x01fae8,
+ 0x01faf0, 0x01faf8,
+ 0x01fb00, 0x01fbca,
+ 0x01fbf0, 0x01fbf9,
+ 0x020000, 0x02a6df,
+ 0x02a700, 0x02b739,
+ 0x02b740, 0x02b81d,
+ 0x02b820, 0x02cea1,
+ 0x02ceb0, 0x02ebe0,
+ 0x02f800, 0x02fa1d,
+ 0x030000, 0x03134a,
+ 0x031350, 0x0323af,
+ 0x0e0100, 0x0e01ef,
+}
+
+var isNotPrint32 = []uint16{ // add 0x10000 to each entry
+ 0x000c,
+ 0x0027,
+ 0x003b,
+ 0x003e,
+ 0x018f,
+ 0x039e,
+ 0x057b,
+ 0x058b,
+ 0x0593,
+ 0x0596,
+ 0x05a2,
+ 0x05b2,
+ 0x05ba,
+ 0x0786,
+ 0x07b1,
+ 0x0809,
+ 0x0836,
+ 0x0856,
+ 0x08f3,
+ 0x0a04,
+ 0x0a14,
+ 0x0a18,
+ 0x0e7f,
+ 0x0eaa,
+ 0x10bd,
+ 0x1135,
+ 0x11e0,
+ 0x1212,
+ 0x1287,
+ 0x1289,
+ 0x128e,
+ 0x129e,
+ 0x1304,
+ 0x1329,
+ 0x1331,
+ 0x1334,
+ 0x133a,
+ 0x145c,
+ 0x1914,
+ 0x1917,
+ 0x1936,
+ 0x1c09,
+ 0x1c37,
+ 0x1ca8,
+ 0x1d07,
+ 0x1d0a,
+ 0x1d3b,
+ 0x1d3e,
+ 0x1d66,
+ 0x1d69,
+ 0x1d8f,
+ 0x1d92,
+ 0x1f11,
+ 0x246f,
+ 0x6a5f,
+ 0x6abf,
+ 0x6b5a,
+ 0x6b62,
+ 0xaff4,
+ 0xaffc,
+ 0xafff,
+ 0xd455,
+ 0xd49d,
+ 0xd4ad,
+ 0xd4ba,
+ 0xd4bc,
+ 0xd4c4,
+ 0xd506,
+ 0xd515,
+ 0xd51d,
+ 0xd53a,
+ 0xd53f,
+ 0xd545,
+ 0xd551,
+ 0xdaa0,
+ 0xe007,
+ 0xe022,
+ 0xe025,
+ 0xe7e7,
+ 0xe7ec,
+ 0xe7ef,
+ 0xe7ff,
+ 0xee04,
+ 0xee20,
+ 0xee23,
+ 0xee28,
+ 0xee33,
+ 0xee38,
+ 0xee3a,
+ 0xee48,
+ 0xee4a,
+ 0xee4c,
+ 0xee50,
+ 0xee53,
+ 0xee58,
+ 0xee5a,
+ 0xee5c,
+ 0xee5e,
+ 0xee60,
+ 0xee63,
+ 0xee6b,
+ 0xee73,
+ 0xee78,
+ 0xee7d,
+ 0xee7f,
+ 0xee8a,
+ 0xeea4,
+ 0xeeaa,
+ 0xf0c0,
+ 0xf0d0,
+ 0xfabe,
+ 0xfb93,
+}
+
+// isGraphic lists the graphic runes not matched by IsPrint.
+var isGraphic = []uint16{
+ 0x00a0,
+ 0x1680,
+ 0x2000,
+ 0x2001,
+ 0x2002,
+ 0x2003,
+ 0x2004,
+ 0x2005,
+ 0x2006,
+ 0x2007,
+ 0x2008,
+ 0x2009,
+ 0x200a,
+ 0x202f,
+ 0x205f,
+ 0x3000,
+}
diff --git a/gnovm/stdlibs/strconv/itoa.gno b/gnovm/stdlibs/strconv/itoa.gno
new file mode 100644
index 00000000000..b0c2666e7cb
--- /dev/null
+++ b/gnovm/stdlibs/strconv/itoa.gno
@@ -0,0 +1,205 @@
+// Copyright 2009 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.
+
+package strconv
+
+import "math/bits"
+
+const fastSmalls = true // enable fast path for small integers
+
+// FormatUint returns the string representation of i in the given base,
+// for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z'
+// for digit values >= 10.
+func FormatUint(i uint64, base int) string {
+ if fastSmalls && i < nSmalls && base == 10 {
+ return small(int(i))
+ }
+ _, s := formatBits(nil, i, base, false, false)
+ return s
+}
+
+// FormatInt returns the string representation of i in the given base,
+// for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z'
+// for digit values >= 10.
+func FormatInt(i int64, base int) string {
+ if fastSmalls && 0 <= i && i < nSmalls && base == 10 {
+ return small(int(i))
+ }
+ _, s := formatBits(nil, uint64(i), base, i < 0, false)
+ return s
+}
+
+// Itoa is equivalent to FormatInt(int64(i), 10).
+func Itoa(i int) string {
+ return FormatInt(int64(i), 10)
+}
+
+// AppendInt appends the string form of the integer i,
+// as generated by FormatInt, to dst and returns the extended buffer.
+func AppendInt(dst []byte, i int64, base int) []byte {
+ if fastSmalls && 0 <= i && i < nSmalls && base == 10 {
+ return append(dst, small(int(i))...)
+ }
+ dst, _ = formatBits(dst, uint64(i), base, i < 0, true)
+ return dst
+}
+
+// AppendUint appends the string form of the unsigned integer i,
+// as generated by FormatUint, to dst and returns the extended buffer.
+func AppendUint(dst []byte, i uint64, base int) []byte {
+ if fastSmalls && i < nSmalls && base == 10 {
+ return append(dst, small(int(i))...)
+ }
+ dst, _ = formatBits(dst, i, base, false, true)
+ return dst
+}
+
+// small returns the string for an i with 0 <= i < nSmalls.
+func small(i int) string {
+ if i < 10 {
+ return digits[i : i+1]
+ }
+ return smallsString[i*2 : i*2+2]
+}
+
+const nSmalls = 100
+
+const smallsString = "00010203040506070809" +
+ "10111213141516171819" +
+ "20212223242526272829" +
+ "30313233343536373839" +
+ "40414243444546474849" +
+ "50515253545556575859" +
+ "60616263646566676869" +
+ "70717273747576777879" +
+ "80818283848586878889" +
+ "90919293949596979899"
+
+const host32bit = ^uint(0)>>32 == 0
+
+const digits = "0123456789abcdefghijklmnopqrstuvwxyz"
+
+// formatBits computes the string representation of u in the given base.
+// If neg is set, u is treated as negative int64 value. If append_ is
+// set, the string is appended to dst and the resulting byte slice is
+// returned as the first result value; otherwise the string is returned
+// as the second result value.
+func formatBits(dst []byte, u uint64, base int, neg, append_ bool) (d []byte, s string) {
+ if base < 2 || base > len(digits) {
+ panic("strconv: illegal AppendInt/FormatInt base")
+ }
+ // 2 <= base && base <= len(digits)
+
+ var a [64 + 1]byte // +1 for sign of 64bit value in base 2
+ i := len(a)
+
+ if neg {
+ u = -u
+ }
+
+ // convert bits
+ // We use uint values where we can because those will
+ // fit into a single register even on a 32bit machine.
+ if base == 10 {
+ // common case: use constants for / because
+ // the compiler can optimize it into a multiply+shift
+
+ if host32bit {
+ // convert the lower digits using 32bit operations
+ for u >= 1e9 {
+ // Avoid using r = a%b in addition to q = a/b
+ // since 64bit division and modulo operations
+ // are calculated by runtime functions on 32bit machines.
+ q := u / 1e9
+ us := uint(u - q*1e9) // u % 1e9 fits into a uint
+ for j := 4; j > 0; j-- {
+ is := us % 100 * 2
+ us /= 100
+ i -= 2
+ a[i+1] = smallsString[is+1]
+ a[i+0] = smallsString[is+0]
+ }
+
+ // us < 10, since it contains the last digit
+ // from the initial 9-digit us.
+ i--
+ a[i] = smallsString[us*2+1]
+
+ u = q
+ }
+ // u < 1e9
+ }
+
+ // u guaranteed to fit into a uint
+ us := uint(u)
+ for us >= 100 {
+ is := us % 100 * 2
+ us /= 100
+ i -= 2
+ a[i+1] = smallsString[is+1]
+ a[i+0] = smallsString[is+0]
+ }
+
+ // us < 100
+ is := us * 2
+ i--
+ a[i] = smallsString[is+1]
+ if us >= 10 {
+ i--
+ a[i] = smallsString[is]
+ }
+
+ } else if isPowerOfTwo(base) {
+ // Use shifts and masks instead of / and %.
+ // Base is a power of 2 and 2 <= base <= len(digits) where len(digits) is 36.
+ // The largest power of 2 below or equal to 36 is 32, which is 1 << 5;
+ // i.e., the largest possible shift count is 5. By &-ind that value with
+ // the constant 7 we tell the compiler that the shift count is always
+ // less than 8 which is smaller than any register width. This allows
+ // the compiler to generate better code for the shift operation.
+ shift := uint(bits.TrailingZeros(uint(base))) & 7
+ b := uint64(base)
+ m := uint(base) - 1 // == 1<= b {
+ i--
+ a[i] = digits[uint(u)&m]
+ u >>= shift
+ }
+ // u < base
+ i--
+ a[i] = digits[uint(u)]
+ } else {
+ // general case
+ b := uint64(base)
+ for u >= b {
+ i--
+ // Avoid using r = a%b in addition to q = a/b
+ // since 64bit division and modulo operations
+ // are calculated by runtime functions on 32bit machines.
+ q := u / b
+ a[i] = digits[uint(u-q*b)]
+ u = q
+ }
+ // u < base
+ i--
+ a[i] = digits[uint(u)]
+ }
+
+ // add sign, if any
+ if neg {
+ i--
+ a[i] = '-'
+ }
+
+ if append_ {
+ d = append(dst, a[i:]...)
+ return
+ }
+ s = string(a[i:])
+ return
+}
+
+func isPowerOfTwo(x int) bool {
+ return x&(x-1) == 0
+}
diff --git a/gnovm/stdlibs/strconv/itoa_test.gno b/gnovm/stdlibs/strconv/itoa_test.gno
new file mode 100644
index 00000000000..b76acc78183
--- /dev/null
+++ b/gnovm/stdlibs/strconv/itoa_test.gno
@@ -0,0 +1,242 @@
+// Copyright 2009 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.
+
+package strconv
+
+import (
+ "testing"
+)
+
+type itob64Test struct {
+ in int64
+ base int
+ out string
+}
+
+var itob64tests = []itob64Test{
+ {0, 10, "0"},
+ {1, 10, "1"},
+ {-1, 10, "-1"},
+ {12345678, 10, "12345678"},
+ {-987654321, 10, "-987654321"},
+ {1<<31 - 1, 10, "2147483647"},
+ {-1<<31 + 1, 10, "-2147483647"},
+ {1 << 31, 10, "2147483648"},
+ {-1 << 31, 10, "-2147483648"},
+ {1<<31 + 1, 10, "2147483649"},
+ {-1<<31 - 1, 10, "-2147483649"},
+ {1<<32 - 1, 10, "4294967295"},
+ {-1<<32 + 1, 10, "-4294967295"},
+ {1 << 32, 10, "4294967296"},
+ {-1 << 32, 10, "-4294967296"},
+ {1<<32 + 1, 10, "4294967297"},
+ {-1<<32 - 1, 10, "-4294967297"},
+ {1 << 50, 10, "1125899906842624"},
+ {1<<63 - 1, 10, "9223372036854775807"},
+ {-1<<63 + 1, 10, "-9223372036854775807"},
+ {-1 << 63, 10, "-9223372036854775808"},
+
+ {0, 2, "0"},
+ {10, 2, "1010"},
+ {-1, 2, "-1"},
+ {1 << 15, 2, "1000000000000000"},
+
+ {-8, 8, "-10"},
+ {057635436545, 8, "57635436545"},
+ {1 << 24, 8, "100000000"},
+
+ {16, 16, "10"},
+ {-0x123456789abcdef, 16, "-123456789abcdef"},
+ {1<<63 - 1, 16, "7fffffffffffffff"},
+ {1<<63 - 1, 2, "111111111111111111111111111111111111111111111111111111111111111"},
+ {-1 << 63, 2, "-1000000000000000000000000000000000000000000000000000000000000000"},
+
+ {16, 17, "g"},
+ {25, 25, "10"},
+ {(((((17*35+24)*35+21)*35+34)*35+12)*35+24)*35 + 32, 35, "holycow"},
+ {(((((17*36+24)*36+21)*36+34)*36+12)*36+24)*36 + 32, 36, "holycow"},
+}
+
+func TestItoa(t *testing.T) {
+ for _, test := range itob64tests {
+ s := FormatInt(test.in, test.base)
+ if s != test.out {
+ t.Errorf("FormatInt(%v, %v) = %v want %v",
+ test.in, test.base, s, test.out)
+ }
+ x := AppendInt([]byte("abc"), test.in, test.base)
+ if string(x) != "abc"+test.out {
+ t.Errorf("AppendInt(%q, %v, %v) = %q want %v",
+ "abc", test.in, test.base, x, test.out)
+ }
+
+ if test.in >= 0 {
+ s := FormatUint(uint64(test.in), test.base)
+ if s != test.out {
+ t.Errorf("FormatUint(%v, %v) = %v want %v",
+ test.in, test.base, s, test.out)
+ }
+ x := AppendUint(nil, uint64(test.in), test.base)
+ if string(x) != test.out {
+ t.Errorf("AppendUint(%q, %v, %v) = %q want %v",
+ "abc", uint64(test.in), test.base, x, test.out)
+ }
+ }
+
+ if test.base == 10 && int64(int(test.in)) == test.in {
+ s := Itoa(int(test.in))
+ if s != test.out {
+ t.Errorf("Itoa(%v) = %v want %v",
+ test.in, s, test.out)
+ }
+ }
+ }
+
+ // Override when base is illegal
+ defer func() {
+ if r := recover(); r == nil {
+ t.Fatalf("expected panic due to illegal base")
+ }
+ }()
+ FormatUint(12345678, 1)
+}
+
+type uitob64Test struct {
+ in uint64
+ base int
+ out string
+}
+
+var uitob64tests = []uitob64Test{
+ {1<<63 - 1, 10, "9223372036854775807"},
+ {1 << 63, 10, "9223372036854775808"},
+ {1<<63 + 1, 10, "9223372036854775809"},
+ {1<<64 - 2, 10, "18446744073709551614"},
+ {1<<64 - 1, 10, "18446744073709551615"},
+ {1<<64 - 1, 2, "1111111111111111111111111111111111111111111111111111111111111111"},
+}
+
+func TestUitoa(t *testing.T) {
+ for _, test := range uitob64tests {
+ s := FormatUint(test.in, test.base)
+ if s != test.out {
+ t.Errorf("FormatUint(%v, %v) = %v want %v",
+ test.in, test.base, s, test.out)
+ }
+ x := AppendUint([]byte("abc"), test.in, test.base)
+ if string(x) != "abc"+test.out {
+ t.Errorf("AppendUint(%q, %v, %v) = %q want %v",
+ "abc", test.in, test.base, x, test.out)
+ }
+
+ }
+}
+
+var varlenUints = []struct {
+ in uint64
+ out string
+}{
+ {1, "1"},
+ {12, "12"},
+ {123, "123"},
+ {1234, "1234"},
+ {12345, "12345"},
+ {123456, "123456"},
+ {1234567, "1234567"},
+ {12345678, "12345678"},
+ {123456789, "123456789"},
+ {1234567890, "1234567890"},
+ {12345678901, "12345678901"},
+ {123456789012, "123456789012"},
+ {1234567890123, "1234567890123"},
+ {12345678901234, "12345678901234"},
+ {123456789012345, "123456789012345"},
+ {1234567890123456, "1234567890123456"},
+ {12345678901234567, "12345678901234567"},
+ {123456789012345678, "123456789012345678"},
+ {1234567890123456789, "1234567890123456789"},
+ {12345678901234567890, "12345678901234567890"},
+}
+
+func TestFormatUintVarlen(t *testing.T) {
+ for _, test := range varlenUints {
+ s := FormatUint(test.in, 10)
+ if s != test.out {
+ t.Errorf("FormatUint(%v, 10) = %v want %v", test.in, s, test.out)
+ }
+ }
+}
+
+func BenchmarkFormatInt(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ for _, test := range itob64tests {
+ s := FormatInt(test.in, test.base)
+ BenchSink += len(s)
+ }
+ }
+}
+
+func BenchmarkAppendInt(b *testing.B) {
+ dst := make([]byte, 0, 30)
+ for i := 0; i < b.N; i++ {
+ for _, test := range itob64tests {
+ dst = AppendInt(dst[:0], test.in, test.base)
+ BenchSink += len(dst)
+ }
+ }
+}
+
+func BenchmarkFormatUint(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ for _, test := range uitob64tests {
+ s := FormatUint(test.in, test.base)
+ BenchSink += len(s)
+ }
+ }
+}
+
+func BenchmarkAppendUint(b *testing.B) {
+ dst := make([]byte, 0, 30)
+ for i := 0; i < b.N; i++ {
+ for _, test := range uitob64tests {
+ dst = AppendUint(dst[:0], test.in, test.base)
+ BenchSink += len(dst)
+ }
+ }
+}
+
+func BenchmarkFormatIntSmall(b *testing.B) {
+ smallInts := []int64{7, 42}
+ for _, smallInt := range smallInts {
+ b.Run(Itoa(int(smallInt)), func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ s := FormatInt(smallInt, 10)
+ BenchSink += len(s)
+ }
+ })
+ }
+}
+
+func BenchmarkAppendIntSmall(b *testing.B) {
+ dst := make([]byte, 0, 30)
+ const smallInt = 42
+ for i := 0; i < b.N; i++ {
+ dst = AppendInt(dst[:0], smallInt, 10)
+ BenchSink += len(dst)
+ }
+}
+
+func BenchmarkAppendUintVarlen(b *testing.B) {
+ for _, test := range varlenUints {
+ b.Run(test.out, func(b *testing.B) {
+ dst := make([]byte, 0, 30)
+ for j := 0; j < b.N; j++ {
+ dst = AppendUint(dst[:0], test.in, 10)
+ BenchSink += len(dst)
+ }
+ })
+ }
+}
+
+var BenchSink int // make sure compiler cannot optimize away benchmarks
diff --git a/gnovm/stdlibs/strconv/quote.gno b/gnovm/stdlibs/strconv/quote.gno
new file mode 100644
index 00000000000..7c384336795
--- /dev/null
+++ b/gnovm/stdlibs/strconv/quote.gno
@@ -0,0 +1,599 @@
+// Copyright 2009 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:generate go run makeisprint.go -output isprint.go
+
+package strconv
+
+import (
+ "unicode/utf8"
+)
+
+const (
+ lowerhex = "0123456789abcdef"
+ upperhex = "0123456789ABCDEF"
+)
+
+// contains reports whether the string contains the byte c.
+func contains(s string, c byte) bool {
+ return index(s, c) != -1
+}
+
+func quoteWith(s string, quote byte, ASCIIonly, graphicOnly bool) string {
+ return string(appendQuotedWith(make([]byte, 0, 3*len(s)/2), s, quote, ASCIIonly, graphicOnly))
+}
+
+func quoteRuneWith(r rune, quote byte, ASCIIonly, graphicOnly bool) string {
+ return string(appendQuotedRuneWith(nil, r, quote, ASCIIonly, graphicOnly))
+}
+
+func appendQuotedWith(buf []byte, s string, quote byte, ASCIIonly, graphicOnly bool) []byte {
+ // Often called with big strings, so preallocate. If there's quoting,
+ // this is conservative but still helps a lot.
+ if cap(buf)-len(buf) < len(s) {
+ nBuf := make([]byte, len(buf), len(buf)+1+len(s)+1)
+ copy(nBuf, buf)
+ buf = nBuf
+ }
+ buf = append(buf, quote)
+ for width := 0; len(s) > 0; s = s[width:] {
+ r := rune(s[0])
+ width = 1
+ if r >= utf8.RuneSelf {
+ r, width = utf8.DecodeRuneInString(s)
+ }
+ if width == 1 && r == utf8.RuneError {
+ buf = append(buf, `\x`...)
+ buf = append(buf, lowerhex[s[0]>>4])
+ buf = append(buf, lowerhex[s[0]&0xF])
+ continue
+ }
+ buf = appendEscapedRune(buf, r, quote, ASCIIonly, graphicOnly)
+ }
+ buf = append(buf, quote)
+ return buf
+}
+
+func appendQuotedRuneWith(buf []byte, r rune, quote byte, ASCIIonly, graphicOnly bool) []byte {
+ buf = append(buf, quote)
+ if !utf8.ValidRune(r) {
+ r = utf8.RuneError
+ }
+ buf = appendEscapedRune(buf, r, quote, ASCIIonly, graphicOnly)
+ buf = append(buf, quote)
+ return buf
+}
+
+func appendEscapedRune(buf []byte, r rune, quote byte, ASCIIonly, graphicOnly bool) []byte {
+ if r == rune(quote) || r == '\\' { // always backslashed
+ buf = append(buf, '\\')
+ buf = append(buf, byte(r))
+ return buf
+ }
+ if ASCIIonly {
+ if r < utf8.RuneSelf && IsPrint(r) {
+ buf = append(buf, byte(r))
+ return buf
+ }
+ } else if IsPrint(r) || graphicOnly && isInGraphicList(r) {
+ return utf8.AppendRune(buf, r)
+ }
+ switch r {
+ case '\a':
+ buf = append(buf, `\a`...)
+ case '\b':
+ buf = append(buf, `\b`...)
+ case '\f':
+ buf = append(buf, `\f`...)
+ case '\n':
+ buf = append(buf, `\n`...)
+ case '\r':
+ buf = append(buf, `\r`...)
+ case '\t':
+ buf = append(buf, `\t`...)
+ case '\v':
+ buf = append(buf, `\v`...)
+ default:
+ switch {
+ case r < ' ' || r == 0x7f:
+ buf = append(buf, `\x`...)
+ buf = append(buf, lowerhex[byte(r)>>4])
+ buf = append(buf, lowerhex[byte(r)&0xF])
+ case !utf8.ValidRune(r):
+ r = 0xFFFD
+ fallthrough
+ case r < 0x10000:
+ buf = append(buf, `\u`...)
+ for s := 12; s >= 0; s -= 4 {
+ buf = append(buf, lowerhex[r>>uint(s)&0xF])
+ }
+ default:
+ buf = append(buf, `\U`...)
+ for s := 28; s >= 0; s -= 4 {
+ buf = append(buf, lowerhex[r>>uint(s)&0xF])
+ }
+ }
+ }
+ return buf
+}
+
+// Quote returns a double-quoted Go string literal representing s. The
+// returned string uses Go escape sequences (\t, \n, \xFF, \u0100) for
+// control characters and non-printable characters as defined by
+// IsPrint.
+func Quote(s string) string {
+ return quoteWith(s, '"', false, false)
+}
+
+// AppendQuote appends a double-quoted Go string literal representing s,
+// as generated by Quote, to dst and returns the extended buffer.
+func AppendQuote(dst []byte, s string) []byte {
+ return appendQuotedWith(dst, s, '"', false, false)
+}
+
+// QuoteToASCII returns a double-quoted Go string literal representing s.
+// The returned string uses Go escape sequences (\t, \n, \xFF, \u0100) for
+// non-ASCII characters and non-printable characters as defined by IsPrint.
+func QuoteToASCII(s string) string {
+ return quoteWith(s, '"', true, false)
+}
+
+// AppendQuoteToASCII appends a double-quoted Go string literal representing s,
+// as generated by QuoteToASCII, to dst and returns the extended buffer.
+func AppendQuoteToASCII(dst []byte, s string) []byte {
+ return appendQuotedWith(dst, s, '"', true, false)
+}
+
+// QuoteToGraphic returns a double-quoted Go string literal representing s.
+// The returned string leaves Unicode graphic characters, as defined by
+// IsGraphic, unchanged and uses Go escape sequences (\t, \n, \xFF, \u0100)
+// for non-graphic characters.
+func QuoteToGraphic(s string) string {
+ return quoteWith(s, '"', false, true)
+}
+
+// AppendQuoteToGraphic appends a double-quoted Go string literal representing s,
+// as generated by QuoteToGraphic, to dst and returns the extended buffer.
+func AppendQuoteToGraphic(dst []byte, s string) []byte {
+ return appendQuotedWith(dst, s, '"', false, true)
+}
+
+// QuoteRune returns a single-quoted Go character literal representing the
+// rune. The returned string uses Go escape sequences (\t, \n, \xFF, \u0100)
+// for control characters and non-printable characters as defined by IsPrint.
+// If r is not a valid Unicode code point, it is interpreted as the Unicode
+// replacement character U+FFFD.
+func QuoteRune(r rune) string {
+ return quoteRuneWith(r, '\'', false, false)
+}
+
+// AppendQuoteRune appends a single-quoted Go character literal representing the rune,
+// as generated by QuoteRune, to dst and returns the extended buffer.
+func AppendQuoteRune(dst []byte, r rune) []byte {
+ return appendQuotedRuneWith(dst, r, '\'', false, false)
+}
+
+// QuoteRuneToASCII returns a single-quoted Go character literal representing
+// the rune. The returned string uses Go escape sequences (\t, \n, \xFF,
+// \u0100) for non-ASCII characters and non-printable characters as defined
+// by IsPrint.
+// If r is not a valid Unicode code point, it is interpreted as the Unicode
+// replacement character U+FFFD.
+func QuoteRuneToASCII(r rune) string {
+ return quoteRuneWith(r, '\'', true, false)
+}
+
+// AppendQuoteRuneToASCII appends a single-quoted Go character literal representing the rune,
+// as generated by QuoteRuneToASCII, to dst and returns the extended buffer.
+func AppendQuoteRuneToASCII(dst []byte, r rune) []byte {
+ return appendQuotedRuneWith(dst, r, '\'', true, false)
+}
+
+// QuoteRuneToGraphic returns a single-quoted Go character literal representing
+// the rune. If the rune is not a Unicode graphic character,
+// as defined by IsGraphic, the returned string will use a Go escape sequence
+// (\t, \n, \xFF, \u0100).
+// If r is not a valid Unicode code point, it is interpreted as the Unicode
+// replacement character U+FFFD.
+func QuoteRuneToGraphic(r rune) string {
+ return quoteRuneWith(r, '\'', false, true)
+}
+
+// AppendQuoteRuneToGraphic appends a single-quoted Go character literal representing the rune,
+// as generated by QuoteRuneToGraphic, to dst and returns the extended buffer.
+func AppendQuoteRuneToGraphic(dst []byte, r rune) []byte {
+ return appendQuotedRuneWith(dst, r, '\'', false, true)
+}
+
+// CanBackquote reports whether the string s can be represented
+// unchanged as a single-line backquoted string without control
+// characters other than tab.
+func CanBackquote(s string) bool {
+ for len(s) > 0 {
+ r, wid := utf8.DecodeRuneInString(s)
+ s = s[wid:]
+ if wid > 1 {
+ if r == '\ufeff' {
+ return false // BOMs are invisible and should not be quoted.
+ }
+ continue // All other multibyte runes are correctly encoded and assumed printable.
+ }
+ if r == utf8.RuneError {
+ return false
+ }
+ if (r < ' ' && r != '\t') || r == '`' || r == '\u007F' {
+ return false
+ }
+ }
+ return true
+}
+
+func unhex(b byte) (v rune, ok bool) {
+ c := rune(b)
+ switch {
+ case '0' <= c && c <= '9':
+ return c - '0', true
+ case 'a' <= c && c <= 'f':
+ return c - 'a' + 10, true
+ case 'A' <= c && c <= 'F':
+ return c - 'A' + 10, true
+ }
+ return
+}
+
+// UnquoteChar decodes the first character or byte in the escaped string
+// or character literal represented by the string s.
+// It returns four values:
+//
+// 1. value, the decoded Unicode code point or byte value;
+// 2. multibyte, a boolean indicating whether the decoded character requires a multibyte UTF-8 representation;
+// 3. tail, the remainder of the string after the character; and
+// 4. an error that will be nil if the character is syntactically valid.
+//
+// The second argument, quote, specifies the type of literal being parsed
+// and therefore which escaped quote character is permitted.
+// If set to a single quote, it permits the sequence \' and disallows unescaped '.
+// If set to a double quote, it permits \" and disallows unescaped ".
+// If set to zero, it does not permit either escape and allows both quote characters to appear unescaped.
+func UnquoteChar(s string, quote byte) (value rune, multibyte bool, tail string, err error) {
+ // easy cases
+ if len(s) == 0 {
+ err = ErrSyntax
+ return
+ }
+ switch c := s[0]; {
+ case c == quote && (quote == '\'' || quote == '"'):
+ err = ErrSyntax
+ return
+ case c >= utf8.RuneSelf:
+ r, size := utf8.DecodeRuneInString(s)
+ return r, true, s[size:], nil
+ case c != '\\':
+ return rune(s[0]), false, s[1:], nil
+ }
+
+ // hard case: c is backslash
+ if len(s) <= 1 {
+ err = ErrSyntax
+ return
+ }
+ c := s[1]
+ s = s[2:]
+
+ switch c {
+ case 'a':
+ value = '\a'
+ case 'b':
+ value = '\b'
+ case 'f':
+ value = '\f'
+ case 'n':
+ value = '\n'
+ case 'r':
+ value = '\r'
+ case 't':
+ value = '\t'
+ case 'v':
+ value = '\v'
+ case 'x', 'u', 'U':
+ n := 0
+ switch c {
+ case 'x':
+ n = 2
+ case 'u':
+ n = 4
+ case 'U':
+ n = 8
+ }
+ var v rune
+ if len(s) < n {
+ err = ErrSyntax
+ return
+ }
+ for j := 0; j < n; j++ {
+ x, ok := unhex(s[j])
+ if !ok {
+ err = ErrSyntax
+ return
+ }
+ v = v<<4 | x
+ }
+ s = s[n:]
+ if c == 'x' {
+ // single-byte string, possibly not UTF-8
+ value = v
+ break
+ }
+ if !utf8.ValidRune(v) {
+ err = ErrSyntax
+ return
+ }
+ value = v
+ multibyte = true
+ case '0', '1', '2', '3', '4', '5', '6', '7':
+ v := rune(c) - '0'
+ if len(s) < 2 {
+ err = ErrSyntax
+ return
+ }
+ for j := 0; j < 2; j++ { // one digit already; two more
+ x := rune(s[j]) - '0'
+ if x < 0 || x > 7 {
+ err = ErrSyntax
+ return
+ }
+ v = (v << 3) | x
+ }
+ s = s[2:]
+ if v > 255 {
+ err = ErrSyntax
+ return
+ }
+ value = v
+ case '\\':
+ value = '\\'
+ case '\'', '"':
+ if c != quote {
+ err = ErrSyntax
+ return
+ }
+ value = rune(c)
+ default:
+ err = ErrSyntax
+ return
+ }
+ tail = s
+ return
+}
+
+// QuotedPrefix returns the quoted string (as understood by Unquote) at the prefix of s.
+// If s does not start with a valid quoted string, QuotedPrefix returns an error.
+func QuotedPrefix(s string) (string, error) {
+ out, _, err := unquote(s, false)
+ return out, err
+}
+
+// Unquote interprets s as a single-quoted, double-quoted,
+// or backquoted Go string literal, returning the string value
+// that s quotes. (If s is single-quoted, it would be a Go
+// character literal; Unquote returns the corresponding
+// one-character string.)
+func Unquote(s string) (string, error) {
+ out, rem, err := unquote(s, true)
+ if len(rem) > 0 {
+ return "", ErrSyntax
+ }
+ return out, err
+}
+
+// unquote parses a quoted string at the start of the input,
+// returning the parsed prefix, the remaining suffix, and any parse errors.
+// If unescape is true, the parsed prefix is unescaped,
+// otherwise the input prefix is provided verbatim.
+func unquote(in string, unescape bool) (out, rem string, err error) {
+ // Determine the quote form and optimistically find the terminating quote.
+ if len(in) < 2 {
+ return "", in, ErrSyntax
+ }
+ quote := in[0]
+ end := index(in[1:], quote)
+ if end < 0 {
+ return "", in, ErrSyntax
+ }
+ end += 2 // position after terminating quote; may be wrong if escape sequences are present
+
+ switch quote {
+ case '`':
+ switch {
+ case !unescape:
+ out = in[:end] // include quotes
+ case !contains(in[:end], '\r'):
+ out = in[len("`") : end-len("`")] // exclude quotes
+ default:
+ // Carriage return characters ('\r') inside raw string literals
+ // are discarded from the raw string value.
+ buf := make([]byte, 0, end-len("`")-len("\r")-len("`"))
+ for i := len("`"); i < end-len("`"); i++ {
+ if in[i] != '\r' {
+ buf = append(buf, in[i])
+ }
+ }
+ out = string(buf)
+ }
+ // NOTE: Prior implementations did not verify that raw strings consist
+ // of valid UTF-8 characters and we continue to not verify it as such.
+ // The Go specification does not explicitly require valid UTF-8,
+ // but only mention that it is implicitly valid for Go source code
+ // (which must be valid UTF-8).
+ return out, in[end:], nil
+ case '"', '\'':
+ // Handle quoted strings without any escape sequences.
+ if !contains(in[:end], '\\') && !contains(in[:end], '\n') {
+ var valid bool
+ switch quote {
+ case '"':
+ valid = utf8.ValidString(in[len(`"`) : end-len(`"`)])
+ case '\'':
+ r, n := utf8.DecodeRuneInString(in[len("'") : end-len("'")])
+ valid = len("'")+n+len("'") == end && (r != utf8.RuneError || n != 1)
+ }
+ if valid {
+ out = in[:end]
+ if unescape {
+ out = out[1 : end-1] // exclude quotes
+ }
+ return out, in[end:], nil
+ }
+ }
+
+ // Handle quoted strings with escape sequences.
+ var buf []byte
+ in0 := in
+ in = in[1:] // skip starting quote
+ if unescape {
+ buf = make([]byte, 0, 3*end/2) // try to avoid more allocations
+ }
+ for len(in) > 0 && in[0] != quote {
+ // Process the next character,
+ // rejecting any unescaped newline characters which are invalid.
+ r, multibyte, rem, err := UnquoteChar(in, quote)
+ if in[0] == '\n' || err != nil {
+ return "", in0, ErrSyntax
+ }
+ in = rem
+
+ // Append the character if unescaping the input.
+ if unescape {
+ if r < utf8.RuneSelf || !multibyte {
+ buf = append(buf, byte(r))
+ } else {
+ buf = utf8.AppendRune(buf, r)
+ }
+ }
+
+ // Single quoted strings must be a single character.
+ if quote == '\'' {
+ break
+ }
+ }
+
+ // Verify that the string ends with a terminating quote.
+ if !(len(in) > 0 && in[0] == quote) {
+ return "", in0, ErrSyntax
+ }
+ in = in[1:] // skip terminating quote
+
+ if unescape {
+ return string(buf), in, nil
+ }
+ return in0[:len(in0)-len(in)], in, nil
+ default:
+ return "", in, ErrSyntax
+ }
+}
+
+// bsearch16 returns the smallest i such that a[i] >= x.
+// If there is no such i, bsearch16 returns len(a).
+func bsearch16(a []uint16, x uint16) int {
+ i, j := 0, len(a)
+ for i < j {
+ h := i + (j-i)>>1
+ if a[h] < x {
+ i = h + 1
+ } else {
+ j = h
+ }
+ }
+ return i
+}
+
+// bsearch32 returns the smallest i such that a[i] >= x.
+// If there is no such i, bsearch32 returns len(a).
+func bsearch32(a []uint32, x uint32) int {
+ i, j := 0, len(a)
+ for i < j {
+ h := i + (j-i)>>1
+ if a[h] < x {
+ i = h + 1
+ } else {
+ j = h
+ }
+ }
+ return i
+}
+
+// TODO: IsPrint is a local implementation of unicode.IsPrint, verified by the tests
+// to give the same answer. It allows this package not to depend on unicode,
+// and therefore not pull in all the Unicode tables. If the linker were better
+// at tossing unused tables, we could get rid of this implementation.
+// That would be nice.
+
+// IsPrint reports whether the rune is defined as printable by Go, with
+// the same definition as unicode.IsPrint: letters, numbers, punctuation,
+// symbols and ASCII space.
+func IsPrint(r rune) bool {
+ // Fast check for Latin-1
+ if r <= 0xFF {
+ if 0x20 <= r && r <= 0x7E {
+ // All the ASCII is printable from space through DEL-1.
+ return true
+ }
+ if 0xA1 <= r && r <= 0xFF {
+ // Similarly for ¡ through ÿ...
+ return r != 0xAD // ...except for the bizarre soft hyphen.
+ }
+ return false
+ }
+
+ // Same algorithm, either on uint16 or uint32 value.
+ // First, find first i such that isPrint[i] >= x.
+ // This is the index of either the start or end of a pair that might span x.
+ // The start is even (isPrint[i&^1]) and the end is odd (isPrint[i|1]).
+ // If we find x in a range, make sure x is not in isNotPrint list.
+
+ if 0 <= r && r < 1<<16 {
+ rr, isPrint, isNotPrint := uint16(r), isPrint16, isNotPrint16
+ i := bsearch16(isPrint, rr)
+ if i >= len(isPrint) || rr < isPrint[i&^1] || isPrint[i|1] < rr {
+ return false
+ }
+ j := bsearch16(isNotPrint, rr)
+ return j >= len(isNotPrint) || isNotPrint[j] != rr
+ }
+
+ rr, isPrint, isNotPrint := uint32(r), isPrint32, isNotPrint32
+ i := bsearch32(isPrint, rr)
+ if i >= len(isPrint) || rr < isPrint[i&^1] || isPrint[i|1] < rr {
+ return false
+ }
+ if r >= 0x20000 {
+ return true
+ }
+ r -= 0x10000
+ j := bsearch16(isNotPrint, uint16(r))
+ return j >= len(isNotPrint) || isNotPrint[j] != uint16(r)
+}
+
+// IsGraphic reports whether the rune is defined as a Graphic by Unicode. Such
+// characters include letters, marks, numbers, punctuation, symbols, and
+// spaces, from categories L, M, N, P, S, and Zs.
+func IsGraphic(r rune) bool {
+ if IsPrint(r) {
+ return true
+ }
+ return isInGraphicList(r)
+}
+
+// isInGraphicList reports whether the rune is in the isGraphic list. This separation
+// from IsGraphic allows quoteWith to avoid two calls to IsPrint.
+// Should be called only if IsPrint fails.
+func isInGraphicList(r rune) bool {
+ // We know r must fit in 16 bits - see makeisprint.go.
+ if r > 0xFFFF {
+ return false
+ }
+ rr := uint16(r)
+ i := bsearch16(isGraphic, rr)
+ return i < len(isGraphic) && rr == isGraphic[i]
+}
diff --git a/gnovm/stdlibs/strconv/quote_test.gno b/gnovm/stdlibs/strconv/quote_test.gno
new file mode 100644
index 00000000000..b11e95461b0
--- /dev/null
+++ b/gnovm/stdlibs/strconv/quote_test.gno
@@ -0,0 +1,383 @@
+// Copyright 2009 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.
+
+package strconv
+
+import (
+ "strings"
+ "testing"
+ "unicode"
+)
+
+// Verify that our IsPrint agrees with unicode.IsPrint.
+func TestIsPrint(t *testing.T) {
+ n := 0
+ for r := rune(0); r <= unicode.MaxRune; r++ {
+ if IsPrint(r) != unicode.IsPrint(r) {
+ t.Errorf("IsPrint(%U)=%t incorrect", r, IsPrint(r))
+ n++
+ if n > 10 {
+ return
+ }
+ }
+ }
+}
+
+// Verify that our IsGraphic agrees with unicode.IsGraphic.
+func TestIsGraphic(t *testing.T) {
+ n := 0
+ for r := rune(0); r <= unicode.MaxRune; r++ {
+ if IsGraphic(r) != unicode.IsGraphic(r) {
+ t.Errorf("IsGraphic(%U)=%t incorrect", r, IsGraphic(r))
+ n++
+ if n > 10 {
+ return
+ }
+ }
+ }
+}
+
+type quoteTest struct {
+ in string
+ out string
+ ascii string
+ graphic string
+}
+
+var quotetests = []quoteTest{
+ {"\a\b\f\r\n\t\v", `"\a\b\f\r\n\t\v"`, `"\a\b\f\r\n\t\v"`, `"\a\b\f\r\n\t\v"`},
+ {"\\", `"\\"`, `"\\"`, `"\\"`},
+ {"abc\xffdef", `"abc\xffdef"`, `"abc\xffdef"`, `"abc\xffdef"`},
+ {"\u263a", `"☺"`, `"\u263a"`, `"☺"`},
+ {"\U0010ffff", `"\U0010ffff"`, `"\U0010ffff"`, `"\U0010ffff"`},
+ {"\x04", `"\x04"`, `"\x04"`, `"\x04"`},
+ // Some non-printable but graphic runes. Final column is double-quoted.
+ {"!\u00a0!\u2000!\u3000!", `"!\u00a0!\u2000!\u3000!"`, `"!\u00a0!\u2000!\u3000!"`, "\"!\u00a0!\u2000!\u3000!\""},
+ {"\x7f", `"\x7f"`, `"\x7f"`, `"\x7f"`},
+}
+
+func TestQuote(t *testing.T) {
+ for _, tt := range quotetests {
+ if out := Quote(tt.in); out != tt.out {
+ t.Errorf("Quote(%s) = %s, want %s", tt.in, out, tt.out)
+ }
+ if out := AppendQuote([]byte("abc"), tt.in); string(out) != "abc"+tt.out {
+ t.Errorf("AppendQuote(%q, %s) = %s, want %s", "abc", tt.in, out, "abc"+tt.out)
+ }
+ }
+}
+
+func TestQuoteToASCII(t *testing.T) {
+ for _, tt := range quotetests {
+ if out := QuoteToASCII(tt.in); out != tt.ascii {
+ t.Errorf("QuoteToASCII(%s) = %s, want %s", tt.in, out, tt.ascii)
+ }
+ if out := AppendQuoteToASCII([]byte("abc"), tt.in); string(out) != "abc"+tt.ascii {
+ t.Errorf("AppendQuoteToASCII(%q, %s) = %s, want %s", "abc", tt.in, out, "abc"+tt.ascii)
+ }
+ }
+}
+
+func TestQuoteToGraphic(t *testing.T) {
+ for _, tt := range quotetests {
+ if out := QuoteToGraphic(tt.in); out != tt.graphic {
+ t.Errorf("QuoteToGraphic(%s) = %s, want %s", tt.in, out, tt.graphic)
+ }
+ if out := AppendQuoteToGraphic([]byte("abc"), tt.in); string(out) != "abc"+tt.graphic {
+ t.Errorf("AppendQuoteToGraphic(%q, %s) = %s, want %s", "abc", tt.in, out, "abc"+tt.graphic)
+ }
+ }
+}
+
+func BenchmarkQuote(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Quote("\a\b\f\r\n\t\v\a\b\f\r\n\t\v\a\b\f\r\n\t\v")
+ }
+}
+
+func BenchmarkQuoteRune(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ QuoteRune('\a')
+ }
+}
+
+var benchQuoteBuf []byte
+
+func BenchmarkAppendQuote(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ benchQuoteBuf = AppendQuote(benchQuoteBuf[:0], "\a\b\f\r\n\t\v\a\b\f\r\n\t\v\a\b\f\r\n\t\v")
+ }
+}
+
+var benchQuoteRuneBuf []byte
+
+func BenchmarkAppendQuoteRune(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ benchQuoteRuneBuf = AppendQuoteRune(benchQuoteRuneBuf[:0], '\a')
+ }
+}
+
+type quoteRuneTest struct {
+ in rune
+ out string
+ ascii string
+ graphic string
+}
+
+var quoterunetests = []quoteRuneTest{
+ {'a', `'a'`, `'a'`, `'a'`},
+ {'\a', `'\a'`, `'\a'`, `'\a'`},
+ {'\\', `'\\'`, `'\\'`, `'\\'`},
+ {0xFF, `'ÿ'`, `'\u00ff'`, `'ÿ'`},
+ {0x263a, `'☺'`, `'\u263a'`, `'☺'`},
+ {0xdead, `'�'`, `'\ufffd'`, `'�'`},
+ {0xfffd, `'�'`, `'\ufffd'`, `'�'`},
+ {0x0010ffff, `'\U0010ffff'`, `'\U0010ffff'`, `'\U0010ffff'`},
+ {0x0010ffff + 1, `'�'`, `'\ufffd'`, `'�'`},
+ {0x04, `'\x04'`, `'\x04'`, `'\x04'`},
+ // Some differences between graphic and printable. Note the last column is double-quoted.
+ {'\u00a0', `'\u00a0'`, `'\u00a0'`, "'\u00a0'"},
+ {'\u2000', `'\u2000'`, `'\u2000'`, "'\u2000'"},
+ {'\u3000', `'\u3000'`, `'\u3000'`, "'\u3000'"},
+}
+
+func TestQuoteRune(t *testing.T) {
+ for _, tt := range quoterunetests {
+ if out := QuoteRune(tt.in); out != tt.out {
+ t.Errorf("QuoteRune(%U) = %s, want %s", tt.in, out, tt.out)
+ }
+ if out := AppendQuoteRune([]byte("abc"), tt.in); string(out) != "abc"+tt.out {
+ t.Errorf("AppendQuoteRune(%q, %U) = %s, want %s", "abc", tt.in, out, "abc"+tt.out)
+ }
+ }
+}
+
+func TestQuoteRuneToASCII(t *testing.T) {
+ for _, tt := range quoterunetests {
+ if out := QuoteRuneToASCII(tt.in); out != tt.ascii {
+ t.Errorf("QuoteRuneToASCII(%U) = %s, want %s", tt.in, out, tt.ascii)
+ }
+ if out := AppendQuoteRuneToASCII([]byte("abc"), tt.in); string(out) != "abc"+tt.ascii {
+ t.Errorf("AppendQuoteRuneToASCII(%q, %U) = %s, want %s", "abc", tt.in, out, "abc"+tt.ascii)
+ }
+ }
+}
+
+func TestQuoteRuneToGraphic(t *testing.T) {
+ for _, tt := range quoterunetests {
+ if out := QuoteRuneToGraphic(tt.in); out != tt.graphic {
+ t.Errorf("QuoteRuneToGraphic(%U) = %s, want %s", tt.in, out, tt.graphic)
+ }
+ if out := AppendQuoteRuneToGraphic([]byte("abc"), tt.in); string(out) != "abc"+tt.graphic {
+ t.Errorf("AppendQuoteRuneToGraphic(%q, %U) = %s, want %s", "abc", tt.in, out, "abc"+tt.graphic)
+ }
+ }
+}
+
+type canBackquoteTest struct {
+ in string
+ out bool
+}
+
+var canbackquotetests = []canBackquoteTest{
+ {"`", false},
+ {string(rune(0)), false},
+ {string(rune(1)), false},
+ {string(rune(2)), false},
+ {string(rune(3)), false},
+ {string(rune(4)), false},
+ {string(rune(5)), false},
+ {string(rune(6)), false},
+ {string(rune(7)), false},
+ {string(rune(8)), false},
+ {string(rune(9)), true}, // \t
+ {string(rune(10)), false},
+ {string(rune(11)), false},
+ {string(rune(12)), false},
+ {string(rune(13)), false},
+ {string(rune(14)), false},
+ {string(rune(15)), false},
+ {string(rune(16)), false},
+ {string(rune(17)), false},
+ {string(rune(18)), false},
+ {string(rune(19)), false},
+ {string(rune(20)), false},
+ {string(rune(21)), false},
+ {string(rune(22)), false},
+ {string(rune(23)), false},
+ {string(rune(24)), false},
+ {string(rune(25)), false},
+ {string(rune(26)), false},
+ {string(rune(27)), false},
+ {string(rune(28)), false},
+ {string(rune(29)), false},
+ {string(rune(30)), false},
+ {string(rune(31)), false},
+ {string(rune(0x7F)), false},
+ {`' !"#$%&'()*+,-./:;<=>?@[\]^_{|}~`, true},
+ {`0123456789`, true},
+ {`ABCDEFGHIJKLMNOPQRSTUVWXYZ`, true},
+ {`abcdefghijklmnopqrstuvwxyz`, true},
+ {`☺`, true},
+ {"\x80", false},
+ {"a\xe0\xa0z", false},
+ {"\ufeffabc", false},
+ {"a\ufeffz", false},
+}
+
+func TestCanBackquote(t *testing.T) {
+ for _, tt := range canbackquotetests {
+ if out := CanBackquote(tt.in); out != tt.out {
+ t.Errorf("CanBackquote(%q) = %v, want %v", tt.in, out, tt.out)
+ }
+ }
+}
+
+type unQuoteTest struct {
+ in string
+ out string
+}
+
+var unquotetests = []unQuoteTest{
+ {`""`, ""},
+ {`"a"`, "a"},
+ {`"abc"`, "abc"},
+ {`"☺"`, "☺"},
+ {`"hello world"`, "hello world"},
+ {`"\xFF"`, "\xFF"},
+ {`"\377"`, "\377"},
+ {`"\u1234"`, "\u1234"},
+ {`"\U00010111"`, "\U00010111"},
+ {`"\U0001011111"`, "\U0001011111"},
+ {`"\a\b\f\n\r\t\v\\\""`, "\a\b\f\n\r\t\v\\\""},
+ {`"'"`, "'"},
+
+ {`'a'`, "a"},
+ {`'☹'`, "☹"},
+ {`'\a'`, "\a"},
+ {`'\x10'`, "\x10"},
+ {`'\377'`, "\377"},
+ {`'\u1234'`, "\u1234"},
+ {`'\U00010111'`, "\U00010111"},
+ {`'\t'`, "\t"},
+ {`' '`, " "},
+ {`'\''`, "'"},
+ {`'"'`, "\""},
+
+ {"``", ``},
+ {"`a`", `a`},
+ {"`abc`", `abc`},
+ {"`☺`", `☺`},
+ {"`hello world`", `hello world`},
+ {"`\\xFF`", `\xFF`},
+ {"`\\377`", `\377`},
+ {"`\\`", `\`},
+ {"`\n`", "\n"},
+ {"` `", ` `},
+ {"` `", ` `},
+ {"`a\rb`", "ab"},
+}
+
+var misquoted = []string{
+ ``,
+ `"`,
+ `"a`,
+ `"'`,
+ `b"`,
+ `"\"`,
+ `"\9"`,
+ `"\19"`,
+ `"\129"`,
+ `'\'`,
+ `'\9'`,
+ `'\19'`,
+ `'\129'`,
+ `'ab'`,
+ `"\x1!"`,
+ `"\U12345678"`,
+ `"\z"`,
+ "`",
+ "`xxx",
+ "``x\r",
+ "`\"",
+ `"\'"`,
+ `'\"'`,
+ "\"\n\"",
+ "\"\\n\n\"",
+ "'\n'",
+ `"\udead"`,
+ `"\ud83d\ude4f"`,
+}
+
+func TestUnquote(t *testing.T) {
+ for _, tt := range unquotetests {
+ testUnquote(t, tt.in, tt.out, nil)
+ }
+ for _, tt := range quotetests {
+ testUnquote(t, tt.out, tt.in, nil)
+ }
+ for _, s := range misquoted {
+ testUnquote(t, s, "", ErrSyntax)
+ }
+}
+
+// Issue 23685: invalid UTF-8 should not go through the fast path.
+func TestUnquoteInvalidUTF8(t *testing.T) {
+ tests := []struct {
+ in string
+
+ // one of:
+ want string
+ wantErr error
+ }{
+ {in: `"foo"`, want: "foo"},
+ {in: `"foo`, wantErr: ErrSyntax},
+ {in: `"` + "\xc0" + `"`, want: "\xef\xbf\xbd"},
+ {in: `"a` + "\xc0" + `"`, want: "a\xef\xbf\xbd"},
+ {in: `"\t` + "\xc0" + `"`, want: "\t\xef\xbf\xbd"},
+ }
+ for _, tt := range tests {
+ testUnquote(t, tt.in, tt.want, tt.wantErr)
+ }
+}
+
+func testUnquote(t *testing.T, in, want string, wantErr error) {
+ // Test Unquote.
+ got, gotErr := Unquote(in)
+ if got != want || gotErr != wantErr {
+ t.Errorf("Unquote(%q) = (%q, %v), want (%q, %v)", in, got, gotErr, want, wantErr)
+ }
+
+ // Test QuotedPrefix.
+ // Adding an arbitrary suffix should not change the result of QuotedPrefix
+ // assume that the suffix doesn't accidentally terminate a truncated input.
+ if gotErr == nil {
+ want = in
+ }
+ suffix := "\n\r\\\"`'" // special characters for quoted strings
+ if len(in) > 0 {
+ suffix = strings.ReplaceAll(suffix, in[:1], "")
+ }
+ in += suffix
+ got, gotErr = QuotedPrefix(in)
+ if gotErr == nil && wantErr != nil {
+ _, wantErr = Unquote(got) // original input had trailing junk, reparse with only valid prefix
+ want = got
+ }
+ if got != want || gotErr != wantErr {
+ t.Errorf("QuotedPrefix(%q) = (%q, %v), want (%q, %v)", in, got, gotErr, want, wantErr)
+ }
+}
+
+func BenchmarkUnquoteEasy(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Unquote(`"Give me a rock, paper and scissors and I will move the world."`)
+ }
+}
+
+func BenchmarkUnquoteHard(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Unquote(`"\x47ive me a \x72ock, \x70aper and \x73cissors and \x49 will move the world."`)
+ }
+}
diff --git a/gnovm/stdlibs/strconv/strconv.gno b/gnovm/stdlibs/strconv/strconv.gno
deleted file mode 100644
index bc7b5d8d1b6..00000000000
--- a/gnovm/stdlibs/strconv/strconv.gno
+++ /dev/null
@@ -1,10 +0,0 @@
-package strconv
-
-func Itoa(n int) string // injected
-func AppendUint(dst []byte, i uint64, base int) []byte // injected
-func Atoi(s string) (int, error) // injected
-func CanBackquote(s string) bool // injected
-func FormatInt(i int64, base int) string // injected
-func FormatUint(i uint64, base int) string // injected
-func Quote(s string) string // injected
-func QuoteToASCII(s string) string // injected
diff --git a/gnovm/stdlibs/strconv/strconv.go b/gnovm/stdlibs/strconv/strconv.go
deleted file mode 100644
index 782a63e84b6..00000000000
--- a/gnovm/stdlibs/strconv/strconv.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package strconv
-
-import "strconv"
-
-func Itoa(n int) string { return strconv.Itoa(n) }
-func AppendUint(dst []byte, i uint64, base int) []byte { return strconv.AppendUint(dst, i, base) }
-func Atoi(s string) (int, error) { return strconv.Atoi(s) }
-func CanBackquote(s string) bool { return strconv.CanBackquote(s) }
-func FormatInt(i int64, base int) string { return strconv.FormatInt(i, base) }
-func FormatUint(i uint64, base int) string { return strconv.FormatUint(i, base) }
-func Quote(s string) string { return strconv.Quote(s) }
-func QuoteToASCII(r string) string { return strconv.QuoteToASCII(r) }
diff --git a/gnovm/stdlibs/strconv/strconv_test.gno b/gnovm/stdlibs/strconv/strconv_test.gno
new file mode 100644
index 00000000000..5421ae84a93
--- /dev/null
+++ b/gnovm/stdlibs/strconv/strconv_test.gno
@@ -0,0 +1,156 @@
+// Copyright 2012 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.
+
+package strconv
+
+import (
+ "strings"
+ "testing"
+)
+
+var (
+ globalBuf [64]byte
+ nextToOne = "1.00000000000000011102230246251565404236316680908203125" + strings.Repeat("0", 10000) + "1"
+ oneMB = make([]byte, 1e6)
+
+ mallocTest = []struct {
+ count int
+ desc string
+ fn func()
+ }{
+ {0, `AppendInt(localBuf[:0], 123, 10)`, func() {
+ var localBuf [64]byte
+ AppendInt(localBuf[:0], 123, 10)
+ }},
+ {0, `AppendInt(globalBuf[:0], 123, 10)`, func() { AppendInt(globalBuf[:0], 123, 10) }},
+ {0, `AppendFloat(localBuf[:0], 1.23, 'g', 5, 64)`, func() {
+ var localBuf [64]byte
+ AppendFloat(localBuf[:0], 1.23, 'g', 5, 64)
+ }},
+ {0, `AppendFloat(globalBuf[:0], 1.23, 'g', 5, 64)`, func() { AppendFloat(globalBuf[:0], 1.23, 'g', 5, 64) }},
+ // In practice we see 7 for the next one, but allow some slop.
+ // Before pre-allocation in appendQuotedWith, we saw 39.
+ {10, `AppendQuoteToASCII(nil, oneMB)`, func() { AppendQuoteToASCII(nil, string(oneMB)) }},
+ {0, `ParseFloat("123.45", 64)`, func() { ParseFloat("123.45", 64) }},
+ {0, `ParseFloat("123.456789123456789", 64)`, func() { ParseFloat("123.456789123456789", 64) }},
+ {0, `ParseFloat("1.000000000000000111022302462515654042363166809082031251", 64)`, func() {
+ ParseFloat("1.000000000000000111022302462515654042363166809082031251", 64)
+ }},
+ {0, `ParseFloat("1.0000000000000001110223024625156540423631668090820312500...001", 64)`, func() {
+ ParseFloat(nextToOne, 64)
+ }},
+ }
+)
+
+// XXX: removed due to lack of AllocsPerRun
+// func TestCountMallocs(t *testing.T) {
+// if testing.Short() {
+// t.Skip("skipping malloc count in short mode")
+// }
+// // Allocate a big messy buffer for AppendQuoteToASCII's test.
+// oneMB = make([]byte, 1e6)
+// for i := range oneMB {
+// oneMB[i] = byte(i)
+// }
+// for _, mt := range mallocTest {
+// allocs := testing.AllocsPerRun(100, mt.fn)
+// if max := float64(mt.count); allocs > max {
+// t.Errorf("%s: %v allocs, want <=%v", mt.desc, allocs, max)
+// }
+// }
+// }
+
+// Sink makes sure the compiler cannot optimize away the benchmarks.
+var Sink struct {
+ Bool bool
+ Int int
+ Int64 int64
+ Uint64 uint64
+ Float64 float64
+ Error error
+ Bytes []byte
+}
+
+/* XXX: removed due to lack of AllocsPerRun
+func TestAllocationsFromBytes(t *testing.T) {
+ const runsPerTest = 100
+ bytes := struct{ Bool, Number, String, Buffer []byte }{
+ Bool: []byte("false"),
+ Number: []byte("123456789"),
+ String: []byte("hello, world!"),
+ Buffer: make([]byte, 1024),
+ }
+
+ checkNoAllocs := func(f func()) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Helper()
+ if allocs := testing.AllocsPerRun(runsPerTest, f); allocs != 0 {
+ t.Errorf("got %v allocs, want 0 allocs", allocs)
+ }
+ }
+ }
+
+ t.Run("Atoi", checkNoAllocs(func() {
+ Sink.Int, Sink.Error = Atoi(string(bytes.Number))
+ }))
+ t.Run("ParseBool", checkNoAllocs(func() {
+ Sink.Bool, Sink.Error = ParseBool(string(bytes.Bool))
+ }))
+ t.Run("ParseInt", checkNoAllocs(func() {
+ Sink.Int64, Sink.Error = ParseInt(string(bytes.Number), 10, 64)
+ }))
+ t.Run("ParseUint", checkNoAllocs(func() {
+ Sink.Uint64, Sink.Error = ParseUint(string(bytes.Number), 10, 64)
+ }))
+ t.Run("ParseFloat", checkNoAllocs(func() {
+ Sink.Float64, Sink.Error = ParseFloat(string(bytes.Number), 64)
+ }))
+ t.Run("ParseComplex", checkNoAllocs(func() {
+ Sink.Complex128, Sink.Error = ParseComplex(string(bytes.Number), 128)
+ }))
+ t.Run("CanBackquote", checkNoAllocs(func() {
+ Sink.Bool = CanBackquote(string(bytes.String))
+ }))
+ t.Run("AppendQuote", checkNoAllocs(func() {
+ Sink.Bytes = AppendQuote(bytes.Buffer[:0], string(bytes.String))
+ }))
+ t.Run("AppendQuoteToASCII", checkNoAllocs(func() {
+ Sink.Bytes = AppendQuoteToASCII(bytes.Buffer[:0], string(bytes.String))
+ }))
+ t.Run("AppendQuoteToGraphic", checkNoAllocs(func() {
+ Sink.Bytes = AppendQuoteToGraphic(bytes.Buffer[:0], string(bytes.String))
+ }))
+}
+*/
+
+func TestErrorPrefixes(t *testing.T) {
+ _, errInt := Atoi("INVALID")
+ _, errBool := ParseBool("INVALID")
+ _, errFloat := ParseFloat("INVALID", 64)
+ _, errInt64 := ParseInt("INVALID", 10, 64)
+ _, errUint64 := ParseUint("INVALID", 10, 64)
+
+ vectors := []struct {
+ err error // Input error
+ want string // Function name wanted
+ }{
+ {errInt, "Atoi"},
+ {errBool, "ParseBool"},
+ {errFloat, "ParseFloat"},
+ {errInt64, "ParseInt"},
+ {errUint64, "ParseUint"},
+ }
+
+ for _, v := range vectors {
+ nerr, ok := v.err.(*NumError)
+ if !ok {
+ t.Errorf("test %s, error was not a *NumError", v.want)
+ continue
+ }
+ if got := nerr.Func; got != v.want {
+ t.Errorf("mismatching Func: got %s, want %s", got, v.want)
+ }
+ }
+
+}
diff --git a/gnovm/stdlibs/unicode/example_test.gno b/gnovm/stdlibs/unicode/example_test.gno
index 3ab11a4b5d2..bd2920dfb56 100644
--- a/gnovm/stdlibs/unicode/example_test.gno
+++ b/gnovm/stdlibs/unicode/example_test.gno
@@ -116,7 +116,7 @@ func ExampleSimpleFold() {
fmt.Printf("%#U\n", unicode.SimpleFold('A')) // 'a'
fmt.Printf("%#U\n", unicode.SimpleFold('a')) // 'A'
fmt.Printf("%#U\n", unicode.SimpleFold('K')) // 'k'
- fmt.Printf("%#U\n", unicode.SimpleFold('k')) // '\u212A' (Kelvin symbol, K)
+ fmt.Printf("%#U\n", unicode.SimpleFold('k')) // '\u212A' (Kelvin symbol, K)
fmt.Printf("%#U\n", unicode.SimpleFold('\u212A')) // 'K'
fmt.Printf("%#U\n", unicode.SimpleFold('1')) // '1'
@@ -124,7 +124,7 @@ func ExampleSimpleFold() {
// U+0061 'a'
// U+0041 'A'
// U+006B 'k'
- // U+212A 'K'
+ // U+212A 'K'
// U+004B 'K'
// U+0031 '1'
}
@@ -194,3 +194,63 @@ func ExampleSpecialCase() {
// U+0130 'İ'
// U+0130 'İ'
}
+
+func ExampleIsDigit() {
+ fmt.Printf("%t\n", unicode.IsDigit('৩'))
+ fmt.Printf("%t\n", unicode.IsDigit('A'))
+ // Output:
+ // true
+ // false
+}
+
+func ExampleIsNumber() {
+ fmt.Printf("%t\n", unicode.IsNumber('Ⅷ'))
+ fmt.Printf("%t\n", unicode.IsNumber('A'))
+ // Output:
+ // true
+ // false
+}
+
+func ExampleIsLetter() {
+ fmt.Printf("%t\n", unicode.IsLetter('A'))
+ fmt.Printf("%t\n", unicode.IsLetter('7'))
+ // Output:
+ // true
+ // false
+}
+
+func ExampleIsLower() {
+ fmt.Printf("%t\n", unicode.IsLower('a'))
+ fmt.Printf("%t\n", unicode.IsLower('A'))
+ // Output:
+ // true
+ // false
+}
+
+func ExampleIsUpper() {
+ fmt.Printf("%t\n", unicode.IsUpper('A'))
+ fmt.Printf("%t\n", unicode.IsUpper('a'))
+ // Output:
+ // true
+ // false
+}
+
+func ExampleIsTitle() {
+ fmt.Printf("%t\n", unicode.IsTitle('Dž'))
+ fmt.Printf("%t\n", unicode.IsTitle('a'))
+ // Output:
+ // true
+ // false
+}
+
+func ExampleIsSpace() {
+ fmt.Printf("%t\n", unicode.IsSpace(' '))
+ fmt.Printf("%t\n", unicode.IsSpace('\n'))
+ fmt.Printf("%t\n", unicode.IsSpace('\t'))
+ fmt.Printf("%t\n", unicode.IsSpace('a'))
+ // Output:
+ // true
+ // true
+ // true
+ // false
+}
diff --git a/gnovm/stdlibs/unicode/letter.gno b/gnovm/stdlibs/unicode/letter.gno
index aa1039aa38d..f3f8e529648 100644
--- a/gnovm/stdlibs/unicode/letter.gno
+++ b/gnovm/stdlibs/unicode/letter.gno
@@ -331,7 +331,7 @@ type foldPair struct {
// SimpleFold('a') = 'A'
//
// SimpleFold('K') = 'k'
-// SimpleFold('k') = '\u212A' (Kelvin symbol, K)
+// SimpleFold('k') = '\u212A' (Kelvin symbol, K)
// SimpleFold('\u212A') = 'K'
//
// SimpleFold('1') = '1'
diff --git a/gnovm/stdlibs/unicode/tables.gno b/gnovm/stdlibs/unicode/tables.gno
index a57e0ca67cb..b3d65d9d5c1 100644
--- a/gnovm/stdlibs/unicode/tables.gno
+++ b/gnovm/stdlibs/unicode/tables.gno
@@ -3,7 +3,7 @@
package unicode
// Version is the Unicode edition from which the tables are derived.
-const Version = "13.0.0"
+const Version = "15.0.0"
// Categories is the set of Unicode category tables.
var Categories = map[string]*RangeTable{
@@ -52,7 +52,8 @@ var _C = &RangeTable{
{0x00ad, 0x0600, 1363},
{0x0601, 0x0605, 1},
{0x061c, 0x06dd, 193},
- {0x070f, 0x08e2, 467},
+ {0x070f, 0x0890, 385},
+ {0x0891, 0x08e2, 81},
{0x180e, 0x200b, 2045},
{0x200c, 0x200f, 1},
{0x202a, 0x202e, 1},
@@ -64,7 +65,7 @@ var _C = &RangeTable{
},
R32: []Range32{
{0x110bd, 0x110cd, 16},
- {0x13430, 0x13438, 1},
+ {0x13430, 0x1343f, 1},
{0x1bca0, 0x1bca3, 1},
{0x1d173, 0x1d17a, 1},
{0xe0001, 0xe0020, 31},
@@ -88,7 +89,8 @@ var _Cf = &RangeTable{
{0x00ad, 0x0600, 1363},
{0x0601, 0x0605, 1},
{0x061c, 0x06dd, 193},
- {0x070f, 0x08e2, 467},
+ {0x070f, 0x0890, 385},
+ {0x0891, 0x08e2, 81},
{0x180e, 0x200b, 2045},
{0x200c, 0x200f, 1},
{0x202a, 0x202e, 1},
@@ -99,7 +101,7 @@ var _Cf = &RangeTable{
},
R32: []Range32{
{0x110bd, 0x110cd, 16},
- {0x13430, 0x13438, 1},
+ {0x13430, 0x1343f, 1},
{0x1bca0, 0x1bca3, 1},
{0x1d173, 0x1d17a, 1},
{0xe0001, 0xe0020, 31},
@@ -169,8 +171,9 @@ var _L = &RangeTable{
{0x0828, 0x0840, 24},
{0x0841, 0x0858, 1},
{0x0860, 0x086a, 1},
- {0x08a0, 0x08b4, 1},
- {0x08b6, 0x08c7, 1},
+ {0x0870, 0x0887, 1},
+ {0x0889, 0x088e, 1},
+ {0x08a0, 0x08c9, 1},
{0x0904, 0x0939, 1},
{0x093d, 0x0950, 19},
{0x0958, 0x0961, 1},
@@ -231,17 +234,18 @@ var _L = &RangeTable{
{0x0c2a, 0x0c39, 1},
{0x0c3d, 0x0c58, 27},
{0x0c59, 0x0c5a, 1},
- {0x0c60, 0x0c61, 1},
- {0x0c80, 0x0c85, 5},
- {0x0c86, 0x0c8c, 1},
+ {0x0c5d, 0x0c60, 3},
+ {0x0c61, 0x0c80, 31},
+ {0x0c85, 0x0c8c, 1},
{0x0c8e, 0x0c90, 1},
{0x0c92, 0x0ca8, 1},
{0x0caa, 0x0cb3, 1},
{0x0cb5, 0x0cb9, 1},
- {0x0cbd, 0x0cde, 33},
- {0x0ce0, 0x0ce1, 1},
- {0x0cf1, 0x0cf2, 1},
- {0x0d04, 0x0d0c, 1},
+ {0x0cbd, 0x0cdd, 32},
+ {0x0cde, 0x0ce0, 2},
+ {0x0ce1, 0x0cf1, 16},
+ {0x0cf2, 0x0d04, 18},
+ {0x0d05, 0x0d0c, 1},
{0x0d0e, 0x0d10, 1},
{0x0d12, 0x0d3a, 1},
{0x0d3d, 0x0d4e, 17},
@@ -307,9 +311,8 @@ var _L = &RangeTable{
{0x1681, 0x169a, 1},
{0x16a0, 0x16ea, 1},
{0x16f1, 0x16f8, 1},
- {0x1700, 0x170c, 1},
- {0x170e, 0x1711, 1},
- {0x1720, 0x1731, 1},
+ {0x1700, 0x1711, 1},
+ {0x171f, 0x1731, 1},
{0x1740, 0x1751, 1},
{0x1760, 0x176c, 1},
{0x176e, 0x1770, 1},
@@ -329,7 +332,7 @@ var _L = &RangeTable{
{0x1a20, 0x1a54, 1},
{0x1aa7, 0x1b05, 94},
{0x1b06, 0x1b33, 1},
- {0x1b45, 0x1b4b, 1},
+ {0x1b45, 0x1b4c, 1},
{0x1b83, 0x1ba0, 1},
{0x1bae, 0x1baf, 1},
{0x1bba, 0x1be5, 1},
@@ -374,9 +377,7 @@ var _L = &RangeTable{
{0x2145, 0x2149, 1},
{0x214e, 0x2183, 53},
{0x2184, 0x2c00, 2684},
- {0x2c01, 0x2c2e, 1},
- {0x2c30, 0x2c5e, 1},
- {0x2c60, 0x2ce4, 1},
+ {0x2c01, 0x2ce4, 1},
{0x2ceb, 0x2cee, 1},
{0x2cf2, 0x2cf3, 1},
{0x2d00, 0x2d25, 1},
@@ -405,8 +406,7 @@ var _L = &RangeTable{
{0x31a0, 0x31bf, 1},
{0x31f0, 0x31ff, 1},
{0x3400, 0x4dbf, 1},
- {0x4e00, 0x9ffc, 1},
- {0xa000, 0xa48c, 1},
+ {0x4e00, 0xa48c, 1},
{0xa4d0, 0xa4fd, 1},
{0xa500, 0xa60c, 1},
{0xa610, 0xa61f, 1},
@@ -416,9 +416,11 @@ var _L = &RangeTable{
{0xa6a0, 0xa6e5, 1},
{0xa717, 0xa71f, 1},
{0xa722, 0xa788, 1},
- {0xa78b, 0xa7bf, 1},
- {0xa7c2, 0xa7ca, 1},
- {0xa7f5, 0xa801, 1},
+ {0xa78b, 0xa7ca, 1},
+ {0xa7d0, 0xa7d1, 1},
+ {0xa7d3, 0xa7d5, 2},
+ {0xa7d6, 0xa7d9, 1},
+ {0xa7f2, 0xa801, 1},
{0xa803, 0xa805, 1},
{0xa807, 0xa80a, 1},
{0xa80c, 0xa822, 1},
@@ -507,9 +509,20 @@ var _L = &RangeTable{
{0x104d8, 0x104fb, 1},
{0x10500, 0x10527, 1},
{0x10530, 0x10563, 1},
+ {0x10570, 0x1057a, 1},
+ {0x1057c, 0x1058a, 1},
+ {0x1058c, 0x10592, 1},
+ {0x10594, 0x10595, 1},
+ {0x10597, 0x105a1, 1},
+ {0x105a3, 0x105b1, 1},
+ {0x105b3, 0x105b9, 1},
+ {0x105bb, 0x105bc, 1},
{0x10600, 0x10736, 1},
{0x10740, 0x10755, 1},
{0x10760, 0x10767, 1},
+ {0x10780, 0x10785, 1},
+ {0x10787, 0x107b0, 1},
+ {0x107b2, 0x107ba, 1},
{0x10800, 0x10805, 1},
{0x10808, 0x1080a, 2},
{0x1080b, 0x10835, 1},
@@ -545,10 +558,13 @@ var _L = &RangeTable{
{0x10f00, 0x10f1c, 1},
{0x10f27, 0x10f30, 9},
{0x10f31, 0x10f45, 1},
+ {0x10f70, 0x10f81, 1},
{0x10fb0, 0x10fc4, 1},
{0x10fe0, 0x10ff6, 1},
{0x11003, 0x11037, 1},
- {0x11083, 0x110af, 1},
+ {0x11071, 0x11072, 1},
+ {0x11075, 0x11083, 14},
+ {0x11084, 0x110af, 1},
{0x110d0, 0x110e8, 1},
{0x11103, 0x11126, 1},
{0x11144, 0x11147, 3},
@@ -559,6 +575,7 @@ var _L = &RangeTable{
{0x111da, 0x111dc, 2},
{0x11200, 0x11211, 1},
{0x11213, 0x1122b, 1},
+ {0x1123f, 0x11240, 1},
{0x11280, 0x11286, 1},
{0x11288, 0x1128a, 2},
{0x1128b, 0x1128d, 1},
@@ -586,6 +603,7 @@ var _L = &RangeTable{
{0x11681, 0x116aa, 1},
{0x116b8, 0x11700, 72},
{0x11701, 0x1171a, 1},
+ {0x11740, 0x11746, 1},
{0x11800, 0x1182b, 1},
{0x118a0, 0x118df, 1},
{0x118ff, 0x11906, 1},
@@ -601,8 +619,8 @@ var _L = &RangeTable{
{0x11a0c, 0x11a32, 1},
{0x11a3a, 0x11a50, 22},
{0x11a5c, 0x11a89, 1},
- {0x11a9d, 0x11ac0, 35},
- {0x11ac1, 0x11af8, 1},
+ {0x11a9d, 0x11ab0, 19},
+ {0x11ab1, 0x11af8, 1},
{0x11c00, 0x11c08, 1},
{0x11c0a, 0x11c2e, 1},
{0x11c40, 0x11c72, 50},
@@ -616,13 +634,19 @@ var _L = &RangeTable{
{0x11d6a, 0x11d89, 1},
{0x11d98, 0x11ee0, 328},
{0x11ee1, 0x11ef2, 1},
+ {0x11f02, 0x11f04, 2},
+ {0x11f05, 0x11f10, 1},
+ {0x11f12, 0x11f33, 1},
{0x11fb0, 0x12000, 80},
{0x12001, 0x12399, 1},
{0x12480, 0x12543, 1},
- {0x13000, 0x1342e, 1},
+ {0x12f90, 0x12ff0, 1},
+ {0x13000, 0x1342f, 1},
+ {0x13441, 0x13446, 1},
{0x14400, 0x14646, 1},
{0x16800, 0x16a38, 1},
{0x16a40, 0x16a5e, 1},
+ {0x16a70, 0x16abe, 1},
{0x16ad0, 0x16aed, 1},
{0x16b00, 0x16b2f, 1},
{0x16b40, 0x16b43, 1},
@@ -637,9 +661,14 @@ var _L = &RangeTable{
{0x17001, 0x187f7, 1},
{0x18800, 0x18cd5, 1},
{0x18d00, 0x18d08, 1},
- {0x1b000, 0x1b11e, 1},
- {0x1b150, 0x1b152, 1},
- {0x1b164, 0x1b167, 1},
+ {0x1aff0, 0x1aff3, 1},
+ {0x1aff5, 0x1affb, 1},
+ {0x1affd, 0x1affe, 1},
+ {0x1b000, 0x1b122, 1},
+ {0x1b132, 0x1b150, 30},
+ {0x1b151, 0x1b152, 1},
+ {0x1b155, 0x1b164, 15},
+ {0x1b165, 0x1b167, 1},
{0x1b170, 0x1b2fb, 1},
{0x1bc00, 0x1bc6a, 1},
{0x1bc70, 0x1bc7c, 1},
@@ -675,10 +704,19 @@ var _L = &RangeTable{
{0x1d78a, 0x1d7a8, 1},
{0x1d7aa, 0x1d7c2, 1},
{0x1d7c4, 0x1d7cb, 1},
+ {0x1df00, 0x1df1e, 1},
+ {0x1df25, 0x1df2a, 1},
+ {0x1e030, 0x1e06d, 1},
{0x1e100, 0x1e12c, 1},
{0x1e137, 0x1e13d, 1},
- {0x1e14e, 0x1e2c0, 370},
- {0x1e2c1, 0x1e2eb, 1},
+ {0x1e14e, 0x1e290, 322},
+ {0x1e291, 0x1e2ad, 1},
+ {0x1e2c0, 0x1e2eb, 1},
+ {0x1e4d0, 0x1e4eb, 1},
+ {0x1e7e0, 0x1e7e6, 1},
+ {0x1e7e8, 0x1e7eb, 1},
+ {0x1e7ed, 0x1e7ee, 1},
+ {0x1e7f0, 0x1e7fe, 1},
{0x1e800, 0x1e8c4, 1},
{0x1e900, 0x1e943, 1},
{0x1e94b, 0x1ee00, 1205},
@@ -706,13 +744,14 @@ var _L = &RangeTable{
{0x1eea1, 0x1eea3, 1},
{0x1eea5, 0x1eea9, 1},
{0x1eeab, 0x1eebb, 1},
- {0x20000, 0x2a6dd, 1},
- {0x2a700, 0x2b734, 1},
+ {0x20000, 0x2a6df, 1},
+ {0x2a700, 0x2b739, 1},
{0x2b740, 0x2b81d, 1},
{0x2b820, 0x2cea1, 1},
{0x2ceb0, 0x2ebe0, 1},
{0x2f800, 0x2fa1d, 1},
{0x30000, 0x3134a, 1},
+ {0x31350, 0x323af, 1},
},
LatinOffset: 6,
}
@@ -807,7 +846,7 @@ var _Ll = &RangeTable{
{0x213c, 0x213d, 1},
{0x2146, 0x2149, 1},
{0x214e, 0x2184, 54},
- {0x2c30, 0x2c5e, 1},
+ {0x2c30, 0x2c5f, 1},
{0x2c61, 0x2c65, 4},
{0x2c66, 0x2c6c, 2},
{0x2c71, 0x2c73, 2},
@@ -831,11 +870,11 @@ var _Ll = &RangeTable{
{0xa794, 0xa795, 1},
{0xa797, 0xa7a9, 2},
{0xa7af, 0xa7b5, 6},
- {0xa7b7, 0xa7bf, 2},
- {0xa7c3, 0xa7c8, 5},
- {0xa7ca, 0xa7f6, 44},
- {0xa7fa, 0xab30, 822},
- {0xab31, 0xab5a, 1},
+ {0xa7b7, 0xa7c3, 2},
+ {0xa7c8, 0xa7ca, 2},
+ {0xa7d1, 0xa7d9, 2},
+ {0xa7f6, 0xa7fa, 4},
+ {0xab30, 0xab5a, 1},
{0xab60, 0xab68, 1},
{0xab70, 0xabbf, 1},
{0xfb00, 0xfb06, 1},
@@ -845,6 +884,10 @@ var _Ll = &RangeTable{
R32: []Range32{
{0x10428, 0x1044f, 1},
{0x104d8, 0x104fb, 1},
+ {0x10597, 0x105a1, 1},
+ {0x105a3, 0x105b1, 1},
+ {0x105b3, 0x105b9, 1},
+ {0x105bb, 0x105bc, 1},
{0x10cc0, 0x10cf2, 1},
{0x118c0, 0x118df, 1},
{0x16e60, 0x16e7f, 1},
@@ -875,8 +918,11 @@ var _Ll = &RangeTable{
{0x1d78a, 0x1d78f, 1},
{0x1d7aa, 0x1d7c2, 1},
{0x1d7c4, 0x1d7c9, 1},
- {0x1d7cb, 0x1e922, 4439},
- {0x1e923, 0x1e943, 1},
+ {0x1d7cb, 0x1df00, 1845},
+ {0x1df01, 0x1df09, 1},
+ {0x1df0b, 0x1df1e, 1},
+ {0x1df25, 0x1df2a, 1},
+ {0x1e922, 0x1e943, 1},
},
LatinOffset: 4,
}
@@ -893,11 +939,11 @@ var _Lm = &RangeTable{
{0x07f4, 0x07f5, 1},
{0x07fa, 0x081a, 32},
{0x0824, 0x0828, 4},
- {0x0971, 0x0e46, 1237},
- {0x0ec6, 0x10fc, 566},
- {0x17d7, 0x1843, 108},
- {0x1aa7, 0x1c78, 465},
- {0x1c79, 0x1c7d, 1},
+ {0x08c9, 0x0971, 168},
+ {0x0e46, 0x0ec6, 128},
+ {0x10fc, 0x17d7, 1755},
+ {0x1843, 0x1aa7, 612},
+ {0x1c78, 0x1c7d, 1},
{0x1d2c, 0x1d6a, 1},
{0x1d78, 0x1d9b, 35},
{0x1d9c, 0x1dbf, 1},
@@ -916,6 +962,7 @@ var _Lm = &RangeTable{
{0xa69c, 0xa69d, 1},
{0xa717, 0xa71f, 1},
{0xa770, 0xa788, 24},
+ {0xa7f2, 0xa7f4, 1},
{0xa7f8, 0xa7f9, 1},
{0xa9cf, 0xa9e6, 23},
{0xaa70, 0xaadd, 109},
@@ -925,12 +972,19 @@ var _Lm = &RangeTable{
{0xff9e, 0xff9f, 1},
},
R32: []Range32{
+ {0x10780, 0x10785, 1},
+ {0x10787, 0x107b0, 1},
+ {0x107b2, 0x107ba, 1},
{0x16b40, 0x16b43, 1},
{0x16f93, 0x16f9f, 1},
{0x16fe0, 0x16fe1, 1},
- {0x16fe3, 0x1e137, 29012},
- {0x1e138, 0x1e13d, 1},
- {0x1e94b, 0x1e94b, 1},
+ {0x16fe3, 0x1aff0, 16397},
+ {0x1aff1, 0x1aff3, 1},
+ {0x1aff5, 0x1affb, 1},
+ {0x1affd, 0x1affe, 1},
+ {0x1e030, 0x1e06d, 1},
+ {0x1e137, 0x1e13d, 1},
+ {0x1e4eb, 0x1e94b, 1120},
},
}
@@ -957,8 +1011,9 @@ var _Lo = &RangeTable{
{0x0800, 0x0815, 1},
{0x0840, 0x0858, 1},
{0x0860, 0x086a, 1},
- {0x08a0, 0x08b4, 1},
- {0x08b6, 0x08c7, 1},
+ {0x0870, 0x0887, 1},
+ {0x0889, 0x088e, 1},
+ {0x08a0, 0x08c8, 1},
{0x0904, 0x0939, 1},
{0x093d, 0x0950, 19},
{0x0958, 0x0961, 1},
@@ -1019,17 +1074,18 @@ var _Lo = &RangeTable{
{0x0c2a, 0x0c39, 1},
{0x0c3d, 0x0c58, 27},
{0x0c59, 0x0c5a, 1},
- {0x0c60, 0x0c61, 1},
- {0x0c80, 0x0c85, 5},
- {0x0c86, 0x0c8c, 1},
+ {0x0c5d, 0x0c60, 3},
+ {0x0c61, 0x0c80, 31},
+ {0x0c85, 0x0c8c, 1},
{0x0c8e, 0x0c90, 1},
{0x0c92, 0x0ca8, 1},
{0x0caa, 0x0cb3, 1},
{0x0cb5, 0x0cb9, 1},
- {0x0cbd, 0x0cde, 33},
- {0x0ce0, 0x0ce1, 1},
- {0x0cf1, 0x0cf2, 1},
- {0x0d04, 0x0d0c, 1},
+ {0x0cbd, 0x0cdd, 32},
+ {0x0cde, 0x0ce0, 2},
+ {0x0ce1, 0x0cf1, 16},
+ {0x0cf2, 0x0d04, 18},
+ {0x0d05, 0x0d0c, 1},
{0x0d0e, 0x0d10, 1},
{0x0d12, 0x0d3a, 1},
{0x0d3d, 0x0d4e, 17},
@@ -1089,9 +1145,8 @@ var _Lo = &RangeTable{
{0x1681, 0x169a, 1},
{0x16a0, 0x16ea, 1},
{0x16f1, 0x16f8, 1},
- {0x1700, 0x170c, 1},
- {0x170e, 0x1711, 1},
- {0x1720, 0x1731, 1},
+ {0x1700, 0x1711, 1},
+ {0x171f, 0x1731, 1},
{0x1740, 0x1751, 1},
{0x1760, 0x176c, 1},
{0x176e, 0x1770, 1},
@@ -1111,7 +1166,7 @@ var _Lo = &RangeTable{
{0x1a00, 0x1a16, 1},
{0x1a20, 0x1a54, 1},
{0x1b05, 0x1b33, 1},
- {0x1b45, 0x1b4b, 1},
+ {0x1b45, 0x1b4c, 1},
{0x1b83, 0x1ba0, 1},
{0x1bae, 0x1baf, 1},
{0x1bba, 0x1be5, 1},
@@ -1143,8 +1198,7 @@ var _Lo = &RangeTable{
{0x31a0, 0x31bf, 1},
{0x31f0, 0x31ff, 1},
{0x3400, 0x4dbf, 1},
- {0x4e00, 0x9ffc, 1},
- {0xa000, 0xa014, 1},
+ {0x4e00, 0xa014, 1},
{0xa016, 0xa48c, 1},
{0xa4d0, 0xa4f7, 1},
{0xa500, 0xa60b, 1},
@@ -1272,10 +1326,13 @@ var _Lo = &RangeTable{
{0x10f00, 0x10f1c, 1},
{0x10f27, 0x10f30, 9},
{0x10f31, 0x10f45, 1},
+ {0x10f70, 0x10f81, 1},
{0x10fb0, 0x10fc4, 1},
{0x10fe0, 0x10ff6, 1},
{0x11003, 0x11037, 1},
- {0x11083, 0x110af, 1},
+ {0x11071, 0x11072, 1},
+ {0x11075, 0x11083, 14},
+ {0x11084, 0x110af, 1},
{0x110d0, 0x110e8, 1},
{0x11103, 0x11126, 1},
{0x11144, 0x11147, 3},
@@ -1286,6 +1343,7 @@ var _Lo = &RangeTable{
{0x111da, 0x111dc, 2},
{0x11200, 0x11211, 1},
{0x11213, 0x1122b, 1},
+ {0x1123f, 0x11240, 1},
{0x11280, 0x11286, 1},
{0x11288, 0x1128a, 2},
{0x1128b, 0x1128d, 1},
@@ -1313,6 +1371,7 @@ var _Lo = &RangeTable{
{0x11681, 0x116aa, 1},
{0x116b8, 0x11700, 72},
{0x11701, 0x1171a, 1},
+ {0x11740, 0x11746, 1},
{0x11800, 0x1182b, 1},
{0x118ff, 0x11906, 1},
{0x11909, 0x1190c, 3},
@@ -1327,8 +1386,8 @@ var _Lo = &RangeTable{
{0x11a0c, 0x11a32, 1},
{0x11a3a, 0x11a50, 22},
{0x11a5c, 0x11a89, 1},
- {0x11a9d, 0x11ac0, 35},
- {0x11ac1, 0x11af8, 1},
+ {0x11a9d, 0x11ab0, 19},
+ {0x11ab1, 0x11af8, 1},
{0x11c00, 0x11c08, 1},
{0x11c0a, 0x11c2e, 1},
{0x11c40, 0x11c72, 50},
@@ -1342,13 +1401,19 @@ var _Lo = &RangeTable{
{0x11d6a, 0x11d89, 1},
{0x11d98, 0x11ee0, 328},
{0x11ee1, 0x11ef2, 1},
+ {0x11f02, 0x11f04, 2},
+ {0x11f05, 0x11f10, 1},
+ {0x11f12, 0x11f33, 1},
{0x11fb0, 0x12000, 80},
{0x12001, 0x12399, 1},
{0x12480, 0x12543, 1},
- {0x13000, 0x1342e, 1},
+ {0x12f90, 0x12ff0, 1},
+ {0x13000, 0x1342f, 1},
+ {0x13441, 0x13446, 1},
{0x14400, 0x14646, 1},
{0x16800, 0x16a38, 1},
{0x16a40, 0x16a5e, 1},
+ {0x16a70, 0x16abe, 1},
{0x16ad0, 0x16aed, 1},
{0x16b00, 0x16b2f, 1},
{0x16b63, 0x16b77, 1},
@@ -1358,17 +1423,26 @@ var _Lo = &RangeTable{
{0x17001, 0x187f7, 1},
{0x18800, 0x18cd5, 1},
{0x18d00, 0x18d08, 1},
- {0x1b000, 0x1b11e, 1},
- {0x1b150, 0x1b152, 1},
- {0x1b164, 0x1b167, 1},
+ {0x1b000, 0x1b122, 1},
+ {0x1b132, 0x1b150, 30},
+ {0x1b151, 0x1b152, 1},
+ {0x1b155, 0x1b164, 15},
+ {0x1b165, 0x1b167, 1},
{0x1b170, 0x1b2fb, 1},
{0x1bc00, 0x1bc6a, 1},
{0x1bc70, 0x1bc7c, 1},
{0x1bc80, 0x1bc88, 1},
{0x1bc90, 0x1bc99, 1},
- {0x1e100, 0x1e12c, 1},
- {0x1e14e, 0x1e2c0, 370},
- {0x1e2c1, 0x1e2eb, 1},
+ {0x1df0a, 0x1e100, 502},
+ {0x1e101, 0x1e12c, 1},
+ {0x1e14e, 0x1e290, 322},
+ {0x1e291, 0x1e2ad, 1},
+ {0x1e2c0, 0x1e2eb, 1},
+ {0x1e4d0, 0x1e4ea, 1},
+ {0x1e7e0, 0x1e7e6, 1},
+ {0x1e7e8, 0x1e7eb, 1},
+ {0x1e7ed, 0x1e7ee, 1},
+ {0x1e7f0, 0x1e7fe, 1},
{0x1e800, 0x1e8c4, 1},
{0x1ee00, 0x1ee03, 1},
{0x1ee05, 0x1ee1f, 1},
@@ -1394,13 +1468,14 @@ var _Lo = &RangeTable{
{0x1eea1, 0x1eea3, 1},
{0x1eea5, 0x1eea9, 1},
{0x1eeab, 0x1eebb, 1},
- {0x20000, 0x2a6dd, 1},
- {0x2a700, 0x2b734, 1},
+ {0x20000, 0x2a6df, 1},
+ {0x2a700, 0x2b739, 1},
{0x2b740, 0x2b81d, 1},
{0x2b820, 0x2cea1, 1},
{0x2ceb0, 0x2ebe0, 1},
{0x2f800, 0x2fa1d, 1},
{0x30000, 0x3134a, 1},
+ {0x31350, 0x323af, 1},
},
LatinOffset: 1,
}
@@ -1501,7 +1576,7 @@ var _Lu = &RangeTable{
{0x2130, 0x2133, 1},
{0x213e, 0x213f, 1},
{0x2145, 0x2183, 62},
- {0x2c00, 0x2c2e, 1},
+ {0x2c00, 0x2c2f, 1},
{0x2c60, 0x2c62, 2},
{0x2c63, 0x2c64, 1},
{0x2c67, 0x2c6d, 2},
@@ -1522,15 +1597,20 @@ var _Lu = &RangeTable{
{0xa796, 0xa7aa, 2},
{0xa7ab, 0xa7ae, 1},
{0xa7b0, 0xa7b4, 1},
- {0xa7b6, 0xa7be, 2},
- {0xa7c2, 0xa7c4, 2},
+ {0xa7b6, 0xa7c4, 2},
{0xa7c5, 0xa7c7, 1},
- {0xa7c9, 0xa7f5, 44},
- {0xff21, 0xff3a, 1},
+ {0xa7c9, 0xa7d0, 7},
+ {0xa7d6, 0xa7d8, 2},
+ {0xa7f5, 0xff21, 22316},
+ {0xff22, 0xff3a, 1},
},
R32: []Range32{
{0x10400, 0x10427, 1},
{0x104b0, 0x104d3, 1},
+ {0x10570, 0x1057a, 1},
+ {0x1057c, 0x1058a, 1},
+ {0x1058c, 0x10592, 1},
+ {0x10594, 0x10595, 1},
{0x10c80, 0x10cb2, 1},
{0x118a0, 0x118bf, 1},
{0x16e40, 0x16e5f, 1},
@@ -1594,7 +1674,8 @@ var _M = &RangeTable{
{0x0825, 0x0827, 1},
{0x0829, 0x082d, 1},
{0x0859, 0x085b, 1},
- {0x08d3, 0x08e1, 1},
+ {0x0898, 0x089f, 1},
+ {0x08ca, 0x08e1, 1},
{0x08e3, 0x0903, 1},
{0x093a, 0x093c, 1},
{0x093e, 0x094f, 1},
@@ -1634,7 +1715,8 @@ var _M = &RangeTable{
{0x0bca, 0x0bcd, 1},
{0x0bd7, 0x0c00, 41},
{0x0c01, 0x0c04, 1},
- {0x0c3e, 0x0c44, 1},
+ {0x0c3c, 0x0c3e, 2},
+ {0x0c3f, 0x0c44, 1},
{0x0c46, 0x0c48, 1},
{0x0c4a, 0x0c4d, 1},
{0x0c55, 0x0c56, 1},
@@ -1646,7 +1728,8 @@ var _M = &RangeTable{
{0x0cca, 0x0ccd, 1},
{0x0cd5, 0x0cd6, 1},
{0x0ce2, 0x0ce3, 1},
- {0x0d00, 0x0d03, 1},
+ {0x0cf3, 0x0d00, 13},
+ {0x0d01, 0x0d03, 1},
{0x0d3b, 0x0d3c, 1},
{0x0d3e, 0x0d44, 1},
{0x0d46, 0x0d48, 1},
@@ -1664,7 +1747,7 @@ var _M = &RangeTable{
{0x0e47, 0x0e4e, 1},
{0x0eb1, 0x0eb4, 3},
{0x0eb5, 0x0ebc, 1},
- {0x0ec8, 0x0ecd, 1},
+ {0x0ec8, 0x0ece, 1},
{0x0f18, 0x0f19, 1},
{0x0f35, 0x0f39, 2},
{0x0f3e, 0x0f3f, 1},
@@ -1683,22 +1766,22 @@ var _M = &RangeTable{
{0x108f, 0x109a, 11},
{0x109b, 0x109d, 1},
{0x135d, 0x135f, 1},
- {0x1712, 0x1714, 1},
+ {0x1712, 0x1715, 1},
{0x1732, 0x1734, 1},
{0x1752, 0x1753, 1},
{0x1772, 0x1773, 1},
{0x17b4, 0x17d3, 1},
{0x17dd, 0x180b, 46},
{0x180c, 0x180d, 1},
- {0x1885, 0x1886, 1},
- {0x18a9, 0x1920, 119},
- {0x1921, 0x192b, 1},
+ {0x180f, 0x1885, 118},
+ {0x1886, 0x18a9, 35},
+ {0x1920, 0x192b, 1},
{0x1930, 0x193b, 1},
{0x1a17, 0x1a1b, 1},
{0x1a55, 0x1a5e, 1},
{0x1a60, 0x1a7c, 1},
{0x1a7f, 0x1ab0, 49},
- {0x1ab1, 0x1ac0, 1},
+ {0x1ab1, 0x1ace, 1},
{0x1b00, 0x1b04, 1},
{0x1b34, 0x1b44, 1},
{0x1b6b, 0x1b73, 1},
@@ -1710,8 +1793,7 @@ var _M = &RangeTable{
{0x1cd4, 0x1ce8, 1},
{0x1ced, 0x1cf4, 7},
{0x1cf7, 0x1cf9, 1},
- {0x1dc0, 0x1df9, 1},
- {0x1dfb, 0x1dff, 1},
+ {0x1dc0, 0x1dff, 1},
{0x20d0, 0x20f0, 1},
{0x2cef, 0x2cf1, 1},
{0x2d7f, 0x2de0, 97},
@@ -1763,12 +1845,17 @@ var _M = &RangeTable{
{0x10ae6, 0x10d24, 574},
{0x10d25, 0x10d27, 1},
{0x10eab, 0x10eac, 1},
+ {0x10efd, 0x10eff, 1},
{0x10f46, 0x10f50, 1},
+ {0x10f82, 0x10f85, 1},
{0x11000, 0x11002, 1},
{0x11038, 0x11046, 1},
- {0x1107f, 0x11082, 1},
+ {0x11070, 0x11073, 3},
+ {0x11074, 0x1107f, 11},
+ {0x11080, 0x11082, 1},
{0x110b0, 0x110ba, 1},
- {0x11100, 0x11102, 1},
+ {0x110c2, 0x11100, 62},
+ {0x11101, 0x11102, 1},
{0x11127, 0x11134, 1},
{0x11145, 0x11146, 1},
{0x11173, 0x11180, 13},
@@ -1777,8 +1864,8 @@ var _M = &RangeTable{
{0x111c9, 0x111cc, 1},
{0x111ce, 0x111cf, 1},
{0x1122c, 0x11237, 1},
- {0x1123e, 0x112df, 161},
- {0x112e0, 0x112ea, 1},
+ {0x1123e, 0x11241, 3},
+ {0x112df, 0x112ea, 1},
{0x11300, 0x11303, 1},
{0x1133b, 0x1133c, 1},
{0x1133e, 0x11344, 1},
@@ -1825,6 +1912,12 @@ var _M = &RangeTable{
{0x11d90, 0x11d91, 1},
{0x11d93, 0x11d97, 1},
{0x11ef3, 0x11ef6, 1},
+ {0x11f00, 0x11f01, 1},
+ {0x11f03, 0x11f34, 49},
+ {0x11f35, 0x11f3a, 1},
+ {0x11f3e, 0x11f42, 1},
+ {0x13440, 0x13447, 7},
+ {0x13448, 0x13455, 1},
{0x16af0, 0x16af4, 1},
{0x16b30, 0x16b36, 1},
{0x16f4f, 0x16f51, 2},
@@ -1832,8 +1925,10 @@ var _M = &RangeTable{
{0x16f8f, 0x16f92, 1},
{0x16fe4, 0x16ff0, 12},
{0x16ff1, 0x1bc9d, 19628},
- {0x1bc9e, 0x1d165, 5319},
- {0x1d166, 0x1d169, 1},
+ {0x1bc9e, 0x1cf00, 4706},
+ {0x1cf01, 0x1cf2d, 1},
+ {0x1cf30, 0x1cf46, 1},
+ {0x1d165, 0x1d169, 1},
{0x1d16d, 0x1d172, 1},
{0x1d17b, 0x1d182, 1},
{0x1d185, 0x1d18b, 1},
@@ -1849,8 +1944,11 @@ var _M = &RangeTable{
{0x1e01b, 0x1e021, 1},
{0x1e023, 0x1e024, 1},
{0x1e026, 0x1e02a, 1},
- {0x1e130, 0x1e136, 1},
- {0x1e2ec, 0x1e2ef, 1},
+ {0x1e08f, 0x1e130, 161},
+ {0x1e131, 0x1e136, 1},
+ {0x1e2ae, 0x1e2ec, 62},
+ {0x1e2ed, 0x1e2ef, 1},
+ {0x1e4ec, 0x1e4ef, 1},
{0x1e8d0, 0x1e8d6, 1},
{0x1e944, 0x1e94a, 1},
{0xe0100, 0xe01ef, 1},
@@ -1890,8 +1988,9 @@ var _Mc = &RangeTable{
{0x0cc7, 0x0cc8, 1},
{0x0cca, 0x0ccb, 1},
{0x0cd5, 0x0cd6, 1},
- {0x0d02, 0x0d03, 1},
- {0x0d3e, 0x0d40, 1},
+ {0x0cf3, 0x0d02, 15},
+ {0x0d03, 0x0d3e, 59},
+ {0x0d3f, 0x0d40, 1},
{0x0d46, 0x0d48, 1},
{0x0d4a, 0x0d4c, 1},
{0x0d57, 0x0d82, 43},
@@ -1911,6 +2010,7 @@ var _Mc = &RangeTable{
{0x1087, 0x108c, 1},
{0x108f, 0x109a, 11},
{0x109b, 0x109c, 1},
+ {0x1715, 0x1734, 31},
{0x17b6, 0x17be, 8},
{0x17bf, 0x17c5, 1},
{0x17c7, 0x17c8, 1},
@@ -2009,7 +2109,10 @@ var _Mc = &RangeTable{
{0x11d8a, 0x11d8e, 1},
{0x11d93, 0x11d94, 1},
{0x11d96, 0x11ef5, 351},
- {0x11ef6, 0x16f51, 20571},
+ {0x11ef6, 0x11f03, 13},
+ {0x11f34, 0x11f35, 1},
+ {0x11f3e, 0x11f3f, 1},
+ {0x11f41, 0x16f51, 20496},
{0x16f52, 0x16f87, 1},
{0x16ff0, 0x16ff1, 1},
{0x1d165, 0x1d166, 1},
@@ -2052,7 +2155,8 @@ var _Mn = &RangeTable{
{0x0825, 0x0827, 1},
{0x0829, 0x082d, 1},
{0x0859, 0x085b, 1},
- {0x08d3, 0x08e1, 1},
+ {0x0898, 0x089f, 1},
+ {0x08ca, 0x08e1, 1},
{0x08e3, 0x0902, 1},
{0x093a, 0x093c, 2},
{0x0941, 0x0948, 1},
@@ -2085,7 +2189,8 @@ var _Mn = &RangeTable{
{0x0b63, 0x0b82, 31},
{0x0bc0, 0x0bcd, 13},
{0x0c00, 0x0c04, 4},
- {0x0c3e, 0x0c40, 1},
+ {0x0c3c, 0x0c3e, 2},
+ {0x0c3f, 0x0c40, 1},
{0x0c46, 0x0c48, 1},
{0x0c4a, 0x0c4d, 1},
{0x0c55, 0x0c56, 1},
@@ -2106,7 +2211,7 @@ var _Mn = &RangeTable{
{0x0e47, 0x0e4e, 1},
{0x0eb1, 0x0eb4, 3},
{0x0eb5, 0x0ebc, 1},
- {0x0ec8, 0x0ecd, 1},
+ {0x0ec8, 0x0ece, 1},
{0x0f18, 0x0f19, 1},
{0x0f35, 0x0f39, 2},
{0x0f71, 0x0f7e, 1},
@@ -2127,7 +2232,7 @@ var _Mn = &RangeTable{
{0x109d, 0x135d, 704},
{0x135e, 0x135f, 1},
{0x1712, 0x1714, 1},
- {0x1732, 0x1734, 1},
+ {0x1732, 0x1733, 1},
{0x1752, 0x1753, 1},
{0x1772, 0x1773, 1},
{0x17b4, 0x17b5, 1},
@@ -2136,9 +2241,9 @@ var _Mn = &RangeTable{
{0x17ca, 0x17d3, 1},
{0x17dd, 0x180b, 46},
{0x180c, 0x180d, 1},
- {0x1885, 0x1886, 1},
- {0x18a9, 0x1920, 119},
- {0x1921, 0x1922, 1},
+ {0x180f, 0x1885, 118},
+ {0x1886, 0x18a9, 35},
+ {0x1920, 0x1922, 1},
{0x1927, 0x1928, 1},
{0x1932, 0x1939, 7},
{0x193a, 0x193b, 1},
@@ -2150,7 +2255,7 @@ var _Mn = &RangeTable{
{0x1a73, 0x1a7c, 1},
{0x1a7f, 0x1ab0, 49},
{0x1ab1, 0x1abd, 1},
- {0x1abf, 0x1ac0, 1},
+ {0x1abf, 0x1ace, 1},
{0x1b00, 0x1b03, 1},
{0x1b34, 0x1b36, 2},
{0x1b37, 0x1b3a, 1},
@@ -2170,8 +2275,7 @@ var _Mn = &RangeTable{
{0x1ce2, 0x1ce8, 1},
{0x1ced, 0x1cf4, 7},
{0x1cf8, 0x1cf9, 1},
- {0x1dc0, 0x1df9, 1},
- {0x1dfb, 0x1dff, 1},
+ {0x1dc0, 0x1dff, 1},
{0x20d0, 0x20dc, 1},
{0x20e1, 0x20e5, 4},
{0x20e6, 0x20f0, 1},
@@ -2223,13 +2327,18 @@ var _Mn = &RangeTable{
{0x10ae6, 0x10d24, 574},
{0x10d25, 0x10d27, 1},
{0x10eab, 0x10eac, 1},
+ {0x10efd, 0x10eff, 1},
{0x10f46, 0x10f50, 1},
+ {0x10f82, 0x10f85, 1},
{0x11001, 0x11038, 55},
{0x11039, 0x11046, 1},
- {0x1107f, 0x11081, 1},
+ {0x11070, 0x11073, 3},
+ {0x11074, 0x1107f, 11},
+ {0x11080, 0x11081, 1},
{0x110b3, 0x110b6, 1},
{0x110b9, 0x110ba, 1},
- {0x11100, 0x11102, 1},
+ {0x110c2, 0x11100, 62},
+ {0x11101, 0x11102, 1},
{0x11127, 0x1112b, 1},
{0x1112d, 0x11134, 1},
{0x11173, 0x11180, 13},
@@ -2240,8 +2349,8 @@ var _Mn = &RangeTable{
{0x11230, 0x11231, 1},
{0x11234, 0x11236, 2},
{0x11237, 0x1123e, 7},
- {0x112df, 0x112e3, 4},
- {0x112e4, 0x112ea, 1},
+ {0x11241, 0x112df, 158},
+ {0x112e3, 0x112ea, 1},
{0x11300, 0x11301, 1},
{0x1133b, 0x1133c, 1},
{0x11340, 0x11366, 38},
@@ -2296,14 +2405,21 @@ var _Mn = &RangeTable{
{0x11d47, 0x11d90, 73},
{0x11d91, 0x11d95, 4},
{0x11d97, 0x11ef3, 348},
- {0x11ef4, 0x16af0, 19452},
- {0x16af1, 0x16af4, 1},
+ {0x11ef4, 0x11f00, 12},
+ {0x11f01, 0x11f36, 53},
+ {0x11f37, 0x11f3a, 1},
+ {0x11f40, 0x11f42, 2},
+ {0x13440, 0x13447, 7},
+ {0x13448, 0x13455, 1},
+ {0x16af0, 0x16af4, 1},
{0x16b30, 0x16b36, 1},
{0x16f4f, 0x16f8f, 64},
{0x16f90, 0x16f92, 1},
{0x16fe4, 0x1bc9d, 19641},
- {0x1bc9e, 0x1d167, 5321},
- {0x1d168, 0x1d169, 1},
+ {0x1bc9e, 0x1cf00, 4706},
+ {0x1cf01, 0x1cf2d, 1},
+ {0x1cf30, 0x1cf46, 1},
+ {0x1d167, 0x1d169, 1},
{0x1d17b, 0x1d182, 1},
{0x1d185, 0x1d18b, 1},
{0x1d1aa, 0x1d1ad, 1},
@@ -2318,8 +2434,11 @@ var _Mn = &RangeTable{
{0x1e01b, 0x1e021, 1},
{0x1e023, 0x1e024, 1},
{0x1e026, 0x1e02a, 1},
- {0x1e130, 0x1e136, 1},
- {0x1e2ec, 0x1e2ef, 1},
+ {0x1e08f, 0x1e130, 161},
+ {0x1e131, 0x1e136, 1},
+ {0x1e2ae, 0x1e2ec, 62},
+ {0x1e2ed, 0x1e2ef, 1},
+ {0x1e4ec, 0x1e4ef, 1},
{0x1e8d0, 0x1e8d6, 1},
{0x1e944, 0x1e94a, 1},
{0xe0100, 0xe01ef, 1},
@@ -2441,17 +2560,21 @@ var _N = &RangeTable{
{0x11c50, 0x11c6c, 1},
{0x11d50, 0x11d59, 1},
{0x11da0, 0x11da9, 1},
+ {0x11f50, 0x11f59, 1},
{0x11fc0, 0x11fd4, 1},
{0x12400, 0x1246e, 1},
{0x16a60, 0x16a69, 1},
+ {0x16ac0, 0x16ac9, 1},
{0x16b50, 0x16b59, 1},
{0x16b5b, 0x16b61, 1},
{0x16e80, 0x16e96, 1},
+ {0x1d2c0, 0x1d2d3, 1},
{0x1d2e0, 0x1d2f3, 1},
{0x1d360, 0x1d378, 1},
{0x1d7ce, 0x1d7ff, 1},
{0x1e140, 0x1e149, 1},
{0x1e2f0, 0x1e2f9, 1},
+ {0x1e4f0, 0x1e4f9, 1},
{0x1e8c7, 0x1e8cf, 1},
{0x1e950, 0x1e959, 1},
{0x1ec71, 0x1ecab, 1},
@@ -2523,11 +2646,14 @@ var _Nd = &RangeTable{
{0x11c50, 0x11c59, 1},
{0x11d50, 0x11d59, 1},
{0x11da0, 0x11da9, 1},
+ {0x11f50, 0x11f59, 1},
{0x16a60, 0x16a69, 1},
+ {0x16ac0, 0x16ac9, 1},
{0x16b50, 0x16b59, 1},
{0x1d7ce, 0x1d7ff, 1},
{0x1e140, 0x1e149, 1},
{0x1e2f0, 0x1e2f9, 1},
+ {0x1e4f0, 0x1e4f9, 1},
{0x1e950, 0x1e959, 1},
{0x1fbf0, 0x1fbf9, 1},
},
@@ -2617,6 +2743,7 @@ var _No = &RangeTable{
{0x11fc0, 0x11fd4, 1},
{0x16b5b, 0x16b61, 1},
{0x16e80, 0x16e96, 1},
+ {0x1d2c0, 0x1d2d3, 1},
{0x1d2e0, 0x1d2f3, 1},
{0x1d360, 0x1d378, 1},
{0x1e8c7, 0x1e8cf, 1},
@@ -2651,9 +2778,9 @@ var _P = &RangeTable{
{0x05f3, 0x05f4, 1},
{0x0609, 0x060a, 1},
{0x060c, 0x060d, 1},
- {0x061b, 0x061e, 3},
- {0x061f, 0x066a, 75},
- {0x066b, 0x066d, 1},
+ {0x061b, 0x061d, 2},
+ {0x061e, 0x061f, 1},
+ {0x066a, 0x066d, 1},
{0x06d4, 0x0700, 44},
{0x0701, 0x070d, 1},
{0x07f7, 0x07f9, 1},
@@ -2686,6 +2813,7 @@ var _P = &RangeTable{
{0x1aa0, 0x1aa6, 1},
{0x1aa8, 0x1aad, 1},
{0x1b5a, 0x1b60, 1},
+ {0x1b7d, 0x1b7e, 1},
{0x1bfc, 0x1bff, 1},
{0x1c3b, 0x1c3f, 1},
{0x1c7e, 0x1c7f, 1},
@@ -2710,8 +2838,8 @@ var _P = &RangeTable{
{0x2d70, 0x2e00, 144},
{0x2e01, 0x2e2e, 1},
{0x2e30, 0x2e4f, 1},
- {0x2e52, 0x3001, 431},
- {0x3002, 0x3003, 1},
+ {0x2e52, 0x2e5d, 1},
+ {0x3001, 0x3003, 1},
{0x3008, 0x3011, 1},
{0x3014, 0x301f, 1},
{0x3030, 0x303d, 13},
@@ -2759,6 +2887,7 @@ var _P = &RangeTable{
{0x10b99, 0x10b9c, 1},
{0x10ead, 0x10f55, 168},
{0x10f56, 0x10f59, 1},
+ {0x10f86, 0x10f89, 1},
{0x11047, 0x1104d, 1},
{0x110bb, 0x110bc, 1},
{0x110be, 0x110c1, 1},
@@ -2775,18 +2904,22 @@ var _P = &RangeTable{
{0x115c1, 0x115d7, 1},
{0x11641, 0x11643, 1},
{0x11660, 0x1166c, 1},
- {0x1173c, 0x1173e, 1},
+ {0x116b9, 0x1173c, 131},
+ {0x1173d, 0x1173e, 1},
{0x1183b, 0x11944, 265},
{0x11945, 0x11946, 1},
{0x119e2, 0x11a3f, 93},
{0x11a40, 0x11a46, 1},
{0x11a9a, 0x11a9c, 1},
{0x11a9e, 0x11aa2, 1},
+ {0x11b00, 0x11b09, 1},
{0x11c41, 0x11c45, 1},
{0x11c70, 0x11c71, 1},
{0x11ef7, 0x11ef8, 1},
+ {0x11f43, 0x11f4f, 1},
{0x11fff, 0x12470, 1137},
{0x12471, 0x12474, 1},
+ {0x12ff1, 0x12ff2, 1},
{0x16a6e, 0x16a6f, 1},
{0x16af5, 0x16b37, 66},
{0x16b38, 0x16b3b, 1},
@@ -2817,11 +2950,11 @@ var _Pd = &RangeTable{
{0x2011, 0x2015, 1},
{0x2e17, 0x2e1a, 3},
{0x2e3a, 0x2e3b, 1},
- {0x2e40, 0x301c, 476},
- {0x3030, 0x30a0, 112},
- {0xfe31, 0xfe32, 1},
- {0xfe58, 0xfe63, 11},
- {0xff0d, 0xff0d, 1},
+ {0x2e40, 0x2e5d, 29},
+ {0x301c, 0x3030, 20},
+ {0x30a0, 0xfe31, 52625},
+ {0xfe32, 0xfe58, 38},
+ {0xfe63, 0xff0d, 170},
},
R32: []Range32{
{0x10ead, 0x10ead, 1},
@@ -2843,6 +2976,7 @@ var _Pe = &RangeTable{
{0x29d9, 0x29db, 2},
{0x29fd, 0x2e23, 1062},
{0x2e25, 0x2e29, 2},
+ {0x2e56, 0x2e5c, 2},
{0x3009, 0x3011, 2},
{0x3015, 0x301b, 2},
{0x301e, 0x301f, 1},
@@ -2895,9 +3029,9 @@ var _Po = &RangeTable{
{0x05f3, 0x05f4, 1},
{0x0609, 0x060a, 1},
{0x060c, 0x060d, 1},
- {0x061b, 0x061e, 3},
- {0x061f, 0x066a, 75},
- {0x066b, 0x066d, 1},
+ {0x061b, 0x061d, 2},
+ {0x061e, 0x061f, 1},
+ {0x066a, 0x066d, 1},
{0x06d4, 0x0700, 44},
{0x0701, 0x070d, 1},
{0x07f7, 0x07f9, 1},
@@ -2928,6 +3062,7 @@ var _Po = &RangeTable{
{0x1aa0, 0x1aa6, 1},
{0x1aa8, 0x1aad, 1},
{0x1b5a, 0x1b60, 1},
+ {0x1b7d, 0x1b7e, 1},
{0x1bfc, 0x1bff, 1},
{0x1c3b, 0x1c3f, 1},
{0x1c7e, 0x1c7f, 1},
@@ -2956,8 +3091,8 @@ var _Po = &RangeTable{
{0x2e3c, 0x2e3f, 1},
{0x2e41, 0x2e43, 2},
{0x2e44, 0x2e4f, 1},
- {0x2e52, 0x3001, 431},
- {0x3002, 0x3003, 1},
+ {0x2e52, 0x2e54, 1},
+ {0x3001, 0x3003, 1},
{0x303d, 0x30fb, 190},
{0xa4fe, 0xa4ff, 1},
{0xa60d, 0xa60f, 1},
@@ -3003,6 +3138,7 @@ var _Po = &RangeTable{
{0x10b39, 0x10b3f, 1},
{0x10b99, 0x10b9c, 1},
{0x10f55, 0x10f59, 1},
+ {0x10f86, 0x10f89, 1},
{0x11047, 0x1104d, 1},
{0x110bb, 0x110bc, 1},
{0x110be, 0x110c1, 1},
@@ -3019,18 +3155,22 @@ var _Po = &RangeTable{
{0x115c1, 0x115d7, 1},
{0x11641, 0x11643, 1},
{0x11660, 0x1166c, 1},
- {0x1173c, 0x1173e, 1},
+ {0x116b9, 0x1173c, 131},
+ {0x1173d, 0x1173e, 1},
{0x1183b, 0x11944, 265},
{0x11945, 0x11946, 1},
{0x119e2, 0x11a3f, 93},
{0x11a40, 0x11a46, 1},
{0x11a9a, 0x11a9c, 1},
{0x11a9e, 0x11aa2, 1},
+ {0x11b00, 0x11b09, 1},
{0x11c41, 0x11c45, 1},
{0x11c70, 0x11c71, 1},
{0x11ef7, 0x11ef8, 1},
+ {0x11f43, 0x11f4f, 1},
{0x11fff, 0x12470, 1137},
{0x12471, 0x12474, 1},
+ {0x12ff1, 0x12ff2, 1},
{0x16a6e, 0x16a6f, 1},
{0x16af5, 0x16b37, 66},
{0x16b38, 0x16b3b, 1},
@@ -3059,8 +3199,9 @@ var _Ps = &RangeTable{
{0x29d8, 0x29da, 2},
{0x29fc, 0x2e22, 1062},
{0x2e24, 0x2e28, 2},
- {0x2e42, 0x3008, 454},
- {0x300a, 0x3010, 2},
+ {0x2e42, 0x2e55, 19},
+ {0x2e57, 0x2e5b, 2},
+ {0x3008, 0x3010, 2},
{0x3014, 0x301a, 2},
{0x301d, 0xfd3f, 52514},
{0xfe17, 0xfe35, 30},
@@ -3101,10 +3242,11 @@ var _S = &RangeTable{
{0x06e9, 0x06fd, 20},
{0x06fe, 0x07f6, 248},
{0x07fe, 0x07ff, 1},
- {0x09f2, 0x09f3, 1},
- {0x09fa, 0x09fb, 1},
- {0x0af1, 0x0b70, 127},
- {0x0bf3, 0x0bfa, 1},
+ {0x0888, 0x09f2, 362},
+ {0x09f3, 0x09fa, 7},
+ {0x09fb, 0x0af1, 246},
+ {0x0b70, 0x0bf3, 131},
+ {0x0bf4, 0x0bfa, 1},
{0x0c7f, 0x0d4f, 208},
{0x0d79, 0x0e3f, 198},
{0x0f01, 0x0f03, 1},
@@ -3132,7 +3274,7 @@ var _S = &RangeTable{
{0x2044, 0x2052, 14},
{0x207a, 0x207c, 1},
{0x208a, 0x208c, 1},
- {0x20a0, 0x20bf, 1},
+ {0x20a0, 0x20c0, 1},
{0x2100, 0x2101, 1},
{0x2103, 0x2106, 1},
{0x2108, 0x2109, 1},
@@ -3190,8 +3332,10 @@ var _S = &RangeTable{
{0xaa77, 0xaa79, 1},
{0xab5b, 0xab6a, 15},
{0xab6b, 0xfb29, 20414},
- {0xfbb2, 0xfbc1, 1},
- {0xfdfc, 0xfdfd, 1},
+ {0xfbb2, 0xfbc2, 1},
+ {0xfd40, 0xfd4f, 1},
+ {0xfdcf, 0xfdfc, 45},
+ {0xfdfd, 0xfdff, 1},
{0xfe62, 0xfe64, 2},
{0xfe65, 0xfe66, 1},
{0xfe69, 0xff04, 155},
@@ -3215,13 +3359,14 @@ var _S = &RangeTable{
{0x11fd5, 0x11ff1, 1},
{0x16b3c, 0x16b3f, 1},
{0x16b45, 0x1bc9c, 20823},
+ {0x1cf50, 0x1cfc3, 1},
{0x1d000, 0x1d0f5, 1},
{0x1d100, 0x1d126, 1},
{0x1d129, 0x1d164, 1},
{0x1d16a, 0x1d16c, 1},
{0x1d183, 0x1d184, 1},
{0x1d18c, 0x1d1a9, 1},
- {0x1d1ae, 0x1d1e8, 1},
+ {0x1d1ae, 0x1d1ea, 1},
{0x1d200, 0x1d241, 1},
{0x1d245, 0x1d300, 187},
{0x1d301, 0x1d356, 1},
@@ -3252,28 +3397,27 @@ var _S = &RangeTable{
{0x1f250, 0x1f251, 1},
{0x1f260, 0x1f265, 1},
{0x1f300, 0x1f6d7, 1},
- {0x1f6e0, 0x1f6ec, 1},
+ {0x1f6dc, 0x1f6ec, 1},
{0x1f6f0, 0x1f6fc, 1},
- {0x1f700, 0x1f773, 1},
- {0x1f780, 0x1f7d8, 1},
+ {0x1f700, 0x1f776, 1},
+ {0x1f77b, 0x1f7d9, 1},
{0x1f7e0, 0x1f7eb, 1},
- {0x1f800, 0x1f80b, 1},
+ {0x1f7f0, 0x1f800, 16},
+ {0x1f801, 0x1f80b, 1},
{0x1f810, 0x1f847, 1},
{0x1f850, 0x1f859, 1},
{0x1f860, 0x1f887, 1},
{0x1f890, 0x1f8ad, 1},
{0x1f8b0, 0x1f8b1, 1},
- {0x1f900, 0x1f978, 1},
- {0x1f97a, 0x1f9cb, 1},
- {0x1f9cd, 0x1fa53, 1},
+ {0x1f900, 0x1fa53, 1},
{0x1fa60, 0x1fa6d, 1},
- {0x1fa70, 0x1fa74, 1},
- {0x1fa78, 0x1fa7a, 1},
- {0x1fa80, 0x1fa86, 1},
- {0x1fa90, 0x1faa8, 1},
- {0x1fab0, 0x1fab6, 1},
- {0x1fac0, 0x1fac2, 1},
- {0x1fad0, 0x1fad6, 1},
+ {0x1fa70, 0x1fa7c, 1},
+ {0x1fa80, 0x1fa88, 1},
+ {0x1fa90, 0x1fabd, 1},
+ {0x1fabf, 0x1fac5, 1},
+ {0x1face, 0x1fadb, 1},
+ {0x1fae0, 0x1fae8, 1},
+ {0x1faf0, 0x1faf8, 1},
{0x1fb00, 0x1fb92, 1},
{0x1fb94, 0x1fbca, 1},
},
@@ -3290,7 +3434,7 @@ var _Sc = &RangeTable{
{0x09fb, 0x0af1, 246},
{0x0bf9, 0x0e3f, 582},
{0x17db, 0x20a0, 2245},
- {0x20a1, 0x20bf, 1},
+ {0x20a1, 0x20c0, 1},
{0xa838, 0xfdfc, 21956},
{0xfe69, 0xff04, 155},
{0xffe0, 0xffe1, 1},
@@ -3314,8 +3458,9 @@ var _Sk = &RangeTable{
{0x02ed, 0x02ef, 2},
{0x02f0, 0x02ff, 1},
{0x0375, 0x0384, 15},
- {0x0385, 0x1fbd, 7224},
- {0x1fbf, 0x1fc1, 1},
+ {0x0385, 0x0888, 1283},
+ {0x1fbd, 0x1fbf, 2},
+ {0x1fc0, 0x1fc1, 1},
{0x1fcd, 0x1fcf, 1},
{0x1fdd, 0x1fdf, 1},
{0x1fed, 0x1fef, 1},
@@ -3326,7 +3471,7 @@ var _Sk = &RangeTable{
{0xa789, 0xa78a, 1},
{0xab5b, 0xab6a, 15},
{0xab6b, 0xfbb2, 20551},
- {0xfbb3, 0xfbc1, 1},
+ {0xfbb3, 0xfbc2, 1},
{0xff3e, 0xff40, 2},
{0xffe3, 0xffe3, 1},
},
@@ -3488,10 +3633,12 @@ var _So = &RangeTable{
{0xa836, 0xa837, 1},
{0xa839, 0xaa77, 574},
{0xaa78, 0xaa79, 1},
- {0xfdfd, 0xffe4, 487},
- {0xffe8, 0xffed, 5},
- {0xffee, 0xfffc, 14},
- {0xfffd, 0xfffd, 1},
+ {0xfd40, 0xfd4f, 1},
+ {0xfdcf, 0xfdfd, 46},
+ {0xfdfe, 0xfdff, 1},
+ {0xffe4, 0xffe8, 4},
+ {0xffed, 0xffee, 1},
+ {0xfffc, 0xfffd, 1},
},
R32: []Range32{
{0x10137, 0x1013f, 1},
@@ -3506,13 +3653,14 @@ var _So = &RangeTable{
{0x11fe1, 0x11ff1, 1},
{0x16b3c, 0x16b3f, 1},
{0x16b45, 0x1bc9c, 20823},
+ {0x1cf50, 0x1cfc3, 1},
{0x1d000, 0x1d0f5, 1},
{0x1d100, 0x1d126, 1},
{0x1d129, 0x1d164, 1},
{0x1d16a, 0x1d16c, 1},
{0x1d183, 0x1d184, 1},
{0x1d18c, 0x1d1a9, 1},
- {0x1d1ae, 0x1d1e8, 1},
+ {0x1d1ae, 0x1d1ea, 1},
{0x1d200, 0x1d241, 1},
{0x1d245, 0x1d300, 187},
{0x1d301, 0x1d356, 1},
@@ -3537,28 +3685,27 @@ var _So = &RangeTable{
{0x1f260, 0x1f265, 1},
{0x1f300, 0x1f3fa, 1},
{0x1f400, 0x1f6d7, 1},
- {0x1f6e0, 0x1f6ec, 1},
+ {0x1f6dc, 0x1f6ec, 1},
{0x1f6f0, 0x1f6fc, 1},
- {0x1f700, 0x1f773, 1},
- {0x1f780, 0x1f7d8, 1},
+ {0x1f700, 0x1f776, 1},
+ {0x1f77b, 0x1f7d9, 1},
{0x1f7e0, 0x1f7eb, 1},
- {0x1f800, 0x1f80b, 1},
+ {0x1f7f0, 0x1f800, 16},
+ {0x1f801, 0x1f80b, 1},
{0x1f810, 0x1f847, 1},
{0x1f850, 0x1f859, 1},
{0x1f860, 0x1f887, 1},
{0x1f890, 0x1f8ad, 1},
{0x1f8b0, 0x1f8b1, 1},
- {0x1f900, 0x1f978, 1},
- {0x1f97a, 0x1f9cb, 1},
- {0x1f9cd, 0x1fa53, 1},
+ {0x1f900, 0x1fa53, 1},
{0x1fa60, 0x1fa6d, 1},
- {0x1fa70, 0x1fa74, 1},
- {0x1fa78, 0x1fa7a, 1},
- {0x1fa80, 0x1fa86, 1},
- {0x1fa90, 0x1faa8, 1},
- {0x1fab0, 0x1fab6, 1},
- {0x1fac0, 0x1fac2, 1},
- {0x1fad0, 0x1fad6, 1},
+ {0x1fa70, 0x1fa7c, 1},
+ {0x1fa80, 0x1fa88, 1},
+ {0x1fa90, 0x1fabd, 1},
+ {0x1fabf, 0x1fac5, 1},
+ {0x1face, 0x1fadb, 1},
+ {0x1fae0, 0x1fae8, 1},
+ {0x1faf0, 0x1faf8, 1},
{0x1fb00, 0x1fb92, 1},
{0x1fb94, 0x1fbca, 1},
},
@@ -3681,6 +3828,7 @@ var Scripts = map[string]*RangeTable{
"Coptic": Coptic,
"Cuneiform": Cuneiform,
"Cypriot": Cypriot,
+ "Cypro_Minoan": Cypro_Minoan,
"Cyrillic": Cyrillic,
"Deseret": Deseret,
"Devanagari": Devanagari,
@@ -3714,6 +3862,7 @@ var Scripts = map[string]*RangeTable{
"Kaithi": Kaithi,
"Kannada": Kannada,
"Katakana": Katakana,
+ "Kawi": Kawi,
"Kayah_Li": Kayah_Li,
"Kharoshthi": Kharoshthi,
"Khitan_Small_Script": Khitan_Small_Script,
@@ -3748,6 +3897,7 @@ var Scripts = map[string]*RangeTable{
"Multani": Multani,
"Myanmar": Myanmar,
"Nabataean": Nabataean,
+ "Nag_Mundari": Nag_Mundari,
"Nandinagari": Nandinagari,
"New_Tai_Lue": New_Tai_Lue,
"Newa": Newa,
@@ -3764,6 +3914,7 @@ var Scripts = map[string]*RangeTable{
"Old_Sogdian": Old_Sogdian,
"Old_South_Arabian": Old_South_Arabian,
"Old_Turkic": Old_Turkic,
+ "Old_Uyghur": Old_Uyghur,
"Oriya": Oriya,
"Osage": Osage,
"Osmanya": Osmanya,
@@ -3795,6 +3946,7 @@ var Scripts = map[string]*RangeTable{
"Tai_Viet": Tai_Viet,
"Takri": Takri,
"Tamil": Tamil,
+ "Tangsa": Tangsa,
"Tangut": Tangut,
"Telugu": Telugu,
"Thaana": Thaana,
@@ -3802,8 +3954,10 @@ var Scripts = map[string]*RangeTable{
"Tibetan": Tibetan,
"Tifinagh": Tifinagh,
"Tirhuta": Tirhuta,
+ "Toto": Toto,
"Ugaritic": Ugaritic,
"Vai": Vai,
+ "Vithkuqi": Vithkuqi,
"Wancho": Wancho,
"Warang_Citi": Warang_Citi,
"Yezidi": Yezidi,
@@ -3825,7 +3979,7 @@ var _Ahom = &RangeTable{
R32: []Range32{
{0x11700, 0x1171a, 1},
{0x1171d, 0x1172b, 1},
- {0x11730, 0x1173f, 1},
+ {0x11730, 0x11746, 1},
},
}
@@ -3841,27 +3995,29 @@ var _Arabic = &RangeTable{
{0x0600, 0x0604, 1},
{0x0606, 0x060b, 1},
{0x060d, 0x061a, 1},
- {0x061c, 0x0620, 2},
- {0x0621, 0x063f, 1},
+ {0x061c, 0x061e, 1},
+ {0x0620, 0x063f, 1},
{0x0641, 0x064a, 1},
{0x0656, 0x066f, 1},
{0x0671, 0x06dc, 1},
{0x06de, 0x06ff, 1},
{0x0750, 0x077f, 1},
- {0x08a0, 0x08b4, 1},
- {0x08b6, 0x08c7, 1},
- {0x08d3, 0x08e1, 1},
+ {0x0870, 0x088e, 1},
+ {0x0890, 0x0891, 1},
+ {0x0898, 0x08e1, 1},
{0x08e3, 0x08ff, 1},
- {0xfb50, 0xfbc1, 1},
+ {0xfb50, 0xfbc2, 1},
{0xfbd3, 0xfd3d, 1},
- {0xfd50, 0xfd8f, 1},
+ {0xfd40, 0xfd8f, 1},
{0xfd92, 0xfdc7, 1},
- {0xfdf0, 0xfdfd, 1},
+ {0xfdcf, 0xfdf0, 33},
+ {0xfdf1, 0xfdff, 1},
{0xfe70, 0xfe74, 1},
{0xfe76, 0xfefc, 1},
},
R32: []Range32{
{0x10e60, 0x10e7e, 1},
+ {0x10efd, 0x10eff, 1},
{0x1ee00, 0x1ee03, 1},
{0x1ee05, 0x1ee1f, 1},
{0x1ee21, 0x1ee22, 1},
@@ -3909,8 +4065,8 @@ var _Avestan = &RangeTable{
var _Balinese = &RangeTable{
R16: []Range16{
- {0x1b00, 0x1b4b, 1},
- {0x1b50, 0x1b7c, 1},
+ {0x1b00, 0x1b4c, 1},
+ {0x1b50, 0x1b7e, 1},
},
}
@@ -3979,7 +4135,7 @@ var _Brahmi = &RangeTable{
R16: []Range16{},
R32: []Range32{
{0x11000, 0x1104d, 1},
- {0x11052, 0x1106f, 1},
+ {0x11052, 0x11075, 1},
{0x1107f, 0x1107f, 1},
},
}
@@ -4008,6 +4164,9 @@ var _Canadian_Aboriginal = &RangeTable{
{0x1400, 0x167f, 1},
{0x18b0, 0x18f5, 1},
},
+ R32: []Range32{
+ {0x11ab0, 0x11abf, 1},
+ },
}
var _Carian = &RangeTable{
@@ -4091,7 +4250,7 @@ var _Common = &RangeTable{
{0x2066, 0x2070, 1},
{0x2074, 0x207e, 1},
{0x2080, 0x208e, 1},
- {0x20a0, 0x20bf, 1},
+ {0x20a0, 0x20c0, 1},
{0x2100, 0x2125, 1},
{0x2127, 0x2129, 1},
{0x212c, 0x2131, 1},
@@ -4104,7 +4263,7 @@ var _Common = &RangeTable{
{0x2900, 0x2b73, 1},
{0x2b76, 0x2b95, 1},
{0x2b97, 0x2bff, 1},
- {0x2e00, 0x2e52, 1},
+ {0x2e00, 0x2e5d, 1},
{0x2ff0, 0x2ffb, 1},
{0x3000, 0x3004, 1},
{0x3006, 0x3008, 2},
@@ -4149,15 +4308,16 @@ var _Common = &RangeTable{
{0x10190, 0x1019c, 1},
{0x101d0, 0x101fc, 1},
{0x102e1, 0x102fb, 1},
- {0x16fe2, 0x16fe3, 1},
{0x1bca0, 0x1bca3, 1},
+ {0x1cf50, 0x1cfc3, 1},
{0x1d000, 0x1d0f5, 1},
{0x1d100, 0x1d126, 1},
{0x1d129, 0x1d166, 1},
{0x1d16a, 0x1d17a, 1},
{0x1d183, 0x1d184, 1},
{0x1d18c, 0x1d1a9, 1},
- {0x1d1ae, 0x1d1e8, 1},
+ {0x1d1ae, 0x1d1ea, 1},
+ {0x1d2c0, 0x1d2d3, 1},
{0x1d2e0, 0x1d2f3, 1},
{0x1d300, 0x1d356, 1},
{0x1d360, 0x1d378, 1},
@@ -4198,28 +4358,27 @@ var _Common = &RangeTable{
{0x1f250, 0x1f251, 1},
{0x1f260, 0x1f265, 1},
{0x1f300, 0x1f6d7, 1},
- {0x1f6e0, 0x1f6ec, 1},
+ {0x1f6dc, 0x1f6ec, 1},
{0x1f6f0, 0x1f6fc, 1},
- {0x1f700, 0x1f773, 1},
- {0x1f780, 0x1f7d8, 1},
+ {0x1f700, 0x1f776, 1},
+ {0x1f77b, 0x1f7d9, 1},
{0x1f7e0, 0x1f7eb, 1},
- {0x1f800, 0x1f80b, 1},
+ {0x1f7f0, 0x1f800, 16},
+ {0x1f801, 0x1f80b, 1},
{0x1f810, 0x1f847, 1},
{0x1f850, 0x1f859, 1},
{0x1f860, 0x1f887, 1},
{0x1f890, 0x1f8ad, 1},
{0x1f8b0, 0x1f8b1, 1},
- {0x1f900, 0x1f978, 1},
- {0x1f97a, 0x1f9cb, 1},
- {0x1f9cd, 0x1fa53, 1},
+ {0x1f900, 0x1fa53, 1},
{0x1fa60, 0x1fa6d, 1},
- {0x1fa70, 0x1fa74, 1},
- {0x1fa78, 0x1fa7a, 1},
- {0x1fa80, 0x1fa86, 1},
- {0x1fa90, 0x1faa8, 1},
- {0x1fab0, 0x1fab6, 1},
- {0x1fac0, 0x1fac2, 1},
- {0x1fad0, 0x1fad6, 1},
+ {0x1fa70, 0x1fa7c, 1},
+ {0x1fa80, 0x1fa88, 1},
+ {0x1fa90, 0x1fabd, 1},
+ {0x1fabf, 0x1fac5, 1},
+ {0x1face, 0x1fadb, 1},
+ {0x1fae0, 0x1fae8, 1},
+ {0x1faf0, 0x1faf8, 1},
{0x1fb00, 0x1fb92, 1},
{0x1fb94, 0x1fbca, 1},
{0x1fbf0, 0x1fbf9, 1},
@@ -4258,6 +4417,13 @@ var _Cypriot = &RangeTable{
},
}
+var _Cypro_Minoan = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x12f90, 0x12ff2, 1},
+ },
+}
+
var _Cyrillic = &RangeTable{
R16: []Range16{
{0x0400, 0x0484, 1},
@@ -4268,6 +4434,10 @@ var _Cyrillic = &RangeTable{
{0xa640, 0xa69f, 1},
{0xfe2e, 0xfe2f, 1},
},
+ R32: []Range32{
+ {0x1e030, 0x1e06d, 1},
+ {0x1e08f, 0x1e08f, 1},
+ },
}
var _Deseret = &RangeTable{
@@ -4284,6 +4454,9 @@ var _Devanagari = &RangeTable{
{0x0966, 0x097f, 1},
{0xa8e0, 0xa8ff, 1},
},
+ R32: []Range32{
+ {0x11b00, 0x11b09, 1},
+ },
}
var _Dives_Akuru = &RangeTable{
@@ -4321,8 +4494,7 @@ var _Duployan = &RangeTable{
var _Egyptian_Hieroglyphs = &RangeTable{
R16: []Range16{},
R32: []Range32{
- {0x13000, 0x1342e, 1},
- {0x13430, 0x13438, 1},
+ {0x13000, 0x13455, 1},
},
}
@@ -4375,6 +4547,12 @@ var _Ethiopic = &RangeTable{
{0xab20, 0xab26, 1},
{0xab28, 0xab2e, 1},
},
+ R32: []Range32{
+ {0x1e7e0, 0x1e7e6, 1},
+ {0x1e7e8, 0x1e7eb, 1},
+ {0x1e7ed, 0x1e7ee, 1},
+ {0x1e7f0, 0x1e7fe, 1},
+ },
}
var _Georgian = &RangeTable{
@@ -4392,8 +4570,7 @@ var _Georgian = &RangeTable{
var _Glagolitic = &RangeTable{
R16: []Range16{
- {0x2c00, 0x2c2e, 1},
- {0x2c30, 0x2c5e, 1},
+ {0x2c00, 0x2c5f, 1},
},
R32: []Range32{
{0x1e000, 0x1e006, 1},
@@ -4531,19 +4708,21 @@ var _Han = &RangeTable{
{0x3021, 0x3029, 1},
{0x3038, 0x303b, 1},
{0x3400, 0x4dbf, 1},
- {0x4e00, 0x9ffc, 1},
+ {0x4e00, 0x9fff, 1},
{0xf900, 0xfa6d, 1},
{0xfa70, 0xfad9, 1},
},
R32: []Range32{
+ {0x16fe2, 0x16fe3, 1},
{0x16ff0, 0x16ff1, 1},
- {0x20000, 0x2a6dd, 1},
- {0x2a700, 0x2b734, 1},
+ {0x20000, 0x2a6df, 1},
+ {0x2a700, 0x2b739, 1},
{0x2b740, 0x2b81d, 1},
{0x2b820, 0x2cea1, 1},
{0x2ceb0, 0x2ebe0, 1},
{0x2f800, 0x2fa1d, 1},
{0x30000, 0x3134a, 1},
+ {0x31350, 0x323af, 1},
},
}
@@ -4609,8 +4788,9 @@ var _Hiragana = &RangeTable{
{0x309d, 0x309f, 1},
},
R32: []Range32{
- {0x1b001, 0x1b11e, 1},
- {0x1b150, 0x1b152, 1},
+ {0x1b001, 0x1b11f, 1},
+ {0x1b132, 0x1b150, 30},
+ {0x1b151, 0x1b152, 1},
{0x1f200, 0x1f200, 1},
},
}
@@ -4630,14 +4810,13 @@ var _Inherited = &RangeTable{
{0x064b, 0x0655, 1},
{0x0670, 0x0951, 737},
{0x0952, 0x0954, 1},
- {0x1ab0, 0x1ac0, 1},
+ {0x1ab0, 0x1ace, 1},
{0x1cd0, 0x1cd2, 1},
{0x1cd4, 0x1ce0, 1},
{0x1ce2, 0x1ce8, 1},
{0x1ced, 0x1cf4, 7},
{0x1cf8, 0x1cf9, 1},
- {0x1dc0, 0x1df9, 1},
- {0x1dfb, 0x1dff, 1},
+ {0x1dc0, 0x1dff, 1},
{0x200c, 0x200d, 1},
{0x20d0, 0x20f0, 1},
{0x302a, 0x302d, 1},
@@ -4647,8 +4826,10 @@ var _Inherited = &RangeTable{
},
R32: []Range32{
{0x101fd, 0x102e0, 227},
- {0x1133b, 0x1d167, 48684},
- {0x1d168, 0x1d169, 1},
+ {0x1133b, 0x1cf00, 48069},
+ {0x1cf01, 0x1cf2d, 1},
+ {0x1cf30, 0x1cf46, 1},
+ {0x1d167, 0x1d169, 1},
{0x1d17b, 0x1d182, 1},
{0x1d185, 0x1d18b, 1},
{0x1d1aa, 0x1d1ad, 1},
@@ -4683,7 +4864,7 @@ var _Javanese = &RangeTable{
var _Kaithi = &RangeTable{
R16: []Range16{},
R32: []Range32{
- {0x11080, 0x110c1, 1},
+ {0x11080, 0x110c2, 1},
{0x110cd, 0x110cd, 1},
},
}
@@ -4699,10 +4880,10 @@ var _Kannada = &RangeTable{
{0x0cc6, 0x0cc8, 1},
{0x0cca, 0x0ccd, 1},
{0x0cd5, 0x0cd6, 1},
- {0x0cde, 0x0ce0, 2},
- {0x0ce1, 0x0ce3, 1},
+ {0x0cdd, 0x0cde, 1},
+ {0x0ce0, 0x0ce3, 1},
{0x0ce6, 0x0cef, 1},
- {0x0cf1, 0x0cf2, 1},
+ {0x0cf1, 0x0cf3, 1},
},
}
@@ -4717,11 +4898,25 @@ var _Katakana = &RangeTable{
{0xff71, 0xff9d, 1},
},
R32: []Range32{
- {0x1b000, 0x1b164, 356},
+ {0x1aff0, 0x1aff3, 1},
+ {0x1aff5, 0x1affb, 1},
+ {0x1affd, 0x1affe, 1},
+ {0x1b000, 0x1b120, 288},
+ {0x1b121, 0x1b122, 1},
+ {0x1b155, 0x1b164, 15},
{0x1b165, 0x1b167, 1},
},
}
+var _Kawi = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x11f00, 0x11f10, 1},
+ {0x11f12, 0x11f3a, 1},
+ {0x11f3e, 0x11f59, 1},
+ },
+}
+
var _Kayah_Li = &RangeTable{
R16: []Range16{
{0xa900, 0xa92d, 1},
@@ -4764,7 +4959,7 @@ var _Khojki = &RangeTable{
R16: []Range16{},
R32: []Range32{
{0x11200, 0x11211, 1},
- {0x11213, 0x1123e, 1},
+ {0x11213, 0x11241, 1},
},
}
@@ -4786,7 +4981,7 @@ var _Lao = &RangeTable{
{0x0ea8, 0x0ebd, 1},
{0x0ec0, 0x0ec4, 1},
{0x0ec6, 0x0ec8, 2},
- {0x0ec9, 0x0ecd, 1},
+ {0x0ec9, 0x0ece, 1},
{0x0ed0, 0x0ed9, 1},
{0x0edc, 0x0edf, 1},
},
@@ -4814,9 +5009,11 @@ var _Latin = &RangeTable{
{0x2160, 0x2188, 1},
{0x2c60, 0x2c7f, 1},
{0xa722, 0xa787, 1},
- {0xa78b, 0xa7bf, 1},
- {0xa7c2, 0xa7ca, 1},
- {0xa7f5, 0xa7ff, 1},
+ {0xa78b, 0xa7ca, 1},
+ {0xa7d0, 0xa7d1, 1},
+ {0xa7d3, 0xa7d5, 2},
+ {0xa7d6, 0xa7d9, 1},
+ {0xa7f2, 0xa7ff, 1},
{0xab30, 0xab5a, 1},
{0xab5c, 0xab64, 1},
{0xab66, 0xab69, 1},
@@ -4824,6 +5021,13 @@ var _Latin = &RangeTable{
{0xff21, 0xff3a, 1},
{0xff41, 0xff5a, 1},
},
+ R32: []Range32{
+ {0x10780, 0x10785, 1},
+ {0x10787, 0x107b0, 1},
+ {0x107b2, 0x107ba, 1},
+ {0x1df00, 0x1df1e, 1},
+ {0x1df25, 0x1df2a, 1},
+ },
LatinOffset: 5,
}
@@ -5014,8 +5218,7 @@ var _Mongolian = &RangeTable{
R16: []Range16{
{0x1800, 0x1801, 1},
{0x1804, 0x1806, 2},
- {0x1807, 0x180e, 1},
- {0x1810, 0x1819, 1},
+ {0x1807, 0x1819, 1},
{0x1820, 0x1878, 1},
{0x1880, 0x18aa, 1},
},
@@ -5060,6 +5263,13 @@ var _Nabataean = &RangeTable{
},
}
+var _Nag_Mundari = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x1e4d0, 0x1e4f9, 1},
+ },
+}
+
var _Nandinagari = &RangeTable{
R16: []Range16{},
R32: []Range32{
@@ -5183,6 +5393,13 @@ var _Old_Turkic = &RangeTable{
},
}
+var _Old_Uyghur = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x10f70, 0x10f89, 1},
+ },
+}
+
var _Oriya = &RangeTable{
R16: []Range16{
{0x0b01, 0x0b03, 1},
@@ -5391,8 +5608,8 @@ var _Syriac = &RangeTable{
var _Tagalog = &RangeTable{
R16: []Range16{
- {0x1700, 0x170c, 1},
- {0x170e, 0x1714, 1},
+ {0x1700, 0x1715, 1},
+ {0x171f, 0x171f, 1},
},
}
@@ -5431,7 +5648,7 @@ var _Tai_Viet = &RangeTable{
var _Takri = &RangeTable{
R16: []Range16{},
R32: []Range32{
- {0x11680, 0x116b8, 1},
+ {0x11680, 0x116b9, 1},
{0x116c0, 0x116c9, 1},
},
}
@@ -5460,6 +5677,14 @@ var _Tamil = &RangeTable{
},
}
+var _Tangsa = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x16a70, 0x16abe, 1},
+ {0x16ac0, 0x16ac9, 1},
+ },
+}
+
var _Tangut = &RangeTable{
R16: []Range16{},
R32: []Range32{
@@ -5476,12 +5701,13 @@ var _Telugu = &RangeTable{
{0x0c0e, 0x0c10, 1},
{0x0c12, 0x0c28, 1},
{0x0c2a, 0x0c39, 1},
- {0x0c3d, 0x0c44, 1},
+ {0x0c3c, 0x0c44, 1},
{0x0c46, 0x0c48, 1},
{0x0c4a, 0x0c4d, 1},
{0x0c55, 0x0c56, 1},
{0x0c58, 0x0c5a, 1},
- {0x0c60, 0x0c63, 1},
+ {0x0c5d, 0x0c60, 3},
+ {0x0c61, 0x0c63, 1},
{0x0c66, 0x0c6f, 1},
{0x0c77, 0x0c7f, 1},
},
@@ -5528,6 +5754,13 @@ var _Tirhuta = &RangeTable{
},
}
+var _Toto = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x1e290, 0x1e2ae, 1},
+ },
+}
+
var _Ugaritic = &RangeTable{
R16: []Range16{},
R32: []Range32{
@@ -5542,6 +5775,20 @@ var _Vai = &RangeTable{
},
}
+var _Vithkuqi = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x10570, 0x1057a, 1},
+ {0x1057c, 0x1058a, 1},
+ {0x1058c, 0x10592, 1},
+ {0x10594, 0x10595, 1},
+ {0x10597, 0x105a1, 1},
+ {0x105a3, 0x105b1, 1},
+ {0x105b3, 0x105b9, 1},
+ {0x105bb, 0x105bc, 1},
+ },
+}
+
var _Wancho = &RangeTable{
R16: []Range16{},
R32: []Range32{
@@ -5611,6 +5858,7 @@ var (
Coptic = _Coptic // Coptic is the set of Unicode characters in script Coptic.
Cuneiform = _Cuneiform // Cuneiform is the set of Unicode characters in script Cuneiform.
Cypriot = _Cypriot // Cypriot is the set of Unicode characters in script Cypriot.
+ Cypro_Minoan = _Cypro_Minoan // Cypro_Minoan is the set of Unicode characters in script Cypro_Minoan.
Cyrillic = _Cyrillic // Cyrillic is the set of Unicode characters in script Cyrillic.
Deseret = _Deseret // Deseret is the set of Unicode characters in script Deseret.
Devanagari = _Devanagari // Devanagari is the set of Unicode characters in script Devanagari.
@@ -5644,6 +5892,7 @@ var (
Kaithi = _Kaithi // Kaithi is the set of Unicode characters in script Kaithi.
Kannada = _Kannada // Kannada is the set of Unicode characters in script Kannada.
Katakana = _Katakana // Katakana is the set of Unicode characters in script Katakana.
+ Kawi = _Kawi // Kawi is the set of Unicode characters in script Kawi.
Kayah_Li = _Kayah_Li // Kayah_Li is the set of Unicode characters in script Kayah_Li.
Kharoshthi = _Kharoshthi // Kharoshthi is the set of Unicode characters in script Kharoshthi.
Khitan_Small_Script = _Khitan_Small_Script // Khitan_Small_Script is the set of Unicode characters in script Khitan_Small_Script.
@@ -5678,6 +5927,7 @@ var (
Multani = _Multani // Multani is the set of Unicode characters in script Multani.
Myanmar = _Myanmar // Myanmar is the set of Unicode characters in script Myanmar.
Nabataean = _Nabataean // Nabataean is the set of Unicode characters in script Nabataean.
+ Nag_Mundari = _Nag_Mundari // Nag_Mundari is the set of Unicode characters in script Nag_Mundari.
Nandinagari = _Nandinagari // Nandinagari is the set of Unicode characters in script Nandinagari.
New_Tai_Lue = _New_Tai_Lue // New_Tai_Lue is the set of Unicode characters in script New_Tai_Lue.
Newa = _Newa // Newa is the set of Unicode characters in script Newa.
@@ -5694,6 +5944,7 @@ var (
Old_Sogdian = _Old_Sogdian // Old_Sogdian is the set of Unicode characters in script Old_Sogdian.
Old_South_Arabian = _Old_South_Arabian // Old_South_Arabian is the set of Unicode characters in script Old_South_Arabian.
Old_Turkic = _Old_Turkic // Old_Turkic is the set of Unicode characters in script Old_Turkic.
+ Old_Uyghur = _Old_Uyghur // Old_Uyghur is the set of Unicode characters in script Old_Uyghur.
Oriya = _Oriya // Oriya is the set of Unicode characters in script Oriya.
Osage = _Osage // Osage is the set of Unicode characters in script Osage.
Osmanya = _Osmanya // Osmanya is the set of Unicode characters in script Osmanya.
@@ -5725,6 +5976,7 @@ var (
Tai_Viet = _Tai_Viet // Tai_Viet is the set of Unicode characters in script Tai_Viet.
Takri = _Takri // Takri is the set of Unicode characters in script Takri.
Tamil = _Tamil // Tamil is the set of Unicode characters in script Tamil.
+ Tangsa = _Tangsa // Tangsa is the set of Unicode characters in script Tangsa.
Tangut = _Tangut // Tangut is the set of Unicode characters in script Tangut.
Telugu = _Telugu // Telugu is the set of Unicode characters in script Telugu.
Thaana = _Thaana // Thaana is the set of Unicode characters in script Thaana.
@@ -5732,8 +5984,10 @@ var (
Tibetan = _Tibetan // Tibetan is the set of Unicode characters in script Tibetan.
Tifinagh = _Tifinagh // Tifinagh is the set of Unicode characters in script Tifinagh.
Tirhuta = _Tirhuta // Tirhuta is the set of Unicode characters in script Tirhuta.
+ Toto = _Toto // Toto is the set of Unicode characters in script Toto.
Ugaritic = _Ugaritic // Ugaritic is the set of Unicode characters in script Ugaritic.
Vai = _Vai // Vai is the set of Unicode characters in script Vai.
+ Vithkuqi = _Vithkuqi // Vithkuqi is the set of Unicode characters in script Vithkuqi.
Wancho = _Wancho // Wancho is the set of Unicode characters in script Wancho.
Warang_Citi = _Warang_Citi // Warang_Citi is the set of Unicode characters in script Warang_Citi.
Yezidi = _Yezidi // Yezidi is the set of Unicode characters in script Yezidi.
@@ -5808,11 +6062,11 @@ var _Dash = &RangeTable{
{0x208b, 0x2212, 391},
{0x2e17, 0x2e1a, 3},
{0x2e3a, 0x2e3b, 1},
- {0x2e40, 0x301c, 476},
- {0x3030, 0x30a0, 112},
- {0xfe31, 0xfe32, 1},
- {0xfe58, 0xfe63, 11},
- {0xff0d, 0xff0d, 1},
+ {0x2e40, 0x2e5d, 29},
+ {0x301c, 0x3030, 20},
+ {0x30a0, 0xfe31, 52625},
+ {0xfe32, 0xfe58, 38},
+ {0xfe63, 0xff0d, 170},
},
R32: []Range32{
{0x10ead, 0x10ead, 1},
@@ -5859,6 +6113,8 @@ var _Diacritic = &RangeTable{
{0x07a6, 0x07b0, 1},
{0x07eb, 0x07f5, 1},
{0x0818, 0x0819, 1},
+ {0x0898, 0x089f, 1},
+ {0x08c9, 0x08d2, 1},
{0x08e3, 0x08fe, 1},
{0x093c, 0x094d, 17},
{0x0951, 0x0954, 1},
@@ -5869,10 +6125,10 @@ var _Diacritic = &RangeTable{
{0x0afe, 0x0aff, 1},
{0x0b3c, 0x0b4d, 17},
{0x0b55, 0x0bcd, 120},
- {0x0c4d, 0x0cbc, 111},
- {0x0ccd, 0x0d3b, 110},
- {0x0d3c, 0x0d4d, 17},
- {0x0dca, 0x0e47, 125},
+ {0x0c3c, 0x0c4d, 17},
+ {0x0cbc, 0x0ccd, 17},
+ {0x0d3b, 0x0d3c, 1},
+ {0x0d4d, 0x0e47, 125},
{0x0e48, 0x0e4c, 1},
{0x0e4e, 0x0eba, 108},
{0x0ec8, 0x0ecc, 1},
@@ -5889,12 +6145,14 @@ var _Diacritic = &RangeTable{
{0x108f, 0x109a, 11},
{0x109b, 0x135d, 706},
{0x135e, 0x135f, 1},
+ {0x1714, 0x1715, 1},
{0x17c9, 0x17d3, 1},
{0x17dd, 0x1939, 348},
{0x193a, 0x193b, 1},
{0x1a75, 0x1a7c, 1},
{0x1a7f, 0x1ab0, 49},
- {0x1ab1, 0x1abd, 1},
+ {0x1ab1, 0x1abe, 1},
+ {0x1ac1, 0x1acb, 1},
{0x1b34, 0x1b44, 16},
{0x1b6b, 0x1b73, 1},
{0x1baa, 0x1bab, 1},
@@ -5905,8 +6163,7 @@ var _Diacritic = &RangeTable{
{0x1cf7, 0x1cf9, 1},
{0x1d2c, 0x1d6a, 1},
{0x1dc4, 0x1dcf, 1},
- {0x1df5, 0x1df9, 1},
- {0x1dfd, 0x1dff, 1},
+ {0x1df5, 0x1dff, 1},
{0x1fbd, 0x1fbf, 2},
{0x1fc0, 0x1fc1, 1},
{0x1fcd, 0x1fcf, 1},
@@ -5943,10 +6200,16 @@ var _Diacritic = &RangeTable{
{0xff9f, 0xffe3, 68},
},
R32: []Range32{
- {0x102e0, 0x10ae5, 2053},
- {0x10ae6, 0x10d22, 572},
- {0x10d23, 0x10d27, 1},
+ {0x102e0, 0x10780, 1184},
+ {0x10781, 0x10785, 1},
+ {0x10787, 0x107b0, 1},
+ {0x107b2, 0x107ba, 1},
+ {0x10ae5, 0x10ae6, 1},
+ {0x10d22, 0x10d27, 1},
+ {0x10efd, 0x10eff, 1},
{0x10f46, 0x10f50, 1},
+ {0x10f82, 0x10f85, 1},
+ {0x11046, 0x11070, 42},
{0x110b9, 0x110ba, 1},
{0x11133, 0x11134, 1},
{0x11173, 0x111c0, 77},
@@ -5968,17 +6231,25 @@ var _Diacritic = &RangeTable{
{0x11a99, 0x11c3f, 422},
{0x11d42, 0x11d44, 2},
{0x11d45, 0x11d97, 82},
+ {0x13447, 0x13455, 1},
{0x16af0, 0x16af4, 1},
{0x16b30, 0x16b36, 1},
{0x16f8f, 0x16f9f, 1},
{0x16ff0, 0x16ff1, 1},
+ {0x1aff0, 0x1aff3, 1},
+ {0x1aff5, 0x1affb, 1},
+ {0x1affd, 0x1affe, 1},
+ {0x1cf00, 0x1cf2d, 1},
+ {0x1cf30, 0x1cf46, 1},
{0x1d167, 0x1d169, 1},
{0x1d16d, 0x1d172, 1},
{0x1d17b, 0x1d182, 1},
{0x1d185, 0x1d18b, 1},
{0x1d1aa, 0x1d1ad, 1},
+ {0x1e030, 0x1e06d, 1},
{0x1e130, 0x1e136, 1},
- {0x1e2ec, 0x1e2ef, 1},
+ {0x1e2ae, 0x1e2ec, 62},
+ {0x1e2ed, 0x1e2ef, 1},
{0x1e8d0, 0x1e8d6, 1},
{0x1e944, 0x1e946, 1},
{0x1e948, 0x1e94a, 1},
@@ -6005,6 +6276,7 @@ var _Extender = &RangeTable{
{0xff70, 0xff70, 1},
},
R32: []Range32{
+ {0x10781, 0x10782, 1},
{0x1135d, 0x115c6, 617},
{0x115c7, 0x115c8, 1},
{0x11a98, 0x16b42, 20650},
@@ -6058,7 +6330,7 @@ var _Ideographic = &RangeTable{
{0x3021, 0x3029, 1},
{0x3038, 0x303a, 1},
{0x3400, 0x4dbf, 1},
- {0x4e00, 0x9ffc, 1},
+ {0x4e00, 0x9fff, 1},
{0xf900, 0xfa6d, 1},
{0xfa70, 0xfad9, 1},
},
@@ -6068,13 +6340,14 @@ var _Ideographic = &RangeTable{
{0x18800, 0x18cd5, 1},
{0x18d00, 0x18d08, 1},
{0x1b170, 0x1b2fb, 1},
- {0x20000, 0x2a6dd, 1},
- {0x2a700, 0x2b734, 1},
+ {0x20000, 0x2a6df, 1},
+ {0x2a700, 0x2b739, 1},
{0x2b740, 0x2b81d, 1},
{0x2b820, 0x2cea1, 1},
{0x2ceb0, 0x2ebe0, 1},
{0x2f800, 0x2fa1d, 1},
{0x30000, 0x3134a, 1},
+ {0x31350, 0x323af, 1},
},
}
@@ -6178,7 +6451,7 @@ var _Other_Alphabetic = &RangeTable{
{0x0bc6, 0x0bc8, 1},
{0x0bca, 0x0bcc, 1},
{0x0bd7, 0x0c00, 41},
- {0x0c01, 0x0c03, 1},
+ {0x0c01, 0x0c04, 1},
{0x0c3e, 0x0c44, 1},
{0x0c46, 0x0c48, 1},
{0x0c4a, 0x0c4c, 1},
@@ -6190,7 +6463,8 @@ var _Other_Alphabetic = &RangeTable{
{0x0cca, 0x0ccc, 1},
{0x0cd5, 0x0cd6, 1},
{0x0ce2, 0x0ce3, 1},
- {0x0d00, 0x0d03, 1},
+ {0x0cf3, 0x0d00, 13},
+ {0x0d01, 0x0d03, 1},
{0x0d3e, 0x0d44, 1},
{0x0d46, 0x0d48, 1},
{0x0d4a, 0x0d4c, 1},
@@ -6207,7 +6481,7 @@ var _Other_Alphabetic = &RangeTable{
{0x0eb4, 0x0eb9, 1},
{0x0ebb, 0x0ebc, 1},
{0x0ecd, 0x0f71, 164},
- {0x0f72, 0x0f81, 1},
+ {0x0f72, 0x0f83, 1},
{0x0f8d, 0x0f97, 1},
{0x0f99, 0x0fbc, 1},
{0x102b, 0x1036, 1},
@@ -6234,6 +6508,7 @@ var _Other_Alphabetic = &RangeTable{
{0x1a55, 0x1a5e, 1},
{0x1a61, 0x1a74, 1},
{0x1abf, 0x1ac0, 1},
+ {0x1acc, 0x1ace, 1},
{0x1b00, 0x1b04, 1},
{0x1b35, 0x1b43, 1},
{0x1b80, 0x1b82, 1},
@@ -6278,9 +6553,11 @@ var _Other_Alphabetic = &RangeTable{
{0x10eab, 0x10eac, 1},
{0x11000, 0x11002, 1},
{0x11038, 0x11045, 1},
- {0x11082, 0x110b0, 46},
- {0x110b1, 0x110b8, 1},
- {0x11100, 0x11102, 1},
+ {0x11073, 0x11074, 1},
+ {0x11080, 0x11082, 1},
+ {0x110b0, 0x110b8, 1},
+ {0x110c2, 0x11100, 62},
+ {0x11101, 0x11102, 1},
{0x11127, 0x11132, 1},
{0x11145, 0x11146, 1},
{0x11180, 0x11182, 1},
@@ -6288,7 +6565,8 @@ var _Other_Alphabetic = &RangeTable{
{0x111ce, 0x111cf, 1},
{0x1122c, 0x11234, 1},
{0x11237, 0x1123e, 7},
- {0x112df, 0x112e8, 1},
+ {0x11241, 0x112df, 158},
+ {0x112e0, 0x112e8, 1},
{0x11300, 0x11303, 1},
{0x1133e, 0x11344, 1},
{0x11347, 0x11348, 1},
@@ -6331,6 +6609,10 @@ var _Other_Alphabetic = &RangeTable{
{0x11d90, 0x11d91, 1},
{0x11d93, 0x11d96, 1},
{0x11ef3, 0x11ef6, 1},
+ {0x11f00, 0x11f01, 1},
+ {0x11f03, 0x11f34, 49},
+ {0x11f35, 0x11f3a, 1},
+ {0x11f3e, 0x11f40, 1},
{0x16f4f, 0x16f51, 2},
{0x16f52, 0x16f87, 1},
{0x16f8f, 0x16f92, 1},
@@ -6341,8 +6623,8 @@ var _Other_Alphabetic = &RangeTable{
{0x1e01b, 0x1e021, 1},
{0x1e023, 0x1e024, 1},
{0x1e026, 0x1e02a, 1},
- {0x1e947, 0x1f130, 2025},
- {0x1f131, 0x1f149, 1},
+ {0x1e08f, 0x1e947, 2232},
+ {0x1f130, 0x1f149, 1},
{0x1f150, 0x1f169, 1},
{0x1f170, 0x1f189, 1},
},
@@ -6410,7 +6692,8 @@ var _Other_Lowercase = &RangeTable{
{0x02c0, 0x02c1, 1},
{0x02e0, 0x02e4, 1},
{0x0345, 0x037a, 53},
- {0x1d2c, 0x1d6a, 1},
+ {0x10fc, 0x1d2c, 3120},
+ {0x1d2d, 0x1d6a, 1},
{0x1d78, 0x1d9b, 35},
{0x1d9c, 0x1dbf, 1},
{0x2071, 0x207f, 14},
@@ -6419,9 +6702,18 @@ var _Other_Lowercase = &RangeTable{
{0x24d0, 0x24e9, 1},
{0x2c7c, 0x2c7d, 1},
{0xa69c, 0xa69d, 1},
- {0xa770, 0xa7f8, 136},
- {0xa7f9, 0xab5c, 867},
- {0xab5d, 0xab5f, 1},
+ {0xa770, 0xa7f2, 130},
+ {0xa7f3, 0xa7f4, 1},
+ {0xa7f8, 0xa7f9, 1},
+ {0xab5c, 0xab5f, 1},
+ {0xab69, 0xab69, 1},
+ },
+ R32: []Range32{
+ {0x10780, 0x10783, 3},
+ {0x10784, 0x10785, 1},
+ {0x10787, 0x107b0, 1},
+ {0x107b2, 0x107ba, 1},
+ {0x1e030, 0x1e06d, 1},
},
LatinOffset: 1,
}
@@ -6607,6 +6899,7 @@ var _Prepended_Concatenation_Mark = &RangeTable{
R16: []Range16{
{0x0600, 0x0605, 1},
{0x06dd, 0x070f, 50},
+ {0x0890, 0x0891, 1},
{0x08e2, 0x08e2, 1},
},
R32: []Range32{
@@ -6649,7 +6942,7 @@ var _Sentence_Terminal = &RangeTable{
R16: []Range16{
{0x0021, 0x002e, 13},
{0x003f, 0x0589, 1354},
- {0x061e, 0x061f, 1},
+ {0x061d, 0x061f, 1},
{0x06d4, 0x0700, 44},
{0x0701, 0x0702, 1},
{0x07f9, 0x0837, 62},
@@ -6665,11 +6958,13 @@ var _Sentence_Terminal = &RangeTable{
{0x1aa9, 0x1aab, 1},
{0x1b5a, 0x1b5b, 1},
{0x1b5e, 0x1b5f, 1},
+ {0x1b7d, 0x1b7e, 1},
{0x1c3b, 0x1c3c, 1},
{0x1c7e, 0x1c7f, 1},
{0x203c, 0x203d, 1},
{0x2047, 0x2049, 1},
{0x2e2e, 0x2e3c, 14},
+ {0x2e53, 0x2e54, 1},
{0x3002, 0xa4ff, 29949},
{0xa60e, 0xa60f, 1},
{0xa6f3, 0xa6f7, 4},
@@ -6687,6 +6982,7 @@ var _Sentence_Terminal = &RangeTable{
R32: []Range32{
{0x10a56, 0x10a57, 1},
{0x10f55, 0x10f59, 1},
+ {0x10f86, 0x10f89, 1},
{0x11047, 0x11048, 1},
{0x110be, 0x110c1, 1},
{0x11141, 0x11143, 1},
@@ -6705,6 +7001,7 @@ var _Sentence_Terminal = &RangeTable{
{0x11a9b, 0x11a9c, 1},
{0x11c41, 0x11c42, 1},
{0x11ef7, 0x11ef8, 1},
+ {0x11f43, 0x11f44, 1},
{0x16a6e, 0x16a6f, 1},
{0x16af5, 0x16b37, 66},
{0x16b38, 0x16b44, 12},
@@ -6741,6 +7038,8 @@ var _Soft_Dotted = &RangeTable{
{0x1d62a, 0x1d62b, 1},
{0x1d65e, 0x1d65f, 1},
{0x1d692, 0x1d693, 1},
+ {0x1df1a, 0x1e04c, 306},
+ {0x1e04d, 0x1e068, 27},
},
LatinOffset: 1,
}
@@ -6753,7 +7052,7 @@ var _Terminal_Punctuation = &RangeTable{
{0x037e, 0x0387, 9},
{0x0589, 0x05c3, 58},
{0x060c, 0x061b, 15},
- {0x061e, 0x061f, 1},
+ {0x061d, 0x061f, 1},
{0x06d4, 0x0700, 44},
{0x0701, 0x070a, 1},
{0x070c, 0x07f8, 236},
@@ -6776,6 +7075,7 @@ var _Terminal_Punctuation = &RangeTable{
{0x1aa8, 0x1aab, 1},
{0x1b5a, 0x1b5b, 1},
{0x1b5d, 0x1b5f, 1},
+ {0x1b7d, 0x1b7e, 1},
{0x1c3b, 0x1c3f, 1},
{0x1c7e, 0x1c7f, 1},
{0x203c, 0x203d, 1},
@@ -6783,6 +7083,7 @@ var _Terminal_Punctuation = &RangeTable{
{0x2e2e, 0x2e3c, 14},
{0x2e41, 0x2e4c, 11},
{0x2e4e, 0x2e4f, 1},
+ {0x2e53, 0x2e54, 1},
{0x3001, 0x3002, 1},
{0xa4fe, 0xa4ff, 1},
{0xa60d, 0xa60f, 1},
@@ -6809,6 +7110,7 @@ var _Terminal_Punctuation = &RangeTable{
{0x10b3a, 0x10b3f, 1},
{0x10b99, 0x10b9c, 1},
{0x10f55, 0x10f59, 1},
+ {0x10f86, 0x10f89, 1},
{0x11047, 0x1104d, 1},
{0x110be, 0x110c1, 1},
{0x11141, 0x11143, 1},
@@ -6829,7 +7131,8 @@ var _Terminal_Punctuation = &RangeTable{
{0x11aa1, 0x11aa2, 1},
{0x11c41, 0x11c43, 1},
{0x11c71, 0x11ef7, 646},
- {0x11ef8, 0x12470, 1400},
+ {0x11ef8, 0x11f43, 75},
+ {0x11f44, 0x12470, 1324},
{0x12471, 0x12474, 1},
{0x16a6e, 0x16a6f, 1},
{0x16af5, 0x16b37, 66},
@@ -6844,7 +7147,7 @@ var _Terminal_Punctuation = &RangeTable{
var _Unified_Ideograph = &RangeTable{
R16: []Range16{
{0x3400, 0x4dbf, 1},
- {0x4e00, 0x9ffc, 1},
+ {0x4e00, 0x9fff, 1},
{0xfa0e, 0xfa0f, 1},
{0xfa11, 0xfa13, 2},
{0xfa14, 0xfa1f, 11},
@@ -6853,19 +7156,21 @@ var _Unified_Ideograph = &RangeTable{
{0xfa28, 0xfa29, 1},
},
R32: []Range32{
- {0x20000, 0x2a6dd, 1},
- {0x2a700, 0x2b734, 1},
+ {0x20000, 0x2a6df, 1},
+ {0x2a700, 0x2b739, 1},
{0x2b740, 0x2b81d, 1},
{0x2b820, 0x2cea1, 1},
{0x2ceb0, 0x2ebe0, 1},
{0x30000, 0x3134a, 1},
+ {0x31350, 0x323af, 1},
},
}
var _Variation_Selector = &RangeTable{
R16: []Range16{
{0x180b, 0x180d, 1},
- {0xfe00, 0xfe0f, 1},
+ {0x180f, 0xfe00, 58865},
+ {0xfe01, 0xfe0f, 1},
},
R32: []Range32{
{0xe0100, 0xe01ef, 1},
@@ -7182,8 +7487,8 @@ var _CaseRanges = []CaseRange{
{0x2183, 0x2184, d{UpperLower, UpperLower, UpperLower}},
{0x24B6, 0x24CF, d{0, 26, 0}},
{0x24D0, 0x24E9, d{-26, 0, -26}},
- {0x2C00, 0x2C2E, d{0, 48, 0}},
- {0x2C30, 0x2C5E, d{-48, 0, -48}},
+ {0x2C00, 0x2C2F, d{0, 48, 0}},
+ {0x2C30, 0x2C5F, d{-48, 0, -48}},
{0x2C60, 0x2C61, d{UpperLower, UpperLower, UpperLower}},
{0x2C62, 0x2C62, d{0, -10743, 0}},
{0x2C63, 0x2C63, d{0, -3814, 0}},
@@ -7225,12 +7530,13 @@ var _CaseRanges = []CaseRange{
{0xA7B1, 0xA7B1, d{0, -42282, 0}},
{0xA7B2, 0xA7B2, d{0, -42261, 0}},
{0xA7B3, 0xA7B3, d{0, 928, 0}},
- {0xA7B4, 0xA7BF, d{UpperLower, UpperLower, UpperLower}},
- {0xA7C2, 0xA7C3, d{UpperLower, UpperLower, UpperLower}},
+ {0xA7B4, 0xA7C3, d{UpperLower, UpperLower, UpperLower}},
{0xA7C4, 0xA7C4, d{0, -48, 0}},
{0xA7C5, 0xA7C5, d{0, -42307, 0}},
{0xA7C6, 0xA7C6, d{0, -35384, 0}},
{0xA7C7, 0xA7CA, d{UpperLower, UpperLower, UpperLower}},
+ {0xA7D0, 0xA7D1, d{UpperLower, UpperLower, UpperLower}},
+ {0xA7D6, 0xA7D9, d{UpperLower, UpperLower, UpperLower}},
{0xA7F5, 0xA7F6, d{UpperLower, UpperLower, UpperLower}},
{0xAB53, 0xAB53, d{-928, 0, -928}},
{0xAB70, 0xABBF, d{-38864, 0, -38864}},
@@ -7240,6 +7546,14 @@ var _CaseRanges = []CaseRange{
{0x10428, 0x1044F, d{-40, 0, -40}},
{0x104B0, 0x104D3, d{0, 40, 0}},
{0x104D8, 0x104FB, d{-40, 0, -40}},
+ {0x10570, 0x1057A, d{0, 39, 0}},
+ {0x1057C, 0x1058A, d{0, 39, 0}},
+ {0x1058C, 0x10592, d{0, 39, 0}},
+ {0x10594, 0x10595, d{0, 39, 0}},
+ {0x10597, 0x105A1, d{-39, 0, -39}},
+ {0x105A3, 0x105B1, d{-39, 0, -39}},
+ {0x105B3, 0x105B9, d{-39, 0, -39}},
+ {0x105BB, 0x105BC, d{-39, 0, -39}},
{0x10C80, 0x10CB2, d{0, 64, 0}},
{0x10CC0, 0x10CF2, d{-64, 0, -64}},
{0x118A0, 0x118BF, d{0, 32, 0}},
@@ -7378,7 +7692,7 @@ var properties = [MaxLatin1 + 1]uint8{
0x7C: pS | pp, // '|'
0x7D: pP | pp, // '}'
0x7E: pS | pp, // '~'
- 0x7F: pC, // '\u007f'
+ 0x7F: pC, // '\x7f'
0x80: pC, // '\u0080'
0x81: pC, // '\u0081'
0x82: pC, // '\u0082'
@@ -7833,7 +8147,7 @@ var foldLl = &RangeTable{
{0x2126, 0x212a, 4},
{0x212b, 0x2132, 7},
{0x2183, 0x2c00, 2685},
- {0x2c01, 0x2c2e, 1},
+ {0x2c01, 0x2c2f, 1},
{0x2c60, 0x2c62, 2},
{0x2c63, 0x2c64, 1},
{0x2c67, 0x2c6d, 2},
@@ -7854,15 +8168,20 @@ var foldLl = &RangeTable{
{0xa796, 0xa7aa, 2},
{0xa7ab, 0xa7ae, 1},
{0xa7b0, 0xa7b4, 1},
- {0xa7b6, 0xa7be, 2},
- {0xa7c2, 0xa7c4, 2},
+ {0xa7b6, 0xa7c4, 2},
{0xa7c5, 0xa7c7, 1},
- {0xa7c9, 0xa7f5, 44},
- {0xff21, 0xff3a, 1},
+ {0xa7c9, 0xa7d0, 7},
+ {0xa7d6, 0xa7d8, 2},
+ {0xa7f5, 0xff21, 22316},
+ {0xff22, 0xff3a, 1},
},
R32: []Range32{
{0x10400, 0x10427, 1},
{0x104b0, 0x104d3, 1},
+ {0x10570, 0x1057a, 1},
+ {0x1057c, 0x1058a, 1},
+ {0x1058c, 0x10592, 1},
+ {0x10594, 0x10595, 1},
{0x10c80, 0x10cb2, 1},
{0x118a0, 0x118bf, 1},
{0x16e40, 0x16e5f, 1},
@@ -7971,7 +8290,7 @@ var foldLu = &RangeTable{
{0x1fd1, 0x1fe0, 15},
{0x1fe1, 0x1fe5, 4},
{0x214e, 0x2184, 54},
- {0x2c30, 0x2c5e, 1},
+ {0x2c30, 0x2c5f, 1},
{0x2c61, 0x2c65, 4},
{0x2c66, 0x2c6c, 2},
{0x2c73, 0x2c76, 3},
@@ -7989,9 +8308,10 @@ var foldLu = &RangeTable{
{0xa78c, 0xa791, 5},
{0xa793, 0xa794, 1},
{0xa797, 0xa7a9, 2},
- {0xa7b5, 0xa7bf, 2},
- {0xa7c3, 0xa7c8, 5},
- {0xa7ca, 0xa7f6, 44},
+ {0xa7b5, 0xa7c3, 2},
+ {0xa7c8, 0xa7ca, 2},
+ {0xa7d1, 0xa7d7, 6},
+ {0xa7d9, 0xa7f6, 29},
{0xab53, 0xab70, 29},
{0xab71, 0xabbf, 1},
{0xff41, 0xff5a, 1},
@@ -7999,6 +8319,10 @@ var foldLu = &RangeTable{
R32: []Range32{
{0x10428, 0x1044f, 1},
{0x104d8, 0x104fb, 1},
+ {0x10597, 0x105a1, 1},
+ {0x105a3, 0x105b1, 1},
+ {0x105b3, 0x105b9, 1},
+ {0x105bb, 0x105bc, 1},
{0x10cc0, 0x10cf2, 1},
{0x118c0, 0x118df, 1},
{0x16e60, 0x16e7f, 1},
@@ -8050,7 +8374,7 @@ var foldInherited = &RangeTable{
},
}
-// Range entries: 3499 16-bit, 1820 32-bit, 5319 total.
-// Range bytes: 20994 16-bit, 21840 32-bit, 42834 total.
+// Range entries: 3535 16-bit, 2031 32-bit, 5566 total.
+// Range bytes: 21210 16-bit, 24372 32-bit, 45582 total.
// Fold orbit bytes: 88 pairs, 352 bytes
diff --git a/gnovm/stdlibs/unicode/utf8/example_test.gno b/gnovm/stdlibs/unicode/utf8/example_test.gno
index 17d6e8d2114..fe434c94767 100644
--- a/gnovm/stdlibs/unicode/utf8/example_test.gno
+++ b/gnovm/stdlibs/unicode/utf8/example_test.gno
@@ -49,6 +49,7 @@ func ExampleDecodeLastRuneInString() {
// l 1
// e 1
// H 1
+
}
func ExampleDecodeRune() {
@@ -213,3 +214,13 @@ func ExampleValidString() {
// true
// false
}
+
+func ExampleAppendRune() {
+ buf1 := utf8.AppendRune(nil, 0x10000)
+ buf2 := utf8.AppendRune([]byte("init"), 0x10000)
+ fmt.Println(string(buf1))
+ fmt.Println(string(buf2))
+ // Output:
+ // 𐀀
+ // init𐀀
+}
diff --git a/gnovm/stdlibs/unicode/utf8/utf8.gno b/gnovm/stdlibs/unicode/utf8/utf8.gno
index 9c70281488d..71d6bf18d01 100644
--- a/gnovm/stdlibs/unicode/utf8/utf8.gno
+++ b/gnovm/stdlibs/unicode/utf8/utf8.gno
@@ -141,7 +141,7 @@ func FullRuneInString(s string) bool {
}
// DecodeRune unpacks the first UTF-8 encoding in p and returns the rune and
-// its width in bytes. If p is empty it returns (RuneError, 0). Otherwise, if
+// its width in bytes. If p is empty it returns ([RuneError], 0). Otherwise, if
// the encoding is invalid, it returns (RuneError, 1). Both are impossible
// results for correct, non-empty UTF-8.
//
@@ -188,8 +188,8 @@ func DecodeRune(p []byte) (r rune, size int) {
return rune(p0&mask4)<<18 | rune(b1&maskx)<<12 | rune(b2&maskx)<<6 | rune(b3&maskx), 4
}
-// DecodeRuneInString is like DecodeRune but its input is a string. If s is
-// empty it returns (RuneError, 0). Otherwise, if the encoding is invalid, it
+// DecodeRuneInString is like [DecodeRune] but its input is a string. If s is
+// empty it returns ([RuneError], 0). Otherwise, if the encoding is invalid, it
// returns (RuneError, 1). Both are impossible results for correct, non-empty
// UTF-8.
//
@@ -237,7 +237,7 @@ func DecodeRuneInString(s string) (r rune, size int) {
}
// DecodeLastRune unpacks the last UTF-8 encoding in p and returns the rune and
-// its width in bytes. If p is empty it returns (RuneError, 0). Otherwise, if
+// its width in bytes. If p is empty it returns ([RuneError], 0). Otherwise, if
// the encoding is invalid, it returns (RuneError, 1). Both are impossible
// results for correct, non-empty UTF-8.
//
@@ -276,8 +276,8 @@ func DecodeLastRune(p []byte) (r rune, size int) {
return r, size
}
-// DecodeLastRuneInString is like DecodeLastRune but its input is a string. If
-// s is empty it returns (RuneError, 0). Otherwise, if the encoding is invalid,
+// DecodeLastRuneInString is like [DecodeLastRune] but its input is a string. If
+// s is empty it returns ([RuneError], 0). Otherwise, if the encoding is invalid,
// it returns (RuneError, 1). Both are impossible results for correct,
// non-empty UTF-8.
//
@@ -337,7 +337,7 @@ func RuneLen(r rune) int {
}
// EncodeRune writes into p (which must be large enough) the UTF-8 encoding of the rune.
-// If the rune is out of range, it writes the encoding of RuneError.
+// If the rune is out of range, it writes the encoding of [RuneError].
// It returns the number of bytes written.
func EncodeRune(p []byte, r rune) int {
// Negative values are erroneous. Making it unsigned addresses the problem.
@@ -352,13 +352,7 @@ func EncodeRune(p []byte, r rune) int {
return 2
case i > MaxRune, surrogateMin <= i && i <= surrogateMax:
r = RuneError
- // XXX fallthrough not implemented
- // fallthrough
- _ = p[2] // eliminate bounds checks
- p[0] = t3 | byte(r>>12)
- p[1] = tx | byte(r>>6)&maskx
- p[2] = tx | byte(r)&maskx
- return 3
+ fallthrough
case i <= rune3Max:
_ = p[2] // eliminate bounds checks
p[0] = t3 | byte(r>>12)
@@ -375,6 +369,32 @@ func EncodeRune(p []byte, r rune) int {
}
}
+// AppendRune appends the UTF-8 encoding of r to the end of p and
+// returns the extended buffer. If the rune is out of range,
+// it appends the encoding of [RuneError].
+func AppendRune(p []byte, r rune) []byte {
+ // This function is inlineable for fast handling of ASCII.
+ if uint32(r) <= rune1Max {
+ return append(p, byte(r))
+ }
+ return appendRuneNonASCII(p, r)
+}
+
+func appendRuneNonASCII(p []byte, r rune) []byte {
+ // Negative values are erroneous. Making it unsigned addresses the problem.
+ switch i := uint32(r); {
+ case i <= rune2Max:
+ return append(p, t2|byte(r>>6), tx|byte(r)&maskx)
+ case i > MaxRune, surrogateMin <= i && i <= surrogateMax:
+ r = RuneError
+ fallthrough
+ case i <= rune3Max:
+ return append(p, t3|byte(r>>12), tx|byte(r>>6)&maskx, tx|byte(r)&maskx)
+ default:
+ return append(p, t4|byte(r>>18), tx|byte(r>>12)&maskx, tx|byte(r>>6)&maskx, tx|byte(r)&maskx)
+ }
+}
+
// RuneCount returns the number of runes in p. Erroneous and short
// encodings are treated as single runes of width 1 byte.
func RuneCount(p []byte) int {
@@ -413,7 +433,7 @@ func RuneCount(p []byte) int {
return n
}
-// RuneCountInString is like RuneCount but its input is a string.
+// RuneCountInString is like [RuneCount] but its input is a string.
func RuneCountInString(s string) (n int) {
ns := len(s)
for i := 0; i < ns; n++ {
@@ -455,6 +475,11 @@ func RuneStart(b byte) bool { return b&0xC0 != 0x80 }
// Valid reports whether p consists entirely of valid UTF-8-encoded runes.
func Valid(p []byte) bool {
+ // This optimization avoids the need to recompute the capacity
+ // when generating code for p[8:], bringing it to parity with
+ // ValidString, which was 20% faster on long ASCII strings.
+ p = p[:len(p):len(p)]
+
// Fast path. Check for and skip 8 bytes of ASCII characters per iteration.
for len(p) >= 8 {
// Combining two 32 bit loads allows the same code to be used
diff --git a/gnovm/stdlibs/unicode/utf8/utf8_test.gno b/gnovm/stdlibs/unicode/utf8/utf8_test.gno
index 7fecb778975..0384b7a88e9 100644
--- a/gnovm/stdlibs/unicode/utf8/utf8_test.gno
+++ b/gnovm/stdlibs/unicode/utf8/utf8_test.gno
@@ -127,6 +127,17 @@ func TestEncodeRune(t *testing.T) {
}
}
+func TestAppendRune(t *testing.T) {
+ for _, m := range utf8map {
+ if buf := utf8.AppendRune(nil, m.r); string(buf) != m.str {
+ t.Errorf("AppendRune(nil, %#04x) = %s, want %s", m.r, buf, m.str)
+ }
+ if buf := utf8.AppendRune([]byte("init"), m.r); string(buf) != "init"+m.str {
+ t.Errorf("AppendRune(init, %#04x) = %s, want %s", m.r, buf, "init"+m.str)
+ }
+ }
+}
+
func TestDecodeRune(t *testing.T) {
for _, m := range utf8map {
b := []byte(m.str)
diff --git a/gnovm/tests/file.go b/gnovm/tests/file.go
index f6bd789f1bf..98e54114af9 100644
--- a/gnovm/tests/file.go
+++ b/gnovm/tests/file.go
@@ -2,6 +2,7 @@ package tests
import (
"bytes"
+ "encoding/json"
"fmt"
"go/ast"
"go/parser"
@@ -14,6 +15,7 @@ import (
"strings"
"github.com/gnolang/gno/gno.land/pkg/gnoland/ugnot"
+ "github.com/gnolang/gno/gnovm"
gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
"github.com/gnolang/gno/gnovm/stdlibs"
teststd "github.com/gnolang/gno/gnovm/tests/stdlibs/std"
@@ -54,7 +56,7 @@ func TestContext(pkgPath string, send std.Coins) *teststd.TestExecContext {
pkgAddr := gno.DerivePkgAddr(pkgPath) // the addr of the pkgPath called.
caller := gno.DerivePkgAddr("user1.gno")
- pkgCoins := std.MustParseCoins(ugnot.ValueString(200000000)).Add(send) // >= send.
+ pkgCoins := std.MustParseCoins(ugnot.ValueString(200_000_000)).Add(send) // >= send.
banker := newTestBanker(pkgAddr.Bech32(), pkgCoins)
ctx := stdlibs.ExecContext{
ChainID: "dev",
@@ -74,6 +76,19 @@ func TestContext(pkgPath string, send std.Coins) *teststd.TestExecContext {
}
}
+// CleanupMachine can be called during two tests while reusing the same Machine instance.
+func CleanupMachine(m *gno.Machine) {
+ prevCtx := m.Context.(*teststd.TestExecContext)
+ prevSend := prevCtx.OrigSend
+
+ newCtx := TestContext("", prevCtx.OrigSend)
+ pkgCoins := std.MustParseCoins(ugnot.ValueString(200_000_000)).Add(prevSend) // >= send.
+ banker := newTestBanker(prevCtx.OrigPkgAddr, pkgCoins)
+ newCtx.OrigPkgAddr = prevCtx.OrigPkgAddr
+ newCtx.Banker = banker
+ m.Context = newCtx
+}
+
type runFileTestOptions struct {
nativeLibs bool
logger loggerFunc
@@ -110,7 +125,7 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error {
opt(&f)
}
- directives, pkgPath, resWanted, errWanted, rops, stacktraceWanted, maxAlloc, send := wantedFromComment(path)
+ directives, pkgPath, resWanted, errWanted, rops, eventsWanted, stacktraceWanted, maxAlloc, send, preWanted := wantedFromComment(path)
if pkgPath == "" {
pkgPath = "main"
}
@@ -186,10 +201,10 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error {
store.SetStrictGo2GnoMapping(true) // in gno.land, natives must be registered.
gno.DisableDebug() // until main call.
// save package using realm crawl procedure.
- memPkg := &std.MemPackage{
+ memPkg := &gnovm.MemPackage{
Name: string(pkgName),
Path: pkgPath,
- Files: []*std.MemFile{
+ Files: []*gnovm.MemFile{
{
Name: "main.gno", // dontcare
Body: string(bz),
@@ -347,6 +362,45 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error {
}
}
}
+ case "Events":
+ // panic if got unexpected error
+
+ if pnc != nil {
+ if tv, ok := pnc.(*gno.TypedValue); ok {
+ panic(fmt.Sprintf("fail on %s: got unexpected error: %s", path, tv.Sprint(m)))
+ } else { // happens on 'unknown import path ...'
+ panic(fmt.Sprintf("fail on %s: got unexpected error: %v", path, pnc))
+ }
+ }
+ // check result
+ events := m.Context.(*teststd.TestExecContext).EventLogger.Events()
+ evtjson, err := json.MarshalIndent(events, "", " ")
+ if err != nil {
+ panic(err)
+ }
+ evtstr := trimTrailingSpaces(string(evtjson))
+ if evtstr != eventsWanted {
+ if f.syncWanted {
+ // write output to file.
+ replaceWantedInPlace(path, "Events", evtstr)
+ } else {
+ // panic so tests immediately fail (for now).
+ if eventsWanted == "" {
+ panic(fmt.Sprintf("fail on %s: got unexpected events: %s", path, evtstr))
+ } else {
+ diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
+ A: difflib.SplitLines(eventsWanted),
+ B: difflib.SplitLines(evtstr),
+ FromFile: "Expected",
+ FromDate: "",
+ ToFile: "Actual",
+ ToDate: "",
+ Context: 1,
+ })
+ panic(fmt.Sprintf("fail on %s: diff:\n%s\n", path, diff))
+ }
+ }
+ }
case "Realm":
// panic if got unexpected error
if pnc != nil {
@@ -377,6 +431,28 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error {
}
}
}
+ case "Preprocessed":
+ // check preprocessed AST.
+ pn := store.GetBlockNode(gno.PackageNodeLocation(pkgPath))
+ pre := pn.(*gno.PackageNode).FileSet.Files[0].String()
+ if pre != preWanted {
+ if f.syncWanted {
+ // write error to file
+ replaceWantedInPlace(path, "Preprocessed", pre)
+ } else {
+ // panic so tests immediately fail (for now).
+ diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
+ A: difflib.SplitLines(preWanted),
+ B: difflib.SplitLines(pre),
+ FromFile: "Expected",
+ FromDate: "",
+ ToFile: "Actual",
+ ToDate: "",
+ Context: 1,
+ })
+ panic(fmt.Sprintf("fail on %s: diff:\n%s\n", path, diff))
+ }
+ }
case "Stacktrace":
if stacktraceWanted != "" {
var stacktrace string
@@ -388,22 +464,27 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error {
stacktrace = m.Stacktrace().String()
}
- if !strings.Contains(stacktrace, stacktraceWanted) {
- diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
- A: difflib.SplitLines(stacktraceWanted),
- B: difflib.SplitLines(stacktrace),
- FromFile: "Expected",
- FromDate: "",
- ToFile: "Actual",
- ToDate: "",
- Context: 1,
- })
- panic(fmt.Sprintf("fail on %s: diff:\n%s\n", path, diff))
+ if f.syncWanted {
+ // write stacktrace to file
+ replaceWantedInPlace(path, "Stacktrace", stacktrace)
+ } else {
+ if !strings.Contains(stacktrace, stacktraceWanted) {
+ diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
+ A: difflib.SplitLines(stacktraceWanted),
+ B: difflib.SplitLines(stacktrace),
+ FromFile: "Expected",
+ FromDate: "",
+ ToFile: "Actual",
+ ToDate: "",
+ Context: 1,
+ })
+ panic(fmt.Sprintf("fail on %s: diff:\n%s\n", path, diff))
+ }
}
}
checkMachineIsEmpty = false
default:
- checkMachineIsEmpty = false
+ return nil
}
}
}
@@ -421,7 +502,7 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error {
return nil
}
-func wantedFromComment(p string) (directives []string, pkgPath, res, err, rops, stacktrace string, maxAlloc int64, send std.Coins) {
+func wantedFromComment(p string) (directives []string, pkgPath, res, err, rops, events, stacktrace string, maxAlloc int64, send std.Coins, pre string) {
fset := token.NewFileSet()
f, err2 := parser.ParseFile(fset, p, nil, parser.ParseComments)
if err2 != nil {
@@ -463,6 +544,14 @@ func wantedFromComment(p string) (directives []string, pkgPath, res, err, rops,
rops = strings.TrimPrefix(text, "Realm:\n")
rops = strings.TrimSpace(rops)
directives = append(directives, "Realm")
+ } else if strings.HasPrefix(text, "Events:\n") {
+ events = strings.TrimPrefix(text, "Events:\n")
+ events = strings.TrimSpace(events)
+ directives = append(directives, "Events")
+ } else if strings.HasPrefix(text, "Preprocessed:\n") {
+ pre = strings.TrimPrefix(text, "Preprocessed:\n")
+ pre = strings.TrimSpace(pre)
+ directives = append(directives, "Preprocessed")
} else if strings.HasPrefix(text, "Stacktrace:\n") {
stacktrace = strings.TrimPrefix(text, "Stacktrace:\n")
stacktrace = strings.TrimSpace(stacktrace)
@@ -597,12 +686,12 @@ func (tb *testBanker) TotalCoin(denom string) int64 {
func (tb *testBanker) IssueCoin(addr crypto.Bech32Address, denom string, amt int64) {
coins, _ := tb.coinTable[addr]
- sum := coins.Add(std.Coins{{denom, amt}})
+ sum := coins.Add(std.Coins{{Denom: denom, Amount: amt}})
tb.coinTable[addr] = sum
}
func (tb *testBanker) RemoveCoin(addr crypto.Bech32Address, denom string, amt int64) {
coins, _ := tb.coinTable[addr]
- rest := coins.Sub(std.Coins{{denom, amt}})
+ rest := coins.Sub(std.Coins{{Denom: denom, Amount: amt}})
tb.coinTable[addr] = rest
}
diff --git a/gnovm/tests/files/assign29_native.gno b/gnovm/tests/files/assign29_native.gno
new file mode 100644
index 00000000000..a404f703fc1
--- /dev/null
+++ b/gnovm/tests/files/assign29_native.gno
@@ -0,0 +1,14 @@
+package main
+
+import (
+ "time"
+)
+
+func main() {
+ time.Now = func() time.Time {
+ return time.Time{}
+ }
+}
+
+// Error:
+// main/files/assign29_native.gno:8:2: cannot assign to time.Now (neither addressable nor a map index expression)
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 5bdd878c146..d61170334d7 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
@@ -1,10 +1,6 @@
// PKGPATH: gno.land/r/test
package test
-import (
- "fmt"
-)
-
type (
word uint
nat []word
@@ -226,7 +222,7 @@ func main() {
// "Location": {
// "Column": "1",
// "File": "main.gno",
-// "Line": "20",
+// "Line": "16",
// "PkgPath": "gno.land/r/test"
// }
// },
diff --git a/gnovm/tests/files/break0.gno b/gnovm/tests/files/break0.gno
new file mode 100644
index 00000000000..17d68dc1dbf
--- /dev/null
+++ b/gnovm/tests/files/break0.gno
@@ -0,0 +1,8 @@
+package main
+
+func main() {
+ break
+}
+
+// Error:
+// main/files/break0.gno:4:2: cannot break with no parent loop or switch
diff --git a/gnovm/tests/files/cont3.gno b/gnovm/tests/files/cont3.gno
new file mode 100644
index 00000000000..8a305d4ceb2
--- /dev/null
+++ b/gnovm/tests/files/cont3.gno
@@ -0,0 +1,8 @@
+package main
+
+func main() {
+ continue
+}
+
+// Error:
+// main/files/cont3.gno:4:2: cannot continue with no parent loop
diff --git a/gnovm/tests/files/for20.gno b/gnovm/tests/files/for20.gno
new file mode 100644
index 00000000000..ad9f1f124d0
--- /dev/null
+++ b/gnovm/tests/files/for20.gno
@@ -0,0 +1,15 @@
+package main
+
+func main() {
+ // Ensure `break` works also when we have a label for the for loop.
+
+loop:
+ for {
+ break
+ }
+
+ println("hey")
+}
+
+// Output:
+// hey
diff --git a/gnovm/tests/files/heap_alloc_defer.gno b/gnovm/tests/files/heap_alloc_defer.gno
new file mode 100644
index 00000000000..788d9326695
--- /dev/null
+++ b/gnovm/tests/files/heap_alloc_defer.gno
@@ -0,0 +1,37 @@
+package main
+
+type Foo struct {
+ num int
+ f func()
+}
+
+func main() {
+ s := []Foo{
+ {
+ num: 1,
+ f: func() { println("hello") },
+ },
+ {
+ num: 2,
+ f: func() { println("hola") },
+ },
+ }
+
+ // tt is heap defined every iteration,
+ // different with for loopvar spec.
+ for _, tt := range s {
+ f := func() {
+ println(tt.num)
+ }
+ f()
+ defer func() {
+ tt.f()
+ }()
+ }
+}
+
+// Output:
+// 1
+// 2
+// hola
+// hola
diff --git a/gnovm/tests/files/heap_alloc_defer2.gno b/gnovm/tests/files/heap_alloc_defer2.gno
new file mode 100644
index 00000000000..dc865b1a430
--- /dev/null
+++ b/gnovm/tests/files/heap_alloc_defer2.gno
@@ -0,0 +1,28 @@
+package main
+
+type rand struct{}
+
+func (r *rand) Seed() {
+ println("seed...")
+}
+
+func fromSeed() *rand {
+ return &rand{}
+}
+
+func genResult(s0 string, x int) (int, bool) {
+ z := 0
+ println(z)
+ r := fromSeed()
+ defer func() { r.Seed() }()
+
+ return -1, true
+}
+
+func main() {
+ genResult("hey", 0)
+}
+
+// Output:
+// 0
+// seed...
diff --git a/gnovm/tests/files/heap_alloc_forloop1.gno b/gnovm/tests/files/heap_alloc_forloop1.gno
new file mode 100644
index 00000000000..c166bf8e167
--- /dev/null
+++ b/gnovm/tests/files/heap_alloc_forloop1.gno
@@ -0,0 +1,31 @@
+package main
+
+import "fmt"
+
+var s1 []*int
+
+func forLoopRef() {
+ defer func() {
+ for i, e := range s1 {
+ fmt.Printf("s1[%d] is: %d\n", i, *e)
+ }
+ }()
+
+ for i := 0; i < 3; i++ {
+ s1 = append(s1, &i)
+ }
+}
+
+func main() {
+ forLoopRef()
+}
+
+// go 1.22 loop var is not supported for now.
+
+// Preprocessed:
+// file{ package main; import fmt fmt; var s1 []*((const-type int)); func forLoopRef() { defer func func(){ for i, e := range s1 { fmt.Printf((const ("s1[%d] is: %d\n" string)), (const-type gonative{interface {}})(i), (const-type gonative{interface {}})(*(e))) } }(); for i := (const (0 int)); i<~VPBlock(1,0)> < (const (3 int)); i<~VPBlock(1,0)>++ { s1 = (const (append func(x []*int,args ...*int)(res []*int)))(s1, &(i<~VPBlock(1,0)>)) } }; func main() { forLoopRef() } }
+
+// Output:
+// s1[0] is: 3
+// s1[1] is: 3
+// s1[2] is: 3
diff --git a/gnovm/tests/files/heap_alloc_forloop1a.gno b/gnovm/tests/files/heap_alloc_forloop1a.gno
new file mode 100644
index 00000000000..6d0895902bd
--- /dev/null
+++ b/gnovm/tests/files/heap_alloc_forloop1a.gno
@@ -0,0 +1,39 @@
+package main
+
+import "fmt"
+
+type Int int
+
+var s1 []*Int
+
+func inc2(j *Int) {
+ *j = *j + 2 // Just as an example, increment j by 2.
+}
+
+func forLoopRef() {
+ defer func() {
+ for i, e := range s1 {
+ fmt.Printf("s1[%d] is: %d\n", i, *e)
+ }
+ }()
+
+ for i := Int(0); i < 10; inc2(&i) {
+ s1 = append(s1, &i)
+ }
+}
+
+func main() {
+ forLoopRef()
+}
+
+// go 1.22 loop var is not supported for now.
+
+// Preprocessed:
+// file{ package main; import fmt fmt; type Int (const-type main.Int); var s1 []*(Int); func inc2(j *(Int)) { *(j) = *(j) + (const (2 main.Int)) }; func forLoopRef() { defer func func(){ for i, e := range s1 { fmt.Printf((const ("s1[%d] is: %d\n" string)), (const-type gonative{interface {}})(i), (const-type gonative{interface {}})(*(e))) } }(); for i := (const (0 main.Int)); i<~VPBlock(1,0)> < (const (10 main.Int)); inc2(&(i<~VPBlock(1,0)>)) { s1 = (const (append func(x []*main.Int,args ...*main.Int)(res []*main.Int)))(s1, &(i<~VPBlock(1,0)>)) } }; func main() { forLoopRef() } }
+
+// Output:
+// s1[0] is: 10
+// s1[1] is: 10
+// s1[2] is: 10
+// s1[3] is: 10
+// s1[4] is: 10
diff --git a/gnovm/tests/files/heap_alloc_forloop1b.gno b/gnovm/tests/files/heap_alloc_forloop1b.gno
new file mode 100644
index 00000000000..35b167e4168
--- /dev/null
+++ b/gnovm/tests/files/heap_alloc_forloop1b.gno
@@ -0,0 +1,37 @@
+package main
+
+import "fmt"
+
+var s1 []*int
+
+func forLoopRef() {
+ defer func() {
+ for i, e := range s1 {
+ fmt.Printf("s1[%d] is: %d\n", i, *e)
+ }
+ }()
+
+ for i := 0; i < 3; i++ {
+ r := i
+ r, ok := 0, true
+ println(ok, r)
+ s1 = append(s1, &i)
+ }
+}
+
+func main() {
+ forLoopRef()
+}
+
+// go 1.22 loop var is not supported for now.
+
+// Preprocessed:
+// file{ package main; import fmt fmt; var s1 []*((const-type int)); func forLoopRef() { defer func func(){ for i, e := range s1 { fmt.Printf((const ("s1[%d] is: %d\n" string)), (const-type gonative{interface {}})(i), (const-type gonative{interface {}})(*(e))) } }(); for i := (const (0 int)); i<~VPBlock(1,0)> < (const (3 int)); i<~VPBlock(1,0)>++ { r := i<~VPBlock(1,0)>; r, ok := (const (0 int)), (const (true bool)); (const (println func(xs ...interface{})()))(ok, r); s1 = (const (append func(x []*int,args ...*int)(res []*int)))(s1, &(i<~VPBlock(1,0)>)) } }; func main() { forLoopRef() } }
+
+// Output:
+// true 0
+// true 0
+// true 0
+// s1[0] is: 3
+// s1[1] is: 3
+// s1[2] is: 3
diff --git a/gnovm/tests/files/heap_alloc_forloop2.gno b/gnovm/tests/files/heap_alloc_forloop2.gno
new file mode 100644
index 00000000000..aa7f6e44dd3
--- /dev/null
+++ b/gnovm/tests/files/heap_alloc_forloop2.gno
@@ -0,0 +1,33 @@
+package main
+
+import "fmt"
+
+var s1 []*int
+
+func forLoopRef() {
+ defer func() {
+ for i, e := range s1 {
+ fmt.Printf("s1[%d] is: %d\n", i, *e)
+ }
+ }()
+
+ for i := 0; i < 3; i++ {
+ z := i + 1
+ s1 = append(s1, &z)
+ }
+}
+
+func main() {
+ forLoopRef()
+}
+
+// This does make 'z' NameExprTypeHeapDefine.
+// You can tell by the preprocess printout of z and z<~...>.
+
+// Preprocessed:
+// file{ package main; import fmt fmt; var s1 []*((const-type int)); func forLoopRef() { defer func func(){ for i, e := range s1 { fmt.Printf((const ("s1[%d] is: %d\n" string)), (const-type gonative{interface {}})(i), (const-type gonative{interface {}})(*(e))) } }(); for i := (const (0 int)); i < (const (3 int)); i++ { z := i + (const (1 int)); s1 = (const (append func(x []*int,args ...*int)(res []*int)))(s1, &(z<~VPBlock(1,1)>)) } }; func main() { forLoopRef() } }
+
+// Output:
+// s1[0] is: 1
+// s1[1] is: 2
+// s1[2] is: 3
diff --git a/gnovm/tests/files/heap_alloc_forloop2a.gno b/gnovm/tests/files/heap_alloc_forloop2a.gno
new file mode 100644
index 00000000000..be4b089ccad
--- /dev/null
+++ b/gnovm/tests/files/heap_alloc_forloop2a.gno
@@ -0,0 +1,34 @@
+package main
+
+import "fmt"
+
+var s1 []*int
+
+func forLoopRef() {
+ defer func() {
+ for i, e := range s1 {
+ fmt.Printf("s1[%d] is: %d\n", i, *e)
+ }
+ }()
+
+ for i := 0; i < 3; i++ {
+ z := i
+ s1 = append(s1, &z)
+ z++
+ }
+}
+
+func main() {
+ forLoopRef()
+}
+
+// This does make 'z' NameExprTypeHeapDefine.
+// You can tell by the preprocess printout of z and z<~...>.
+
+// Preprocessed:
+// file{ package main; import fmt fmt; var s1 []*((const-type int)); func forLoopRef() { defer func func(){ for i, e := range s1 { fmt.Printf((const ("s1[%d] is: %d\n" string)), (const-type gonative{interface {}})(i), (const-type gonative{interface {}})(*(e))) } }(); for i := (const (0 int)); i < (const (3 int)); i++ { z := i; s1 = (const (append func(x []*int,args ...*int)(res []*int)))(s1, &(z<~VPBlock(1,1)>)); z<~VPBlock(1,1)>++ } }; func main() { forLoopRef() } }
+
+// Output:
+// s1[0] is: 1
+// s1[1] is: 2
+// s1[2] is: 3
diff --git a/gnovm/tests/files/heap_alloc_forloop3.gno b/gnovm/tests/files/heap_alloc_forloop3.gno
new file mode 100644
index 00000000000..91c9b627120
--- /dev/null
+++ b/gnovm/tests/files/heap_alloc_forloop3.gno
@@ -0,0 +1,33 @@
+package main
+
+type f func()
+
+var fs []f
+
+func forLoopClosure() {
+ defer func() {
+ for _, f := range fs {
+ f()
+ }
+ }()
+
+ for i := 0; i < 3; i++ {
+ z := i
+ fs = append(fs, func() { println(z) })
+ }
+}
+
+func main() {
+ forLoopClosure()
+}
+
+// This does make 'z' NameExprTypeHeapDefine.
+// You can tell by the preprocess printout of z and z<()~...>.
+
+// Preprocessed:
+// file{ package main; type f (const-type main.f); var fs []f; func forLoopClosure() { defer func func(){ for _, f := range fs { f() } }(); for i := (const (0 int)); i < (const (3 int)); i++ { z := i; fs = (const (append func(x []main.f,args ...main.f)(res []main.f)))(fs, (const-type main.f)(func func(){ (const (println func(xs ...interface{})()))(z<~VPBlock(1,0)>) }>)) } }; func main() { forLoopClosure() } }
+
+// Output:
+// 0
+// 1
+// 2
diff --git a/gnovm/tests/files/heap_alloc_forloop3a.gno b/gnovm/tests/files/heap_alloc_forloop3a.gno
new file mode 100644
index 00000000000..fd361e7134e
--- /dev/null
+++ b/gnovm/tests/files/heap_alloc_forloop3a.gno
@@ -0,0 +1,38 @@
+package main
+
+type f func()
+
+var fs []f
+
+func forLoopClosure() {
+ defer func() {
+ for _, f := range fs {
+ f()
+ }
+ }()
+
+ for i := 0; i < 3; i++ {
+ x := i
+ println(x)
+ z := i
+ fs = append(fs, func() { println(z) })
+ }
+}
+
+func main() {
+ forLoopClosure()
+}
+
+// This does make 'z' NameExprTypeHeapDefine.
+// You can tell by the preprocess printout of z and z<()~...>.
+
+// Preprocessed:
+// file{ package main; type f (const-type main.f); var fs []f; func forLoopClosure() { defer func func(){ for _, f := range fs { f() } }(); for i := (const (0 int)); i < (const (3 int)); i++ { x := i; (const (println func(xs ...interface{})()))(x); z := i; fs = (const (append func(x []main.f,args ...main.f)(res []main.f)))(fs, (const-type main.f)(func func(){ (const (println func(xs ...interface{})()))(z<~VPBlock(1,0)>) }>)) } }; func main() { forLoopClosure() } }
+
+// Output:
+// 0
+// 1
+// 2
+// 0
+// 1
+// 2
diff --git a/gnovm/tests/files/heap_alloc_forloop4.gno b/gnovm/tests/files/heap_alloc_forloop4.gno
new file mode 100644
index 00000000000..3cddb1a60fe
--- /dev/null
+++ b/gnovm/tests/files/heap_alloc_forloop4.gno
@@ -0,0 +1,31 @@
+package main
+
+type f func()
+
+var fs []f
+
+func forLoopClosure() {
+ defer func() {
+ for _, f := range fs {
+ f()
+ }
+ }()
+
+ for i := 0; i < 3; i++ {
+ fs = append(fs, func() { println(i) })
+ }
+}
+
+func main() {
+ forLoopClosure()
+}
+
+// go 1.22 loop var is not supported for now.
+
+// Preprocessed:
+// file{ package main; type f (const-type main.f); var fs []f; func forLoopClosure() { defer func func(){ for _, f := range fs { f() } }(); for i := (const (0 int)); i<~VPBlock(1,0)> < (const (3 int)); i<~VPBlock(1,0)>++ { fs = (const (append func(x []main.f,args ...main.f)(res []main.f)))(fs, (const-type main.f)(func func(){ (const (println func(xs ...interface{})()))(i<~VPBlock(1,0)>) }>)) } }; func main() { forLoopClosure() } }
+
+// Output:
+// 3
+// 3
+// 3
diff --git a/gnovm/tests/files/heap_alloc_forloop5.gno b/gnovm/tests/files/heap_alloc_forloop5.gno
new file mode 100644
index 00000000000..4f563ec866b
--- /dev/null
+++ b/gnovm/tests/files/heap_alloc_forloop5.gno
@@ -0,0 +1,32 @@
+package main
+
+type f func()
+
+var fs []f
+
+func forLoopClosure() {
+ defer func() {
+ for _, f := range fs {
+ f()
+ }
+ }()
+
+ for i := 0; i < 3; i++ {
+ fs = append(fs, func() {
+ z := i
+ println(z)
+ })
+ }
+}
+
+func main() {
+ forLoopClosure()
+}
+
+// Preprocessed:
+// file{ package main; type f (const-type main.f); var fs []f; func forLoopClosure() { defer func func(){ for _, f := range fs { f() } }(); for i := (const (0 int)); i<~VPBlock(1,0)> < (const (3 int)); i<~VPBlock(1,0)>++ { fs = (const (append func(x []main.f,args ...main.f)(res []main.f)))(fs, (const-type main.f)(func func(){ z := i<~VPBlock(1,1)>; (const (println func(xs ...interface{})()))(z) }>)) } }; func main() { forLoopClosure() } }
+
+// Output:
+// 3
+// 3
+// 3
diff --git a/gnovm/tests/files/heap_alloc_forloop5a.gno b/gnovm/tests/files/heap_alloc_forloop5a.gno
new file mode 100644
index 00000000000..039be2b86a8
--- /dev/null
+++ b/gnovm/tests/files/heap_alloc_forloop5a.gno
@@ -0,0 +1,33 @@
+package main
+
+type f func()
+
+var fs []f
+
+func forLoopClosure() {
+ defer func() {
+ for _, f := range fs {
+ f()
+ }
+ }()
+
+ for i := 0; i < 3; i++ {
+ x := i
+ fs = append(fs, func() {
+ z := x
+ println(z)
+ })
+ }
+}
+
+func main() {
+ forLoopClosure()
+}
+
+// Preprocessed:
+// file{ package main; type f (const-type main.f); var fs []f; func forLoopClosure() { defer func func(){ for _, f := range fs { f() } }(); for i := (const (0 int)); i < (const (3 int)); i++ { x := i; fs = (const (append func(x []main.f,args ...main.f)(res []main.f)))(fs, (const-type main.f)(func func(){ z := x<~VPBlock(1,1)>; (const (println func(xs ...interface{})()))(z) }>)) } }; func main() { forLoopClosure() } }
+
+// Output:
+// 0
+// 1
+// 2
diff --git a/gnovm/tests/files/heap_alloc_forloop6.gno b/gnovm/tests/files/heap_alloc_forloop6.gno
new file mode 100644
index 00000000000..6cfa8a65fc8
--- /dev/null
+++ b/gnovm/tests/files/heap_alloc_forloop6.gno
@@ -0,0 +1,25 @@
+package main
+
+func main() {
+ var fns []func() int
+
+ for i := 0; i < 3; i++ {
+ z := i
+ f := func() int {
+ return z
+ }
+ fns = append(fns, f)
+ }
+
+ for _, fn := range fns {
+ println(fn())
+ }
+}
+
+// Preprocessed:
+// file{ package main; func main() { var fns []func() (const-type int); for i := (const (0 int)); i < (const (3 int)); i++ { z := i; f := func func() (const-type int){ return z<~VPBlock(1,1)> }>; fns = (const (append func(x []func()( int),args ...func()( int))(res []func()( int))))(fns, f) }; for _, fn := range fns { (const (println func(xs ...interface{})()))(fn()) } } }
+
+// Output:
+// 0
+// 1
+// 2
diff --git a/gnovm/tests/files/heap_alloc_forloop6a.gno b/gnovm/tests/files/heap_alloc_forloop6a.gno
new file mode 100644
index 00000000000..6365fcd8c62
--- /dev/null
+++ b/gnovm/tests/files/heap_alloc_forloop6a.gno
@@ -0,0 +1,26 @@
+package main
+
+func main() {
+ var fns []func() int
+ for i := 0; i < 5; i++ {
+ f := func() int {
+ return i
+ }
+ fns = append(fns, f)
+ }
+ for _, fn := range fns {
+ println(fn())
+ }
+}
+
+// go 1.22 loop var is not supported for now.
+
+// Preprocessed:
+// file{ package main; func main() { var fns []func() (const-type int); for i := (const (0 int)); i<~VPBlock(1,0)> < (const (5 int)); i<~VPBlock(1,0)>++ { f := func func() (const-type int){ return i<~VPBlock(1,1)> }>; fns = (const (append func(x []func()( int),args ...func()( int))(res []func()( int))))(fns, f) }; for _, fn := range fns { (const (println func(xs ...interface{})()))(fn()) } } }
+
+// Output:
+// 5
+// 5
+// 5
+// 5
+// 5
diff --git a/gnovm/tests/files/heap_alloc_forloop6b.gno b/gnovm/tests/files/heap_alloc_forloop6b.gno
new file mode 100644
index 00000000000..73d9e5cd6a7
--- /dev/null
+++ b/gnovm/tests/files/heap_alloc_forloop6b.gno
@@ -0,0 +1,25 @@
+package main
+
+func main() {
+ var fns []func() int
+ for i := 0; i < 2; i++ {
+ x := i
+ y := 0
+ f := func() int {
+ x += y
+ x += 1
+ return x
+ }
+ fns = append(fns, f)
+ }
+ for _, fn := range fns {
+ println(fn())
+ }
+}
+
+// Preprocessed:
+// file{ package main; func main() { var fns []func() (const-type int); for i := (const (0 int)); i < (const (2 int)); i++ { x := i; y := (const (0 int)); f := func func() (const-type int){ x<~VPBlock(1,1)> += y<~VPBlock(1,2)>; x<~VPBlock(1,1)> += (const (1 int)); return x<~VPBlock(1,1)> }, y<()~VPBlock(1,2)>>; fns = (const (append func(x []func()( int),args ...func()( int))(res []func()( int))))(fns, f) }; for _, fn := range fns { (const (println func(xs ...interface{})()))(fn()) } } }
+
+// Output:
+// 1
+// 2
diff --git a/gnovm/tests/files/heap_alloc_forloop6c.gno b/gnovm/tests/files/heap_alloc_forloop6c.gno
new file mode 100644
index 00000000000..f8d2d410f6c
--- /dev/null
+++ b/gnovm/tests/files/heap_alloc_forloop6c.gno
@@ -0,0 +1,23 @@
+package main
+
+func main() {
+ var fns []func() int
+ for i := 0; i < 2; i++ {
+ f := func() int {
+ return i
+ }
+ fns = append(fns, f)
+ }
+ for _, fn := range fns {
+ println(fn())
+ }
+}
+
+// go 1.22 loop var is not supported for now.
+
+// Preprocessed:
+// file{ package main; func main() { var fns []func() (const-type int); for i := (const (0 int)); i<~VPBlock(1,0)> < (const (2 int)); i<~VPBlock(1,0)>++ { f := func func() (const-type int){ return i<~VPBlock(1,1)> }>; fns = (const (append func(x []func()( int),args ...func()( int))(res []func()( int))))(fns, f) }; for _, fn := range fns { (const (println func(xs ...interface{})()))(fn()) } } }
+
+// Output:
+// 2
+// 2
diff --git a/gnovm/tests/files/heap_alloc_forloop6f.gno b/gnovm/tests/files/heap_alloc_forloop6f.gno
new file mode 100644
index 00000000000..fcc2cdfdcc1
--- /dev/null
+++ b/gnovm/tests/files/heap_alloc_forloop6f.gno
@@ -0,0 +1,26 @@
+package main
+
+func main() {
+ var fns []func() int
+ for i := 0; i < 5; i++ {
+ var x int
+ f := func() int {
+ return x
+ }
+ x = i
+ fns = append(fns, f)
+ }
+ for _, fn := range fns {
+ println(fn())
+ }
+}
+
+// Preprocessed:
+// file{ package main; func main() { var fns []func() (const-type int); for i := (const (0 int)); i < (const (5 int)); i++ { var x (const-type int); f := func func() (const-type int){ return x<~VPBlock(1,1)> }>; x<~VPBlock(1,1)> = i; fns = (const (append func(x []func()( int),args ...func()( int))(res []func()( int))))(fns, f) }; for _, fn := range fns { (const (println func(xs ...interface{})()))(fn()) } } }
+
+// Output:
+// 0
+// 1
+// 2
+// 3
+// 4
diff --git a/gnovm/tests/files/heap_alloc_forloop6g.gno b/gnovm/tests/files/heap_alloc_forloop6g.gno
new file mode 100644
index 00000000000..4ff7856c97c
--- /dev/null
+++ b/gnovm/tests/files/heap_alloc_forloop6g.gno
@@ -0,0 +1,27 @@
+package main
+
+func main() {
+ var fns []func() int
+ for i := 0; i < 5; i++ {
+ x := i
+ { // another block
+ f := func() int {
+ return x
+ }
+ fns = append(fns, f)
+ }
+ }
+ for _, fn := range fns {
+ println(fn())
+ }
+}
+
+// Preprocessed:
+// file{ package main; func main() { var fns []func() (const-type int); for i := (const (0 int)); i < (const (5 int)); i++ { x := i; { f := func func() (const-type int){ return x<~VPBlock(1,1)> }>; fns = (const (append func(x []func()( int),args ...func()( int))(res []func()( int))))(fns, f) } }; for _, fn := range fns { (const (println func(xs ...interface{})()))(fn()) } } }
+
+// Output:
+// 0
+// 1
+// 2
+// 3
+// 4
diff --git a/gnovm/tests/files/heap_alloc_forloop6h.gno b/gnovm/tests/files/heap_alloc_forloop6h.gno
new file mode 100644
index 00000000000..75b84bebf91
--- /dev/null
+++ b/gnovm/tests/files/heap_alloc_forloop6h.gno
@@ -0,0 +1,33 @@
+package main
+
+func main() {
+ var fns []func() int
+ for i := 0; i < 2; i++ {
+ x := i
+ for j := 0; j < 2; j++ {
+ y := j
+ f := func() int {
+ return x + y
+ }
+ fns = append(fns, f)
+ }
+ }
+ for _, fn := range fns {
+ println(fn())
+ }
+}
+
+// Preprocessed:
+// file{ package main; func main() { var fns []func() (const-type int); for i := (const (0 int)); i < (const (2 int)); i++ { x := i; for j := (const (0 int)); j < (const (2 int)); j++ { y := j; f := func func() (const-type int){ return x<~VPBlock(1,1)> + y<~VPBlock(1,2)> }, y<()~VPBlock(1,1)>>; fns = (const (append func(x []func()( int),args ...func()( int))(res []func()( int))))(fns, f) } }; for _, fn := range fns { (const (println func(xs ...interface{})()))(fn()) } } }
+
+// Go Output:
+// 0
+// 1
+// 1
+// 2
+
+// Output:
+// 0
+// 1
+// 1
+// 2
diff --git a/gnovm/tests/files/heap_alloc_forloop6h0.gno b/gnovm/tests/files/heap_alloc_forloop6h0.gno
new file mode 100644
index 00000000000..0225bd62cf6
--- /dev/null
+++ b/gnovm/tests/files/heap_alloc_forloop6h0.gno
@@ -0,0 +1,27 @@
+package main
+
+func main() {
+ var fns []func() int
+ for i := 0; i < 2; i++ {
+ for j := 0; j < 2; j++ {
+ f := func() int {
+ return i + j
+ }
+ fns = append(fns, f)
+ }
+ }
+ for _, fn := range fns {
+ println(fn())
+ }
+}
+
+// go 1.22 loop var is not supported for now.
+
+// Preprocessed:
+// file{ package main; func main() { var fns []func() (const-type int); for i := (const (0 int)); i<~VPBlock(1,0)> < (const (2 int)); i<~VPBlock(1,0)>++ { for j := (const (0 int)); j<~VPBlock(1,0)> < (const (2 int)); j<~VPBlock(1,0)>++ { f := func func() (const-type int){ return i<~VPBlock(1,1)> + j<~VPBlock(1,2)> }