From ddfff6096fc96631f15a9fae1bd6f72e15d40e13 Mon Sep 17 00:00:00 2001 From: Morgan Date: Mon, 13 Jan 2025 11:35:05 +0100 Subject: [PATCH 01/18] chore(gnovm): remove unused attributes (#3492) --- gnovm/pkg/gnolang/nodes.go | 6 ++---- gnovm/pkg/gnolang/preprocess.go | 12 +----------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index 0496d37ed72..b85d1ac7026 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -153,10 +153,8 @@ const ( 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_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" ATTR_LAST_BLOCK_STMT GnoAttribute = "ATTR_LAST_BLOCK_STMT" ) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 79695d8888a..ddfd1851989 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -2723,17 +2723,10 @@ func findGotoLoopDefines(ctx BlockNode, bn BlockNode) { // Otherwise mark stmt as gotoloop. case Stmt: // we're done if we - // re-encounter origGotoStmtm. + // re-encounter origGotoStmt. 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 @@ -4804,9 +4797,6 @@ func setNodeLines(n Node) { // based on sparse expectations on block nodes, and ensures uniqueness of BlockNode.Locations. // Ensures uniqueness of BlockNode.Locations. func setNodeLocations(pkgPath string, fileName string, n Node) { - if n.GetAttribute(ATTR_LOCATIONED) == true { - return // locations already set (typically n is a filenode). - } if pkgPath == "" || fileName == "" { panic("missing package path or file name") } From 663edac6246046f6cac0af912b0c38722f5eaa66 Mon Sep 17 00:00:00 2001 From: 6h057 <15034695+omarsy@users.noreply.github.com> Date: Mon, 13 Jan 2025 12:11:33 +0100 Subject: [PATCH 02/18] feat(gnovm): add software floating point package (#3185) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes: #312 The idea to use softfloat and work originates from this PR: https://github.com/gnolang/gno/pull/2863
Contributors' checklist... - [ ] Added new tests, or not needed, or not feasible - [ ] Provided an example (e.g. screenshot) to aid review or the PR is self-explanatory - [ ] Updated the official documentation or not needed - [ ] No breaking changes were made, or a `BREAKING CHANGE: xxx` message was included in the description - [ ] Added references to related issues and PRs - [ ] Provided any useful hints for running manual tests
--------- Co-authored-by: Morgan Bazalgette Co-authored-by: Miloš Živković --- gno.land/pkg/sdk/vm/convert.go | 5 +- gnovm/pkg/gnolang/frame.go | 5 +- gnovm/pkg/gnolang/gonative.go | 19 +- gnovm/pkg/gnolang/internal/softfloat/copy.sh | 32 + .../internal/softfloat/runtime_softfloat64.go | 631 ++++++++++++++++++ .../softfloat/runtime_softfloat64_test.go | 204 ++++++ .../gnolang/internal/softfloat/softfloat.go | 134 ++++ gnovm/pkg/gnolang/op_binary.go | 46 +- gnovm/pkg/gnolang/op_inc_dec.go | 9 +- gnovm/pkg/gnolang/op_unary.go | 5 +- gnovm/pkg/gnolang/values.go | 21 +- gnovm/pkg/gnolang/values_conversions.go | 258 +++---- gnovm/pkg/gnolang/values_conversions_test.go | 3 +- gnovm/pkg/gnolang/values_string.go | 9 +- gnovm/tests/files/float8.gno | 166 +++++ 15 files changed, 1360 insertions(+), 187 deletions(-) create mode 100644 gnovm/pkg/gnolang/internal/softfloat/copy.sh create mode 100644 gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64.go create mode 100644 gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64_test.go create mode 100644 gnovm/pkg/gnolang/internal/softfloat/softfloat.go create mode 100644 gnovm/tests/files/float8.gno diff --git a/gno.land/pkg/sdk/vm/convert.go b/gno.land/pkg/sdk/vm/convert.go index cafb6cad67f..dbaabcfbc4b 100644 --- a/gno.land/pkg/sdk/vm/convert.go +++ b/gno.land/pkg/sdk/vm/convert.go @@ -3,6 +3,7 @@ package vm import ( "encoding/base64" "fmt" + "math" "strconv" "strings" @@ -143,11 +144,11 @@ func convertArgToGno(arg string, argT gno.Type) (tv gno.TypedValue) { return case gno.Float32Type: value := convertFloat(arg, 32) - tv.SetFloat32(float32(value)) + tv.SetFloat32(math.Float32bits(float32(value))) return case gno.Float64Type: value := convertFloat(arg, 64) - tv.SetFloat64(value) + tv.SetFloat64(math.Float64bits(value)) return default: panic(fmt.Sprintf("unexpected primitive type %s", bt.String())) diff --git a/gnovm/pkg/gnolang/frame.go b/gnovm/pkg/gnolang/frame.go index 2ac1027eb32..60f19979b7a 100644 --- a/gnovm/pkg/gnolang/frame.go +++ b/gnovm/pkg/gnolang/frame.go @@ -2,6 +2,7 @@ package gnolang import ( "fmt" + "math" "strings" ) @@ -207,9 +208,9 @@ func toConstExpTrace(cte *ConstExpr) string { case Uint64Type: return fmt.Sprintf("%d", tv.GetUint64()) case Float32Type: - return fmt.Sprintf("%v", tv.GetFloat32()) + return fmt.Sprintf("%v", math.Float32frombits(tv.GetFloat32())) case Float64Type: - return fmt.Sprintf("%v", tv.GetFloat64()) + return fmt.Sprintf("%v", math.Float64frombits(tv.GetFloat64())) } } diff --git a/gnovm/pkg/gnolang/gonative.go b/gnovm/pkg/gnolang/gonative.go index 5a39c76b5e1..85fc8b70051 100644 --- a/gnovm/pkg/gnolang/gonative.go +++ b/gnovm/pkg/gnolang/gonative.go @@ -2,7 +2,10 @@ package gnolang import ( "fmt" + "math" "reflect" + + "github.com/gnolang/gno/gnovm/pkg/gnolang/internal/softfloat" ) // NOTE @@ -329,9 +332,9 @@ func go2GnoValue(alloc *Allocator, rv reflect.Value) (tv TypedValue) { case reflect.Uint64: tv.SetUint64(rv.Uint()) case reflect.Float32: - tv.SetFloat32(float32(rv.Float())) + tv.SetFloat32(softfloat.F64to32(math.Float64bits(rv.Float()))) case reflect.Float64: - tv.SetFloat64(rv.Float()) + tv.SetFloat64(math.Float64bits(rv.Float())) case reflect.Array: tv.V = alloc.NewNative(rv) case reflect.Slice: @@ -428,11 +431,11 @@ func go2GnoValueUpdate(alloc *Allocator, rlm *Realm, lvl int, tv *TypedValue, rv } case Float32Kind: if lvl != 0 { - tv.SetFloat32(float32(rv.Float())) + tv.SetFloat32(softfloat.F64to32(math.Float64bits(rv.Float()))) } case Float64Kind: if lvl != 0 { - tv.SetFloat64(rv.Float()) + tv.SetFloat64(math.Float64bits(rv.Float())) } case BigintKind: panic("not yet implemented") @@ -644,9 +647,9 @@ func go2GnoValue2(alloc *Allocator, store Store, rv reflect.Value, recursive boo case reflect.Uint64: tv.SetUint64(rv.Uint()) case reflect.Float32: - tv.SetFloat32(float32(rv.Float())) + tv.SetFloat32(softfloat.F64to32(math.Float64bits(rv.Float()))) case reflect.Float64: - tv.SetFloat64(rv.Float()) + tv.SetFloat64(math.Float64bits(rv.Float())) case reflect.Array: rvl := rv.Len() if rv.Type().Elem().Kind() == reflect.Uint8 { @@ -1049,9 +1052,9 @@ func gno2GoValue(tv *TypedValue, rv reflect.Value) (ret reflect.Value) { case Uint64Type: rv.SetUint(tv.GetUint64()) case Float32Type: - rv.SetFloat(float64(tv.GetFloat32())) + rv.SetFloat(math.Float64frombits(softfloat.F32to64(tv.GetFloat32()))) case Float64Type: - rv.SetFloat(tv.GetFloat64()) + rv.SetFloat(math.Float64frombits(tv.GetFloat64())) default: panic(fmt.Sprintf( "unexpected type %s", diff --git a/gnovm/pkg/gnolang/internal/softfloat/copy.sh b/gnovm/pkg/gnolang/internal/softfloat/copy.sh new file mode 100644 index 00000000000..6d2a8f80462 --- /dev/null +++ b/gnovm/pkg/gnolang/internal/softfloat/copy.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +# softfloat64.go: +# - add header +# - change package name +cat > runtime_softfloat64.go << EOF +// Code generated by copy.sh. DO NOT EDIT. +// This file is copied from \$GOROOT/src/runtime/softfloat64.go. +// It is the software floating point implementation used by the Go runtime. + +EOF +cat "$GOROOT/src/runtime/softfloat64.go" >> ./runtime_softfloat64.go +sed -i 's/^package runtime$/package softfloat/' runtime_softfloat64.go + +# softfloat64_test.go: +# - add header +# - change package name +# - change import to right package +# - change GOARCH to runtime.GOARCH, and import the "runtime" package +cat > runtime_softfloat64_test.go << EOF +// Code generated by copy.sh. DO NOT EDIT. +// This file is copied from \$GOROOT/src/runtime/softfloat64_test.go. +// It is the tests for the software floating point implementation +// used by the Go runtime. + +EOF +cat "$GOROOT/src/runtime/softfloat64_test.go" >> ./runtime_softfloat64_test.go +sed -i 's/^package runtime_test$/package softfloat_test/ +s#^\t\. "runtime"$#\t. "github.com/gnolang/gno/gnovm/pkg/gnolang/internal/softfloat"# +s/GOARCH/runtime.GOARCH/g +16a\ + "runtime"' runtime_softfloat64_test.go \ No newline at end of file diff --git a/gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64.go b/gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64.go new file mode 100644 index 00000000000..cf2ad5afd8a --- /dev/null +++ b/gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64.go @@ -0,0 +1,631 @@ +// Code generated by copy.sh. DO NOT EDIT. +// This file is copied from $GOROOT/src/runtime/softfloat64.go. +// It is the software floating point implementation used by the Go runtime. + +// Copyright 2010 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. + +// Software IEEE754 64-bit floating point. +// Only referred to (and thus linked in) by softfloat targets +// and by tests in this directory. + +package softfloat + +const ( + mantbits64 uint = 52 + expbits64 uint = 11 + bias64 = -1<<(expbits64-1) + 1 + + nan64 uint64 = (1<>mantbits64) & (1<>mantbits32) & (1<= 4<>= 1 + exp++ + } + if mant >= 2<= 4<>= 1 + exp++ + } + } + mant >>= 1 + exp++ + } + if exp >= 1<>= 1 + exp++ + } + if mant&1 != 0 && (trunc != 0 || mant&2 != 0) { + mant++ + } + mant >>= 1 + exp++ + if mant < 1<= 4<>= 1 + exp++ + } + if mant >= 2<= 4<>= 1 + exp++ + } + } + mant >>= 1 + exp++ + } + if exp >= 1<>= 1 + exp++ + } + if mant&1 != 0 && (trunc != 0 || mant&2 != 0) { + mant++ + } + mant >>= 1 + exp++ + if mant < 1<>= shift + if fs == gs { + fm += gm + } else { + fm -= gm + if trunc != 0 { + fm-- + } + } + if fm == 0 { + fs = 0 + } + return fpack64(fs, fm, fe-2, trunc) +} + +func fsub64(f, g uint64) uint64 { + return fadd64(f, fneg64(g)) +} + +func fneg64(f uint64) uint64 { + return f ^ (1 << (mantbits64 + expbits64)) +} + +func fmul64(f, g uint64) uint64 { + fs, fm, fe, fi, fn := funpack64(f) + gs, gm, ge, gi, gn := funpack64(g) + + // Special cases. + switch { + case fn || gn: // NaN * g or f * NaN = NaN + return nan64 + + case fi && gi: // Inf * Inf = Inf (with sign adjusted) + return f ^ gs + + case fi && gm == 0, fm == 0 && gi: // 0 * Inf = Inf * 0 = NaN + return nan64 + + case fm == 0: // 0 * x = 0 (with sign adjusted) + return f ^ gs + + case gm == 0: // x * 0 = 0 (with sign adjusted) + return g ^ fs + } + + // 53-bit * 53-bit = 107- or 108-bit + lo, hi := mullu(fm, gm) + shift := mantbits64 - 1 + trunc := lo & (1<>shift + return fpack64(fs^gs, mant, fe+ge-1, trunc) +} + +func fdiv64(f, g uint64) uint64 { + fs, fm, fe, fi, fn := funpack64(f) + gs, gm, ge, gi, gn := funpack64(g) + + // Special cases. + switch { + case fn || gn: // NaN / g = f / NaN = NaN + return nan64 + + case fi && gi: // ±Inf / ±Inf = NaN + return nan64 + + case !fi && !gi && fm == 0 && gm == 0: // 0 / 0 = NaN + return nan64 + + case fi, !gi && gm == 0: // Inf / g = f / 0 = Inf + return fs ^ gs ^ inf64 + + case gi, fm == 0: // f / Inf = 0 / g = Inf + return fs ^ gs ^ 0 + } + _, _, _, _ = fi, fn, gi, gn + + // 53-bit<<54 / 53-bit = 53- or 54-bit. + shift := mantbits64 + 2 + q, r := divlu(fm>>(64-shift), fm<> 32) + if fi { + return fs32 ^ inf32 + } + const d = mantbits64 - mantbits32 - 1 + return fpack32(fs32, uint32(fm>>d), fe-1, uint32(fm&(1< gs: // f < 0, g > 0 + return -1, false + + case fs < gs: // f > 0, g < 0 + return +1, false + + // Same sign, not NaN. + // Can compare encodings directly now. + // Reverse for sign. + case fs == 0 && f < g, fs != 0 && f > g: + return -1, false + + case fs == 0 && f > g, fs != 0 && f < g: + return +1, false + } + + // f == g + return 0, false +} + +func f64toint(f uint64) (val int64, ok bool) { + fs, fm, fe, fi, fn := funpack64(f) + + switch { + case fi, fn: // NaN + return 0, false + + case fe < -1: // f < 0.5 + return 0, false + + case fe > 63: // f >= 2^63 + if fs != 0 && fm == 0 { // f == -2^63 + return -1 << 63, true + } + if fs != 0 { + return 0, false + } + return 0, false + } + + for fe > int(mantbits64) { + fe-- + fm <<= 1 + } + for fe < int(mantbits64) { + fe++ + fm >>= 1 + } + val = int64(fm) + if fs != 0 { + val = -val + } + return val, true +} + +func fintto64(val int64) (f uint64) { + fs := uint64(val) & (1 << 63) + mant := uint64(val) + if fs != 0 { + mant = -mant + } + return fpack64(fs, mant, int(mantbits64), 0) +} +func fintto32(val int64) (f uint32) { + fs := uint64(val) & (1 << 63) + mant := uint64(val) + if fs != 0 { + mant = -mant + } + // Reduce mantissa size until it fits into a uint32. + // Keep track of the bits we throw away, and if any are + // nonzero or them into the lowest bit. + exp := int(mantbits32) + var trunc uint32 + for mant >= 1<<32 { + trunc |= uint32(mant) & 1 + mant >>= 1 + exp++ + } + + return fpack32(uint32(fs>>32), uint32(mant), exp, trunc) +} + +// 64x64 -> 128 multiply. +// adapted from hacker's delight. +func mullu(u, v uint64) (lo, hi uint64) { + const ( + s = 32 + mask = 1<> s + v0 := v & mask + v1 := v >> s + w0 := u0 * v0 + t := u1*v0 + w0>>s + w1 := t & mask + w2 := t >> s + w1 += u0 * v1 + return u * v, u1*v1 + w2 + w1>>s +} + +// 128/64 -> 64 quotient, 64 remainder. +// adapted from hacker's delight +func divlu(u1, u0, v uint64) (q, r uint64) { + const b = 1 << 32 + + if u1 >= v { + return 1<<64 - 1, 1<<64 - 1 + } + + // s = nlz(v); v <<= s + s := uint(0) + for v&(1<<63) == 0 { + s++ + v <<= 1 + } + + vn1 := v >> 32 + vn0 := v & (1<<32 - 1) + un32 := u1<>(64-s) + un10 := u0 << s + un1 := un10 >> 32 + un0 := un10 & (1<<32 - 1) + q1 := un32 / vn1 + rhat := un32 - q1*vn1 + +again1: + if q1 >= b || q1*vn0 > b*rhat+un1 { + q1-- + rhat += vn1 + if rhat < b { + goto again1 + } + } + + un21 := un32*b + un1 - q1*v + q0 := un21 / vn1 + rhat = un21 - q0*vn1 + +again2: + if q0 >= b || q0*vn0 > b*rhat+un0 { + q0-- + rhat += vn1 + if rhat < b { + goto again2 + } + } + + return q1*b + q0, (un21*b + un0 - q0*v) >> s +} + +func fadd32(x, y uint32) uint32 { + return f64to32(fadd64(f32to64(x), f32to64(y))) +} + +func fmul32(x, y uint32) uint32 { + return f64to32(fmul64(f32to64(x), f32to64(y))) +} + +func fdiv32(x, y uint32) uint32 { + // TODO: are there double-rounding problems here? See issue 48807. + return f64to32(fdiv64(f32to64(x), f32to64(y))) +} + +func feq32(x, y uint32) bool { + cmp, nan := fcmp64(f32to64(x), f32to64(y)) + return cmp == 0 && !nan +} + +func fgt32(x, y uint32) bool { + cmp, nan := fcmp64(f32to64(x), f32to64(y)) + return cmp >= 1 && !nan +} + +func fge32(x, y uint32) bool { + cmp, nan := fcmp64(f32to64(x), f32to64(y)) + return cmp >= 0 && !nan +} + +func feq64(x, y uint64) bool { + cmp, nan := fcmp64(x, y) + return cmp == 0 && !nan +} + +func fgt64(x, y uint64) bool { + cmp, nan := fcmp64(x, y) + return cmp >= 1 && !nan +} + +func fge64(x, y uint64) bool { + cmp, nan := fcmp64(x, y) + return cmp >= 0 && !nan +} + +func fint32to32(x int32) uint32 { + return fintto32(int64(x)) +} + +func fint32to64(x int32) uint64 { + return fintto64(int64(x)) +} + +func fint64to32(x int64) uint32 { + return fintto32(x) +} + +func fint64to64(x int64) uint64 { + return fintto64(x) +} + +func f32toint32(x uint32) int32 { + val, _ := f64toint(f32to64(x)) + return int32(val) +} + +func f32toint64(x uint32) int64 { + val, _ := f64toint(f32to64(x)) + return val +} + +func f64toint32(x uint64) int32 { + val, _ := f64toint(x) + return int32(val) +} + +func f64toint64(x uint64) int64 { + val, _ := f64toint(x) + return val +} + +func f64touint64(x uint64) uint64 { + var m uint64 = 0x43e0000000000000 // float64 1<<63 + if fgt64(m, x) { + return uint64(f64toint64(x)) + } + y := fadd64(x, -m) + z := uint64(f64toint64(y)) + return z | (1 << 63) +} + +func f32touint64(x uint32) uint64 { + var m uint32 = 0x5f000000 // float32 1<<63 + if fgt32(m, x) { + return uint64(f32toint64(x)) + } + y := fadd32(x, -m) + z := uint64(f32toint64(y)) + return z | (1 << 63) +} + +func fuint64to64(x uint64) uint64 { + if int64(x) >= 0 { + return fint64to64(int64(x)) + } + // See ../cmd/compile/internal/ssagen/ssa.go:uint64Tofloat + y := x & 1 + z := x >> 1 + z = z | y + r := fint64to64(int64(z)) + return fadd64(r, r) +} + +func fuint64to32(x uint64) uint32 { + if int64(x) >= 0 { + return fint64to32(int64(x)) + } + // See ../cmd/compile/internal/ssagen/ssa.go:uint64Tofloat + y := x & 1 + z := x >> 1 + z = z | y + r := fint64to32(int64(z)) + return fadd32(r, r) +} diff --git a/gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64_test.go b/gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64_test.go new file mode 100644 index 00000000000..c57fe08b0ef --- /dev/null +++ b/gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64_test.go @@ -0,0 +1,204 @@ +// Code generated by copy.sh. DO NOT EDIT. +// This file is copied from $GOROOT/src/runtime/softfloat64_test.go. +// It is the tests for the software floating point implementation +// used by the Go runtime. + +// Copyright 2010 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 softfloat_test + +import ( + "math" + "math/rand" + . "github.com/gnolang/gno/gnovm/pkg/gnolang/internal/softfloat" + "testing" + "runtime" +) + +// turn uint64 op into float64 op +func fop(f func(x, y uint64) uint64) func(x, y float64) float64 { + return func(x, y float64) float64 { + bx := math.Float64bits(x) + by := math.Float64bits(y) + return math.Float64frombits(f(bx, by)) + } +} + +func add(x, y float64) float64 { return x + y } +func sub(x, y float64) float64 { return x - y } +func mul(x, y float64) float64 { return x * y } +func div(x, y float64) float64 { return x / y } + +func TestFloat64(t *testing.T) { + base := []float64{ + 0, + math.Copysign(0, -1), + -1, + 1, + math.NaN(), + math.Inf(+1), + math.Inf(-1), + 0.1, + 1.5, + 1.9999999999999998, // all 1s mantissa + 1.3333333333333333, // 1.010101010101... + 1.1428571428571428, // 1.001001001001... + 1.112536929253601e-308, // first normal + 2, + 4, + 8, + 16, + 32, + 64, + 128, + 256, + 3, + 12, + 1234, + 123456, + -0.1, + -1.5, + -1.9999999999999998, + -1.3333333333333333, + -1.1428571428571428, + -2, + -3, + 1e-200, + 1e-300, + 1e-310, + 5e-324, + 1e-105, + 1e-305, + 1e+200, + 1e+306, + 1e+307, + 1e+308, + } + all := make([]float64, 200) + copy(all, base) + for i := len(base); i < len(all); i++ { + all[i] = rand.NormFloat64() + } + + test(t, "+", add, fop(Fadd64), all) + test(t, "-", sub, fop(Fsub64), all) + if runtime.GOARCH != "386" { // 386 is not precise! + test(t, "*", mul, fop(Fmul64), all) + test(t, "/", div, fop(Fdiv64), all) + } +} + +// 64 -hw-> 32 -hw-> 64 +func trunc32(f float64) float64 { + return float64(float32(f)) +} + +// 64 -sw->32 -hw-> 64 +func to32sw(f float64) float64 { + return float64(math.Float32frombits(F64to32(math.Float64bits(f)))) +} + +// 64 -hw->32 -sw-> 64 +func to64sw(f float64) float64 { + return math.Float64frombits(F32to64(math.Float32bits(float32(f)))) +} + +// float64 -hw-> int64 -hw-> float64 +func hwint64(f float64) float64 { + return float64(int64(f)) +} + +// float64 -hw-> int32 -hw-> float64 +func hwint32(f float64) float64 { + return float64(int32(f)) +} + +// float64 -sw-> int64 -hw-> float64 +func toint64sw(f float64) float64 { + i, ok := F64toint(math.Float64bits(f)) + if !ok { + // There's no right answer for out of range. + // Match the hardware to pass the test. + i = int64(f) + } + return float64(i) +} + +// float64 -hw-> int64 -sw-> float64 +func fromint64sw(f float64) float64 { + return math.Float64frombits(Fintto64(int64(f))) +} + +var nerr int + +func err(t *testing.T, format string, args ...any) { + t.Errorf(format, args...) + + // cut errors off after a while. + // otherwise we spend all our time + // allocating memory to hold the + // formatted output. + if nerr++; nerr >= 10 { + t.Fatal("too many errors") + } +} + +func test(t *testing.T, op string, hw, sw func(float64, float64) float64, all []float64) { + for _, f := range all { + for _, g := range all { + h := hw(f, g) + s := sw(f, g) + if !same(h, s) { + err(t, "%g %s %g = sw %g, hw %g\n", f, op, g, s, h) + } + testu(t, "to32", trunc32, to32sw, h) + testu(t, "to64", trunc32, to64sw, h) + testu(t, "toint64", hwint64, toint64sw, h) + testu(t, "fromint64", hwint64, fromint64sw, h) + testcmp(t, f, h) + testcmp(t, h, f) + testcmp(t, g, h) + testcmp(t, h, g) + } + } +} + +func testu(t *testing.T, op string, hw, sw func(float64) float64, v float64) { + h := hw(v) + s := sw(v) + if !same(h, s) { + err(t, "%s %g = sw %g, hw %g\n", op, v, s, h) + } +} + +func hwcmp(f, g float64) (cmp int, isnan bool) { + switch { + case f < g: + return -1, false + case f > g: + return +1, false + case f == g: + return 0, false + } + return 0, true // must be NaN +} + +func testcmp(t *testing.T, f, g float64) { + hcmp, hisnan := hwcmp(f, g) + scmp, sisnan := Fcmp64(math.Float64bits(f), math.Float64bits(g)) + if int32(hcmp) != scmp || hisnan != sisnan { + err(t, "cmp(%g, %g) = sw %v, %v, hw %v, %v\n", f, g, scmp, sisnan, hcmp, hisnan) + } +} + +func same(f, g float64) bool { + if math.IsNaN(f) && math.IsNaN(g) { + return true + } + if math.Copysign(1, f) != math.Copysign(1, g) { + return false + } + return f == g +} diff --git a/gnovm/pkg/gnolang/internal/softfloat/softfloat.go b/gnovm/pkg/gnolang/internal/softfloat/softfloat.go new file mode 100644 index 00000000000..30f66dff620 --- /dev/null +++ b/gnovm/pkg/gnolang/internal/softfloat/softfloat.go @@ -0,0 +1,134 @@ +// Package softfloat is a copy of the Go runtime's softfloat64.go file. +// It is a pure software floating point implementation. It can be used to +// perform determinstic, hardware-independent floating point computations. +// +// This package uses shortnames to refer to its different operations. Here is a +// quick reference: +// +// add f + g +// sub f - g +// mul f * g +// div f / g +// neg (- f) +// eq f == g +// gt f > g +// ge f >= g +package softfloat + +// This file mostly exports the functions from runtime_softfloat64.go + +//go:generate sh copy.sh + +const ( + mask = 0x7FF + shift = 64 - 11 - 1 + bias = 1023 +) + +func Fadd64(f, g uint64) uint64 { return fadd64(f, g) } +func Fsub64(f, g uint64) uint64 { return fsub64(f, g) } +func Fmul64(f, g uint64) uint64 { return fmul64(f, g) } +func Fdiv64(f, g uint64) uint64 { return fdiv64(f, g) } +func Fneg64(f uint64) uint64 { return fneg64(f) } +func Feq64(f, g uint64) bool { return feq64(f, g) } +func Fgt64(f, g uint64) bool { return fgt64(f, g) } +func Fge64(f, g uint64) bool { return fge64(f, g) } + +func Fadd32(f, g uint32) uint32 { return fadd32(f, g) } +func Fsub32(f, g uint32) uint32 { return fadd32(f, Fneg32(g)) } +func Fmul32(f, g uint32) uint32 { return fmul32(f, g) } +func Fdiv32(f, g uint32) uint32 { return fdiv32(f, g) } +func Feq32(f, g uint32) bool { return feq32(f, g) } +func Fgt32(f, g uint32) bool { return fgt32(f, g) } +func Fge32(f, g uint32) bool { return fge32(f, g) } +func Flt32(f, g uint32) bool { + cmp, nan := fcmp64(f32to64(f), f32to64(g)) + return cmp <= -1 && !nan +} + +func Fle32(f, g uint32) bool { + cmp, nan := fcmp64(f32to64(f), f32to64(g)) + return cmp <= 0 && !nan +} + +func Flt64(f, g uint64) bool { + cmp, nan := fcmp64(f, g) + return cmp <= -1 && !nan +} + +func Fle64(f, g uint64) bool { + cmp, nan := fcmp64(f, g) + return cmp <= 0 && !nan +} + +func Fcmp64(f, g uint64) (cmp int32, isnan bool) { return fcmp64(f, g) } + +func Fneg32(f uint32) uint32 { + // Not defined in runtime - this is a copy similar to fneg64. + return f ^ (1 << (mantbits32 + expbits32)) +} + +// Conversions + +func Fintto64(val int64) (f uint64) { return fintto64(val) } +func Fintto32(val int64) (f uint32) { return fintto32(val) } + +func F32to64(f uint32) uint64 { return f32to64(f) } +func F32toint32(x uint32) int32 { return f32toint32(x) } +func F32toint64(x uint32) int64 { return f32toint64(x) } +func F32touint64(x uint32) uint64 { return f32touint64(x) } +func F64to32(f uint64) uint32 { return f64to32(f) } +func F64toint(f uint64) (val int64, ok bool) { return f64toint(f) } +func F64toint32(x uint64) int32 { return f64toint32(x) } +func F64toint64(x uint64) int64 { return f64toint64(x) } +func F64touint64(x uint64) uint64 { return f64touint64(x) } +func Fint32to32(x int32) uint32 { return fint32to32(x) } +func Fint32to64(x int32) uint64 { return fint32to64(x) } +func Fint64to32(x int64) uint32 { return fint64to32(x) } +func Fint64to64(x int64) uint64 { return fint64to64(x) } +func Fuint64to32(x uint64) uint32 { return fuint64to32(x) } +func Fuint64to64(x uint64) uint64 { return fuint64to64(x) } + +// unpack64 unpacks the float64 f into sign, exp, mantissa, isInf, isNaN. + +func Funpack32(f uint32) (sign, mant uint32, exp int, inf, nan bool) { return funpack32(f) } +func Funpack64(f uint64) (sign, mant uint64, exp int, inf, nan bool) { return funpack64(f) } + +// Trunc + +func Ftrunc64(f uint64) uint64 { return trunc(f) } +func Ftrunc32(f uint32) uint32 { return f64to32(trunc(f32to64(f))) } + +func trunc(x uint64) uint64 { + cmp, _ := Fcmp64(x, Fintto64(0)) + if _, _, _, isInf, IsNaN := Funpack64(x); cmp == 0 || isInf || IsNaN { + return x + } + + d, _ := modf(x) + return d +} + +func modf(u uint64) (it uint64, frac uint64) { + if Flt64(u, fint64to64(1)) { + switch { + case Flt64(u, fint64to64(0)): + it, frac = modf(Fneg64(u)) + return -it, -frac + case feq64(u, fint64to64(0)): + return u, u // Return -0, -0 when f == -0 + } + return 0, u + } + + it = u + e := uint(it>>shift)&mask - bias + + // Keep the top 12+e bits, the integer part; clear the rest. + if e < 64-12 { + it &^= 1<<(64-12-e) - 1 + } + + frac = fsub64(u, it) + return +} diff --git a/gnovm/pkg/gnolang/op_binary.go b/gnovm/pkg/gnolang/op_binary.go index 0f66da5e685..765f3ccbfbd 100644 --- a/gnovm/pkg/gnolang/op_binary.go +++ b/gnovm/pkg/gnolang/op_binary.go @@ -6,6 +6,7 @@ import ( "math/big" "github.com/cockroachdb/apd/v3" + "github.com/gnolang/gno/gnovm/pkg/gnolang/internal/softfloat" "github.com/gnolang/gno/tm2/pkg/overflow" ) @@ -394,9 +395,9 @@ func isEql(store Store, lv, rv *TypedValue) bool { case Uint64Kind: return (lv.GetUint64() == rv.GetUint64()) case Float32Kind: - return (lv.GetFloat32() == rv.GetFloat32()) // XXX determinism? + return softfloat.Feq32(lv.GetFloat32(), rv.GetFloat32()) case Float64Kind: - return (lv.GetFloat64() == rv.GetFloat64()) // XXX determinism? + return softfloat.Feq64(lv.GetFloat64(), rv.GetFloat64()) case BigintKind: lb := lv.V.(BigintValue).V rb := rv.V.(BigintValue).V @@ -535,9 +536,9 @@ func isLss(lv, rv *TypedValue) bool { case Uint64Kind: return (lv.GetUint64() < rv.GetUint64()) case Float32Kind: - return (lv.GetFloat32() < rv.GetFloat32()) // XXX determinism? + return softfloat.Flt32(lv.GetFloat32(), rv.GetFloat32()) case Float64Kind: - return (lv.GetFloat64() < rv.GetFloat64()) // XXX determinism? + return softfloat.Flt64(lv.GetFloat64(), rv.GetFloat64()) case BigintKind: lb := lv.V.(BigintValue).V rb := rv.V.(BigintValue).V @@ -579,9 +580,9 @@ func isLeq(lv, rv *TypedValue) bool { case Uint64Kind: return (lv.GetUint64() <= rv.GetUint64()) case Float32Kind: - return (lv.GetFloat32() <= rv.GetFloat32()) // XXX determinism? + return softfloat.Fle32(lv.GetFloat32(), rv.GetFloat32()) case Float64Kind: - return (lv.GetFloat64() <= rv.GetFloat64()) // XXX determinism? + return softfloat.Fle64(lv.GetFloat64(), rv.GetFloat64()) case BigintKind: lb := lv.V.(BigintValue).V rb := rv.V.(BigintValue).V @@ -623,9 +624,9 @@ func isGtr(lv, rv *TypedValue) bool { case Uint64Kind: return (lv.GetUint64() > rv.GetUint64()) case Float32Kind: - return (lv.GetFloat32() > rv.GetFloat32()) // XXX determinism? + return softfloat.Fgt32(lv.GetFloat32(), rv.GetFloat32()) case Float64Kind: - return (lv.GetFloat64() > rv.GetFloat64()) // XXX determinism? + return softfloat.Fgt64(lv.GetFloat64(), rv.GetFloat64()) case BigintKind: lb := lv.V.(BigintValue).V rb := rv.V.(BigintValue).V @@ -667,9 +668,9 @@ func isGeq(lv, rv *TypedValue) bool { case Uint64Kind: return (lv.GetUint64() >= rv.GetUint64()) case Float32Kind: - return (lv.GetFloat32() >= rv.GetFloat32()) // XXX determinism? + return softfloat.Fge32(lv.GetFloat32(), rv.GetFloat32()) case Float64Kind: - return (lv.GetFloat64() >= rv.GetFloat64()) // XXX determinism? + return softfloat.Fge64(lv.GetFloat64(), rv.GetFloat64()) case BigintKind: lb := lv.V.(BigintValue).V rb := rv.V.(BigintValue).V @@ -732,10 +733,10 @@ func addAssign(alloc *Allocator, lv, rv *TypedValue) *Exception { lv.SetUint64(lv.GetUint64() + rv.GetUint64()) case Float32Type: // NOTE: gno doesn't fuse *+. - lv.SetFloat32(lv.GetFloat32() + rv.GetFloat32()) // XXX determinism? + lv.SetFloat32(softfloat.Fadd32(lv.GetFloat32(), rv.GetFloat32())) case Float64Type: // NOTE: gno doesn't fuse *+. - lv.SetFloat64(lv.GetFloat64() + rv.GetFloat64()) // XXX determinism? + lv.SetFloat64(softfloat.Fadd64(lv.GetFloat64(), rv.GetFloat64())) case BigintType, UntypedBigintType: lb := lv.GetBigInt() lb = big.NewInt(0).Add(lb, rv.GetBigInt()) @@ -807,10 +808,10 @@ func subAssign(lv, rv *TypedValue) *Exception { lv.SetUint64(lv.GetUint64() - rv.GetUint64()) case Float32Type: // NOTE: gno doesn't fuse *+. - lv.SetFloat32(lv.GetFloat32() - rv.GetFloat32()) // XXX determinism? + lv.SetFloat32(softfloat.Fsub32(lv.GetFloat32(), rv.GetFloat32())) case Float64Type: // NOTE: gno doesn't fuse *+. - lv.SetFloat64(lv.GetFloat64() - rv.GetFloat64()) // XXX determinism? + lv.SetFloat64(softfloat.Fsub64(lv.GetFloat64(), rv.GetFloat64())) case BigintType, UntypedBigintType: lb := lv.GetBigInt() lb = big.NewInt(0).Sub(lb, rv.GetBigInt()) @@ -879,10 +880,10 @@ func mulAssign(lv, rv *TypedValue) *Exception { lv.SetUint64(lv.GetUint64() * rv.GetUint64()) case Float32Type: // NOTE: gno doesn't fuse *+. - lv.SetFloat32(lv.GetFloat32() * rv.GetFloat32()) // XXX determinism? + lv.SetFloat32(softfloat.Fmul32(lv.GetFloat32(), rv.GetFloat32())) case Float64Type: // NOTE: gno doesn't fuse *+. - lv.SetFloat64(lv.GetFloat64() * rv.GetFloat64()) // XXX determinism? + lv.SetFloat64(softfloat.Fmul64(lv.GetFloat64(), rv.GetFloat64())) case BigintType, UntypedBigintType: lb := lv.GetBigInt() lb = big.NewInt(0).Mul(lb, rv.GetBigInt()) @@ -975,20 +976,17 @@ func quoAssign(lv, rv *TypedValue) *Exception { // XXX Handling float overflows is more complex. case Float32Type: // NOTE: gno doesn't fuse *+. - y := rv.GetFloat32() - ok = y != 0 + ok = !softfloat.Feq32(rv.GetFloat32(), softfloat.Fintto32(0)) + if ok { - lv.SetFloat32(lv.GetFloat32() / y) + lv.SetFloat32(softfloat.Fdiv32(lv.GetFloat32(), rv.GetFloat32())) } - // XXX FOR DETERMINISM, PANIC IF NAN. case Float64Type: // NOTE: gno doesn't fuse *+. - y := rv.GetFloat64() - ok = y != 0 + ok = !softfloat.Feq64(rv.GetFloat64(), softfloat.Fintto64(0)) if ok { - lv.SetFloat64(lv.GetFloat64() / y) + lv.SetFloat64(softfloat.Fdiv64(lv.GetFloat64(), rv.GetFloat64())) } - // XXX FOR DETERMINISM, PANIC IF NAN. case BigintType, UntypedBigintType: if rv.GetBigInt().Sign() == 0 { ok = false diff --git a/gnovm/pkg/gnolang/op_inc_dec.go b/gnovm/pkg/gnolang/op_inc_dec.go index 1e68e195596..c67a4be6ed5 100644 --- a/gnovm/pkg/gnolang/op_inc_dec.go +++ b/gnovm/pkg/gnolang/op_inc_dec.go @@ -5,6 +5,7 @@ import ( "math/big" "github.com/cockroachdb/apd/v3" + "github.com/gnolang/gno/gnovm/pkg/gnolang/internal/softfloat" "github.com/gnolang/gno/tm2/pkg/overflow" ) @@ -57,9 +58,9 @@ func (m *Machine) doOpInc() { case Uint64Type: lv.SetUint64(lv.GetUint64() + 1) case Float32Type: - lv.SetFloat32(lv.GetFloat32() + 1) + lv.SetFloat32(softfloat.Fadd32(lv.GetFloat32(), softfloat.Fintto32(1))) case Float64Type: - lv.SetFloat64(lv.GetFloat64() + 1) + lv.SetFloat64(softfloat.Fadd64(lv.GetFloat64(), softfloat.Fintto64(1))) case BigintType, UntypedBigintType: lb := lv.GetBigInt() lb = big.NewInt(0).Add(lb, big.NewInt(1)) @@ -129,9 +130,9 @@ func (m *Machine) doOpDec() { case Uint64Type: lv.SetUint64(lv.GetUint64() - 1) case Float32Type: - lv.SetFloat32(lv.GetFloat32() - 1) + lv.SetFloat32(softfloat.Fsub32(lv.GetFloat32(), softfloat.Fintto32(1))) case Float64Type: - lv.SetFloat64(lv.GetFloat64() - 1) + lv.SetFloat64(softfloat.Fsub64(lv.GetFloat64(), softfloat.Fintto64(1))) case BigintType, UntypedBigintType: lb := lv.GetBigInt() lb = big.NewInt(0).Sub(lb, big.NewInt(1)) diff --git a/gnovm/pkg/gnolang/op_unary.go b/gnovm/pkg/gnolang/op_unary.go index 9c330c7f8f1..469c80b8dac 100644 --- a/gnovm/pkg/gnolang/op_unary.go +++ b/gnovm/pkg/gnolang/op_unary.go @@ -5,6 +5,7 @@ import ( "math/big" "github.com/cockroachdb/apd/v3" + "github.com/gnolang/gno/gnovm/pkg/gnolang/internal/softfloat" ) func (m *Machine) doOpUpos() { @@ -46,9 +47,9 @@ func (m *Machine) doOpUneg() { case Uint64Type: xv.SetUint64(-xv.GetUint64()) case Float32Type: - xv.SetFloat32(-xv.GetFloat32()) + xv.SetFloat32(softfloat.Fneg32(xv.GetFloat32())) case Float64Type: - xv.SetFloat64(-xv.GetFloat64()) + xv.SetFloat64(softfloat.Fneg64(xv.GetFloat64())) case UntypedBigintType, BigintType: bv := xv.V.(BigintValue) xv.V = BigintValue{V: new(big.Int).Neg(bv.V)} diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index 4c2e2835f95..da887764c8e 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -3,7 +3,6 @@ package gnolang import ( "encoding/binary" "fmt" - "math" "math/big" "reflect" "strconv" @@ -1121,13 +1120,13 @@ func (tv *TypedValue) PrimitiveBytes() (data []byte) { return data case Float32Type: data = make([]byte, 4) - u32 := math.Float32bits(tv.GetFloat32()) + u32 := tv.GetFloat32() binary.LittleEndian.PutUint32( data, u32) return data case Float64Type: data = make([]byte, 8) - u64 := math.Float64bits(tv.GetFloat64()) + u64 := tv.GetFloat64() binary.LittleEndian.PutUint64( data, u64) return data @@ -1450,7 +1449,7 @@ func (tv *TypedValue) GetUint64() uint64 { return *(*uint64)(unsafe.Pointer(&tv.N)) } -func (tv *TypedValue) SetFloat32(n float32) { +func (tv *TypedValue) SetFloat32(n uint32) { if debug { if tv.T.Kind() != Float32Kind || isNative(tv.T) { panic(fmt.Sprintf( @@ -1458,10 +1457,10 @@ func (tv *TypedValue) SetFloat32(n float32) { tv.T.String())) } } - *(*float32)(unsafe.Pointer(&tv.N)) = n + *(*uint32)(unsafe.Pointer(&tv.N)) = n } -func (tv *TypedValue) GetFloat32() float32 { +func (tv *TypedValue) GetFloat32() uint32 { if debug { if tv.T != nil && tv.T.Kind() != Float32Kind { panic(fmt.Sprintf( @@ -1469,10 +1468,10 @@ func (tv *TypedValue) GetFloat32() float32 { tv.T.String())) } } - return *(*float32)(unsafe.Pointer(&tv.N)) + return *(*uint32)(unsafe.Pointer(&tv.N)) } -func (tv *TypedValue) SetFloat64(n float64) { +func (tv *TypedValue) SetFloat64(n uint64) { if debug { if tv.T.Kind() != Float64Kind || isNative(tv.T) { panic(fmt.Sprintf( @@ -1480,10 +1479,10 @@ func (tv *TypedValue) SetFloat64(n float64) { tv.T.String())) } } - *(*float64)(unsafe.Pointer(&tv.N)) = n + *(*uint64)(unsafe.Pointer(&tv.N)) = n } -func (tv *TypedValue) GetFloat64() float64 { +func (tv *TypedValue) GetFloat64() uint64 { if debug { if tv.T != nil && tv.T.Kind() != Float64Kind { panic(fmt.Sprintf( @@ -1491,7 +1490,7 @@ func (tv *TypedValue) GetFloat64() float64 { tv.T.String())) } } - return *(*float64)(unsafe.Pointer(&tv.N)) + return *(*uint64)(unsafe.Pointer(&tv.N)) } func (tv *TypedValue) GetBigInt() *big.Int { diff --git a/gnovm/pkg/gnolang/values_conversions.go b/gnovm/pkg/gnolang/values_conversions.go index baeded76c1a..e1c9378fe67 100644 --- a/gnovm/pkg/gnolang/values_conversions.go +++ b/gnovm/pkg/gnolang/values_conversions.go @@ -8,6 +8,7 @@ import ( "strconv" "github.com/cockroachdb/apd/v3" + "github.com/gnolang/gno/gnovm/pkg/gnolang/internal/softfloat" ) // t cannot be nil or untyped or DataByteType. @@ -163,11 +164,11 @@ GNO_CASE: tv.T = t tv.SetUint64(x) case Float32Kind: - x := float32(tv.GetInt()) // XXX determinism? + x := softfloat.Fintto32(int64(tv.GetInt())) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := float64(tv.GetInt()) // XXX determinism? + x := softfloat.Fintto64(int64(tv.GetInt())) tv.T = t tv.SetFloat64(x) case StringKind: @@ -233,11 +234,11 @@ GNO_CASE: tv.T = t tv.SetUint64(x) case Float32Kind: - x := float32(tv.GetInt8()) // XXX determinism? + x := softfloat.Fint32to32(int32(tv.GetInt8())) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := float64(tv.GetInt8()) // XXX determinism? + x := softfloat.Fint32to64(int32(tv.GetInt8())) tv.T = t tv.SetFloat64(x) case StringKind: @@ -304,11 +305,11 @@ GNO_CASE: tv.T = t tv.SetUint64(x) case Float32Kind: - x := float32(tv.GetInt16()) // XXX determinism? + x := softfloat.Fint32to32(int32(tv.GetInt16())) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := float64(tv.GetInt16()) // XXX determinism? + x := softfloat.Fint32to64(int32(tv.GetInt16())) tv.T = t tv.SetFloat64(x) case StringKind: @@ -379,11 +380,11 @@ GNO_CASE: tv.T = t tv.SetUint64(x) case Float32Kind: - x := float32(tv.GetInt32()) // XXX determinism? + x := softfloat.Fint32to32(tv.GetInt32()) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := float64(tv.GetInt32()) // XXX determinism? + x := softfloat.Fint32to64(tv.GetInt32()) tv.T = t tv.SetFloat64(x) case StringKind: @@ -456,11 +457,11 @@ GNO_CASE: tv.T = t tv.SetUint64(x) case Float32Kind: - x := float32(tv.GetInt64()) // XXX determinism? + x := softfloat.Fint64to32(tv.GetInt64()) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := float64(tv.GetInt64()) // XXX determinism? + x := softfloat.Fint64to64(tv.GetInt64()) tv.T = t tv.SetFloat64(x) case StringKind: @@ -533,11 +534,11 @@ GNO_CASE: tv.T = t tv.SetUint64(x) case Float32Kind: - x := float32(tv.GetUint()) // XXX determinism? + x := softfloat.Fuint64to32(uint64(tv.GetUint())) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := float64(tv.GetUint()) // XXX determinism? + x := softfloat.Fuint64to64(uint64(tv.GetUint())) tv.T = t tv.SetFloat64(x) case StringKind: @@ -602,11 +603,11 @@ GNO_CASE: tv.T = t tv.SetUint64(x) case Float32Kind: - x := float32(tv.GetUint8()) // XXX determinism? + x := softfloat.Fuint64to32(uint64(tv.GetUint8())) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := float64(tv.GetUint8()) // XXX determinism? + x := softfloat.Fuint64to64(uint64(tv.GetUint8())) tv.T = t tv.SetFloat64(x) case StringKind: @@ -673,11 +674,11 @@ GNO_CASE: tv.T = t tv.SetUint64(x) case Float32Kind: - x := float32(tv.GetUint16()) // XXX determinism? + x := softfloat.Fuint64to32(uint64(tv.GetUint16())) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := float64(tv.GetUint16()) // XXX determinism? + x := softfloat.Fuint64to64(uint64(tv.GetUint16())) tv.T = t tv.SetFloat64(x) case StringKind: @@ -746,11 +747,11 @@ GNO_CASE: tv.T = t tv.SetUint64(x) case Float32Kind: - x := float32(tv.GetUint32()) // XXX determinism? + x := softfloat.Fuint64to32(uint64(tv.GetUint32())) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := float64(tv.GetUint32()) // XXX determinism? + x := softfloat.Fuint64to64(uint64(tv.GetUint32())) tv.T = t tv.SetFloat64(x) case StringKind: @@ -825,11 +826,11 @@ GNO_CASE: tv.T = t tv.SetUint64(x) case Float32Kind: - x := float32(tv.GetUint64()) // XXX determinism? + x := softfloat.Fuint64to32(tv.GetUint64()) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := float64(tv.GetUint64()) // XXX determinism? + x := softfloat.Fuint64to64(tv.GetUint64()) tv.T = t tv.SetFloat64(x) case StringKind: @@ -847,156 +848,155 @@ GNO_CASE: switch k { case IntKind: validate(Float32Kind, IntKind, func() bool { - val := float64(tv.GetFloat32()) - trunc := math.Trunc(val) + trunc := softfloat.Ftrunc32(tv.GetFloat32()) - if val != trunc { + if !softfloat.Feq32(trunc, tv.GetFloat32()) { return false } - return int64(trunc) >= math.MinInt && int64(trunc) <= math.MaxInt + truncInt64 := softfloat.F32toint64(trunc) + return truncInt64 >= math.MinInt && truncInt64 <= math.MaxInt }) - x := int(tv.GetFloat32()) // XXX determinism? + x := int(softfloat.F32toint64(tv.GetFloat32())) tv.T = t tv.SetInt(x) case Int8Kind: validate(Float32Kind, Int8Kind, func() bool { - val := float64(tv.GetFloat32()) - trunc := math.Trunc(val) + trunc := softfloat.Ftrunc32(tv.GetFloat32()) - if val != trunc { + if !softfloat.Feq32(trunc, tv.GetFloat32()) { return false } - return int64(trunc) >= math.MinInt8 && int64(trunc) <= math.MaxInt8 + truncInt64 := softfloat.F32toint64(trunc) + return truncInt64 >= math.MinInt8 && truncInt64 <= math.MaxInt8 }) - x := int8(tv.GetFloat32()) // XXX determinism? + x := int8(softfloat.F32toint32(tv.GetFloat32())) tv.T = t tv.SetInt8(x) case Int16Kind: validate(Float32Kind, Int16Kind, func() bool { - val := float64(tv.GetFloat32()) - trunc := math.Trunc(val) + trunc := softfloat.Ftrunc32(tv.GetFloat32()) - if val != trunc { + if !softfloat.Feq32(trunc, tv.GetFloat32()) { return false } - return int64(trunc) >= math.MinInt16 && int64(trunc) <= math.MaxInt16 + truncInt64 := softfloat.F32toint64(trunc) + return truncInt64 >= math.MinInt16 && truncInt64 <= math.MaxInt16 }) - x := int16(tv.GetFloat32()) // XXX determinism? + x := int16(softfloat.F32toint32(tv.GetFloat32())) tv.T = t tv.SetInt16(x) case Int32Kind: validate(Float32Kind, Int32Kind, func() bool { - val := float64(tv.GetFloat32()) - trunc := math.Trunc(val) + trunc := softfloat.Ftrunc32(tv.GetFloat32()) - if val != trunc { + if !softfloat.Feq32(trunc, tv.GetFloat32()) { return false } - return int64(trunc) >= math.MinInt32 && int64(trunc) <= math.MaxInt32 + truncInt64 := softfloat.F32toint64(trunc) + return truncInt64 >= math.MinInt32 && truncInt64 <= math.MaxInt32 }) - x := int32(tv.GetFloat32()) // XXX determinism? + x := softfloat.F32toint32(tv.GetFloat32()) tv.T = t tv.SetInt32(x) case Int64Kind: validate(Float32Kind, Int64Kind, func() bool { - val := float64(tv.GetFloat32()) - trunc := math.Trunc(val) + trunc := softfloat.Ftrunc32(tv.GetFloat32()) - return val == trunc + return softfloat.Feq32(trunc, tv.GetFloat32()) }) - x := int64(tv.GetFloat32()) // XXX determinism? + x := softfloat.F32toint64(tv.GetFloat32()) tv.T = t tv.SetInt64(x) case UintKind: validate(Float32Kind, UintKind, func() bool { - val := float64(tv.GetFloat32()) - trunc := math.Trunc(val) + trunc := softfloat.Ftrunc32(tv.GetFloat32()) - if val != trunc { + if !softfloat.Feq32(trunc, tv.GetFloat32()) { return false } - return trunc >= 0 && trunc <= math.MaxUint + truncUint64 := softfloat.F32touint64(trunc) + return truncUint64 >= 0 && truncUint64 <= math.MaxUint }) - x := uint(tv.GetFloat32()) // XXX determinism? + x := uint(softfloat.F32touint64(tv.GetFloat32())) tv.T = t tv.SetUint(x) case Uint8Kind: validate(Float32Kind, Uint8Kind, func() bool { - val := float64(tv.GetFloat32()) - trunc := math.Trunc(val) + trunc := softfloat.Ftrunc32(tv.GetFloat32()) - if val != trunc { + if !softfloat.Feq32(trunc, tv.GetFloat32()) { return false } - return int64(trunc) >= 0 && int64(trunc) <= math.MaxUint8 + truncUint64 := softfloat.F32touint64(trunc) + return truncUint64 >= 0 && truncUint64 <= math.MaxUint8 }) - x := uint8(tv.GetFloat32()) // XXX determinism? + x := uint8(softfloat.F32touint64(tv.GetFloat32())) tv.T = t tv.SetUint8(x) case Uint16Kind: validate(Float32Kind, Uint16Kind, func() bool { - val := float64(tv.GetFloat32()) - trunc := math.Trunc(val) + trunc := softfloat.Ftrunc32(tv.GetFloat32()) - if val != trunc { + if !softfloat.Feq32(trunc, tv.GetFloat32()) { return false } - return int64(trunc) >= 0 && int64(trunc) <= math.MaxUint16 + truncUint64 := softfloat.F32touint64(trunc) + return truncUint64 >= 0 && truncUint64 <= math.MaxUint16 }) - x := uint16(tv.GetFloat32()) // XXX determinism? + x := uint16(softfloat.F32touint64(tv.GetFloat32())) tv.T = t tv.SetUint16(x) case Uint32Kind: validate(Float32Kind, Uint32Kind, func() bool { - val := float64(tv.GetFloat32()) - trunc := math.Trunc(val) + trunc := softfloat.Ftrunc32(tv.GetFloat32()) - if val != trunc { + if !softfloat.Feq32(trunc, tv.GetFloat32()) { return false } - return int64(trunc) >= 0 && int64(trunc) <= math.MaxUint32 + truncUint64 := softfloat.F32touint64(trunc) + return truncUint64 >= 0 && truncUint64 <= math.MaxUint32 }) - x := uint32(tv.GetFloat32()) // XXX determinism? + x := uint32(softfloat.F32touint64(tv.GetFloat32())) tv.T = t tv.SetUint32(x) case Uint64Kind: validate(Float32Kind, Uint64Kind, func() bool { - val := float64(tv.GetFloat32()) - trunc := math.Trunc(val) + trunc := softfloat.Ftrunc32(tv.GetFloat32()) - if val != trunc { + if !softfloat.Feq32(trunc, tv.GetFloat32()) { return false } - return trunc >= 0 && trunc <= math.MaxUint + truncUint64 := softfloat.F32touint64(trunc) + return truncUint64 >= 0 && truncUint64 <= math.MaxUint }) - x := uint64(tv.GetFloat32()) // XXX determinism? + x := softfloat.F32touint64(tv.GetFloat32()) tv.T = t tv.SetUint64(x) case Float32Kind: - x := tv.GetFloat32() // XXX determinism? + x := tv.GetFloat32() // ??? tv.T = t tv.SetFloat32(x) case Float64Kind: - x := float64(tv.GetFloat32()) // XXX determinism? + x := softfloat.F32to64(tv.GetFloat32()) tv.T = t tv.SetFloat64(x) default: @@ -1008,160 +1008,160 @@ GNO_CASE: switch k { case IntKind: validate(Float64Kind, IntKind, func() bool { - val := tv.GetFloat64() - trunc := math.Trunc(val) + trunc := softfloat.Ftrunc64(tv.GetFloat64()) - if val != trunc { + if !softfloat.Feq64(trunc, tv.GetFloat64()) { return false } - return int64(trunc) >= math.MinInt && int64(trunc) <= math.MaxInt + truncInt64 := softfloat.F64toint64(trunc) + return truncInt64 >= math.MinInt && truncInt64 <= math.MaxInt }) - x := int(tv.GetFloat64()) // XXX determinism? + xp, _ := softfloat.F64toint(tv.GetFloat64()) + x := int(xp) tv.T = t tv.SetInt(x) case Int8Kind: validate(Float64Kind, Int8Kind, func() bool { - val := tv.GetFloat64() - trunc := math.Trunc(val) + trunc := softfloat.Ftrunc64(tv.GetFloat64()) - if val != trunc { + if !softfloat.Feq64(trunc, tv.GetFloat64()) { return false } - return int64(trunc) >= math.MinInt8 && int64(trunc) <= math.MaxInt8 + truncInt64 := softfloat.F64toint64(trunc) + return truncInt64 >= math.MinInt8 && truncInt64 <= math.MaxInt8 }) - x := int8(tv.GetFloat64()) // XXX determinism? + x := int8(softfloat.F64toint32(tv.GetFloat64())) tv.T = t tv.SetInt8(x) case Int16Kind: validate(Float64Kind, Int16Kind, func() bool { - val := tv.GetFloat64() - trunc := math.Trunc(val) + trunc := softfloat.Ftrunc64(tv.GetFloat64()) - if val != trunc { + if !softfloat.Feq64(trunc, tv.GetFloat64()) { return false } - return int64(trunc) >= math.MinInt16 && int64(trunc) <= math.MaxInt16 + truncInt64 := softfloat.F64toint64(trunc) + return truncInt64 >= math.MinInt16 && truncInt64 <= math.MaxInt16 }) - x := int16(tv.GetFloat64()) // XXX determinism? + x := int16(softfloat.F64toint32(tv.GetFloat64())) tv.T = t tv.SetInt16(x) case Int32Kind: validate(Float64Kind, Int32Kind, func() bool { - val := tv.GetFloat64() - trunc := math.Trunc(val) + trunc := softfloat.Ftrunc64(tv.GetFloat64()) - if val != trunc { + if !softfloat.Feq64(trunc, tv.GetFloat64()) { return false } - return int64(trunc) >= math.MinInt32 && int64(trunc) <= math.MaxInt32 + truncInt64 := softfloat.F64toint64(trunc) + return truncInt64 >= math.MinInt32 && truncInt64 <= math.MaxInt32 }) - x := int32(tv.GetFloat64()) // XXX determinism? + x := softfloat.F64toint32(tv.GetFloat64()) tv.T = t tv.SetInt32(x) case Int64Kind: validate(Float64Kind, Int64Kind, func() bool { - val := tv.GetFloat64() - trunc := math.Trunc(val) + trunc := softfloat.Ftrunc64(tv.GetFloat64()) - return val == trunc + return softfloat.Feq64(trunc, tv.GetFloat64()) }) - x := int64(tv.GetFloat64()) // XXX determinism? + x := softfloat.F64toint64(tv.GetFloat64()) tv.T = t tv.SetInt64(x) case UintKind: validate(Float64Kind, UintKind, func() bool { - val := tv.GetFloat64() - trunc := math.Trunc(val) + trunc := softfloat.Ftrunc64(tv.GetFloat64()) - if val != trunc { + if !softfloat.Feq64(trunc, tv.GetFloat64()) { return false } - return trunc >= 0 && trunc <= math.MaxUint + truncUint64 := softfloat.F64touint64(trunc) + + return truncUint64 >= 0 && truncUint64 <= math.MaxUint }) - x := uint(tv.GetFloat64()) // XXX determinism? + x := uint(softfloat.F64touint64(tv.GetFloat64())) tv.T = t tv.SetUint(x) case Uint8Kind: validate(Float64Kind, Uint8Kind, func() bool { - val := tv.GetFloat64() - trunc := math.Trunc(val) + trunc := softfloat.Ftrunc64(tv.GetFloat64()) - if val != trunc { + if !softfloat.Feq64(trunc, tv.GetFloat64()) { return false } - return int64(trunc) >= 0 && int64(trunc) <= math.MaxUint8 + truncUint64 := softfloat.F64touint64(trunc) + return truncUint64 >= 0 && truncUint64 <= math.MaxUint8 }) - x := uint8(tv.GetFloat64()) // XXX determinism? + x := uint8(softfloat.F64touint64(tv.GetFloat64())) tv.T = t tv.SetUint8(x) case Uint16Kind: validate(Float64Kind, Uint16Kind, func() bool { - val := tv.GetFloat64() - trunc := math.Trunc(val) + trunc := softfloat.Ftrunc64(tv.GetFloat64()) - if val != trunc { + if !softfloat.Feq64(trunc, tv.GetFloat64()) { return false } - return int64(trunc) >= 0 && int64(trunc) <= math.MaxUint16 + truncUint64 := softfloat.F64touint64(trunc) + return truncUint64 >= 0 && truncUint64 <= math.MaxUint16 }) - x := uint16(tv.GetFloat64()) // XXX determinism? + x := uint16(softfloat.F64touint64(tv.GetFloat64())) tv.T = t tv.SetUint16(x) case Uint32Kind: validate(Float64Kind, Uint32Kind, func() bool { - val := tv.GetFloat64() - trunc := math.Trunc(val) - - if val != trunc { + trunc := softfloat.Ftrunc64(tv.GetFloat64()) + if !softfloat.Feq64(trunc, tv.GetFloat64()) { return false } - return int64(trunc) >= 0 && int64(trunc) <= math.MaxUint32 + truncUint64 := softfloat.F64touint64(trunc) + return truncUint64 >= 0 && truncUint64 <= math.MaxUint32 }) - x := uint32(tv.GetFloat64()) // XXX determinism? + x := uint32(softfloat.F64touint64(tv.GetFloat64())) tv.T = t tv.SetUint32(x) case Uint64Kind: validate(Float64Kind, Uint64Kind, func() bool { - val := tv.GetFloat64() - trunc := math.Trunc(val) + trunc := softfloat.Ftrunc64(tv.GetFloat64()) - if val != trunc { + if !softfloat.Feq64(tv.GetFloat64(), trunc) { return false } - return trunc >= 0 && trunc <= math.MaxUint64 + truncUint64 := softfloat.F64touint64(trunc) + return truncUint64 >= 0 && truncUint64 <= math.MaxUint64 }) - x := uint64(tv.GetFloat64()) // XXX determinism? + x := softfloat.F64touint64(tv.GetFloat64()) tv.T = t tv.SetUint64(x) case Float32Kind: validate(Float64Kind, Float32Kind, func() bool { - return tv.GetFloat64() <= math.MaxFloat32 + return softfloat.Fle64(tv.GetFloat64(), math.Float64bits(float64(math.MaxFloat32))) }) - x := float32(tv.GetFloat64()) // XXX determinism? + x := softfloat.F64to32(tv.GetFloat64()) tv.T = t tv.SetFloat32(x) case Float64Kind: - x := tv.GetFloat64() // XXX determinism? + x := tv.GetFloat64() // ??? tv.T = t tv.SetFloat64(x) default: @@ -1481,7 +1481,7 @@ func ConvertUntypedBigintTo(dst *TypedValue, bv BigintValue, t Type) { if f32 == 0 && (acc == big.Below || acc == big.Above) { panic("bigint underflows float32 (too close to zero)") } - dst.SetFloat32(f32) + dst.SetFloat32(math.Float32bits(f32)) return // done case Float64Kind: dst.T = t @@ -1495,7 +1495,7 @@ func ConvertUntypedBigintTo(dst *TypedValue, bv BigintValue, t Type) { if f64 == 0 && (acc == big.Below || acc == big.Above) { panic("bigint underflows float64 (too close to zero)") } - dst.SetFloat64(f64) + dst.SetFloat64(math.Float64bits(f64)) return // done case BigdecKind: dst.T = t @@ -1610,7 +1610,7 @@ func ConvertUntypedBigdecTo(dst *TypedValue, bv BigdecValue, t Type) { dst.T = Float64Type dst.V = nil f, _ := bd.Float64() - dst.SetFloat64(f) + dst.SetFloat64(math.Float64bits(f)) return case IntKind, Int8Kind, Int16Kind, Int32Kind, Int64Kind: fallthrough @@ -1636,7 +1636,7 @@ func ConvertUntypedBigdecTo(dst *TypedValue, bv BigdecValue, t Type) { if math.IsInf(float64(f32), 0) { panic("cannot convert untyped bigdec to float32 -- too close to +-Inf") } - dst.SetFloat32(f32) + dst.SetFloat32(math.Float32bits(f32)) return case Float64Kind: dst.T = t @@ -1648,7 +1648,7 @@ func ConvertUntypedBigdecTo(dst *TypedValue, bv BigdecValue, t Type) { if math.IsInf(f64, 0) { panic("cannot convert untyped bigdec to float64 -- too close to +-Inf") } - dst.SetFloat64(f64) + dst.SetFloat64(math.Float64bits(f64)) return default: panic(fmt.Sprintf( diff --git a/gnovm/pkg/gnolang/values_conversions_test.go b/gnovm/pkg/gnolang/values_conversions_test.go index 7ffa3e98c71..5538e973bdc 100644 --- a/gnovm/pkg/gnolang/values_conversions_test.go +++ b/gnovm/pkg/gnolang/values_conversions_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/cockroachdb/apd/v3" + "github.com/gnolang/gno/gnovm/pkg/gnolang/internal/softfloat" "github.com/stretchr/testify/require" ) @@ -24,7 +25,7 @@ func TestConvertUntypedBigdecToFloat(t *testing.T) { ConvertUntypedBigdecTo(dst, bd, typ) - require.Equal(t, float64(0), dst.GetFloat64()) + require.True(t, softfloat.Feq64(dst.GetFloat64(), 0)) } func TestBitShiftingOverflow(t *testing.T) { diff --git a/gnovm/pkg/gnolang/values_string.go b/gnovm/pkg/gnolang/values_string.go index a414f440e4e..fdf0c8f55de 100644 --- a/gnovm/pkg/gnolang/values_string.go +++ b/gnovm/pkg/gnolang/values_string.go @@ -2,6 +2,7 @@ package gnolang import ( "fmt" + "math" "reflect" "strconv" "strings" @@ -339,9 +340,9 @@ func (tv *TypedValue) ProtectedSprint(seen *seenValues, considerDeclaredType boo case Uint64Type: return fmt.Sprintf("%d", tv.GetUint64()) case Float32Type: - return fmt.Sprintf("%v", tv.GetFloat32()) + return fmt.Sprintf("%v", math.Float32frombits(tv.GetFloat32())) case Float64Type: - return fmt.Sprintf("%v", tv.GetFloat64()) + return fmt.Sprintf("%v", math.Float64frombits(tv.GetFloat64())) case UntypedBigintType, BigintType: return tv.V.(BigintValue).V.String() case UntypedBigdecType, BigdecType: @@ -447,9 +448,9 @@ func (tv TypedValue) ProtectedString(seen *seenValues) string { case Uint64Type: vs = fmt.Sprintf("%d", tv.GetUint64()) case Float32Type: - vs = fmt.Sprintf("%v", tv.GetFloat32()) + vs = fmt.Sprintf("%v", math.Float32frombits(tv.GetFloat32())) case Float64Type: - vs = fmt.Sprintf("%v", tv.GetFloat64()) + vs = fmt.Sprintf("%v", math.Float64frombits(tv.GetFloat64())) // Complex types that require recusion protection. default: vs = nilStr diff --git a/gnovm/tests/files/float8.gno b/gnovm/tests/files/float8.gno new file mode 100644 index 00000000000..989d1b4fd61 --- /dev/null +++ b/gnovm/tests/files/float8.gno @@ -0,0 +1,166 @@ +package main + +import "math" + +func main() { + asVars() + asConsts() +} + +func asVars() { + var i8 int8 = 127 + var i16 int16 = 32767 + var i32 int32 = 2147483647 + var i64 int64 = 9223372036854775807 + var i int = 9223372036854775807 + var u8 uint8 = 255 + var u16 uint16 = 65535 + var u32 uint32 = 4294967295 + var u64 uint64 = 18446744073709551615 + var f32Max float32 = math.MaxFloat32 + var f64Max float64 = math.MaxFloat64 + var f64Min float64 = math.SmallestNonzeroFloat64 + var f32Min float32 = math.SmallestNonzeroFloat32 + println(f32Max / 2) + println(f64Max / 2) + println((f32Max - 1) + 1) + println((f64Max - 1) + 1) + println((f32Max / 2) * 2) + println((f64Max / 2) * 2) + println(f32Max - 1) + println(f64Max - 1) + println(f64Min / 2) + println(f32Min / 2) + println(float32(i8)) + println(float64(i8)) + println(float32(i16)) + println(float64(i16)) + println(float32(i32)) + println(float64(i32)) + println(float32(i64)) + println(float64(i64)) + println(float32(i)) + println(float64(i)) + println(float32(u8)) + println(float64(u8)) + println(float32(u16)) + println(float64(u16)) + println(float32(u32)) + println(float64(u32)) + println(float32(u64)) + println(float64(u64)) + println(float32(f32Max)) + println(float64(f32Max)) + println(float64(f64Max)) +} + +func asConsts() { + const i8 int8 = 127 + const i16 int16 = 32767 + const i32 int32 = 2147483647 + const i64 int64 = 9223372036854775807 + const i int = 9223372036854775807 + const u8 uint8 = 255 + const u16 uint16 = 65535 + const u32 uint32 = 4294967295 + const u64 uint64 = 18446744073709551615 + const f32Max float32 = math.MaxFloat32 + const f64Max float64 = math.MaxFloat64 + const f64Min float64 = math.SmallestNonzeroFloat64 + const f32Min float32 = math.SmallestNonzeroFloat32 + println(f32Max / 2) + println(f64Max / 2) + println((f32Max - 1) + 1) + println((f64Max - 1) + 1) + println((f32Max / 2) * 2) + println((f64Max / 2) * 2) + println(f32Max - 1) + println(f64Max - 1) + println(f64Min / 2) + println(f32Min / 2) + println(float32(i8)) + println(float64(i8)) + println(float32(i16)) + println(float64(i16)) + println(float32(i32)) + println(float64(i32)) + println(float32(i64)) + println(float64(i64)) + println(float32(i)) + println(float64(i)) + println(float32(u8)) + println(float64(u8)) + println(float32(u16)) + println(float64(u16)) + println(float32(u32)) + println(float64(u32)) + println(float32(u64)) + println(float64(u64)) + println(float32(f32Max)) + println(float64(f32Max)) + println(float64(f64Max)) +} + +// Output: +// 1.7014117e+38 +// 8.988465674311579e+307 +// 3.4028235e+38 +// 1.7976931348623157e+308 +// 3.4028235e+38 +// 1.7976931348623157e+308 +// 3.4028235e+38 +// 1.7976931348623157e+308 +// 0 +// 0 +// 127 +// 127 +// 32767 +// 32767 +// 2.1474836e+09 +// 2.147483647e+09 +// 9.223372e+18 +// 9.223372036854776e+18 +// 9.223372e+18 +// 9.223372036854776e+18 +// 255 +// 255 +// 65535 +// 65535 +// 4.2949673e+09 +// 4.294967295e+09 +// 1.8446744e+19 +// 1.8446744073709552e+19 +// 3.4028235e+38 +// 3.4028234663852886e+38 +// 1.7976931348623157e+308 +// 1.7014117e+38 +// 8.988465674311579e+307 +// 3.4028235e+38 +// 1.7976931348623157e+308 +// 3.4028235e+38 +// 1.7976931348623157e+308 +// 3.4028235e+38 +// 1.7976931348623157e+308 +// 0 +// 0 +// 127 +// 127 +// 32767 +// 32767 +// 2.1474836e+09 +// 2.147483647e+09 +// 9.223372e+18 +// 9.223372036854776e+18 +// 9.223372e+18 +// 9.223372036854776e+18 +// 255 +// 255 +// 65535 +// 65535 +// 4.2949673e+09 +// 4.294967295e+09 +// 1.8446744e+19 +// 1.8446744073709552e+19 +// 3.4028235e+38 +// 3.4028234663852886e+38 +// 1.7976931348623157e+308 From ee8dcc6f2180fea94c17da38c9bcaf519a45dd7b Mon Sep 17 00:00:00 2001 From: Morgan Date: Mon, 13 Jan 2025 13:02:43 +0100 Subject: [PATCH 03/18] feat(github-bot): config improvements (#3466) - change the "docs" merge requirements to allow also for a third-party contribution approved by both core+devrel - disallow merging on all prs with the "don't merge" label - don't require additional information on dependabot PRs --- contribs/github-bot/internal/config/config.go | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/contribs/github-bot/internal/config/config.go b/contribs/github-bot/internal/config/config.go index 86acc6cfa83..f80fc86cb11 100644 --- a/contribs/github-bot/internal/config/config.go +++ b/contribs/github-bot/internal/config/config.go @@ -38,17 +38,22 @@ func Config(gh *client.GitHub) ([]AutomaticCheck, []ManualCheck) { { Description: "Changes to 'docs' folder must be reviewed/authored by at least one devrel and one tech-staff", If: c.FileChanged(gh, "^docs/"), - Then: r.Or( - r.And( - r.AuthorInTeam(gh, "devrels"), + Then: r.And( + r.Or( + r.AuthorInTeam(gh, "tech-staff"), r.ReviewByTeamMembers(gh, "tech-staff", 1), ), - r.And( - r.AuthorInTeam(gh, "tech-staff"), + r.Or( + r.AuthorInTeam(gh, "devrels"), r.ReviewByTeamMembers(gh, "devrels", 1), ), ), }, + { + Description: "Must not contain the \"don't merge\" label", + If: c.Label("don't merge"), + Then: r.Never(), + }, } manual := []ManualCheck{ @@ -59,8 +64,11 @@ func Config(gh *client.GitHub) ([]AutomaticCheck, []ManualCheck) { }, { Description: "The pull request description provides enough details", - If: c.Not(c.AuthorInTeam(gh, "core-contributors")), - Teams: Teams{"core-contributors"}, + If: c.And( + c.Not(c.AuthorInTeam(gh, "core-contributors")), + c.Not(c.Author("dependabot[bot]")), + ), + Teams: Teams{"core-contributors"}, }, { Description: "Determine if infra needs to be updated before merging", From cc5cb36fe65fb3d4f2d8cb0df2dc40f686fb55d3 Mon Sep 17 00:00:00 2001 From: Miguel Victoria Villaquiran Date: Mon, 13 Jan 2025 13:31:45 +0100 Subject: [PATCH 04/18] feat(stdlibs): add package crypto/bech32 (#3375) Related to #1475 --------- Co-authored-by: Nathan Toups <612924+n2p5@users.noreply.github.com> --- .../gno.land/p/demo/testutils/crypto_test.gno | 12 + .../r/demo/keystore/keystore_test.gno | 6 +- .../r/demo/microblog/microblog_test.gno | 2 +- .../testdata/assertorigincall.txtar | 2 +- .../testdata/grc20_invalid_address.txtar | 2 +- .../integration/testdata/grc20_registry.txtar | 4 +- .../integration/testdata/grc721_emit.txtar | 10 +- .../pkg/integration/testdata/issue_1786.txtar | 8 +- .../pkg/integration/testdata/prevrealm.txtar | 2 +- .../pkg/integration/testdata/wugnot.txtar | 14 +- gnovm/stdlibs/crypto/bech32/bech32.gno | 445 +++++++++++ gnovm/stdlibs/crypto/bech32/bech32_test.gno | 691 ++++++++++++++++++ gnovm/stdlibs/crypto/bech32/error.gno | 83 +++ gnovm/stdlibs/crypto/bech32/version.gno | 43 ++ gnovm/stdlibs/generated.go | 101 +-- gnovm/stdlibs/std/crypto.gno | 67 ++ gnovm/stdlibs/std/crypto_test.gno | 19 + gnovm/stdlibs/std/native.gno | 15 - gnovm/stdlibs/std/native.go | 21 - gnovm/tests/files/std5.gno | 2 +- gnovm/tests/files/std8.gno | 2 +- gnovm/tests/files/zrealm_natbind0.gno | 2 +- 22 files changed, 1391 insertions(+), 162 deletions(-) create mode 100644 examples/gno.land/p/demo/testutils/crypto_test.gno create mode 100644 gnovm/stdlibs/crypto/bech32/bech32.gno create mode 100644 gnovm/stdlibs/crypto/bech32/bech32_test.gno create mode 100644 gnovm/stdlibs/crypto/bech32/error.gno create mode 100644 gnovm/stdlibs/crypto/bech32/version.gno diff --git a/examples/gno.land/p/demo/testutils/crypto_test.gno b/examples/gno.land/p/demo/testutils/crypto_test.gno new file mode 100644 index 00000000000..ac77b76dadf --- /dev/null +++ b/examples/gno.land/p/demo/testutils/crypto_test.gno @@ -0,0 +1,12 @@ +package testutils + +import ( + "testing" + + "gno.land/p/demo/uassert" +) + +func TestTestAddress(t *testing.T) { + testAddr := TestAddress("author1") + uassert.Equal(t, "g1v96hg6r0wgc47h6lta047h6lta047h6lm33tq6", string(testAddr)) +} diff --git a/examples/gno.land/r/demo/keystore/keystore_test.gno b/examples/gno.land/r/demo/keystore/keystore_test.gno index ffd8e60936f..41597016ea3 100644 --- a/examples/gno.land/r/demo/keystore/keystore_test.gno +++ b/examples/gno.land/r/demo/keystore/keystore_test.gno @@ -11,7 +11,11 @@ import ( ) func TestRender(t *testing.T) { - const ( + // https://github.com/gnolang/gno/pull/3375 changed const -> var : + // For some reason non native functions fails on constants with + // constant overflows (code=2), this does not happens when using a variable + // TODO: check this issue after and if this PR is merged + var ( author1 std.Address = testutils.TestAddress("author1") author2 std.Address = testutils.TestAddress("author2") ) diff --git a/examples/gno.land/r/demo/microblog/microblog_test.gno b/examples/gno.land/r/demo/microblog/microblog_test.gno index a3c8f04ee7f..9ad98d3cbfe 100644 --- a/examples/gno.land/r/demo/microblog/microblog_test.gno +++ b/examples/gno.land/r/demo/microblog/microblog_test.gno @@ -10,7 +10,7 @@ import ( ) func TestMicroblog(t *testing.T) { - const ( + var ( author1 std.Address = testutils.TestAddress("author1") author2 std.Address = testutils.TestAddress("author2") ) diff --git a/gno.land/pkg/integration/testdata/assertorigincall.txtar b/gno.land/pkg/integration/testdata/assertorigincall.txtar index 2c4a27f9d06..2c5da25c0aa 100644 --- a/gno.land/pkg/integration/testdata/assertorigincall.txtar +++ b/gno.land/pkg/integration/testdata/assertorigincall.txtar @@ -43,7 +43,7 @@ gnokey maketx call -pkgpath gno.land/r/myrlm -func B -gas-fee 100000ugnot -gas-w stdout 'OK!' ## 3. MsgCall -> myrlm.C: PASS -gnokey maketx call -pkgpath gno.land/r/myrlm -func C -gas-fee 100000ugnot -gas-wanted 700000 -broadcast -chainid tendermint_test test1 +gnokey maketx call -pkgpath gno.land/r/myrlm -func C -gas-fee 100000ugnot -gas-wanted 1500000 -broadcast -chainid tendermint_test test1 stdout 'OK!' ## 4. MsgCall -> r/foo.A -> myrlm.A: PANIC diff --git a/gno.land/pkg/integration/testdata/grc20_invalid_address.txtar b/gno.land/pkg/integration/testdata/grc20_invalid_address.txtar index d3dcc86725c..0068384903e 100644 --- a/gno.land/pkg/integration/testdata/grc20_invalid_address.txtar +++ b/gno.land/pkg/integration/testdata/grc20_invalid_address.txtar @@ -4,7 +4,7 @@ loadpkg gno.land/r/demo/foo20 gnoland start # execute Faucet -gnokey maketx call -pkgpath gno.land/r/demo/foo20 -func Faucet -gas-fee 1000000ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1 +gnokey maketx call -pkgpath gno.land/r/demo/foo20 -func Faucet -gas-fee 10000000ugnot -gas-wanted 40000000 -broadcast -chainid=tendermint_test test1 stdout 'OK!' # execute Transfer for invalid address diff --git a/gno.land/pkg/integration/testdata/grc20_registry.txtar b/gno.land/pkg/integration/testdata/grc20_registry.txtar index df11e92f8db..4377e10a575 100644 --- a/gno.land/pkg/integration/testdata/grc20_registry.txtar +++ b/gno.land/pkg/integration/testdata/grc20_registry.txtar @@ -6,7 +6,7 @@ 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 150000 -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 1500000 -broadcast -chainid=tendermint_test test1 stdout 'not found' # add foo20, and foo20wrapper @@ -14,7 +14,7 @@ gnokey maketx addpkg -pkgdir $WORK/foo20 -pkgpath gno.land/r/foo20 -gas-fee 1000 gnokey maketx addpkg -pkgdir $WORK/foo20wrapper -pkgpath gno.land/r/foo20wrapper -gas-fee 1000000ugnot -gas-wanted 10000000 -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 800000 -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 8000000 -broadcast -chainid=tendermint_test test1 stdout 'same address, success!' -- registry/registry.gno -- diff --git a/gno.land/pkg/integration/testdata/grc721_emit.txtar b/gno.land/pkg/integration/testdata/grc721_emit.txtar index 6b4770e37c6..45101b74634 100644 --- a/gno.land/pkg/integration/testdata/grc721_emit.txtar +++ b/gno.land/pkg/integration/testdata/grc721_emit.txtar @@ -6,23 +6,23 @@ loadpkg gno.land/r/foo721 $WORK/foo721 gnoland start # Mint -gnokey maketx call -pkgpath gno.land/r/foo721 -func Mint -args g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5 -args 1 -gas-fee 1000000ugnot -gas-wanted 3500000 -broadcast -chainid=tendermint_test test1 +gnokey maketx call -pkgpath gno.land/r/foo721 -func Mint -args g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5 -args 1 -gas-fee 1000000ugnot -gas-wanted 35000000 -broadcast -chainid=tendermint_test test1 stdout '\[{\"type\":\"Mint\",\"attrs\":\[{\"key\":\"to\",\"value\":\"g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5\"},{\"key\":\"tokenId\",\"value\":\"1\"}],\"pkg_path\":\"gno.land\/r\/foo721\",\"func\":\"mint\"}\]' # Approve -gnokey maketx call -pkgpath gno.land/r/foo721 -func Approve -args g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj -args 1 -gas-fee 1000000ugnot -gas-wanted 3500000 -broadcast -chainid=tendermint_test test1 +gnokey maketx call -pkgpath gno.land/r/foo721 -func Approve -args g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj -args 1 -gas-fee 1000000ugnot -gas-wanted 35000000 -broadcast -chainid=tendermint_test test1 stdout '\[{\"type\":\"Approval\",\"attrs\":\[{\"key\":\"owner\",\"value\":\"g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5\"},{\"key\":\"to\",\"value\":\"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj\"},{\"key\":\"tokenId\",\"value\":\"1\"}],\"pkg_path\":\"gno.land\/r\/foo721\",\"func\":\"Approve\"}\]' # SetApprovalForAll -gnokey maketx call -pkgpath gno.land/r/foo721 -func SetApprovalForAll -args g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj -args false -gas-fee 1000000ugnot -gas-wanted 3500000 -broadcast -chainid=tendermint_test test1 +gnokey maketx call -pkgpath gno.land/r/foo721 -func SetApprovalForAll -args g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj -args false -gas-fee 1000000ugnot -gas-wanted 35000000 -broadcast -chainid=tendermint_test test1 stdout '\[{\"type\":\"ApprovalForAll\",\"attrs\":\[{\"key\":\"owner\",\"value\":\"g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5\"},{\"key\":\"to\",\"value\":\"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj\"},{\"key\":\"approved\",\"value\":\"false\"}],\"pkg_path\":\"gno\.land/r/foo721\",\"func\":\"setApprovalForAll\"}\]' # TransferFrom -gnokey maketx call -pkgpath gno.land/r/foo721 -func TransferFrom -args g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5 -args g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj -args 1 -gas-fee 1000000ugnot -gas-wanted 3500000 -broadcast -chainid=tendermint_test test1 +gnokey maketx call -pkgpath gno.land/r/foo721 -func TransferFrom -args g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5 -args g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj -args 1 -gas-fee 1000000ugnot -gas-wanted 35000000 -broadcast -chainid=tendermint_test test1 stdout '\[{\"type\":\"Transfer\",\"attrs\":\[{\"key\":\"from\",\"value\":\"g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5\"},{\"key\":\"to\",\"value\":\"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj\"},{\"key\":\"tokenId\",\"value\":\"1\"}],\"pkg_path\":\"gno\.land/r/foo721\",\"func\":\"transfer\"}\]' # Burn -gnokey maketx call -pkgpath gno.land/r/foo721 -func Burn -args 1 -gas-fee 1000000ugnot -gas-wanted 3500000 -broadcast -chainid=tendermint_test test1 +gnokey maketx call -pkgpath gno.land/r/foo721 -func Burn -args 1 -gas-fee 1000000ugnot -gas-wanted 35000000 -broadcast -chainid=tendermint_test test1 stdout '\[{\"type\":\"Burn\",\"attrs\":\[{\"key\":\"from\",\"value\":\"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj\"},{\"key\":\"tokenId\",\"value\":\"1\"}],\"pkg_path\":\"gno\.land/r/foo721\",\"func\":\"Burn\"}\]' diff --git a/gno.land/pkg/integration/testdata/issue_1786.txtar b/gno.land/pkg/integration/testdata/issue_1786.txtar index 0e66a882a6d..1cbaf2c6643 100644 --- a/gno.land/pkg/integration/testdata/issue_1786.txtar +++ b/gno.land/pkg/integration/testdata/issue_1786.txtar @@ -9,20 +9,20 @@ gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/demo/proxywugnot -gas-fee stdout OK! # approve wugnot to `proxywugnot ≈ g1fndyg0we60rdfchyy5dwxzkfmhl5u34j932rg3` -gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Approve -args "g1fndyg0we60rdfchyy5dwxzkfmhl5u34j932rg3" -args 10000 -gas-fee 1000000ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1 +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Approve -args "g1fndyg0we60rdfchyy5dwxzkfmhl5u34j932rg3" -args 10000 -gas-fee 1000000ugnot -gas-wanted 40000000 -broadcast -chainid=tendermint_test test1 stdout OK! # send 10000ugnot to `proxywugnot` to wrap it -gnokey maketx call -pkgpath gno.land/r/demo/proxywugnot --send "10000ugnot" -func ProxyWrap -gas-fee 1000000ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1 +gnokey maketx call -pkgpath gno.land/r/demo/proxywugnot --send "10000ugnot" -func ProxyWrap -gas-fee 1000000ugnot -gas-wanted 40000000 -broadcast -chainid=tendermint_test test1 stdout OK! # check user's wugnot balance -gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func BalanceOf -args "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5" -gas-fee 1000000ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1 +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func BalanceOf -args "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5" -gas-fee 1000000ugnot -gas-wanted 40000000 -broadcast -chainid=tendermint_test test1 stdout OK! stdout '10000 uint64' # unwrap 500 wugnot -gnokey maketx call -pkgpath gno.land/r/demo/proxywugnot -func ProxyUnwrap -args 500 -gas-fee 1000000ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1 +gnokey maketx call -pkgpath gno.land/r/demo/proxywugnot -func ProxyUnwrap -args 500 -gas-fee 1000000ugnot -gas-wanted 40000000 -broadcast -chainid=tendermint_test test1 # XXX without patching anything it will panic # panic msg: insufficient coins error diff --git a/gno.land/pkg/integration/testdata/prevrealm.txtar b/gno.land/pkg/integration/testdata/prevrealm.txtar index 4bbe16c3205..31f0ca336ba 100644 --- a/gno.land/pkg/integration/testdata/prevrealm.txtar +++ b/gno.land/pkg/integration/testdata/prevrealm.txtar @@ -34,7 +34,7 @@ env RFOO_USER_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 700000 -broadcast -chainid tendermint_test test1 +gnokey maketx call -pkgpath gno.land/r/myrlm -func A -gas-fee 100000ugnot -gas-wanted 1500000 -broadcast -chainid tendermint_test test1 stdout ${test1_user_addr} ## 2. MsgCall -> myrealm.B -> myrlm.A: user address diff --git a/gno.land/pkg/integration/testdata/wugnot.txtar b/gno.land/pkg/integration/testdata/wugnot.txtar index 5fa7dab2945..5a63e0148e2 100644 --- a/gno.land/pkg/integration/testdata/wugnot.txtar +++ b/gno.land/pkg/integration/testdata/wugnot.txtar @@ -2,14 +2,14 @@ loadpkg gno.land/r/demo/wugnot gnoland start -gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Render -gas-fee 1000000ugnot -gas-wanted 5000000 -args '' -broadcast -chainid=tendermint_test test1 +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Render -gas-fee 10000000ugnot -gas-wanted 5000000 -args '' -broadcast -chainid=tendermint_test test1 stdout '# wrapped GNOT \(\$wugnot\)' stdout 'Decimals..: 0' stdout 'Total supply..: 0' stdout 'Known accounts..: 0' stdout 'OK!' -gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Deposit -send 12345678ugnot -gas-fee 1000000ugnot -gas-wanted 5000000 -broadcast -chainid=tendermint_test test1 +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Deposit -send 12345678ugnot -gas-fee 1000000ugnot -gas-wanted 50000000 -broadcast -chainid=tendermint_test test1 stdout 'OK!' gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Render -gas-fee 1000000ugnot -gas-wanted 5000000 -args '' -broadcast -chainid=tendermint_test test1 @@ -18,7 +18,7 @@ stdout 'Known accounts..: 1' stdout 'OK!' # XXX: use test2 instead (depends on https://github.com/gnolang/gno/issues/1269#issuecomment-1806386069) -gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Deposit -send 12345678ugnot -gas-fee 1000000ugnot -gas-wanted 5000000 -broadcast -chainid=tendermint_test test1 +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Deposit -send 12345678ugnot -gas-fee 1000000ugnot -gas-wanted 100000000 -broadcast -chainid=tendermint_test test1 stdout 'OK!' gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Render -gas-fee 1000000ugnot -gas-wanted 5000000 -args '' -broadcast -chainid=tendermint_test test1 @@ -27,19 +27,19 @@ stdout 'Known accounts..: 1' # should be 2 once we can use test2 stdout 'OK!' # XXX: replace hardcoded address with test3 -gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Transfer -gas-fee 1000000ugnot -gas-wanted 5000000 -args 'g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq' -args '10000000' -broadcast -chainid=tendermint_test test1 +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Transfer -gas-fee 1000000ugnot -gas-wanted 100000000 -args 'g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq' -args '10000000' -broadcast -chainid=tendermint_test test1 stdout 'OK!' -gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Render -gas-fee 1000000ugnot -gas-wanted 5000000 -args '' -broadcast -chainid=tendermint_test test1 +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Render -gas-fee 1000000ugnot -gas-wanted 50000000 -args '' -broadcast -chainid=tendermint_test test1 stdout 'Total supply..: 24691356' stdout 'Known accounts..: 2' # should be 3 once we can use test2 stdout 'OK!' # XXX: use test3 instead (depends on https://github.com/gnolang/gno/issues/1269#issuecomment-1806386069) -gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Withdraw -args 10000000 -gas-fee 1000000ugnot -gas-wanted 5000000 -broadcast -chainid=tendermint_test test1 +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Withdraw -args 10000000 -gas-fee 1000000ugnot -gas-wanted 100000000 -broadcast -chainid=tendermint_test test1 stdout 'OK!' -gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Render -gas-fee 1000000ugnot -gas-wanted 5000000 -args '' -broadcast -chainid=tendermint_test test1 +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Render -gas-fee 1000000ugnot -gas-wanted 100000000 -args '' -broadcast -chainid=tendermint_test test1 stdout 'Total supply..: 14691356' stdout 'Known accounts..: 2' # should be 3 once we can use test2 stdout 'OK!' diff --git a/gnovm/stdlibs/crypto/bech32/bech32.gno b/gnovm/stdlibs/crypto/bech32/bech32.gno new file mode 100644 index 00000000000..92994b28813 --- /dev/null +++ b/gnovm/stdlibs/crypto/bech32/bech32.gno @@ -0,0 +1,445 @@ +// Copyright (c) 2017 The btcsuite developers +// Copyright (c) 2019 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package bech32 + +import ( + "strings" +) + +// charset is the set of characters used in the data section of bech32 strings. +// Note that this is ordered, such that for a given charset[i], i is the binary +// value of the character. +const charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" + +// gen encodes the generator polynomial for the bech32 BCH checksum. +var gen = []int{0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3} + +// toBytes converts each character in the string 'chars' to the value of the +// index of the corresponding character in 'charset'. +func toBytes(chars string) ([]byte, error) { + decoded := make([]byte, 0, len(chars)) + for i := 0; i < len(chars); i++ { + index := strings.IndexByte(charset, chars[i]) + if index < 0 { + return nil, ErrNonCharsetChar(chars[i]) + } + decoded = append(decoded, byte(index)) + } + return decoded, nil +} + +// bech32Polymod calculates the BCH checksum for a given hrp, values and +// checksum data. Checksum is optional, and if nil a 0 checksum is assumed. +// +// Values and checksum (if provided) MUST be encoded as 5 bits per element (base +// 32), otherwise the results are undefined. +// +// For more details on the polymod calculation, please refer to BIP 173. +func bech32Polymod(hrp string, values, checksum []byte) int { + chk := 1 + + // Account for the high bits of the HRP in the checksum. + for i := 0; i < len(hrp); i++ { + b := chk >> 25 + hiBits := int(hrp[i]) >> 5 + chk = (chk&0x1ffffff)<<5 ^ hiBits + for i := 0; i < 5; i++ { + if (b>>uint(i))&1 == 1 { + chk ^= gen[i] + } + } + } + + // Account for the separator (0) between high and low bits of the HRP. + // x^0 == x, so we eliminate the redundant xor used in the other rounds. + b := chk >> 25 + chk = (chk & 0x1ffffff) << 5 + for i := 0; i < 5; i++ { + if (b>>uint(i))&1 == 1 { + chk ^= gen[i] + } + } + + // Account for the low bits of the HRP. + for i := 0; i < len(hrp); i++ { + b := chk >> 25 + loBits := int(hrp[i]) & 31 + chk = (chk&0x1ffffff)<<5 ^ loBits + for i := 0; i < 5; i++ { + if (b>>uint(i))&1 == 1 { + chk ^= gen[i] + } + } + } + + // Account for the values. + for _, v := range values { + b := chk >> 25 + chk = (chk&0x1ffffff)<<5 ^ int(v) + for i := 0; i < 5; i++ { + if (b>>uint(i))&1 == 1 { + chk ^= gen[i] + } + } + } + + if checksum == nil { + // A nil checksum is used during encoding, so assume all bytes are zero. + // x^0 == x, so we eliminate the redundant xor used in the other rounds. + for v := 0; v < 6; v++ { + b := chk >> 25 + chk = (chk & 0x1ffffff) << 5 + for i := 0; i < 5; i++ { + if (b>>uint(i))&1 == 1 { + chk ^= gen[i] + } + } + } + } else { + // Checksum is provided during decoding, so use it. + for _, v := range checksum { + b := chk >> 25 + chk = (chk&0x1ffffff)<<5 ^ int(v) + for i := 0; i < 5; i++ { + if (b>>uint(i))&1 == 1 { + chk ^= gen[i] + } + } + } + } + + return chk +} + +// writeBech32Checksum calculates the checksum data expected for a string that +// will have the given hrp and payload data and writes it to the provided string +// builder. +// +// The payload data MUST be encoded as a base 32 (5 bits per element) byte slice +// and the hrp MUST only use the allowed character set (ascii chars between 33 +// and 126), otherwise the results are undefined. +// +// For more details on the checksum calculation, please refer to BIP 173. +func writeBech32Checksum(hrp string, data []byte, bldr *strings.Builder, + version Version) { + + bech32Const := int(VersionToConsts[version]) + polymod := bech32Polymod(hrp, data, nil) ^ bech32Const + for i := 0; i < 6; i++ { + b := byte((polymod >> uint(5*(5-i))) & 31) + + // This can't fail, given we explicitly cap the previous b byte by the + // first 31 bits. + c := charset[b] + bldr.WriteByte(c) + } +} + +// bech32VerifyChecksum verifies whether the bech32 string specified by the +// provided hrp and payload data (encoded as 5 bits per element byte slice) has +// the correct checksum suffix. The version of bech32 used (bech32 OG, or +// bech32m) is also returned to allow the caller to perform proper address +// validation (segwitv0 should use bech32, v1+ should use bech32m). +// +// Data MUST have more than 6 elements, otherwise this function panics. +// +// For more details on the checksum verification, please refer to BIP 173. +func bech32VerifyChecksum(hrp string, data []byte) (Version, bool) { + checksum := data[len(data)-6:] + values := data[:len(data)-6] + polymod := bech32Polymod(hrp, values, checksum) + + // Before BIP-350, we'd always check this against a static constant of + // 1 to know if the checksum was computed properly. As we want to + // generically support decoding for bech32m as well as bech32, we'll + // look up the returned value and compare it to the set of defined + // constants. + bech32Version, ok := ConstsToVersion[ChecksumConst(polymod)] + if ok { + return bech32Version, true + } + + return VersionUnknown, false +} + +// DecodeNoLimitWithVersion is a bech32 checksum version aware arbitrary string +// length decoder. This function will return the version of the decoded +// checksum constant so higher level validation can be performed to ensure the +// correct version of bech32 was used when encoding. +// +// Note that the returned data is 5-bit (base32) encoded and the human-readable +// part will be lowercase. +func DecodeNoLimitWithVersion(bech string) (string, []byte, Version, error) { + // The minimum allowed size of a bech32 string is 8 characters, since it + // needs a non-empty HRP, a separator, and a 6 character checksum. + if len(bech) < 8 { + return "", nil, VersionUnknown, ErrInvalidLength(len(bech)) + } + + // Only ASCII characters between 33 and 126 are allowed. + var hasLower, hasUpper bool + for i := 0; i < len(bech); i++ { + if bech[i] < 33 || bech[i] > 126 { + return "", nil, VersionUnknown, ErrInvalidCharacter(bech[i]) + } + + // The characters must be either all lowercase or all uppercase. Testing + // directly with ascii codes is safe here, given the previous test. + hasLower = hasLower || (bech[i] >= 97 && bech[i] <= 122) + hasUpper = hasUpper || (bech[i] >= 65 && bech[i] <= 90) + if hasLower && hasUpper { + return "", nil, VersionUnknown, ErrMixedCase{} + } + } + + // Bech32 standard uses only the lowercase for of strings for checksum + // calculation. + if hasUpper { + bech = strings.ToLower(bech) + } + + // The string is invalid if the last '1' is non-existent, it is the + // first character of the string (no human-readable part) or one of the + // last 6 characters of the string (since checksum cannot contain '1'). + one := strings.LastIndexByte(bech, '1') + if one < 1 || one+7 > len(bech) { + return "", nil, VersionUnknown, ErrInvalidSeparatorIndex(one) + } + + // The human-readable part is everything before the last '1'. + hrp := bech[:one] + data := bech[one+1:] + + // Each character corresponds to the byte with value of the index in + // 'charset'. + decoded, err := toBytes(data) + if err != nil { + return "", nil, VersionUnknown, err + } + + // Verify if the checksum (stored inside decoded[:]) is valid, given the + // previously decoded hrp. + bech32Version, ok := bech32VerifyChecksum(hrp, decoded) + if !ok { + // Invalid checksum. Calculate what it should have been, so that the + // error contains this information. + + // Extract the payload bytes and actual checksum in the string. + actual := bech[len(bech)-6:] + payload := decoded[:len(decoded)-6] + + // Calculate the expected checksum, given the hrp and payload + // data. We'll actually compute _both_ possibly valid checksum + // to further aide in debugging. + var expectedBldr strings.Builder + expectedBldr.Grow(6) + writeBech32Checksum(hrp, payload, &expectedBldr, Version0) + expectedVersion0 := expectedBldr.String() + + var b strings.Builder + b.Grow(6) + writeBech32Checksum(hrp, payload, &expectedBldr, VersionM) + expectedVersionM := expectedBldr.String() + + err = ErrInvalidChecksum{ + Expected: expectedVersion0, + ExpectedM: expectedVersionM, + Actual: actual, + } + return "", nil, VersionUnknown, err + } + + // We exclude the last 6 bytes, which is the checksum. + return hrp, decoded[:len(decoded)-6], bech32Version, nil +} + +// DecodeNoLimit decodes a bech32 encoded string, returning the human-readable +// part and the data part excluding the checksum. This function does NOT +// validate against the BIP-173 maximum length allowed for bech32 strings and +// is meant for use in custom applications (such as lightning network payment +// requests), NOT on-chain addresses. +// +// Note that the returned data is 5-bit (base32) encoded and the human-readable +// part will be lowercase. +func DecodeNoLimit(bech string) (string, []byte, error) { + hrp, data, _, err := DecodeNoLimitWithVersion(bech) + return hrp, data, err +} + +// Decode decodes a bech32 encoded string, returning the human-readable part and +// the data part excluding the checksum. +// +// Note that the returned data is 5-bit (base32) encoded and the human-readable +// part will be lowercase. +func Decode(bech string) (string, []byte, error) { + // The maximum allowed length for a bech32 string is 90. + if len(bech) > 90 { + return "", nil, ErrInvalidLength(len(bech)) + } + + hrp, data, _, err := DecodeNoLimitWithVersion(bech) + return hrp, data, err +} + +// DecodeGeneric is identical to the existing Decode method, but will also +// return bech32 version that matches the decoded checksum. This method should +// be used when decoding segwit addresses, as it enables additional +// verification to ensure the proper checksum is used. +func DecodeGeneric(bech string) (string, []byte, Version, error) { + // The maximum allowed length for a bech32 string is 90. + if len(bech) > 90 { + return "", nil, VersionUnknown, ErrInvalidLength(len(bech)) + } + + return DecodeNoLimitWithVersion(bech) +} + +// encodeGeneric is the base bech32 encoding function that is aware of the +// existence of the checksum versions. This method is private, as the Encode +// and EncodeM methods are intended to be used instead. +func encodeGeneric(hrp string, data []byte, + version Version) (string, error) { + + // The resulting bech32 string is the concatenation of the lowercase + // hrp, the separator 1, data and the 6-byte checksum. + hrp = strings.ToLower(hrp) + var bldr strings.Builder + bldr.Grow(len(hrp) + 1 + len(data) + 6) + bldr.WriteString(hrp) + bldr.WriteString("1") + + // Write the data part, using the bech32 charset. + for _, b := range data { + if int(b) >= len(charset) { + return "", ErrInvalidDataByte(b) + } + bldr.WriteByte(charset[b]) + } + + // Calculate and write the checksum of the data. + writeBech32Checksum(hrp, data, &bldr, version) + + return bldr.String(), nil +} + +// Encode encodes a byte slice into a bech32 string with the given +// human-readable part (HRP). The HRP will be converted to lowercase if needed +// since mixed cased encodings are not permitted and lowercase is used for +// checksum purposes. Note that the bytes must each encode 5 bits (base32). +func Encode(hrp string, data []byte) (string, error) { + return encodeGeneric(hrp, data, Version0) +} + +// EncodeM is the exactly same as the Encode method, but it uses the new +// bech32m constant instead of the original one. It should be used whenever one +// attempts to encode a segwit address of v1 and beyond. +func EncodeM(hrp string, data []byte) (string, error) { + return encodeGeneric(hrp, data, VersionM) +} + +// ConvertBits converts a byte slice where each byte is encoding fromBits bits, +// to a byte slice where each byte is encoding toBits bits. +func ConvertBits(data []byte, fromBits, toBits uint8, pad bool) ([]byte, error) { + if fromBits < 1 || fromBits > 8 || toBits < 1 || toBits > 8 { + return nil, ErrInvalidBitGroups{} + } + + // Determine the maximum size the resulting array can have after base + // conversion, so that we can size it a single time. This might be off + // by a byte depending on whether padding is used or not and if the input + // data is a multiple of both fromBits and toBits, but we ignore that and + // just size it to the maximum possible. + maxSize := len(data)*int(fromBits)/int(toBits) + 1 + + // The final bytes, each byte encoding toBits bits. + regrouped := make([]byte, 0, maxSize) + + // Keep track of the next byte we create and how many bits we have + // added to it out of the toBits goal. + nextByte := byte(0) + filledBits := uint8(0) + + for _, b := range data { + + // Discard unused bits. + b <<= 8 - fromBits + + // How many bits remaining to extract from the input data. + remFromBits := fromBits + for remFromBits > 0 { + // How many bits remaining to be added to the next byte. + remToBits := toBits - filledBits + + // The number of bytes to next extract is the minimum of + // remFromBits and remToBits. + toExtract := remFromBits + if remToBits < toExtract { + toExtract = remToBits + } + + // Add the next bits to nextByte, shifting the already + // added bits to the left. + nextByte = (nextByte << toExtract) | (b >> (8 - toExtract)) + + // Discard the bits we just extracted and get ready for + // next iteration. + b <<= toExtract + remFromBits -= toExtract + filledBits += toExtract + + // If the nextByte is completely filled, we add it to + // our regrouped bytes and start on the next byte. + if filledBits == toBits { + regrouped = append(regrouped, nextByte) + filledBits = 0 + nextByte = 0 + } + } + } + + // We pad any unfinished group if specified. + if pad && filledBits > 0 { + nextByte <<= toBits - filledBits + regrouped = append(regrouped, nextByte) + filledBits = 0 + nextByte = 0 + } + + // Any incomplete group must be <= 4 bits, and all zeroes. + if filledBits > 0 && (filledBits > 4 || nextByte != 0) { + return nil, ErrInvalidIncompleteGroup{} + } + + return regrouped, nil +} + +// EncodeFromBase256 converts a base256-encoded byte slice into a base32-encoded +// byte slice and then encodes it into a bech32 string with the given +// human-readable part (HRP). The HRP will be converted to lowercase if needed +// since mixed cased encodings are not permitted and lowercase is used for +// checksum purposes. +func EncodeFromBase256(hrp string, data []byte) (string, error) { + converted, err := ConvertBits(data, 8, 5, true) + if err != nil { + return "", err + } + return Encode(hrp, converted) +} + +// DecodeToBase256 decodes a bech32-encoded string into its associated +// human-readable part (HRP) and base32-encoded data, converts that data to a +// base256-encoded byte slice and returns it along with the lowercase HRP. +func DecodeToBase256(bech string) (string, []byte, error) { + hrp, data, err := Decode(bech) + if err != nil { + return "", nil, err + } + converted, err := ConvertBits(data, 5, 8, false) + if err != nil { + return "", nil, err + } + return hrp, converted, nil +} diff --git a/gnovm/stdlibs/crypto/bech32/bech32_test.gno b/gnovm/stdlibs/crypto/bech32/bech32_test.gno new file mode 100644 index 00000000000..3f637c40345 --- /dev/null +++ b/gnovm/stdlibs/crypto/bech32/bech32_test.gno @@ -0,0 +1,691 @@ +// Copyright (c) 2017-2020 The btcsuite developers +// Copyright (c) 2019 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package bech32 + +import ( + "bytes" + "encoding/hex" + "fmt" + "strings" + "testing" +) + +// TestBech32 tests whether decoding and re-encoding the valid BIP-173 test +// vectors works and if decoding invalid test vectors fails for the correct +// reason. +func TestBech32(t *testing.T) { + tests := []struct { + str string + expectedError error + }{ + {"A12UEL5L", nil}, + {"a12uel5l", nil}, + {"an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs", nil}, + {"abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", nil}, + {"11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", nil}, + {"split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", nil}, + {"split1checkupstagehandshakeupstreamerranterredcaperred2y9e2w", ErrInvalidChecksum{"2y9e3w", "2y9e3wlc445v", "2y9e2w"}}, // invalid checksum + {"s lit1checkupstagehandshakeupstreamerranterredcaperredp8hs2p", ErrInvalidCharacter(' ')}, // invalid character (space) in hrp + {"spl\x7Ft1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", ErrInvalidCharacter(127)}, // invalid character (DEL) in hrp + {"split1cheo2y9e2w", ErrNonCharsetChar('o')}, // invalid character (o) in data part + {"split1a2y9w", ErrInvalidSeparatorIndex(5)}, // too short data part + {"1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", ErrInvalidSeparatorIndex(0)}, // empty hrp + {"11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", ErrInvalidLength(91)}, // too long + + // Additional test vectors used in bitcoin core + {" 1nwldj5", ErrInvalidCharacter(' ')}, + {"\x7f" + "1axkwrx", ErrInvalidCharacter(0x7f)}, + {"\x801eym55h", ErrInvalidCharacter(0x80)}, + {"an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx", ErrInvalidLength(91)}, + {"pzry9x0s0muk", ErrInvalidSeparatorIndex(-1)}, + {"1pzry9x0s0muk", ErrInvalidSeparatorIndex(0)}, + {"x1b4n0q5v", ErrNonCharsetChar(98)}, + {"li1dgmt3", ErrInvalidSeparatorIndex(2)}, + {"de1lg7wt\xff", ErrInvalidCharacter(0xff)}, + {"A1G7SGD8", ErrInvalidChecksum{"2uel5l", "2uel5llqfn3a", "g7sgd8"}}, + {"10a06t8", ErrInvalidLength(7)}, + {"1qzzfhee", ErrInvalidSeparatorIndex(0)}, + {"a12UEL5L", ErrMixedCase{}}, + {"A12uEL5L", ErrMixedCase{}}, + } + + for i, test := range tests { + str := test.str + hrp, decoded, err := Decode(str) + if test.expectedError != err { + t.Errorf("%d: expected decoding error %v "+ + "instead got %v", i, test.expectedError, err) + continue + } + + if err != nil { + // End test case here if a decoding error was expected. + continue + } + + // Check that it encodes to the same string + encoded, err := Encode(hrp, decoded) + if err != nil { + t.Errorf("encoding failed: %v", err) + } + + if encoded != strings.ToLower(str) { + t.Errorf("expected data to encode to %v, but got %v", + str, encoded) + } + + // Flip a bit in the string an make sure it is caught. + pos := strings.LastIndexAny(str, "1") + flipped := str[:pos+1] + string((str[pos+1] ^ 1)) + str[pos+2:] + _, _, err = Decode(flipped) + if err == nil { + t.Error("expected decoding to fail") + } + } +} + +// TestBech32M tests that the following set of strings, based on the test +// vectors in BIP-350 are either valid or invalid using the new bech32m +// checksum algo. Some of these strings are similar to the set of above test +// vectors, but end up with different checksums. +func TestBech32M(t *testing.T) { + tests := []struct { + str string + expectedError error + }{ + {"A1LQFN3A", nil}, + {"a1lqfn3a", nil}, + {"an83characterlonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11sg7hg6", nil}, + {"abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx", nil}, + {"11llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllludsr8", nil}, + {"split1checkupstagehandshakeupstreamerranterredcaperredlc445v", nil}, + {"?1v759aa", nil}, + + // Additional test vectors used in bitcoin core + {"\x201xj0phk", ErrInvalidCharacter('\x20')}, + {"\x7f1g6xzxy", ErrInvalidCharacter('\x7f')}, + {"\x801vctc34", ErrInvalidCharacter('\x80')}, + {"an84characterslonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11d6pts4", ErrInvalidLength(91)}, + {"qyrz8wqd2c9m", ErrInvalidSeparatorIndex(-1)}, + {"1qyrz8wqd2c9m", ErrInvalidSeparatorIndex(0)}, + {"y1b0jsk6g", ErrNonCharsetChar(98)}, + {"lt1igcx5c0", ErrNonCharsetChar(105)}, + {"in1muywd", ErrInvalidSeparatorIndex(2)}, + {"mm1crxm3i", ErrNonCharsetChar(105)}, + {"au1s5cgom", ErrNonCharsetChar(111)}, + {"M1VUXWEZ", ErrInvalidChecksum{"mzl49c", "mzl49cw70eq6", "vuxwez"}}, + {"16plkw9", ErrInvalidLength(7)}, + {"1p2gdwpf", ErrInvalidSeparatorIndex(0)}, + + {" 1nwldj5", ErrInvalidCharacter(' ')}, + {"\x7f" + "1axkwrx", ErrInvalidCharacter(0x7f)}, + {"\x801eym55h", ErrInvalidCharacter(0x80)}, + } + + for i, test := range tests { + str := test.str + hrp, decoded, err := Decode(str) + if test.expectedError != err { + t.Errorf("%d: (%v) expected decoding error %v "+ + "instead got %v", i, str, test.expectedError, + err) + continue + } + + if err != nil { + // End test case here if a decoding error was expected. + continue + } + + // Check that it encodes to the same string, using bech32 m. + encoded, err := EncodeM(hrp, decoded) + if err != nil { + t.Errorf("encoding failed: %v", err) + } + + if encoded != strings.ToLower(str) { + t.Errorf("expected data to encode to %v, but got %v", + str, encoded) + } + + // Flip a bit in the string an make sure it is caught. + pos := strings.LastIndexAny(str, "1") + flipped := str[:pos+1] + string((str[pos+1] ^ 1)) + str[pos+2:] + _, _, err = Decode(flipped) + if err == nil { + t.Error("expected decoding to fail") + } + } +} + +// TestBech32DecodeGeneric tests that given a bech32 string, or a bech32m +// string, the proper checksum version is returned so that callers can perform +// segwit addr validation. +func TestBech32DecodeGeneric(t *testing.T) { + tests := []struct { + str string + version Version + }{ + {"A1LQFN3A", VersionM}, + {"a1lqfn3a", VersionM}, + {"an83characterlonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11sg7hg6", VersionM}, + {"abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx", VersionM}, + {"11llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllludsr8", VersionM}, + {"split1checkupstagehandshakeupstreamerranterredcaperredlc445v", VersionM}, + {"?1v759aa", VersionM}, + + {"A12UEL5L", Version0}, + {"a12uel5l", Version0}, + {"an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs", Version0}, + {"abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", Version0}, + {"11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", Version0}, + {"split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", Version0}, + + {"BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4", Version0}, + {"tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", Version0}, + {"bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y", VersionM}, + {"BC1SW50QGDZ25J", VersionM}, + {"bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs", VersionM}, + {"tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy", Version0}, + {"tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c", VersionM}, + {"bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0", VersionM}, + } + for i, test := range tests { + _, _, version, err := DecodeGeneric(test.str) + if err != nil { + t.Errorf("%d: (%v) unexpected error during "+ + "decoding: %v", i, test.str, err) + continue + } + + if version != test.version { + t.Errorf("(%v): invalid version: expected %v, got %v", + test.str, test.version, version) + } + } +} + +// TestMixedCaseEncode ensures mixed case HRPs are converted to lowercase as +// expected when encoding and that decoding the produced encoding when converted +// to all uppercase produces the lowercase HRP and original data. +func TestMixedCaseEncode(t *testing.T) { + tests := []struct { + name string + hrp string + data string + encoded string + }{{ + name: "all uppercase HRP with no data", + hrp: "A", + data: "", + encoded: "a12uel5l", + }, { + name: "all uppercase HRP with data", + hrp: "UPPERCASE", + data: "787878", + encoded: "uppercase10pu8sss7kmp", + }, { + name: "mixed case HRP even offsets uppercase", + hrp: "AbCdEf", + data: "00443214c74254b635cf84653a56d7c675be77df", + encoded: "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", + }, { + name: "mixed case HRP odd offsets uppercase ", + hrp: "aBcDeF", + data: "00443214c74254b635cf84653a56d7c675be77df", + encoded: "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", + }, { + name: "all lowercase HRP", + hrp: "abcdef", + data: "00443214c74254b635cf84653a56d7c675be77df", + encoded: "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", + }} + + for _, test := range tests { + // Convert the text hex to bytes, convert those bytes from base256 to + // base32, then ensure the encoded result with the HRP provided in the + // test data is as expected. + data, err := hex.DecodeString(test.data) + if err != nil { + t.Errorf("%q: invalid hex %q: %v", test.name, test.data, err) + continue + } + convertedData, err := ConvertBits(data, 8, 5, true) + if err != nil { + t.Errorf("%q: unexpected convert bits error: %v", test.name, + err) + continue + } + gotEncoded, err := Encode(test.hrp, convertedData) + if err != nil { + t.Errorf("%q: unexpected encode error: %v", test.name, err) + continue + } + if gotEncoded != test.encoded { + t.Errorf("%q: mismatched encoding -- got %q, want %q", test.name, + gotEncoded, test.encoded) + continue + } + + // Ensure the decoding the expected lowercase encoding converted to all + // uppercase produces the lowercase HRP and original data. + gotHRP, gotData, err := Decode(strings.ToUpper(test.encoded)) + if err != nil { + t.Errorf("%q: unexpected decode error: %v", test.name, err) + continue + } + wantHRP := strings.ToLower(test.hrp) + if gotHRP != wantHRP { + t.Errorf("%q: mismatched decoded HRP -- got %q, want %q", test.name, + gotHRP, wantHRP) + continue + } + convertedGotData, err := ConvertBits(gotData, 5, 8, false) + if err != nil { + t.Errorf("%q: unexpected convert bits error: %v", test.name, + err) + continue + } + if !bytes.Equal(convertedGotData, data) { + t.Errorf("%q: mismatched data -- got %x, want %x", test.name, + convertedGotData, data) + continue + } + } +} + +// TestCanDecodeUnlimitedBech32 tests whether decoding a large bech32 string works +// when using the DecodeNoLimit version +func TestCanDecodeUnlimitedBech32(t *testing.T) { + input := "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq5kx0yd" + + // Sanity check that an input of this length errors on regular Decode() + _, _, err := Decode(input) + if err == nil { + t.Fatalf("Test vector not appropriate") + } + + // Try and decode it. + hrp, data, err := DecodeNoLimit(input) + if err != nil { + t.Fatalf("Expected decoding of large string to work. Got error: %v", err) + } + + // Verify data for correctness. + if hrp != "1" { + t.Fatalf("Unexpected hrp: %v", hrp) + } + decodedHex := fmt.Sprintf("%x", data) + expected := "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000" + if decodedHex != expected { + t.Fatalf("Unexpected decoded data: %s", decodedHex) + } +} + +// TestBech32Base256 ensures decoding and encoding various bech32, HRPs, and +// data produces the expected results when using EncodeFromBase256 and +// DecodeToBase256. It includes tests for proper handling of case +// manipulations. +func TestBech32Base256(t *testing.T) { + tests := []struct { + name string // test name + encoded string // bech32 string to decode + hrp string // expected human-readable part + data string // expected hex-encoded data + err error // expected error + }{{ + name: "all uppercase, no data", + encoded: "A12UEL5L", + hrp: "a", + data: "", + }, { + name: "long hrp with separator and excluded chars, no data", + encoded: "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs", + hrp: "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio", + data: "", + }, { + name: "6 char hrp with data with leading zero", + encoded: "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", + hrp: "abcdef", + data: "00443214c74254b635cf84653a56d7c675be77df", + }, { + name: "hrp same as separator and max length encoded string", + encoded: "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", + hrp: "1", + data: "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + }, { + name: "5 char hrp with data chosen to produce human-readable data part", + encoded: "split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", + hrp: "split", + data: "c5f38b70305f519bf66d85fb6cf03058f3dde463ecd7918f2dc743918f2d", + }, { + name: "same as previous but with checksum invalidated", + encoded: "split1checkupstagehandshakeupstreamerranterredcaperred2y9e2w", + err: ErrInvalidChecksum{"2y9e3w", "2y9e3wlc445v", "2y9e2w"}, + }, { + name: "hrp with invalid character (space)", + encoded: "s lit1checkupstagehandshakeupstreamerranterredcaperredp8hs2p", + err: ErrInvalidCharacter(' '), + }, { + name: "hrp with invalid character (DEL)", + encoded: "spl\x7ft1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", + err: ErrInvalidCharacter(127), + }, { + name: "data part with invalid character (o)", + encoded: "split1cheo2y9e2w", + err: ErrNonCharsetChar('o'), + }, { + name: "data part too short", + encoded: "split1a2y9w", + err: ErrInvalidSeparatorIndex(5), + }, { + name: "empty hrp", + encoded: "1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", + err: ErrInvalidSeparatorIndex(0), + }, { + name: "no separator", + encoded: "pzry9x0s0muk", + err: ErrInvalidSeparatorIndex(-1), + }, { + name: "too long by one char", + encoded: "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", + err: ErrInvalidLength(91), + }, { + name: "invalid due to mixed case in hrp", + encoded: "aBcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", + err: ErrMixedCase{}, + }, { + name: "invalid due to mixed case in data part", + encoded: "abcdef1Qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", + err: ErrMixedCase{}, + }} + + for _, test := range tests { + // Ensure the decode either produces an error or not as expected. + str := test.encoded + gotHRP, gotData, err := DecodeToBase256(str) + if test.err != err { + t.Errorf("%q: unexpected decode error -- got %v, want %v", + test.name, err, test.err) + continue + } + if err != nil { + // End test case here if a decoding error was expected. + continue + } + + // Ensure the expected HRP and original data are as expected. + if gotHRP != test.hrp { + t.Errorf("%q: mismatched decoded HRP -- got %q, want %q", test.name, + gotHRP, test.hrp) + continue + } + data, err := hex.DecodeString(test.data) + if err != nil { + t.Errorf("%q: invalid hex %q: %v", test.name, test.data, err) + continue + } + if !bytes.Equal(gotData, data) { + t.Errorf("%q: mismatched data -- got %x, want %x", test.name, + gotData, data) + continue + } + + // Encode the same data with the HRP converted to all uppercase and + // ensure the result is the lowercase version of the original encoded + // bech32 string. + gotEncoded, err := EncodeFromBase256(strings.ToUpper(test.hrp), data) + if err != nil { + t.Errorf("%q: unexpected uppercase HRP encode error: %v", test.name, + err) + } + wantEncoded := strings.ToLower(str) + if gotEncoded != wantEncoded { + t.Errorf("%q: mismatched encoding -- got %q, want %q", test.name, + gotEncoded, wantEncoded) + } + + // Encode the same data with the HRP converted to all lowercase and + // ensure the result is the lowercase version of the original encoded + // bech32 string. + gotEncoded, err = EncodeFromBase256(strings.ToLower(test.hrp), data) + if err != nil { + t.Errorf("%q: unexpected lowercase HRP encode error: %v", test.name, + err) + } + if gotEncoded != wantEncoded { + t.Errorf("%q: mismatched encoding -- got %q, want %q", test.name, + gotEncoded, wantEncoded) + } + + // Encode the same data with the HRP converted to mixed upper and + // lowercase and ensure the result is the lowercase version of the + // original encoded bech32 string. + var mixedHRPBuilder strings.Builder + for i, r := range test.hrp { + if i%2 == 0 { + mixedHRPBuilder.WriteString(strings.ToUpper(string(r))) + continue + } + mixedHRPBuilder.WriteRune(r) + } + gotEncoded, err = EncodeFromBase256(mixedHRPBuilder.String(), data) + if err != nil { + t.Errorf("%q: unexpected lowercase HRP encode error: %v", test.name, + err) + } + if gotEncoded != wantEncoded { + t.Errorf("%q: mismatched encoding -- got %q, want %q", test.name, + gotEncoded, wantEncoded) + } + + // Ensure a bit flip in the string is caught. + pos := strings.LastIndexAny(test.encoded, "1") + flipped := str[:pos+1] + string((str[pos+1] ^ 1)) + str[pos+2:] + _, _, err = DecodeToBase256(flipped) + if err == nil { + t.Error("expected decoding to fail") + } + } +} + +// BenchmarkEncodeDecodeCycle performs a benchmark for a full encode/decode +// cycle of a bech32 string. It also reports the allocation count, which we +// expect to be 2 for a fully optimized cycle. +func BenchmarkEncodeDecodeCycle(b *testing.B) { + // Use a fixed, 49-byte raw data for testing. + inputData, err := hex.DecodeString("cbe6365ddbcda9a9915422c3f091c13f8c7b2f263b8d34067bd12c274408473fa764871c9dd51b1bb34873b3473b633ed1") + if err != nil { + b.Fatalf("failed to initialize input data: %v", err) + } + + // Convert this into a 79-byte, base 32 byte slice. + base32Input, err := ConvertBits(inputData, 8, 5, true) + if err != nil { + b.Fatalf("failed to convert input to 32 bits-per-element: %v", err) + } + + // Use a fixed hrp for the tests. This should generate an encoded bech32 + // string of size 90 (the maximum allowed by BIP-173). + hrp := "bc" + + // Begin the benchmark. Given that we test one roundtrip per iteration + // (that is, one Encode() and one Decode() operation), we expect at most + // 2 allocations per reported test op. + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + str, err := Encode(hrp, base32Input) + if err != nil { + b.Fatalf("failed to encode input: %v", err) + } + + _, _, err = Decode(str) + if err != nil { + b.Fatalf("failed to decode string: %v", err) + } + } +} + +// TestConvertBits tests whether base conversion works using TestConvertBits(). +func TestConvertBits(t *testing.T) { + tests := []struct { + input string + output string + fromBits uint8 + toBits uint8 + pad bool + }{ + // Trivial empty conversions. + {"", "", 8, 5, false}, + {"", "", 8, 5, true}, + {"", "", 5, 8, false}, + {"", "", 5, 8, true}, + + // Conversions of 0 value with/without padding. + {"00", "00", 8, 5, false}, + {"00", "0000", 8, 5, true}, + {"0000", "00", 5, 8, false}, + {"0000", "0000", 5, 8, true}, + + // Testing when conversion ends exactly at the byte edge. This makes + // both padded and unpadded versions the same. + {"0000000000", "0000000000000000", 8, 5, false}, + {"0000000000", "0000000000000000", 8, 5, true}, + {"0000000000000000", "0000000000", 5, 8, false}, + {"0000000000000000", "0000000000", 5, 8, true}, + + // Conversions of full byte sequences. + {"ffffff", "1f1f1f1f1e", 8, 5, true}, + {"1f1f1f1f1e", "ffffff", 5, 8, false}, + {"1f1f1f1f1e", "ffffff00", 5, 8, true}, + + // Sample random conversions. + {"c9ca", "190705", 8, 5, false}, + {"c9ca", "19070500", 8, 5, true}, + {"19070500", "c9ca", 5, 8, false}, + {"19070500", "c9ca00", 5, 8, true}, + + // Test cases tested on TestConvertBitsFailures with their corresponding + // fixes. + {"ff", "1f1c", 8, 5, true}, + {"1f1c10", "ff20", 5, 8, true}, + + // Large conversions. + { + "cbe6365ddbcda9a9915422c3f091c13f8c7b2f263b8d34067bd12c274408473fa764871c9dd51b1bb34873b3473b633ed1", + "190f13030c170e1b1916141a13040a14040b011f01040e01071e0607160b1906070e06130801131b1a0416020e110008081c1f1a0e19040703120e1d0a06181b160d0407070c1a07070d11131d1408", + 8, 5, true, + }, + { + "190f13030c170e1b1916141a13040a14040b011f01040e01071e0607160b1906070e06130801131b1a0416020e110008081c1f1a0e19040703120e1d0a06181b160d0407070c1a07070d11131d1408", + "cbe6365ddbcda9a9915422c3f091c13f8c7b2f263b8d34067bd12c274408473fa764871c9dd51b1bb34873b3473b633ed100", + 5, 8, true, + }, + } + + for i, tc := range tests { + input, err := hex.DecodeString(tc.input) + if err != nil { + t.Fatalf("invalid test input data: %v", err) + } + + expected, err := hex.DecodeString(tc.output) + if err != nil { + t.Fatalf("invalid test output data: %v", err) + } + + actual, err := ConvertBits(input, tc.fromBits, tc.toBits, tc.pad) + if err != nil { + t.Fatalf("test case %d failed: %v", i, err) + } + + if !bytes.Equal(actual, expected) { + t.Fatalf("test case %d has wrong output; expected=%x actual=%x", + i, expected, actual) + } + } +} + +// TestConvertBitsFailures tests for the expected conversion failures of +// ConvertBits(). +func TestConvertBitsFailures(t *testing.T) { + tests := []struct { + input string + fromBits uint8 + toBits uint8 + pad bool + err error + }{ + // Not enough output bytes when not using padding. + {"ff", 8, 5, false, ErrInvalidIncompleteGroup{}}, + {"1f1c10", 5, 8, false, ErrInvalidIncompleteGroup{}}, + + // Unsupported bit conversions. + {"", 0, 5, false, ErrInvalidBitGroups{}}, + {"", 10, 5, false, ErrInvalidBitGroups{}}, + {"", 5, 0, false, ErrInvalidBitGroups{}}, + {"", 5, 10, false, ErrInvalidBitGroups{}}, + } + + for i, tc := range tests { + input, err := hex.DecodeString(tc.input) + if err != nil { + t.Fatalf("invalid test input data: %v", err) + } + + _, err = ConvertBits(input, tc.fromBits, tc.toBits, tc.pad) + if err != tc.err { + t.Fatalf("test case %d failure: expected '%v' got '%v'", i, + tc.err, err) + } + } + +} + +// BenchmarkConvertBitsDown benchmarks the speed and memory allocation behavior +// of ConvertBits when converting from a higher base into a lower base (e.g. 8 +// => 5). +// +// Only a single allocation is expected, which is used for the output array. +func BenchmarkConvertBitsDown(b *testing.B) { + // Use a fixed, 49-byte raw data for testing. + inputData, err := hex.DecodeString("cbe6365ddbcda9a9915422c3f091c13f8c7b2f263b8d34067bd12c274408473fa764871c9dd51b1bb34873b3473b633ed1") + if err != nil { + b.Fatalf("failed to initialize input data: %v", err) + } + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := ConvertBits(inputData, 8, 5, true) + if err != nil { + b.Fatalf("error converting bits: %v", err) + } + } +} + +// BenchmarkConvertBitsUp benchmarks the speed and memory allocation behavior +// of ConvertBits when converting from a lower base into a higher base (e.g. 5 +// => 8). +// +// Only a single allocation is expected, which is used for the output array. +func BenchmarkConvertBitsUp(b *testing.B) { + // Use a fixed, 79-byte raw data for testing. + inputData, err := hex.DecodeString("190f13030c170e1b1916141a13040a14040b011f01040e01071e0607160b1906070e06130801131b1a0416020e110008081c1f1a0e19040703120e1d0a06181b160d0407070c1a07070d11131d1408") + if err != nil { + b.Fatalf("failed to initialize input data: %v", err) + } + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := ConvertBits(inputData, 8, 5, true) + if err != nil { + b.Fatalf("error converting bits: %v", err) + } + } +} diff --git a/gnovm/stdlibs/crypto/bech32/error.gno b/gnovm/stdlibs/crypto/bech32/error.gno new file mode 100644 index 00000000000..dafe9b68516 --- /dev/null +++ b/gnovm/stdlibs/crypto/bech32/error.gno @@ -0,0 +1,83 @@ +// Copyright (c) 2019 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package bech32 + +// ErrMixedCase is returned when the bech32 string has both lower and uppercase +// characters. +type ErrMixedCase struct{} + +func (e ErrMixedCase) Error() string { + return "string not all lowercase or all uppercase" +} + +// ErrInvalidBitGroups is returned when conversion is attempted between byte +// slices using bit-per-element of unsupported value. +type ErrInvalidBitGroups struct{} + +func (e ErrInvalidBitGroups) Error() string { + return "only bit groups between 1 and 8 allowed" +} + +// ErrInvalidIncompleteGroup is returned when then byte slice used as input has +// data of wrong length. +type ErrInvalidIncompleteGroup struct{} + +func (e ErrInvalidIncompleteGroup) Error() string { + return "invalid incomplete group" +} + +// ErrInvalidLength is returned when the bech32 string has an invalid length +// given the BIP-173 defined restrictions. +type ErrInvalidLength int + +func (e ErrInvalidLength) Error() string { + return "invalid bech32 string length" +} + +// ErrInvalidCharacter is returned when the bech32 string has a character +// outside the range of the supported charset. +type ErrInvalidCharacter rune + +func (e ErrInvalidCharacter) Error() string { + return "invalid character in string: " + string(e) +} + +// ErrInvalidSeparatorIndex is returned when the separator character '1' is +// in an invalid position in the bech32 string. +type ErrInvalidSeparatorIndex int + +func (e ErrInvalidSeparatorIndex) Error() string { + return "invalid separator index" + string(e) +} + +// ErrNonCharsetChar is returned when a character outside of the specific +// bech32 charset is used in the string. +type ErrNonCharsetChar rune + +func (e ErrNonCharsetChar) Error() string { + return "invalid character not part of charset" +} + +// ErrInvalidChecksum is returned when the extracted checksum of the string +// is different than what was expected. Both the original version, as well as +// the new bech32m checksum may be specified. +type ErrInvalidChecksum struct { + Expected string + ExpectedM string + Actual string +} + +func (e ErrInvalidChecksum) Error() string { + return "invalid checksum (expected (bech32=" + e.Expected + + " bech32m=)" + e.ExpectedM + ", got " + e.Actual + ")" +} + +// ErrInvalidDataByte is returned when a byte outside the range required for +// conversion into a string was found. +type ErrInvalidDataByte byte + +func (e ErrInvalidDataByte) Error() string { + return "invalid data byte: " + string(e) +} diff --git a/gnovm/stdlibs/crypto/bech32/version.gno b/gnovm/stdlibs/crypto/bech32/version.gno new file mode 100644 index 00000000000..147037db9aa --- /dev/null +++ b/gnovm/stdlibs/crypto/bech32/version.gno @@ -0,0 +1,43 @@ +package bech32 + +// ChecksumConst is a type that represents the currently defined bech32 +// checksum constants. +type ChecksumConst int + +const ( + // Version0Const is the original constant used in the checksum + // verification for bech32. + Version0Const ChecksumConst = 1 + + // VersionMConst is the new constant used for bech32m checksum + // verification. + VersionMConst ChecksumConst = 0x2bc830a3 +) + +// Version defines the current set of bech32 versions. +type Version uint8 + +const ( + // Version0 defines the original bech version. + Version0 Version = iota + + // VersionM is the new bech32 version defined in BIP-350, also known as + // bech32m. + VersionM + + // VersionUnknown denotes an unknown bech version. + VersionUnknown +) + +// VersionToConsts maps bech32 versions to the checksum constant to be used +// when encoding, and asserting a particular version when decoding. +var VersionToConsts = map[Version]ChecksumConst{ + Version0: Version0Const, + VersionM: VersionMConst, +} + +// ConstsToVersion maps a bech32 constant to the version it's associated with. +var ConstsToVersion = map[ChecksumConst]Version{ + Version0Const: Version0, + VersionMConst: VersionM, +} diff --git a/gnovm/stdlibs/generated.go b/gnovm/stdlibs/generated.go index d5ab052028f..6e757561ef2 100644 --- a/gnovm/stdlibs/generated.go +++ b/gnovm/stdlibs/generated.go @@ -640,106 +640,6 @@ var nativeFuncs = [...]NativeFunc{ )) }, }, - { - "std", - "derivePkgAddr", - []gno.FieldTypeExpr{ - {Name: gno.N("p0"), Type: gno.X("string")}, - }, - []gno.FieldTypeExpr{ - {Name: gno.N("r0"), Type: gno.X("string")}, - }, - false, - 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_std.X_derivePkgAddr(p0) - - m.PushValue(gno.Go2GnoValue( - m.Alloc, - m.Store, - reflect.ValueOf(&r0).Elem(), - )) - }, - }, - { - "std", - "encodeBech32", - []gno.FieldTypeExpr{ - {Name: gno.N("p0"), Type: gno.X("string")}, - {Name: gno.N("p1"), Type: gno.X("[20]byte")}, - }, - []gno.FieldTypeExpr{ - {Name: gno.N("r0"), Type: gno.X("string")}, - }, - false, - func(m *gno.Machine) { - b := m.LastBlock() - var ( - p0 string - rp0 = reflect.ValueOf(&p0).Elem() - p1 [20]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_std.X_encodeBech32(p0, p1) - - m.PushValue(gno.Go2GnoValue( - m.Alloc, - m.Store, - reflect.ValueOf(&r0).Elem(), - )) - }, - }, - { - "std", - "decodeBech32", - []gno.FieldTypeExpr{ - {Name: gno.N("p0"), Type: gno.X("string")}, - }, - []gno.FieldTypeExpr{ - {Name: gno.N("r0"), Type: gno.X("string")}, - {Name: gno.N("r1"), Type: gno.X("[20]byte")}, - {Name: gno.N("r2"), Type: gno.X("bool")}, - }, - false, - func(m *gno.Machine) { - b := m.LastBlock() - var ( - p0 string - rp0 = reflect.ValueOf(&p0).Elem() - ) - - gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) - - r0, r1, r2 := libs_std.X_decodeBech32(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(), - )) - m.PushValue(gno.Go2GnoValue( - m.Alloc, - m.Store, - reflect.ValueOf(&r2).Elem(), - )) - }, - }, { "std", "assertCallerIsRealm", @@ -977,6 +877,7 @@ var initOrder = [...]string{ "bytes", "strings", "bufio", + "crypto/bech32", "encoding/binary", "math/bits", "math", diff --git a/gnovm/stdlibs/std/crypto.gno b/gnovm/stdlibs/std/crypto.gno index 402a6af3e22..adb6f687b56 100644 --- a/gnovm/stdlibs/std/crypto.gno +++ b/gnovm/stdlibs/std/crypto.gno @@ -1,7 +1,16 @@ package std +import ( + "crypto/bech32" + "crypto/sha256" + "errors" +) + type Address string // NOTE: bech32 +// bech32AddrPrefix defines the Bech32 prefix of an address +const bech32AddrPrefix = "g" + func (a Address) String() string { return string(a) } @@ -15,3 +24,61 @@ func (a Address) IsValid() bool { const RawAddressSize = 20 type RawAddress [RawAddressSize]byte + +func EncodeBech32(prefix string, bz [20]byte) Address { + b32, err := convertAndEncode(prefix, bz[:]) + if err != nil { + panic(err) // should not happen + } + return Address(b32) +} + +func DecodeBech32(addr Address) (string, [20]byte, bool) { + prefix, bz, err := decodeAndConvert(string(addr)) + if err != nil || len(bz) != 20 { + return "", [20]byte{}, false + } + return prefix, convertTo20Byte(bz), true +} + +func convertAndEncode(hrp string, data []byte) (string, error) { + converted, err := bech32.ConvertBits(data, 8, 5, true) + if err != nil { + return "", errors.New("encoding bech32 failed: " + err.Error()) + } + return bech32.Encode(hrp, converted) +} + +func decodeAndConvert(bech string) (string, []byte, error) { + hrp, data, err := bech32.DecodeNoLimit(bech) + if err != nil { + return "", nil, errors.New("decoding bech32 failed" + err.Error()) + } + converted, err := bech32.ConvertBits(data, 5, 8, false) + if err != nil { + return "", nil, errors.New("decoding bech32 failed" + err.Error()) + } + return hrp, converted, nil +} + +func DerivePkgAddr(pkgPath string) Address { + data := sumTruncated([]byte("pkgPath:" + pkgPath)) + return EncodeBech32(bech32AddrPrefix, data) +} + +func sumTruncated(bz []byte) [20]byte { + hash := sha256.Sum256(bz) + return convertTo20Byte(hash[:RawAddressSize]) +} + +func convertTo20Byte(in []byte) [20]byte { + /* For some reason [20]byte(bz) fails with: 'cannot convert []uint8 to ArrayKind' + Maybe there is an issue to create + */ + result := [20]byte{} + for index, b := range in { + result[index] = b + } + + return result +} diff --git a/gnovm/stdlibs/std/crypto_test.gno b/gnovm/stdlibs/std/crypto_test.gno index 75283c03523..70b42e43860 100644 --- a/gnovm/stdlibs/std/crypto_test.gno +++ b/gnovm/stdlibs/std/crypto_test.gno @@ -39,3 +39,22 @@ func TestValid(t *testing.T) { } } } + +func TestDerivePkgAddr(t *testing.T) { + type test struct { + inputPath string + expected string + } + + testCases := []test{ + {inputPath: "gno.land/r/gnoland/faucet", expected: "g1ttrq7mp4zy6dssnmgyyktnn4hcj3ys8xhju0n7"}, + {inputPath: "gno.land/r/demo/tamagotchi", expected: "g1a3tu874agjlkrpzt9x90xv3uzncapcn959yte4"}, + } + + for _, tc := range testCases { + result := DerivePkgAddr(tc.inputPath) + if result.String() != tc.expected { + t.Fatalf("Expected: %t, got: %t", tc.expected, result) + } + } +} diff --git a/gnovm/stdlibs/std/native.gno b/gnovm/stdlibs/std/native.gno index 0dcde1148e1..9cf8808a07e 100644 --- a/gnovm/stdlibs/std/native.gno +++ b/gnovm/stdlibs/std/native.gno @@ -45,25 +45,10 @@ func GetCallerAt(n int) Address { return Address(callerAt(n)) } -func DerivePkgAddr(pkgPath string) Address { - return Address(derivePkgAddr(pkgPath)) -} - -func EncodeBech32(prefix string, bz [20]byte) Address { - return Address(encodeBech32(prefix, bz)) -} - -func DecodeBech32(addr Address) (prefix string, bz [20]byte, ok bool) { - return decodeBech32(string(addr)) -} - // Variations which don't use named types. func origSend() (denoms []string, amounts []int64) func origCaller() string func origPkgAddr() string func callerAt(n int) string 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 fb181d9be31..9e398e907a2 100644 --- a/gnovm/stdlibs/std/native.go +++ b/gnovm/stdlibs/std/native.go @@ -2,7 +2,6 @@ package std import ( gno "github.com/gnolang/gno/gnovm/pkg/gnolang" - "github.com/gnolang/gno/tm2/pkg/bech32" "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/std" ) @@ -142,26 +141,6 @@ func currentRealm(m *gno.Machine) (address, pkgPath string) { return X_getRealm(m, 0) } -func X_derivePkgAddr(pkgPath string) string { - return string(gno.DerivePkgAddr(pkgPath).Bech32()) -} - -func X_encodeBech32(prefix string, bytes [20]byte) string { - b32, err := bech32.ConvertAndEncode(prefix, bytes[:]) - if err != nil { - panic(err) // should not happen - } - return b32 -} - -func X_decodeBech32(addr string) (prefix string, bytes [20]byte, ok bool) { - prefix, bz, err := bech32.Decode(addr) - if err != nil || len(bz) != 20 { - return "", [20]byte{}, false - } - 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) { diff --git a/gnovm/tests/files/std5.gno b/gnovm/tests/files/std5.gno index e339d7a6364..2f9e98bb4ec 100644 --- a/gnovm/tests/files/std5.gno +++ b/gnovm/tests/files/std5.gno @@ -13,7 +13,7 @@ func main() { // Stacktrace: // panic: frame not found -// callerAt(n) +// callerAt(n) // gonative:std.callerAt // std.GetCallerAt(2) // std/native.gno:45 diff --git a/gnovm/tests/files/std8.gno b/gnovm/tests/files/std8.gno index ee717bf16be..dfc2b8ca5fd 100644 --- a/gnovm/tests/files/std8.gno +++ b/gnovm/tests/files/std8.gno @@ -23,7 +23,7 @@ func main() { // Stacktrace: // panic: frame not found -// callerAt(n) +// callerAt(n) // gonative:std.callerAt // std.GetCallerAt(4) // std/native.gno:45 diff --git a/gnovm/tests/files/zrealm_natbind0.gno b/gnovm/tests/files/zrealm_natbind0.gno index 8e5f641e734..e6ebef6252e 100644 --- a/gnovm/tests/files/zrealm_natbind0.gno +++ b/gnovm/tests/files/zrealm_natbind0.gno @@ -69,7 +69,7 @@ func main() { // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, -// "ObjectID": "a7f5397443359ea76c50be82c77f1f893a060925:9" +// "ObjectID": "a7f5397443359ea76c50be82c77f1f893a060925:10" // }, // "FileName": "native.gno", // "IsMethod": false, From d54d00470dd1be891e9c22896aa3841fcfe02eb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milo=C5=A1=20=C5=BDivkovi=C4=87?= Date: Tue, 14 Jan 2025 15:16:14 +0100 Subject: [PATCH 05/18] feat: add `EstimateGas` to gnoclient (#3498) ## Description Closes #1826 along with https://github.com/gnolang/tm2-js-client/pull/192 This PR introduces a method `EstimateGas` to estimate the gas used by a transaction in gnoclient. We can use this call to get a ballpark estimate if a transaction succeeds, and if it does, how much gas it actually used. --- gno.land/pkg/gnoclient/client_test.go | 157 ++++++++++++++++++++++++++ gno.land/pkg/gnoclient/client_txs.go | 47 +++++++- 2 files changed, 203 insertions(+), 1 deletion(-) diff --git a/gno.land/pkg/gnoclient/client_test.go b/gno.land/pkg/gnoclient/client_test.go index 1f8563d34fe..54a15420a66 100644 --- a/gno.land/pkg/gnoclient/client_test.go +++ b/gno.land/pkg/gnoclient/client_test.go @@ -1,8 +1,11 @@ package gnoclient import ( + "errors" "testing" + "github.com/gnolang/gno/tm2/pkg/amino" + abciErrors "github.com/gnolang/gno/tm2/pkg/bft/abci/example/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -1409,3 +1412,157 @@ func addPackageSigningSeparately(t *testing.T, client Client, cfg BaseTxCfg, msg require.NotNil(t, res) return res, nil } + +func TestClient_EstimateGas(t *testing.T) { + t.Parallel() + + t.Run("RPC client not set", func(t *testing.T) { + t.Parallel() + + c := &Client{ + RPCClient: nil, // not set + } + + estimate, err := c.EstimateGas(&std.Tx{}) + + assert.Zero(t, estimate) + assert.ErrorIs(t, err, ErrMissingRPCClient) + }) + + t.Run("unsuccessful query, rpc error", func(t *testing.T) { + t.Parallel() + + var ( + rpcErr = errors.New("rpc error") + mockRPCClient = &mockRPCClient{ + abciQuery: func(path string, data []byte) (*ctypes.ResultABCIQuery, error) { + require.Equal(t, simulatePath, path) + + var tx std.Tx + + require.NoError(t, amino.Unmarshal(data, &tx)) + + return nil, rpcErr + }, + } + ) + + c := &Client{ + RPCClient: mockRPCClient, + } + + estimate, err := c.EstimateGas(&std.Tx{}) + + assert.Zero(t, estimate) + assert.ErrorIs(t, err, rpcErr) + }) + + t.Run("unsuccessful query, process error", func(t *testing.T) { + t.Parallel() + + var ( + response = &ctypes.ResultABCIQuery{ + Response: abci.ResponseQuery{ + ResponseBase: abci.ResponseBase{ + Error: abciErrors.UnknownError{}, + }, + }, + } + mockRPCClient = &mockRPCClient{ + abciQuery: func(path string, data []byte) (*ctypes.ResultABCIQuery, error) { + require.Equal(t, simulatePath, path) + + var tx std.Tx + + require.NoError(t, amino.Unmarshal(data, &tx)) + + return response, nil + }, + } + ) + + c := &Client{ + RPCClient: mockRPCClient, + } + + estimate, err := c.EstimateGas(&std.Tx{}) + + assert.Zero(t, estimate) + assert.ErrorIs(t, err, abciErrors.UnknownError{}) + }) + + t.Run("invalid response format", func(t *testing.T) { + t.Parallel() + + var ( + response = &ctypes.ResultABCIQuery{ + Response: abci.ResponseQuery{ + Value: []byte("totally valid amino"), + }, + } + mockRPCClient = &mockRPCClient{ + abciQuery: func(path string, data []byte) (*ctypes.ResultABCIQuery, error) { + require.Equal(t, simulatePath, path) + + var tx std.Tx + + require.NoError(t, amino.Unmarshal(data, &tx)) + + return response, nil + }, + } + ) + + c := &Client{ + RPCClient: mockRPCClient, + } + + estimate, err := c.EstimateGas(&std.Tx{}) + + assert.Zero(t, estimate) + assert.ErrorContains(t, err, "unable to unmarshal gas estimation response") + }) + + t.Run("valid gas estimation", func(t *testing.T) { + t.Parallel() + + var ( + gasUsed = int64(100000) + deliverResp = &abci.ResponseDeliverTx{ + GasUsed: gasUsed, + } + ) + + // Encode the response + encodedResp, err := amino.Marshal(deliverResp) + require.NoError(t, err) + + var ( + response = &ctypes.ResultABCIQuery{ + Response: abci.ResponseQuery{ + Value: encodedResp, // valid amino binary + }, + } + mockRPCClient = &mockRPCClient{ + abciQuery: func(path string, data []byte) (*ctypes.ResultABCIQuery, error) { + require.Equal(t, simulatePath, path) + + var tx std.Tx + + require.NoError(t, amino.Unmarshal(data, &tx)) + + return response, nil + }, + } + ) + + c := &Client{ + RPCClient: mockRPCClient, + } + + estimate, err := c.EstimateGas(&std.Tx{}) + + require.NoError(t, err) + assert.Equal(t, gasUsed, estimate) + }) +} diff --git a/gno.land/pkg/gnoclient/client_txs.go b/gno.land/pkg/gnoclient/client_txs.go index d7f6f053242..ab520eceda1 100644 --- a/gno.land/pkg/gnoclient/client_txs.go +++ b/gno.land/pkg/gnoclient/client_txs.go @@ -1,8 +1,11 @@ package gnoclient import ( + "fmt" + "github.com/gnolang/gno/gno.land/pkg/sdk/vm" "github.com/gnolang/gno/tm2/pkg/amino" + 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/errors" "github.com/gnolang/gno/tm2/pkg/sdk/bank" @@ -16,6 +19,8 @@ var ( ErrMissingRPCClient = errors.New("missing RPCClient") ) +const simulatePath = ".app/simulate" + // BaseTxCfg defines the base transaction configuration, shared by all message types type BaseTxCfg struct { GasFee string // Gas fee @@ -292,4 +297,44 @@ func (c *Client) BroadcastTxCommit(signedTx *std.Tx) (*ctypes.ResultBroadcastTxC return bres, nil } -// TODO: Add more functionality, examples, and unit tests. +// EstimateGas returns the least amount of gas required +// for the transaction to go through on the chain (minimum gas wanted). +// The estimation process assumes the transaction is properly signed +func (c *Client) EstimateGas(tx *std.Tx) (int64, error) { + // Make sure the RPC client is set + if err := c.validateRPCClient(); err != nil { + return 0, err + } + + // Prepare the transaction. + // The transaction needs to be amino-binary encoded + // in order to be estimated + encodedTx, err := amino.Marshal(tx) + if err != nil { + return 0, fmt.Errorf("unable to marshal tx: %w", err) + } + + // Perform the simulation query + resp, err := c.RPCClient.ABCIQuery(simulatePath, encodedTx) + if err != nil { + return 0, fmt.Errorf("unable to perform ABCI query: %w", err) + } + + // Extract the query response + if err = resp.Response.Error; err != nil { + return 0, fmt.Errorf("error encountered during ABCI query: %w", err) + } + + var deliverTx abci.ResponseDeliverTx + if err = amino.Unmarshal(resp.Response.Value, &deliverTx); err != nil { + return 0, fmt.Errorf("unable to unmarshal gas estimation response: %w", err) + } + + if err = deliverTx.Error; err != nil { + return 0, fmt.Errorf("error encountered during gas estimation: %w", err) + } + + // Return the actual value returned by the node + // for executing the transaction + return deliverTx.GasUsed, nil +} From 7761c3ba29695b1621c618d8382d696433622761 Mon Sep 17 00:00:00 2001 From: Marc Vertes Date: Tue, 14 Jan 2025 17:20:59 +0100 Subject: [PATCH 06/18] chore: revert "feat(gnovm): implement overflow checking at VM level" (#3508) Revert #3250 This reverts commit 68aff6464dfba782903cdb5e3b318a9b233a479e. This PR was merged before discussions were complete. It was finally rejected because it does not comply with Go specification, which stipulates that overflow on signed integers do not trigger runtime panic. See https://go.dev/ref/spec#Integer_overflow --- examples/gno.land/p/demo/grc/grc20/token.gno | 22 +- gnovm/pkg/gnolang/op_bench_test.go | 70 --- gnovm/pkg/gnolang/op_binary.go | 305 +++++------ gnovm/pkg/gnolang/op_inc_dec.go | 25 +- gnovm/stdlibs/generated.go | 1 + gnovm/stdlibs/math/const_test.gno | 77 +-- gnovm/stdlibs/math/overflow/overflow.gno | 501 ++++++++++++++++++ gnovm/stdlibs/math/overflow/overflow_test.gno | 200 +++++++ gnovm/stdlibs/std/coins.gno | 26 +- gnovm/tests/files/overflow0.gno | 10 - gnovm/tests/files/overflow1.gno | 10 - gnovm/tests/files/overflow2.gno | 10 - gnovm/tests/files/overflow3.gno | 10 - gnovm/tests/files/overflow4.gno | 10 - gnovm/tests/files/overflow5.gno | 10 - gnovm/tests/files/recover14.gno | 2 +- misc/genstd/util.go | 3 +- 17 files changed, 874 insertions(+), 418 deletions(-) delete mode 100644 gnovm/pkg/gnolang/op_bench_test.go create mode 100644 gnovm/stdlibs/math/overflow/overflow.gno create mode 100644 gnovm/stdlibs/math/overflow/overflow_test.gno delete mode 100644 gnovm/tests/files/overflow0.gno delete mode 100644 gnovm/tests/files/overflow1.gno delete mode 100644 gnovm/tests/files/overflow2.gno delete mode 100644 gnovm/tests/files/overflow3.gno delete mode 100644 gnovm/tests/files/overflow4.gno delete mode 100644 gnovm/tests/files/overflow5.gno diff --git a/examples/gno.land/p/demo/grc/grc20/token.gno b/examples/gno.land/p/demo/grc/grc20/token.gno index 4986eaebf04..3ab3abc63a3 100644 --- a/examples/gno.land/p/demo/grc/grc20/token.gno +++ b/examples/gno.land/p/demo/grc/grc20/token.gno @@ -1,6 +1,7 @@ package grc20 import ( + "math/overflow" "std" "strconv" @@ -169,24 +170,17 @@ func (led *PrivateLedger) Approve(owner, spender std.Address, amount uint64) err } // Mint increases the total supply of the token and adds the specified amount to the specified address. -func (led *PrivateLedger) Mint(address std.Address, amount uint64) (err error) { +func (led *PrivateLedger) Mint(address std.Address, amount uint64) error { if !address.IsValid() { return ErrInvalidAddress } - defer func() { - if r := recover(); r != nil { - if r != "addition overflow" { - panic(r) - } - err = ErrOverflow - } - }() - - // Convert amount and totalSupply to signed integers to enable - // overflow checking (not occuring on unsigned) when computing the sum. - // The maximum value for totalSupply is therefore 1<<63. - sum := int64(led.totalSupply) + int64(amount) + // XXX: math/overflow is not supporting uint64. + // This checks prevents overflow but makes the totalSupply limited to a uint63. + sum, ok := overflow.Add64(int64(led.totalSupply), int64(amount)) + if !ok { + return ErrOverflow + } led.totalSupply = uint64(sum) currentBalance := led.balanceOf(address) diff --git a/gnovm/pkg/gnolang/op_bench_test.go b/gnovm/pkg/gnolang/op_bench_test.go deleted file mode 100644 index 5874f980285..00000000000 --- a/gnovm/pkg/gnolang/op_bench_test.go +++ /dev/null @@ -1,70 +0,0 @@ -package gnolang - -import ( - "testing" - - "github.com/gnolang/gno/tm2/pkg/overflow" -) - -func BenchmarkOpAdd(b *testing.B) { - m := NewMachine("bench", nil) - x := TypedValue{T: IntType} - x.SetInt(4) - y := TypedValue{T: IntType} - y.SetInt(3) - - b.ResetTimer() - - for range b.N { - m.PushOp(OpHalt) - m.PushExpr(&BinaryExpr{}) - m.PushValue(x) - m.PushValue(y) - m.PushOp(OpAdd) - m.Run() - } -} - -//go:noinline -func AddNoOverflow(x, y int) int { return x + y } - -func BenchmarkAddNoOverflow(b *testing.B) { - x, y := 4, 3 - c := 0 - for range b.N { - c = AddNoOverflow(x, y) - } - if c != 7 { - b.Error("invalid result") - } -} - -func BenchmarkAddOverflow(b *testing.B) { - x, y := 4, 3 - c := 0 - for range b.N { - c = overflow.Addp(x, y) - } - if c != 7 { - b.Error("invalid result") - } -} - -func TestOpAdd1(t *testing.T) { - m := NewMachine("test", nil) - a := TypedValue{T: IntType} - a.SetInt(4) - b := TypedValue{T: IntType} - b.SetInt(3) - t.Log("a:", a, "b:", b) - - start := m.NumValues - m.PushOp(OpHalt) - m.PushExpr(&BinaryExpr{}) - m.PushValue(a) - m.PushValue(b) - m.PushOp(OpAdd) - m.Run() - res := m.ReapValues(start) - t.Log("res:", res) -} diff --git a/gnovm/pkg/gnolang/op_binary.go b/gnovm/pkg/gnolang/op_binary.go index 765f3ccbfbd..0e8eec9db23 100644 --- a/gnovm/pkg/gnolang/op_binary.go +++ b/gnovm/pkg/gnolang/op_binary.go @@ -7,7 +7,6 @@ import ( "github.com/cockroachdb/apd/v3" "github.com/gnolang/gno/gnovm/pkg/gnolang/internal/softfloat" - "github.com/gnolang/gno/tm2/pkg/overflow" ) // ---------------------------------------- @@ -185,9 +184,7 @@ func (m *Machine) doOpAdd() { } // add rv to lv. - if err := addAssign(m.Alloc, lv, rv); err != nil { - panic(err) - } + addAssign(m.Alloc, lv, rv) } func (m *Machine) doOpSub() { @@ -201,9 +198,7 @@ func (m *Machine) doOpSub() { } // sub rv from lv. - if err := subAssign(lv, rv); err != nil { - panic(err) - } + subAssign(lv, rv) } func (m *Machine) doOpBor() { @@ -259,7 +254,8 @@ func (m *Machine) doOpQuo() { } // lv / rv - if err := quoAssign(lv, rv); err != nil { + err := quoAssign(lv, rv) + if err != nil { panic(err) } } @@ -275,7 +271,8 @@ func (m *Machine) doOpRem() { } // lv % rv - if err := remAssign(lv, rv); err != nil { + err := remAssign(lv, rv) + if err != nil { panic(err) } } @@ -687,38 +684,23 @@ func isGeq(lv, rv *TypedValue) bool { } } -// addAssign adds lv to rv and stores the result to lv. -// It returns an exception in case of overflow on signed integers. -// The assignement is performed even in case of exception. -func addAssign(alloc *Allocator, lv, rv *TypedValue) *Exception { +// for doOpAdd and doOpAddAssign. +func addAssign(alloc *Allocator, lv, rv *TypedValue) { // set the result in lv. // NOTE this block is replicated in op_assign.go - ok := true switch baseOf(lv.T) { case StringType, UntypedStringType: lv.V = alloc.NewString(lv.GetString() + rv.GetString()) - // Signed integers may overflow, which triggers an exception. case IntType: - var r int - r, ok = overflow.Add(lv.GetInt(), rv.GetInt()) - lv.SetInt(r) + lv.SetInt(lv.GetInt() + rv.GetInt()) case Int8Type: - var r int8 - r, ok = overflow.Add8(lv.GetInt8(), rv.GetInt8()) - lv.SetInt8(r) + lv.SetInt8(lv.GetInt8() + rv.GetInt8()) case Int16Type: - var r int16 - r, ok = overflow.Add16(lv.GetInt16(), rv.GetInt16()) - lv.SetInt16(r) + lv.SetInt16(lv.GetInt16() + rv.GetInt16()) case Int32Type, UntypedRuneType: - var r int32 - r, ok = overflow.Add32(lv.GetInt32(), rv.GetInt32()) - lv.SetInt32(r) + lv.SetInt32(lv.GetInt32() + rv.GetInt32()) case Int64Type: - var r int64 - r, ok = overflow.Add64(lv.GetInt64(), rv.GetInt64()) - lv.SetInt64(r) - // Unsigned integers do not overflow, they just wrap. + lv.SetInt64(lv.GetInt64() + rv.GetInt64()) case UintType: lv.SetUint(lv.GetUint() + rv.GetUint()) case Uint8Type: @@ -758,42 +740,23 @@ func addAssign(alloc *Allocator, lv, rv *TypedValue) *Exception { lv.T, )) } - if !ok { - return &Exception{Value: typedString("addition overflow")} - } - return nil } -// subAssign subtracts lv to rv and stores the result to lv. -// It returns an exception in case of overflow on signed integers. -// The subtraction is performed even in case of exception. -func subAssign(lv, rv *TypedValue) *Exception { +// for doOpSub and doOpSubAssign. +func subAssign(lv, rv *TypedValue) { // set the result in lv. // NOTE this block is replicated in op_assign.go - ok := true switch baseOf(lv.T) { - // Signed integers may overflow, which triggers an exception. case IntType: - var r int - r, ok = overflow.Sub(lv.GetInt(), rv.GetInt()) - lv.SetInt(r) + lv.SetInt(lv.GetInt() - rv.GetInt()) case Int8Type: - var r int8 - r, ok = overflow.Sub8(lv.GetInt8(), rv.GetInt8()) - lv.SetInt8(r) + lv.SetInt8(lv.GetInt8() - rv.GetInt8()) case Int16Type: - var r int16 - r, ok = overflow.Sub16(lv.GetInt16(), rv.GetInt16()) - lv.SetInt16(r) + lv.SetInt16(lv.GetInt16() - rv.GetInt16()) case Int32Type, UntypedRuneType: - var r int32 - r, ok = overflow.Sub32(lv.GetInt32(), rv.GetInt32()) - lv.SetInt32(r) + lv.SetInt32(lv.GetInt32() - rv.GetInt32()) case Int64Type: - var r int64 - r, ok = overflow.Sub64(lv.GetInt64(), rv.GetInt64()) - lv.SetInt64(r) - // Unsigned integers do not overflow, they just wrap. + lv.SetInt64(lv.GetInt64() - rv.GetInt64()) case UintType: lv.SetUint(lv.GetUint() - rv.GetUint()) case Uint8Type: @@ -833,39 +796,23 @@ func subAssign(lv, rv *TypedValue) *Exception { lv.T, )) } - if !ok { - return &Exception{Value: typedString("subtraction overflow")} - } - return nil } // for doOpMul and doOpMulAssign. -func mulAssign(lv, rv *TypedValue) *Exception { +func mulAssign(lv, rv *TypedValue) { // set the result in lv. // NOTE this block is replicated in op_assign.go - ok := true switch baseOf(lv.T) { - // Signed integers may overflow, which triggers a panic. case IntType: - var r int - r, ok = overflow.Mul(lv.GetInt(), rv.GetInt()) - lv.SetInt(r) + lv.SetInt(lv.GetInt() * rv.GetInt()) case Int8Type: - var r int8 - r, ok = overflow.Mul8(lv.GetInt8(), rv.GetInt8()) - lv.SetInt8(r) + lv.SetInt8(lv.GetInt8() * rv.GetInt8()) case Int16Type: - var r int16 - r, ok = overflow.Mul16(lv.GetInt16(), rv.GetInt16()) - lv.SetInt16(r) + lv.SetInt16(lv.GetInt16() * rv.GetInt16()) case Int32Type, UntypedRuneType: - var r int32 - r, ok = overflow.Mul32(lv.GetInt32(), rv.GetInt32()) - lv.SetInt32(r) + lv.SetInt32(lv.GetInt32() * rv.GetInt32()) case Int64Type: - var r int64 - r, ok = overflow.Mul64(lv.GetInt64(), rv.GetInt64()) - lv.SetInt64(r) + lv.SetInt64(lv.GetInt64() * rv.GetInt64()) case UintType: lv.SetUint(lv.GetUint() * rv.GetUint()) case Uint8Type: @@ -903,102 +850,95 @@ func mulAssign(lv, rv *TypedValue) *Exception { lv.T, )) } - if !ok { - return &Exception{Value: typedString("multiplication overflow")} - } - return nil } // for doOpQuo and doOpQuoAssign. func quoAssign(lv, rv *TypedValue) *Exception { + expt := &Exception{ + Value: typedString("division by zero"), + } + // set the result in lv. // NOTE this block is replicated in op_assign.go - ok := true switch baseOf(lv.T) { - // Signed integers may overflow or cause a division by 0, which triggers a panic. case IntType: - var q int - q, _, ok = overflow.Quotient(lv.GetInt(), rv.GetInt()) - lv.SetInt(q) + if rv.GetInt() == 0 { + return expt + } + lv.SetInt(lv.GetInt() / rv.GetInt()) case Int8Type: - var q int8 - q, _, ok = overflow.Quotient8(lv.GetInt8(), rv.GetInt8()) - lv.SetInt8(q) + if rv.GetInt8() == 0 { + return expt + } + lv.SetInt8(lv.GetInt8() / rv.GetInt8()) case Int16Type: - var q int16 - q, _, ok = overflow.Quotient16(lv.GetInt16(), rv.GetInt16()) - lv.SetInt16(q) + if rv.GetInt16() == 0 { + return expt + } + lv.SetInt16(lv.GetInt16() / rv.GetInt16()) case Int32Type, UntypedRuneType: - var q int32 - q, _, ok = overflow.Quotient32(lv.GetInt32(), rv.GetInt32()) - lv.SetInt32(q) + if rv.GetInt32() == 0 { + return expt + } + lv.SetInt32(lv.GetInt32() / rv.GetInt32()) case Int64Type: - var q int64 - q, _, ok = overflow.Quotient64(lv.GetInt64(), rv.GetInt64()) - lv.SetInt64(q) - // Unsigned integers do not cause overflow, but a division by 0 may still occur. + if rv.GetInt64() == 0 { + return expt + } + lv.SetInt64(lv.GetInt64() / rv.GetInt64()) case UintType: - y := rv.GetUint() - ok = y != 0 - if ok { - lv.SetUint(lv.GetUint() / y) + if rv.GetUint() == 0 { + return expt } + lv.SetUint(lv.GetUint() / rv.GetUint()) case Uint8Type: - y := rv.GetUint8() - ok = y != 0 - if ok { - lv.SetUint8(lv.GetUint8() / y) + if rv.GetUint8() == 0 { + return expt } + lv.SetUint8(lv.GetUint8() / rv.GetUint8()) case DataByteType: - y := rv.GetUint8() - ok = y != 0 - if ok { - lv.SetDataByte(lv.GetDataByte() / y) + if rv.GetUint8() == 0 { + return expt } + lv.SetDataByte(lv.GetDataByte() / rv.GetUint8()) case Uint16Type: - y := rv.GetUint16() - ok = y != 0 - if ok { - lv.SetUint16(lv.GetUint16() / y) + if rv.GetUint16() == 0 { + return expt } + lv.SetUint16(lv.GetUint16() / rv.GetUint16()) case Uint32Type: - y := rv.GetUint32() - ok = y != 0 - if ok { - lv.SetUint32(lv.GetUint32() / y) + if rv.GetUint32() == 0 { + return expt } + lv.SetUint32(lv.GetUint32() / rv.GetUint32()) case Uint64Type: - y := rv.GetUint64() - ok = y != 0 - if ok { - lv.SetUint64(lv.GetUint64() / y) + if rv.GetUint64() == 0 { + return expt } - // XXX Handling float overflows is more complex. + lv.SetUint64(lv.GetUint64() / rv.GetUint64()) case Float32Type: // NOTE: gno doesn't fuse *+. - ok = !softfloat.Feq32(rv.GetFloat32(), softfloat.Fintto32(0)) + ok := !softfloat.Feq32(rv.GetFloat32(), softfloat.Fintto32(0)) if ok { lv.SetFloat32(softfloat.Fdiv32(lv.GetFloat32(), rv.GetFloat32())) } case Float64Type: // NOTE: gno doesn't fuse *+. - ok = !softfloat.Feq64(rv.GetFloat64(), softfloat.Fintto64(0)) + ok := !softfloat.Feq64(rv.GetFloat64(), softfloat.Fintto64(0)) if ok { lv.SetFloat64(softfloat.Fdiv64(lv.GetFloat64(), rv.GetFloat64())) } case BigintType, UntypedBigintType: if rv.GetBigInt().Sign() == 0 { - ok = false - break + return expt } lb := lv.GetBigInt() lb = big.NewInt(0).Quo(lb, rv.GetBigInt()) lv.V = BigintValue{V: lb} case BigdecType, UntypedBigdecType: if rv.GetBigDec().Cmp(apd.New(0, 0)) == 0 { - ok = false - break + return expt } lb := lv.GetBigDec() rb := rv.GetBigDec() @@ -1015,83 +955,81 @@ func quoAssign(lv, rv *TypedValue) *Exception { )) } - if !ok { - return &Exception{Value: typedString("division by zero or overflow")} - } return nil } // for doOpRem and doOpRemAssign. func remAssign(lv, rv *TypedValue) *Exception { + expt := &Exception{ + Value: typedString("division by zero"), + } + // set the result in lv. // NOTE this block is replicated in op_assign.go - ok := true switch baseOf(lv.T) { - // Signed integers may overflow or cause a division by 0, which triggers a panic. case IntType: - var r int - _, r, ok = overflow.Quotient(lv.GetInt(), rv.GetInt()) - lv.SetInt(r) + if rv.GetInt() == 0 { + return expt + } + lv.SetInt(lv.GetInt() % rv.GetInt()) case Int8Type: - var r int8 - _, r, ok = overflow.Quotient8(lv.GetInt8(), rv.GetInt8()) - lv.SetInt8(r) + if rv.GetInt8() == 0 { + return expt + } + lv.SetInt8(lv.GetInt8() % rv.GetInt8()) case Int16Type: - var r int16 - _, r, ok = overflow.Quotient16(lv.GetInt16(), rv.GetInt16()) - lv.SetInt16(r) + if rv.GetInt16() == 0 { + return expt + } + lv.SetInt16(lv.GetInt16() % rv.GetInt16()) case Int32Type, UntypedRuneType: - var r int32 - _, r, ok = overflow.Quotient32(lv.GetInt32(), rv.GetInt32()) - lv.SetInt32(r) + if rv.GetInt32() == 0 { + return expt + } + lv.SetInt32(lv.GetInt32() % rv.GetInt32()) case Int64Type: - var r int64 - _, r, ok = overflow.Quotient64(lv.GetInt64(), rv.GetInt64()) - lv.SetInt64(r) - // Unsigned integers do not cause overflow, but a division by 0 may still occur. + if rv.GetInt64() == 0 { + return expt + } + lv.SetInt64(lv.GetInt64() % rv.GetInt64()) case UintType: - y := rv.GetUint() - ok = y != 0 - if ok { - lv.SetUint(lv.GetUint() % y) + if rv.GetUint() == 0 { + return expt } + lv.SetUint(lv.GetUint() % rv.GetUint()) case Uint8Type: - y := rv.GetUint8() - ok = y != 0 - if ok { - lv.SetUint8(lv.GetUint8() % y) + if rv.GetUint8() == 0 { + return expt } + lv.SetUint8(lv.GetUint8() % rv.GetUint8()) case DataByteType: - y := rv.GetUint8() - ok = y != 0 - if ok { - lv.SetDataByte(lv.GetDataByte() % y) + if rv.GetUint8() == 0 { + return expt } + lv.SetDataByte(lv.GetDataByte() % rv.GetUint8()) case Uint16Type: - y := rv.GetUint16() - ok = y != 0 - if ok { - lv.SetUint16(lv.GetUint16() % y) + if rv.GetUint16() == 0 { + return expt } + lv.SetUint16(lv.GetUint16() % rv.GetUint16()) case Uint32Type: - y := rv.GetUint32() - ok = y != 0 - if ok { - lv.SetUint32(lv.GetUint32() % y) + if rv.GetUint32() == 0 { + return expt } + lv.SetUint32(lv.GetUint32() % rv.GetUint32()) case Uint64Type: - y := rv.GetUint64() - ok = y != 0 - if ok { - lv.SetUint64(lv.GetUint64() % y) + if rv.GetUint64() == 0 { + return expt } + lv.SetUint64(lv.GetUint64() % rv.GetUint64()) case BigintType, UntypedBigintType: - ok = rv.GetBigInt().Sign() != 0 - if ok { - lb := lv.GetBigInt() - lb = big.NewInt(0).Rem(lb, rv.GetBigInt()) - lv.V = BigintValue{V: lb} + if rv.GetBigInt().Sign() == 0 { + return expt } + + lb := lv.GetBigInt() + lb = big.NewInt(0).Rem(lb, rv.GetBigInt()) + lv.V = BigintValue{V: lb} default: panic(fmt.Sprintf( "operators %% and %%= not defined for %s", @@ -1099,9 +1037,6 @@ func remAssign(lv, rv *TypedValue) *Exception { )) } - if !ok { - return &Exception{Value: typedString("division by zero or overflow")} - } return nil } diff --git a/gnovm/pkg/gnolang/op_inc_dec.go b/gnovm/pkg/gnolang/op_inc_dec.go index c67a4be6ed5..708aae821ac 100644 --- a/gnovm/pkg/gnolang/op_inc_dec.go +++ b/gnovm/pkg/gnolang/op_inc_dec.go @@ -6,7 +6,6 @@ import ( "github.com/cockroachdb/apd/v3" "github.com/gnolang/gno/gnovm/pkg/gnolang/internal/softfloat" - "github.com/gnolang/gno/tm2/pkg/overflow" ) func (m *Machine) doOpInc() { @@ -33,18 +32,16 @@ func (m *Machine) doOpInc() { // because it could be a type alias // type num int switch baseOf(lv.T) { - // Signed integers may overflow, which triggers a panic. case IntType: - lv.SetInt(overflow.Addp(lv.GetInt(), 1)) + lv.SetInt(lv.GetInt() + 1) case Int8Type: - lv.SetInt8(overflow.Add8p(lv.GetInt8(), 1)) + lv.SetInt8(lv.GetInt8() + 1) case Int16Type: - lv.SetInt16(overflow.Add16p(lv.GetInt16(), 1)) + lv.SetInt16(lv.GetInt16() + 1) case Int32Type: - lv.SetInt32(overflow.Add32p(lv.GetInt32(), 1)) + lv.SetInt32(lv.GetInt32() + 1) case Int64Type: - lv.SetInt64(overflow.Add64p(lv.GetInt64(), 1)) - // Unsigned integers do not overflow, they just wrap. + lv.SetInt64(lv.GetInt64() + 1) case UintType: lv.SetUint(lv.GetUint() + 1) case Uint8Type: @@ -105,18 +102,16 @@ func (m *Machine) doOpDec() { } } switch baseOf(lv.T) { - // Signed integers may overflow, which triggers a panic. case IntType: - lv.SetInt(overflow.Subp(lv.GetInt(), 1)) + lv.SetInt(lv.GetInt() - 1) case Int8Type: - lv.SetInt8(overflow.Sub8p(lv.GetInt8(), 1)) + lv.SetInt8(lv.GetInt8() - 1) case Int16Type: - lv.SetInt16(overflow.Sub16p(lv.GetInt16(), 1)) + lv.SetInt16(lv.GetInt16() - 1) case Int32Type: - lv.SetInt32(overflow.Sub32p(lv.GetInt32(), 1)) + lv.SetInt32(lv.GetInt32() - 1) case Int64Type: - lv.SetInt64(overflow.Sub64p(lv.GetInt64(), 1)) - // Unsigned integers do not overflow, they just wrap. + lv.SetInt64(lv.GetInt64() - 1) case UintType: lv.SetUint(lv.GetUint() - 1) case Uint8Type: diff --git a/gnovm/stdlibs/generated.go b/gnovm/stdlibs/generated.go index 6e757561ef2..ab35fc6b6bf 100644 --- a/gnovm/stdlibs/generated.go +++ b/gnovm/stdlibs/generated.go @@ -896,6 +896,7 @@ var initOrder = [...]string{ "hash", "hash/adler32", "html", + "math/overflow", "math/rand", "path", "sort", diff --git a/gnovm/stdlibs/math/const_test.gno b/gnovm/stdlibs/math/const_test.gno index fbe59d61878..b892a12898b 100644 --- a/gnovm/stdlibs/math/const_test.gno +++ b/gnovm/stdlibs/math/const_test.gno @@ -31,76 +31,19 @@ func TestMaxUint(t *testing.T) { } func TestMaxInt(t *testing.T) { - defer func() { - if r := recover(); r != nil { - if r != "addition overflow" { - panic(r) - } - } - }() - v := int(math.MaxInt) - if v+1 == math.MinInt { - t.Errorf("int should overflow") + if v := int(math.MaxInt); v+1 != math.MinInt { + t.Errorf("MaxInt should wrap around to MinInt: %d", v+1) } - t.Errorf("expected panic did not occur") -} - -func TestMaxInt8(t *testing.T) { - defer func() { - if r := recover(); r != nil { - if r != "addition overflow" { - panic(r) - } - } - }() - v := int8(math.MaxInt8) - if v+1 == math.MinInt8 { - t.Errorf("int8 should overflow") + if v := int8(math.MaxInt8); v+1 != math.MinInt8 { + t.Errorf("MaxInt8 should wrap around to MinInt8: %d", v+1) } - t.Errorf("expected panic did not occur") -} - -func TestMaxInt16(t *testing.T) { - defer func() { - if r := recover(); r != nil { - if r != "addition overflow" { - panic(r) - } - } - }() - v := int16(math.MaxInt16) - if v+1 == math.MinInt16 { - t.Errorf("int16 should overflow") + if v := int16(math.MaxInt16); v+1 != math.MinInt16 { + t.Errorf("MaxInt16 should wrap around to MinInt16: %d", v+1) } - t.Errorf("expected panic did not occur") -} - -func TestMaxInt32(t *testing.T) { - defer func() { - if r := recover(); r != nil { - if r != "addition overflow" { - panic(r) - } - } - }() - v := int32(math.MaxInt32) - if v+1 == math.MinInt32 { - t.Errorf("int32 should overflow") + if v := int32(math.MaxInt32); v+1 != math.MinInt32 { + t.Errorf("MaxInt32 should wrap around to MinInt32: %d", v+1) } - t.Errorf("expected panic did not occur") -} - -func TestMaxInt64(t *testing.T) { - defer func() { - if r := recover(); r != nil { - if r != "addition overflow" { - panic(r) - } - } - }() - v := int64(math.MaxInt64) - if v+1 == math.MinInt64 { - t.Errorf("int64 should overflow") + if v := int64(math.MaxInt64); v+1 != math.MinInt64 { + t.Errorf("MaxInt64 should wrap around to MinInt64: %d", v+1) } - t.Errorf("expected panic did not occur") } diff --git a/gnovm/stdlibs/math/overflow/overflow.gno b/gnovm/stdlibs/math/overflow/overflow.gno new file mode 100644 index 00000000000..0bc2e03a522 --- /dev/null +++ b/gnovm/stdlibs/math/overflow/overflow.gno @@ -0,0 +1,501 @@ +// This is modified from https://github.com/JohnCGriffin/overflow (MIT). +// NOTE: there was a bug with the original Quotient* functions, and +// testing method. These have been fixed here, and tests ported to +// tests/files/maths_int*.go respectively. +// Note: moved over from p/demo/maths. + +/* +Package overflow offers overflow-checked integer arithmetic operations +for int, int32, and int64. Each of the operations returns a +result,bool combination. This was prompted by the need to know when +to flow into higher precision types from the math.big library. + +For instance, assuing a 64 bit machine: + +10 + 20 -> 30 +int(math.MaxInt64) + 1 -> -9223372036854775808 + +whereas + +overflow.Add(10,20) -> (30, true) +overflow.Add(math.MaxInt64,1) -> (0, false) + +Add, Sub, Mul, Div are for int. Add64, Add32, etc. are specifically sized. + +If anybody wishes an unsigned version, submit a pull request for code +and new tests. +*/ +package overflow + +import "math" + +//go:generate ./overflow_template.sh + +func _is64Bit() bool { + maxU32 := uint(math.MaxUint32) + return ((maxU32 << 1) >> 1) == maxU32 +} + +/********** PARTIAL TEST COVERAGE FROM HERE DOWN ************* + +The only way that I could see to do this is a combination of +my normal 64 bit system and a GopherJS running on Node. My +understanding is that its ints are 32 bit. + +So, FEEL FREE to carefully review the code visually. + +*************************************************************/ + +// Unspecified size, i.e. normal signed int + +// Add sums two ints, returning the result and a boolean status. +func Add(a, b int) (int, bool) { + if _is64Bit() { + r64, ok := Add64(int64(a), int64(b)) + return int(r64), ok + } + r32, ok := Add32(int32(a), int32(b)) + return int(r32), ok +} + +// Sub returns the difference of two ints and a boolean status. +func Sub(a, b int) (int, bool) { + if _is64Bit() { + r64, ok := Sub64(int64(a), int64(b)) + return int(r64), ok + } + r32, ok := Sub32(int32(a), int32(b)) + return int(r32), ok +} + +// Mul returns the product of two ints and a boolean status. +func Mul(a, b int) (int, bool) { + if _is64Bit() { + r64, ok := Mul64(int64(a), int64(b)) + return int(r64), ok + } + r32, ok := Mul32(int32(a), int32(b)) + return int(r32), ok +} + +// Div returns the quotient of two ints and a boolean status +func Div(a, b int) (int, bool) { + if _is64Bit() { + r64, ok := Div64(int64(a), int64(b)) + return int(r64), ok + } + r32, ok := Div32(int32(a), int32(b)) + return int(r32), ok +} + +// Quo returns the quotient, remainder and status of two ints +func Quo(a, b int) (int, int, bool) { + if _is64Bit() { + q64, r64, ok := Quo64(int64(a), int64(b)) + return int(q64), int(r64), ok + } + q32, r32, ok := Quo32(int32(a), int32(b)) + return int(q32), int(r32), ok +} + +/************* Panic versions for int ****************/ + +// Addp returns the sum of two ints, panicking on overflow +func Addp(a, b int) int { + r, ok := Add(a, b) + if !ok { + panic("addition overflow") + } + return r +} + +// Subp returns the difference of two ints, panicking on overflow. +func Subp(a, b int) int { + r, ok := Sub(a, b) + if !ok { + panic("subtraction overflow") + } + return r +} + +// Mulp returns the product of two ints, panicking on overflow. +func Mulp(a, b int) int { + r, ok := Mul(a, b) + if !ok { + panic("multiplication overflow") + } + return r +} + +// Divp returns the quotient of two ints, panicking on overflow. +func Divp(a, b int) int { + r, ok := Div(a, b) + if !ok { + panic("division failure") + } + return r +} + +//---------------------------------------- +// This is generated code, created by overflow_template.sh executed +// by "go generate" + +// Add8 performs + operation on two int8 operands +// returning a result and status +func Add8(a, b int8) (int8, bool) { + c := a + b + if (c > a) == (b > 0) { + return c, true + } + return c, false +} + +// Add8p is the unchecked panicking version of Add8 +func Add8p(a, b int8) int8 { + r, ok := Add8(a, b) + if !ok { + panic("addition overflow") + } + return r +} + +// Sub8 performs - operation on two int8 operands +// returning a result and status +func Sub8(a, b int8) (int8, bool) { + c := a - b + if (c < a) == (b > 0) { + return c, true + } + return c, false +} + +// Sub8p is the unchecked panicking version of Sub8 +func Sub8p(a, b int8) int8 { + r, ok := Sub8(a, b) + if !ok { + panic("subtraction overflow") + } + return r +} + +// Mul8 performs * operation on two int8 operands +// returning a result and status +func Mul8(a, b int8) (int8, bool) { + if a == 0 || b == 0 { + return 0, true + } + c := a * b + if (c < 0) == ((a < 0) != (b < 0)) { + if c/b == a { + return c, true + } + } + return c, false +} + +// Mul8p is the unchecked panicking version of Mul8 +func Mul8p(a, b int8) int8 { + r, ok := Mul8(a, b) + if !ok { + panic("multiplication overflow") + } + return r +} + +// Div8 performs / operation on two int8 operands +// returning a result and status +func Div8(a, b int8) (int8, bool) { + q, _, ok := Quo8(a, b) + return q, ok +} + +// Div8p is the unchecked panicking version of Div8 +func Div8p(a, b int8) int8 { + r, ok := Div8(a, b) + if !ok { + panic("division failure") + } + return r +} + +// Quo8 performs + operation on two int8 operands +// returning a quotient, a remainder and status +func Quo8(a, b int8) (int8, int8, bool) { + if b == 0 { + return 0, 0, false + } else if b == -1 && a == int8(math.MinInt8) { + return 0, 0, false + } + c := a / b + return c, a % b, true +} + +// Add16 performs + operation on two int16 operands +// returning a result and status +func Add16(a, b int16) (int16, bool) { + c := a + b + if (c > a) == (b > 0) { + return c, true + } + return c, false +} + +// Add16p is the unchecked panicking version of Add16 +func Add16p(a, b int16) int16 { + r, ok := Add16(a, b) + if !ok { + panic("addition overflow") + } + return r +} + +// Sub16 performs - operation on two int16 operands +// returning a result and status +func Sub16(a, b int16) (int16, bool) { + c := a - b + if (c < a) == (b > 0) { + return c, true + } + return c, false +} + +// Sub16p is the unchecked panicking version of Sub16 +func Sub16p(a, b int16) int16 { + r, ok := Sub16(a, b) + if !ok { + panic("subtraction overflow") + } + return r +} + +// Mul16 performs * operation on two int16 operands +// returning a result and status +func Mul16(a, b int16) (int16, bool) { + if a == 0 || b == 0 { + return 0, true + } + c := a * b + if (c < 0) == ((a < 0) != (b < 0)) { + if c/b == a { + return c, true + } + } + return c, false +} + +// Mul16p is the unchecked panicking version of Mul16 +func Mul16p(a, b int16) int16 { + r, ok := Mul16(a, b) + if !ok { + panic("multiplication overflow") + } + return r +} + +// Div16 performs / operation on two int16 operands +// returning a result and status +func Div16(a, b int16) (int16, bool) { + q, _, ok := Quo16(a, b) + return q, ok +} + +// Div16p is the unchecked panicking version of Div16 +func Div16p(a, b int16) int16 { + r, ok := Div16(a, b) + if !ok { + panic("division failure") + } + return r +} + +// Quo16 performs + operation on two int16 operands +// returning a quotient, a remainder and status +func Quo16(a, b int16) (int16, int16, bool) { + if b == 0 { + return 0, 0, false + } else if b == -1 && a == int16(math.MinInt16) { + return 0, 0, false + } + c := a / b + return c, a % b, true +} + +// Add32 performs + operation on two int32 operands +// returning a result and status +func Add32(a, b int32) (int32, bool) { + c := a + b + if (c > a) == (b > 0) { + return c, true + } + return c, false +} + +// Add32p is the unchecked panicking version of Add32 +func Add32p(a, b int32) int32 { + r, ok := Add32(a, b) + if !ok { + panic("addition overflow") + } + return r +} + +// Sub32 performs - operation on two int32 operands +// returning a result and status +func Sub32(a, b int32) (int32, bool) { + c := a - b + if (c < a) == (b > 0) { + return c, true + } + return c, false +} + +// Sub32p is the unchecked panicking version of Sub32 +func Sub32p(a, b int32) int32 { + r, ok := Sub32(a, b) + if !ok { + panic("subtraction overflow") + } + return r +} + +// Mul32 performs * operation on two int32 operands +// returning a result and status +func Mul32(a, b int32) (int32, bool) { + if a == 0 || b == 0 { + return 0, true + } + c := a * b + if (c < 0) == ((a < 0) != (b < 0)) { + if c/b == a { + return c, true + } + } + return c, false +} + +// Mul32p is the unchecked panicking version of Mul32 +func Mul32p(a, b int32) int32 { + r, ok := Mul32(a, b) + if !ok { + panic("multiplication overflow") + } + return r +} + +// Div32 performs / operation on two int32 operands +// returning a result and status +func Div32(a, b int32) (int32, bool) { + q, _, ok := Quo32(a, b) + return q, ok +} + +// Div32p is the unchecked panicking version of Div32 +func Div32p(a, b int32) int32 { + r, ok := Div32(a, b) + if !ok { + panic("division failure") + } + return r +} + +// Quo32 performs + operation on two int32 operands +// returning a quotient, a remainder and status +func Quo32(a, b int32) (int32, int32, bool) { + if b == 0 { + return 0, 0, false + } else if b == -1 && a == int32(math.MinInt32) { + return 0, 0, false + } + c := a / b + return c, a % b, true +} + +// Add64 performs + operation on two int64 operands +// returning a result and status +func Add64(a, b int64) (int64, bool) { + c := a + b + if (c > a) == (b > 0) { + return c, true + } + return c, false +} + +// Add64p is the unchecked panicking version of Add64 +func Add64p(a, b int64) int64 { + r, ok := Add64(a, b) + if !ok { + panic("addition overflow") + } + return r +} + +// Sub64 performs - operation on two int64 operands +// returning a result and status +func Sub64(a, b int64) (int64, bool) { + c := a - b + if (c < a) == (b > 0) { + return c, true + } + return c, false +} + +// Sub64p is the unchecked panicking version of Sub64 +func Sub64p(a, b int64) int64 { + r, ok := Sub64(a, b) + if !ok { + panic("subtraction overflow") + } + return r +} + +// Mul64 performs * operation on two int64 operands +// returning a result and status +func Mul64(a, b int64) (int64, bool) { + if a == 0 || b == 0 { + return 0, true + } + c := a * b + if (c < 0) == ((a < 0) != (b < 0)) { + if c/b == a { + return c, true + } + } + return c, false +} + +// Mul64p is the unchecked panicking version of Mul64 +func Mul64p(a, b int64) int64 { + r, ok := Mul64(a, b) + if !ok { + panic("multiplication overflow") + } + return r +} + +// Div64 performs / operation on two int64 operands +// returning a result and status +func Div64(a, b int64) (int64, bool) { + q, _, ok := Quo64(a, b) + return q, ok +} + +// Div64p is the unchecked panicking version of Div64 +func Div64p(a, b int64) int64 { + r, ok := Div64(a, b) + if !ok { + panic("division failure") + } + return r +} + +// Quo64 performs + operation on two int64 operands +// returning a quotient, a remainder and status +func Quo64(a, b int64) (int64, int64, bool) { + if b == 0 { + return 0, 0, false + } else if b == -1 && a == math.MinInt64 { + return 0, 0, false + } + c := a / b + return c, a % b, true +} diff --git a/gnovm/stdlibs/math/overflow/overflow_test.gno b/gnovm/stdlibs/math/overflow/overflow_test.gno new file mode 100644 index 00000000000..b7881aec480 --- /dev/null +++ b/gnovm/stdlibs/math/overflow/overflow_test.gno @@ -0,0 +1,200 @@ +package overflow + +import ( + "math" + "testing" +) + +// sample all possibilities of 8 bit numbers +// by checking against 64 bit numbers + +func TestAlgorithms(t *testing.T) { + errors := 0 + + for a64 := int64(math.MinInt8); a64 <= int64(math.MaxInt8); a64++ { + for b64 := int64(math.MinInt8); b64 <= int64(math.MaxInt8) && errors < 10; b64++ { + + a8 := int8(a64) + b8 := int8(b64) + + if int64(a8) != a64 || int64(b8) != b64 { + t.Fatal("LOGIC FAILURE IN TEST") + } + + // ADDITION + { + r64 := a64 + b64 + + // now the verification + result, ok := Add8(a8, b8) + if ok && int64(result) != r64 { + t.Errorf("failed to fail on %v + %v = %v instead of %v\n", + a8, b8, result, r64) + errors++ + } + if !ok && int64(result) == r64 { + t.Fail() + errors++ + } + } + + // SUBTRACTION + { + r64 := a64 - b64 + + // now the verification + result, ok := Sub8(a8, b8) + if ok && int64(result) != r64 { + t.Errorf("failed to fail on %v - %v = %v instead of %v\n", + a8, b8, result, r64) + } + if !ok && int64(result) == r64 { + t.Fail() + errors++ + } + } + + // MULTIPLICATION + { + r64 := a64 * b64 + + // now the verification + result, ok := Mul8(a8, b8) + if ok && int64(result) != r64 { + t.Errorf("failed to fail on %v * %v = %v instead of %v\n", + a8, b8, result, r64) + errors++ + } + if !ok && int64(result) == r64 { + t.Fail() + errors++ + } + } + + // DIVISION + if b8 != 0 { + r64 := a64 / b64 + rem64 := a64 % b64 + + // now the verification + result, rem, ok := Quo8(a8, b8) + if ok && int64(result) != r64 { + t.Errorf("failed to fail on %v / %v = %v instead of %v\n", + a8, b8, result, r64) + errors++ + } + if ok && int64(rem) != rem64 { + t.Errorf("failed to fail on %v %% %v = %v instead of %v\n", + a8, b8, rem, rem64) + errors++ + } + } + } + } +} + +func TestQuotient(t *testing.T) { + q, r, ok := Quo(100, 3) + if r != 1 || q != 33 || !ok { + t.Errorf("expected 100/3 => 33, r=1") + } + if _, _, ok = Quo(1, 0); ok { + t.Error("unexpected lack of failure") + } +} + +func TestLong(t *testing.T) { + if testing.Short() { + t.Skip() + } + + ctr := int64(0) + + for a64 := int64(math.MinInt16); a64 <= int64(math.MaxInt16); a64++ { + for b64 := int64(math.MinInt16); b64 <= int64(math.MaxInt16); b64++ { + a16 := int16(a64) + b16 := int16(b64) + if int64(a16) != a64 || int64(b16) != b64 { + panic("LOGIC FAILURE IN TEST") + } + ctr++ + + // ADDITION + { + r64 := a64 + b64 + + // now the verification + result, ok := Add16(a16, b16) + if int64(math.MinInt16) <= r64 && r64 <= int64(math.MaxInt16) { + if !ok || int64(result) != r64 { + println("add", a16, b16, result, r64) + panic("incorrect result for non-overflow") + } + } else { + if ok { + println("add", a16, b16, result, r64) + panic("incorrect ok result") + } + } + } + + // SUBTRACTION + { + r64 := a64 - b64 + + // now the verification + result, ok := Sub16(a16, b16) + if int64(math.MinInt16) <= r64 && r64 <= int64(math.MaxInt16) { + if !ok || int64(result) != r64 { + println("sub", a16, b16, result, r64) + panic("incorrect result for non-overflow") + } + } else { + if ok { + println("sub", a16, b16, result, r64) + panic("incorrect ok result") + } + } + } + + // MULTIPLICATION + { + r64 := a64 * b64 + + // now the verification + result, ok := Mul16(a16, b16) + if int64(math.MinInt16) <= r64 && r64 <= int64(math.MaxInt16) { + if !ok || int64(result) != r64 { + println("mul", a16, b16, result, r64) + panic("incorrect result for non-overflow") + } + } else { + if ok { + println("mul", a16, b16, result, r64) + panic("incorrect ok result") + } + } + } + + // DIVISION + if b16 != 0 { + r64 := a64 / b64 + + // now the verification + result, _, ok := Quo16(a16, b16) + if int64(math.MinInt16) <= r64 && r64 <= int64(math.MaxInt16) { + if !ok || int64(result) != r64 { + println("quo", a16, b16, result, r64) + panic("incorrect result for non-overflow") + } + } else { + if ok { + println("quo", a16, b16, result, r64) + panic("incorrect ok result") + } + } + } + } + } + println("done", ctr) +} diff --git a/gnovm/stdlibs/std/coins.gno b/gnovm/stdlibs/std/coins.gno index 679674e443e..47e88e238d2 100644 --- a/gnovm/stdlibs/std/coins.gno +++ b/gnovm/stdlibs/std/coins.gno @@ -1,6 +1,9 @@ package std -import "strconv" +import ( + "math/overflow" + "strconv" +) // NOTE: this is selectively copied over from tm2/pkgs/std/coin.go @@ -53,7 +56,13 @@ func (c Coin) IsEqual(other Coin) bool { // An invalid result panics. func (c Coin) Add(other Coin) Coin { mustMatchDenominations(c.Denom, other.Denom) - c.Amount += other.Amount + + sum, ok := overflow.Add64(c.Amount, other.Amount) + if !ok { + panic("coin add overflow/underflow: " + strconv.Itoa(int(c.Amount)) + " +/- " + strconv.Itoa(int(other.Amount))) + } + + c.Amount = sum return c } @@ -63,7 +72,13 @@ func (c Coin) Add(other Coin) Coin { // An invalid result panics. func (c Coin) Sub(other Coin) Coin { mustMatchDenominations(c.Denom, other.Denom) - c.Amount -= other.Amount + + dff, ok := overflow.Sub64(c.Amount, other.Amount) + if !ok { + panic("coin sub overflow/underflow: " + strconv.Itoa(int(c.Amount)) + " +/- " + strconv.Itoa(int(other.Amount))) + } + c.Amount = dff + return c } @@ -98,7 +113,10 @@ func NewCoins(coins ...Coin) Coins { for _, coin := range coins { if currentAmount, exists := coinMap[coin.Denom]; exists { - coinMap[coin.Denom] = currentAmount + coin.Amount + var ok bool + if coinMap[coin.Denom], ok = overflow.Add64(currentAmount, coin.Amount); !ok { + panic("coin sub overflow/underflow: " + strconv.Itoa(int(currentAmount)) + " +/- " + strconv.Itoa(int(coin.Amount))) + } } else { coinMap[coin.Denom] = coin.Amount } diff --git a/gnovm/tests/files/overflow0.gno b/gnovm/tests/files/overflow0.gno deleted file mode 100644 index 1313f064322..00000000000 --- a/gnovm/tests/files/overflow0.gno +++ /dev/null @@ -1,10 +0,0 @@ -package main - -func main() { - var a, b, c int8 = -1<<7, -1, 0 - c = a / b // overflow: -128 instead of 128 - println(c) -} - -// Error: -// division by zero or overflow diff --git a/gnovm/tests/files/overflow1.gno b/gnovm/tests/files/overflow1.gno deleted file mode 100644 index a416e9a3498..00000000000 --- a/gnovm/tests/files/overflow1.gno +++ /dev/null @@ -1,10 +0,0 @@ -package main - -func main() { - var a, b, c int16 = -1<<15, -1, 0 - c = a / b // overflow: -32768 instead of 32768 - println(c) -} - -// Error: -// division by zero or overflow diff --git a/gnovm/tests/files/overflow2.gno b/gnovm/tests/files/overflow2.gno deleted file mode 100644 index 353729bcdf2..00000000000 --- a/gnovm/tests/files/overflow2.gno +++ /dev/null @@ -1,10 +0,0 @@ -package main - -func main() { - var a, b, c int32 = -1<<31, -1, 0 - c = a / b // overflow: -2147483648 instead of 2147483648 - println(c) -} - -// Error: -// division by zero or overflow diff --git a/gnovm/tests/files/overflow3.gno b/gnovm/tests/files/overflow3.gno deleted file mode 100644 index a09c59dfb03..00000000000 --- a/gnovm/tests/files/overflow3.gno +++ /dev/null @@ -1,10 +0,0 @@ -package main - -func main() { - var a, b, c int64 = -1<<63, -1, 0 - c = a / b // overflow: -9223372036854775808 instead of 9223372036854775808 - println(c) -} - -// Error: -// division by zero or overflow diff --git a/gnovm/tests/files/overflow4.gno b/gnovm/tests/files/overflow4.gno deleted file mode 100644 index 26b05567b07..00000000000 --- a/gnovm/tests/files/overflow4.gno +++ /dev/null @@ -1,10 +0,0 @@ -package main - -func main() { - var a, b, c int = -1<<63, -1, 0 - c = a / b // overflow: -9223372036854775808 instead of 9223372036854775808 - println(c) -} - -// Error: -// division by zero or overflow diff --git a/gnovm/tests/files/overflow5.gno b/gnovm/tests/files/overflow5.gno deleted file mode 100644 index ef7f976eb24..00000000000 --- a/gnovm/tests/files/overflow5.gno +++ /dev/null @@ -1,10 +0,0 @@ -package main - -func main() { - var a, b, c int = -5, 7, 0 - c = a % b // 0 quotient triggers a false negative in gnolang/overflow - println(c) -} - -// Output: -// -5 diff --git a/gnovm/tests/files/recover14.gno b/gnovm/tests/files/recover14.gno index 3c96404fcbe..30a34ab291a 100644 --- a/gnovm/tests/files/recover14.gno +++ b/gnovm/tests/files/recover14.gno @@ -12,4 +12,4 @@ func main() { } // Output: -// recover: division by zero or overflow +// recover: division by zero diff --git a/misc/genstd/util.go b/misc/genstd/util.go index 13e90836f36..025fe4b673e 100644 --- a/misc/genstd/util.go +++ b/misc/genstd/util.go @@ -70,8 +70,7 @@ func findDirs() (gitRoot string, relPath string, err error) { } p := wd for { - // .git is normally a directory, or a file in case of a git worktree. - if _, e := os.Stat(filepath.Join(p, ".git")); e == nil { + if s, e := os.Stat(filepath.Join(p, ".git")); e == nil && s.IsDir() { // make relPath relative to the git root rp := strings.TrimPrefix(wd, p+string(filepath.Separator)) // normalize separator to / From 6909efaac0d4211c3ce894e052fd0d7b90112809 Mon Sep 17 00:00:00 2001 From: Morgan Date: Tue, 14 Jan 2025 17:23:39 +0100 Subject: [PATCH 07/18] refactor(gnovm): move go type checking to own file (#3491) --- gnovm/pkg/gnolang/go2gno.go | 173 ------------- gnovm/pkg/gnolang/go2gno_test.go | 352 ------------------------- gnovm/pkg/gnolang/gotypecheck.go | 179 +++++++++++++ gnovm/pkg/gnolang/gotypecheck_test.go | 359 ++++++++++++++++++++++++++ 4 files changed, 538 insertions(+), 525 deletions(-) create mode 100644 gnovm/pkg/gnolang/gotypecheck.go create mode 100644 gnovm/pkg/gnolang/gotypecheck_test.go diff --git a/gnovm/pkg/gnolang/go2gno.go b/gnovm/pkg/gnolang/go2gno.go index 82d5c69b08b..4c9de87a6a7 100644 --- a/gnovm/pkg/gnolang/go2gno.go +++ b/gnovm/pkg/gnolang/go2gno.go @@ -31,24 +31,16 @@ package gnolang */ import ( - "bytes" "fmt" "go/ast" - "go/format" "go/parser" "go/token" - "go/types" "os" - "path" "reflect" - "slices" "strconv" - "strings" "github.com/davecgh/go-spew/spew" - "github.com/gnolang/gno/gnovm" "github.com/gnolang/gno/tm2/pkg/errors" - "go.uber.org/multierr" ) func MustReadFile(path string) *FileNode { @@ -483,171 +475,6 @@ func Go2Gno(fs *token.FileSet, gon ast.Node) (n Node) { } } -//---------------------------------------- -// type checking (using go/types) -// XXX move to gotypecheck.go. - -// MemPackageGetter implements the GetMemPackage() method. It is a subset of -// [Store], separated for ease of testing. -type MemPackageGetter interface { - GetMemPackage(path string) *gnovm.MemPackage -} - -// TypeCheckMemPackage performs type validation and checking on the given -// mempkg. To retrieve dependencies, it uses getter. -// -// The syntax checking is performed entirely using Go's go/types package. -// -// If format is true, the code will be automatically updated with the -// formatted source code. -func TypeCheckMemPackage(mempkg *gnovm.MemPackage, getter MemPackageGetter, format bool) error { - return typeCheckMemPackage(mempkg, getter, false, format) -} - -// TypeCheckMemPackageTest performs the same type checks as [TypeCheckMemPackage], -// but allows re-declarations. -// -// Note: like TypeCheckMemPackage, this function ignores tests and filetests. -func TypeCheckMemPackageTest(mempkg *gnovm.MemPackage, getter MemPackageGetter) error { - return typeCheckMemPackage(mempkg, getter, true, false) -} - -func typeCheckMemPackage(mempkg *gnovm.MemPackage, getter MemPackageGetter, testing, format bool) error { - var errs error - imp := &gnoImporter{ - getter: getter, - cache: map[string]gnoImporterResult{}, - cfg: &types.Config{ - Error: func(err error) { - errs = multierr.Append(errs, err) - }, - }, - allowRedefinitions: testing, - } - imp.cfg.Importer = imp - - _, err := imp.parseCheckMemPackage(mempkg, format) - // prefer to return errs instead of err: - // err will generally contain only the first error encountered. - if errs != nil { - return errs - } - return err -} - -type gnoImporterResult struct { - pkg *types.Package - err error -} - -type gnoImporter struct { - getter MemPackageGetter - cache map[string]gnoImporterResult - cfg *types.Config - - // allow symbol redefinitions? (test standard libraries) - allowRedefinitions bool -} - -// Unused, but satisfies the Importer interface. -func (g *gnoImporter) Import(path string) (*types.Package, error) { - return g.ImportFrom(path, "", 0) -} - -type importNotFoundError string - -func (e importNotFoundError) Error() string { return "import not found: " + string(e) } - -// ImportFrom returns the imported package for the given import -// path when imported by a package file located in dir. -func (g *gnoImporter) ImportFrom(path, _ string, _ types.ImportMode) (*types.Package, error) { - if pkg, ok := g.cache[path]; ok { - return pkg.pkg, pkg.err - } - mpkg := g.getter.GetMemPackage(path) - if mpkg == nil { - err := importNotFoundError(path) - g.cache[path] = gnoImporterResult{err: err} - return nil, err - } - fmt := false - result, err := g.parseCheckMemPackage(mpkg, fmt) - g.cache[path] = gnoImporterResult{pkg: result, err: err} - return result, err -} - -func (g *gnoImporter) parseCheckMemPackage(mpkg *gnovm.MemPackage, fmt bool) (*types.Package, error) { - // This map is used to allow for function re-definitions, which are allowed - // in Gno (testing context) but not in Go. - // This map links each function identifier with a closure to remove its - // associated declaration. - var delFunc map[string]func() - if g.allowRedefinitions { - delFunc = make(map[string]func()) - } - - fset := token.NewFileSet() - files := make([]*ast.File, 0, len(mpkg.Files)) - var errs error - for _, file := range mpkg.Files { - // Ignore non-gno files. - // TODO: support filetest type checking. (should probably handle as each its - // own separate pkg, which should also be typechecked) - if !strings.HasSuffix(file.Name, ".gno") || - strings.HasSuffix(file.Name, "_test.gno") || - strings.HasSuffix(file.Name, "_filetest.gno") { - continue - } - - const parseOpts = parser.ParseComments | parser.DeclarationErrors | parser.SkipObjectResolution - f, err := parser.ParseFile(fset, path.Join(mpkg.Path, file.Name), file.Body, parseOpts) - if err != nil { - errs = multierr.Append(errs, err) - continue - } - - if delFunc != nil { - deleteOldIdents(delFunc, f) - } - - // enforce formatting - if fmt { - var buf bytes.Buffer - err = format.Node(&buf, fset, f) - if err != nil { - errs = multierr.Append(errs, err) - continue - } - file.Body = buf.String() - } - - files = append(files, f) - } - if errs != nil { - return nil, errs - } - - return g.cfg.Check(mpkg.Path, fset, files, nil) -} - -func deleteOldIdents(idents map[string]func(), f *ast.File) { - for _, decl := range f.Decls { - fd, ok := decl.(*ast.FuncDecl) - if !ok || fd.Recv != nil { // ignore methods - continue - } - if del := idents[fd.Name.Name]; del != nil { - del() - } - decl := decl - idents[fd.Name.Name] = func() { - // NOTE: cannot use the index as a file may contain multiple decls to be removed, - // so removing one would make all "later" indexes wrong. - f.Decls = slices.DeleteFunc(f.Decls, func(d ast.Decl) bool { return decl == d }) - } - } -} - //---------------------------------------- // utility methods diff --git a/gnovm/pkg/gnolang/go2gno_test.go b/gnovm/pkg/gnolang/go2gno_test.go index 8aba5d7f293..920c5acd9c8 100644 --- a/gnovm/pkg/gnolang/go2gno_test.go +++ b/gnovm/pkg/gnolang/go2gno_test.go @@ -4,10 +4,7 @@ import ( "fmt" "testing" - "github.com/gnolang/gno/gnovm" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.uber.org/multierr" ) func TestParseForLoop(t *testing.T) { @@ -28,352 +25,3 @@ func main(){ fmt.Printf("AST:\n%#v\n\n", n) fmt.Printf("AST.String():\n%s\n", n.String()) } - -type mockPackageGetter []*gnovm.MemPackage - -func (mi mockPackageGetter) GetMemPackage(path string) *gnovm.MemPackage { - for _, pkg := range mi { - if pkg.Path == path { - return pkg - } - } - return nil -} - -type mockPackageGetterCounts struct { - mockPackageGetter - counts map[string]int -} - -func (mpg mockPackageGetterCounts) GetMemPackage(path string) *gnovm.MemPackage { - mpg.counts[path]++ - return mpg.mockPackageGetter.GetMemPackage(path) -} - -func TestTypeCheckMemPackage(t *testing.T) { - t.Parallel() - - // if len(ss) > 0, then multierr.Errors must decompose it in errors, and - // each error in order must contain the associated string. - errContains := func(s0 string, ss ...string) func(*testing.T, error) { - return func(t *testing.T, err error) { - t.Helper() - errs := multierr.Errors(err) - if len(errs) == 0 { - t.Errorf("expected an error, got nil") - return - } - want := len(ss) + 1 - if len(errs) != want { - t.Errorf("expected %d errors, got %d", want, len(errs)) - return - } - assert.ErrorContains(t, errs[0], s0) - for idx, err := range errs[1:] { - assert.ErrorContains(t, err, ss[idx]) - } - } - } - - type testCase struct { - name string - pkg *gnovm.MemPackage - getter MemPackageGetter - check func(*testing.T, error) - } - tt := []testCase{ - { - "Simple", - &gnovm.MemPackage{ - Name: "hello", - Path: "gno.land/p/demo/hello", - Files: []*gnovm.MemFile{ - { - Name: "hello.gno", - Body: ` - package hello - type S struct{} - func A() S { return S{} } - func B() S { return A() }`, - }, - }, - }, - nil, - nil, - }, - { - "WrongReturn", - &gnovm.MemPackage{ - Name: "hello", - Path: "gno.land/p/demo/hello", - Files: []*gnovm.MemFile{ - { - Name: "hello.gno", - Body: ` - package hello - type S struct{} - func A() S { return S{} } - func B() S { return 11 }`, - }, - }, - }, - nil, - errContains("cannot use 11"), - }, - { - "ParseError", - &gnovm.MemPackage{ - Name: "hello", - Path: "gno.land/p/demo/hello", - Files: []*gnovm.MemFile{ - { - Name: "hello.gno", - Body: ` - package hello! - func B() int { return 11 }`, - }, - }, - }, - nil, - errContains("found '!'"), - }, - { - "MultiError", - &gnovm.MemPackage{ - Name: "main", - Path: "gno.land/p/demo/main", - Files: []*gnovm.MemFile{ - { - Name: "hello.gno", - Body: ` - package main - func main() { - _, _ = 11 - return 88, 88 - }`, - }, - }, - }, - nil, - errContains("assignment mismatch", "too many return values"), - }, - { - "TestsIgnored", - &gnovm.MemPackage{ - Name: "hello", - Path: "gno.land/p/demo/hello", - Files: []*gnovm.MemFile{ - { - Name: "hello.gno", - Body: ` - package hello - func B() int { return 11 }`, - }, - { - Name: "hello_test.gno", - Body: `This is not valid Gno code, but it doesn't matter because test - files are not checked.`, - }, - }, - }, - nil, - nil, - }, - { - "ImportFailed", - &gnovm.MemPackage{ - Name: "hello", - Path: "gno.land/p/demo/hello", - Files: []*gnovm.MemFile{ - { - Name: "hello.gno", - Body: ` - package hello - import "std" - func Hello() std.Address { return "hello" }`, - }, - }, - }, - mockPackageGetter{}, - errContains("import not found: std"), - }, - { - "ImportSucceeded", - &gnovm.MemPackage{ - Name: "hello", - Path: "gno.land/p/demo/hello", - Files: []*gnovm.MemFile{ - { - Name: "hello.gno", - Body: ` - package hello - import "std" - func Hello() std.Address { return "hello" }`, - }, - }, - }, - mockPackageGetter{ - &gnovm.MemPackage{ - Name: "std", - Path: "std", - Files: []*gnovm.MemFile{ - { - Name: "gnovm.gno", - Body: ` - package std - type Address string`, - }, - }, - }, - }, - nil, - }, - { - "ImportBadIdent", - &gnovm.MemPackage{ - Name: "hello", - Path: "gno.land/p/demo/hello", - Files: []*gnovm.MemFile{ - { - Name: "hello.gno", - Body: ` - package hello - import "std" - func Hello() std.Address { return "hello" }`, - }, - }, - }, - mockPackageGetter{ - &gnovm.MemPackage{ - Name: "a_completely_different_identifier", - Path: "std", - Files: []*gnovm.MemFile{ - { - Name: "gnovm.gno", - Body: ` - package a_completely_different_identifier - type Address string`, - }, - }, - }, - }, - errContains("undefined: std", "a_completely_different_identifier and not used"), - }, - } - - cacheMpg := mockPackageGetterCounts{ - mockPackageGetter{ - &gnovm.MemPackage{ - Name: "bye", - Path: "bye", - Files: []*gnovm.MemFile{ - { - Name: "bye.gno", - Body: ` - package bye - import "std" - func Bye() std.Address { return "bye" }`, - }, - }, - }, - &gnovm.MemPackage{ - Name: "std", - Path: "std", - Files: []*gnovm.MemFile{ - { - Name: "gnovm.gno", - Body: ` - package std - type Address string`, - }, - }, - }, - }, - make(map[string]int), - } - - tt = append(tt, testCase{ - "ImportWithCache", - // This test will make use of the importer's internal cache for package `std`. - &gnovm.MemPackage{ - Name: "hello", - Path: "gno.land/p/demo/hello", - Files: []*gnovm.MemFile{ - { - Name: "hello.gno", - Body: ` - package hello - import ( - "std" - "bye" - ) - func Hello() std.Address { return bye.Bye() }`, - }, - }, - }, - cacheMpg, - func(t *testing.T, err error) { - t.Helper() - require.NoError(t, err) - assert.Equal(t, map[string]int{"std": 1, "bye": 1}, cacheMpg.counts) - }, - }) - - for _, tc := range tt { - tc := tc - t.Run(tc.name, func(t *testing.T) { - t.Parallel() - - format := false - err := TypeCheckMemPackage(tc.pkg, tc.getter, format) - if tc.check == nil { - assert.NoError(t, err) - } else { - tc.check(t, err) - } - }) - } -} - -func TestTypeCheckMemPackage_format(t *testing.T) { - t.Parallel() - - input := ` - package hello - func Hello(name string) string {return "hello" + name -} - - - -` - - pkg := &gnovm.MemPackage{ - Name: "hello", - Path: "gno.land/p/demo/hello", - Files: []*gnovm.MemFile{ - { - Name: "hello.gno", - Body: input, - }, - }, - } - - mpkgGetter := mockPackageGetter{} - format := false - err := TypeCheckMemPackage(pkg, mpkgGetter, format) - assert.NoError(t, err) - assert.Equal(t, input, pkg.Files[0].Body) // unchanged - - expected := `package hello - -func Hello(name string) string { - return "hello" + name -} -` - - format = true - err = TypeCheckMemPackage(pkg, mpkgGetter, format) - assert.NoError(t, err) - assert.NotEqual(t, input, pkg.Files[0].Body) - assert.Equal(t, expected, pkg.Files[0].Body) -} diff --git a/gnovm/pkg/gnolang/gotypecheck.go b/gnovm/pkg/gnolang/gotypecheck.go new file mode 100644 index 00000000000..8f86deb3dc5 --- /dev/null +++ b/gnovm/pkg/gnolang/gotypecheck.go @@ -0,0 +1,179 @@ +package gnolang + +import ( + "bytes" + "go/ast" + "go/format" + "go/parser" + "go/token" + "go/types" + "path" + "slices" + "strings" + + "github.com/gnolang/gno/gnovm" + "go.uber.org/multierr" +) + +// type checking (using go/types) + +// MemPackageGetter implements the GetMemPackage() method. It is a subset of +// [Store], separated for ease of testing. +type MemPackageGetter interface { + GetMemPackage(path string) *gnovm.MemPackage +} + +// TypeCheckMemPackage performs type validation and checking on the given +// mempkg. To retrieve dependencies, it uses getter. +// +// The syntax checking is performed entirely using Go's go/types package. +// +// If format is true, the code will be automatically updated with the +// formatted source code. +func TypeCheckMemPackage(mempkg *gnovm.MemPackage, getter MemPackageGetter, format bool) error { + return typeCheckMemPackage(mempkg, getter, false, format) +} + +// TypeCheckMemPackageTest performs the same type checks as [TypeCheckMemPackage], +// but allows re-declarations. +// +// Note: like TypeCheckMemPackage, this function ignores tests and filetests. +func TypeCheckMemPackageTest(mempkg *gnovm.MemPackage, getter MemPackageGetter) error { + return typeCheckMemPackage(mempkg, getter, true, false) +} + +func typeCheckMemPackage(mempkg *gnovm.MemPackage, getter MemPackageGetter, testing, format bool) error { + var errs error + imp := &gnoImporter{ + getter: getter, + cache: map[string]gnoImporterResult{}, + cfg: &types.Config{ + Error: func(err error) { + errs = multierr.Append(errs, err) + }, + }, + allowRedefinitions: testing, + } + imp.cfg.Importer = imp + + _, err := imp.parseCheckMemPackage(mempkg, format) + // prefer to return errs instead of err: + // err will generally contain only the first error encountered. + if errs != nil { + return errs + } + return err +} + +type gnoImporterResult struct { + pkg *types.Package + err error +} + +type gnoImporter struct { + getter MemPackageGetter + cache map[string]gnoImporterResult + cfg *types.Config + + // allow symbol redefinitions? (test standard libraries) + allowRedefinitions bool +} + +// Unused, but satisfies the Importer interface. +func (g *gnoImporter) Import(path string) (*types.Package, error) { + return g.ImportFrom(path, "", 0) +} + +type importNotFoundError string + +func (e importNotFoundError) Error() string { return "import not found: " + string(e) } + +// ImportFrom returns the imported package for the given import +// path when imported by a package file located in dir. +func (g *gnoImporter) ImportFrom(path, _ string, _ types.ImportMode) (*types.Package, error) { + if pkg, ok := g.cache[path]; ok { + return pkg.pkg, pkg.err + } + mpkg := g.getter.GetMemPackage(path) + if mpkg == nil { + err := importNotFoundError(path) + g.cache[path] = gnoImporterResult{err: err} + return nil, err + } + fmt := false + result, err := g.parseCheckMemPackage(mpkg, fmt) + g.cache[path] = gnoImporterResult{pkg: result, err: err} + return result, err +} + +func (g *gnoImporter) parseCheckMemPackage(mpkg *gnovm.MemPackage, fmt bool) (*types.Package, error) { + // This map is used to allow for function re-definitions, which are allowed + // in Gno (testing context) but not in Go. + // This map links each function identifier with a closure to remove its + // associated declaration. + var delFunc map[string]func() + if g.allowRedefinitions { + delFunc = make(map[string]func()) + } + + fset := token.NewFileSet() + files := make([]*ast.File, 0, len(mpkg.Files)) + var errs error + for _, file := range mpkg.Files { + // Ignore non-gno files. + // TODO: support filetest type checking. (should probably handle as each its + // own separate pkg, which should also be typechecked) + if !strings.HasSuffix(file.Name, ".gno") || + strings.HasSuffix(file.Name, "_test.gno") || + strings.HasSuffix(file.Name, "_filetest.gno") { + continue + } + + const parseOpts = parser.ParseComments | parser.DeclarationErrors | parser.SkipObjectResolution + f, err := parser.ParseFile(fset, path.Join(mpkg.Path, file.Name), file.Body, parseOpts) + if err != nil { + errs = multierr.Append(errs, err) + continue + } + + if delFunc != nil { + deleteOldIdents(delFunc, f) + } + + // enforce formatting + if fmt { + var buf bytes.Buffer + err = format.Node(&buf, fset, f) + if err != nil { + errs = multierr.Append(errs, err) + continue + } + file.Body = buf.String() + } + + files = append(files, f) + } + if errs != nil { + return nil, errs + } + + return g.cfg.Check(mpkg.Path, fset, files, nil) +} + +func deleteOldIdents(idents map[string]func(), f *ast.File) { + for _, decl := range f.Decls { + fd, ok := decl.(*ast.FuncDecl) + if !ok || fd.Recv != nil { // ignore methods + continue + } + if del := idents[fd.Name.Name]; del != nil { + del() + } + decl := decl + idents[fd.Name.Name] = func() { + // NOTE: cannot use the index as a file may contain multiple decls to be removed, + // so removing one would make all "later" indexes wrong. + f.Decls = slices.DeleteFunc(f.Decls, func(d ast.Decl) bool { return decl == d }) + } + } +} diff --git a/gnovm/pkg/gnolang/gotypecheck_test.go b/gnovm/pkg/gnolang/gotypecheck_test.go new file mode 100644 index 00000000000..259a1bc3e78 --- /dev/null +++ b/gnovm/pkg/gnolang/gotypecheck_test.go @@ -0,0 +1,359 @@ +package gnolang + +import ( + "testing" + + "github.com/gnolang/gno/gnovm" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/multierr" +) + +type mockPackageGetter []*gnovm.MemPackage + +func (mi mockPackageGetter) GetMemPackage(path string) *gnovm.MemPackage { + for _, pkg := range mi { + if pkg.Path == path { + return pkg + } + } + return nil +} + +type mockPackageGetterCounts struct { + mockPackageGetter + counts map[string]int +} + +func (mpg mockPackageGetterCounts) GetMemPackage(path string) *gnovm.MemPackage { + mpg.counts[path]++ + return mpg.mockPackageGetter.GetMemPackage(path) +} + +func TestTypeCheckMemPackage(t *testing.T) { + t.Parallel() + + // if len(ss) > 0, then multierr.Errors must decompose it in errors, and + // each error in order must contain the associated string. + errContains := func(s0 string, ss ...string) func(*testing.T, error) { + return func(t *testing.T, err error) { + t.Helper() + errs := multierr.Errors(err) + if len(errs) == 0 { + t.Errorf("expected an error, got nil") + return + } + want := len(ss) + 1 + if len(errs) != want { + t.Errorf("expected %d errors, got %d", want, len(errs)) + return + } + assert.ErrorContains(t, errs[0], s0) + for idx, err := range errs[1:] { + assert.ErrorContains(t, err, ss[idx]) + } + } + } + + type testCase struct { + name string + pkg *gnovm.MemPackage + getter MemPackageGetter + check func(*testing.T, error) + } + tt := []testCase{ + { + "Simple", + &gnovm.MemPackage{ + Name: "hello", + Path: "gno.land/p/demo/hello", + Files: []*gnovm.MemFile{ + { + Name: "hello.gno", + Body: ` + package hello + type S struct{} + func A() S { return S{} } + func B() S { return A() }`, + }, + }, + }, + nil, + nil, + }, + { + "WrongReturn", + &gnovm.MemPackage{ + Name: "hello", + Path: "gno.land/p/demo/hello", + Files: []*gnovm.MemFile{ + { + Name: "hello.gno", + Body: ` + package hello + type S struct{} + func A() S { return S{} } + func B() S { return 11 }`, + }, + }, + }, + nil, + errContains("cannot use 11"), + }, + { + "ParseError", + &gnovm.MemPackage{ + Name: "hello", + Path: "gno.land/p/demo/hello", + Files: []*gnovm.MemFile{ + { + Name: "hello.gno", + Body: ` + package hello! + func B() int { return 11 }`, + }, + }, + }, + nil, + errContains("found '!'"), + }, + { + "MultiError", + &gnovm.MemPackage{ + Name: "main", + Path: "gno.land/p/demo/main", + Files: []*gnovm.MemFile{ + { + Name: "hello.gno", + Body: ` + package main + func main() { + _, _ = 11 + return 88, 88 + }`, + }, + }, + }, + nil, + errContains("assignment mismatch", "too many return values"), + }, + { + "TestsIgnored", + &gnovm.MemPackage{ + Name: "hello", + Path: "gno.land/p/demo/hello", + Files: []*gnovm.MemFile{ + { + Name: "hello.gno", + Body: ` + package hello + func B() int { return 11 }`, + }, + { + Name: "hello_test.gno", + Body: `This is not valid Gno code, but it doesn't matter because test + files are not checked.`, + }, + }, + }, + nil, + nil, + }, + { + "ImportFailed", + &gnovm.MemPackage{ + Name: "hello", + Path: "gno.land/p/demo/hello", + Files: []*gnovm.MemFile{ + { + Name: "hello.gno", + Body: ` + package hello + import "std" + func Hello() std.Address { return "hello" }`, + }, + }, + }, + mockPackageGetter{}, + errContains("import not found: std"), + }, + { + "ImportSucceeded", + &gnovm.MemPackage{ + Name: "hello", + Path: "gno.land/p/demo/hello", + Files: []*gnovm.MemFile{ + { + Name: "hello.gno", + Body: ` + package hello + import "std" + func Hello() std.Address { return "hello" }`, + }, + }, + }, + mockPackageGetter{ + &gnovm.MemPackage{ + Name: "std", + Path: "std", + Files: []*gnovm.MemFile{ + { + Name: "gnovm.gno", + Body: ` + package std + type Address string`, + }, + }, + }, + }, + nil, + }, + { + "ImportBadIdent", + &gnovm.MemPackage{ + Name: "hello", + Path: "gno.land/p/demo/hello", + Files: []*gnovm.MemFile{ + { + Name: "hello.gno", + Body: ` + package hello + import "std" + func Hello() std.Address { return "hello" }`, + }, + }, + }, + mockPackageGetter{ + &gnovm.MemPackage{ + Name: "a_completely_different_identifier", + Path: "std", + Files: []*gnovm.MemFile{ + { + Name: "gnovm.gno", + Body: ` + package a_completely_different_identifier + type Address string`, + }, + }, + }, + }, + errContains("undefined: std", "a_completely_different_identifier and not used"), + }, + } + + cacheMpg := mockPackageGetterCounts{ + mockPackageGetter{ + &gnovm.MemPackage{ + Name: "bye", + Path: "bye", + Files: []*gnovm.MemFile{ + { + Name: "bye.gno", + Body: ` + package bye + import "std" + func Bye() std.Address { return "bye" }`, + }, + }, + }, + &gnovm.MemPackage{ + Name: "std", + Path: "std", + Files: []*gnovm.MemFile{ + { + Name: "gnovm.gno", + Body: ` + package std + type Address string`, + }, + }, + }, + }, + make(map[string]int), + } + + tt = append(tt, testCase{ + "ImportWithCache", + // This test will make use of the importer's internal cache for package `std`. + &gnovm.MemPackage{ + Name: "hello", + Path: "gno.land/p/demo/hello", + Files: []*gnovm.MemFile{ + { + Name: "hello.gno", + Body: ` + package hello + import ( + "std" + "bye" + ) + func Hello() std.Address { return bye.Bye() }`, + }, + }, + }, + cacheMpg, + func(t *testing.T, err error) { + t.Helper() + require.NoError(t, err) + assert.Equal(t, map[string]int{"std": 1, "bye": 1}, cacheMpg.counts) + }, + }) + + for _, tc := range tt { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + format := false + err := TypeCheckMemPackage(tc.pkg, tc.getter, format) + if tc.check == nil { + assert.NoError(t, err) + } else { + tc.check(t, err) + } + }) + } +} + +func TestTypeCheckMemPackage_format(t *testing.T) { + t.Parallel() + + input := ` + package hello + func Hello(name string) string {return "hello" + name +} + + + +` + + pkg := &gnovm.MemPackage{ + Name: "hello", + Path: "gno.land/p/demo/hello", + Files: []*gnovm.MemFile{ + { + Name: "hello.gno", + Body: input, + }, + }, + } + + mpkgGetter := mockPackageGetter{} + format := false + err := TypeCheckMemPackage(pkg, mpkgGetter, format) + assert.NoError(t, err) + assert.Equal(t, input, pkg.Files[0].Body) // unchanged + + expected := `package hello + +func Hello(name string) string { + return "hello" + name +} +` + + format = true + err = TypeCheckMemPackage(pkg, mpkgGetter, format) + assert.NoError(t, err) + assert.NotEqual(t, input, pkg.Files[0].Body) + assert.Equal(t, expected, pkg.Files[0].Body) +} From b3d4855050496222e565377ce2422677a7f1a961 Mon Sep 17 00:00:00 2001 From: Morgan Date: Tue, 14 Jan 2025 18:33:54 +0100 Subject: [PATCH 08/18] test(gnovm): move tests in values_conversions_test.go to filetests (#3490) --- gnovm/pkg/gnolang/values_conversions_test.go | 132 ------------------- gnovm/tests/files/overflow10.gno | 8 ++ gnovm/tests/files/overflow11.gno | 8 ++ gnovm/tests/files/overflow12.gno | 8 ++ gnovm/tests/files/overflow13.gno | 8 ++ gnovm/tests/files/overflow14.gno | 8 ++ gnovm/tests/files/overflow15.gno | 9 ++ gnovm/tests/files/overflow16.gno | 9 ++ gnovm/tests/files/overflow6.gno | 8 ++ gnovm/tests/files/overflow7.gno | 8 ++ gnovm/tests/files/overflow8.gno | 8 ++ gnovm/tests/files/overflow9.gno | 8 ++ 12 files changed, 90 insertions(+), 132 deletions(-) create mode 100644 gnovm/tests/files/overflow10.gno create mode 100644 gnovm/tests/files/overflow11.gno create mode 100644 gnovm/tests/files/overflow12.gno create mode 100644 gnovm/tests/files/overflow13.gno create mode 100644 gnovm/tests/files/overflow14.gno create mode 100644 gnovm/tests/files/overflow15.gno create mode 100644 gnovm/tests/files/overflow16.gno create mode 100644 gnovm/tests/files/overflow6.gno create mode 100644 gnovm/tests/files/overflow7.gno create mode 100644 gnovm/tests/files/overflow8.gno create mode 100644 gnovm/tests/files/overflow9.gno diff --git a/gnovm/pkg/gnolang/values_conversions_test.go b/gnovm/pkg/gnolang/values_conversions_test.go index 5538e973bdc..b8b06df4228 100644 --- a/gnovm/pkg/gnolang/values_conversions_test.go +++ b/gnovm/pkg/gnolang/values_conversions_test.go @@ -2,7 +2,6 @@ package gnolang import ( "math" - "strings" "testing" "github.com/cockroachdb/apd/v3" @@ -27,134 +26,3 @@ func TestConvertUntypedBigdecToFloat(t *testing.T) { require.True(t, softfloat.Feq64(dst.GetFloat64(), 0)) } - -func TestBitShiftingOverflow(t *testing.T) { - t.Parallel() - - testFunc := func(source, msg string) { - defer func() { - if len(msg) == 0 { - return - } - - r := recover() - - if r == nil { - t.Fail() - } - - err := r.(*PreprocessError) - c := strings.Contains(err.Error(), msg) - if !c { - t.Fatalf(`expected "%s", got "%s"`, msg, r) - } - }() - - m := NewMachine("test", nil) - - n := MustParseFile("main.go", source) - m.RunFiles(n) - m.RunMain() - } - - type cases struct { - source string - msg string - } - - tests := []cases{ - { - `package test - -func main() { - const a = int32(1) << 33 -}`, - `test/main.go:3:1: constant overflows`, - }, - { - `package test - -func main() { - const a1 = int8(1) << 8 -}`, - `test/main.go:3:1: constant overflows`, - }, - { - `package test - -func main() { - const a2 = int16(1) << 16 -}`, - `test/main.go:3:1: constant overflows`, - }, - { - `package test - -func main() { - const a3 = int32(1) << 33 -}`, - `test/main.go:3:1: constant overflows`, - }, - { - `package test - -func main() { - const a4 = int64(1) << 65 -}`, - `test/main.go:3:1: constant overflows`, - }, - { - `package test - -func main() { - const b1 = uint8(1) << 8 -}`, - `test/main.go:3:1: constant overflows`, - }, - { - `package test - -func main() { - const b2 = uint16(1) << 16 -}`, - `test/main.go:3:1: constant overflows`, - }, - { - `package test - -func main() { - const b3 = uint32(1) << 33 -}`, - `test/main.go:3:1: constant overflows`, - }, - { - `package test - -func main() { - const b4 = uint64(1) << 65 -}`, - `test/main.go:3:1: constant overflows`, - }, - { - `package test - - func main() { - const c1 = 1 << 128 - }`, - ``, - }, - { - `package test - - func main() { - const c1 = 1 << 128 - println(c1) - }`, - `test/main.go:5:4: bigint overflows target kind`, - }, - } - - for _, tc := range tests { - testFunc(tc.source, tc.msg) - } -} diff --git a/gnovm/tests/files/overflow10.gno b/gnovm/tests/files/overflow10.gno new file mode 100644 index 00000000000..c3224eaf471 --- /dev/null +++ b/gnovm/tests/files/overflow10.gno @@ -0,0 +1,8 @@ +package main + +func main() { + const a4 = int64(1) << 65 +} + +// Error: +// main/files/overflow10.gno:3:1: constant overflows diff --git a/gnovm/tests/files/overflow11.gno b/gnovm/tests/files/overflow11.gno new file mode 100644 index 00000000000..bac883f104f --- /dev/null +++ b/gnovm/tests/files/overflow11.gno @@ -0,0 +1,8 @@ +package main + +func main() { + const b1 = uint8(1) << 8 +} + +// Error: +// main/files/overflow11.gno:3:1: constant overflows diff --git a/gnovm/tests/files/overflow12.gno b/gnovm/tests/files/overflow12.gno new file mode 100644 index 00000000000..52ca8fc80d4 --- /dev/null +++ b/gnovm/tests/files/overflow12.gno @@ -0,0 +1,8 @@ +package main + +func main() { + const b2 = uint16(1) << 16 +} + +// Error: +// main/files/overflow12.gno:3:1: constant overflows diff --git a/gnovm/tests/files/overflow13.gno b/gnovm/tests/files/overflow13.gno new file mode 100644 index 00000000000..40b6885b3ff --- /dev/null +++ b/gnovm/tests/files/overflow13.gno @@ -0,0 +1,8 @@ +package main + +func main() { + const b3 = uint32(1) << 33 +} + +// Error: +// main/files/overflow13.gno:3:1: constant overflows diff --git a/gnovm/tests/files/overflow14.gno b/gnovm/tests/files/overflow14.gno new file mode 100644 index 00000000000..0da66bb7b9d --- /dev/null +++ b/gnovm/tests/files/overflow14.gno @@ -0,0 +1,8 @@ +package main + +func main() { + const b4 = uint64(1) << 65 +} + +// Error: +// main/files/overflow14.gno:3:1: constant overflows diff --git a/gnovm/tests/files/overflow15.gno b/gnovm/tests/files/overflow15.gno new file mode 100644 index 00000000000..f41848d5d21 --- /dev/null +++ b/gnovm/tests/files/overflow15.gno @@ -0,0 +1,9 @@ +package main + +func main() { + const c1 = 1 << 128 + println(uint64(c1 >> 65)) +} + +// Output: +// 9223372036854775808 diff --git a/gnovm/tests/files/overflow16.gno b/gnovm/tests/files/overflow16.gno new file mode 100644 index 00000000000..36cc7689529 --- /dev/null +++ b/gnovm/tests/files/overflow16.gno @@ -0,0 +1,9 @@ +package main + +func main() { + const c1 = 1 << 128 + println(c1) +} + +// Error: +// main/files/overflow16.gno:5:2: bigint overflows target kind diff --git a/gnovm/tests/files/overflow6.gno b/gnovm/tests/files/overflow6.gno new file mode 100644 index 00000000000..b9c49027336 --- /dev/null +++ b/gnovm/tests/files/overflow6.gno @@ -0,0 +1,8 @@ +package main + +func main() { + const a = int32(1) << 33 +} + +// Error: +// main/files/overflow6.gno:3:1: constant overflows diff --git a/gnovm/tests/files/overflow7.gno b/gnovm/tests/files/overflow7.gno new file mode 100644 index 00000000000..28202811fca --- /dev/null +++ b/gnovm/tests/files/overflow7.gno @@ -0,0 +1,8 @@ +package main + +func main() { + const a1 = int8(1) << 8 +} + +// Error: +// main/files/overflow7.gno:3:1: constant overflows diff --git a/gnovm/tests/files/overflow8.gno b/gnovm/tests/files/overflow8.gno new file mode 100644 index 00000000000..f25fb0aa833 --- /dev/null +++ b/gnovm/tests/files/overflow8.gno @@ -0,0 +1,8 @@ +package main + +func main() { + const a2 = int16(1) << 16 +} + +// Error: +// main/files/overflow8.gno:3:1: constant overflows diff --git a/gnovm/tests/files/overflow9.gno b/gnovm/tests/files/overflow9.gno new file mode 100644 index 00000000000..5eae14b67aa --- /dev/null +++ b/gnovm/tests/files/overflow9.gno @@ -0,0 +1,8 @@ +package main + +func main() { + const a3 = int32(1) << 33 +} + +// Error: +// main/files/overflow9.gno:3:1: constant overflows From c79c16dc0d33d260f876b7e034531e180cc0ac86 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:21:01 +0100 Subject: [PATCH 09/18] chore(deps): bump anchore/sbom-action from 0.17.8 to 0.17.9 in the actions group (#3476) Bumps the actions group with 1 update: [anchore/sbom-action](https://github.com/anchore/sbom-action). Updates `anchore/sbom-action` from 0.17.8 to 0.17.9
Release notes

Sourced from anchore/sbom-action's releases.

v0.17.9

Changes in v0.17.9

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=anchore/sbom-action&package-manager=github_actions&previous-version=0.17.8&new-version=0.17.9)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/releaser-master.yml | 2 +- .github/workflows/releaser-nightly.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/releaser-master.yml b/.github/workflows/releaser-master.yml index 6e3eed31914..7c81789b060 100644 --- a/.github/workflows/releaser-master.yml +++ b/.github/workflows/releaser-master.yml @@ -27,7 +27,7 @@ jobs: cache: true - uses: sigstore/cosign-installer@v3.7.0 - - uses: anchore/sbom-action/download-syft@v0.17.8 + - uses: anchore/sbom-action/download-syft@v0.17.9 - uses: docker/login-action@v3 with: diff --git a/.github/workflows/releaser-nightly.yml b/.github/workflows/releaser-nightly.yml index 4f6e636af1b..47b6cabb223 100644 --- a/.github/workflows/releaser-nightly.yml +++ b/.github/workflows/releaser-nightly.yml @@ -24,7 +24,7 @@ jobs: cache: true - uses: sigstore/cosign-installer@v3.7.0 - - uses: anchore/sbom-action/download-syft@v0.17.8 + - uses: anchore/sbom-action/download-syft@v0.17.9 - uses: docker/login-action@v3 with: From 3adb2ac390f60467e6d77c3aa7ff4507cb37e0e8 Mon Sep 17 00:00:00 2001 From: 6h057 <15034695+omarsy@users.noreply.github.com> Date: Tue, 14 Jan 2025 21:29:49 +0100 Subject: [PATCH 10/18] feat(gnovm): align Gno constant handling with Go specifications (#2828) Related Issues: Closes #2628 This PR aims to align Gno's constant handling with the Go specification regarding constant expressions (see [Go Specification on Constants](https://go.dev/ref/spec#Constant_expressions)). 1. **Primitive Type Requirement** - **Should Work:** ```go const t = 1 ``` - **Should Return an Error:** ```go const t = []string{"1"} ``` **Error:** ``` (const (slice[("1" string)] []string)) (value of type []string) is not constant ``` 2. **Function Calls Disallowed** Only built-in functions should be allowed. - **Should Work:** ```go const t = len("s") ``` - **Should Return an Error:** ```go func v() string { return "" } const t = v() ``` **Error:** ``` v() (value of type string) is not constant ``` 3. **Constant Operands Requirement** Constant expressions may contain only constant operands and are evaluated at compile time. - **Should Work:** ```go const t = 1 const v = t ``` - **Should Raise an Error:** ```go t := 1 const v = t ``` **Error:** ``` t (variable of type int) is not constant ``` 4. **Type Assertion Forbidden** - This code: ```go var i interface{} = 1 const t, ok = i.(int) ``` - **Should Raise This Error:** ``` i.(int) (comma, ok expression of type int) is not constant ``` 5. **Index Expression Forbidden** - This code: ```go var i = []string{} const t, ok = i[0] ``` - **Should Return This Error:** ``` i[0] (variable of type string) is not constant ```
Contributors' checklist... - [ ] Added new tests, or not needed, or not feasible - [ ] Provided an example (e.g. screenshot) to aid review or the PR is self-explanatory - [ ] Updated the official documentation or not needed - [ ] No breaking changes were made, or a `BREAKING CHANGE: xxx` message was included in the description - [ ] Added references to related issues and PRs - [ ] Provided any useful hints for running manual tests - [ ] Added new benchmarks to [generated graphs](https://gnoland.github.io/benchmarks), if any. More info [here](https://github.com/gnolang/gno/blob/master/.benchmarks/README.md).
--- .../r/demo/keystore/keystore_test.gno | 4 - gnovm/pkg/gnolang/preprocess.go | 1 + gnovm/pkg/gnolang/type_check.go | 145 ++++++++++++++++++ gnovm/tests/files/const23.gno | 11 ++ gnovm/tests/files/const24.gno | 82 ++++++++++ gnovm/tests/files/const25.gno | 11 ++ gnovm/tests/files/const26.gno | 15 ++ gnovm/tests/files/const27.gno | 16 ++ gnovm/tests/files/const28.gno | 12 ++ gnovm/tests/files/const29.gno | 12 ++ gnovm/tests/files/const30.gno | 15 ++ gnovm/tests/files/const31.gno | 15 ++ gnovm/tests/files/const32.gno | 11 ++ gnovm/tests/files/const33.gno | 12 ++ gnovm/tests/files/const34.gno | 10 ++ gnovm/tests/files/const35.gno | 12 ++ gnovm/tests/files/const36.gno | 13 ++ gnovm/tests/files/const37_native.gno | 10 ++ gnovm/tests/files/const37_stdlibs.gno | 10 ++ gnovm/tests/files/const38.gno | 11 ++ gnovm/tests/files/const39.gno | 14 ++ gnovm/tests/files/const40.gno | 8 + gnovm/tests/files/const41.gno | 8 + gnovm/tests/files/const42.gno | 8 + gnovm/tests/files/const43.gno | 10 ++ gnovm/tests/files/const44.gno | 10 ++ gnovm/tests/files/const45_a.gno | 14 ++ gnovm/tests/files/const45_b.gno | 14 ++ gnovm/tests/files/const46.gno | 10 ++ gnovm/tests/files/const47.gno | 10 ++ gnovm/tests/files/const48.gno | 9 ++ gnovm/tests/files/const49.gno | 16 ++ gnovm/tests/files/const50.gno | 12 ++ 33 files changed, 567 insertions(+), 4 deletions(-) create mode 100644 gnovm/tests/files/const23.gno create mode 100644 gnovm/tests/files/const24.gno create mode 100644 gnovm/tests/files/const25.gno create mode 100644 gnovm/tests/files/const26.gno create mode 100644 gnovm/tests/files/const27.gno create mode 100644 gnovm/tests/files/const28.gno create mode 100644 gnovm/tests/files/const29.gno create mode 100644 gnovm/tests/files/const30.gno create mode 100644 gnovm/tests/files/const31.gno create mode 100644 gnovm/tests/files/const32.gno create mode 100644 gnovm/tests/files/const33.gno create mode 100644 gnovm/tests/files/const34.gno create mode 100644 gnovm/tests/files/const35.gno create mode 100644 gnovm/tests/files/const36.gno create mode 100644 gnovm/tests/files/const37_native.gno create mode 100644 gnovm/tests/files/const37_stdlibs.gno create mode 100644 gnovm/tests/files/const38.gno create mode 100644 gnovm/tests/files/const39.gno create mode 100644 gnovm/tests/files/const40.gno create mode 100644 gnovm/tests/files/const41.gno create mode 100644 gnovm/tests/files/const42.gno create mode 100644 gnovm/tests/files/const43.gno create mode 100644 gnovm/tests/files/const44.gno create mode 100644 gnovm/tests/files/const45_a.gno create mode 100644 gnovm/tests/files/const45_b.gno create mode 100644 gnovm/tests/files/const46.gno create mode 100644 gnovm/tests/files/const47.gno create mode 100644 gnovm/tests/files/const48.gno create mode 100644 gnovm/tests/files/const49.gno create mode 100644 gnovm/tests/files/const50.gno diff --git a/examples/gno.land/r/demo/keystore/keystore_test.gno b/examples/gno.land/r/demo/keystore/keystore_test.gno index 41597016ea3..9b5fafa2f95 100644 --- a/examples/gno.land/r/demo/keystore/keystore_test.gno +++ b/examples/gno.land/r/demo/keystore/keystore_test.gno @@ -11,10 +11,6 @@ import ( ) func TestRender(t *testing.T) { - // https://github.com/gnolang/gno/pull/3375 changed const -> var : - // For some reason non native functions fails on constants with - // constant overflows (code=2), this does not happens when using a variable - // TODO: check this issue after and if this PR is merged var ( author1 std.Address = testutils.TestAddress("author1") author2 std.Address = testutils.TestAddress("author2") diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index ddfd1851989..ffa0f518331 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -2294,6 +2294,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { // NOTE: may or may not be a *ConstExpr, // but if not, make one now. for i, vx := range n.Values { + assertValidConstExpr(store, last, n, vx) n.Values[i] = evalConst(store, last, vx) } } else { diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go index 70166116fcc..f96cb71e4b6 100644 --- a/gnovm/pkg/gnolang/type_check.go +++ b/gnovm/pkg/gnolang/type_check.go @@ -215,6 +215,151 @@ func assertAssignableTo(n Node, xt, dt Type, autoNative bool) { } } +func assertValidConstExpr(store Store, last BlockNode, n *ValueDecl, expr Expr) { + if n.Type != nil { + nt := evalStaticType(store, last, n.Type) + if xnt, ok := nt.(*NativeType); ok { + nt = go2GnoBaseType(xnt.Type) + } + + if _, ok := baseOf(nt).(PrimitiveType); !ok { + panic(fmt.Sprintf("invalid constant type %s", nt.String())) + } + } + + nt := evalStaticTypeOf(store, last, expr) + if xnt, ok := nt.(*NativeType); ok { + nt = go2GnoBaseType(xnt.Type) + } + + if nt == nil { + panic(fmt.Sprintf("%s (variable of type nil) is not constant", expr)) + } + + if _, ok := baseOf(nt).(PrimitiveType); !ok { + panic(fmt.Sprintf("%s (variable of type %s) is not constant", expr, nt)) + } + + assertValidConstValue(store, last, expr) +} + +func assertValidConstValue(store Store, last BlockNode, currExpr Expr) { +Main: + switch currExpr := currExpr.(type) { + case *ConstExpr: + case *UnaryExpr: + // *, & is filter out previously since they are not primitive + assertValidConstValue(store, last, currExpr.X) + case *TypeAssertExpr: + ty := evalStaticTypeOf(store, last, currExpr) + if _, ok := ty.(*TypeType); ok { + ty = evalStaticType(store, last, currExpr) + } + panic(fmt.Sprintf("%s (comma, ok expression of type %s) is not constant", currExpr.String(), currExpr.Type)) + case *CallExpr: + ift := evalStaticTypeOf(store, last, currExpr.Func) + switch baseOf(ift).(type) { + case *FuncType: + tup := evalStaticTypeOfRaw(store, last, currExpr).(*tupleType) + + // check for built-in functions + if cx, ok := currExpr.Func.(*ConstExpr); ok { + if fv, ok := cx.V.(*FuncValue); ok { + if fv.PkgPath == uversePkgPath { + // TODO: should support min, max, real, imag + switch { + case fv.Name == "len": + at := evalStaticTypeOf(store, last, currExpr.Args[0]) + if _, ok := baseOf(at).(*ArrayType); ok { + // ok + break Main + } + assertValidConstValue(store, last, currExpr.Args[0]) + break Main + case fv.Name == "cap": + at := evalStaticTypeOf(store, last, currExpr.Args[0]) + if _, ok := baseOf(at).(*ArrayType); ok { + // ok + break Main + } + assertValidConstValue(store, last, currExpr.Args[0]) + break Main + } + } + } + } + + switch { + case len(tup.Elts) == 0: + panic(fmt.Sprintf("%s (no value) used as value", currExpr.String())) + case len(tup.Elts) == 1: + panic(fmt.Sprintf("%s (value of type %s) is not constant", currExpr.String(), tup.Elts[0])) + default: + panic(fmt.Sprintf("multiple-value %s (value of type %s) in single-value context", currExpr.String(), tup.Elts)) + } + case *TypeType: + for _, arg := range currExpr.Args { + assertValidConstValue(store, last, arg) + } + case *NativeType: + // Todo: should add a test after the fix of https://github.com/gnolang/gno/issues/3006 + ty := evalStaticType(store, last, currExpr.Func) + panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.String(), ty)) + default: + panic(fmt.Sprintf( + "unexpected func type %v (%v)", + ift, reflect.TypeOf(ift))) + } + case *BinaryExpr: + assertValidConstValue(store, last, currExpr.Left) + assertValidConstValue(store, last, currExpr.Right) + case *SelectorExpr: + xt := evalStaticTypeOf(store, last, currExpr.X) + switch xt := xt.(type) { + case *PackageType: + var pv *PackageValue + if cx, ok := currExpr.X.(*ConstExpr); ok { + // NOTE: *Machine.TestMemPackage() needs this + // to pass in an imported package as *ConstEzpr. + pv = cx.V.(*PackageValue) + } else { + // otherwise, packages can only be referred to by + // *NameExprs, and cannot be copied. + pvc := evalConst(store, last, currExpr.X) + pv_, ok := pvc.V.(*PackageValue) + if !ok { + panic(fmt.Sprintf( + "missing package in selector expr %s", + currExpr.String())) + } + pv = pv_ + } + if pv.GetBlock(store).Source.GetIsConst(store, currExpr.Sel) { + break Main + } + + tt := pv.GetBlock(store).Source.GetStaticTypeOf(store, currExpr.Sel) + panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.String(), tt)) + case *PointerType, *DeclaredType, *StructType, *InterfaceType, *TypeType, *NativeType: + ty := evalStaticTypeOf(store, last, currExpr) + if _, ok := ty.(*TypeType); ok { + ty = evalStaticType(store, last, currExpr) + } + panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.String(), ty)) + default: + panic(fmt.Sprintf( + "unexpected selector expression type %v", + reflect.TypeOf(xt))) + } + default: + ift := evalStaticTypeOf(store, last, currExpr) + if _, ok := ift.(*TypeType); ok { + ift = evalStaticType(store, last, currExpr) + } + panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.String(), ift)) + } +} + // checkValDefineMismatch checks for mismatch between the number of variables and values in a ValueDecl or AssignStmt. func checkValDefineMismatch(n Node) { var ( diff --git a/gnovm/tests/files/const23.gno b/gnovm/tests/files/const23.gno new file mode 100644 index 00000000000..f445c3e8eb2 --- /dev/null +++ b/gnovm/tests/files/const23.gno @@ -0,0 +1,11 @@ +package main + +import "fmt" + +func main() { + const t []string = []string{} + fmt.Println(t) +} + +// Error: +// main/files/const23.gno:6:8: invalid constant type []string diff --git a/gnovm/tests/files/const24.gno b/gnovm/tests/files/const24.gno new file mode 100644 index 00000000000..d82c50c86aa --- /dev/null +++ b/gnovm/tests/files/const24.gno @@ -0,0 +1,82 @@ +package main + +import ( + "fmt" + "time" +) + +func main() { + const a int = 1_000_000 + const b byte = byte(1) + const c float64 = 1_000_000.000 + const d string = "Hello, World!" + const e rune = 'a' + const g bool = true + const h uint = 1_000 + const i int8 = 1 + const j int16 = 1 + const k int32 = 1 + const l int64 = 1 + const m uint8 = 1 + const n uint16 = 1 + const o uint32 = 1 + const p uint64 = 1 + const r float32 = 1_000_000.000 + const s = r + const t = len("s") + const u = 1 + len("s") + 3 + ars := [10]string{} + const v = len(ars) + const w = cap(ars) + const x = time.Second + const y = +len("ay") + + fmt.Println(a) + fmt.Println(b) + fmt.Println(c) + fmt.Println(d) + fmt.Println(e) + fmt.Println(g) + fmt.Println(h) + fmt.Println(i) + fmt.Println(j) + fmt.Println(k) + fmt.Println(l) + fmt.Println(m) + fmt.Println(n) + fmt.Println(o) + fmt.Println(p) + fmt.Println(r) + fmt.Println(s) + fmt.Println(t) + fmt.Println(u) + fmt.Println(v) + fmt.Println(w) + println(x) + fmt.Println(y) +} + +// Output: +// 1000000 +// 1 +// 1e+06 +// Hello, World! +// 97 +// true +// 1000 +// 1 +// 1 +// 1 +// 1 +// 1 +// 1 +// 1 +// 1 +// 1e+06 +// 1e+06 +// 1 +// 5 +// 10 +// 10 +// 1s +// 2 diff --git a/gnovm/tests/files/const25.gno b/gnovm/tests/files/const25.gno new file mode 100644 index 00000000000..64e0358bdef --- /dev/null +++ b/gnovm/tests/files/const25.gno @@ -0,0 +1,11 @@ +package main + +import "fmt" + +func main() { + const t = []string{"1"} + fmt.Println(t) +} + +// Error: +// main/files/const25.gno:6:8: [](const-type string){(const ("1" string))} (variable of type []string) is not constant diff --git a/gnovm/tests/files/const26.gno b/gnovm/tests/files/const26.gno new file mode 100644 index 00000000000..a1533e98c57 --- /dev/null +++ b/gnovm/tests/files/const26.gno @@ -0,0 +1,15 @@ +package main + +import "fmt" + +func v() string { + return "" +} + +func main() { + const t = v() + fmt.Println(t) +} + +// Error: +// main/files/const26.gno:10:8: v() (value of type string) is not constant diff --git a/gnovm/tests/files/const27.gno b/gnovm/tests/files/const27.gno new file mode 100644 index 00000000000..4be731e16a7 --- /dev/null +++ b/gnovm/tests/files/const27.gno @@ -0,0 +1,16 @@ +package main + +import "fmt" + +func v() string { + return "" +} + +func main() { + var i interface{} = 1 + const t, ok = i.(int) + fmt.Println(t, ok) +} + +// Error: +// main/files/const27.gno:11:8: i.((const-type int)) (comma, ok expression of type (const-type int)) is not constant diff --git a/gnovm/tests/files/const28.gno b/gnovm/tests/files/const28.gno new file mode 100644 index 00000000000..e4762c3205c --- /dev/null +++ b/gnovm/tests/files/const28.gno @@ -0,0 +1,12 @@ +package main + +import "fmt" + +func main() { + var s []string = []string{"1"} + const t, ok = s[0] + fmt.Println(t, ok) +} + +// Error: +// main/files/const28.gno:7:8: s[(const (0 int))] (variable of type string) is not constant diff --git a/gnovm/tests/files/const29.gno b/gnovm/tests/files/const29.gno new file mode 100644 index 00000000000..41d8d0a816d --- /dev/null +++ b/gnovm/tests/files/const29.gno @@ -0,0 +1,12 @@ +package main + +import "fmt" + +func main() { + s := "1" + const t = s + fmt.Println(t) +} + +// Error: +// main/files/const29.gno:7:8: s (variable of type string) is not constant diff --git a/gnovm/tests/files/const30.gno b/gnovm/tests/files/const30.gno new file mode 100644 index 00000000000..4a166013cdc --- /dev/null +++ b/gnovm/tests/files/const30.gno @@ -0,0 +1,15 @@ +package main + +import "fmt" + +func v() { + return +} + +func main() { + const t = v() + fmt.Println(t) +} + +// Error: +// main/files/const30.gno:10:8: v (no value) used as value diff --git a/gnovm/tests/files/const31.gno b/gnovm/tests/files/const31.gno new file mode 100644 index 00000000000..e37577789e6 --- /dev/null +++ b/gnovm/tests/files/const31.gno @@ -0,0 +1,15 @@ +package main + +import "fmt" + +func v() (string, string) { + return "", "" +} + +func main() { + const t, v = v() + fmt.Println(t) +} + +// Error: +// main/files/const31.gno:10:8: (const (v func()( string, string)))() (variable of type (string,string)) is not constant diff --git a/gnovm/tests/files/const32.gno b/gnovm/tests/files/const32.gno new file mode 100644 index 00000000000..83d3ae5e73c --- /dev/null +++ b/gnovm/tests/files/const32.gno @@ -0,0 +1,11 @@ +package main + +import "fmt" + +func main() { + const t = 1 + 2 + len([]string{}) + fmt.Println(t) +} + +// Error: +// main/files/const32.gno:6:8: [](const-type string){} (variable of type []string) is not constant diff --git a/gnovm/tests/files/const33.gno b/gnovm/tests/files/const33.gno new file mode 100644 index 00000000000..42652d3b458 --- /dev/null +++ b/gnovm/tests/files/const33.gno @@ -0,0 +1,12 @@ +package main + +var x = 1 +var y = 1 + +const b = x == y + +func main() { + println("ok") +} +// Error: +// main/files/const33.gno:6:7: x (variable of type int) is not constant diff --git a/gnovm/tests/files/const34.gno b/gnovm/tests/files/const34.gno new file mode 100644 index 00000000000..9c79cf86b88 --- /dev/null +++ b/gnovm/tests/files/const34.gno @@ -0,0 +1,10 @@ +package main + +const a = func() { println("hey") } + +func main() { + println("ok") +} + +// Error: +// main/files/const34.gno:3:7: func func(){ (const (println func(xs ...interface{})()))((const ("hey" string))) } (variable of type func()()) is not constant diff --git a/gnovm/tests/files/const35.gno b/gnovm/tests/files/const35.gno new file mode 100644 index 00000000000..e85d68eb8db --- /dev/null +++ b/gnovm/tests/files/const35.gno @@ -0,0 +1,12 @@ +package main + +var x = 1 + +const ff = +(1 << x) + +func main() { + println("ok") +} + +// Error: +// main/files/const35.gno:5:7: x (variable of type int) is not constant diff --git a/gnovm/tests/files/const36.gno b/gnovm/tests/files/const36.gno new file mode 100644 index 00000000000..8a677a35be9 --- /dev/null +++ b/gnovm/tests/files/const36.gno @@ -0,0 +1,13 @@ +package main + +type s struct { + x int +} + +func main() { + s := s{1} + const v = s.x +} + +// Error: +// main/files/const36.gno:9:8: s.x (variable of type int) is not constant diff --git a/gnovm/tests/files/const37_native.gno b/gnovm/tests/files/const37_native.gno new file mode 100644 index 00000000000..6a164328a3b --- /dev/null +++ b/gnovm/tests/files/const37_native.gno @@ -0,0 +1,10 @@ +package main + +import "time" + +func main() { + const v = time.UTC +} + +// Error: +// main/files/const37_native.gno:6:8: time.UTC (variable of type *time.Location) is not constant diff --git a/gnovm/tests/files/const37_stdlibs.gno b/gnovm/tests/files/const37_stdlibs.gno new file mode 100644 index 00000000000..9ec614f2538 --- /dev/null +++ b/gnovm/tests/files/const37_stdlibs.gno @@ -0,0 +1,10 @@ +package main + +import "time" + +func main() { + const v = time.UTC +} + +// Error: +// main/files/const37_stdlibs.gno:6:8: time.UTC (variable of type *time.Location) is not constant \ No newline at end of file diff --git a/gnovm/tests/files/const38.gno b/gnovm/tests/files/const38.gno new file mode 100644 index 00000000000..815e74fa76a --- /dev/null +++ b/gnovm/tests/files/const38.gno @@ -0,0 +1,11 @@ +package main + +import "std" + +func main() { + v := std.Coin{} + const c = v.Denom +} + +// Error: +// main/files/const38.gno:7:8: v.Denom (variable of type string) is not constant diff --git a/gnovm/tests/files/const39.gno b/gnovm/tests/files/const39.gno new file mode 100644 index 00000000000..68ff7dd5630 --- /dev/null +++ b/gnovm/tests/files/const39.gno @@ -0,0 +1,14 @@ +package main + +type T struct { + a int +} + +func (tv T) Mv(a int) int { return 0 } + +func main() { + const t = T.Mv +} + +// Error: +// main/files/const39.gno:10:8: T.Mv (variable of type func(tv main.T,a int)( int)) is not constant diff --git a/gnovm/tests/files/const40.gno b/gnovm/tests/files/const40.gno new file mode 100644 index 00000000000..d1dedc382e6 --- /dev/null +++ b/gnovm/tests/files/const40.gno @@ -0,0 +1,8 @@ +package main + +func main() { + const t = [0]string{} +} + +// Error: +// main/files/const40.gno:4:8: [(const (0 int))](const-type string){} (variable of type [0]string) is not constant diff --git a/gnovm/tests/files/const41.gno b/gnovm/tests/files/const41.gno new file mode 100644 index 00000000000..b4424dcef94 --- /dev/null +++ b/gnovm/tests/files/const41.gno @@ -0,0 +1,8 @@ +package main + +func main() { + const t [0]string = [0]string{} +} + +// Error: +// main/files/const41.gno:4:8: invalid constant type [0]string \ No newline at end of file diff --git a/gnovm/tests/files/const42.gno b/gnovm/tests/files/const42.gno new file mode 100644 index 00000000000..5763a2fc121 --- /dev/null +++ b/gnovm/tests/files/const42.gno @@ -0,0 +1,8 @@ +package main + +func main() { + const t int +} + +// Error: +// main/files/const42.gno:4:8: missing init expr for t \ No newline at end of file diff --git a/gnovm/tests/files/const43.gno b/gnovm/tests/files/const43.gno new file mode 100644 index 00000000000..d929e526579 --- /dev/null +++ b/gnovm/tests/files/const43.gno @@ -0,0 +1,10 @@ +package main + +func main() { + a := [2]int{1, 2} + + const b = a +} + +// Error: +// main/files/const43.gno:6:8: a (variable of type [2]int) is not constant diff --git a/gnovm/tests/files/const44.gno b/gnovm/tests/files/const44.gno new file mode 100644 index 00000000000..69924b8ea9d --- /dev/null +++ b/gnovm/tests/files/const44.gno @@ -0,0 +1,10 @@ +package main + +const a = interface{}(nil) + +func main() { + println("ok") +} + +// Error: +// main/files/const44.gno:3:7: (const (undefined)) (variable of type interface{}) is not constant diff --git a/gnovm/tests/files/const45_a.gno b/gnovm/tests/files/const45_a.gno new file mode 100644 index 00000000000..fef13e2fc68 --- /dev/null +++ b/gnovm/tests/files/const45_a.gno @@ -0,0 +1,14 @@ +package main + +type MyStruct struct { + arr [2]int +} + +const a = len(MyStruct{arr: [2]int{1, 2}}.arr) + +func main() { + println("ok") +} + +// Output: +// ok diff --git a/gnovm/tests/files/const45_b.gno b/gnovm/tests/files/const45_b.gno new file mode 100644 index 00000000000..7da25d3268c --- /dev/null +++ b/gnovm/tests/files/const45_b.gno @@ -0,0 +1,14 @@ +package main + +type MyStruct struct { + arr []int +} + +const a = len(MyStruct{arr: []int{1, 2}}.arr) + +func main() { + println("ok") +} + +// Error: +// main/files/const45_b.gno:7:7: MyStruct{arr: [](const-type int){(const (1 int)), (const (2 int))}}.arr (variable of type []int) is not constant diff --git a/gnovm/tests/files/const46.gno b/gnovm/tests/files/const46.gno new file mode 100644 index 00000000000..4722cba294e --- /dev/null +++ b/gnovm/tests/files/const46.gno @@ -0,0 +1,10 @@ +package main + +const a = len(map[string][2]int{"arr": {1, 2}}["arr"]) + +func main() { + println("ok") +} + +// Output: +// ok diff --git a/gnovm/tests/files/const47.gno b/gnovm/tests/files/const47.gno new file mode 100644 index 00000000000..528ba469562 --- /dev/null +++ b/gnovm/tests/files/const47.gno @@ -0,0 +1,10 @@ +package main + +const a = len(map[string][2]interface{}{"arr": {1, 2}}["arr"]) + +func main() { + println("ok") +} + +// Output: +// ok diff --git a/gnovm/tests/files/const48.gno b/gnovm/tests/files/const48.gno new file mode 100644 index 00000000000..001fd911fa5 --- /dev/null +++ b/gnovm/tests/files/const48.gno @@ -0,0 +1,9 @@ +package main + +func main() { + const a = len(map[string][]int{"arr": {1, 2}}) + println("ok", a) +} + +// Error: +// main/files/const48.gno:4:8: map[(const-type string)] [](const-type int){(const ("arr" string)): (const-type []int){(const (1 int)), (const (2 int))}} (variable of type map[string][]int) is not constant diff --git a/gnovm/tests/files/const49.gno b/gnovm/tests/files/const49.gno new file mode 100644 index 00000000000..4b3f38e4d5e --- /dev/null +++ b/gnovm/tests/files/const49.gno @@ -0,0 +1,16 @@ +package main + +import "io" + +type ( + s [2]int + m s +) + +func main() { + const v = len(m{1, 2}) + println(v) +} + +// Output: +// 2 diff --git a/gnovm/tests/files/const50.gno b/gnovm/tests/files/const50.gno new file mode 100644 index 00000000000..5144173bf74 --- /dev/null +++ b/gnovm/tests/files/const50.gno @@ -0,0 +1,12 @@ +package main + +var x = "a" + +const v = len(x) + +func main() { + println("ok") +} + +// Error: +// main/files/const50.gno:5:7: x (variable of type string) is not constant From bd1d104e7bd88ab8980ad2d91bac65b7eaffc354 Mon Sep 17 00:00:00 2001 From: Ursulovic <97893481+Ursulovic@users.noreply.github.com> Date: Wed, 15 Jan 2025 00:08:43 +0100 Subject: [PATCH 11/18] feat(examples): Ivan's registry, home realm (#3354) Created registry for further development of realms, finalising home realm. --------- Co-authored-by: Ivan Ursulovic Co-authored-by: Ivan Ursulovic Co-authored-by: Morgan --- examples/gno.land/r/ursulovic/home/gno.mod | 1 + examples/gno.land/r/ursulovic/home/home.gno | 159 ++++++++++++++++++ .../gno.land/r/ursulovic/home/home_test.gno | 97 +++++++++++ .../gno.land/r/ursulovic/registry/gno.mod | 1 + .../r/ursulovic/registry/registry.gno | 59 +++++++ 5 files changed, 317 insertions(+) create mode 100644 examples/gno.land/r/ursulovic/home/gno.mod create mode 100644 examples/gno.land/r/ursulovic/home/home.gno create mode 100644 examples/gno.land/r/ursulovic/home/home_test.gno create mode 100644 examples/gno.land/r/ursulovic/registry/gno.mod create mode 100644 examples/gno.land/r/ursulovic/registry/registry.gno diff --git a/examples/gno.land/r/ursulovic/home/gno.mod b/examples/gno.land/r/ursulovic/home/gno.mod new file mode 100644 index 00000000000..78163ab2bb5 --- /dev/null +++ b/examples/gno.land/r/ursulovic/home/gno.mod @@ -0,0 +1 @@ +module gno.land/r/ursulovic/home diff --git a/examples/gno.land/r/ursulovic/home/home.gno b/examples/gno.land/r/ursulovic/home/home.gno new file mode 100644 index 00000000000..c03d8a66868 --- /dev/null +++ b/examples/gno.land/r/ursulovic/home/home.gno @@ -0,0 +1,159 @@ +package home + +import ( + "std" + "strconv" + "strings" + + "gno.land/p/demo/ownable" + "gno.land/p/moul/md" + "gno.land/r/leon/hof" + + "gno.land/r/ursulovic/registry" +) + +var ( + aboutMe string + selectedImage string + Ownable *ownable.Ownable + + githubUrl string + linkedinUrl string + connectUrl string + imageUpdatePrice int64 + + isValidUrl func(string) bool +) + +func init() { + Ownable = ownable.NewWithAddress(registry.MainAddress()) + + aboutMe = "Hi, I'm Ivan Ursulovic, a computer engineering graduate, blockchain enthusiast, and backend developer specializing in ASP.NET. I love learning new things and taking on challenges." + selectedImage = "https://i.ibb.co/W28NPkw/beograd.webp" + + githubUrl = "https://github.com/ursulovic" + linkedinUrl = "https://www.linkedin.com/in/ivan-ursulovic-953310190/" + imageUpdatePrice = 5000000 + isValidUrl = defaultURLValidation + hof.Register() +} + +func Render(s string) string { + var sb strings.Builder + sb.WriteString(renderAboutMe()) + sb.WriteString(renderSelectedImage()) + sb.WriteString(renderContactsUrl()) + return sb.String() +} + +func defaultURLValidation(url string) bool { + const urlPrefix string = "https://i.ibb.co/" + + if !strings.HasPrefix(url, urlPrefix) { + return false + } + + if !(strings.HasSuffix(url, ".jpg") || + strings.HasSuffix(url, ".png") || + strings.HasSuffix(url, ".gif") || + strings.HasSuffix(url, ".webp")) { + return false + } + + urlPath := strings.TrimPrefix(url, "https://i.ibb.co/") + parts := strings.Split(urlPath, "/") + + if len(parts) != 2 || len(parts[0]) == 0 || len(parts[1]) == 0 { + return false + } + + return true +} + +func UpdateSelectedImage(url string) { + if !isValidUrl(url) { + panic("Url is not valid!") + } + + sentCoins := std.GetOrigSend() + + if len(sentCoins) != 1 && sentCoins.AmountOf("ugnot") == imageUpdatePrice { + panic("Please send exactly " + strconv.Itoa(int(imageUpdatePrice)) + " ugnot") + } + + selectedImage = url +} + +func renderSelectedImage() string { + var sb strings.Builder + + sb.WriteString(md.HorizontalRule()) + sb.WriteString("\n") + + sb.WriteString(md.H2("📸 Featured Image")) + sb.WriteString("\n") + + sb.WriteString(md.Image("", selectedImage)) + sb.WriteString("\n") + + sb.WriteString(md.H4("✨ " + md.Link("Change this image for "+strconv.Itoa(int(imageUpdatePrice/1000000))+" GNOT. To update, set a direct image URL from ImgBB.", "https://gno.studio/connect/view/gno.land/r/ursulovic/home?network=portal-loop") + " ✨")) + + return sb.String() +} + +func renderAboutMe() string { + var sb strings.Builder + + sb.WriteString(md.H1("👋 Welcome to Ivan's Homepage!")) + sb.WriteString("\n") + + sb.WriteString(md.H2("👨‍💻 About Me")) + sb.WriteString("\n") + + sb.WriteString(md.Blockquote(aboutMe)) + + return sb.String() +} + +func renderContactsUrl() string { + var sb strings.Builder + + sb.WriteString(md.HorizontalRule()) + sb.WriteString("\n") + + sb.WriteString(md.H2("🔗 Let's Connect")) + sb.WriteString("\n") + + items := []string{ + "🐙 " + md.Link("GitHub", githubUrl), + "💼 " + md.Link("LinkedIn", linkedinUrl), + } + sb.WriteString(md.BulletList(items)) + + return sb.String() +} + +func UpdateGithubUrl(url string) { + Ownable.AssertCallerIsOwner() + githubUrl = url +} + +func UpdateLinkedinUrl(url string) { + Ownable.AssertCallerIsOwner() + linkedinUrl = url +} + +func UpdateAboutMe(text string) { + Ownable.AssertCallerIsOwner() + aboutMe = text +} + +func UpdateImagePrice(newPrice int64) { + Ownable.AssertCallerIsOwner() + imageUpdatePrice = newPrice +} + +func UpdateIsValidUrlFunction(f func(string) bool) { + Ownable.AssertCallerIsOwner() + isValidUrl = f +} diff --git a/examples/gno.land/r/ursulovic/home/home_test.gno b/examples/gno.land/r/ursulovic/home/home_test.gno new file mode 100644 index 00000000000..ff3f763d62a --- /dev/null +++ b/examples/gno.land/r/ursulovic/home/home_test.gno @@ -0,0 +1,97 @@ +package home + +import ( + "std" + "testing" + + "gno.land/p/demo/testutils" +) + +func TestUpdateGithubUrl(t *testing.T) { + caller := std.Address("g1d24j8fwnc0w5q427fauyey4gdd30qgu69k6n0x") + std.TestSetOrigCaller(caller) + + newUrl := "https://github.com/example" + + UpdateGithubUrl(newUrl) + + if githubUrl != newUrl { + t.Fatalf("GitHub url not updated properly!") + } +} + +func TestUpdateLinkedinUrl(t *testing.T) { + caller := std.Address("g1d24j8fwnc0w5q427fauyey4gdd30qgu69k6n0x") + std.TestSetOrigCaller(caller) + + newUrl := "https://www.linkedin.com/in/example" + + UpdateGithubUrl(newUrl) + + if githubUrl != newUrl { + t.Fatalf("LinkedIn url not updated properly!") + } +} + +func TestUpdateAboutMe(t *testing.T) { + caller := std.Address("g1d24j8fwnc0w5q427fauyey4gdd30qgu69k6n0x") + std.TestSetOrigCaller(caller) + + newAboutMe := "This is new description!" + + UpdateAboutMe(newAboutMe) + + if aboutMe != newAboutMe { + t.Fatalf("About mew not updated properly!") + } +} + +func TestUpdateSelectedImage(t *testing.T) { + var user = testutils.TestAddress("user") + std.TestSetOrigCaller(user) + + validImageUrl := "https://i.ibb.co/hLtmnX0/beautiful-rain-forest-ang-ka-nature-trail-doi-inthanon-national-park-thailand-36703721.webp" + + coinsSent := std.NewCoins(std.NewCoin("ugnot", 5000000)) // Update to match the price expected by your function + std.TestSetOrigSend(coinsSent, std.NewCoins()) + + UpdateSelectedImage(validImageUrl) + + if selectedImage != validImageUrl { + t.Fatalf("Valid image URL rejected!") + } + + invalidImageUrl := "https://ibb.co/Kb3rQNn" + + defer func() { + if r := recover(); r == nil { + t.Fatalf("Expected panic for invalid image URL, but got no panic") + } + }() + + UpdateSelectedImage(invalidImageUrl) + + invalidCoins := std.NewCoins(std.NewCoin("ugnot", 1000000)) + std.TestSetOrigSend(invalidCoins, std.NewCoins()) + + defer func() { + if r := recover(); r == nil { + t.Fatalf("Expected panic for incorrect coin denomination or amount, but got no panic") + } + }() + + UpdateSelectedImage(validImageUrl) +} + +func TestUpdateImagePrice(t *testing.T) { + caller := std.Address("g1d24j8fwnc0w5q427fauyey4gdd30qgu69k6n0x") + std.TestSetOrigCaller(caller) + + var newImageUpdatePrice int64 = 3000000 + + UpdateImagePrice(newImageUpdatePrice) + + if imageUpdatePrice != newImageUpdatePrice { + t.Fatalf("Image update price not updated properly!") + } +} diff --git a/examples/gno.land/r/ursulovic/registry/gno.mod b/examples/gno.land/r/ursulovic/registry/gno.mod new file mode 100644 index 00000000000..ee1f5d38780 --- /dev/null +++ b/examples/gno.land/r/ursulovic/registry/gno.mod @@ -0,0 +1 @@ +module gno.land/r/ursulovic/registry diff --git a/examples/gno.land/r/ursulovic/registry/registry.gno b/examples/gno.land/r/ursulovic/registry/registry.gno new file mode 100644 index 00000000000..0bbd6c80df5 --- /dev/null +++ b/examples/gno.land/r/ursulovic/registry/registry.gno @@ -0,0 +1,59 @@ +package registry + +import ( + "errors" + "std" +) + +var ( + mainAddress std.Address + backupAddress std.Address + + ErrInvalidAddr = errors.New("Ivan's registry: Invalid address") + ErrUnauthorized = errors.New("Ivan's registry: Unauthorized") +) + +func init() { + mainAddress = "g1d24j8fwnc0w5q427fauyey4gdd30qgu69k6n0x" + backupAddress = "g1mw2xft3eava9kfhqw3fjj3kkf3pkammty0mtv7" +} + +func MainAddress() std.Address { + return mainAddress +} + +func BackupAddress() std.Address { + return backupAddress +} + +func SetMainAddress(addr std.Address) error { + assertAuthorized() + + if !addr.IsValid() { + return ErrInvalidAddr + } + + mainAddress = addr + return nil +} + +func SetBackupAddress(addr std.Address) error { + assertAuthorized() + + if !addr.IsValid() { + return ErrInvalidAddr + } + + backupAddress = addr + return nil +} + +// It will stay here for now, might be useful later +func assertAuthorized() { + caller := std.PrevRealm().Addr() + isAuthorized := caller == mainAddress || caller == backupAddress + + if !isAuthorized { + panic(ErrUnauthorized) + } +} From 90ff3e440446a2604c32d185f0bb63d31c499a51 Mon Sep 17 00:00:00 2001 From: 6h057 <15034695+omarsy@users.noreply.github.com> Date: Wed, 15 Jan 2025 10:19:20 +0100 Subject: [PATCH 12/18] fix(gnovm): save object when refCount changed (#2992) This PR a fix in the gnovm to ensure that objects are saved correctly when their reference count changes. closes: #2266 #1543
Contributors' checklist... - [ ] Added new tests, or not needed, or not feasible - [ ] Provided an example (e.g. screenshot) to aid review or the PR is self-explanatory - [ ] Updated the official documentation or not needed - [ ] No breaking changes were made, or a `BREAKING CHANGE: xxx` message was included in the description - [ ] Added references to related issues and PRs - [ ] Provided any useful hints for running manual tests
--- contribs/gnodev/pkg/dev/node_test.go | 2 +- examples/gno.land/p/demo/avl/z_0_filetest.gno | 113 - examples/gno.land/p/demo/avl/z_1_filetest.gno | 161 +- examples/gno.land/p/demo/avl/z_2_filetest.gno | 50 +- .../gno.land/r/demo/boards/z_4_filetest.gno | 19 + .../testdata/addpkg_namespace.txtar | 2 +- .../pkg/integration/testdata/ghverify.txtar | 2 +- .../pkg/integration/testdata/issue_1543.txtar | 41 + .../pkg/integration/testdata/issue_2266.txtar | 42 + .../integration/testdata/simulate_gas.txtar | 4 +- gno.land/pkg/sdk/vm/gas_test.go | 4 +- .../cmd/gno/testdata/test/realm_correct.txtar | 67 +- .../gno/testdata/test/realm_incorrect.txtar | 2 +- gnovm/cmd/gno/testdata/test/realm_sync.txtar | 65 - gnovm/pkg/gnolang/realm.go | 8 +- gnovm/pkg/gnolang/store.go | 4 +- .../more/realm_compositelit_filetest.gno | 146 -- gnovm/tests/files/heap_item_value.gno | 125 - gnovm/tests/files/heap_item_value_init.gno | 168 +- gnovm/tests/files/zrealm0.gno | 65 - gnovm/tests/files/zrealm1.gno | 156 -- gnovm/tests/files/zrealm13.gno | 77 + gnovm/tests/files/zrealm14.gno | 168 ++ gnovm/tests/files/zrealm15.gno | 178 ++ gnovm/tests/files/zrealm16.gno | 115 + gnovm/tests/files/zrealm2.gno | 192 -- gnovm/tests/files/zrealm3.gno | 186 -- gnovm/tests/files/zrealm4.gno | 120 +- gnovm/tests/files/zrealm5.gno | 120 +- gnovm/tests/files/zrealm6.gno | 141 +- gnovm/tests/files/zrealm7.gno | 236 +- gnovm/tests/files/zrealm_avl0.gno | 113 - gnovm/tests/files/zrealm_avl1.gno | 161 +- gnovm/tests/files/zrealm_crossrealm21.gno | 672 +---- gnovm/tests/files/zrealm_crossrealm22.gno | 2176 +---------------- gnovm/tests/files/zrealm_natbind0.gno | 150 -- gnovm/tests/files/zrealm_tests0.gno | 1665 +------------ 37 files changed, 1002 insertions(+), 6714 deletions(-) create mode 100644 gno.land/pkg/integration/testdata/issue_1543.txtar create mode 100644 gno.land/pkg/integration/testdata/issue_2266.txtar create mode 100644 gnovm/tests/files/zrealm13.gno create mode 100644 gnovm/tests/files/zrealm14.gno create mode 100644 gnovm/tests/files/zrealm15.gno create mode 100644 gnovm/tests/files/zrealm16.gno diff --git a/contribs/gnodev/pkg/dev/node_test.go b/contribs/gnodev/pkg/dev/node_test.go index 4a4acc232b9..38fab0a3360 100644 --- a/contribs/gnodev/pkg/dev/node_test.go +++ b/contribs/gnodev/pkg/dev/node_test.go @@ -438,7 +438,7 @@ func testingCallRealm(t *testing.T, node *Node, msgs ...vm.MsgCall) (*core_types txcfg := gnoclient.BaseTxCfg{ GasFee: ugnot.ValueString(1000000), // Gas fee - GasWanted: 2_000_000, // Gas wanted + GasWanted: 3_000_000, // Gas wanted } // Set Caller in the msgs diff --git a/examples/gno.land/p/demo/avl/z_0_filetest.gno b/examples/gno.land/p/demo/avl/z_0_filetest.gno index 2dce5e7f1ac..1db1adebd3e 100644 --- a/examples/gno.land/p/demo/avl/z_0_filetest.gno +++ b/examples/gno.land/p/demo/avl/z_0_filetest.gno @@ -215,116 +215,3 @@ func main() { // } // } // } -// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ -// "Blank": {}, -// "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", -// "IsEscaped": true, -// "ModTime": "5", -// "RefCount": "2" -// }, -// "Parent": null, -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "0", -// "File": "", -// "Line": "0", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Values": [ -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": { -// "@type": "/gno.RefValue", -// "Hash": "ae86874f9b47fa5e64c30b3e92e9d07f2ec967a4", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" -// }, -// "Index": "0", -// "TV": null -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" -// }, -// "FileName": "z_0.gno", -// "IsMethod": false, -// "Name": "init.1", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/test", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "z_0.gno", -// "Line": "10", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" -// }, -// "FileName": "z_0.gno", -// "IsMethod": false, -// "Name": "main", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/test", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "z_0.gno", -// "Line": "15", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// } -// ] -// } diff --git a/examples/gno.land/p/demo/avl/z_1_filetest.gno b/examples/gno.land/p/demo/avl/z_1_filetest.gno index 97ca5ed2135..572c49333bc 100644 --- a/examples/gno.land/p/demo/avl/z_1_filetest.gno +++ b/examples/gno.land/p/demo/avl/z_1_filetest.gno @@ -24,6 +24,44 @@ func main() { // Realm: // switchrealm["gno.land/r/test"] +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:6]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6", +// "ModTime": "11", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", +// "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "1375f6f96a1a3f298347dc8fc0065afa36cb7f0f", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7" +// } +// } +// } +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:8]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8", +// "ModTime": "13", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", +// "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "b28057ab7be6383785c0a5503e8a531bdbc21851", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9" +// } +// } +// } // c[a8ada09dee16d791fd406d629fe29bb0ed084a30:15]={ // "Fields": [ // { @@ -143,7 +181,7 @@ func main() { // "@type": "/gno.PointerValue", // "Base": { // "@type": "/gno.RefValue", -// "Hash": "2f3adc5d0f2a3fe0331cfa93572a7abdde14c9aa", +// "Hash": "cafae89e4d4aaaefe7fdf0691084508d4274a981", // "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8" // }, // "Index": "0", @@ -191,7 +229,7 @@ func main() { // }, // "V": { // "@type": "/gno.RefValue", -// "Hash": "fe20a19f956511f274dc77854e9e5468387260f4", +// "Hash": "b2e446f490656c19a83c43055de29c96e92a1549", // "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:13" // } // } @@ -235,7 +273,7 @@ func main() { // "@type": "/gno.PointerValue", // "Base": { // "@type": "/gno.RefValue", -// "Hash": "c89a71bdf045e8bde2059dc9d33839f916e02e5d", +// "Hash": "4e56eeb96eb1d9b27cf603140cd03a1622b6358b", // "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" // }, // "Index": "0", @@ -254,7 +292,7 @@ func main() { // "@type": "/gno.PointerValue", // "Base": { // "@type": "/gno.RefValue", -// "Hash": "90fa67f8c47db4b9b2a60425dff08d5a3385100f", +// "Hash": "7b61530859954d1d14b2f696c91c5f37d39c21e7", // "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:12" // }, // "Index": "0", @@ -283,123 +321,10 @@ func main() { // }, // "V": { // "@type": "/gno.RefValue", -// "Hash": "83e42caaf53070dd95b5f859053eb51ed900bbda", +// "Hash": "fedc6d430b38c985dc6a985b2fcaee97e88ba6da", // "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:11" // } // } // } -// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ -// "Blank": {}, -// "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", -// "IsEscaped": true, -// "ModTime": "9", -// "RefCount": "2" -// }, -// "Parent": null, -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "0", -// "File": "", -// "Line": "0", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Values": [ -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": { -// "@type": "/gno.RefValue", -// "Hash": "1faa9fa4ba1935121a6d3f0a623772e9d4499b0a", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:10" -// }, -// "Index": "0", -// "TV": null -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" -// }, -// "FileName": "z_1.gno", -// "IsMethod": false, -// "Name": "init.1", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/test", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "z_1.gno", -// "Line": "10", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" -// }, -// "FileName": "z_1.gno", -// "IsMethod": false, -// "Name": "main", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/test", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "z_1.gno", -// "Line": "15", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// } -// ] -// } // d[a8ada09dee16d791fd406d629fe29bb0ed084a30:4] // d[a8ada09dee16d791fd406d629fe29bb0ed084a30:5] diff --git a/examples/gno.land/p/demo/avl/z_2_filetest.gno b/examples/gno.land/p/demo/avl/z_2_filetest.gno index 43067c31e8f..c45088075d6 100644 --- a/examples/gno.land/p/demo/avl/z_2_filetest.gno +++ b/examples/gno.land/p/demo/avl/z_2_filetest.gno @@ -23,6 +23,44 @@ func main() { // Realm: // switchrealm["gno.land/r/test"] +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:7]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", +// "ModTime": "12", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6", +// "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "ba7550123807b8da857e38b72f66204b1ec582a2", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8" +// } +// } +// } +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:9]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9", +// "ModTime": "14", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6", +// "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "3cb8485664c356fcb5c88dfb96b7455133a6b022", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:10" +// } +// } +// } // c[a8ada09dee16d791fd406d629fe29bb0ed084a30:16]={ // "Fields": [ // { @@ -142,7 +180,7 @@ func main() { // "@type": "/gno.PointerValue", // "Base": { // "@type": "/gno.RefValue", -// "Hash": "849a50d6c78d65742752e3c89ad8dd556e2e63cb", +// "Hash": "db39c9c0a60e0d5b30dbaf9be6150d3fec16aa4b", // "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9" // }, // "Index": "0", @@ -190,7 +228,7 @@ func main() { // }, // "V": { // "@type": "/gno.RefValue", -// "Hash": "a1160b0060ad752dbfe5fe436f7734bb19136150", +// "Hash": "2e9127534f91b385426d76e8e164f50f635cc1de", // "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:14" // } // } @@ -234,7 +272,7 @@ func main() { // "@type": "/gno.PointerValue", // "Base": { // "@type": "/gno.RefValue", -// "Hash": "fd95e08763159ac529e26986d652e752e78b6325", +// "Hash": "43e03b0c877b40c34e12bc2b15560e8ecd42ae9d", // "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7" // }, // "Index": "0", @@ -253,7 +291,7 @@ func main() { // "@type": "/gno.PointerValue", // "Base": { // "@type": "/gno.RefValue", -// "Hash": "3ecdcf148fe2f9e97b72a3bedf303b2ba56d4f4b", +// "Hash": "4b123e2424d900a427f9dee88a70ce61f3cdcf5b", // "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:13" // }, // "Index": "0", @@ -282,7 +320,7 @@ func main() { // }, // "V": { // "@type": "/gno.RefValue", -// "Hash": "63126557dba88f8556f7a0ccbbfc1d218ae7a302", +// "Hash": "76d9227e755efd6674d8fa34e12decb7a9855488", // "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:12" // } // } @@ -301,7 +339,7 @@ func main() { // "@type": "/gno.PointerValue", // "Base": { // "@type": "/gno.RefValue", -// "Hash": "d31c7e797793e03ffe0bbcb72f963264f8300d22", +// "Hash": "ff46b4dd63457c3fd59801e725f65af524ec829d", // "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:11" // }, // "Index": "0", diff --git a/examples/gno.land/r/demo/boards/z_4_filetest.gno b/examples/gno.land/r/demo/boards/z_4_filetest.gno index c6cf6397b3a..b781e94e4db 100644 --- a/examples/gno.land/r/demo/boards/z_4_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_4_filetest.gno @@ -885,6 +885,25 @@ func main() { // "RefCount": "1" // } // } +// u[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:84]={ +// "ObjectInfo": { +// "ID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:84", +// "IsEscaped": true, +// "ModTime": "127", +// "RefCount": "6" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/demo/boards.Board" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "a88a9b837af217656ee27084309f7cd02cd94cb3", +// "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:85" +// } +// } +// } // switchrealm["gno.land/r/demo/boards"] // switchrealm["gno.land/r/demo/users"] // switchrealm["gno.land/r/demo/users"] diff --git a/gno.land/pkg/integration/testdata/addpkg_namespace.txtar b/gno.land/pkg/integration/testdata/addpkg_namespace.txtar index f529c176f36..2cfd00acda4 100644 --- a/gno.land/pkg/integration/testdata/addpkg_namespace.txtar +++ b/gno.land/pkg/integration/testdata/addpkg_namespace.txtar @@ -73,7 +73,7 @@ stdout 'OK!' # Test gui publishing on guiland/one # gui addpkg -> gno.land/r/guiland/one -gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/guiland/one -gas-fee 1000000ugnot -gas-wanted 1700000 -broadcast -chainid=tendermint_test gui +gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/guiland/one -gas-fee 1000000ugnot -gas-wanted 1800000 -broadcast -chainid=tendermint_test gui stdout 'OK!' # Test admin publishing on guiland/two diff --git a/gno.land/pkg/integration/testdata/ghverify.txtar b/gno.land/pkg/integration/testdata/ghverify.txtar index b53849e85b5..0f2d21f6bd5 100644 --- a/gno.land/pkg/integration/testdata/ghverify.txtar +++ b/gno.land/pkg/integration/testdata/ghverify.txtar @@ -25,7 +25,7 @@ stdout "" stderr 'invalid ingest id: a' # the agent publishes their response to the task and the verification is complete -gnokey maketx call -pkgpath gno.land/r/gnoland/ghverify -func GnorkleEntrypoint -args 'ingest,g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5,OK' -gas-fee 1000000ugnot -gas-wanted 5000000 -broadcast -chainid=tendermint_test test1 +gnokey maketx call -pkgpath gno.land/r/gnoland/ghverify -func GnorkleEntrypoint -args 'ingest,g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5,OK' -gas-fee 1000000ugnot -gas-wanted 6000000 -broadcast -chainid=tendermint_test test1 stdout OK! # get verified github handle by gno address diff --git a/gno.land/pkg/integration/testdata/issue_1543.txtar b/gno.land/pkg/integration/testdata/issue_1543.txtar new file mode 100644 index 00000000000..388f126fcda --- /dev/null +++ b/gno.land/pkg/integration/testdata/issue_1543.txtar @@ -0,0 +1,41 @@ +# test issue + +loadpkg gno.land/r/demo/realm $WORK + +# start a new node +gnoland start + + +gnokey maketx call -pkgpath gno.land/r/demo/realm --func Fill --args 0 --gas-fee 1000000ugnot --gas-wanted 2000000 --broadcast -chainid=tendermint_test test1 +gnokey maketx call -pkgpath gno.land/r/demo/realm --func UnFill --args 0 --gas-fee 1000000ugnot --gas-wanted 2000000 --broadcast -chainid=tendermint_test test1 +gnokey maketx call -pkgpath gno.land/r/demo/realm --func Fill --args 0 --gas-fee 1000000ugnot --gas-wanted 2000000 --broadcast -chainid=tendermint_test test1 + + +-- realm.gno -- +package main + +type A struct { + A string +} +type B struct { + A *A + B string +} + +var ( + a = &A{A: "here"} + b [2]*B +) + +func Fill(i int) { + c := B{ + A: a, + B: "", + } + b[i] = &c +} + +func UnFill(i int) { + b[i] = nil +} + diff --git a/gno.land/pkg/integration/testdata/issue_2266.txtar b/gno.land/pkg/integration/testdata/issue_2266.txtar new file mode 100644 index 00000000000..046f57802e3 --- /dev/null +++ b/gno.land/pkg/integration/testdata/issue_2266.txtar @@ -0,0 +1,42 @@ +# test issue + +loadpkg gno.land/r/demo/realm $WORK + +# start a new node +gnoland start + + +gnokey maketx call -pkgpath gno.land/r/demo/realm --func Fill --args 0 --gas-fee 1000000ugnot --gas-wanted 2000000 --broadcast -chainid=tendermint_test test1 +gnokey maketx call -pkgpath gno.land/r/demo/realm --func Fill --args 1 --gas-fee 1000000ugnot --gas-wanted 2000000 --broadcast -chainid=tendermint_test test1 +gnokey maketx call -pkgpath gno.land/r/demo/realm --func UnFill --gas-fee 1000000ugnot --gas-wanted 2000000 --broadcast -chainid=tendermint_test test1 + + +-- realm.gno -- +package main + +type A struct { + A string +} +type B struct { + A *A + B string +} + +var ( + a = &A{A: "here"} + b [2]*B +) + +func Fill(i int) { + c := B{ + A: a, + B: "", + } + b[i] = &c +} + +func UnFill() { + b[0] = nil + b[1] = nil +} + diff --git a/gno.land/pkg/integration/testdata/simulate_gas.txtar b/gno.land/pkg/integration/testdata/simulate_gas.txtar index 57be82b75ff..0dcb9ba424b 100644 --- a/gno.land/pkg/integration/testdata/simulate_gas.txtar +++ b/gno.land/pkg/integration/testdata/simulate_gas.txtar @@ -6,11 +6,11 @@ gnoland start # simulate only gnokey maketx call -pkgpath gno.land/r/simulate -func Hello -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test -simulate only test1 -stdout 'GAS USED: 99339' +stdout 'GAS USED: 99371' # simulate skip gnokey maketx call -pkgpath gno.land/r/simulate -func Hello -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test -simulate skip test1 -stdout 'GAS USED: 99339' # same as simulate only +stdout 'GAS USED: 99371' # same as simulate only -- package/package.gno -- diff --git a/gno.land/pkg/sdk/vm/gas_test.go b/gno.land/pkg/sdk/vm/gas_test.go index 276aa9db0b0..acde3d315c6 100644 --- a/gno.land/pkg/sdk/vm/gas_test.go +++ b/gno.land/pkg/sdk/vm/gas_test.go @@ -74,8 +74,8 @@ func TestAddPkgDeliverTx(t *testing.T) { assert.True(t, res.IsOK()) - // NOTE: let's try to keep this bellow 100_000 :) - assert.Equal(t, int64(135365), gasDeliver) + // NOTE: let's try to keep this bellow 150_000 :) + assert.Equal(t, int64(143845), gasDeliver) } // Enough gas for a failed transaction. diff --git a/gnovm/cmd/gno/testdata/test/realm_correct.txtar b/gnovm/cmd/gno/testdata/test/realm_correct.txtar index ced183bec67..ae1212133fd 100644 --- a/gnovm/cmd/gno/testdata/test/realm_correct.txtar +++ b/gnovm/cmd/gno/testdata/test/realm_correct.txtar @@ -18,69 +18,4 @@ func main() { } // Realm: -// switchrealm["gno.land/r/xx"] -// u[aea84df38908f9569d0f552575606e6e6e7e22dd:2]={ -// "Blank": {}, -// "ObjectInfo": { -// "ID": "aea84df38908f9569d0f552575606e6e6e7e22dd:2", -// "IsEscaped": true, -// "ModTime": "3", -// "RefCount": "2" -// }, -// "Parent": null, -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "0", -// "File": "", -// "Line": "0", -// "PkgPath": "gno.land/r/xx" -// } -// }, -// "Values": [ -// { -// "N": "AQAAAAAAAAA=", -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "32" -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "aea84df38908f9569d0f552575606e6e6e7e22dd:3" -// }, -// "FileName": "x.gno", -// "IsMethod": false, -// "Name": "main", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/xx", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "x.gno", -// "Line": "6", -// "PkgPath": "gno.land/r/xx" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// } -// ] -// } +// switchrealm["gno.land/r/xx"] \ No newline at end of file diff --git a/gnovm/cmd/gno/testdata/test/realm_incorrect.txtar b/gnovm/cmd/gno/testdata/test/realm_incorrect.txtar index 234d0f81e77..84f4e3438ee 100644 --- a/gnovm/cmd/gno/testdata/test/realm_incorrect.txtar +++ b/gnovm/cmd/gno/testdata/test/realm_incorrect.txtar @@ -7,7 +7,7 @@ stderr '=== RUN file/x_filetest.gno' stderr 'Realm diff:' stderr '--- Expected' stderr '\+\+\+ Actual' -stderr '@@ -1,2 \+1,67 @@' +stderr '@@ -1,2 \+1,2 @@' stderr '-xxx' stderr '\+switchrealm\["gno.land/r/xx"\]' stderr 'x_filetest.gno failed' diff --git a/gnovm/cmd/gno/testdata/test/realm_sync.txtar b/gnovm/cmd/gno/testdata/test/realm_sync.txtar index c93e6d86e8f..65a930b2f03 100644 --- a/gnovm/cmd/gno/testdata/test/realm_sync.txtar +++ b/gnovm/cmd/gno/testdata/test/realm_sync.txtar @@ -33,68 +33,3 @@ func main() { // Realm: // switchrealm["gno.land/r/xx"] -// u[aea84df38908f9569d0f552575606e6e6e7e22dd:2]={ -// "Blank": {}, -// "ObjectInfo": { -// "ID": "aea84df38908f9569d0f552575606e6e6e7e22dd:2", -// "IsEscaped": true, -// "ModTime": "3", -// "RefCount": "2" -// }, -// "Parent": null, -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "0", -// "File": "", -// "Line": "0", -// "PkgPath": "gno.land/r/xx" -// } -// }, -// "Values": [ -// { -// "N": "AQAAAAAAAAA=", -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "32" -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "aea84df38908f9569d0f552575606e6e6e7e22dd:3" -// }, -// "FileName": "x.gno", -// "IsMethod": false, -// "Name": "main", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/xx", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "x.gno", -// "Line": "6", -// "PkgPath": "gno.land/r/xx" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// } -// ] -// } diff --git a/gnovm/pkg/gnolang/realm.go b/gnovm/pkg/gnolang/realm.go index 04de760037a..509fcd67a60 100644 --- a/gnovm/pkg/gnolang/realm.go +++ b/gnovm/pkg/gnolang/realm.go @@ -192,6 +192,9 @@ func (rlm *Realm) DidUpdate(po, xo, co Object) { if co != nil { co.IncRefCount() if co.GetRefCount() > 1 { + if co.GetIsReal() { + rlm.MarkDirty(co) + } if co.GetIsEscaped() { // already escaped } else { @@ -211,6 +214,8 @@ func (rlm *Realm) DidUpdate(po, xo, co Object) { if xo.GetIsReal() { rlm.MarkNewDeleted(xo) } + } else if xo.GetIsReal() { + rlm.MarkDirty(xo) } } } @@ -464,6 +469,7 @@ func (rlm *Realm) incRefCreatedDescendants(store Store, oo Object) { child.SetIsNewReal(true) } } else if rc > 1 { + rlm.MarkDirty(child) if child.GetIsEscaped() { // already escaped, do nothing. } else { @@ -537,7 +543,7 @@ func (rlm *Realm) decRefDeletedDescendants(store Store, oo Object) { if rc == 0 { rlm.decRefDeletedDescendants(store, child) } else if rc > 0 { - // do nothing + rlm.MarkDirty(child) } else { panic("deleted descendants should not have a reference count of less than zero") } diff --git a/gnovm/pkg/gnolang/store.go b/gnovm/pkg/gnolang/store.go index bc56a7c6313..3a70d07381b 100644 --- a/gnovm/pkg/gnolang/store.go +++ b/gnovm/pkg/gnolang/store.go @@ -519,7 +519,7 @@ func (ds *defaultStore) SetObject(oo Object) { } ds.cacheObjects[oid] = oo // make store op log entry - if ds.opslog != nil { + if _, ok := oo.(*Block); !ok && ds.opslog != nil { var op StoreOpType if oo.GetIsNewReal() { op = StoreOpNew @@ -560,7 +560,7 @@ func (ds *defaultStore) DelObject(oo Object) { ds.baseStore.Delete([]byte(key)) } // make realm op log entry - if ds.opslog != nil { + if _, ok := oo.(*Block); !ok && ds.opslog != nil { ds.opslog = append(ds.opslog, StoreOp{Type: StoreOpDel, Object: oo}) } 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 45f83bade5f..8514c2676aa 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 @@ -89,149 +89,3 @@ func main() { // } // } // } -// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ -// "Blank": {}, -// "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", -// "IsEscaped": true, -// "ModTime": "3", -// "RefCount": "2" -// }, -// "Parent": null, -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "0", -// "File": "", -// "Line": "0", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Values": [ -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.PrimitiveType", -// "value": "2048" -// }, -// "Methods": [], -// "Name": "word", -// "PkgPath": "gno.land/r/test" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.SliceType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/test.word" -// }, -// "Vrd": false -// }, -// "Methods": [], -// "Name": "nat", -// "PkgPath": "gno.land/r/test" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/test.Int" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": { -// "@type": "/gno.RefValue", -// "Hash": "3c89d875f7d6daa94113aa4c7e03432ba56202c2", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" -// }, -// "Index": "0", -// "TV": null -// } -// }, -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.StructType", -// "Fields": [ -// { -// "Embedded": false, -// "Name": "abs", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/test.nat" -// } -// } -// ], -// "PkgPath": "gno.land/r/test" -// }, -// "Methods": [], -// "Name": "Int", -// "PkgPath": "gno.land/r/test" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" -// }, -// "FileName": "files/assign_unnamed_type/more/realm_compositelit.gno", -// "IsMethod": false, -// "Name": "main", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/test", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "files/assign_unnamed_type/more/realm_compositelit.gno", -// "Line": "16", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// } -// ] -// } diff --git a/gnovm/tests/files/heap_item_value.gno b/gnovm/tests/files/heap_item_value.gno index 80bf702bec2..7a728d2c6f9 100644 --- a/gnovm/tests/files/heap_item_value.gno +++ b/gnovm/tests/files/heap_item_value.gno @@ -51,128 +51,3 @@ func main() { // } // } // } -// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ -// "Blank": {}, -// "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", -// "IsEscaped": true, -// "ModTime": "3", -// "RefCount": "2" -// }, -// "Parent": null, -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "0", -// "File": "", -// "Line": "0", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Values": [ -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.StructType", -// "Fields": [ -// { -// "Embedded": false, -// "Name": "A", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "32" -// } -// } -// ], -// "PkgPath": "gno.land/r/test" -// }, -// "Methods": [], -// "Name": "S", -// "PkgPath": "gno.land/r/test" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/test.S" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" -// }, -// "Index": "0", -// "TV": null -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/test.S" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" -// }, -// "Index": "0", -// "TV": null -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" -// }, -// "FileName": "files/heap_item_value.gno", -// "IsMethod": false, -// "Name": "main", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/test", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "files/heap_item_value.gno", -// "Line": "10", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// } -// ] -// } diff --git a/gnovm/tests/files/heap_item_value_init.gno b/gnovm/tests/files/heap_item_value_init.gno index 2722cce8675..6be9caed1f4 100644 --- a/gnovm/tests/files/heap_item_value_init.gno +++ b/gnovm/tests/files/heap_item_value_init.gno @@ -19,167 +19,23 @@ func main() { // Realm: // switchrealm["gno.land/r/test"] -// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ -// "Blank": {}, +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:4]={ // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", // "IsEscaped": true, // "ModTime": "6", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", // "RefCount": "2" // }, -// "Parent": null, -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "0", -// "File": "", -// "Line": "0", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Values": [ -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.StructType", -// "Fields": [ -// { -// "Embedded": false, -// "Name": "A", -// "Tag": "", -// "Type": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.PrimitiveType", -// "value": "32" -// } -// } -// } -// ], -// "PkgPath": "gno.land/r/test" -// }, -// "Methods": [], -// "Name": "S", -// "PkgPath": "gno.land/r/test" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/test.S" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" -// }, -// "Index": "0", -// "TV": null -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/test.S" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" -// }, -// "Index": "0", -// "TV": null -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" -// }, -// "FileName": "files/heap_item_value_init.gno", -// "IsMethod": false, -// "Name": "init.3", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/test", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "files/heap_item_value_init.gno", -// "Line": "10", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/test.S" // }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" -// }, -// "FileName": "files/heap_item_value_init.gno", -// "IsMethod": false, -// "Name": "main", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/test", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "files/heap_item_value_init.gno", -// "Line": "16", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "a05e5e1e2d2a27d94408a9325a58068e60b504df", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5" // } -// ] +// } // } diff --git a/gnovm/tests/files/zrealm0.gno b/gnovm/tests/files/zrealm0.gno index 5685da2154b..d3bf0e79223 100644 --- a/gnovm/tests/files/zrealm0.gno +++ b/gnovm/tests/files/zrealm0.gno @@ -20,68 +20,3 @@ func main() { // Realm: // switchrealm["gno.land/r/test"] -// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ -// "Blank": {}, -// "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", -// "IsEscaped": true, -// "ModTime": "3", -// "RefCount": "2" -// }, -// "Parent": null, -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "0", -// "File": "", -// "Line": "0", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Values": [ -// { -// "N": "AQAAAAAAAAA=", -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "32" -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" -// }, -// "FileName": "files/zrealm0.gno", -// "IsMethod": false, -// "Name": "main", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/test", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "files/zrealm0.gno", -// "Line": "6", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// } -// ] -// } diff --git a/gnovm/tests/files/zrealm1.gno b/gnovm/tests/files/zrealm1.gno index bb44a93bad4..eef42cc9378 100644 --- a/gnovm/tests/files/zrealm1.gno +++ b/gnovm/tests/files/zrealm1.gno @@ -47,159 +47,3 @@ func main() { // "RefCount": "1" // } // } -// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ -// "Blank": {}, -// "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", -// "IsEscaped": true, -// "ModTime": "3", -// "RefCount": "2" -// }, -// "Parent": null, -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "0", -// "File": "", -// "Line": "0", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Values": [ -// { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/test.InnerNode" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "ae4e9e2d205cc0081d4ee249e1d188ebe270b220", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" -// } -// }, -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.InterfaceType", -// "Generic": "", -// "Methods": [], -// "PkgPath": "gno.land/r/test" -// }, -// "Methods": [], -// "Name": "Node", -// "PkgPath": "gno.land/r/test" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.InterfaceType", -// "Generic": "", -// "Methods": [], -// "PkgPath": "gno.land/r/test" -// }, -// "Methods": [], -// "Name": "Key", -// "PkgPath": "gno.land/r/test" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.StructType", -// "Fields": [ -// { -// "Embedded": false, -// "Name": "Key", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/test.Key" -// } -// }, -// { -// "Embedded": false, -// "Name": "Left", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/test.Node" -// } -// }, -// { -// "Embedded": false, -// "Name": "Right", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/test.Node" -// } -// } -// ], -// "PkgPath": "gno.land/r/test" -// }, -// "Methods": [], -// "Name": "InnerNode", -// "PkgPath": "gno.land/r/test" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" -// }, -// "FileName": "files/zrealm1.gno", -// "IsMethod": false, -// "Name": "main", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/test", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "files/zrealm1.gno", -// "Line": "17", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// } -// ] -// } diff --git a/gnovm/tests/files/zrealm13.gno b/gnovm/tests/files/zrealm13.gno new file mode 100644 index 00000000000..f43f440f731 --- /dev/null +++ b/gnovm/tests/files/zrealm13.gno @@ -0,0 +1,77 @@ +// PKGPATH: gno.land/r/test +package test + +var ( + a = &A{A: "here"} + b [2]*B +) + + +type A struct { + A string +} +type B struct { + A *A + B string +} + +func init() { + c := B{ + A: a, + B: "c", + } + b[0] = &c + + d := B{ + A: a, + B: "d", + } + b[1] = &d +} + +func main() { + b[0] = nil +} + + +// Realm: +// switchrealm["gno.land/r/test"] +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:5]={ +// "Data": null, +// "List": [ +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/test.B" +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/test.B" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7" +// }, +// "Index": "1", +// "TV": null +// } +// } +// ], +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", +// "ModTime": "9", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", +// "RefCount": "1" +// } +// } diff --git a/gnovm/tests/files/zrealm14.gno b/gnovm/tests/files/zrealm14.gno new file mode 100644 index 00000000000..ffffa4883fd --- /dev/null +++ b/gnovm/tests/files/zrealm14.gno @@ -0,0 +1,168 @@ +// PKGPATH: gno.land/r/test +package test + +var ( + a = &A{A: "here"} + b [2]*B +) + + +type A struct { + A string +} +type B struct { + A *A + B string +} + +func init() { + c := B{ + A: a, + B: "c", + } + b[0] = &c + + d := B{ + A: a, + B: "d", + } + b[1] = &d +} + +func main() { + b[0] = nil + b[1] = nil +} + +// Realm: +// switchrealm["gno.land/r/test"] +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:5]={ +// "Data": null, +// "List": [ +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/test.B" +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/test.B" +// } +// } +// } +// ], +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", +// "ModTime": "9", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", +// "RefCount": "1" +// } +// } +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:8]={ +// "Fields": [ +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/test.A" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" +// }, +// "Index": "0", +// "TV": null +// } +// }, +// { +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "16" +// }, +// "V": { +// "@type": "/gno.StringValue", +// "value": "c" +// } +// } +// ], +// "ObjectInfo": { +// "Hash": "c22ccb7832b422c83fec9943b751cb134fcbed0b", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", +// "RefCount": "0" +// } +// } +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:9]={ +// "Fields": [ +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/test.A" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" +// }, +// "Index": "0", +// "TV": null +// } +// }, +// { +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "16" +// }, +// "V": { +// "@type": "/gno.StringValue", +// "value": "d" +// } +// } +// ], +// "ObjectInfo": { +// "Hash": "86c916fd78da57d354cb38019923bf64c1a3471c", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", +// "RefCount": "0" +// } +// } +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:3]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3", +// "IsEscaped": true, +// "ModTime": "9", +// "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/test.A" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "95dca2f5b12899b6367402ecdac04c7ca59a03d9", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" +// } +// } +// } +// d[a8ada09dee16d791fd406d629fe29bb0ed084a30:8] +// d[a8ada09dee16d791fd406d629fe29bb0ed084a30:9] diff --git a/gnovm/tests/files/zrealm15.gno b/gnovm/tests/files/zrealm15.gno new file mode 100644 index 00000000000..4ca6ef3b03d --- /dev/null +++ b/gnovm/tests/files/zrealm15.gno @@ -0,0 +1,178 @@ +// PKGPATH: gno.land/r/test +package test + +var ( + a = &A{} + b [2]*B + s *S +) + +type S struct { + S string +} +type A struct { + A *S +} +type B struct { + A *A + B *S +} + +func init() { + s = &S{ + S: "c", + } + c := B{ + A: a, + B: s, + } + b[0] = &c + b[1] = &c + a.A = s +} + +func main() { + b[0] = nil + b[1] = nil + a.A = nil +} + + +// Realm: +// switchrealm["gno.land/r/test"] +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:5]={ +// "Data": null, +// "List": [ +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/test.B" +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/test.B" +// } +// } +// } +// ], +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", +// "ModTime": "10", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", +// "RefCount": "1" +// } +// } +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:10]={ +// "Fields": [ +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/test.A" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" +// }, +// "Index": "0", +// "TV": null +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/test.S" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7" +// }, +// "Index": "0", +// "TV": null +// } +// } +// ], +// "ObjectInfo": { +// "Hash": "5bf603ab337f9f40f8b22441562319d67be402b2", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:10", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9", +// "RefCount": "0" +// } +// } +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:4]={ +// "Fields": [ +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/test.S" +// } +// } +// } +// ], +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", +// "ModTime": "10", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3", +// "RefCount": "1" +// } +// } +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:7]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", +// "IsEscaped": true, +// "ModTime": "10", +// "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/test.S" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "6e4c3c716e28df1d3a25efeb654a7b7a379ce3b0", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8" +// } +// } +// } +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:3]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3", +// "IsEscaped": true, +// "ModTime": "10", +// "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/test.A" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "e82e410f22425e48d5f6c611160084a4dd50d3d1", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" +// } +// } +// } +// d[a8ada09dee16d791fd406d629fe29bb0ed084a30:10] diff --git a/gnovm/tests/files/zrealm16.gno b/gnovm/tests/files/zrealm16.gno new file mode 100644 index 00000000000..b7bef5028b0 --- /dev/null +++ b/gnovm/tests/files/zrealm16.gno @@ -0,0 +1,115 @@ +// PKGPATH: gno.land/r/test +package test + +var ( + a = &A{A: "here"} + a2 = &A{A: "here"} + b [2]*B +) + +type A struct { + A string +} +type B struct { + A *A + B string +} + +func init() { + c := B{ + A: a, + B: "c", + } + b[0] = &c + + d := B{ + A: a, + B: "d", + } + b[1] = &d +} + +func main() { + b[0].A = a2 +} + +// Realm: +// switchrealm["gno.land/r/test"] +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:10]={ +// "Fields": [ +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/test.A" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5" +// }, +// "Index": "0", +// "TV": null +// } +// }, +// { +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "16" +// }, +// "V": { +// "@type": "/gno.StringValue", +// "value": "c" +// } +// } +// ], +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:10", +// "ModTime": "11", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9", +// "RefCount": "1" +// } +// } +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:5]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", +// "IsEscaped": true, +// "ModTime": "11", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", +// "RefCount": "2" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/test.A" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "7c9f93a63419d852f132afaf2245ddcac5e8c3fb", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" +// } +// } +// } +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:3]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3", +// "IsEscaped": true, +// "ModTime": "11", +// "RefCount": "2" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/test.A" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "95dca2f5b12899b6367402ecdac04c7ca59a03d9", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" +// } +// } +// } diff --git a/gnovm/tests/files/zrealm2.gno b/gnovm/tests/files/zrealm2.gno index 7bae02e48bc..57cd8bee349 100644 --- a/gnovm/tests/files/zrealm2.gno +++ b/gnovm/tests/files/zrealm2.gno @@ -50,196 +50,4 @@ func main() { // "RefCount": "1" // } // } -// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ -// "Blank": {}, -// "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", -// "IsEscaped": true, -// "ModTime": "4", -// "RefCount": "2" -// }, -// "Parent": null, -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "0", -// "File": "", -// "Line": "0", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Values": [ -// { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/test.InnerNode" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "61d4aa77a87c01e07038c6030d6aca299d0fdc1b", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5" -// } -// }, -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.InterfaceType", -// "Generic": "", -// "Methods": [], -// "PkgPath": "gno.land/r/test" -// }, -// "Methods": [], -// "Name": "Node", -// "PkgPath": "gno.land/r/test" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.InterfaceType", -// "Generic": "", -// "Methods": [], -// "PkgPath": "gno.land/r/test" -// }, -// "Methods": [], -// "Name": "Key", -// "PkgPath": "gno.land/r/test" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.StructType", -// "Fields": [ -// { -// "Embedded": false, -// "Name": "Key", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/test.Key" -// } -// }, -// { -// "Embedded": false, -// "Name": "Left", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/test.Node" -// } -// }, -// { -// "Embedded": false, -// "Name": "Right", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/test.Node" -// } -// } -// ], -// "PkgPath": "gno.land/r/test" -// }, -// "Methods": [], -// "Name": "InnerNode", -// "PkgPath": "gno.land/r/test" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" -// }, -// "FileName": "files/zrealm2.gno", -// "IsMethod": false, -// "Name": "init.4", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/test", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "files/zrealm2.gno", -// "Line": "17", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" -// }, -// "FileName": "files/zrealm2.gno", -// "IsMethod": false, -// "Name": "main", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/test", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "files/zrealm2.gno", -// "Line": "23", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// } -// ] -// } // d[a8ada09dee16d791fd406d629fe29bb0ed084a30:4] diff --git a/gnovm/tests/files/zrealm3.gno b/gnovm/tests/files/zrealm3.gno index 386fd4fe470..cc9317c32fd 100644 --- a/gnovm/tests/files/zrealm3.gno +++ b/gnovm/tests/files/zrealm3.gno @@ -82,191 +82,5 @@ func main() { // } // } // } -// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ -// "Blank": {}, -// "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", -// "IsEscaped": true, -// "ModTime": "5", -// "RefCount": "2" -// }, -// "Parent": null, -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "0", -// "File": "", -// "Line": "0", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Values": [ -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/test.Node" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": { -// "@type": "/gno.RefValue", -// "Hash": "8197b7c5b4f2c7bf9c12b1c614f6b4dc6e7ce8dd", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" -// }, -// "Index": "0", -// "TV": null -// } -// }, -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.InterfaceType", -// "Generic": "", -// "Methods": [], -// "PkgPath": "gno.land/r/test" -// }, -// "Methods": [], -// "Name": "Key", -// "PkgPath": "gno.land/r/test" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.StructType", -// "Fields": [ -// { -// "Embedded": false, -// "Name": "Key", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/test.Key" -// } -// }, -// { -// "Embedded": false, -// "Name": "Left", -// "Tag": "", -// "Type": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/test.Node" -// } -// } -// }, -// { -// "Embedded": false, -// "Name": "Right", -// "Tag": "", -// "Type": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/test.Node" -// } -// } -// } -// ], -// "PkgPath": "gno.land/r/test" -// }, -// "Methods": [], -// "Name": "Node", -// "PkgPath": "gno.land/r/test" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" -// }, -// "FileName": "files/zrealm3.gno", -// "IsMethod": false, -// "Name": "init.3", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/test", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "files/zrealm3.gno", -// "Line": "14", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" -// }, -// "FileName": "files/zrealm3.gno", -// "IsMethod": false, -// "Name": "main", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/test", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "files/zrealm3.gno", -// "Line": "20", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// } -// ] -// } // d[a8ada09dee16d791fd406d629fe29bb0ed084a30:4] // d[a8ada09dee16d791fd406d629fe29bb0ed084a30:5] diff --git a/gnovm/tests/files/zrealm4.gno b/gnovm/tests/files/zrealm4.gno index 8c3857aa37c..f8c5502fcb1 100644 --- a/gnovm/tests/files/zrealm4.gno +++ b/gnovm/tests/files/zrealm4.gno @@ -78,116 +78,22 @@ func main() { // "RefCount": "1" // } // } -// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ -// "Blank": {}, +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:4]={ // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", -// "IsEscaped": true, +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", // "ModTime": "5", -// "RefCount": "2" -// }, -// "Parent": null, -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "0", -// "File": "", -// "Line": "0", -// "PkgPath": "gno.land/r/test" -// } +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", +// "RefCount": "1" // }, -// "Values": [ -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": { -// "@type": "/gno.RefValue", -// "Hash": "7b9d58f40430bbbcbafd47eefb7a6dd342477f71", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" -// }, -// "Index": "0", -// "TV": null -// } +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" // }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" -// }, -// "FileName": "files/zrealm4.gno", -// "IsMethod": false, -// "Name": "init.1", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/test", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "files/zrealm4.gno", -// "Line": "11", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" -// }, -// "FileName": "files/zrealm4.gno", -// "IsMethod": false, -// "Name": "main", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/test", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "files/zrealm4.gno", -// "Line": "15", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "d57ee28f5030eb8c612b374155fd545675633288", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5" // } -// ] +// } // } diff --git a/gnovm/tests/files/zrealm5.gno b/gnovm/tests/files/zrealm5.gno index c1062d51e8d..13d6e4a6b64 100644 --- a/gnovm/tests/files/zrealm5.gno +++ b/gnovm/tests/files/zrealm5.gno @@ -162,116 +162,22 @@ func main() { // "RefCount": "1" // } // } -// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ -// "Blank": {}, +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:4]={ // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", -// "IsEscaped": true, +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", // "ModTime": "5", -// "RefCount": "2" -// }, -// "Parent": null, -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "0", -// "File": "", -// "Line": "0", -// "PkgPath": "gno.land/r/test" -// } +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", +// "RefCount": "1" // }, -// "Values": [ -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": { -// "@type": "/gno.RefValue", -// "Hash": "7b9d58f40430bbbcbafd47eefb7a6dd342477f71", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" -// }, -// "Index": "0", -// "TV": null -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" -// }, -// "FileName": "files/zrealm5.gno", -// "IsMethod": false, -// "Name": "init.1", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/test", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "files/zrealm5.gno", -// "Line": "11", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" // }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" -// }, -// "FileName": "files/zrealm5.gno", -// "IsMethod": false, -// "Name": "main", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/test", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "files/zrealm5.gno", -// "Line": "15", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "115779a405a1cae45446397ea897a98a4043cbb2", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5" // } -// ] +// } // } diff --git a/gnovm/tests/files/zrealm6.gno b/gnovm/tests/files/zrealm6.gno index 1c50baa2d15..ba162c4d468 100644 --- a/gnovm/tests/files/zrealm6.gno +++ b/gnovm/tests/files/zrealm6.gno @@ -163,6 +163,25 @@ func main() { // "RefCount": "1" // } // } +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:6]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6", +// "ModTime": "7", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", +// "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "5d64092f4f064ca58bdeffa32f6a119545b401c8", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7" +// } +// } +// } // u[a8ada09dee16d791fd406d629fe29bb0ed084a30:5]={ // "Fields": [ // { @@ -213,7 +232,7 @@ func main() { // "@type": "/gno.PointerValue", // "Base": { // "@type": "/gno.RefValue", -// "Hash": "7c63a8fd451cd7c470c1851f1ead037246422ded", +// "Hash": "32593d23afa555fe99d433dbca1130b3843da97a", // "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" // }, // "Index": "0", @@ -228,116 +247,22 @@ func main() { // "RefCount": "1" // } // } -// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ -// "Blank": {}, +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:4]={ // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", -// "IsEscaped": true, +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", // "ModTime": "7", -// "RefCount": "2" -// }, -// "Parent": null, -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "0", -// "File": "", -// "Line": "0", -// "PkgPath": "gno.land/r/test" -// } +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", +// "RefCount": "1" // }, -// "Values": [ -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": { -// "@type": "/gno.RefValue", -// "Hash": "ade9fce2a987ef1924040a1d75c0172410c66952", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" -// }, -// "Index": "0", -// "TV": null -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" -// }, -// "FileName": "files/zrealm6.gno", -// "IsMethod": false, -// "Name": "init.1", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/test", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "files/zrealm6.gno", -// "Line": "11", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" // }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" -// }, -// "FileName": "files/zrealm6.gno", -// "IsMethod": false, -// "Name": "main", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/test", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "files/zrealm6.gno", -// "Line": "16", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "131993c49dced230bd7071e9bae8d95e28733b73", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5" // } -// ] +// } // } diff --git a/gnovm/tests/files/zrealm7.gno b/gnovm/tests/files/zrealm7.gno index e22b90f0e0f..5b932962db4 100644 --- a/gnovm/tests/files/zrealm7.gno +++ b/gnovm/tests/files/zrealm7.gno @@ -164,26 +164,7 @@ func main() { // "RefCount": "1" // } // } -// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:8]={ -// "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8", -// "ModTime": "9", -// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", -// "RefCount": "1" -// }, -// "Value": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "2c172bbe0183ccc73c59d9acb196c45b0331c39e", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9" -// } -// } -// } -// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:7]={ +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:5]={ // "Fields": [ // { // "T": { @@ -192,7 +173,7 @@ func main() { // }, // "V": { // "@type": "/gno.StringValue", -// "value": "key1" +// "value": "key0" // } // }, // { @@ -202,11 +183,11 @@ func main() { // }, // "V": { // "@type": "/gno.StringValue", -// "value": "value1" +// "value": "value0" // } // }, // { -// "N": "AwAAAAAAAAA=", +// "N": "AQAAAAAAAAA=", // "T": { // "@type": "/gno.PrimitiveType", // "value": "32" @@ -219,16 +200,6 @@ func main() { // "@type": "/gno.RefType", // "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" // } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": { -// "@type": "/gno.RefValue", -// "Hash": "a4fa9bdf45caf8c6b5be7a3752704423817b3ef2", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" -// }, -// "Index": "0", -// "TV": null // } // }, // { @@ -238,27 +209,55 @@ func main() { // "@type": "/gno.RefType", // "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" // } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": { -// "@type": "/gno.RefValue", -// "Hash": "43f69f24b7827a331921b4af0f667346d186e0c3", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8" -// }, -// "Index": "0", -// "TV": null // } // } // ], // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", // "ModTime": "9", -// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", // "RefCount": "1" // } // } -// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:5]={ +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:4]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", +// "ModTime": "9", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", +// "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "d7fbb234dca9f194f35fe5409a62db9daf39b0fc", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5" +// } +// } +// } +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:8]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8", +// "ModTime": "9", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", +// "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "2c172bbe0183ccc73c59d9acb196c45b0331c39e", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9" +// } +// } +// } +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:7]={ // "Fields": [ // { // "T": { @@ -267,7 +266,7 @@ func main() { // }, // "V": { // "@type": "/gno.StringValue", -// "value": "key0" +// "value": "key1" // } // }, // { @@ -277,11 +276,11 @@ func main() { // }, // "V": { // "@type": "/gno.StringValue", -// "value": "value0" +// "value": "value1" // } // }, // { -// "N": "AQAAAAAAAAA=", +// "N": "AwAAAAAAAAA=", // "T": { // "@type": "/gno.PrimitiveType", // "value": "32" @@ -294,6 +293,16 @@ func main() { // "@type": "/gno.RefType", // "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" // } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "76a40dcf03d32c312c2213265c14d4de1b12a810", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4" +// }, +// "Index": "0", +// "TV": null // } // }, // { @@ -303,13 +312,23 @@ func main() { // "@type": "/gno.RefType", // "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" // } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": { +// "@type": "/gno.RefValue", +// "Hash": "43f69f24b7827a331921b4af0f667346d186e0c3", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8" +// }, +// "Index": "0", +// "TV": null // } // } // ], // "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", // "ModTime": "9", -// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6", // "RefCount": "1" // } // } @@ -327,121 +346,8 @@ func main() { // }, // "V": { // "@type": "/gno.RefValue", -// "Hash": "f56fbd9c8db299689cc0cf806fe741b6a6e641e6", +// "Hash": "92b2f4ebab764951f64086bce480f898f755de5a", // "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7" // } // } // } -// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ -// "Blank": {}, -// "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", -// "IsEscaped": true, -// "ModTime": "9", -// "RefCount": "2" -// }, -// "Parent": null, -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "0", -// "File": "", -// "Line": "0", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Values": [ -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": { -// "@type": "/gno.RefValue", -// "Hash": "450aef9858564ed4ec1c418f1e8dac828079016b", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" -// }, -// "Index": "0", -// "TV": null -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" -// }, -// "FileName": "files/zrealm7.gno", -// "IsMethod": false, -// "Name": "init.1", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/test", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "files/zrealm7.gno", -// "Line": "11", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" -// }, -// "FileName": "files/zrealm7.gno", -// "IsMethod": false, -// "Name": "main", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/test", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "files/zrealm7.gno", -// "Line": "17", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// } -// ] -// } diff --git a/gnovm/tests/files/zrealm_avl0.gno b/gnovm/tests/files/zrealm_avl0.gno index 362d04ec0f8..1db1adebd3e 100644 --- a/gnovm/tests/files/zrealm_avl0.gno +++ b/gnovm/tests/files/zrealm_avl0.gno @@ -215,116 +215,3 @@ func main() { // } // } // } -// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ -// "Blank": {}, -// "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", -// "IsEscaped": true, -// "ModTime": "5", -// "RefCount": "2" -// }, -// "Parent": null, -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "0", -// "File": "", -// "Line": "0", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Values": [ -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": { -// "@type": "/gno.RefValue", -// "Hash": "ae86874f9b47fa5e64c30b3e92e9d07f2ec967a4", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" -// }, -// "Index": "0", -// "TV": null -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" -// }, -// "FileName": "files/zrealm_avl0.gno", -// "IsMethod": false, -// "Name": "init.1", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/test", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "files/zrealm_avl0.gno", -// "Line": "10", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" -// }, -// "FileName": "files/zrealm_avl0.gno", -// "IsMethod": false, -// "Name": "main", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/test", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "files/zrealm_avl0.gno", -// "Line": "15", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// } -// ] -// } diff --git a/gnovm/tests/files/zrealm_avl1.gno b/gnovm/tests/files/zrealm_avl1.gno index bfcf9f7975e..572c49333bc 100644 --- a/gnovm/tests/files/zrealm_avl1.gno +++ b/gnovm/tests/files/zrealm_avl1.gno @@ -24,6 +24,44 @@ func main() { // Realm: // switchrealm["gno.land/r/test"] +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:6]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6", +// "ModTime": "11", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", +// "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "1375f6f96a1a3f298347dc8fc0065afa36cb7f0f", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7" +// } +// } +// } +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:8]={ +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8", +// "ModTime": "13", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5", +// "RefCount": "1" +// }, +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "b28057ab7be6383785c0a5503e8a531bdbc21851", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9" +// } +// } +// } // c[a8ada09dee16d791fd406d629fe29bb0ed084a30:15]={ // "Fields": [ // { @@ -143,7 +181,7 @@ func main() { // "@type": "/gno.PointerValue", // "Base": { // "@type": "/gno.RefValue", -// "Hash": "2f3adc5d0f2a3fe0331cfa93572a7abdde14c9aa", +// "Hash": "cafae89e4d4aaaefe7fdf0691084508d4274a981", // "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8" // }, // "Index": "0", @@ -191,7 +229,7 @@ func main() { // }, // "V": { // "@type": "/gno.RefValue", -// "Hash": "fe20a19f956511f274dc77854e9e5468387260f4", +// "Hash": "b2e446f490656c19a83c43055de29c96e92a1549", // "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:13" // } // } @@ -235,7 +273,7 @@ func main() { // "@type": "/gno.PointerValue", // "Base": { // "@type": "/gno.RefValue", -// "Hash": "c89a71bdf045e8bde2059dc9d33839f916e02e5d", +// "Hash": "4e56eeb96eb1d9b27cf603140cd03a1622b6358b", // "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" // }, // "Index": "0", @@ -254,7 +292,7 @@ func main() { // "@type": "/gno.PointerValue", // "Base": { // "@type": "/gno.RefValue", -// "Hash": "90fa67f8c47db4b9b2a60425dff08d5a3385100f", +// "Hash": "7b61530859954d1d14b2f696c91c5f37d39c21e7", // "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:12" // }, // "Index": "0", @@ -283,123 +321,10 @@ func main() { // }, // "V": { // "@type": "/gno.RefValue", -// "Hash": "83e42caaf53070dd95b5f859053eb51ed900bbda", +// "Hash": "fedc6d430b38c985dc6a985b2fcaee97e88ba6da", // "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:11" // } // } // } -// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ -// "Blank": {}, -// "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", -// "IsEscaped": true, -// "ModTime": "9", -// "RefCount": "2" -// }, -// "Parent": null, -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "0", -// "File": "", -// "Line": "0", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Values": [ -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": { -// "@type": "/gno.RefValue", -// "Hash": "1faa9fa4ba1935121a6d3f0a623772e9d4499b0a", -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:10" -// }, -// "Index": "0", -// "TV": null -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" -// }, -// "FileName": "files/zrealm_avl1.gno", -// "IsMethod": false, -// "Name": "init.1", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/test", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "files/zrealm_avl1.gno", -// "Line": "10", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" -// }, -// "FileName": "files/zrealm_avl1.gno", -// "IsMethod": false, -// "Name": "main", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/test", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "files/zrealm_avl1.gno", -// "Line": "15", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// } -// ] -// } // d[a8ada09dee16d791fd406d629fe29bb0ed084a30:4] // d[a8ada09dee16d791fd406d629fe29bb0ed084a30:5] diff --git a/gnovm/tests/files/zrealm_crossrealm21.gno b/gnovm/tests/files/zrealm_crossrealm21.gno index 634fbea13c8..e3b29f671a4 100644 --- a/gnovm/tests/files/zrealm_crossrealm21.gno +++ b/gnovm/tests/files/zrealm_crossrealm21.gno @@ -24,673 +24,25 @@ func main() { // Realm: // switchrealm["gno.land/r/demo/tests/crossrealm"] -// u[1712ac7adcfdc8e58a67e5615e20fb312394c4df:2]={ -// "Blank": {}, +// u[0edc46caf30c00efd87b6c272673239eafbd051e:3]={ // "ObjectInfo": { -// "ID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:2", +// "ID": "0edc46caf30c00efd87b6c272673239eafbd051e:3", // "IsEscaped": true, // "ModTime": "5", +// "OwnerID": "0edc46caf30c00efd87b6c272673239eafbd051e:2", // "RefCount": "2" // }, -// "Parent": null, -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "0", -// "File": "", -// "Line": "0", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Values": [ -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.StructType", -// "Fields": [ -// { -// "Embedded": false, -// "Name": "A", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "32" -// } -// } -// ], -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// }, -// "Methods": [ -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "ls", -// "Tag": "", -// "Type": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.LocalStruct" -// } -// } -// } -// ], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": null, -// "FileName": "crossrealm.gno", -// "IsMethod": true, -// "Name": "String", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "12", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "ls", -// "Tag": "", -// "Type": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.LocalStruct" -// } -// } -// } -// ], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// } -// } -// ] -// } -// } -// } -// ], -// "Name": "LocalStruct", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.LocalStruct" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": { -// "@type": "/gno.RefValue", -// "Hash": "a75fdb389fedfcbbaa7f446d528c1e149726347c", -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:4" -// }, -// "Index": "0", -// "TV": null -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "init.2", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "19", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/tests/p_crossrealm.Container" -// } -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "Make1", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "24", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/tests/p_crossrealm.Container" -// } -// } -// } -// ] -// } -// } +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/demo/tests/crossrealm_b.fooer" // }, -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.InterfaceType", -// "Generic": "", -// "Methods": [ -// { -// "Embedded": false, -// "Name": "Foo", -// "Tag": "", -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// ], -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// }, -// "Methods": [], -// "Name": "Fooer", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm_b.fooer" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "0edc46caf30c00efd87b6c272673239eafbd051e:3" -// }, -// "Index": "0", -// "TV": null -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "f", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "SetFooer", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "35", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "f", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "GetFooer", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "40", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "CallFooerFoo", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "42", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ] -// }, -// "Methods": [], -// "Name": "FooerGetter", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter" -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "fg", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter" -// } -// } -// ], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "SetFooerGetter", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "48", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "fg", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter" -// } -// } -// ], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "GetFooerGetter", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "53", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "CallFooerGetterFoo", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "57", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "8222b763e9bc04b4b7805e165e9f1324a39f28b6", +// "ObjectID": "0edc46caf30c00efd87b6c272673239eafbd051e:4" // } -// ] +// } // } // switchrealm["gno.land/r/demo/tests/crossrealm_b"] // switchrealm["gno.land/r/demo/tests/crossrealm"] diff --git a/gnovm/tests/files/zrealm_crossrealm22.gno b/gnovm/tests/files/zrealm_crossrealm22.gno index 18985f7719d..afb9dab6d59 100644 --- a/gnovm/tests/files/zrealm_crossrealm22.gno +++ b/gnovm/tests/files/zrealm_crossrealm22.gno @@ -38,744 +38,25 @@ func main() { // Realm: // switchrealm["gno.land/r/demo/tests/crossrealm"] -// c[1712ac7adcfdc8e58a67e5615e20fb312394c4df:6]={ -// "Blank": {}, +// u[0edc46caf30c00efd87b6c272673239eafbd051e:3]={ // "ObjectInfo": { -// "ID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:6", -// "ModTime": "0", -// "OwnerID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:2", -// "RefCount": "1" -// }, -// "Parent": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "f5a516808f8976c33939133293d598ce3bca4e8d:3" -// }, -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "files/zrealm_crossrealm22.gno", -// "Line": "11", -// "PkgPath": "gno.land/r/crossrealm_test" -// } -// }, -// "Values": [ -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm_b.fooer" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "0edc46caf30c00efd87b6c272673239eafbd051e:3" -// }, -// "Index": "0", -// "TV": null -// } -// } -// ] -// } -// u[1712ac7adcfdc8e58a67e5615e20fb312394c4df:2]={ -// "Blank": {}, -// "ObjectInfo": { -// "ID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:2", +// "ID": "0edc46caf30c00efd87b6c272673239eafbd051e:3", // "IsEscaped": true, -// "ModTime": "5", +// "ModTime": "6", +// "OwnerID": "0edc46caf30c00efd87b6c272673239eafbd051e:2", // "RefCount": "2" // }, -// "Parent": null, -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "0", -// "File": "", -// "Line": "0", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/demo/tests/crossrealm_b.fooer" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "8222b763e9bc04b4b7805e165e9f1324a39f28b6", +// "ObjectID": "0edc46caf30c00efd87b6c272673239eafbd051e:4" // } -// }, -// "Values": [ -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.StructType", -// "Fields": [ -// { -// "Embedded": false, -// "Name": "A", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "32" -// } -// } -// ], -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// }, -// "Methods": [ -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "ls", -// "Tag": "", -// "Type": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.LocalStruct" -// } -// } -// } -// ], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": null, -// "FileName": "crossrealm.gno", -// "IsMethod": true, -// "Name": "String", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "12", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "ls", -// "Tag": "", -// "Type": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.LocalStruct" -// } -// } -// } -// ], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// } -// } -// ] -// } -// } -// } -// ], -// "Name": "LocalStruct", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.LocalStruct" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": { -// "@type": "/gno.RefValue", -// "Hash": "a75fdb389fedfcbbaa7f446d528c1e149726347c", -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:4" -// }, -// "Index": "0", -// "TV": null -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "init.2", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "19", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/tests/p_crossrealm.Container" -// } -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "Make1", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "24", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/tests/p_crossrealm.Container" -// } -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.InterfaceType", -// "Generic": "", -// "Methods": [ -// { -// "Embedded": false, -// "Name": "Foo", -// "Tag": "", -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// ], -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// }, -// "Methods": [], -// "Name": "Fooer", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "f", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "SetFooer", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "35", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "f", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "GetFooer", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "40", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "CallFooerFoo", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "42", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ] -// }, -// "Methods": [], -// "Name": "FooerGetter", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter" -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Hash": "23de97a577d573252d00394ce9b71c24b0646546", -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:6" -// }, -// "FileName": "", -// "IsMethod": false, -// "Name": "", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/crossrealm_test", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "28", -// "File": "files/zrealm_crossrealm22.gno", -// "Line": "13", -// "PkgPath": "gno.land/r/crossrealm_test" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "fg", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter" -// } -// } -// ], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "SetFooerGetter", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "48", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "fg", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter" -// } -// } -// ], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "GetFooerGetter", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "53", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "CallFooerGetterFoo", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "57", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// } -// ] +// } // } // switchrealm["gno.land/r/crossrealm_test"] // switchrealm["gno.land/r/demo/tests/crossrealm_b"] @@ -826,701 +107,26 @@ func main() { // } // } // switchrealm["gno.land/r/demo/tests/crossrealm"] -// u[1712ac7adcfdc8e58a67e5615e20fb312394c4df:2]={ -// "Blank": {}, +// u[0edc46caf30c00efd87b6c272673239eafbd051e:3]={ // "ObjectInfo": { -// "ID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:2", +// "ID": "0edc46caf30c00efd87b6c272673239eafbd051e:3", // "IsEscaped": true, // "ModTime": "6", -// "RefCount": "2" -// }, -// "Parent": null, -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "0", -// "File": "", -// "Line": "0", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } +// "OwnerID": "0edc46caf30c00efd87b6c272673239eafbd051e:2", +// "RefCount": "1" // }, -// "Values": [ -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.StructType", -// "Fields": [ -// { -// "Embedded": false, -// "Name": "A", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "32" -// } -// } -// ], -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// }, -// "Methods": [ -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "ls", -// "Tag": "", -// "Type": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.LocalStruct" -// } -// } -// } -// ], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": null, -// "FileName": "crossrealm.gno", -// "IsMethod": true, -// "Name": "String", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "12", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "ls", -// "Tag": "", -// "Type": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.LocalStruct" -// } -// } -// } -// ], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// } -// } -// ] -// } -// } -// } -// ], -// "Name": "LocalStruct", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.LocalStruct" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": { -// "@type": "/gno.RefValue", -// "Hash": "a75fdb389fedfcbbaa7f446d528c1e149726347c", -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:4" -// }, -// "Index": "0", -// "TV": null -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "init.2", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "19", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/tests/p_crossrealm.Container" -// } -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "Make1", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "24", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/tests/p_crossrealm.Container" -// } -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.InterfaceType", -// "Generic": "", -// "Methods": [ -// { -// "Embedded": false, -// "Name": "Foo", -// "Tag": "", -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// ], -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// }, -// "Methods": [], -// "Name": "Fooer", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "f", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "SetFooer", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "35", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "f", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "GetFooer", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "40", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "CallFooerFoo", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "42", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ] -// }, -// "Methods": [], -// "Name": "FooerGetter", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter" -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "0edc46caf30c00efd87b6c272673239eafbd051e:5" -// }, -// "FileName": "", -// "IsMethod": false, -// "Name": "", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm_b", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "23", -// "File": "crossrealm.gno", -// "Line": "23", -// "PkgPath": "gno.land/r/demo/tests/crossrealm_b" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "fg", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter" -// } -// } -// ], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "SetFooerGetter", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "48", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "fg", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter" -// } -// } -// ], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "GetFooerGetter", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "53", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "CallFooerGetterFoo", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "57", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/demo/tests/crossrealm_b.fooer" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "759fd5b507fff8ea1b18d401550d918387a63445", +// "ObjectID": "0edc46caf30c00efd87b6c272673239eafbd051e:4" // } -// ] +// } // } -// d[1712ac7adcfdc8e58a67e5615e20fb312394c4df:6] // switchrealm["gno.land/r/demo/tests/crossrealm_b"] // switchrealm["gno.land/r/demo/tests/crossrealm_b"] // switchrealm["gno.land/r/demo/tests/crossrealm"] @@ -1547,732 +153,6 @@ func main() { // } // switchrealm["gno.land/r/demo/tests/crossrealm_b"] // switchrealm["gno.land/r/demo/tests/crossrealm"] -// c[1712ac7adcfdc8e58a67e5615e20fb312394c4df:7]={ -// "Blank": {}, -// "ObjectInfo": { -// "ID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:7", -// "ModTime": "0", -// "OwnerID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:2", -// "RefCount": "1" -// }, -// "Parent": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "0edc46caf30c00efd87b6c272673239eafbd051e:5" -// }, -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "23", -// "File": "crossrealm.gno", -// "Line": "24", -// "PkgPath": "gno.land/r/demo/tests/crossrealm_b" -// } -// }, -// "Values": [ -// { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter" -// } -// } -// ] -// } -// u[1712ac7adcfdc8e58a67e5615e20fb312394c4df:2]={ -// "Blank": {}, -// "ObjectInfo": { -// "ID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:2", -// "IsEscaped": true, -// "ModTime": "6", -// "RefCount": "2" -// }, -// "Parent": null, -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "0", -// "File": "", -// "Line": "0", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Values": [ -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.StructType", -// "Fields": [ -// { -// "Embedded": false, -// "Name": "A", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "32" -// } -// } -// ], -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// }, -// "Methods": [ -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "ls", -// "Tag": "", -// "Type": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.LocalStruct" -// } -// } -// } -// ], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": null, -// "FileName": "crossrealm.gno", -// "IsMethod": true, -// "Name": "String", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "12", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "ls", -// "Tag": "", -// "Type": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.LocalStruct" -// } -// } -// } -// ], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// } -// } -// ] -// } -// } -// } -// ], -// "Name": "LocalStruct", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.LocalStruct" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": { -// "@type": "/gno.RefValue", -// "Hash": "a75fdb389fedfcbbaa7f446d528c1e149726347c", -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:4" -// }, -// "Index": "0", -// "TV": null -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "init.2", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "19", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/tests/p_crossrealm.Container" -// } -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "Make1", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "24", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/tests/p_crossrealm.Container" -// } -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.InterfaceType", -// "Generic": "", -// "Methods": [ -// { -// "Embedded": false, -// "Name": "Foo", -// "Tag": "", -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// ], -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// }, -// "Methods": [], -// "Name": "Fooer", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "f", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "SetFooer", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "35", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "f", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "GetFooer", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "40", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "CallFooerFoo", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "42", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ] -// }, -// "Methods": [], -// "Name": "FooerGetter", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter" -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Hash": "89352b352826005a86eee78e6c832b43ae0ab6a6", -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:7" -// }, -// "FileName": "", -// "IsMethod": false, -// "Name": "", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm_b", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "62", -// "File": "crossrealm.gno", -// "Line": "24", -// "PkgPath": "gno.land/r/demo/tests/crossrealm_b" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.Fooer" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "fg", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter" -// } -// } -// ], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "SetFooerGetter", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "48", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "fg", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter" -// } -// } -// ], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "GetFooerGetter", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "53", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests/crossrealm.FooerGetter" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "1712ac7adcfdc8e58a67e5615e20fb312394c4df:3" -// }, -// "FileName": "crossrealm.gno", -// "IsMethod": false, -// "Name": "CallFooerGetterFoo", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests/crossrealm", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "crossrealm.gno", -// "Line": "57", -// "PkgPath": "gno.land/r/demo/tests/crossrealm" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// } -// ] -// } // switchrealm["gno.land/r/demo/tests/crossrealm_b"] // switchrealm["gno.land/r/demo/tests/crossrealm_b"] // switchrealm["gno.land/r/demo/tests/crossrealm"] diff --git a/gnovm/tests/files/zrealm_natbind0.gno b/gnovm/tests/files/zrealm_natbind0.gno index e6ebef6252e..6f8045107dc 100644 --- a/gnovm/tests/files/zrealm_natbind0.gno +++ b/gnovm/tests/files/zrealm_natbind0.gno @@ -28,153 +28,3 @@ func main() { // Realm: // switchrealm["gno.land/r/test"] -// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ -// "Blank": {}, -// "ObjectInfo": { -// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", -// "IsEscaped": true, -// "ModTime": "3", -// "RefCount": "2" -// }, -// "Parent": null, -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "0", -// "File": "", -// "Line": "0", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Values": [ -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a7f5397443359ea76c50be82c77f1f893a060925:10" -// }, -// "FileName": "native.gno", -// "IsMethod": false, -// "Name": "GetChainID", -// "NativeName": "GetChainID", -// "NativePkg": "std", -// "PkgPath": "std", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "native.gno", -// "Line": "13", -// "PkgPath": "std" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" -// }, -// "FileName": "files/zrealm_natbind0.gno", -// "IsMethod": false, -// "Name": "init.1", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/test", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "files/zrealm_natbind0.gno", -// "Line": "10", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" -// }, -// "FileName": "files/zrealm_natbind0.gno", -// "IsMethod": false, -// "Name": "main", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/test", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "files/zrealm_natbind0.gno", -// "Line": "14", -// "PkgPath": "gno.land/r/test" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// } -// ] -// } diff --git a/gnovm/tests/files/zrealm_tests0.gno b/gnovm/tests/files/zrealm_tests0.gno index afb7e4a7c3b..872046ef795 100644 --- a/gnovm/tests/files/zrealm_tests0.gno +++ b/gnovm/tests/files/zrealm_tests0.gno @@ -80,7 +80,7 @@ func main() { // "@type": "/gno.PointerValue", // "Base": { // "@type": "/gno.RefValue", -// "Hash": "148d314678615253ee2032d7ecff6b144b474baf", +// "Hash": "4e0d77a91ba35733bf82329317bf8c8dffa6f655", // "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:12" // }, // "Index": "0", @@ -133,1644 +133,43 @@ func main() { // "RefCount": "1" // } // } -// u[0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:2]={ -// "Blank": {}, +// u[0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:12]={ // "ObjectInfo": { -// "ID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:2", -// "IsEscaped": true, -// "ModTime": "16", -// "RefCount": "5" +// "ID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:12", +// "ModTime": "17", +// "OwnerID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:11", +// "RefCount": "1" // }, -// "Parent": null, -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "0", -// "File": "", -// "Line": "0", -// "PkgPath": "gno.land/r/demo/tests" +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/demo/tests_foo.FooStringer" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "f163acee63433b44deb3168fef87beb588847322", +// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:13" // } +// } +// } +// u[0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:15]={ +// "ObjectInfo": { +// "ID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:15", +// "ModTime": "17", +// "OwnerID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:14", +// "RefCount": "1" // }, -// "Values": [ -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.InterfaceType", -// "Generic": "", -// "Methods": [ -// { -// "Embedded": false, -// "Name": "String", -// "Tag": "", -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// } -// } -// ] -// } -// } -// ], -// "PkgPath": "gno.land/r/demo/tests" -// }, -// "Methods": [], -// "Name": "Stringer", -// "PkgPath": "gno.land/r/demo/tests" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.SliceType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests.Stringer" -// }, -// "Vrd": false -// }, -// "V": { -// "@type": "/gno.SliceValue", -// "Base": { -// "@type": "/gno.RefValue", -// "Hash": "3c58838c5667649add1ff8ee48a1cdc187fcd2ef", -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:17" -// }, -// "Length": "3", -// "Maxcap": "3", -// "Offset": "0" -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "str", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests.Stringer" -// } -// } -// ], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:3" -// }, -// "FileName": "interfaces.gno", -// "IsMethod": false, -// "Name": "AddStringer", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "interfaces.gno", -// "Line": "13", -// "PkgPath": "gno.land/r/demo/tests" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "str", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests.Stringer" -// } -// } -// ], -// "Results": [] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "path", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// } -// } -// ], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:3" -// }, -// "FileName": "interfaces.gno", -// "IsMethod": false, -// "Name": "Render", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "interfaces.gno", -// "Line": "20", -// "PkgPath": "gno.land/r/demo/tests" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "path", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// } -// } -// ], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.PrimitiveType", -// "value": "2048" -// }, -// "Methods": [], -// "Name": "Word", -// "PkgPath": "gno.land/r/demo/tests" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.SliceType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests.Word" -// }, -// "Vrd": false -// }, -// "Methods": [ -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "n", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests.nat" -// } -// } -// ], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests.nat" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": null, -// "FileName": "realm_method38d.gno", -// "IsMethod": true, -// "Name": "Add", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "realm_method38d.gno", -// "Line": "5", -// "PkgPath": "gno.land/r/demo/tests" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "n", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests.nat" -// } -// } -// ], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests.nat" -// } -// } -// ] -// } -// } -// } -// ], -// "Name": "nat", -// "PkgPath": "gno.land/r/demo/tests" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests.Int" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": { -// "@type": "/gno.RefValue", -// "Hash": "49283398258e135138cd8e234142d5daaa8c661d", -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:4" -// }, -// "Index": "0", -// "TV": null -// } -// }, -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.StructType", -// "Fields": [ -// { -// "Embedded": false, -// "Name": "neg", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "4" -// } -// }, -// { -// "Embedded": false, -// "Name": "abs", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests.nat" -// } -// } -// ], -// "PkgPath": "gno.land/r/demo/tests" -// }, -// "Methods": [], -// "Name": "Int", -// "PkgPath": "gno.land/r/demo/tests" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests.nat" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:7" -// }, -// "FileName": "realm_compositelit.gno", -// "IsMethod": false, -// "Name": "GetZeroType", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "realm_compositelit.gno", -// "Line": "19", -// "PkgPath": "gno.land/r/demo/tests" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests.nat" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests.nat" -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests.nat" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:8" -// }, -// "FileName": "realm_method38d.gno", -// "IsMethod": false, -// "Name": "GetAbs", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "realm_method38d.gno", -// "Line": "9", -// "PkgPath": "gno.land/r/demo/tests" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests.nat" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests.nat" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:8" -// }, -// "FileName": "realm_method38d.gno", -// "IsMethod": false, -// "Name": "AbsAdd", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "realm_method38d.gno", -// "Line": "15", -// "PkgPath": "gno.land/r/demo/tests" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests.nat" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "32" -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:9" -// }, -// "FileName": "tests.gno", -// "IsMethod": false, -// "Name": "IncCounter", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "tests.gno", -// "Line": "12", -// "PkgPath": "gno.land/r/demo/tests" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "32" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:9" -// }, -// "FileName": "tests.gno", -// "IsMethod": false, -// "Name": "Counter", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "tests.gno", -// "Line": "16", -// "PkgPath": "gno.land/r/demo/tests" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "32" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:9" -// }, -// "FileName": "tests.gno", -// "IsMethod": false, -// "Name": "CurrentRealmPath", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "tests.gno", -// "Line": "20", -// "PkgPath": "gno.land/r/demo/tests" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.RefType", -// "ID": "std.Address" -// }, -// "V": { -// "@type": "/gno.StringValue", -// "value": "g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm" -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "std.Address" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:9" -// }, -// "FileName": "tests.gno", -// "IsMethod": false, -// "Name": "InitOrigCaller", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "tests.gno", -// "Line": "26", -// "PkgPath": "gno.land/r/demo/tests" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "std.Address" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:9" -// }, -// "FileName": "tests.gno", -// "IsMethod": false, -// "Name": "CallAssertOriginCall", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "tests.gno", -// "Line": "30", -// "PkgPath": "gno.land/r/demo/tests" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "4" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:9" -// }, -// "FileName": "tests.gno", -// "IsMethod": false, -// "Name": "CallIsOriginCall", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "tests.gno", -// "Line": "34", -// "PkgPath": "gno.land/r/demo/tests" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "4" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:9" -// }, -// "FileName": "tests.gno", -// "IsMethod": false, -// "Name": "CallSubtestsAssertOriginCall", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "tests.gno", -// "Line": "38", -// "PkgPath": "gno.land/r/demo/tests" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "4" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:9" -// }, -// "FileName": "tests.gno", -// "IsMethod": false, -// "Name": "CallSubtestsIsOriginCall", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "tests.gno", -// "Line": "42", -// "PkgPath": "gno.land/r/demo/tests" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "4" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.StructType", -// "Fields": [ -// { -// "Embedded": false, -// "Name": "Field", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// } -// } -// ], -// "PkgPath": "gno.land/r/demo/tests" -// }, -// "Methods": [ -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "t", -// "Tag": "", -// "Type": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests.TestRealmObject" -// } -// } -// } -// ], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": null, -// "FileName": "tests.gno", -// "IsMethod": true, -// "Name": "Modify", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "tests.gno", -// "Line": "59", -// "PkgPath": "gno.land/r/demo/tests" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "t", -// "Tag": "", -// "Type": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests.TestRealmObject" -// } -// } -// } -// ], -// "Results": [] -// } -// } -// } -// ], -// "Name": "TestRealmObject", -// "PkgPath": "gno.land/r/demo/tests" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests.TestRealmObject" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "5e56ba76fc0add1a3a67f7a8b6709f4f27215f93", -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:10" -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "t", -// "Tag": "", -// "Type": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests.TestRealmObject" -// } -// } -// } -// ], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:9" -// }, -// "FileName": "tests.gno", -// "IsMethod": false, -// "Name": "ModifyTestRealmObject", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "tests.gno", -// "Line": "55", -// "PkgPath": "gno.land/r/demo/tests" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "t", -// "Tag": "", -// "Type": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests.TestRealmObject" -// } -// } -// } -// ], -// "Results": [] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.TypeType" -// }, -// "V": { -// "@type": "/gno.TypeValue", -// "Type": { -// "@type": "/gno.DeclaredType", -// "Base": { -// "@type": "/gno.StructType", -// "Fields": [ -// { -// "Embedded": false, -// "Name": "Name", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// } -// }, -// { -// "Embedded": false, -// "Name": "Child", -// "Tag": "", -// "Type": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests.TestNode" -// } -// } -// } -// ], -// "PkgPath": "gno.land/r/demo/tests" -// }, -// "Methods": [], -// "Name": "TestNode", -// "PkgPath": "gno.land/r/demo/tests" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests.TestNode" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests.TestNode" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests.TestNode" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:9" -// }, -// "FileName": "tests.gno", -// "IsMethod": false, -// "Name": "InitTestNodes", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "tests.gno", -// "Line": "77", -// "PkgPath": "gno.land/r/demo/tests" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:9" -// }, -// "FileName": "tests.gno", -// "IsMethod": false, -// "Name": "ModTestNodes", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "tests.gno", -// "Line": "82", -// "PkgPath": "gno.land/r/demo/tests" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:9" -// }, -// "FileName": "tests.gno", -// "IsMethod": false, -// "Name": "PrintTestNodes", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "tests.gno", -// "Line": "90", -// "PkgPath": "gno.land/r/demo/tests" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "std.Realm" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:9" -// }, -// "FileName": "tests.gno", -// "IsMethod": false, -// "Name": "GetPrevRealm", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "tests.gno", -// "Line": "94", -// "PkgPath": "gno.land/r/demo/tests" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "std.Realm" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "std.Realm" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:9" -// }, -// "FileName": "tests.gno", -// "IsMethod": false, -// "Name": "GetRSubtestsPrevRealm", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "tests.gno", -// "Line": "98", -// "PkgPath": "gno.land/r/demo/tests" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.RefType", -// "ID": "std.Realm" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "fn", -// "Tag": "", -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// ], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:9" -// }, -// "FileName": "tests.gno", -// "IsMethod": false, -// "Name": "Exec", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "tests.gno", -// "Line": "102", -// "PkgPath": "gno.land/r/demo/tests" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "fn", -// "Tag": "", -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [] -// } -// } -// ], -// "Results": [] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "4" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:9" -// }, -// "FileName": "tests.gno", -// "IsMethod": false, -// "Name": "IsCallerSubPath", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "tests.gno", -// "Line": "106", -// "PkgPath": "gno.land/r/demo/tests" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "4" -// } -// } -// ] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "4" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:9" -// }, -// "FileName": "tests.gno", -// "IsMethod": false, -// "Name": "IsCallerParentPath", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "tests.gno", -// "Line": "110", -// "PkgPath": "gno.land/r/demo/tests" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "4" -// } -// } -// ] -// } -// } +// "Value": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/r/demo/tests_foo.FooStringer" // }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "4" -// } -// } -// ] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:9" -// }, -// "FileName": "tests.gno", -// "IsMethod": false, -// "Name": "HasCallerSameNamespace", -// "NativeName": "", -// "NativePkg": "", -// "PkgPath": "gno.land/r/demo/tests", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "Column": "1", -// "File": "tests.gno", -// "Line": "114", -// "PkgPath": "gno.land/r/demo/tests" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [], -// "Results": [ -// { -// "Embedded": false, -// "Name": "", -// "Tag": "", -// "Type": { -// "@type": "/gno.PrimitiveType", -// "value": "4" -// } -// } -// ] -// } -// } +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "2a96c074210eb2db31b7941e31d62deb04e59937", +// "ObjectID": "0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:16" // } -// ] +// } // } // d[0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:14] // switchrealm["gno.land/r/demo/tests_foo"] From 8cca690ae01f22f12683a6e4bf2f1265ac4cff04 Mon Sep 17 00:00:00 2001 From: Nemanja Matic <106317308+Nemanya8@users.noreply.github.com> Date: Wed, 15 Jan 2025 05:05:02 -0500 Subject: [PATCH 13/18] feat(cmd/gno): allow gno test to default to the current directory (#3453) This PR resolves https://github.com/gnolang/gno/issues/3420 by enhancing the gno test command. The command now assumes the current directory (.) as the default path when no directory is specified, aligning its behavior with go test. Changes to CI: - Removed the no_args test. - Added new tests to cover the following scenarios: - Valid test execution. - Valid file-based test. - Test with flags. - Empty directory. - Empty test file. This PR is a repost of https://github.com/gnolang/gno/pull/3429 with a cleaner commit history. CC @notJoon @moul --------- Co-authored-by: Nemanya21 Co-authored-by: Leon Hudak <33522493+leohhhn@users.noreply.github.com> --- gnovm/cmd/gno/test.go | 6 +- gnovm/cmd/gno/testdata/test/no_args.txtar | 6 -- .../gno/testdata/test/no_path_empty_dir.txtar | 6 ++ .../gno/testdata/test/no_path_empty_gno.txtar | 8 ++ .../gno/testdata/test/no_path_flag_run.txtar | 99 +++++++++++++++++++ 5 files changed, 117 insertions(+), 8 deletions(-) delete mode 100644 gnovm/cmd/gno/testdata/test/no_args.txtar create mode 100644 gnovm/cmd/gno/testdata/test/no_path_empty_dir.txtar create mode 100644 gnovm/cmd/gno/testdata/test/no_path_empty_gno.txtar create mode 100644 gnovm/cmd/gno/testdata/test/no_path_flag_run.txtar diff --git a/gnovm/cmd/gno/test.go b/gnovm/cmd/gno/test.go index fec0de7c221..ea06b25d8e2 100644 --- a/gnovm/cmd/gno/test.go +++ b/gnovm/cmd/gno/test.go @@ -146,8 +146,9 @@ func (c *testCfg) RegisterFlags(fs *flag.FlagSet) { } func execTest(cfg *testCfg, args []string, io commands.IO) error { - if len(args) < 1 { - return flag.ErrHelp + // Default to current directory if no args provided + if len(args) == 0 { + args = []string{"."} } // guess opts.RootDir @@ -159,6 +160,7 @@ func execTest(cfg *testCfg, args []string, io commands.IO) error { if err != nil { return fmt.Errorf("list targets from patterns: %w", err) } + if len(paths) == 0 { io.ErrPrintln("no packages to test") return nil diff --git a/gnovm/cmd/gno/testdata/test/no_args.txtar b/gnovm/cmd/gno/testdata/test/no_args.txtar deleted file mode 100644 index bd9cd4fc965..00000000000 --- a/gnovm/cmd/gno/testdata/test/no_args.txtar +++ /dev/null @@ -1,6 +0,0 @@ -# Run gno test without args - -! gno test - -! stdout .+ -stderr 'USAGE' diff --git a/gnovm/cmd/gno/testdata/test/no_path_empty_dir.txtar b/gnovm/cmd/gno/testdata/test/no_path_empty_dir.txtar new file mode 100644 index 00000000000..6f8b54d7ea4 --- /dev/null +++ b/gnovm/cmd/gno/testdata/test/no_path_empty_dir.txtar @@ -0,0 +1,6 @@ +# Run gno test without path argument on an empty dir + +gno test + +! stdout .+ +stderr '[no test files]' \ No newline at end of file diff --git a/gnovm/cmd/gno/testdata/test/no_path_empty_gno.txtar b/gnovm/cmd/gno/testdata/test/no_path_empty_gno.txtar new file mode 100644 index 00000000000..846ce5bbd88 --- /dev/null +++ b/gnovm/cmd/gno/testdata/test/no_path_empty_gno.txtar @@ -0,0 +1,8 @@ +# Test empty gno without path argument + +gno test + +! stdout .+ +stderr '\? \. \[no test files\]' + +-- empty.gno -- \ No newline at end of file diff --git a/gnovm/cmd/gno/testdata/test/no_path_flag_run.txtar b/gnovm/cmd/gno/testdata/test/no_path_flag_run.txtar new file mode 100644 index 00000000000..3db2a4c9295 --- /dev/null +++ b/gnovm/cmd/gno/testdata/test/no_path_flag_run.txtar @@ -0,0 +1,99 @@ +# Run test on gno.land/p/demo/ufmt without path argument + +gno test + +gno test -v + +! stdout .+ +stderr '=== RUN TestRun/hello' +stderr '=== RUN TestRun/hi_you' +stderr '=== RUN TestRun/hi_me' +stderr '=== RUN TestRun' +stderr '--- PASS: TestRun' + +gno test -v -run .* + +! stdout .+ +stderr '=== RUN TestRun/hello' +stderr '=== RUN TestRun/hi_you' +stderr '=== RUN TestRun/hi_me' +stderr '=== RUN TestRun' +stderr '--- PASS: TestRun' + +gno test -v -run NotExists + +! stdout .+ +! stderr '=== RUN TestRun' + +gno test -v -run .*/hello + +! stdout .+ +stderr '=== RUN TestRun/hello' +! stderr '=== RUN TestRun/hi_you' +! stderr '=== RUN TestRun/hi_me' +stderr '=== RUN TestRun' +stderr '--- PASS: TestRun' + +gno test -v -run .*/hi + +! stdout .+ +! stderr '=== RUN TestRun/hello' +stderr '=== RUN TestRun/hi_you' +stderr '=== RUN TestRun/hi_me' +stderr '=== RUN TestRun' +stderr '--- PASS: TestRun' + +gno test -v -run .*/NotExists + +! stdout .+ +stderr '=== RUN TestRun' +stderr '--- PASS: TestRun' + +gno test -v -run Run/.* + +! stdout .+ +stderr '=== RUN TestRun/hello' +stderr '=== RUN TestRun/hi_you' +stderr '=== RUN TestRun/hi_me' +stderr '=== RUN TestRun' +stderr '--- PASS: TestRun' + +gno test -v -run Run/ + +! stdout .+ +stderr '=== RUN TestRun/hello' +stderr '=== RUN TestRun/hi_you' +stderr '=== RUN TestRun/hi_me' +stderr '=== RUN TestRun' +stderr '--- PASS: TestRun' + +gno test -v -run Run/hello + +! stdout .+ +stderr '=== RUN TestRun/hello' +! stderr '=== RUN TestRun/hi_you' +! stderr '=== RUN TestRun/hi_me' +stderr '=== RUN TestRun' +stderr '--- PASS: TestRun' + +-- run.gno -- +package run + +-- run_test.gno -- +package run + +import ( + "fmt" + "testing" +) + +func TestRun(t *testing.T) { + cases := []string { + "hello", + "hi you", + "hi me", + } + for _, tc := range cases { + t.Run(tc, func(t *testing.T) {}) + } +} \ No newline at end of file From ae0c9f40340b854c0a033b8552dfa78ec9650b9b Mon Sep 17 00:00:00 2001 From: Alexis Colin Date: Wed, 15 Jan 2025 19:13:21 +0900 Subject: [PATCH 14/18] fix(gnoweb): fix mobile breadcrumb (#3516) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes the breadcrumb bar of gnoweb on mobile. Before: Capture d’écran 2025-01-15 à 16 02 30 After: Capture d’écran 2025-01-15 à 16 03 05 --- gno.land/pkg/gnoweb/components/breadcrumb.gohtml | 6 +++--- gno.land/pkg/gnoweb/public/styles.css | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gno.land/pkg/gnoweb/components/breadcrumb.gohtml b/gno.land/pkg/gnoweb/components/breadcrumb.gohtml index 9295b17b6f5..3824eb5894f 100644 --- a/gno.land/pkg/gnoweb/components/breadcrumb.gohtml +++ b/gno.land/pkg/gnoweb/components/breadcrumb.gohtml @@ -2,15 +2,15 @@
    {{- range $index, $part := .Parts }} {{- if $index }} -
  1. +
  2. {{- else }} -
  3. +
  4. {{- end }} {{ $part.Name }}
  5. {{- end }} {{- if .Args }} -
  6. +
  7. {{ .Args }}
  8. {{- end }} diff --git a/gno.land/pkg/gnoweb/public/styles.css b/gno.land/pkg/gnoweb/public/styles.css index a6771695454..4ff1a266c0c 100644 --- a/gno.land/pkg/gnoweb/public/styles.css +++ b/gno.land/pkg/gnoweb/public/styles.css @@ -1,3 +1,3 @@ @font-face{font-family:Roboto;font-style:normal;font-weight:900;font-display:swap;src:url(fonts/roboto/roboto-mono-normal.woff2) format("woff2"),url(fonts/roboto/roboto-mono-normal.woff) format("woff")}@font-face{font-family:Inter var;font-weight:100 900;font-display:block;font-style:oblique 0deg 10deg;src:url(fonts/intervar/Intervar.woff2) format("woff2")}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: } -/*! tailwindcss v3.4.14 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #bdbdbd}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:Roboto,Menlo,Consolas,Ubuntu Mono,Roboto Mono,DejaVu Sans Mono,monospace;;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#7c7c7c}input::placeholder,textarea::placeholder{opacity:1;color:#7c7c7c}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}html{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity));font-family:Inter var,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji,sans-serif;font-size:1rem;--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity));font-feature-settings:"kern" on,"liga" on,"calt" on,"zero" on;-webkit-font-feature-settings:"kern" on,"liga" on,"calt" on,"zero" on;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;text-size-adjust:100%;-moz-osx-font-smoothing:grayscale;font-smoothing:antialiased;font-variant-ligatures:contextual common-ligatures;font-kerning:normal;text-rendering:optimizeLegibility}svg{max-height:100%;max-width:100%}form{margin-top:0;margin-bottom:0}.realm-content{overflow-wrap:break-word;padding-top:2.5rem;font-size:1rem}.realm-content>:first-child{margin-top:0!important}.realm-content a{font-weight:500;--tw-text-opacity:1;color:rgb(34 108 87/var(--tw-text-opacity))}.realm-content a:hover{text-decoration-line:underline}.realm-content h1,.realm-content h2,.realm-content h3,.realm-content h4{margin-top:3rem;line-height:1.25;--tw-text-opacity:1;color:rgb(8 8 9/var(--tw-text-opacity))}.realm-content h2,.realm-content h2 *{font-weight:700}.realm-content h3,.realm-content h3 *,.realm-content h4,.realm-content h4 *{font-weight:600}.realm-content h1+h2,.realm-content h2+h3,.realm-content h3+h4{margin-top:1rem}.realm-content h1{font-size:2.375rem;font-weight:700}.realm-content h2{font-size:1.5rem}.realm-content h3{margin-top:2.5rem;font-size:1.25rem}.realm-content h3,.realm-content h4{--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity))}.realm-content h4{margin-top:1.5rem;margin-bottom:1.5rem;font-size:1.125rem;font-weight:500}.realm-content p{margin-top:1.25rem;margin-bottom:1.25rem}.realm-content strong{font-weight:700;--tw-text-opacity:1;color:rgb(8 8 9/var(--tw-text-opacity))}.realm-content strong *{font-weight:700}.realm-content em{font-style:oblique 10deg}.realm-content blockquote{margin-top:1rem;margin-bottom:1rem;border-left-width:4px;--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity));padding-left:1rem;--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity));font-style:oblique 10deg}.realm-content ol,.realm-content ul{margin-top:1.5rem;margin-bottom:1.5rem;padding-left:1rem}.realm-content ol li,.realm-content ul li{margin-bottom:.5rem}.realm-content img{margin-top:2rem;margin-bottom:2rem;max-width:100%}.realm-content figure{margin-top:1.5rem;margin-bottom:1.5rem;text-align:center}.realm-content figcaption{font-size:.875rem;--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity))}.realm-content :not(pre)>code{border-radius:.25rem;background-color:rgb(226 226 226/var(--tw-bg-opacity));padding:.125rem .25rem;font-size:.96em}.realm-content :not(pre)>code,.realm-content pre{--tw-bg-opacity:1;font-family:Roboto,Menlo,Consolas,Ubuntu Mono,Roboto Mono,DejaVu Sans Mono,monospace;}.realm-content pre{overflow-x:auto;border-radius:.375rem;background-color:rgb(240 240 240/var(--tw-bg-opacity));padding:1rem}.realm-content hr{margin-top:2.5rem;margin-bottom:2.5rem;border-top-width:1px;--tw-border-opacity:1;border-color:rgb(226 226 226/var(--tw-border-opacity))}.realm-content table{margin-top:2rem;margin-bottom:2rem;width:100%;border-collapse:collapse}.realm-content td,.realm-content th{border-width:1px;--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity));padding:.5rem 1rem}.realm-content th{--tw-bg-opacity:1;background-color:rgb(226 226 226/var(--tw-bg-opacity));font-weight:700}.realm-content caption{margin-top:.5rem;text-align:left;font-size:.875rem;--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity))}.realm-content q{margin-top:1.5rem;margin-bottom:1.5rem;border-left-width:4px;--tw-border-opacity:1;border-left-color:rgb(204 204 204/var(--tw-border-opacity));padding-left:1rem;--tw-text-opacity:1;color:rgb(85 85 85/var(--tw-text-opacity));font-style:oblique 10deg;quotes:"“" "”" "‘" "’"}.realm-content q:after,.realm-content q:before{margin-right:.25rem;font-size:1.5rem;--tw-text-opacity:1;color:rgb(153 153 153/var(--tw-text-opacity));content:open-quote;vertical-align:-.4rem}.realm-content q:after{content:close-quote}.realm-content q:before{content:open-quote}.realm-content q:after{content:close-quote}.realm-content ol ol,.realm-content ol ul,.realm-content ul ol,.realm-content ul ul{margin-top:.75rem;margin-bottom:.5rem;padding-left:1rem}.realm-content ul{list-style-type:disc}.realm-content ol{list-style-type:decimal}.realm-content table th:first-child,.realm-content td:first-child{padding-left:0}.realm-content table th:last-child,.realm-content td:last-child{padding-right:0}.realm-content abbr[title]{cursor:help;border-bottom-width:1px;border-style:dotted}.realm-content details{margin-top:1.25rem;margin-bottom:1.25rem}.realm-content summary{cursor:pointer;font-weight:700}.realm-content a code{color:inherit}.realm-content video{margin-top:2rem;margin-bottom:2rem;max-width:100%}.realm-content math{font-family:Roboto,Menlo,Consolas,Ubuntu Mono,Roboto Mono,DejaVu Sans Mono,monospace;}.realm-content small{font-size:.875rem}.realm-content del{text-decoration-line:line-through}.realm-content sub{vertical-align:sub;font-size:.75rem}.realm-content sup{vertical-align:super;font-size:.75rem}.realm-content button,.realm-content input{border-width:1px;--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity));padding:.5rem 1rem}main :is(h1,h2,h3,h4){scroll-margin-top:6rem}::-moz-selection{--tw-bg-opacity:1;background-color:rgb(34 108 87/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}::selection{--tw-bg-opacity:1;background-color:rgb(34 108 87/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.sidemenu .peer:checked+label>svg{--tw-text-opacity:1;color:rgb(34 108 87/var(--tw-text-opacity))}.toc-expend-btn:has(#toc-expend:checked)+nav{display:block}.toc-expend-btn:has(#toc-expend:checked) .toc-expend-btn_ico{--tw-rotate:180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.main-header:has(#sidemenu-docs:checked)+main #sidebar #sidebar-docs,.main-header:has(#sidemenu-meta:checked)+main #sidebar #sidebar-meta,.main-header:has(#sidemenu-source:checked)+main #sidebar #sidebar-source,.main-header:has(#sidemenu-summary:checked)+main #sidebar #sidebar-summary{display:block}@media (min-width:40rem){:is(.main-header:has(#sidemenu-source:checked),.main-header:has(#sidemenu-docs:checked),.main-header:has(#sidemenu-meta:checked)) .main-navigation,:is(.main-header:has(#sidemenu-source:checked),.main-header:has(#sidemenu-docs:checked),.main-header:has(#sidemenu-meta:checked))+main .realm-content{grid-column:span 6/span 6}:is(.main-header:has(#sidemenu-source:checked),.main-header:has(#sidemenu-docs:checked),.main-header:has(#sidemenu-meta:checked)) .sidemenu,:is(.main-header:has(#sidemenu-source:checked),.main-header:has(#sidemenu-docs:checked),.main-header:has(#sidemenu-meta:checked))+main #sidebar{grid-column:span 4/span 4}}:is(.main-header:has(#sidemenu-source:checked),.main-header:has(#sidemenu-docs:checked),.main-header:has(#sidemenu-meta:checked))+main #sidebar:before{position:absolute;top:0;left:-1.75rem;z-index:-1;display:block;height:100%;width:50vw;--tw-bg-opacity:1;background-color:rgb(226 226 226/var(--tw-bg-opacity));--tw-content:"";content:var(--tw-content)}main :is(.source-code)>pre{overflow:scroll;border-radius:.375rem;--tw-bg-opacity:1!important;background-color:rgb(255 255 255/var(--tw-bg-opacity))!important;padding:1rem .25rem;font-family:Roboto,Menlo,Consolas,Ubuntu Mono,Roboto Mono,DejaVu Sans Mono,monospace;;font-size:.875rem}@media (min-width:40rem){main :is(.source-code)>pre{padding:2rem .75rem;font-size:1rem}}main .realm-content>pre a:hover{text-decoration-line:none}main :is(.realm-content,.source-code)>pre .chroma-ln:target{background-color:transparent!important}main :is(.realm-content,.source-code)>pre .chroma-line:has(.chroma-ln:target),main :is(.realm-content,.source-code)>pre .chroma-line:has(.chroma-ln:target) .chroma-cl,main :is(.realm-content,.source-code)>pre .chroma-line:has(.chroma-lnlinks:hover),main :is(.realm-content,.source-code)>pre .chroma-line:has(.chroma-lnlinks:hover) .chroma-cl{border-radius:.375rem;--tw-bg-opacity:1!important;background-color:rgb(226 226 226/var(--tw-bg-opacity))!important}main :is(.realm-content,.source-code)>pre .chroma-ln{scroll-margin-top:6rem}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.bottom-1{bottom:.25rem}.left-0{left:0}.right-2{right:.5rem}.right-3{right:.75rem}.top-0{top:0}.top-1\/2{top:50%}.top-14{top:3.5rem}.top-2{top:.5rem}.z-1{z-index:1}.z-max{z-index:9999}.col-span-1{grid-column:span 1/span 1}.col-span-10{grid-column:span 10/span 10}.col-span-3{grid-column:span 3/span 3}.col-span-7{grid-column:span 7/span 7}.row-span-1{grid-row:span 1/span 1}.row-start-1{grid-row-start:1}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-8{margin-bottom:2rem}.mr-10{margin-right:2.5rem}.mt-1{margin-top:.25rem}.mt-10{margin-top:2.5rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.grid{display:grid}.hidden{display:none}.h-10{height:2.5rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-full{height:100%}.max-h-screen{max-height:100vh}.min-h-full{min-height:100%}.min-h-screen{min-height:100vh}.w-10{width:2.5rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-full{width:100%}.min-w-2{min-width:.5rem}.min-w-48{min-width:12rem}.max-w-screen-max{max-width:98.75rem}.shrink-0{flex-shrink:0}.grow-\[2\]{flex-grow:2}.-translate-y-1\/2{--tw-translate-y:-50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-pointer{cursor:pointer}.list-none{list-style-type:none}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-flow-dense{grid-auto-flow:dense}.auto-rows-min{grid-auto-rows:min-content}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-10{grid-template-columns:repeat(10,minmax(0,1fr))}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.items-stretch{align-items:stretch}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-8{gap:2rem}.gap-x-20{-moz-column-gap:5rem;column-gap:5rem}.gap-x-3{-moz-column-gap:.75rem;column-gap:.75rem}.gap-y-2{row-gap:.5rem}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-scroll{overflow:scroll}.whitespace-pre-wrap{white-space:pre-wrap}.rounded{border-radius:.375rem}.rounded-sm{border-radius:.25rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-t{border-top-width:1px}.border-gray-100{--tw-border-opacity:1;border-color:rgb(226 226 226/var(--tw-border-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(226 226 226/var(--tw-bg-opacity))}.bg-gray-300{--tw-bg-opacity:1;background-color:rgb(153 153 153/var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(240 240 240/var(--tw-bg-opacity))}.bg-light{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-transparent{background-color:transparent}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-4{padding:1rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-10{padding-left:2.5rem;padding-right:2.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-px{padding-top:1px;padding-bottom:1px}.pb-24{padding-bottom:6rem}.pb-3{padding-bottom:.75rem}.pb-4{padding-bottom:1rem}.pb-6{padding-bottom:1.5rem}.pb-8{padding-bottom:2rem}.pl-4{padding-left:1rem}.pr-10{padding-right:2.5rem}.pt-0\.5{padding-top:.125rem}.pt-2{padding-top:.5rem}.font-mono{font-family:Roboto,Menlo,Consolas,Ubuntu Mono,Roboto Mono,DejaVu Sans Mono,monospace;}.text-100{font-size:.875rem}.text-200{font-size:1rem}.text-50{font-size:.75rem}.text-600{font-size:1.5rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.capitalize{text-transform:capitalize}.leading-tight{line-height:1.25}.text-gray-300{--tw-text-opacity:1;color:rgb(153 153 153/var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity:1;color:rgb(124 124 124/var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity:1;color:rgb(19 19 19/var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgb(8 8 9/var(--tw-text-opacity))}.text-green-600{--tw-text-opacity:1;color:rgb(34 108 87/var(--tw-text-opacity))}.text-light{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.outline-none{outline:2px solid transparent;outline-offset:2px}.text-stroke{-webkit-text-stroke:currentColor;-webkit-text-stroke-width:.6px}.no-scrollbar::-webkit-scrollbar{display:none}.no-scrollbar{-ms-overflow-style:none;scrollbar-width:none}.\*\:pl-0>*{padding-left:0}.before\:px-\[0\.18rem\]:before{content:var(--tw-content);padding-left:.18rem;padding-right:.18rem}.before\:text-gray-300:before{content:var(--tw-content);--tw-text-opacity:1;color:rgb(153 153 153/var(--tw-text-opacity))}.before\:content-\[\'\/\'\]:before{--tw-content:"/";content:var(--tw-content)}.before\:content-\[\'\:\'\]:before{--tw-content:":";content:var(--tw-content)}.before\:content-\[\'open\'\]:before{--tw-content:"open";content:var(--tw-content)}.after\:pointer-events-none:after{content:var(--tw-content);pointer-events:none}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:bottom-0:after{content:var(--tw-content);bottom:0}.after\:left-0:after{content:var(--tw-content);left:0}.after\:top-0:after{content:var(--tw-content);top:0}.after\:block:after{content:var(--tw-content);display:block}.after\:h-1:after{content:var(--tw-content);height:.25rem}.after\:h-full:after{content:var(--tw-content);height:100%}.after\:w-full:after{content:var(--tw-content);width:100%}.after\:rounded-t-sm:after{content:var(--tw-content);border-top-left-radius:.25rem;border-top-right-radius:.25rem}.after\:bg-gray-100:after{content:var(--tw-content);--tw-bg-opacity:1;background-color:rgb(226 226 226/var(--tw-bg-opacity))}.after\:bg-green-600:after{content:var(--tw-content);--tw-bg-opacity:1;background-color:rgb(34 108 87/var(--tw-bg-opacity))}.first\:border-t:first-child{border-top-width:1px}.hover\:border-gray-300:hover{--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity))}.hover\:bg-gray-100:hover{--tw-bg-opacity:1;background-color:rgb(226 226 226/var(--tw-bg-opacity))}.hover\:bg-gray-50:hover{--tw-bg-opacity:1;background-color:rgb(240 240 240/var(--tw-bg-opacity))}.hover\:bg-green-600:hover{--tw-bg-opacity:1;background-color:rgb(34 108 87/var(--tw-bg-opacity))}.hover\:text-gray-600:hover{--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity))}.hover\:text-green-600:hover{--tw-text-opacity:1;color:rgb(34 108 87/var(--tw-text-opacity))}.hover\:text-light:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.hover\:underline:hover{text-decoration-line:underline}.focus\:border-gray-300:focus{--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity))}.focus\:border-l-gray-300:focus{--tw-border-opacity:1;border-left-color:rgb(153 153 153/var(--tw-border-opacity))}.group:hover .group-hover\:border-gray-300{--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity))}.group:hover .group-hover\:border-l-gray-300{--tw-border-opacity:1;border-left-color:rgb(153 153 153/var(--tw-border-opacity))}.group.is-active .group-\[\.is-active\]\:text-green-600{--tw-text-opacity:1;color:rgb(34 108 87/var(--tw-text-opacity))}.peer:checked~.peer-checked\:before\:content-\[\'close\'\]:before{--tw-content:"close";content:var(--tw-content)}.peer:focus-within~.peer-focus-within\:hidden{display:none}.has-\[ul\:empty\]\:hidden:has(ul:empty){display:none}.has-\[\:focus-within\]\:border-gray-300:has(:focus-within){--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity))}.has-\[\:focus\]\:border-gray-300:has(:focus){--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity))}@media (min-width:30rem){.sm\:gap-6{gap:1.5rem}}@media (min-width:40rem){.md\:col-span-3{grid-column:span 3/span 3}.md\:mb-0{margin-bottom:0}.md\:h-4{height:1rem}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:items-center{align-items:center}.md\:gap-x-8{-moz-column-gap:2rem;column-gap:2rem}.md\:px-10{padding-left:2.5rem;padding-right:2.5rem}.md\:pb-0{padding-bottom:0}}@media (min-width:51.25rem){.lg\:order-2{order:2}.lg\:col-span-3{grid-column:span 3/span 3}.lg\:col-span-7{grid-column:span 7/span 7}.lg\:row-span-2{grid-row:span 2/span 2}.lg\:row-start-1{grid-row-start:1}.lg\:row-start-2{grid-row-start:2}.lg\:mb-4{margin-bottom:1rem}.lg\:mt-0{margin-top:0}.lg\:mt-10{margin-top:2.5rem}.lg\:block{display:block}.lg\:hidden{display:none}.lg\:grid-cols-10{grid-template-columns:repeat(10,minmax(0,1fr))}.lg\:flex-row{flex-direction:row}.lg\:justify-start{justify-content:flex-start}.lg\:justify-between{justify-content:space-between}.lg\:gap-x-20{-moz-column-gap:5rem;column-gap:5rem}.lg\:border-none{border-style:none}.lg\:bg-transparent{background-color:transparent}.lg\:p-0{padding:0}.lg\:px-0{padding-left:0;padding-right:0}.lg\:px-2{padding-left:.5rem;padding-right:.5rem}.lg\:py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.lg\:pb-28{padding-bottom:7rem}.lg\:pt-2{padding-top:.5rem}.lg\:text-200{font-size:1rem}.lg\:font-semibold{font-weight:600}.lg\:hover\:bg-transparent:hover{background-color:transparent}}@media (min-width:63.75rem){.xl\:inline{display:inline}.xl\:hidden{display:none}.xl\:grid-cols-10{grid-template-columns:repeat(10,minmax(0,1fr))}.xl\:flex-row{flex-direction:row}.xl\:items-center{align-items:center}.xl\:gap-20{gap:5rem}.xl\:gap-6{gap:1.5rem}.xl\:pt-0{padding-top:0}}@media (min-width:85.375rem){.xxl\:inline-block{display:inline-block}.xxl\:h-4{height:1rem}.xxl\:w-4{width:1rem}.xxl\:gap-20{gap:5rem}.xxl\:gap-x-32{-moz-column-gap:8rem;column-gap:8rem}.xxl\:pr-1{padding-right:.25rem}} \ No newline at end of file +/*! tailwindcss v3.4.14 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #bdbdbd}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:Roboto,Menlo,Consolas,Ubuntu Mono,Roboto Mono,DejaVu Sans Mono,monospace;;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#7c7c7c}input::placeholder,textarea::placeholder{opacity:1;color:#7c7c7c}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}html{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity));font-family:Inter var,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji,sans-serif;font-size:1rem;--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity));font-feature-settings:"kern" on,"liga" on,"calt" on,"zero" on;-webkit-font-feature-settings:"kern" on,"liga" on,"calt" on,"zero" on;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;text-size-adjust:100%;-moz-osx-font-smoothing:grayscale;font-smoothing:antialiased;font-variant-ligatures:contextual common-ligatures;font-kerning:normal;text-rendering:optimizeLegibility}svg{max-height:100%;max-width:100%}form{margin-top:0;margin-bottom:0}.realm-content{overflow-wrap:break-word;padding-top:2.5rem;font-size:1rem}.realm-content>:first-child{margin-top:0!important}.realm-content a{font-weight:500;--tw-text-opacity:1;color:rgb(34 108 87/var(--tw-text-opacity))}.realm-content a:hover{text-decoration-line:underline}.realm-content h1,.realm-content h2,.realm-content h3,.realm-content h4{margin-top:3rem;line-height:1.25;--tw-text-opacity:1;color:rgb(8 8 9/var(--tw-text-opacity))}.realm-content h2,.realm-content h2 *{font-weight:700}.realm-content h3,.realm-content h3 *,.realm-content h4,.realm-content h4 *{font-weight:600}.realm-content h1+h2,.realm-content h2+h3,.realm-content h3+h4{margin-top:1rem}.realm-content h1{font-size:2.375rem;font-weight:700}.realm-content h2{font-size:1.5rem}.realm-content h3{margin-top:2.5rem;font-size:1.25rem}.realm-content h3,.realm-content h4{--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity))}.realm-content h4{margin-top:1.5rem;margin-bottom:1.5rem;font-size:1.125rem;font-weight:500}.realm-content p{margin-top:1.25rem;margin-bottom:1.25rem}.realm-content strong{font-weight:700;--tw-text-opacity:1;color:rgb(8 8 9/var(--tw-text-opacity))}.realm-content strong *{font-weight:700}.realm-content em{font-style:oblique 10deg}.realm-content blockquote{margin-top:1rem;margin-bottom:1rem;border-left-width:4px;--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity));padding-left:1rem;--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity));font-style:oblique 10deg}.realm-content ol,.realm-content ul{margin-top:1.5rem;margin-bottom:1.5rem;padding-left:1rem}.realm-content ol li,.realm-content ul li{margin-bottom:.5rem}.realm-content img{margin-top:2rem;margin-bottom:2rem;max-width:100%}.realm-content figure{margin-top:1.5rem;margin-bottom:1.5rem;text-align:center}.realm-content figcaption{font-size:.875rem;--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity))}.realm-content :not(pre)>code{border-radius:.25rem;background-color:rgb(226 226 226/var(--tw-bg-opacity));padding:.125rem .25rem;font-size:.96em}.realm-content :not(pre)>code,.realm-content pre{--tw-bg-opacity:1;font-family:Roboto,Menlo,Consolas,Ubuntu Mono,Roboto Mono,DejaVu Sans Mono,monospace;}.realm-content pre{overflow-x:auto;border-radius:.375rem;background-color:rgb(240 240 240/var(--tw-bg-opacity));padding:1rem}.realm-content hr{margin-top:2.5rem;margin-bottom:2.5rem;border-top-width:1px;--tw-border-opacity:1;border-color:rgb(226 226 226/var(--tw-border-opacity))}.realm-content table{margin-top:2rem;margin-bottom:2rem;width:100%;border-collapse:collapse}.realm-content td,.realm-content th{border-width:1px;--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity));padding:.5rem 1rem}.realm-content th{--tw-bg-opacity:1;background-color:rgb(226 226 226/var(--tw-bg-opacity));font-weight:700}.realm-content caption{margin-top:.5rem;text-align:left;font-size:.875rem;--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity))}.realm-content q{margin-top:1.5rem;margin-bottom:1.5rem;border-left-width:4px;--tw-border-opacity:1;border-left-color:rgb(204 204 204/var(--tw-border-opacity));padding-left:1rem;--tw-text-opacity:1;color:rgb(85 85 85/var(--tw-text-opacity));font-style:oblique 10deg;quotes:"“" "”" "‘" "’"}.realm-content q:after,.realm-content q:before{margin-right:.25rem;font-size:1.5rem;--tw-text-opacity:1;color:rgb(153 153 153/var(--tw-text-opacity));content:open-quote;vertical-align:-.4rem}.realm-content q:after{content:close-quote}.realm-content q:before{content:open-quote}.realm-content q:after{content:close-quote}.realm-content ol ol,.realm-content ol ul,.realm-content ul ol,.realm-content ul ul{margin-top:.75rem;margin-bottom:.5rem;padding-left:1rem}.realm-content ul{list-style-type:disc}.realm-content ol{list-style-type:decimal}.realm-content table th:first-child,.realm-content td:first-child{padding-left:0}.realm-content table th:last-child,.realm-content td:last-child{padding-right:0}.realm-content abbr[title]{cursor:help;border-bottom-width:1px;border-style:dotted}.realm-content details{margin-top:1.25rem;margin-bottom:1.25rem}.realm-content summary{cursor:pointer;font-weight:700}.realm-content a code{color:inherit}.realm-content video{margin-top:2rem;margin-bottom:2rem;max-width:100%}.realm-content math{font-family:Roboto,Menlo,Consolas,Ubuntu Mono,Roboto Mono,DejaVu Sans Mono,monospace;}.realm-content small{font-size:.875rem}.realm-content del{text-decoration-line:line-through}.realm-content sub{vertical-align:sub;font-size:.75rem}.realm-content sup{vertical-align:super;font-size:.75rem}.realm-content button,.realm-content input{border-width:1px;--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity));padding:.5rem 1rem}main :is(h1,h2,h3,h4){scroll-margin-top:6rem}::-moz-selection{--tw-bg-opacity:1;background-color:rgb(34 108 87/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}::selection{--tw-bg-opacity:1;background-color:rgb(34 108 87/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.sidemenu .peer:checked+label>svg{--tw-text-opacity:1;color:rgb(34 108 87/var(--tw-text-opacity))}.toc-expend-btn:has(#toc-expend:checked)+nav{display:block}.toc-expend-btn:has(#toc-expend:checked) .toc-expend-btn_ico{--tw-rotate:180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.main-header:has(#sidemenu-docs:checked)+main #sidebar #sidebar-docs,.main-header:has(#sidemenu-meta:checked)+main #sidebar #sidebar-meta,.main-header:has(#sidemenu-source:checked)+main #sidebar #sidebar-source,.main-header:has(#sidemenu-summary:checked)+main #sidebar #sidebar-summary{display:block}@media (min-width:40rem){:is(.main-header:has(#sidemenu-source:checked),.main-header:has(#sidemenu-docs:checked),.main-header:has(#sidemenu-meta:checked)) .main-navigation,:is(.main-header:has(#sidemenu-source:checked),.main-header:has(#sidemenu-docs:checked),.main-header:has(#sidemenu-meta:checked))+main .realm-content{grid-column:span 6/span 6}:is(.main-header:has(#sidemenu-source:checked),.main-header:has(#sidemenu-docs:checked),.main-header:has(#sidemenu-meta:checked)) .sidemenu,:is(.main-header:has(#sidemenu-source:checked),.main-header:has(#sidemenu-docs:checked),.main-header:has(#sidemenu-meta:checked))+main #sidebar{grid-column:span 4/span 4}}:is(.main-header:has(#sidemenu-source:checked),.main-header:has(#sidemenu-docs:checked),.main-header:has(#sidemenu-meta:checked))+main #sidebar:before{position:absolute;top:0;left:-1.75rem;z-index:-1;display:block;height:100%;width:50vw;--tw-bg-opacity:1;background-color:rgb(226 226 226/var(--tw-bg-opacity));--tw-content:"";content:var(--tw-content)}main :is(.source-code)>pre{overflow:scroll;border-radius:.375rem;--tw-bg-opacity:1!important;background-color:rgb(255 255 255/var(--tw-bg-opacity))!important;padding:1rem .25rem;font-family:Roboto,Menlo,Consolas,Ubuntu Mono,Roboto Mono,DejaVu Sans Mono,monospace;;font-size:.875rem}@media (min-width:40rem){main :is(.source-code)>pre{padding:2rem .75rem;font-size:1rem}}main .realm-content>pre a:hover{text-decoration-line:none}main :is(.realm-content,.source-code)>pre .chroma-ln:target{background-color:transparent!important}main :is(.realm-content,.source-code)>pre .chroma-line:has(.chroma-ln:target),main :is(.realm-content,.source-code)>pre .chroma-line:has(.chroma-ln:target) .chroma-cl,main :is(.realm-content,.source-code)>pre .chroma-line:has(.chroma-lnlinks:hover),main :is(.realm-content,.source-code)>pre .chroma-line:has(.chroma-lnlinks:hover) .chroma-cl{border-radius:.375rem;--tw-bg-opacity:1!important;background-color:rgb(226 226 226/var(--tw-bg-opacity))!important}main :is(.realm-content,.source-code)>pre .chroma-ln{scroll-margin-top:6rem}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.bottom-1{bottom:.25rem}.left-0{left:0}.right-2{right:.5rem}.right-3{right:.75rem}.top-0{top:0}.top-1\/2{top:50%}.top-14{top:3.5rem}.top-2{top:.5rem}.z-1{z-index:1}.z-max{z-index:9999}.col-span-1{grid-column:span 1/span 1}.col-span-10{grid-column:span 10/span 10}.col-span-3{grid-column:span 3/span 3}.col-span-7{grid-column:span 7/span 7}.row-span-1{grid-row:span 1/span 1}.row-start-1{grid-row-start:1}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-8{margin-bottom:2rem}.mr-10{margin-right:2.5rem}.mt-1{margin-top:.25rem}.mt-10{margin-top:2.5rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.grid{display:grid}.hidden{display:none}.h-10{height:2.5rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-full{height:100%}.max-h-screen{max-height:100vh}.min-h-full{min-height:100%}.min-h-screen{min-height:100vh}.w-10{width:2.5rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-full{width:100%}.min-w-2{min-width:.5rem}.min-w-48{min-width:12rem}.max-w-screen-max{max-width:98.75rem}.shrink-0{flex-shrink:0}.grow-\[2\]{flex-grow:2}.-translate-y-1\/2{--tw-translate-y:-50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-pointer{cursor:pointer}.list-none{list-style-type:none}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-flow-dense{grid-auto-flow:dense}.auto-rows-min{grid-auto-rows:min-content}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-10{grid-template-columns:repeat(10,minmax(0,1fr))}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.items-stretch{align-items:stretch}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-8{gap:2rem}.gap-x-20{-moz-column-gap:5rem;column-gap:5rem}.gap-x-3{-moz-column-gap:.75rem;column-gap:.75rem}.gap-y-2{row-gap:.5rem}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-scroll{overflow:scroll}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.rounded{border-radius:.375rem}.rounded-sm{border-radius:.25rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-t{border-top-width:1px}.border-gray-100{--tw-border-opacity:1;border-color:rgb(226 226 226/var(--tw-border-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(226 226 226/var(--tw-bg-opacity))}.bg-gray-300{--tw-bg-opacity:1;background-color:rgb(153 153 153/var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(240 240 240/var(--tw-bg-opacity))}.bg-light{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-transparent{background-color:transparent}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-4{padding:1rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-10{padding-left:2.5rem;padding-right:2.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-px{padding-top:1px;padding-bottom:1px}.pb-24{padding-bottom:6rem}.pb-3{padding-bottom:.75rem}.pb-4{padding-bottom:1rem}.pb-6{padding-bottom:1.5rem}.pb-8{padding-bottom:2rem}.pl-4{padding-left:1rem}.pr-10{padding-right:2.5rem}.pt-0\.5{padding-top:.125rem}.pt-2{padding-top:.5rem}.font-mono{font-family:Roboto,Menlo,Consolas,Ubuntu Mono,Roboto Mono,DejaVu Sans Mono,monospace;}.text-100{font-size:.875rem}.text-200{font-size:1rem}.text-50{font-size:.75rem}.text-600{font-size:1.5rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.capitalize{text-transform:capitalize}.leading-tight{line-height:1.25}.text-gray-300{--tw-text-opacity:1;color:rgb(153 153 153/var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity:1;color:rgb(124 124 124/var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity:1;color:rgb(19 19 19/var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgb(8 8 9/var(--tw-text-opacity))}.text-green-600{--tw-text-opacity:1;color:rgb(34 108 87/var(--tw-text-opacity))}.text-light{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.outline-none{outline:2px solid transparent;outline-offset:2px}.text-stroke{-webkit-text-stroke:currentColor;-webkit-text-stroke-width:.6px}.no-scrollbar::-webkit-scrollbar{display:none}.no-scrollbar{-ms-overflow-style:none;scrollbar-width:none}.\*\:pl-0>*{padding-left:0}.before\:px-\[0\.18rem\]:before{content:var(--tw-content);padding-left:.18rem;padding-right:.18rem}.before\:text-gray-300:before{content:var(--tw-content);--tw-text-opacity:1;color:rgb(153 153 153/var(--tw-text-opacity))}.before\:content-\[\'\/\'\]:before{--tw-content:"/";content:var(--tw-content)}.before\:content-\[\'\:\'\]:before{--tw-content:":";content:var(--tw-content)}.before\:content-\[\'open\'\]:before{--tw-content:"open";content:var(--tw-content)}.after\:pointer-events-none:after{content:var(--tw-content);pointer-events:none}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:bottom-0:after{content:var(--tw-content);bottom:0}.after\:left-0:after{content:var(--tw-content);left:0}.after\:top-0:after{content:var(--tw-content);top:0}.after\:block:after{content:var(--tw-content);display:block}.after\:h-1:after{content:var(--tw-content);height:.25rem}.after\:h-full:after{content:var(--tw-content);height:100%}.after\:w-full:after{content:var(--tw-content);width:100%}.after\:rounded-t-sm:after{content:var(--tw-content);border-top-left-radius:.25rem;border-top-right-radius:.25rem}.after\:bg-gray-100:after{content:var(--tw-content);--tw-bg-opacity:1;background-color:rgb(226 226 226/var(--tw-bg-opacity))}.after\:bg-green-600:after{content:var(--tw-content);--tw-bg-opacity:1;background-color:rgb(34 108 87/var(--tw-bg-opacity))}.first\:border-t:first-child{border-top-width:1px}.hover\:border-gray-300:hover{--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity))}.hover\:bg-gray-100:hover{--tw-bg-opacity:1;background-color:rgb(226 226 226/var(--tw-bg-opacity))}.hover\:bg-gray-50:hover{--tw-bg-opacity:1;background-color:rgb(240 240 240/var(--tw-bg-opacity))}.hover\:bg-green-600:hover{--tw-bg-opacity:1;background-color:rgb(34 108 87/var(--tw-bg-opacity))}.hover\:text-gray-600:hover{--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity))}.hover\:text-green-600:hover{--tw-text-opacity:1;color:rgb(34 108 87/var(--tw-text-opacity))}.hover\:text-light:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.hover\:underline:hover{text-decoration-line:underline}.focus\:border-gray-300:focus{--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity))}.focus\:border-l-gray-300:focus{--tw-border-opacity:1;border-left-color:rgb(153 153 153/var(--tw-border-opacity))}.group:hover .group-hover\:border-gray-300{--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity))}.group:hover .group-hover\:border-l-gray-300{--tw-border-opacity:1;border-left-color:rgb(153 153 153/var(--tw-border-opacity))}.group.is-active .group-\[\.is-active\]\:text-green-600{--tw-text-opacity:1;color:rgb(34 108 87/var(--tw-text-opacity))}.peer:checked~.peer-checked\:before\:content-\[\'close\'\]:before{--tw-content:"close";content:var(--tw-content)}.peer:focus-within~.peer-focus-within\:hidden{display:none}.has-\[ul\:empty\]\:hidden:has(ul:empty){display:none}.has-\[\:focus-within\]\:border-gray-300:has(:focus-within){--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity))}.has-\[\:focus\]\:border-gray-300:has(:focus){--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity))}@media (min-width:30rem){.sm\:gap-6{gap:1.5rem}}@media (min-width:40rem){.md\:col-span-3{grid-column:span 3/span 3}.md\:mb-0{margin-bottom:0}.md\:h-4{height:1rem}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:items-center{align-items:center}.md\:gap-x-8{-moz-column-gap:2rem;column-gap:2rem}.md\:px-10{padding-left:2.5rem;padding-right:2.5rem}.md\:pb-0{padding-bottom:0}}@media (min-width:51.25rem){.lg\:order-2{order:2}.lg\:col-span-3{grid-column:span 3/span 3}.lg\:col-span-7{grid-column:span 7/span 7}.lg\:row-span-2{grid-row:span 2/span 2}.lg\:row-start-1{grid-row-start:1}.lg\:row-start-2{grid-row-start:2}.lg\:mb-4{margin-bottom:1rem}.lg\:mt-0{margin-top:0}.lg\:mt-10{margin-top:2.5rem}.lg\:block{display:block}.lg\:hidden{display:none}.lg\:grid-cols-10{grid-template-columns:repeat(10,minmax(0,1fr))}.lg\:flex-row{flex-direction:row}.lg\:justify-start{justify-content:flex-start}.lg\:justify-between{justify-content:space-between}.lg\:gap-x-20{-moz-column-gap:5rem;column-gap:5rem}.lg\:border-none{border-style:none}.lg\:bg-transparent{background-color:transparent}.lg\:p-0{padding:0}.lg\:px-0{padding-left:0;padding-right:0}.lg\:px-2{padding-left:.5rem;padding-right:.5rem}.lg\:py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.lg\:pb-28{padding-bottom:7rem}.lg\:pt-2{padding-top:.5rem}.lg\:text-200{font-size:1rem}.lg\:font-semibold{font-weight:600}.lg\:hover\:bg-transparent:hover{background-color:transparent}}@media (min-width:63.75rem){.xl\:inline{display:inline}.xl\:hidden{display:none}.xl\:grid-cols-10{grid-template-columns:repeat(10,minmax(0,1fr))}.xl\:flex-row{flex-direction:row}.xl\:items-center{align-items:center}.xl\:gap-20{gap:5rem}.xl\:gap-6{gap:1.5rem}.xl\:pt-0{padding-top:0}}@media (min-width:85.375rem){.xxl\:inline-block{display:inline-block}.xxl\:h-4{height:1rem}.xxl\:w-4{width:1rem}.xxl\:gap-20{gap:5rem}.xxl\:gap-x-32{-moz-column-gap:8rem;column-gap:8rem}.xxl\:pr-1{padding-right:.25rem}} \ No newline at end of file From f91eb4ace354b0798752d06eb228311d4b490147 Mon Sep 17 00:00:00 2001 From: Manfred Touron <94029+moul@users.noreply.github.com> Date: Wed, 15 Jan 2025 15:41:27 +0100 Subject: [PATCH 15/18] feat(examples): add rolist (#3400) - [x] add `avl/rolist` - [x] add `I...` interfaces for `avl/...`'s main structs. --------- Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/avl/list/list.gno | 16 ++ examples/gno.land/p/demo/avl/rolist/gno.mod | 1 + .../gno.land/p/demo/avl/rolist/rolist.gno | 119 +++++++++++++ .../p/demo/avl/rolist/rolist_test.gno | 162 ++++++++++++++++++ .../gno.land/p/demo/avl/rotree/rotree.gno | 19 +- 5 files changed, 315 insertions(+), 2 deletions(-) create mode 100644 examples/gno.land/p/demo/avl/rolist/gno.mod create mode 100644 examples/gno.land/p/demo/avl/rolist/rolist.gno create mode 100644 examples/gno.land/p/demo/avl/rolist/rolist_test.gno diff --git a/examples/gno.land/p/demo/avl/list/list.gno b/examples/gno.land/p/demo/avl/list/list.gno index 0875eb66e01..594f5fa2a1f 100644 --- a/examples/gno.land/p/demo/avl/list/list.gno +++ b/examples/gno.land/p/demo/avl/list/list.gno @@ -41,6 +41,22 @@ import ( "gno.land/p/demo/seqid" ) +// IList defines the interface for list operations +type IList interface { + Len() int + Append(values ...interface{}) + Get(index int) interface{} + Set(index int, value interface{}) bool + Delete(index int) (interface{}, bool) + Slice(startIndex, endIndex int) []interface{} + ForEach(fn func(index int, value interface{}) bool) + Clone() *List + DeleteRange(startIndex, endIndex int) int +} + +// Verify List implements IList interface +var _ IList = (*List)(nil) + // List represents an ordered sequence of items backed by an AVL tree type List struct { tree avl.Tree diff --git a/examples/gno.land/p/demo/avl/rolist/gno.mod b/examples/gno.land/p/demo/avl/rolist/gno.mod new file mode 100644 index 00000000000..682513c2cc3 --- /dev/null +++ b/examples/gno.land/p/demo/avl/rolist/gno.mod @@ -0,0 +1 @@ +module gno.land/p/demo/avl/rolist diff --git a/examples/gno.land/p/demo/avl/rolist/rolist.gno b/examples/gno.land/p/demo/avl/rolist/rolist.gno new file mode 100644 index 00000000000..23a85d9c885 --- /dev/null +++ b/examples/gno.land/p/demo/avl/rolist/rolist.gno @@ -0,0 +1,119 @@ +// Package rolist provides a read-only wrapper for list.List with safe value transformation. +// +// It is useful when you want to expose a read-only view of a list while ensuring that +// the sensitive data cannot be modified. +// +// Example: +// +// // Define a user structure with sensitive data +// type User struct { +// Name string +// Balance int +// Internal string // sensitive field +// } +// +// // Create and populate the original list +// privateList := list.New() +// privateList.Append(&User{ +// Name: "Alice", +// Balance: 100, +// Internal: "sensitive", +// }) +// +// // Create a safe transformation function that copies the struct +// // while excluding sensitive data +// makeEntrySafeFn := func(v interface{}) interface{} { +// u := v.(*User) +// return &User{ +// Name: u.Name, +// Balance: u.Balance, +// Internal: "", // omit sensitive data +// } +// } +// +// // Create a read-only view of the list +// publicList := rolist.Wrap(list, makeEntrySafeFn) +// +// // Safely access the data +// value := publicList.Get(0) +// user := value.(*User) +// // user.Name == "Alice" +// // user.Balance == 100 +// // user.Internal == "" (sensitive data is filtered) +package rolist + +import ( + "gno.land/p/demo/avl/list" +) + +// IReadOnlyList defines the read-only operations available on a list. +type IReadOnlyList interface { + Len() int + Get(index int) interface{} + Slice(startIndex, endIndex int) []interface{} + ForEach(fn func(index int, value interface{}) bool) +} + +// ReadOnlyList wraps a list.List and provides read-only access. +type ReadOnlyList struct { + list *list.List + makeEntrySafeFn func(interface{}) interface{} +} + +// Verify interface implementations +var _ IReadOnlyList = (*ReadOnlyList)(nil) +var _ IReadOnlyList = (interface{ list.IList })(nil) // is subset of list.IList + +// Wrap creates a new ReadOnlyList from an existing list.List and a safety transformation function. +// If makeEntrySafeFn is nil, values will be returned as-is without transformation. +func Wrap(list *list.List, makeEntrySafeFn func(interface{}) interface{}) *ReadOnlyList { + return &ReadOnlyList{ + list: list, + makeEntrySafeFn: makeEntrySafeFn, + } +} + +// getSafeValue applies the makeEntrySafeFn if it exists, otherwise returns the original value +func (rol *ReadOnlyList) getSafeValue(value interface{}) interface{} { + if rol.makeEntrySafeFn == nil { + return value + } + return rol.makeEntrySafeFn(value) +} + +// Len returns the number of elements in the list. +func (rol *ReadOnlyList) Len() int { + return rol.list.Len() +} + +// Get returns the value at the specified index, converted to a safe format. +// Returns nil if index is out of bounds. +func (rol *ReadOnlyList) Get(index int) interface{} { + value := rol.list.Get(index) + if value == nil { + return nil + } + return rol.getSafeValue(value) +} + +// Slice returns a slice of values from startIndex (inclusive) to endIndex (exclusive), +// with all values converted to a safe format. +func (rol *ReadOnlyList) Slice(startIndex, endIndex int) []interface{} { + values := rol.list.Slice(startIndex, endIndex) + if values == nil { + return nil + } + + result := make([]interface{}, len(values)) + for i, v := range values { + result[i] = rol.getSafeValue(v) + } + return result +} + +// ForEach iterates through all elements in the list, providing safe versions of the values. +func (rol *ReadOnlyList) ForEach(fn func(index int, value interface{}) bool) { + rol.list.ForEach(func(index int, value interface{}) bool { + return fn(index, rol.getSafeValue(value)) + }) +} diff --git a/examples/gno.land/p/demo/avl/rolist/rolist_test.gno b/examples/gno.land/p/demo/avl/rolist/rolist_test.gno new file mode 100644 index 00000000000..03b0a8cba30 --- /dev/null +++ b/examples/gno.land/p/demo/avl/rolist/rolist_test.gno @@ -0,0 +1,162 @@ +package rolist + +import ( + "testing" + + "gno.land/p/demo/avl/list" +) + +func TestExample(t *testing.T) { + // User represents our internal data structure + type User struct { + ID string + Name string + Balance int + Internal string // sensitive internal data + } + + // Create and populate the original list + l := &list.List{} + l.Append( + &User{ + ID: "1", + Name: "Alice", + Balance: 100, + Internal: "sensitive_data_1", + }, + &User{ + ID: "2", + Name: "Bob", + Balance: 200, + Internal: "sensitive_data_2", + }, + ) + + // Define a makeEntrySafeFn that: + // 1. Creates a defensive copy of the User struct + // 2. Omits sensitive internal data + makeEntrySafeFn := func(v interface{}) interface{} { + originalUser := v.(*User) + return &User{ + ID: originalUser.ID, + Name: originalUser.Name, + Balance: originalUser.Balance, + Internal: "", // Omit sensitive data + } + } + + // Create a read-only view of the list + roList := Wrap(l, makeEntrySafeFn) + + // Test retrieving and verifying a user + t.Run("Get User", func(t *testing.T) { + // Get user from read-only list + value := roList.Get(0) + if value == nil { + t.Fatal("User at index 0 not found") + } + + user := value.(*User) + + // Verify user data is correct + if user.Name != "Alice" || user.Balance != 100 { + t.Errorf("Unexpected user data: got name=%s balance=%d", user.Name, user.Balance) + } + + // Verify sensitive data is not exposed + if user.Internal != "" { + t.Error("Sensitive data should not be exposed") + } + + // Verify it's a different instance than the original + originalUser := l.Get(0).(*User) + if user == originalUser { + t.Error("Read-only list should return a copy, not the original pointer") + } + }) + + // Test slice functionality + t.Run("Slice Users", func(t *testing.T) { + users := roList.Slice(0, 2) + if len(users) != 2 { + t.Fatalf("Expected 2 users, got %d", len(users)) + } + + for _, v := range users { + user := v.(*User) + if user.Internal != "" { + t.Error("Sensitive data exposed in slice") + } + } + }) + + // Test ForEach functionality + t.Run("ForEach Users", func(t *testing.T) { + count := 0 + roList.ForEach(func(index int, value interface{}) bool { + user := value.(*User) + if user.Internal != "" { + t.Error("Sensitive data exposed during iteration") + } + count++ + return false + }) + + if count != 2 { + t.Errorf("Expected 2 users, got %d", count) + } + }) +} + +func TestNilMakeEntrySafeFn(t *testing.T) { + // Create a list with some test data + l := &list.List{} + originalValue := []int{1, 2, 3} + l.Append(originalValue) + + // Create a ReadOnlyList with nil makeEntrySafeFn + roList := Wrap(l, nil) + + // Test that we get back the original value + value := roList.Get(0) + if value == nil { + t.Fatal("Value not found") + } + + // Verify it's the exact same slice (not a copy) + retrievedSlice := value.([]int) + if &retrievedSlice[0] != &originalValue[0] { + t.Error("Expected to get back the original slice reference") + } +} + +func TestReadOnlyList(t *testing.T) { + // Example of a makeEntrySafeFn that appends "_readonly" to demonstrate transformation + makeEntrySafeFn := func(value interface{}) interface{} { + return value.(string) + "_readonly" + } + + l := &list.List{} + l.Append("value1", "value2", "value3") + + roList := Wrap(l, makeEntrySafeFn) + + tests := []struct { + name string + index int + expected interface{} + }{ + {"ExistingIndex0", 0, "value1_readonly"}, + {"ExistingIndex1", 1, "value2_readonly"}, + {"NonExistingIndex", 3, nil}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + value := roList.Get(tt.index) + if value != tt.expected { + t.Errorf("For index %d, expected %v, got %v", tt.index, tt.expected, value) + } + }) + } +} diff --git a/examples/gno.land/p/demo/avl/rotree/rotree.gno b/examples/gno.land/p/demo/avl/rotree/rotree.gno index 3e093c4d0e0..17cb4e20ced 100644 --- a/examples/gno.land/p/demo/avl/rotree/rotree.gno +++ b/examples/gno.land/p/demo/avl/rotree/rotree.gno @@ -82,8 +82,23 @@ type ReadOnlyTree struct { makeEntrySafeFn func(interface{}) interface{} } -// Verify that ReadOnlyTree implements ITree -var _ avl.ITree = (*ReadOnlyTree)(nil) +// IReadOnlyTree defines the read-only operations available on a tree. +type IReadOnlyTree interface { + Size() int + Has(key string) bool + Get(key string) (interface{}, bool) + GetByIndex(index int) (string, interface{}) + Iterate(start, end string, cb avl.IterCbFn) bool + ReverseIterate(start, end string, cb avl.IterCbFn) bool + IterateByOffset(offset int, count int, cb avl.IterCbFn) bool + ReverseIterateByOffset(offset int, count int, cb avl.IterCbFn) bool +} + +// Verify that ReadOnlyTree implements both ITree and IReadOnlyTree +var ( + _ avl.ITree = (*ReadOnlyTree)(nil) + _ IReadOnlyTree = (*ReadOnlyTree)(nil) +) // getSafeValue applies the makeEntrySafeFn if it exists, otherwise returns the original value func (roTree *ReadOnlyTree) getSafeValue(value interface{}) interface{} { From 1ff162bd8df47246e01ecfe973702271fde6436f Mon Sep 17 00:00:00 2001 From: Manfred Touron <94029+moul@users.noreply.github.com> Date: Wed, 15 Jan 2025 15:42:59 +0100 Subject: [PATCH 16/18] feat(examples): add p/moul/collection (#3321) Addresses #1467 Related with #3317 --------- Signed-off-by: moul <94029+moul@users.noreply.github.com> --- .../gno.land/p/demo/avl/list/list_test.gno | 64 ++ .../gno.land/p/moul/collection/collection.gno | 509 +++++++++ .../p/moul/collection/collection_test.gno | 987 ++++++++++++++++++ examples/gno.land/p/moul/collection/entry.gno | 149 +++ examples/gno.land/p/moul/collection/gno.mod | 1 + 5 files changed, 1710 insertions(+) create mode 100644 examples/gno.land/p/moul/collection/collection.gno create mode 100644 examples/gno.land/p/moul/collection/collection_test.gno create mode 100644 examples/gno.land/p/moul/collection/entry.gno create mode 100644 examples/gno.land/p/moul/collection/gno.mod diff --git a/examples/gno.land/p/demo/avl/list/list_test.gno b/examples/gno.land/p/demo/avl/list/list_test.gno index 265fbdb5eb1..0293692f660 100644 --- a/examples/gno.land/p/demo/avl/list/list_test.gno +++ b/examples/gno.land/p/demo/avl/list/list_test.gno @@ -2,6 +2,8 @@ package list import ( "testing" + + "gno.land/p/demo/ufmt" ) func TestList_Basic(t *testing.T) { @@ -395,6 +397,68 @@ func TestList_IndexConsistency(t *testing.T) { } } +func TestList_RecursiveSafety(t *testing.T) { + // Create a new list + l := &List{} + + // Add some initial values + l.Append("id1") + l.Append("id2") + l.Append("id3") + + // Test deep list traversal + found := false + l.ForEach(func(i int, v interface{}) bool { + if str, ok := v.(string); ok { + if str == "id2" { + found = true + return true // stop iteration + } + } + return false // continue iteration + }) + + if !found { + t.Error("Failed to find expected value in list") + } + + short := testing.Short() + + // Test recursive safety by performing multiple operations + for i := 0; i < 1000; i++ { + // Add new value + l.Append(ufmt.Sprintf("id%d", i+4)) + + if !short { + // Search for a value + var lastFound bool + l.ForEach(func(j int, v interface{}) bool { + if str, ok := v.(string); ok { + if str == ufmt.Sprintf("id%d", i+3) { + lastFound = true + return true + } + } + return false + }) + + if !lastFound { + t.Errorf("Failed to find value id%d after insertion", i+3) + } + } + } + + // Verify final length + expectedLen := 1003 // 3 initial + 1000 added + if l.Len() != expectedLen { + t.Errorf("Expected length %d, got %d", expectedLen, l.Len()) + } + + if short { + t.Skip("skipping extended recursive safety test in short mode") + } +} + // Helper function to compare slices func sliceEqual(a, b []interface{}) bool { if len(a) != len(b) { diff --git a/examples/gno.land/p/moul/collection/collection.gno b/examples/gno.land/p/moul/collection/collection.gno new file mode 100644 index 00000000000..f6d26e6a3ee --- /dev/null +++ b/examples/gno.land/p/moul/collection/collection.gno @@ -0,0 +1,509 @@ +// Package collection provides a generic collection implementation with support for +// multiple indexes, including unique indexes and case-insensitive indexes. +// It is designed to be used with any type and allows efficient lookups using +// different fields or computed values. +// +// Example usage: +// +// // Define a data type +// type User struct { +// Name string +// Email string +// Age int +// Username string +// Tags []string +// } +// +// // Create a new collection +// c := collection.New() +// +// // Add indexes with different options +// c.AddIndex("name", func(v interface{}) string { +// return v.(*User).Name +// }, UniqueIndex) +// +// c.AddIndex("email", func(v interface{}) string { +// return v.(*User).Email +// }, UniqueIndex|CaseInsensitiveIndex) +// +// c.AddIndex("age", func(v interface{}) string { +// return strconv.Itoa(v.(*User).Age) +// }, DefaultIndex) // Non-unique index +// +// c.AddIndex("username", func(v interface{}) string { +// return v.(*User).Username +// }, UniqueIndex|SparseIndex) // Allow empty usernames +// +// // For tags, we index all tags for the user +// c.AddIndex("tag", func(v interface{}) []string { +// return v.(*User).Tags +// }, DefaultIndex) // Non-unique to allow multiple users with same tag +// +// // Store an object +// id := c.Set(&User{ +// Name: "Alice", +// Email: "alice@example.com", +// Age: 30, +// Tags: []string{"admin", "moderator"}, // User can have multiple tags +// }) +// +// // Retrieve by any index +// entry := c.GetFirst("email", "alice@example.com") +// adminUsers := c.GetAll("tag", "admin") // Find all users with admin tag +// modUsers := c.GetAll("tag", "moderator") // Find all users with moderator tag +// +// Index options can be combined using the bitwise OR operator. +// Available options: +// - DefaultIndex: Regular index with no special behavior +// - UniqueIndex: Ensures values are unique within the index +// - CaseInsensitiveIndex: Makes string comparisons case-insensitive +// - SparseIndex: Skips indexing empty values (nil or empty string) +// +// Example: UniqueIndex|CaseInsensitiveIndex for a case-insensitive unique index +package collection + +import ( + "errors" + "strings" + + "gno.land/p/demo/avl" + "gno.land/p/demo/seqid" +) + +// New creates a new Collection instance with an initialized ID index. +// The ID index is a special unique index that is always present and +// serves as the primary key for all objects in the collection. +func New() *Collection { + c := &Collection{ + indexes: make(map[string]*Index), + idGen: seqid.ID(0), + } + // Initialize _id index + c.indexes[IDIndex] = &Index{ + options: UniqueIndex, + tree: avl.NewTree(), + } + return c +} + +// Collection represents a collection of objects with multiple indexes +type Collection struct { + indexes map[string]*Index + idGen seqid.ID +} + +const ( + // IDIndex is the reserved name for the primary key index + IDIndex = "_id" +) + +// IndexOption represents configuration options for an index using bit flags +type IndexOption uint64 + +const ( + // DefaultIndex is a basic index with no special options + DefaultIndex IndexOption = 0 + + // UniqueIndex ensures no duplicate values are allowed + UniqueIndex IndexOption = 1 << iota + + // CaseInsensitiveIndex automatically converts string values to lowercase + CaseInsensitiveIndex + + // SparseIndex only indexes non-empty values + SparseIndex +) + +// Index represents an index with its configuration and data. +// The index function can return either: +// - string: for single-value indexes +// - []string: for multi-value indexes where one object can be indexed under multiple keys +// +// The backing tree stores either a single ID or []string for multiple IDs per key. +type Index struct { + fn interface{} + options IndexOption + tree avl.ITree +} + +// AddIndex adds a new index to the collection with the specified options +// +// Parameters: +// - name: the unique name of the index (e.g., "tags") +// - indexFn: a function that extracts either a string or []string from an object +// - options: bit flags for index configuration (e.g., UniqueIndex) +func (c *Collection) AddIndex(name string, indexFn interface{}, options IndexOption) { + if name == IDIndex { + panic("_id is a reserved index name") + } + c.indexes[name] = &Index{ + fn: indexFn, + options: options, + tree: avl.NewTree(), + } +} + +// storeIndex handles how we store an ID in the index tree +func (idx *Index) store(key string, idStr string) { + stored, exists := idx.tree.Get(key) + if !exists { + // First entry for this key + idx.tree.Set(key, idStr) + return + } + + // Handle existing entries + switch existing := stored.(type) { + case string: + if existing == idStr { + return // Already stored + } + // Convert to array + idx.tree.Set(key, []string{existing, idStr}) + case []string: + // Check if ID already exists + for _, id := range existing { + if id == idStr { + return + } + } + // Append new ID + idx.tree.Set(key, append(existing, idStr)) + } +} + +// removeIndex handles how we remove an ID from the index tree +func (idx *Index) remove(key string, idStr string) { + stored, exists := idx.tree.Get(key) + if !exists { + return + } + + switch existing := stored.(type) { + case string: + if existing == idStr { + idx.tree.Remove(key) + } + case []string: + newIds := make([]string, 0, len(existing)) + for _, id := range existing { + if id != idStr { + newIds = append(newIds, id) + } + } + if len(newIds) == 0 { + idx.tree.Remove(key) + } else if len(newIds) == 1 { + idx.tree.Set(key, newIds[0]) + } else { + idx.tree.Set(key, newIds) + } + } +} + +// generateKeys extracts one or more keys from an object for a given index. +func generateKeys(idx *Index, obj interface{}) ([]string, bool) { + if obj == nil { + return nil, false + } + + switch fnTyped := idx.fn.(type) { + case func(interface{}) string: + // Single-value index + key := fnTyped(obj) + return []string{key}, true + case func(interface{}) []string: + // Multi-value index + keys := fnTyped(obj) + return keys, true + default: + panic("invalid index function type") + } +} + +// Set adds or updates an object in the collection. +// Returns a positive ID if successful. +// Returns 0 if: +// - The object is nil +// - A uniqueness constraint would be violated +// - Index generation fails for any index +func (c *Collection) Set(obj interface{}) uint64 { + if obj == nil { + return 0 + } + + // Generate new ID + id := c.idGen.Next() + idStr := id.String() + + // Check uniqueness constraints first + for name, idx := range c.indexes { + if name == IDIndex { + continue + } + keys, ok := generateKeys(idx, obj) + if !ok { + return 0 + } + + for _, key := range keys { + // Skip empty values for sparse indexes + if idx.options&SparseIndex != 0 && key == "" { + continue + } + if idx.options&CaseInsensitiveIndex != 0 { + key = strings.ToLower(key) + } + // Only check uniqueness for unique + single-value indexes + // (UniqueIndex is ambiguous; skipping that scenario) + if idx.options&UniqueIndex != 0 { + if existing, exists := idx.tree.Get(key); exists && existing != nil { + return 0 + } + } + } + } + + // Store in _id index first (the actual object) + c.indexes[IDIndex].tree.Set(idStr, obj) + + // Store in all other indexes + for name, idx := range c.indexes { + if name == IDIndex { + continue + } + keys, ok := generateKeys(idx, obj) + if !ok { + // Rollback: remove from _id index + c.indexes[IDIndex].tree.Remove(idStr) + return 0 + } + + for _, key := range keys { + if idx.options&SparseIndex != 0 && key == "" { + continue + } + if idx.options&CaseInsensitiveIndex != 0 { + key = strings.ToLower(key) + } + idx.store(key, idStr) + } + } + + return uint64(id) +} + +// Get retrieves entries matching the given key in the specified index. +// Returns an iterator over the matching entries. +func (c *Collection) Get(indexName string, key string) EntryIterator { + idx, exists := c.indexes[indexName] + if !exists { + return EntryIterator{err: errors.New("index not found: " + indexName)} + } + + if idx.options&CaseInsensitiveIndex != 0 { + key = strings.ToLower(key) + } + + if indexName == IDIndex { + // For ID index, validate the ID format first + _, err := seqid.FromString(key) + if err != nil { + return EntryIterator{err: err} + } + } + + return EntryIterator{ + collection: c, + indexName: indexName, + key: key, + } +} + +// GetFirst returns the first matching entry or nil if none found +func (c *Collection) GetFirst(indexName, key string) *Entry { + iter := c.Get(indexName, key) + if iter.Next() { + return iter.Value() + } + return nil +} + +// Delete removes an object by its ID and returns true if something was deleted +func (c *Collection) Delete(id uint64) bool { + idStr := seqid.ID(id).String() + + // Get the object first to clean up other indexes + obj, exists := c.indexes[IDIndex].tree.Get(idStr) + if !exists { + return false + } + + // Remove from all indexes + for name, idx := range c.indexes { + if name == IDIndex { + idx.tree.Remove(idStr) + continue + } + keys, ok := generateKeys(idx, obj) + if !ok { + continue + } + for _, key := range keys { + if idx.options&CaseInsensitiveIndex != 0 { + key = strings.ToLower(key) + } + idx.remove(key, idStr) + } + } + return true +} + +// Update updates an existing object and returns true if successful +// Returns true if the update was successful. +// Returns false if: +// - The object is nil +// - The ID doesn't exist +// - A uniqueness constraint would be violated +// - Index generation fails for any index +// +// If the update fails, the collection remains unchanged. +func (c *Collection) Update(id uint64, obj interface{}) bool { + if obj == nil { + return false + } + idStr := seqid.ID(id).String() + oldObj, exists := c.indexes[IDIndex].tree.Get(idStr) + if !exists { + return false + } + + // Check unique constraints + for name, idx := range c.indexes { + if name == IDIndex { + continue + } + + if idx.options&UniqueIndex != 0 { + newKeys, newOk := generateKeys(idx, obj) + _, oldOk := generateKeys(idx, oldObj) + if !newOk || !oldOk { + return false + } + + for _, newKey := range newKeys { + if idx.options&CaseInsensitiveIndex != 0 { + newKey = strings.ToLower(newKey) + } + + found, _ := idx.tree.Get(newKey) + if found != nil { + if storedID, ok := found.(string); !ok || storedID != idStr { + return false + } + } + } + } + } + + // Store old index entries for potential rollback + oldEntries := make(map[string][]string) + for name, idx := range c.indexes { + if name == IDIndex { + continue + } + oldKeys, ok := generateKeys(idx, oldObj) + if !ok { + continue + } + var adjusted []string + for _, okey := range oldKeys { + if idx.options&CaseInsensitiveIndex != 0 { + okey = strings.ToLower(okey) + } + // Remove the oldObj from the index right away + idx.remove(okey, idStr) + adjusted = append(adjusted, okey) + } + oldEntries[name] = adjusted + } + + // Update the object in the _id index + c.indexes[IDIndex].tree.Set(idStr, obj) + + // Add new index entries + for name, idx := range c.indexes { + if name == IDIndex { + continue + } + newKeys, ok := generateKeys(idx, obj) + if !ok { + // Rollback: restore old object and old index entries + c.indexes[IDIndex].tree.Set(idStr, oldObj) + for idxName, keys := range oldEntries { + for _, oldKey := range keys { + c.indexes[idxName].store(oldKey, idStr) + } + } + return false + } + for _, nkey := range newKeys { + if idx.options&CaseInsensitiveIndex != 0 { + nkey = strings.ToLower(nkey) + } + idx.store(nkey, idStr) + } + } + + return true +} + +// GetAll retrieves all entries matching the given key in the specified index. +func (c *Collection) GetAll(indexName string, key string) []Entry { + idx, exists := c.indexes[indexName] + if !exists { + return nil + } + + if idx.options&CaseInsensitiveIndex != 0 { + key = strings.ToLower(key) + } + + if indexName == IDIndex { + if obj, exists := idx.tree.Get(key); exists { + return []Entry{{ID: key, Obj: obj}} + } + return nil + } + + idData, exists := idx.tree.Get(key) + if !exists { + return nil + } + + // Handle both single and multi-value cases based on the actual data type + switch stored := idData.(type) { + case []string: + result := make([]Entry, 0, len(stored)) + for _, idStr := range stored { + if obj, exists := c.indexes[IDIndex].tree.Get(idStr); exists { + result = append(result, Entry{ID: idStr, Obj: obj}) + } + } + return result + case string: + if obj, exists := c.indexes[IDIndex].tree.Get(stored); exists { + return []Entry{{ID: stored, Obj: obj}} + } + } + return nil +} + +// GetIndex returns the underlying tree for an index +func (c *Collection) GetIndex(name string) avl.ITree { + idx, exists := c.indexes[name] + if !exists { + return nil + } + return idx.tree +} diff --git a/examples/gno.land/p/moul/collection/collection_test.gno b/examples/gno.land/p/moul/collection/collection_test.gno new file mode 100644 index 00000000000..3e03d222ce8 --- /dev/null +++ b/examples/gno.land/p/moul/collection/collection_test.gno @@ -0,0 +1,987 @@ +package collection + +import ( + "errors" + "strconv" + "strings" + "testing" + + "gno.land/p/demo/seqid" + "gno.land/p/demo/ufmt" +) + +type Person struct { + Name string + Age int + Email string + Username string + Tags []string +} + +func (p Person) String() string { + return ufmt.Sprintf("name=%s age=%d email=%s username=%s tags=%s", + p.Name, p.Age, p.Email, p.Username, strings.Join(p.Tags, ",")) +} + +// TestOperation represents a single operation in a test sequence +type TestOperation struct { + op string // "set" or "update" + person *Person + id uint64 // for updates + wantID uint64 + wantErr bool +} + +// TestCase represents a complete test case with setup and operations +type TestCase struct { + name string + setupIndex func(*Collection) + operations []TestOperation +} + +func TestBasicOperations(t *testing.T) { + c := New() + + // Add indexes + c.AddIndex("name", func(v interface{}) string { + return v.(*Person).Name + }, UniqueIndex) + c.AddIndex("age", func(v interface{}) string { + return strconv.Itoa(v.(*Person).Age) + }, DefaultIndex) + + // Test basic Set and Get + p1 := &Person{Name: "Alice", Age: 30, Email: "alice@test.com"} + id1 := c.Set(p1) + if id1 == 0 { + t.Error("Failed to set first object") + } + + // Get by ID + iter := c.Get(IDIndex, seqid.ID(id1).String()) + if !iter.Next() { + t.Error("Failed to get object by ID") + } + entry := iter.Value() + if entry.Obj.(*Person).Name != "Alice" { + t.Error("Got wrong object") + } +} + +func TestUniqueConstraints(t *testing.T) { + tests := []struct { + name string + setup func(*Collection) uint64 + wantID bool + }{ + { + name: "First person", + setup: func(c *Collection) uint64 { + return c.Set(&Person{Name: "Alice"}) + }, + wantID: true, + }, + { + name: "Duplicate name", + setup: func(c *Collection) uint64 { + c.Set(&Person{Name: "Alice"}) + return c.Set(&Person{Name: "Alice"}) + }, + wantID: false, + }, + { + name: "Same age (non-unique index)", + setup: func(c *Collection) uint64 { + c.AddIndex("age", func(v interface{}) string { + return strconv.Itoa(v.(*Person).Age) + }, DefaultIndex) + c.Set(&Person{Name: "Alice", Age: 30}) + return c.Set(&Person{Name: "Bob", Age: 30}) + }, + wantID: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := New() + c.AddIndex("name", func(v interface{}) string { + return v.(*Person).Name + }, UniqueIndex) + + id := tt.setup(c) + if (id != 0) != tt.wantID { + t.Errorf("Set() got id = %v, want non-zero: %v", id, tt.wantID) + } + }) + } +} + +func TestUpdates(t *testing.T) { + c := New() + c.AddIndex("name", func(v interface{}) string { + return v.(*Person).Name + }, UniqueIndex) + c.AddIndex("username", func(v interface{}) string { + return strings.ToLower(v.(*Person).Username) + }, UniqueIndex|CaseInsensitiveIndex) + + // Initial setup + p1 := &Person{Name: "Alice", Username: "alice123"} + p2 := &Person{Name: "Bob", Username: "bob456"} + + id1 := c.Set(p1) + id2 := c.Set(p2) + + tests := []struct { + name string + id uint64 + newPerson *Person + wantRet bool + }{ + { + name: "Update to non-conflicting values", + id: id1, + newPerson: &Person{Name: "Alice2", Username: "alice1234"}, + wantRet: true, + }, + { + name: "Update to conflicting username", + id: id1, + newPerson: &Person{Name: "Alice2", Username: "bob456"}, + wantRet: false, + }, + { + name: "Update non-existent ID", + id: 99999, + newPerson: &Person{Name: "Test", Username: "test"}, + wantRet: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotID := c.Update(tt.id, tt.newPerson) + if gotID != tt.wantRet { + t.Errorf("Update() got = %v, want %v", gotID, tt.wantRet) + } + }) + } +} + +func TestDelete(t *testing.T) { + c := New() + c.AddIndex("name", func(v interface{}) string { + return v.(*Person).Name + }, UniqueIndex) + + p1 := &Person{Name: "Alice"} + id1 := c.Set(p1) + + tests := []struct { + name string + id uint64 + wantRet bool + }{ + { + name: "Delete existing object", + id: id1, + wantRet: true, + }, + { + name: "Delete non-existent object", + id: 99999, + wantRet: false, + }, + { + name: "Delete already deleted object", + id: id1, + wantRet: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotID := c.Delete(tt.id) + if gotID != tt.wantRet { + t.Errorf("Delete() got = %v, want %v", gotID, tt.wantRet) + } + }) + } +} + +func TestEdgeCases(t *testing.T) { + c := New() + c.AddIndex("name", func(v interface{}) string { + return v.(*Person).Name + }, UniqueIndex) + + tests := []struct { + name string + operation func() bool + wantPanic bool + }{ + { + name: "Set nil object", + operation: func() bool { + return c.Set(nil) != 0 + }, + wantPanic: false, + }, + { + name: "Set wrong type", + operation: func() bool { + return c.Set("not a person") != 0 + }, + wantPanic: true, + }, + { + name: "Update with nil", + operation: func() bool { + id := c.Set(&Person{Name: "Test"}) + return c.Update(id, nil) + }, + wantPanic: false, + }, + { + name: "Get with invalid index name", + operation: func() bool { + iter := c.Get("invalid_index", "key") + if iter.Empty() { + return false + } + entry := iter.Value() + if entry == nil { + return false + } + id, err := seqid.FromString(entry.ID) + if err != nil { + return false + } + return true + }, + wantPanic: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var got bool + panicked := false + + func() { + defer func() { + if r := recover(); r != nil { + panicked = true + } + }() + got = tt.operation() + }() + + if panicked != tt.wantPanic { + t.Errorf("Operation panicked = %v, want panic = %v", panicked, tt.wantPanic) + } + if !panicked && got != false { + t.Errorf("Operation returned %v, want 0", got) + } + }) + } +} + +func TestIndexTypes(t *testing.T) { + c := New() + + // Test different types of indexes + c.AddIndex("composite", func(v interface{}) string { + p := v.(*Person) + return p.Name + ":" + strconv.Itoa(p.Age) + }, UniqueIndex) + + c.AddIndex("case_insensitive", func(v interface{}) string { + return strings.ToLower(v.(*Person).Username) + }, UniqueIndex|CaseInsensitiveIndex) + + // Test composite index + p1 := &Person{Name: "Alice", Age: 30, Username: "Alice123"} + id1 := c.Set(p1) + if id1 == 0 { + t.Error("Failed to set object with composite index") + } + + // Test case-insensitive index + p2 := &Person{Name: "Bob", Age: 25, Username: "alice123"} + id2 := c.Set(p2) + if id2 != 0 { + t.Error("Case-insensitive index failed to prevent duplicate") + } +} + +func TestIndexOptions(t *testing.T) { + tests := []struct { + name string + setup func(*Collection) uint64 + wantID bool + wantErr bool + }{ + { + name: "Unique case-sensitive index", + setup: func(c *Collection) uint64 { + c.AddIndex("username", func(v interface{}) string { + return v.(*Person).Username + }, UniqueIndex) + + id1 := c.Set(&Person{Username: "Alice"}) + return c.Set(&Person{Username: "Alice"}) // Should fail + }, + wantID: false, + }, + { + name: "Unique case-insensitive index", + setup: func(c *Collection) uint64 { + c.AddIndex("email", func(v interface{}) string { + return v.(*Person).Email + }, UniqueIndex|CaseInsensitiveIndex) + + id1 := c.Set(&Person{Email: "test@example.com"}) + return c.Set(&Person{Email: "TEST@EXAMPLE.COM"}) // Should fail + }, + wantID: false, + }, + { + name: "Default index", + setup: func(c *Collection) uint64 { + c.AddIndex("age", func(v interface{}) string { + return strconv.Itoa(v.(*Person).Age) + }, DefaultIndex) + + // First person with age 30 + id1 := c.Set(&Person{Age: 30}) + if id1 == 0 { + t.Error("Failed to set first person") + } + + // Second person with same age should succeed + return c.Set(&Person{Age: 30}) + }, + wantID: true, + }, + { + name: "Multiple options", + setup: func(c *Collection) uint64 { + c.AddIndex("name", func(v interface{}) string { + return v.(*Person).Name + }, UniqueIndex|CaseInsensitiveIndex|SparseIndex) + + id1 := c.Set(&Person{Name: "Alice"}) + return c.Set(&Person{Name: "ALICE"}) // Should fail + }, + wantID: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := New() // Create new collection for each test + id := tt.setup(c) + if (id != 0) != tt.wantID { + t.Errorf("got id = %v, want non-zero: %v", id, tt.wantID) + } + }) + } +} + +func TestConcurrentOperations(t *testing.T) { + c := New() + c.AddIndex("name", func(v interface{}) string { + return v.(*Person).Name + }, UniqueIndex) + + p1 := &Person{Name: "Alice"} + id1 := c.Set(p1) + iter := c.Get("_id", seqid.ID(id1).String()) + success := c.Update(id1, &Person{Name: "Alice2"}) + + if iter.Empty() || !success { + t.Error("Concurrent operations failed") + } +} + +func TestSparseIndexBehavior(t *testing.T) { + c := New() + c.AddIndex("optional_field", func(v interface{}) string { + return v.(*Person).Username + }, SparseIndex) + + tests := []struct { + name string + person *Person + wantID bool + }{ + { + name: "Empty optional field", + person: &Person{Name: "Alice", Email: "alice@test.com"}, + wantID: true, + }, + { + name: "Populated optional field", + person: &Person{Name: "Bob", Email: "bob@test.com", Username: "bobby"}, + wantID: true, + }, + { + name: "Multiple empty fields", + person: &Person{Name: "Charlie"}, + wantID: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + id := c.Set(tt.person) + if (id != 0) != tt.wantID { + t.Errorf("Set() got id = %v, want non-zero: %v", id, tt.wantID) + } + }) + } +} + +func TestIndexKeyGeneration(t *testing.T) { + c := New() + c.AddIndex("composite", func(v interface{}) string { + p := v.(*Person) + return p.Name + ":" + strconv.Itoa(p.Age) + }, UniqueIndex) + + tests := []struct { + name string + person *Person + wantID bool + }{ + { + name: "Valid composite key", + person: &Person{Name: "Alice", Age: 30}, + wantID: true, + }, + { + name: "Duplicate composite key", + person: &Person{Name: "Alice", Age: 30}, + wantID: false, + }, + { + name: "Different composite key", + person: &Person{Name: "Alice", Age: 31}, + wantID: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + id := c.Set(tt.person) + if (id != 0) != tt.wantID { + t.Errorf("Set() got id = %v, want non-zero: %v", id, tt.wantID) + } + }) + } +} + +func TestGetIndex(t *testing.T) { + c := New() + c.AddIndex("name", func(v interface{}) string { + return v.(*Person).Name + }, UniqueIndex) + + tests := []struct { + name string + indexName string + wantNil bool + }{ + { + name: "Get existing index", + indexName: "name", + wantNil: false, + }, + { + name: "Get _id index", + indexName: IDIndex, + wantNil: false, + }, + { + name: "Get non-existent index", + indexName: "invalid", + wantNil: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tree := c.GetIndex(tt.indexName) + if (tree == nil) != tt.wantNil { + t.Errorf("GetIndex() got nil = %v, want nil = %v", tree == nil, tt.wantNil) + } + }) + } +} + +func TestAddIndexPanic(t *testing.T) { + c := New() + defer func() { + if r := recover(); r == nil { + t.Error("Expected panic when adding _id index") + } + }() + + c.AddIndex(IDIndex, func(v interface{}) string { + return "" + }, DefaultIndex) +} + +func TestCaseInsensitiveOperations(t *testing.T) { + c := New() + c.AddIndex("email", func(v interface{}) string { + return v.(*Person).Email + }, UniqueIndex|CaseInsensitiveIndex) + + p := &Person{Email: "Test@Example.com"} + id := c.Set(p) + + tests := []struct { + name string + key string + wantObj bool + operation string // "get" or "getAll" + wantCount int + }{ + {"Get exact match", "Test@Example.com", true, "get", 1}, + {"Get different case", "test@example.COM", true, "get", 1}, + {"Get non-existent", "other@example.com", false, "get", 0}, + {"GetAll exact match", "Test@Example.com", true, "getAll", 1}, + {"GetAll different case", "test@example.COM", true, "getAll", 1}, + {"GetAll non-existent", "other@example.com", false, "getAll", 0}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.operation == "get" { + iter := c.Get("email", tt.key) + if iter.Empty() { + if tt.wantObj { + t.Error("Expected iterator to not be empty") + } + return + } + hasValue := iter.Next() + if hasValue != tt.wantObj { + t.Errorf("Get() got object = %v, want object = %v", hasValue, tt.wantObj) + } + if hasValue { + entry := iter.Value() + if entry.ID != seqid.ID(id).String() { + t.Errorf("Get() got id = %v, want id = %v", entry.ID, seqid.ID(id).String()) + } + } + } else { + entries := c.GetAll("email", tt.key) + if len(entries) != tt.wantCount { + t.Errorf("GetAll() returned %d entries, want %d", len(entries), tt.wantCount) + } + if tt.wantCount > 0 { + entry := entries[0] + if entry.ID != seqid.ID(id).String() { + t.Errorf("GetAll() returned ID %s, want %s", entry.ID, seqid.ID(id).String()) + } + } + } + }) + } +} + +func TestGetInvalidID(t *testing.T) { + c := New() + iter := c.Get(IDIndex, "not-a-valid-id") + if !iter.Empty() { + t.Errorf("Get() with invalid ID format got an entry, want nil") + } +} + +func TestGetAll(t *testing.T) { + c := New() + c.AddIndex("tags", func(v interface{}) []string { + return v.(*Person).Tags + }, DefaultIndex) + c.AddIndex("age", func(v interface{}) string { + return strconv.Itoa(v.(*Person).Age) + }, DefaultIndex) + c.AddIndex("name", func(v interface{}) string { + return v.(*Person).Name + }, UniqueIndex) + + // Create test data + people := []*Person{ + {Name: "Alice", Age: 30, Tags: []string{"dev", "go"}}, + {Name: "Bob", Age: 30, Tags: []string{"dev", "python"}}, + {Name: "Charlie", Age: 25, Tags: []string{"dev", "rust"}}, + } + + ids := make([]uint64, len(people)) + for i, p := range people { + ids[i] = c.Set(p) + if ids[i] == 0 { + t.Fatalf("Failed to set person %s", p.Name) + } + } + + tests := []struct { + name string + indexName string + key string + wantCount int + }{ + { + name: "Multi-value index with multiple matches", + indexName: "tags", + key: "dev", + wantCount: 3, + }, + { + name: "Multi-value index with single match", + indexName: "tags", + key: "go", + wantCount: 1, + }, + { + name: "Multi-value index with no matches", + indexName: "tags", + key: "java", + wantCount: 0, + }, + { + name: "Single-value non-unique index with multiple matches", + indexName: "age", + key: "30", + wantCount: 2, + }, + { + name: "Single-value unique index", + indexName: "name", + key: "Alice", + wantCount: 1, + }, + { + name: "Non-existent index", + indexName: "invalid", + key: "value", + wantCount: 0, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + iter := c.Get(tt.indexName, tt.key) + count := 0 + for iter.Next() { + entry := iter.Value() + if entry.ID == "" { + t.Error("Got entry with empty ID") + } + if entry.Obj == nil { + t.Error("Got entry with nil Obj") + } + count++ + } + if count != tt.wantCount { + t.Errorf("Got %d entries, want %d", count, tt.wantCount) + } + }) + } +} + +func TestIndexOperations(t *testing.T) { + tests := []struct { + name string + setup func(*Collection) (uint64, error) + verify func(*Collection, uint64) error + wantErr bool + }{ + { + name: "Basic set and get", + setup: func(c *Collection) (uint64, error) { + c.AddIndex("name", func(v interface{}) string { + return v.(*Person).Name + }, UniqueIndex) + return c.Set(&Person{Name: "Alice", Age: 30}), nil + }, + verify: func(c *Collection, id uint64) error { + iter := c.Get(IDIndex, seqid.ID(id).String()) + if !iter.Next() { + return errors.New("failed to get object by ID") + } + entry := iter.Value() + if entry.Obj.(*Person).Name != "Alice" { + return errors.New("got wrong object") + } + return nil + }, + }, + { + name: "Composite index", + setup: func(c *Collection) (uint64, error) { + c.AddIndex("composite", func(v interface{}) string { + p := v.(*Person) + return p.Name + ":" + strconv.Itoa(p.Age) + }, UniqueIndex) + return c.Set(&Person{Name: "Alice", Age: 30}), nil + }, + verify: func(c *Collection, id uint64) error { + iter := c.Get("composite", "Alice:30") + if !iter.Next() { + return errors.New("failed to get object by composite index") + } + return nil + }, + }, + // Add more test cases combining unique scenarios from original tests + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := New() + id, err := tt.setup(c) + if (err != nil) != tt.wantErr { + t.Errorf("setup error = %v, wantErr %v", err, tt.wantErr) + return + } + if err == nil { + if err := tt.verify(c, id); err != nil { + t.Errorf("verification failed: %v", err) + } + } + }) + } +} + +func TestMultiValueIndexes(t *testing.T) { + c := New() + c.AddIndex("tags", func(v interface{}) []string { + return v.(*Person).Tags + }, DefaultIndex) + + tests := []struct { + name string + setup []*Person + searchTag string + wantCount int + }{ + { + name: "Multiple tags, multiple matches", + setup: []*Person{ + {Name: "Alice", Tags: []string{"dev", "go"}}, + {Name: "Bob", Tags: []string{"dev", "python"}}, + {Name: "Charlie", Tags: []string{"dev", "rust"}}, + }, + searchTag: "dev", + wantCount: 3, + }, + { + name: "Single tag match", + setup: []*Person{ + {Name: "Alice", Tags: []string{"dev", "go"}}, + {Name: "Bob", Tags: []string{"dev", "python"}}, + }, + searchTag: "go", + wantCount: 1, + }, + { + name: "No matches", + setup: []*Person{ + {Name: "Alice", Tags: []string{"dev", "go"}}, + {Name: "Bob", Tags: []string{"dev", "python"}}, + }, + searchTag: "java", + wantCount: 0, + }, + { + name: "Empty tags", + setup: []*Person{ + {Name: "Alice", Tags: []string{}}, + {Name: "Bob", Tags: nil}, + }, + searchTag: "dev", + wantCount: 0, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := New() + c.AddIndex("tags", func(v interface{}) []string { + return v.(*Person).Tags + }, DefaultIndex) + + // Setup test data + for _, p := range tt.setup { + if id := c.Set(p); id == 0 { + t.Fatalf("Failed to set person %s", p.Name) + } + } + + // Test Get operation + iter := c.Get("tags", tt.searchTag) + count := 0 + for iter.Next() { + count++ + } + if count != tt.wantCount { + t.Errorf("Get() got %d matches, want %d", count, tt.wantCount) + } + }) + } +} + +func TestGetOperations(t *testing.T) { + c := New() + c.AddIndex("name", func(v interface{}) string { + return v.(*Person).Name + }, UniqueIndex) + c.AddIndex("age", func(v interface{}) string { + return strconv.Itoa(v.(*Person).Age) + }, DefaultIndex) + + // Setup test data + testPeople := []*Person{ + {Name: "Alice", Age: 30}, + {Name: "Bob", Age: 30}, + {Name: "Charlie", Age: 25}, + } + + ids := make([]uint64, len(testPeople)) + for i, p := range testPeople { + ids[i] = c.Set(p) + if ids[i] == 0 { + t.Fatalf("Failed to set person %s", p.Name) + } + } + + tests := []struct { + name string + indexName string + key string + wantCount int + wantErr bool + }{ + { + name: "Get by ID", + indexName: IDIndex, + key: seqid.ID(ids[0]).String(), + wantCount: 1, + wantErr: false, + }, + { + name: "Get by unique index", + indexName: "name", + key: "Alice", + wantCount: 1, + wantErr: false, + }, + { + name: "Get by non-unique index", + indexName: "age", + key: "30", + wantCount: 2, + wantErr: false, + }, + { + name: "Get with invalid index", + indexName: "invalid_index", + key: "value", + wantCount: 0, + wantErr: true, + }, + { + name: "Get with invalid ID format", + indexName: IDIndex, + key: "not-a-valid-id", + wantCount: 0, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + iter := c.Get(tt.indexName, tt.key) + if iter.Empty() { + if !tt.wantErr { + t.Errorf("Get() returned empty iterator, wanted %d results", tt.wantCount) + } + return + } + + count := 0 + for iter.Next() { + entry := iter.Value() + if entry.ID == "" { + t.Error("Got entry with empty ID") + } + if entry.Obj == nil { + t.Error("Got entry with nil Obj") + } + count++ + } + + if count != tt.wantCount { + t.Errorf("Get() returned %d results, want %d", count, tt.wantCount) + } + }) + } +} + +func TestEntryString(t *testing.T) { + tests := []struct { + name string + entry *Entry + expected string + }{ + { + name: "Nil entry", + entry: nil, + expected: "", + }, + { + name: "Person entry", + entry: &Entry{ + ID: "123", + Obj: &Person{Name: "Alice", Age: 30}, + }, + expected: `Entry{ID: 123, Obj: name=Alice age=30 email= username= tags=}`, + }, + { + name: "Entry with nil object", + entry: &Entry{ + ID: "456", + Obj: nil, + }, + expected: `Entry{ID: 456, Obj: }`, + }, + { + name: "Entry with complete person", + entry: &Entry{ + ID: "789", + Obj: &Person{ + Name: "Bob", + Age: 25, + Email: "bob@example.com", + Username: "bobby", + Tags: []string{"dev", "go"}, + }, + }, + expected: `Entry{ID: 789, Obj: name=Bob age=25 email=bob@example.com username=bobby tags=dev,go}`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.entry.String() + if got != tt.expected { + t.Errorf("Entry.String() = %q, want %q", got, tt.expected) + } + }) + } +} diff --git a/examples/gno.land/p/moul/collection/entry.gno b/examples/gno.land/p/moul/collection/entry.gno new file mode 100644 index 00000000000..8daa893b61d --- /dev/null +++ b/examples/gno.land/p/moul/collection/entry.gno @@ -0,0 +1,149 @@ +package collection + +import "gno.land/p/demo/ufmt" + +// Entry represents a single object in the collection with its ID +type Entry struct { + ID string + Obj interface{} +} + +// String returns a string representation of the Entry +func (e *Entry) String() string { + if e == nil { + return "" + } + return ufmt.Sprintf("Entry{ID: %s, Obj: %v}", e.ID, e.Obj) +} + +// EntryIterator provides iteration over collection entries +type EntryIterator struct { + collection *Collection + indexName string + key string + currentID string + currentObj interface{} + err error + closed bool + + // For multi-value cases + ids []string + currentIdx int +} + +func (ei *EntryIterator) Close() error { + ei.closed = true + ei.currentID = "" + ei.currentObj = nil + ei.ids = nil + return nil +} + +func (ei *EntryIterator) Next() bool { + if ei == nil || ei.closed || ei.err != nil { + return false + } + + // Handle ID index specially + if ei.indexName == IDIndex { + if ei.currentID != "" { // We've already returned the single value + return false + } + obj, exists := ei.collection.indexes[IDIndex].tree.Get(ei.key) + if !exists { + return false + } + ei.currentID = ei.key + ei.currentObj = obj + return true + } + + // Get the index + idx, exists := ei.collection.indexes[ei.indexName] + if !exists { + return false + } + + // Initialize ids slice if needed + if ei.ids == nil { + idData, exists := idx.tree.Get(ei.key) + if !exists { + return false + } + + switch stored := idData.(type) { + case []string: + ei.ids = stored + ei.currentIdx = -1 + case string: + ei.ids = []string{stored} + ei.currentIdx = -1 + default: + return false + } + } + + // Move to next ID + ei.currentIdx++ + if ei.currentIdx >= len(ei.ids) { + return false + } + + // Fetch the actual object + ei.currentID = ei.ids[ei.currentIdx] + obj, exists := ei.collection.indexes[IDIndex].tree.Get(ei.currentID) + if !exists { + // Skip invalid entries + return ei.Next() + } + ei.currentObj = obj + return true +} + +func (ei *EntryIterator) Error() error { + return ei.err +} + +func (ei *EntryIterator) Value() *Entry { + if ei == nil || ei.closed || ei.currentID == "" { + return nil + } + return &Entry{ + ID: ei.currentID, + Obj: ei.currentObj, + } +} + +func (ei *EntryIterator) Empty() bool { + if ei == nil || ei.closed || ei.err != nil { + return true + } + + // Handle ID index specially + if ei.indexName == IDIndex { + _, exists := ei.collection.indexes[IDIndex].tree.Get(ei.key) + return !exists + } + + // Get the index + idx, exists := ei.collection.indexes[ei.indexName] + if !exists { + return true + } + + // Check if key exists in index + idData, exists := idx.tree.Get(ei.key) + if !exists { + return true + } + + // Check if there are any valid IDs + switch stored := idData.(type) { + case []string: + return len(stored) == 0 + case string: + return stored == "" + default: + return true + } +} diff --git a/examples/gno.land/p/moul/collection/gno.mod b/examples/gno.land/p/moul/collection/gno.mod new file mode 100644 index 00000000000..a6eeca36837 --- /dev/null +++ b/examples/gno.land/p/moul/collection/gno.mod @@ -0,0 +1 @@ +module gno.land/p/moul/collection From 3fa9940b85a9eeac64475cec4d5e10877c6dc14a Mon Sep 17 00:00:00 2001 From: Dmitry <98899785+mdqst@users.noreply.github.com> Date: Wed, 15 Jan 2025 22:15:03 +0300 Subject: [PATCH 17/18] chore(autocounterd): fix `ShortHelp`, improve error messages (#3504) Per title. --- misc/autocounterd/cmd/cmd_start.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/misc/autocounterd/cmd/cmd_start.go b/misc/autocounterd/cmd/cmd_start.go index ecf70f750be..e5155bf0718 100644 --- a/misc/autocounterd/cmd/cmd_start.go +++ b/misc/autocounterd/cmd/cmd_start.go @@ -44,7 +44,7 @@ func NewStartCmd(io commands.IO) *commands.Command { commands.Metadata{ Name: "start", ShortUsage: "start [flags]", - ShortHelp: "Runs the linter for the specified packages", + ShortHelp: "Increments the counter in the specified realm at regular intervals", }, cfg, func(_ context.Context, args []string) error { @@ -68,15 +68,15 @@ func execStart(cfg *startCfg, args []string, io commands.IO) error { signer, err := gnoclient.SignerFromBip39(cfg.mnemonic, cfg.chainID, "", uint32(0), uint32(0)) if err != nil { - return err + return fmt.Errorf("failed to create signer: %w", err) } if err := signer.Validate(); err != nil { - return err + return fmt.Errorf("invalid signer: %w", err) } rpcClient, err := rpcclient.NewHTTPClient(cfg.rpcURL) if err != nil { - return err + return fmt.Errorf("failed to create RPC client: %w", err) } client := gnoclient.Client{ @@ -97,7 +97,7 @@ func execStart(cfg *startCfg, args []string, io commands.IO) error { }) if err != nil { - fmt.Printf("[ERROR] Failed to call Incr on %s, %+v\n", cfg.realmPath, err.Error()) + fmt.Printf("[ERROR] Failed to call Incr on %s: %v\n", cfg.realmPath, err) } else { fmt.Println("[INFO] Counter incremented with success") } From cb2255f61c0abd0144550df223182334c6d50c51 Mon Sep 17 00:00:00 2001 From: Alexis Colin Date: Thu, 16 Jan 2025 17:10:39 +0900 Subject: [PATCH 18/18] fix(gnoweb): enable table on gnoweb (#3525) --- gno.land/pkg/gnoweb/app.go | 2 ++ gno.land/pkg/gnoweb/frontend/css/input.css | 14 ++------------ gno.land/pkg/gnoweb/public/styles.css | 2 +- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/gno.land/pkg/gnoweb/app.go b/gno.land/pkg/gnoweb/app.go index 1deea86644b..e99aa7ea056 100644 --- a/gno.land/pkg/gnoweb/app.go +++ b/gno.land/pkg/gnoweb/app.go @@ -15,6 +15,7 @@ import ( "github.com/gnolang/gno/gno.land/pkg/gnoweb/components" "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" "github.com/yuin/goldmark" + "github.com/yuin/goldmark/extension" mdhtml "github.com/yuin/goldmark/renderer/html" ) @@ -77,6 +78,7 @@ func NewRouter(logger *slog.Logger, cfg *AppConfig) (http.Handler, error) { markdown.NewHighlighting( markdown.WithFormatOptions(chromaOptions...), ), + extension.Table, ), } if cfg.UnsafeHTML { diff --git a/gno.land/pkg/gnoweb/frontend/css/input.css b/gno.land/pkg/gnoweb/frontend/css/input.css index f86076dbe54..59e41ff4a7c 100644 --- a/gno.land/pkg/gnoweb/frontend/css/input.css +++ b/gno.land/pkg/gnoweb/frontend/css/input.css @@ -147,12 +147,12 @@ } .realm-content table { - @apply w-full border-collapse my-8; + @apply border-collapse my-8 block w-full max-w-full overflow-x-auto border-collapse; } .realm-content th, .realm-content td { - @apply border border-gray-300 px-4 py-2; + @apply border px-4 py-2 break-words whitespace-normal; } .realm-content th { @@ -190,16 +190,6 @@ @apply list-decimal; } - .realm-content table th:first-child, - .realm-content td:first-child { - @apply pl-0; - } - - .realm-content table th:last-child, - .realm-content td:last-child { - @apply pr-0; - } - .realm-content abbr[title] { @apply border-b border-dotted cursor-help; } diff --git a/gno.land/pkg/gnoweb/public/styles.css b/gno.land/pkg/gnoweb/public/styles.css index 4ff1a266c0c..8e8d7ed802d 100644 --- a/gno.land/pkg/gnoweb/public/styles.css +++ b/gno.land/pkg/gnoweb/public/styles.css @@ -1,3 +1,3 @@ @font-face{font-family:Roboto;font-style:normal;font-weight:900;font-display:swap;src:url(fonts/roboto/roboto-mono-normal.woff2) format("woff2"),url(fonts/roboto/roboto-mono-normal.woff) format("woff")}@font-face{font-family:Inter var;font-weight:100 900;font-display:block;font-style:oblique 0deg 10deg;src:url(fonts/intervar/Intervar.woff2) format("woff2")}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: } -/*! tailwindcss v3.4.14 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #bdbdbd}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:Roboto,Menlo,Consolas,Ubuntu Mono,Roboto Mono,DejaVu Sans Mono,monospace;;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#7c7c7c}input::placeholder,textarea::placeholder{opacity:1;color:#7c7c7c}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}html{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity));font-family:Inter var,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji,sans-serif;font-size:1rem;--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity));font-feature-settings:"kern" on,"liga" on,"calt" on,"zero" on;-webkit-font-feature-settings:"kern" on,"liga" on,"calt" on,"zero" on;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;text-size-adjust:100%;-moz-osx-font-smoothing:grayscale;font-smoothing:antialiased;font-variant-ligatures:contextual common-ligatures;font-kerning:normal;text-rendering:optimizeLegibility}svg{max-height:100%;max-width:100%}form{margin-top:0;margin-bottom:0}.realm-content{overflow-wrap:break-word;padding-top:2.5rem;font-size:1rem}.realm-content>:first-child{margin-top:0!important}.realm-content a{font-weight:500;--tw-text-opacity:1;color:rgb(34 108 87/var(--tw-text-opacity))}.realm-content a:hover{text-decoration-line:underline}.realm-content h1,.realm-content h2,.realm-content h3,.realm-content h4{margin-top:3rem;line-height:1.25;--tw-text-opacity:1;color:rgb(8 8 9/var(--tw-text-opacity))}.realm-content h2,.realm-content h2 *{font-weight:700}.realm-content h3,.realm-content h3 *,.realm-content h4,.realm-content h4 *{font-weight:600}.realm-content h1+h2,.realm-content h2+h3,.realm-content h3+h4{margin-top:1rem}.realm-content h1{font-size:2.375rem;font-weight:700}.realm-content h2{font-size:1.5rem}.realm-content h3{margin-top:2.5rem;font-size:1.25rem}.realm-content h3,.realm-content h4{--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity))}.realm-content h4{margin-top:1.5rem;margin-bottom:1.5rem;font-size:1.125rem;font-weight:500}.realm-content p{margin-top:1.25rem;margin-bottom:1.25rem}.realm-content strong{font-weight:700;--tw-text-opacity:1;color:rgb(8 8 9/var(--tw-text-opacity))}.realm-content strong *{font-weight:700}.realm-content em{font-style:oblique 10deg}.realm-content blockquote{margin-top:1rem;margin-bottom:1rem;border-left-width:4px;--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity));padding-left:1rem;--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity));font-style:oblique 10deg}.realm-content ol,.realm-content ul{margin-top:1.5rem;margin-bottom:1.5rem;padding-left:1rem}.realm-content ol li,.realm-content ul li{margin-bottom:.5rem}.realm-content img{margin-top:2rem;margin-bottom:2rem;max-width:100%}.realm-content figure{margin-top:1.5rem;margin-bottom:1.5rem;text-align:center}.realm-content figcaption{font-size:.875rem;--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity))}.realm-content :not(pre)>code{border-radius:.25rem;background-color:rgb(226 226 226/var(--tw-bg-opacity));padding:.125rem .25rem;font-size:.96em}.realm-content :not(pre)>code,.realm-content pre{--tw-bg-opacity:1;font-family:Roboto,Menlo,Consolas,Ubuntu Mono,Roboto Mono,DejaVu Sans Mono,monospace;}.realm-content pre{overflow-x:auto;border-radius:.375rem;background-color:rgb(240 240 240/var(--tw-bg-opacity));padding:1rem}.realm-content hr{margin-top:2.5rem;margin-bottom:2.5rem;border-top-width:1px;--tw-border-opacity:1;border-color:rgb(226 226 226/var(--tw-border-opacity))}.realm-content table{margin-top:2rem;margin-bottom:2rem;width:100%;border-collapse:collapse}.realm-content td,.realm-content th{border-width:1px;--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity));padding:.5rem 1rem}.realm-content th{--tw-bg-opacity:1;background-color:rgb(226 226 226/var(--tw-bg-opacity));font-weight:700}.realm-content caption{margin-top:.5rem;text-align:left;font-size:.875rem;--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity))}.realm-content q{margin-top:1.5rem;margin-bottom:1.5rem;border-left-width:4px;--tw-border-opacity:1;border-left-color:rgb(204 204 204/var(--tw-border-opacity));padding-left:1rem;--tw-text-opacity:1;color:rgb(85 85 85/var(--tw-text-opacity));font-style:oblique 10deg;quotes:"“" "”" "‘" "’"}.realm-content q:after,.realm-content q:before{margin-right:.25rem;font-size:1.5rem;--tw-text-opacity:1;color:rgb(153 153 153/var(--tw-text-opacity));content:open-quote;vertical-align:-.4rem}.realm-content q:after{content:close-quote}.realm-content q:before{content:open-quote}.realm-content q:after{content:close-quote}.realm-content ol ol,.realm-content ol ul,.realm-content ul ol,.realm-content ul ul{margin-top:.75rem;margin-bottom:.5rem;padding-left:1rem}.realm-content ul{list-style-type:disc}.realm-content ol{list-style-type:decimal}.realm-content table th:first-child,.realm-content td:first-child{padding-left:0}.realm-content table th:last-child,.realm-content td:last-child{padding-right:0}.realm-content abbr[title]{cursor:help;border-bottom-width:1px;border-style:dotted}.realm-content details{margin-top:1.25rem;margin-bottom:1.25rem}.realm-content summary{cursor:pointer;font-weight:700}.realm-content a code{color:inherit}.realm-content video{margin-top:2rem;margin-bottom:2rem;max-width:100%}.realm-content math{font-family:Roboto,Menlo,Consolas,Ubuntu Mono,Roboto Mono,DejaVu Sans Mono,monospace;}.realm-content small{font-size:.875rem}.realm-content del{text-decoration-line:line-through}.realm-content sub{vertical-align:sub;font-size:.75rem}.realm-content sup{vertical-align:super;font-size:.75rem}.realm-content button,.realm-content input{border-width:1px;--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity));padding:.5rem 1rem}main :is(h1,h2,h3,h4){scroll-margin-top:6rem}::-moz-selection{--tw-bg-opacity:1;background-color:rgb(34 108 87/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}::selection{--tw-bg-opacity:1;background-color:rgb(34 108 87/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.sidemenu .peer:checked+label>svg{--tw-text-opacity:1;color:rgb(34 108 87/var(--tw-text-opacity))}.toc-expend-btn:has(#toc-expend:checked)+nav{display:block}.toc-expend-btn:has(#toc-expend:checked) .toc-expend-btn_ico{--tw-rotate:180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.main-header:has(#sidemenu-docs:checked)+main #sidebar #sidebar-docs,.main-header:has(#sidemenu-meta:checked)+main #sidebar #sidebar-meta,.main-header:has(#sidemenu-source:checked)+main #sidebar #sidebar-source,.main-header:has(#sidemenu-summary:checked)+main #sidebar #sidebar-summary{display:block}@media (min-width:40rem){:is(.main-header:has(#sidemenu-source:checked),.main-header:has(#sidemenu-docs:checked),.main-header:has(#sidemenu-meta:checked)) .main-navigation,:is(.main-header:has(#sidemenu-source:checked),.main-header:has(#sidemenu-docs:checked),.main-header:has(#sidemenu-meta:checked))+main .realm-content{grid-column:span 6/span 6}:is(.main-header:has(#sidemenu-source:checked),.main-header:has(#sidemenu-docs:checked),.main-header:has(#sidemenu-meta:checked)) .sidemenu,:is(.main-header:has(#sidemenu-source:checked),.main-header:has(#sidemenu-docs:checked),.main-header:has(#sidemenu-meta:checked))+main #sidebar{grid-column:span 4/span 4}}:is(.main-header:has(#sidemenu-source:checked),.main-header:has(#sidemenu-docs:checked),.main-header:has(#sidemenu-meta:checked))+main #sidebar:before{position:absolute;top:0;left:-1.75rem;z-index:-1;display:block;height:100%;width:50vw;--tw-bg-opacity:1;background-color:rgb(226 226 226/var(--tw-bg-opacity));--tw-content:"";content:var(--tw-content)}main :is(.source-code)>pre{overflow:scroll;border-radius:.375rem;--tw-bg-opacity:1!important;background-color:rgb(255 255 255/var(--tw-bg-opacity))!important;padding:1rem .25rem;font-family:Roboto,Menlo,Consolas,Ubuntu Mono,Roboto Mono,DejaVu Sans Mono,monospace;;font-size:.875rem}@media (min-width:40rem){main :is(.source-code)>pre{padding:2rem .75rem;font-size:1rem}}main .realm-content>pre a:hover{text-decoration-line:none}main :is(.realm-content,.source-code)>pre .chroma-ln:target{background-color:transparent!important}main :is(.realm-content,.source-code)>pre .chroma-line:has(.chroma-ln:target),main :is(.realm-content,.source-code)>pre .chroma-line:has(.chroma-ln:target) .chroma-cl,main :is(.realm-content,.source-code)>pre .chroma-line:has(.chroma-lnlinks:hover),main :is(.realm-content,.source-code)>pre .chroma-line:has(.chroma-lnlinks:hover) .chroma-cl{border-radius:.375rem;--tw-bg-opacity:1!important;background-color:rgb(226 226 226/var(--tw-bg-opacity))!important}main :is(.realm-content,.source-code)>pre .chroma-ln{scroll-margin-top:6rem}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.bottom-1{bottom:.25rem}.left-0{left:0}.right-2{right:.5rem}.right-3{right:.75rem}.top-0{top:0}.top-1\/2{top:50%}.top-14{top:3.5rem}.top-2{top:.5rem}.z-1{z-index:1}.z-max{z-index:9999}.col-span-1{grid-column:span 1/span 1}.col-span-10{grid-column:span 10/span 10}.col-span-3{grid-column:span 3/span 3}.col-span-7{grid-column:span 7/span 7}.row-span-1{grid-row:span 1/span 1}.row-start-1{grid-row-start:1}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-8{margin-bottom:2rem}.mr-10{margin-right:2.5rem}.mt-1{margin-top:.25rem}.mt-10{margin-top:2.5rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.grid{display:grid}.hidden{display:none}.h-10{height:2.5rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-full{height:100%}.max-h-screen{max-height:100vh}.min-h-full{min-height:100%}.min-h-screen{min-height:100vh}.w-10{width:2.5rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-full{width:100%}.min-w-2{min-width:.5rem}.min-w-48{min-width:12rem}.max-w-screen-max{max-width:98.75rem}.shrink-0{flex-shrink:0}.grow-\[2\]{flex-grow:2}.-translate-y-1\/2{--tw-translate-y:-50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-pointer{cursor:pointer}.list-none{list-style-type:none}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-flow-dense{grid-auto-flow:dense}.auto-rows-min{grid-auto-rows:min-content}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-10{grid-template-columns:repeat(10,minmax(0,1fr))}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.items-stretch{align-items:stretch}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-8{gap:2rem}.gap-x-20{-moz-column-gap:5rem;column-gap:5rem}.gap-x-3{-moz-column-gap:.75rem;column-gap:.75rem}.gap-y-2{row-gap:.5rem}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-scroll{overflow:scroll}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.rounded{border-radius:.375rem}.rounded-sm{border-radius:.25rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-t{border-top-width:1px}.border-gray-100{--tw-border-opacity:1;border-color:rgb(226 226 226/var(--tw-border-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(226 226 226/var(--tw-bg-opacity))}.bg-gray-300{--tw-bg-opacity:1;background-color:rgb(153 153 153/var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(240 240 240/var(--tw-bg-opacity))}.bg-light{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-transparent{background-color:transparent}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-4{padding:1rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-10{padding-left:2.5rem;padding-right:2.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-px{padding-top:1px;padding-bottom:1px}.pb-24{padding-bottom:6rem}.pb-3{padding-bottom:.75rem}.pb-4{padding-bottom:1rem}.pb-6{padding-bottom:1.5rem}.pb-8{padding-bottom:2rem}.pl-4{padding-left:1rem}.pr-10{padding-right:2.5rem}.pt-0\.5{padding-top:.125rem}.pt-2{padding-top:.5rem}.font-mono{font-family:Roboto,Menlo,Consolas,Ubuntu Mono,Roboto Mono,DejaVu Sans Mono,monospace;}.text-100{font-size:.875rem}.text-200{font-size:1rem}.text-50{font-size:.75rem}.text-600{font-size:1.5rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.capitalize{text-transform:capitalize}.leading-tight{line-height:1.25}.text-gray-300{--tw-text-opacity:1;color:rgb(153 153 153/var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity:1;color:rgb(124 124 124/var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity:1;color:rgb(19 19 19/var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgb(8 8 9/var(--tw-text-opacity))}.text-green-600{--tw-text-opacity:1;color:rgb(34 108 87/var(--tw-text-opacity))}.text-light{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.outline-none{outline:2px solid transparent;outline-offset:2px}.text-stroke{-webkit-text-stroke:currentColor;-webkit-text-stroke-width:.6px}.no-scrollbar::-webkit-scrollbar{display:none}.no-scrollbar{-ms-overflow-style:none;scrollbar-width:none}.\*\:pl-0>*{padding-left:0}.before\:px-\[0\.18rem\]:before{content:var(--tw-content);padding-left:.18rem;padding-right:.18rem}.before\:text-gray-300:before{content:var(--tw-content);--tw-text-opacity:1;color:rgb(153 153 153/var(--tw-text-opacity))}.before\:content-\[\'\/\'\]:before{--tw-content:"/";content:var(--tw-content)}.before\:content-\[\'\:\'\]:before{--tw-content:":";content:var(--tw-content)}.before\:content-\[\'open\'\]:before{--tw-content:"open";content:var(--tw-content)}.after\:pointer-events-none:after{content:var(--tw-content);pointer-events:none}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:bottom-0:after{content:var(--tw-content);bottom:0}.after\:left-0:after{content:var(--tw-content);left:0}.after\:top-0:after{content:var(--tw-content);top:0}.after\:block:after{content:var(--tw-content);display:block}.after\:h-1:after{content:var(--tw-content);height:.25rem}.after\:h-full:after{content:var(--tw-content);height:100%}.after\:w-full:after{content:var(--tw-content);width:100%}.after\:rounded-t-sm:after{content:var(--tw-content);border-top-left-radius:.25rem;border-top-right-radius:.25rem}.after\:bg-gray-100:after{content:var(--tw-content);--tw-bg-opacity:1;background-color:rgb(226 226 226/var(--tw-bg-opacity))}.after\:bg-green-600:after{content:var(--tw-content);--tw-bg-opacity:1;background-color:rgb(34 108 87/var(--tw-bg-opacity))}.first\:border-t:first-child{border-top-width:1px}.hover\:border-gray-300:hover{--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity))}.hover\:bg-gray-100:hover{--tw-bg-opacity:1;background-color:rgb(226 226 226/var(--tw-bg-opacity))}.hover\:bg-gray-50:hover{--tw-bg-opacity:1;background-color:rgb(240 240 240/var(--tw-bg-opacity))}.hover\:bg-green-600:hover{--tw-bg-opacity:1;background-color:rgb(34 108 87/var(--tw-bg-opacity))}.hover\:text-gray-600:hover{--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity))}.hover\:text-green-600:hover{--tw-text-opacity:1;color:rgb(34 108 87/var(--tw-text-opacity))}.hover\:text-light:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.hover\:underline:hover{text-decoration-line:underline}.focus\:border-gray-300:focus{--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity))}.focus\:border-l-gray-300:focus{--tw-border-opacity:1;border-left-color:rgb(153 153 153/var(--tw-border-opacity))}.group:hover .group-hover\:border-gray-300{--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity))}.group:hover .group-hover\:border-l-gray-300{--tw-border-opacity:1;border-left-color:rgb(153 153 153/var(--tw-border-opacity))}.group.is-active .group-\[\.is-active\]\:text-green-600{--tw-text-opacity:1;color:rgb(34 108 87/var(--tw-text-opacity))}.peer:checked~.peer-checked\:before\:content-\[\'close\'\]:before{--tw-content:"close";content:var(--tw-content)}.peer:focus-within~.peer-focus-within\:hidden{display:none}.has-\[ul\:empty\]\:hidden:has(ul:empty){display:none}.has-\[\:focus-within\]\:border-gray-300:has(:focus-within){--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity))}.has-\[\:focus\]\:border-gray-300:has(:focus){--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity))}@media (min-width:30rem){.sm\:gap-6{gap:1.5rem}}@media (min-width:40rem){.md\:col-span-3{grid-column:span 3/span 3}.md\:mb-0{margin-bottom:0}.md\:h-4{height:1rem}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:items-center{align-items:center}.md\:gap-x-8{-moz-column-gap:2rem;column-gap:2rem}.md\:px-10{padding-left:2.5rem;padding-right:2.5rem}.md\:pb-0{padding-bottom:0}}@media (min-width:51.25rem){.lg\:order-2{order:2}.lg\:col-span-3{grid-column:span 3/span 3}.lg\:col-span-7{grid-column:span 7/span 7}.lg\:row-span-2{grid-row:span 2/span 2}.lg\:row-start-1{grid-row-start:1}.lg\:row-start-2{grid-row-start:2}.lg\:mb-4{margin-bottom:1rem}.lg\:mt-0{margin-top:0}.lg\:mt-10{margin-top:2.5rem}.lg\:block{display:block}.lg\:hidden{display:none}.lg\:grid-cols-10{grid-template-columns:repeat(10,minmax(0,1fr))}.lg\:flex-row{flex-direction:row}.lg\:justify-start{justify-content:flex-start}.lg\:justify-between{justify-content:space-between}.lg\:gap-x-20{-moz-column-gap:5rem;column-gap:5rem}.lg\:border-none{border-style:none}.lg\:bg-transparent{background-color:transparent}.lg\:p-0{padding:0}.lg\:px-0{padding-left:0;padding-right:0}.lg\:px-2{padding-left:.5rem;padding-right:.5rem}.lg\:py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.lg\:pb-28{padding-bottom:7rem}.lg\:pt-2{padding-top:.5rem}.lg\:text-200{font-size:1rem}.lg\:font-semibold{font-weight:600}.lg\:hover\:bg-transparent:hover{background-color:transparent}}@media (min-width:63.75rem){.xl\:inline{display:inline}.xl\:hidden{display:none}.xl\:grid-cols-10{grid-template-columns:repeat(10,minmax(0,1fr))}.xl\:flex-row{flex-direction:row}.xl\:items-center{align-items:center}.xl\:gap-20{gap:5rem}.xl\:gap-6{gap:1.5rem}.xl\:pt-0{padding-top:0}}@media (min-width:85.375rem){.xxl\:inline-block{display:inline-block}.xxl\:h-4{height:1rem}.xxl\:w-4{width:1rem}.xxl\:gap-20{gap:5rem}.xxl\:gap-x-32{-moz-column-gap:8rem;column-gap:8rem}.xxl\:pr-1{padding-right:.25rem}} \ No newline at end of file +/*! tailwindcss v3.4.14 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #bdbdbd}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:Roboto,Menlo,Consolas,Ubuntu Mono,Roboto Mono,DejaVu Sans Mono,monospace;;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#7c7c7c}input::placeholder,textarea::placeholder{opacity:1;color:#7c7c7c}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}html{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity));font-family:Inter var,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji,sans-serif;font-size:1rem;--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity));font-feature-settings:"kern" on,"liga" on,"calt" on,"zero" on;-webkit-font-feature-settings:"kern" on,"liga" on,"calt" on,"zero" on;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;text-size-adjust:100%;-moz-osx-font-smoothing:grayscale;font-smoothing:antialiased;font-variant-ligatures:contextual common-ligatures;font-kerning:normal;text-rendering:optimizeLegibility}svg{max-height:100%;max-width:100%}form{margin-top:0;margin-bottom:0}.realm-content{overflow-wrap:break-word;padding-top:2.5rem;font-size:1rem}.realm-content>:first-child{margin-top:0!important}.realm-content a{font-weight:500;--tw-text-opacity:1;color:rgb(34 108 87/var(--tw-text-opacity))}.realm-content a:hover{text-decoration-line:underline}.realm-content h1,.realm-content h2,.realm-content h3,.realm-content h4{margin-top:3rem;line-height:1.25;--tw-text-opacity:1;color:rgb(8 8 9/var(--tw-text-opacity))}.realm-content h2,.realm-content h2 *{font-weight:700}.realm-content h3,.realm-content h3 *,.realm-content h4,.realm-content h4 *{font-weight:600}.realm-content h1+h2,.realm-content h2+h3,.realm-content h3+h4{margin-top:1rem}.realm-content h1{font-size:2.375rem;font-weight:700}.realm-content h2{font-size:1.5rem}.realm-content h3{margin-top:2.5rem;font-size:1.25rem}.realm-content h3,.realm-content h4{--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity))}.realm-content h4{margin-top:1.5rem;margin-bottom:1.5rem;font-size:1.125rem;font-weight:500}.realm-content p{margin-top:1.25rem;margin-bottom:1.25rem}.realm-content strong{font-weight:700;--tw-text-opacity:1;color:rgb(8 8 9/var(--tw-text-opacity))}.realm-content strong *{font-weight:700}.realm-content em{font-style:oblique 10deg}.realm-content blockquote{margin-top:1rem;margin-bottom:1rem;border-left-width:4px;--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity));padding-left:1rem;--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity));font-style:oblique 10deg}.realm-content ol,.realm-content ul{margin-top:1.5rem;margin-bottom:1.5rem;padding-left:1rem}.realm-content ol li,.realm-content ul li{margin-bottom:.5rem}.realm-content img{margin-top:2rem;margin-bottom:2rem;max-width:100%}.realm-content figure{margin-top:1.5rem;margin-bottom:1.5rem;text-align:center}.realm-content figcaption{font-size:.875rem;--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity))}.realm-content :not(pre)>code{border-radius:.25rem;background-color:rgb(226 226 226/var(--tw-bg-opacity));padding:.125rem .25rem;font-size:.96em}.realm-content :not(pre)>code,.realm-content pre{--tw-bg-opacity:1;font-family:Roboto,Menlo,Consolas,Ubuntu Mono,Roboto Mono,DejaVu Sans Mono,monospace;}.realm-content pre{overflow-x:auto;border-radius:.375rem;background-color:rgb(240 240 240/var(--tw-bg-opacity));padding:1rem}.realm-content hr{margin-top:2.5rem;margin-bottom:2.5rem;border-top-width:1px;--tw-border-opacity:1;border-color:rgb(226 226 226/var(--tw-border-opacity))}.realm-content table{margin-top:2rem;margin-bottom:2rem;display:block;width:100%;max-width:100%;border-collapse:collapse;overflow-x:auto}.realm-content td,.realm-content th{white-space:normal;overflow-wrap:break-word;border-width:1px;padding:.5rem 1rem}.realm-content th{--tw-bg-opacity:1;background-color:rgb(226 226 226/var(--tw-bg-opacity));font-weight:700}.realm-content caption{margin-top:.5rem;text-align:left;font-size:.875rem;--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity))}.realm-content q{margin-top:1.5rem;margin-bottom:1.5rem;border-left-width:4px;--tw-border-opacity:1;border-left-color:rgb(204 204 204/var(--tw-border-opacity));padding-left:1rem;--tw-text-opacity:1;color:rgb(85 85 85/var(--tw-text-opacity));font-style:oblique 10deg;quotes:"“" "”" "‘" "’"}.realm-content q:after,.realm-content q:before{margin-right:.25rem;font-size:1.5rem;--tw-text-opacity:1;color:rgb(153 153 153/var(--tw-text-opacity));content:open-quote;vertical-align:-.4rem}.realm-content q:after{content:close-quote}.realm-content q:before{content:open-quote}.realm-content q:after{content:close-quote}.realm-content ol ol,.realm-content ol ul,.realm-content ul ol,.realm-content ul ul{margin-top:.75rem;margin-bottom:.5rem;padding-left:1rem}.realm-content ul{list-style-type:disc}.realm-content ol{list-style-type:decimal}.realm-content abbr[title]{cursor:help;border-bottom-width:1px;border-style:dotted}.realm-content details{margin-top:1.25rem;margin-bottom:1.25rem}.realm-content summary{cursor:pointer;font-weight:700}.realm-content a code{color:inherit}.realm-content video{margin-top:2rem;margin-bottom:2rem;max-width:100%}.realm-content math{font-family:Roboto,Menlo,Consolas,Ubuntu Mono,Roboto Mono,DejaVu Sans Mono,monospace;}.realm-content small{font-size:.875rem}.realm-content del{text-decoration-line:line-through}.realm-content sub{vertical-align:sub;font-size:.75rem}.realm-content sup{vertical-align:super;font-size:.75rem}.realm-content button,.realm-content input{border-width:1px;--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity));padding:.5rem 1rem}main :is(h1,h2,h3,h4){scroll-margin-top:6rem}::-moz-selection{--tw-bg-opacity:1;background-color:rgb(34 108 87/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}::selection{--tw-bg-opacity:1;background-color:rgb(34 108 87/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.sidemenu .peer:checked+label>svg{--tw-text-opacity:1;color:rgb(34 108 87/var(--tw-text-opacity))}.toc-expend-btn:has(#toc-expend:checked)+nav{display:block}.toc-expend-btn:has(#toc-expend:checked) .toc-expend-btn_ico{--tw-rotate:180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.main-header:has(#sidemenu-docs:checked)+main #sidebar #sidebar-docs,.main-header:has(#sidemenu-meta:checked)+main #sidebar #sidebar-meta,.main-header:has(#sidemenu-source:checked)+main #sidebar #sidebar-source,.main-header:has(#sidemenu-summary:checked)+main #sidebar #sidebar-summary{display:block}@media (min-width:40rem){:is(.main-header:has(#sidemenu-source:checked),.main-header:has(#sidemenu-docs:checked),.main-header:has(#sidemenu-meta:checked)) .main-navigation,:is(.main-header:has(#sidemenu-source:checked),.main-header:has(#sidemenu-docs:checked),.main-header:has(#sidemenu-meta:checked))+main .realm-content{grid-column:span 6/span 6}:is(.main-header:has(#sidemenu-source:checked),.main-header:has(#sidemenu-docs:checked),.main-header:has(#sidemenu-meta:checked)) .sidemenu,:is(.main-header:has(#sidemenu-source:checked),.main-header:has(#sidemenu-docs:checked),.main-header:has(#sidemenu-meta:checked))+main #sidebar{grid-column:span 4/span 4}}:is(.main-header:has(#sidemenu-source:checked),.main-header:has(#sidemenu-docs:checked),.main-header:has(#sidemenu-meta:checked))+main #sidebar:before{position:absolute;top:0;left:-1.75rem;z-index:-1;display:block;height:100%;width:50vw;--tw-bg-opacity:1;background-color:rgb(226 226 226/var(--tw-bg-opacity));--tw-content:"";content:var(--tw-content)}main :is(.source-code)>pre{overflow:scroll;border-radius:.375rem;--tw-bg-opacity:1!important;background-color:rgb(255 255 255/var(--tw-bg-opacity))!important;padding:1rem .25rem;font-family:Roboto,Menlo,Consolas,Ubuntu Mono,Roboto Mono,DejaVu Sans Mono,monospace;;font-size:.875rem}@media (min-width:40rem){main :is(.source-code)>pre{padding:2rem .75rem;font-size:1rem}}main .realm-content>pre a:hover{text-decoration-line:none}main :is(.realm-content,.source-code)>pre .chroma-ln:target{background-color:transparent!important}main :is(.realm-content,.source-code)>pre .chroma-line:has(.chroma-ln:target),main :is(.realm-content,.source-code)>pre .chroma-line:has(.chroma-ln:target) .chroma-cl,main :is(.realm-content,.source-code)>pre .chroma-line:has(.chroma-lnlinks:hover),main :is(.realm-content,.source-code)>pre .chroma-line:has(.chroma-lnlinks:hover) .chroma-cl{border-radius:.375rem;--tw-bg-opacity:1!important;background-color:rgb(226 226 226/var(--tw-bg-opacity))!important}main :is(.realm-content,.source-code)>pre .chroma-ln{scroll-margin-top:6rem}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.bottom-1{bottom:.25rem}.left-0{left:0}.right-2{right:.5rem}.right-3{right:.75rem}.top-0{top:0}.top-1\/2{top:50%}.top-14{top:3.5rem}.top-2{top:.5rem}.z-1{z-index:1}.z-max{z-index:9999}.col-span-1{grid-column:span 1/span 1}.col-span-10{grid-column:span 10/span 10}.col-span-3{grid-column:span 3/span 3}.col-span-7{grid-column:span 7/span 7}.row-span-1{grid-row:span 1/span 1}.row-start-1{grid-row-start:1}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-8{margin-bottom:2rem}.mr-10{margin-right:2.5rem}.mt-1{margin-top:.25rem}.mt-10{margin-top:2.5rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.grid{display:grid}.hidden{display:none}.h-10{height:2.5rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-full{height:100%}.max-h-screen{max-height:100vh}.min-h-full{min-height:100%}.min-h-screen{min-height:100vh}.w-10{width:2.5rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-full{width:100%}.min-w-2{min-width:.5rem}.min-w-48{min-width:12rem}.max-w-screen-max{max-width:98.75rem}.shrink-0{flex-shrink:0}.grow-\[2\]{flex-grow:2}.-translate-y-1\/2{--tw-translate-y:-50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-pointer{cursor:pointer}.list-none{list-style-type:none}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-flow-dense{grid-auto-flow:dense}.auto-rows-min{grid-auto-rows:min-content}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-10{grid-template-columns:repeat(10,minmax(0,1fr))}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.items-stretch{align-items:stretch}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-8{gap:2rem}.gap-x-20{-moz-column-gap:5rem;column-gap:5rem}.gap-x-3{-moz-column-gap:.75rem;column-gap:.75rem}.gap-y-2{row-gap:.5rem}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-scroll{overflow:scroll}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.rounded{border-radius:.375rem}.rounded-sm{border-radius:.25rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-t{border-top-width:1px}.border-gray-100{--tw-border-opacity:1;border-color:rgb(226 226 226/var(--tw-border-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(226 226 226/var(--tw-bg-opacity))}.bg-gray-300{--tw-bg-opacity:1;background-color:rgb(153 153 153/var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(240 240 240/var(--tw-bg-opacity))}.bg-light{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-transparent{background-color:transparent}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-4{padding:1rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-10{padding-left:2.5rem;padding-right:2.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-px{padding-top:1px;padding-bottom:1px}.pb-24{padding-bottom:6rem}.pb-3{padding-bottom:.75rem}.pb-4{padding-bottom:1rem}.pb-6{padding-bottom:1.5rem}.pb-8{padding-bottom:2rem}.pl-4{padding-left:1rem}.pr-10{padding-right:2.5rem}.pt-0\.5{padding-top:.125rem}.pt-2{padding-top:.5rem}.font-mono{font-family:Roboto,Menlo,Consolas,Ubuntu Mono,Roboto Mono,DejaVu Sans Mono,monospace;}.text-100{font-size:.875rem}.text-200{font-size:1rem}.text-50{font-size:.75rem}.text-600{font-size:1.5rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.capitalize{text-transform:capitalize}.leading-tight{line-height:1.25}.text-gray-300{--tw-text-opacity:1;color:rgb(153 153 153/var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity:1;color:rgb(124 124 124/var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity:1;color:rgb(19 19 19/var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgb(8 8 9/var(--tw-text-opacity))}.text-green-600{--tw-text-opacity:1;color:rgb(34 108 87/var(--tw-text-opacity))}.text-light{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.outline-none{outline:2px solid transparent;outline-offset:2px}.text-stroke{-webkit-text-stroke:currentColor;-webkit-text-stroke-width:.6px}.no-scrollbar::-webkit-scrollbar{display:none}.no-scrollbar{-ms-overflow-style:none;scrollbar-width:none}.\*\:pl-0>*{padding-left:0}.before\:px-\[0\.18rem\]:before{content:var(--tw-content);padding-left:.18rem;padding-right:.18rem}.before\:text-gray-300:before{content:var(--tw-content);--tw-text-opacity:1;color:rgb(153 153 153/var(--tw-text-opacity))}.before\:content-\[\'\/\'\]:before{--tw-content:"/";content:var(--tw-content)}.before\:content-\[\'\:\'\]:before{--tw-content:":";content:var(--tw-content)}.before\:content-\[\'open\'\]:before{--tw-content:"open";content:var(--tw-content)}.after\:pointer-events-none:after{content:var(--tw-content);pointer-events:none}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:bottom-0:after{content:var(--tw-content);bottom:0}.after\:left-0:after{content:var(--tw-content);left:0}.after\:top-0:after{content:var(--tw-content);top:0}.after\:block:after{content:var(--tw-content);display:block}.after\:h-1:after{content:var(--tw-content);height:.25rem}.after\:h-full:after{content:var(--tw-content);height:100%}.after\:w-full:after{content:var(--tw-content);width:100%}.after\:rounded-t-sm:after{content:var(--tw-content);border-top-left-radius:.25rem;border-top-right-radius:.25rem}.after\:bg-gray-100:after{content:var(--tw-content);--tw-bg-opacity:1;background-color:rgb(226 226 226/var(--tw-bg-opacity))}.after\:bg-green-600:after{content:var(--tw-content);--tw-bg-opacity:1;background-color:rgb(34 108 87/var(--tw-bg-opacity))}.first\:border-t:first-child{border-top-width:1px}.hover\:border-gray-300:hover{--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity))}.hover\:bg-gray-100:hover{--tw-bg-opacity:1;background-color:rgb(226 226 226/var(--tw-bg-opacity))}.hover\:bg-gray-50:hover{--tw-bg-opacity:1;background-color:rgb(240 240 240/var(--tw-bg-opacity))}.hover\:bg-green-600:hover{--tw-bg-opacity:1;background-color:rgb(34 108 87/var(--tw-bg-opacity))}.hover\:text-gray-600:hover{--tw-text-opacity:1;color:rgb(84 89 93/var(--tw-text-opacity))}.hover\:text-green-600:hover{--tw-text-opacity:1;color:rgb(34 108 87/var(--tw-text-opacity))}.hover\:text-light:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.hover\:underline:hover{text-decoration-line:underline}.focus\:border-gray-300:focus{--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity))}.focus\:border-l-gray-300:focus{--tw-border-opacity:1;border-left-color:rgb(153 153 153/var(--tw-border-opacity))}.group:hover .group-hover\:border-gray-300{--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity))}.group:hover .group-hover\:border-l-gray-300{--tw-border-opacity:1;border-left-color:rgb(153 153 153/var(--tw-border-opacity))}.group.is-active .group-\[\.is-active\]\:text-green-600{--tw-text-opacity:1;color:rgb(34 108 87/var(--tw-text-opacity))}.peer:checked~.peer-checked\:before\:content-\[\'close\'\]:before{--tw-content:"close";content:var(--tw-content)}.peer:focus-within~.peer-focus-within\:hidden{display:none}.has-\[ul\:empty\]\:hidden:has(ul:empty){display:none}.has-\[\:focus-within\]\:border-gray-300:has(:focus-within){--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity))}.has-\[\:focus\]\:border-gray-300:has(:focus){--tw-border-opacity:1;border-color:rgb(153 153 153/var(--tw-border-opacity))}@media (min-width:30rem){.sm\:gap-6{gap:1.5rem}}@media (min-width:40rem){.md\:col-span-3{grid-column:span 3/span 3}.md\:mb-0{margin-bottom:0}.md\:h-4{height:1rem}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:items-center{align-items:center}.md\:gap-x-8{-moz-column-gap:2rem;column-gap:2rem}.md\:px-10{padding-left:2.5rem;padding-right:2.5rem}.md\:pb-0{padding-bottom:0}}@media (min-width:51.25rem){.lg\:order-2{order:2}.lg\:col-span-3{grid-column:span 3/span 3}.lg\:col-span-7{grid-column:span 7/span 7}.lg\:row-span-2{grid-row:span 2/span 2}.lg\:row-start-1{grid-row-start:1}.lg\:row-start-2{grid-row-start:2}.lg\:mb-4{margin-bottom:1rem}.lg\:mt-0{margin-top:0}.lg\:mt-10{margin-top:2.5rem}.lg\:block{display:block}.lg\:hidden{display:none}.lg\:grid-cols-10{grid-template-columns:repeat(10,minmax(0,1fr))}.lg\:flex-row{flex-direction:row}.lg\:justify-start{justify-content:flex-start}.lg\:justify-between{justify-content:space-between}.lg\:gap-x-20{-moz-column-gap:5rem;column-gap:5rem}.lg\:border-none{border-style:none}.lg\:bg-transparent{background-color:transparent}.lg\:p-0{padding:0}.lg\:px-0{padding-left:0;padding-right:0}.lg\:px-2{padding-left:.5rem;padding-right:.5rem}.lg\:py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.lg\:pb-28{padding-bottom:7rem}.lg\:pt-2{padding-top:.5rem}.lg\:text-200{font-size:1rem}.lg\:font-semibold{font-weight:600}.lg\:hover\:bg-transparent:hover{background-color:transparent}}@media (min-width:63.75rem){.xl\:inline{display:inline}.xl\:hidden{display:none}.xl\:grid-cols-10{grid-template-columns:repeat(10,minmax(0,1fr))}.xl\:flex-row{flex-direction:row}.xl\:items-center{align-items:center}.xl\:gap-20{gap:5rem}.xl\:gap-6{gap:1.5rem}.xl\:pt-0{padding-top:0}}@media (min-width:85.375rem){.xxl\:inline-block{display:inline-block}.xxl\:h-4{height:1rem}.xxl\:w-4{width:1rem}.xxl\:gap-20{gap:5rem}.xxl\:gap-x-32{-moz-column-gap:8rem;column-gap:8rem}.xxl\:pr-1{padding-right:.25rem}} \ No newline at end of file