From 4c95054b301210fb19bcab4865a2aeaa621fc20a Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Thu, 4 Jan 2024 15:16:25 +0800 Subject: [PATCH 01/32] fixup --- gnovm/Makefile | 1 + gnovm/pkg/gnolang/debug.go | 23 +++++- gnovm/pkg/gnolang/frame.go | 2 +- gnovm/pkg/gnolang/machine.go | 8 +- gnovm/pkg/gnolang/nodes.go | 13 +++- gnovm/pkg/gnolang/op_assign.go | 6 ++ gnovm/pkg/gnolang/op_call.go | 35 +++++++++ gnovm/pkg/gnolang/op_eval.go | 34 ++++++++ gnovm/pkg/gnolang/op_exec.go | 7 ++ gnovm/pkg/gnolang/op_expressions.go | 55 ++++++++++++- gnovm/pkg/gnolang/preprocess.go | 112 ++++++++++++++++++++++++++- gnovm/pkg/gnolang/realm.go | 2 + gnovm/pkg/gnolang/transcribe.go | 37 +++++++++ gnovm/pkg/gnolang/types.go | 3 + gnovm/pkg/gnolang/values.go | 30 ++++++- gnovm/tests/file_test.go | 5 ++ gnovm/tests/files/debug/1.gno | 22 ++++++ gnovm/tests/files/debug/1a.gno | 22 ++++++ gnovm/tests/files/debug/1b.gno | 22 ++++++ gnovm/tests/files/debug/closure0.gno | 17 ++++ gnovm/tests/files/debug/closure1.gno | 20 +++++ gnovm/tests/files/debug/closure2.gno | 28 +++++++ gnovm/tests/files/debug/closure3.gno | 23 ++++++ gnovm/tests/files/debug/closure4.gno | 23 ++++++ gnovm/tests/files/debug/closure5.gno | 23 ++++++ gnovm/tests/files/debug/closure6.gno | 23 ++++++ gnovm/tests/files/debug/closure7.gno | 28 +++++++ gnovm/tests/files/debug/closure8.gno | 10 +++ 28 files changed, 623 insertions(+), 11 deletions(-) create mode 100644 gnovm/tests/files/debug/1.gno create mode 100644 gnovm/tests/files/debug/1a.gno create mode 100644 gnovm/tests/files/debug/1b.gno create mode 100644 gnovm/tests/files/debug/closure0.gno create mode 100644 gnovm/tests/files/debug/closure1.gno create mode 100644 gnovm/tests/files/debug/closure2.gno create mode 100644 gnovm/tests/files/debug/closure3.gno create mode 100644 gnovm/tests/files/debug/closure4.gno create mode 100644 gnovm/tests/files/debug/closure5.gno create mode 100644 gnovm/tests/files/debug/closure6.gno create mode 100644 gnovm/tests/files/debug/closure7.gno create mode 100644 gnovm/tests/files/debug/closure8.gno diff --git a/gnovm/Makefile b/gnovm/Makefile index cc7154492d8..cbe6802d32d 100644 --- a/gnovm/Makefile +++ b/gnovm/Makefile @@ -60,6 +60,7 @@ _test.gnolang.native:; go test tests/*.go -test.short -run "TestFilesNativ _test.gnolang.stdlibs:; go test tests/*.go -test.short -run 'TestFiles$$/' $(GOTEST_FLAGS) _test.gnolang.native.sync:; go test tests/*.go -test.short -run "TestFilesNative/" --update-golden-tests $(GOTEST_FLAGS) _test.gnolang.stdlibs.sync:; go test tests/*.go -test.short -run 'TestFiles$$/' --update-golden-tests $(GOTEST_FLAGS) +_test.gnolang.debug:; go test tests/*.go -test.short -run 'TestDebug$$/' --update-golden-tests $(GOTEST_FLAGS) ######################################## # Code gen diff --git a/gnovm/pkg/gnolang/debug.go b/gnovm/pkg/gnolang/debug.go index cb21da12ef2..33ad29bbaae 100644 --- a/gnovm/pkg/gnolang/debug.go +++ b/gnovm/pkg/gnolang/debug.go @@ -18,14 +18,21 @@ import ( // so it is still faster to first check the truth value // before calling debug.Println or debug.Printf. -type debugging bool +type ( + debugging bool + debugPreprocess bool +) // using a const is probably faster. // const debug debugging = true // or flip -var debug debugging = false +var ( + debug debugging = false + debugPP debugPreprocess = false +) func init() { debug = os.Getenv("DEBUG") == "1" + debugPP = os.Getenv("DEBUG_PP") == "1" if debug { go func() { // e.g. @@ -63,6 +70,18 @@ func (d debugging) Printf(format string, args ...interface{}) { } } +func (d debugPreprocess) Println(args ...interface{}) { + if d { + fmt.Println(append([]interface{}{"DEBUG:"}, args...)...) + } +} + +func (d debugPreprocess) Printf(format string, args ...interface{}) { + if d { + fmt.Printf("DEBUG: "+format, args...) + } +} + var derrors []string = nil // Instead of actually panic'ing, which messes with tests, errors are sometimes diff --git a/gnovm/pkg/gnolang/frame.go b/gnovm/pkg/gnolang/frame.go index 7f87fa26097..f495cbe4d35 100644 --- a/gnovm/pkg/gnolang/frame.go +++ b/gnovm/pkg/gnolang/frame.go @@ -31,7 +31,7 @@ type Frame struct { func (fr Frame) String() string { if fr.Func != nil { return fmt.Sprintf("[FRAME FUNC:%v RECV:%s (%d args) %d/%d/%d/%d/%d LASTPKG:%s LASTRLM:%v]", - fr.Func, + *fr.Func, fr.Receiver, fr.NumArgs, fr.NumOps, diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go index a22d335ad3b..b359572690b 100644 --- a/gnovm/pkg/gnolang/machine.go +++ b/gnovm/pkg/gnolang/machine.go @@ -905,6 +905,7 @@ const ( OpStructLit Op = 0x50 // X{...} OpFuncLit Op = 0x51 // func(T){Body} OpConvert Op = 0x52 // Y(X) + OpPreFuncLit Op = 0x53 // /* Native operators */ OpArrayLitGoNative Op = 0x60 @@ -1038,6 +1039,7 @@ const ( OpCPUMapLit = 1 OpCPUStructLit = 1 OpCPUFuncLit = 1 + OpCPUPreFuncLit = 1 OpCPUConvert = 1 /* Native operators */ @@ -1291,6 +1293,9 @@ func (m *Machine) Run() { case OpFuncLit: m.incrCPU(OpCPUFuncLit) m.doOpFuncLit() + case OpPreFuncLit: + m.incrCPU(OpCPUPreFuncLit) + m.doOpPreFuncLit() case OpMapLit: m.incrCPU(OpCPUMapLit) m.doOpMapLit() @@ -1628,7 +1633,8 @@ func (m *Machine) ReapValues(start int) []TypedValue { func (m *Machine) PushBlock(b *Block) { if debug { - m.Println("+B") + //m.Println("+B") + m.Printf("+B: %v \n", b) } m.Blocks = append(m.Blocks, b) } diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index b127cd32421..c38823395fb 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -494,10 +494,15 @@ type KeyValueExprs []KeyValueExpr type FuncLitExpr struct { Attributes StaticBlock - Type FuncTypeExpr // function type - Body // function body + Type FuncTypeExpr // function type + Body // function body + Closure Closure } +//type ClosureObject struct { +// StaticBlock +//} + // The preprocessor replaces const expressions // with *ConstExpr nodes. type ConstExpr struct { @@ -1348,6 +1353,7 @@ func (x *PackageNode) NewPackage() *PackageValue { // NOTE: declared methods do not get their closures set here. See // *DeclaredType.GetValueAt() which returns a filled copy. func (x *PackageNode) PrepareNewValues(pv *PackageValue) []TypedValue { + debug.Println("-----PrepareNewValues") if pv.PkgPath == "" { // nothing to prepare for throwaway packages. // TODO: double check to see if still relevant. @@ -1375,6 +1381,7 @@ func (x *PackageNode) PrepareNewValues(pv *PackageValue) []TypedValue { // copy function value and assign closure from package value. fv = fv.Copy(nilAllocator) fv.Closure = pv.fBlocksMap[fv.FileName] + debug.Printf("fv.Closure: %v \n", fv.Closure) if fv.Closure == nil { panic(fmt.Sprintf("file block missing for file %q", fv.FileName)) } @@ -1588,6 +1595,7 @@ func (sb *StaticBlock) GetParentNode(store Store) BlockNode { // Implements BlockNode. // As a side effect, notes externally defined names. func (sb *StaticBlock) GetPathForName(store Store, n Name) ValuePath { + //debug.Printf("-----GetPathForName: %v \n", n) if n == "_" { return NewValuePathBlock(0, 0, "_") } @@ -1701,6 +1709,7 @@ func (sb *StaticBlock) GetStaticTypeOfAt(store Store, path ValuePath) Type { // Implements BlockNode. func (sb *StaticBlock) GetLocalIndex(n Name) (uint16, bool) { + //debug.Printf("GetLocalIndex: %v \n", n) for i, name := range sb.Names { if name == n { if debug { diff --git a/gnovm/pkg/gnolang/op_assign.go b/gnovm/pkg/gnolang/op_assign.go index 0cc30861355..4388fc522ef 100644 --- a/gnovm/pkg/gnolang/op_assign.go +++ b/gnovm/pkg/gnolang/op_assign.go @@ -1,11 +1,17 @@ package gnolang func (m *Machine) doOpDefine() { + debug.Println("-----doOpDefine") s := m.PopStmt().(*AssignStmt) + debug.Printf("m.NumValues: %d \n", m.NumValues) // Define each value evaluated for Lhs. // NOTE: PopValues() returns a slice in // forward order, not the usual reverse. + //m.PopValue() rvs := m.PopValues(len(s.Lhs)) + debug.Printf("s:%v \n", s) + debug.Printf("lhs:%v \n", s.Lhs) + debug.Printf("rvs:%v \n", rvs) lb := m.LastBlock() for i := 0; i < len(s.Lhs); i++ { // Get name and value of i'th term. diff --git a/gnovm/pkg/gnolang/op_call.go b/gnovm/pkg/gnolang/op_call.go index eebee978919..ef4fc7ab0c8 100644 --- a/gnovm/pkg/gnolang/op_call.go +++ b/gnovm/pkg/gnolang/op_call.go @@ -8,7 +8,10 @@ import ( func (m *Machine) doOpPrecall() { cx := m.PopExpr().(*CallExpr) + + debug.Printf("-----doOpPrecall------callExpr :%v \n", cx) v := m.PeekValue(1 + cx.NumArgs).V + debug.Printf("-----doOpPrecall------v :%v \n", v) if debug { if v == nil { // This may happen due to an undefined uverse or @@ -19,6 +22,9 @@ func (m *Machine) doOpPrecall() { } switch fv := v.(type) { case *FuncValue: + debug.Printf("-----funcValue: %v------ \n", fv) + debug.Printf("-----body: %v------ \n", fv.GetBodyFromSource(m.Store)) + m.PushFrameCall(cx, fv, TypedValue{}) m.PushOp(OpCall) case *BoundMethodValue: @@ -46,6 +52,7 @@ func (m *Machine) doOpPrecall() { var gReturnStmt = &ReturnStmt{} func (m *Machine) doOpCall() { + debug.Println("-----doOpCall-----") // NOTE: Frame won't be popped until the statement is complete, to // discard the correct number of results for func calls in ExprStmts. fr := m.LastFrame() @@ -56,6 +63,33 @@ func (m *Machine) doOpCall() { isMethod := 0 // 1 if true // Create new block scope. clo := fr.Func.GetClosure(m.Store) + //debug.Printf("-----fv: %v ----- \n", fv) + //debug.Printf("-----ft: %v ----- \n", ft) + debug.Printf("-----got closure: %v ----- \n", clo) + //debug.Printf("-----func source: %v ----- \n", fr.Func.GetClosure(m.Store)) + debug.Println("----------") + //debugPP.Printf("-----new block for closure: %v ----- \n", b) + // TODO: update b here using captured vars + captures := fr.Func.Captures + if captures != nil { + debug.Printf("captures before call: %v, len(names): %d, len(values): %d \n", *captures, len(captures.names), len(captures.values)) + names := clo.GetSource(m.Store).GetBlockNames() + debug.Printf("names: %v \n", names) + for i1, n1 := range captures.names { + var index int + for i2, n2 := range names { + if n1 == n2 { // match and replace + index = i2 + debug.Printf("index of %s in target block is: %d \n", n1, index) + debug.Printf("target tv[%d] in captured values is :%v \n", i1, captures.values[i1]) + // replace lv values with index + clo.UpdateValue(index, captures.values[i1]) + } + } + } + } + fr.Func.Captures = nil + b := m.Alloc.NewBlock(fr.Func.GetSource(m.Store), clo) m.PushBlock(b) if fv.nativeBody == nil && fv.NativePkg != "" { @@ -67,6 +101,7 @@ func (m *Machine) doOpCall() { } if fv.nativeBody == nil { fbody := fv.GetBodyFromSource(m.Store) + debug.Printf("fbody: %v \n", fbody) if len(ft.Results) == 0 { // Push final empty *ReturnStmt; // TODO: transform in preprocessor instead to return only diff --git a/gnovm/pkg/gnolang/op_eval.go b/gnovm/pkg/gnolang/op_eval.go index 283d035dca2..167aef4c250 100644 --- a/gnovm/pkg/gnolang/op_eval.go +++ b/gnovm/pkg/gnolang/op_eval.go @@ -306,7 +306,41 @@ func (m *Machine) doOpEval() { m.PushExpr(x.Type) m.PushOp(OpEval) case *FuncLitExpr: + debug.Println("-----FuncLitExpr") + b := m.LastBlock() + debug.Printf("b: %v \n", b) + //debug.Printf("x: %v, body: %v \n", x, x.GetBody()) + // + //bk := x.GetBlock() + //bs := x.GetBodyStmt() + //names := x.GetBlockNames() + //by := x.GetBody() + // TODO: capture + // 1. span slice for funcLitExpr in preprocess + // 2. set value in eval funcLitExpr + // 3. add interceptor to nameExpr evaluation process, namely, use captured vars + + // block structure + // for block + // funcLitBlock + + //debug.Printf("body:%v\n", by) + //debug.Printf("bodyStmt:%v \n", bs) + //debug.Printf("block:%v \n", bk) + //debug.Printf("names:%v \n", names) + + //bn := m.Alloc.NewBlock(x, m.LastBlock()) + //b.bodyStmt = bodyStmt{ + // Body: x.Body, + // BodyLen: len(x.Body), + // NextBodyIndex: -2, + //} + //debug.Printf("bn: %v \n", bn) + //m.PushBlock(bn) + //m.PushOp(OpPopBlock) + m.PushOp(OpFuncLit) + m.PushOp(OpPreFuncLit) // evaluate func type m.PushExpr(&x.Type) m.PushOp(OpEval) diff --git a/gnovm/pkg/gnolang/op_exec.go b/gnovm/pkg/gnolang/op_exec.go index 300303135ad..55c28dccd6a 100644 --- a/gnovm/pkg/gnolang/op_exec.go +++ b/gnovm/pkg/gnolang/op_exec.go @@ -54,6 +54,7 @@ func (m *Machine) doOpExec(op Op) { s := m.PeekStmt(1) // TODO: PeekStmt1()? if debug { debug.Printf("PEEK STMT: %v\n", s) + debug.Printf("op: %v\n", op) debug.Printf("%v\n", m) } @@ -431,8 +432,10 @@ EXEC_SWITCH: if debug { debug.Printf("EXEC: %v\n", s) } + // TODO: add case log for debug switch cs := s.(type) { case *AssignStmt: + debug.Println("-----AssignStmt") switch cs.Op { case ASSIGN: m.PushOp(OpAssign) @@ -493,8 +496,12 @@ EXEC_SWITCH: m.PushExpr(cs.X) m.PushOp(OpEval) case *ForStmt: + debug.Println("-----ForStmt") m.PushFrameBasic(cs) + debug.Printf("cs: %v \n", cs) + debug.Printf("parent: %v \n", m.LastBlock()) b := m.Alloc.NewBlock(cs, m.LastBlock()) + debug.Printf("b: %v \n", b) b.bodyStmt = bodyStmt{ Body: cs.Body, BodyLen: len(cs.Body), diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index b3bf240aea1..7c2d14d7b0b 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -677,11 +677,60 @@ func (m *Machine) doOpStructLit() { }) } +type closureObject struct { + data map[string]interface{} +} + +func (m *Machine) doOpPreFuncLit() { + debug.Println("-----doOpPreFuncLit") + x := m.PeekExpr(1).(*FuncLitExpr) + lb := m.LastBlock() + b := m.Alloc.NewBlock(x, lb) + m.PushBlock(b) + m.PushOp(OpPopBlock) + + closure := x.Closure + for i, nx := range closure.nxs { + debug.Printf("closure[%s]: %v \n", i, nx) + m.PushExpr(nx) + m.PushOp(OpEval) + } +} + func (m *Machine) doOpFuncLit() { + debug.Println("-----doOpFuncLit") x := m.PopExpr().(*FuncLitExpr) - ft := m.PopValue().V.(TypeValue).Type.(*FuncType) lb := m.LastBlock() + debug.Printf("lb: %v \n", lb) + + captured := &Captured{} + for _, nx := range x.Closure.nxs { + debug.Printf("range closures of : %s \n", string(nx.Name)) + v := *m.PopValue() + if !v.IsUndefined() { // e.g. args of func. forward + debug.Printf("capturing v: %v \n", v) + captured.names = append(captured.names, nx.Name) + captured.values = append(captured.values, v) + } else { + debug.Println("undefined, skip") + } + + //var index int + //for i, n := range names { + // if name == n { // match and replace + // index = i + // debug.Printf("index of %s is: %d \n", name, index) + // // replace lv values with index + // nb.UpdateValue(index, v) + // } + //} + } + debug.Printf("---captured: %v \n", captured) + + ft := m.PopValue().V.(TypeValue).Type.(*FuncType) + m.Alloc.AllocateFunc() + m.PushValue(TypedValue{ T: ft, V: &FuncValue{ @@ -690,11 +739,11 @@ func (m *Machine) doOpFuncLit() { Source: x, Name: "", Closure: lb, + Captures: captured, PkgPath: m.Package.PkgPath, body: x.Body, nativeBody: nil, - }, - }) + }}) } func (m *Machine) doOpConvert() { diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index ee943226c85..9713ed7cbfc 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -8,6 +8,100 @@ import ( "github.com/gnolang/gno/tm2/pkg/errors" ) +var closures []*Closure +var fxs []*FuncLitExpr + +func init() { + closures = make([]*Closure, 0) + fxs = make([]*FuncLitExpr, 0) +} + +func pushClosure(c *Closure) { + closures = append(closures, c) +} + +func popClosure() { + debug.Println("-c") + if len(closures) != 0 { + closures = closures[:len(closures)-1] + } +} + +func pushFxs(fx *FuncLitExpr) { + fxs = append(fxs, fx) +} + +func popFx() { + debug.Println("-fx") + if len(fxs) != 0 { + fxs = fxs[:len(fxs)-1] + } +} + +func dumpFxs() { + println("============Dump fxs===========") + println("len: ", len(fxs)) + for i, fx := range fxs { + fmt.Printf("fx[%d]: %v\n", i, fx) + } + println("============end===============") + println("\n") +} +func dumpClosures() { + println("============Dump closures=======") + println("len: ", len(closures)) + for _, c := range closures { + for i, name := range c.names { + fmt.Printf("name[%s]: %v\n", i, name) + } + for i, nx := range c.nxs { + fmt.Printf("nx[%s]: %v\n", i, nx) + } + //for i, fx := range c.Fxs { + // fmt.Printf("fx[%d]: %v\n", i, fx) + //} + } + println("============end===============") + println("\n") +} + +func currentClosure() *Closure { + if len(closures) == 0 { + return nil + } + return closures[len(closures)-1] +} + +func currentFx() *FuncLitExpr { + if len(fxs) == 0 { + return nil + } + return fxs[len(fxs)-1] +} + +type Closure struct { + //nxs map[Name]*NameExpr + names []Name + nxs []*NameExpr + //Fxs map[string]FuncLitExpr +} + +func NewClosure() *Closure { + return &Closure{} +} + +func (clo *Closure) Push(nx *NameExpr, fx *FuncLitExpr) { + debug.Printf("+nx: %v \n", nx) + debug.Printf("+fx: %v \n", fx) + clo.names = append(clo.names, nx.Name) + clo.nxs = append(clo.nxs, nx) +} + +//func (clo *Closure) Pop() { +// clo.nxs = clo.nxs[:len(clo.nxs)-1] +// clo.Fxs = clo.Fxs[:len(clo.Fxs)-1] +//} + // In the case of a *FileSet, some declaration steps have to happen // in a restricted parallel way across all the files. // Anything predefined or preprocessed here get skipped during the Preprocess @@ -222,6 +316,8 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // TRANS_ENTER ----------------------- case *ImportDecl, *ValueDecl, *TypeDecl, *FuncDecl: + debug.Println("-----trans enter Decls") + debug.Printf("node: %v \n", n) // NOTE func decl usually must happen with a // file, and so last is usually a *FileNode, // but for testing convenience we allow @@ -275,6 +371,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // TRANS_BLOCK ----------------------- case *ForStmt: + debugPP.Println("-----ForStmt-----") pushInitBlock(n, &last, &stack) // TRANS_BLOCK ----------------------- @@ -287,12 +384,14 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // TRANS_BLOCK ----------------------- case *IfCaseStmt: + debugPP.Printf("-----IfCaseStmt-----") pushRealBlock(n, &last, &stack) // parent if statement. ifs := ns[len(ns)-1].(*IfStmt) // anything declared in ifs are copied. for _, n := range ifs.GetBlockNames() { tv := ifs.GetValueRef(nil, n) + debugPP.Printf("tv : %v, *tv: %v \n", tv, *tv) last.Define(n, *tv) } @@ -356,16 +455,20 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // TRANS_BLOCK ----------------------- case *FuncLitExpr: + debug.Println("---PP funcLitExpr") + // retrieve cached function type. ft := evalStaticType(store, last, &n.Type).(*FuncType) // push func body block. pushInitBlock(n, &last, &stack) // define parameters in new block. for _, p := range ft.Params { + debug.Println("---PP define params") last.Define(p.Name, anyValue(p.Type)) } // define results in new block. for i, rf := range ft.Results { + debug.Println("---PP define results") if 0 < len(rf.Name) { last.Define(rf.Name, anyValue(rf.Type)) } else { @@ -375,6 +478,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { last.Define(Name(rn), anyValue(rf.Type)) } } + debug.Println("---PP funcLitExpr end---") // TRANS_BLOCK ----------------------- case *SelectCaseStmt: @@ -635,6 +739,9 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { switch n := n.(type) { // TRANS_LEAVE ----------------------- case *NameExpr: + debug.Println("---PP NameExpr") + debug.Println("---PP NameExpr, ftype: ", ftype) + // Validity: check that name isn't reserved. if isReservedName(n.Name) { panic(fmt.Sprintf( @@ -742,6 +849,8 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } } } + //popClosure() + //popFx() // TRANS_LEAVE ----------------------- case *BasicLitExpr: @@ -1979,7 +2088,7 @@ func pushRealBlock(bn BlockNode, last *BlockNode, stack *[]BlockNode) { // anything declared in orig are copied. for _, n := range orig.GetBlockNames() { tv := orig.GetValueRef(nil, n) - bn.Define(n, *tv) + bn.Define(n, *tv) // pointer, actually } } @@ -2985,6 +3094,7 @@ func predefineNow2(store Store, last BlockNode, d Decl, m map[Name]struct{}) (De // must be called for name declarations within (non-file, // non-package) stmt bodies. func tryPredefine(store Store, last BlockNode, d Decl) (un Name) { + debug.Println("-----tryPredefine") if d.GetAttribute(ATTR_PREDEFINED) == true { panic("decl node already predefined!") } diff --git a/gnovm/pkg/gnolang/realm.go b/gnovm/pkg/gnolang/realm.go index d10a9280301..1567859e2f6 100644 --- a/gnovm/pkg/gnolang/realm.go +++ b/gnovm/pkg/gnolang/realm.go @@ -1053,6 +1053,7 @@ func copyTypeWithRefs(typ Type) Type { // Also checks for integrity of immediate children -- they must already be // persistent (real), and not dirty, or else this function panics. func copyValueWithRefs(parent Object, val Value) Value { + debug.Println("-----copyValueWithRefs") switch cv := val.(type) { case nil: return nil @@ -1129,6 +1130,7 @@ func copyValueWithRefs(parent Object, val Value) Value { if cv.Closure != nil { closure = toRefValue(parent, cv.Closure) } + debug.Printf("closure: %v \n", closure) // nativeBody funcs which don't come from NativeStore (and thus don't // have NativePkg/Name) can't be persisted, and should not be able // to get here anyway. diff --git a/gnovm/pkg/gnolang/transcribe.go b/gnovm/pkg/gnolang/transcribe.go index c5b72336c83..24d919ba6d5 100644 --- a/gnovm/pkg/gnolang/transcribe.go +++ b/gnovm/pkg/gnolang/transcribe.go @@ -148,6 +148,23 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc // visit any children of n. switch cnn := nn.(type) { case *NameExpr: + debug.Printf("-----trans, nameExpr: %v \n", cnn) + //dumpClosures() + //dumpFxs() + // TODO: how to filter out unrelated namedExpr + // set current closure + // TODO: do we need to filter out already define in current block + currentClo := currentClosure() + debug.Printf("currentClo: %v \n", currentClo) + + if currentClo != nil { + currentClo.Push(cnn, currentFx()) + currentFx := currentFx() + currentFx.Closure = *currentClo + + dumpClosures() + dumpFxs() + } case *BasicLitExpr: case *BinaryExpr: cnn.Left = transcribe(t, nns, TRANS_BINARY_LEFT, 0, cnn.Left, &c).(Expr) // XXX wished this worked with nil. @@ -260,6 +277,10 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc cnn.Elts[idx] = KeyValueExpr{Key: k, Value: v} } case *FuncLitExpr: + debug.Printf("-----trans, funcLitExpr: %v \n", cnn) + dumpClosures() + dumpFxs() + cnn.Type = *transcribe(t, nns, TRANS_FUNCLIT_TYPE, 0, &cnn.Type, &c).(*FuncTypeExpr) if isStopOrSkip(nc, c) { return @@ -271,14 +292,29 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*FuncLitExpr) } + + debug.Println("---start trans funcLit body stmt") + debug.Println("push target closure and fx") + pushClosure(NewClosure()) + pushFxs(cnn) for idx := range cnn.Body { cnn.Body[idx] = transcribe(t, nns, TRANS_FUNCLIT_BODY, idx, cnn.Body[idx], &c).(Stmt) + if isBreak(c) { break } else if isStopOrSkip(nc, c) { + // pop before return + debug.Printf("---stop or skip, pop and return \n") + popClosure() + popFx() + return } } + // defer pop + debug.Printf("---done trans body \n") + popClosure() + popFx() case *FieldTypeExpr: cnn.Type = transcribe(t, nns, TRANS_FIELDTYPE_TYPE, 0, cnn.Type, &c).(Expr) if isStopOrSkip(nc, c) { @@ -534,6 +570,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } } case *ReturnStmt: + debug.Printf("-----trans, return stmt: %v \n", cnn) for idx := range cnn.Results { cnn.Results[idx] = transcribe(t, nns, TRANS_RETURN_RESULT, idx, cnn.Results[idx], &c).(Expr) if isBreak(c) { diff --git a/gnovm/pkg/gnolang/types.go b/gnovm/pkg/gnolang/types.go index 65bf7227458..69e7d56e4e8 100644 --- a/gnovm/pkg/gnolang/types.go +++ b/gnovm/pkg/gnolang/types.go @@ -1605,6 +1605,7 @@ func (dt *DeclaredType) FindEmbeddedFieldType(callerPath string, n Name, m map[T // runtime: *TV.GetPointerTo(path) // -> *DT.GetValueAt(path) func (dt *DeclaredType) GetValueAt(alloc *Allocator, store Store, path ValuePath) TypedValue { + debug.Printf("-----GetValueAt: %v \n", path) switch path.Type { case VPInterface: panic("should not happen") @@ -1616,7 +1617,9 @@ func (dt *DeclaredType) GetValueAt(alloc *Allocator, store Store, path ValuePath // Fill in *FV.Closure. ft := mtv.T fv := mtv.V.(*FuncValue).Copy(alloc) + debug.Printf("fv: %v \n", fv) fv.Closure = fv.GetClosure(store) + debug.Printf("closure: %v \n", fv.Closure) return TypedValue{T: ft, V: fv} } else { panic("DeclaredType.GetValueAt() expects depth == 0") diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index 3607ccc4669..ede203f0b1f 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -513,6 +513,11 @@ func (sv *StructValue) Copy(alloc *Allocator) *StructValue { return alloc.NewStruct(fields) } +type Captured struct { + names []Name + values []TypedValue +} + // ---------------------------------------- // FuncValue @@ -532,7 +537,8 @@ type FuncValue struct { Source BlockNode // for block mem allocation Name Name // name of function/method Closure Value // *Block or RefValue to closure (may be nil for file blocks; lazy) - FileName Name // file name where declared + Captures *Captured + FileName Name // file name where declared PkgPath string NativePkg string // for native bindings through NativeStore NativeName Name // not redundant with Name; this cannot be changed in userspace @@ -612,8 +618,10 @@ func (fv *FuncValue) GetPackage(store Store) *PackageValue { // file-level declared methods and functions. For those, caller // should set .Closure manually after *FuncValue.Copy(). func (fv *FuncValue) GetClosure(store Store) *Block { + debug.Println("-----GetClosure") switch cv := fv.Closure.(type) { case nil: + debug.Println("-----nil") if fv.FileName == "" { return nil } @@ -624,10 +632,12 @@ func (fv *FuncValue) GetClosure(store Store) *Block { } return fb case RefValue: + debug.Printf("-----RefValue, cv.ObjectID: %v \n", cv.ObjectID) block := store.GetObject(cv.ObjectID).(*Block) fv.Closure = block return block case *Block: + debug.Println("-----block") return cv default: panic("should not happen") @@ -1557,6 +1567,7 @@ func (tv *TypedValue) ComputeMapKey(store Store, omitType bool) MapKey { // cu: convert untyped after assignment. pass false // for const definitions, but true for all else. func (tv *TypedValue) Assign(alloc *Allocator, tv2 TypedValue, cu bool) { + debug.Printf("Assign, tv:%v, tv2:%v \n", tv, tv2) if debug { if tv.T == DataByteType { // assignment to data byte types should only @@ -1570,6 +1581,7 @@ func (tv *TypedValue) Assign(alloc *Allocator, tv2 TypedValue, cu bool) { } } *tv = tv2.Copy(alloc) + debug.Printf("*tv: %+v \n", *tv) if cu && isUntyped(tv.T) { ConvertUntypedTo(tv, defaultTypeOf(tv.T)) } @@ -2252,6 +2264,17 @@ func NewBlock(source BlockNode, parent *Block) *Block { } } +func (b *Block) UpdateValue(index int, tv TypedValue) { + debug.Printf("-----UpdateValue, index: %d, tv: %v \n", index, tv) + debug.Printf("b before update: %v \n", b) + for i, _ := range b.Values { + if i == index { + b.Values[i] = tv + } + } + debug.Printf("b after update: %v \n", b) +} + func (b *Block) String() string { return b.StringIndented(" ") } @@ -2310,6 +2333,9 @@ func (b *Block) GetParent(store Store) *Block { } func (b *Block) GetPointerToInt(store Store, index int) PointerValue { + debug.Printf("GetPointerToInt, index: %d \n", index) + debug.Printf("b: %v \n", b) + debug.Printf("len of values: %v \n", len(b.Values)) vv := fillValueTV(store, &b.Values[index]) return PointerValue{ TV: vv, @@ -2319,6 +2345,8 @@ func (b *Block) GetPointerToInt(store Store, index int) PointerValue { } func (b *Block) GetPointerTo(store Store, path ValuePath) PointerValue { + debug.Printf("-----GetPointerTo, path : %v\n", path) + debug.Printf("b: %v \n", b) if path.IsBlockBlankPath() { if debug { if path.Name != "_" { diff --git a/gnovm/tests/file_test.go b/gnovm/tests/file_test.go index f070546ab74..091b1f8ffd0 100644 --- a/gnovm/tests/file_test.go +++ b/gnovm/tests/file_test.go @@ -32,6 +32,11 @@ func TestFiles(t *testing.T) { runFileTests(t, baseDir, []string{"*_native*"}) } +func TestDebug(t *testing.T) { + baseDir := filepath.Join(".", "files/debug") + runFileTests(t, baseDir, nil) +} + func TestChallenges(t *testing.T) { baseDir := filepath.Join(".", "challenges") runFileTests(t, baseDir, nil) diff --git a/gnovm/tests/files/debug/1.gno b/gnovm/tests/files/debug/1.gno new file mode 100644 index 00000000000..7f4231373ea --- /dev/null +++ b/gnovm/tests/files/debug/1.gno @@ -0,0 +1,22 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 5; i++ { + x := i + f := func() int { + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 diff --git a/gnovm/tests/files/debug/1a.gno b/gnovm/tests/files/debug/1a.gno new file mode 100644 index 00000000000..4a2a7630f6d --- /dev/null +++ b/gnovm/tests/files/debug/1a.gno @@ -0,0 +1,22 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 2; i++ { + x := i + y := 0 + f := func() int { + x += y + //x += 1 + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/files/debug/1b.gno b/gnovm/tests/files/debug/1b.gno new file mode 100644 index 00000000000..0822368fe3a --- /dev/null +++ b/gnovm/tests/files/debug/1b.gno @@ -0,0 +1,22 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 2; i++ { + x := i + y := 0 + f := func() int { + x += y + x += 1 + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 1 +// 2 diff --git a/gnovm/tests/files/debug/closure0.gno b/gnovm/tests/files/debug/closure0.gno new file mode 100644 index 00000000000..acc1abfd404 --- /dev/null +++ b/gnovm/tests/files/debug/closure0.gno @@ -0,0 +1,17 @@ +package main + +type adder func(int, int) int + +func genAdd(k int) adder { + return func(i, j int) int { + return i + j + k + } +} + +func main() { + f := genAdd(5) + println(f(3, 4)) +} + +// Output: +// 12 diff --git a/gnovm/tests/files/debug/closure1.gno b/gnovm/tests/files/debug/closure1.gno new file mode 100644 index 00000000000..f0bbda7ca1e --- /dev/null +++ b/gnovm/tests/files/debug/closure1.gno @@ -0,0 +1,20 @@ +package main + +type adder func(int, int) int + +func genAdd(k int) adder { + return func(i, j int) int { + return i + j + k + } +} + +func main() { + f := genAdd(5) + g := genAdd(8) + println(f(3, 4)) + println(g(3, 4)) +} + +// Output: +// 12 +// 15 diff --git a/gnovm/tests/files/debug/closure2.gno b/gnovm/tests/files/debug/closure2.gno new file mode 100644 index 00000000000..e43496b9f43 --- /dev/null +++ b/gnovm/tests/files/debug/closure2.gno @@ -0,0 +1,28 @@ +package main + +func adder() func(int) int { + sum := 0 + return func(x int) int { + sum = sum + x + return sum + } +} + +func main() { + pos, neg := adder(), adder() + for i := 0; i < 10; i++ { + println(pos(i), neg(-2*i)) + } +} + +// Output: +// 0 0 +// 1 -2 +// 3 -6 +// 6 -12 +// 10 -20 +// 15 -30 +// 21 -42 +// 28 -56 +// 36 -72 +// 45 -90 diff --git a/gnovm/tests/files/debug/closure3.gno b/gnovm/tests/files/debug/closure3.gno new file mode 100644 index 00000000000..5e365a67cb4 --- /dev/null +++ b/gnovm/tests/files/debug/closure3.gno @@ -0,0 +1,23 @@ +package main + +type T1 struct { + Name string +} + +func (t *T1) genAdd(k int) func(int) int { + return func(i int) int { + println(t.Name) + return i + k + } +} + +var t = &T1{"test"} + +func main() { + f := t.genAdd(4) + println(f(5)) +} + +// Output: +// test +// 9 diff --git a/gnovm/tests/files/debug/closure4.gno b/gnovm/tests/files/debug/closure4.gno new file mode 100644 index 00000000000..61c0a77f1ba --- /dev/null +++ b/gnovm/tests/files/debug/closure4.gno @@ -0,0 +1,23 @@ +package main + +type T1 struct { + Name string +} + +func (t *T1) genAdd(k int) func(int) int { + return func(i int) int { + println(t.Name) + return i + k + } +} + +var t = T1{"test"} + +func main() { + f := t.genAdd(4) + println(f(5)) +} + +// Output: +// test +// 9 diff --git a/gnovm/tests/files/debug/closure5.gno b/gnovm/tests/files/debug/closure5.gno new file mode 100644 index 00000000000..e6d3c223607 --- /dev/null +++ b/gnovm/tests/files/debug/closure5.gno @@ -0,0 +1,23 @@ +package main + +type T1 struct { + Name string +} + +func (t T1) genAdd(k int) func(int) int { + return func(i int) int { + println(t.Name) + return i + k + } +} + +var t = T1{"test"} + +func main() { + f := t.genAdd(4) + println(f(5)) +} + +// Output: +// test +// 9 diff --git a/gnovm/tests/files/debug/closure6.gno b/gnovm/tests/files/debug/closure6.gno new file mode 100644 index 00000000000..5e365a67cb4 --- /dev/null +++ b/gnovm/tests/files/debug/closure6.gno @@ -0,0 +1,23 @@ +package main + +type T1 struct { + Name string +} + +func (t *T1) genAdd(k int) func(int) int { + return func(i int) int { + println(t.Name) + return i + k + } +} + +var t = &T1{"test"} + +func main() { + f := t.genAdd(4) + println(f(5)) +} + +// Output: +// test +// 9 diff --git a/gnovm/tests/files/debug/closure7.gno b/gnovm/tests/files/debug/closure7.gno new file mode 100644 index 00000000000..d4c0c96b0d5 --- /dev/null +++ b/gnovm/tests/files/debug/closure7.gno @@ -0,0 +1,28 @@ +package main + +import ( + "fmt" +) + +type Config struct { + A string +} + +var conf *Config = &Config{} + +func SetConfig() func(*Config) { + return func(cf *Config) { + conf = cf + } +} + +func main() { + conf := &Config{ + A: "foo", + } + + fmt.Println(conf.A) +} + +// Output: +// foo diff --git a/gnovm/tests/files/debug/closure8.gno b/gnovm/tests/files/debug/closure8.gno new file mode 100644 index 00000000000..6ef1b43fca6 --- /dev/null +++ b/gnovm/tests/files/debug/closure8.gno @@ -0,0 +1,10 @@ +package main + +var f = func(a int) int { return 2 + a } + +func main() { + println(f(3)) +} + +// Output: +// 5 From 1b87465e8de49af80b68b83d8fd7785e1d83e879 Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Thu, 4 Jan 2024 18:16:25 +0800 Subject: [PATCH 02/32] fixup --- gnovm/pkg/gnolang/op_call.go | 9 +-- gnovm/pkg/gnolang/op_expressions.go | 10 --- gnovm/pkg/gnolang/preprocess.go | 94 ----------------------------- gnovm/pkg/gnolang/transcribe.go | 94 ++++++++++++++++++++++++++--- gnovm/tests/files/debug/1c.gno | 18 ++++++ gnovm/tests/files/debug/1d.gno | 29 +++++++++ gnovm/tests/files/debug/2.gno | 26 ++++++++ 7 files changed, 162 insertions(+), 118 deletions(-) create mode 100644 gnovm/tests/files/debug/1c.gno create mode 100644 gnovm/tests/files/debug/1d.gno create mode 100644 gnovm/tests/files/debug/2.gno diff --git a/gnovm/pkg/gnolang/op_call.go b/gnovm/pkg/gnolang/op_call.go index ef4fc7ab0c8..28a65f28a31 100644 --- a/gnovm/pkg/gnolang/op_call.go +++ b/gnovm/pkg/gnolang/op_call.go @@ -63,13 +63,8 @@ func (m *Machine) doOpCall() { isMethod := 0 // 1 if true // Create new block scope. clo := fr.Func.GetClosure(m.Store) - //debug.Printf("-----fv: %v ----- \n", fv) - //debug.Printf("-----ft: %v ----- \n", ft) debug.Printf("-----got closure: %v ----- \n", clo) - //debug.Printf("-----func source: %v ----- \n", fr.Func.GetClosure(m.Store)) - debug.Println("----------") - //debugPP.Printf("-----new block for closure: %v ----- \n", b) - // TODO: update b here using captured vars + // update block vars using captured vars captures := fr.Func.Captures if captures != nil { debug.Printf("captures before call: %v, len(names): %d, len(values): %d \n", *captures, len(captures.names), len(captures.values)) @@ -88,6 +83,8 @@ func (m *Machine) doOpCall() { } } } + // only need initial snapshot + // TODO: better way to zero it fr.Func.Captures = nil b := m.Alloc.NewBlock(fr.Func.GetSource(m.Store), clo) diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index 7c2d14d7b0b..5e513541a01 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -714,16 +714,6 @@ func (m *Machine) doOpFuncLit() { } else { debug.Println("undefined, skip") } - - //var index int - //for i, n := range names { - // if name == n { // match and replace - // index = i - // debug.Printf("index of %s is: %d \n", name, index) - // // replace lv values with index - // nb.UpdateValue(index, v) - // } - //} } debug.Printf("---captured: %v \n", captured) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 9713ed7cbfc..097c1d9ac52 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -8,100 +8,6 @@ import ( "github.com/gnolang/gno/tm2/pkg/errors" ) -var closures []*Closure -var fxs []*FuncLitExpr - -func init() { - closures = make([]*Closure, 0) - fxs = make([]*FuncLitExpr, 0) -} - -func pushClosure(c *Closure) { - closures = append(closures, c) -} - -func popClosure() { - debug.Println("-c") - if len(closures) != 0 { - closures = closures[:len(closures)-1] - } -} - -func pushFxs(fx *FuncLitExpr) { - fxs = append(fxs, fx) -} - -func popFx() { - debug.Println("-fx") - if len(fxs) != 0 { - fxs = fxs[:len(fxs)-1] - } -} - -func dumpFxs() { - println("============Dump fxs===========") - println("len: ", len(fxs)) - for i, fx := range fxs { - fmt.Printf("fx[%d]: %v\n", i, fx) - } - println("============end===============") - println("\n") -} -func dumpClosures() { - println("============Dump closures=======") - println("len: ", len(closures)) - for _, c := range closures { - for i, name := range c.names { - fmt.Printf("name[%s]: %v\n", i, name) - } - for i, nx := range c.nxs { - fmt.Printf("nx[%s]: %v\n", i, nx) - } - //for i, fx := range c.Fxs { - // fmt.Printf("fx[%d]: %v\n", i, fx) - //} - } - println("============end===============") - println("\n") -} - -func currentClosure() *Closure { - if len(closures) == 0 { - return nil - } - return closures[len(closures)-1] -} - -func currentFx() *FuncLitExpr { - if len(fxs) == 0 { - return nil - } - return fxs[len(fxs)-1] -} - -type Closure struct { - //nxs map[Name]*NameExpr - names []Name - nxs []*NameExpr - //Fxs map[string]FuncLitExpr -} - -func NewClosure() *Closure { - return &Closure{} -} - -func (clo *Closure) Push(nx *NameExpr, fx *FuncLitExpr) { - debug.Printf("+nx: %v \n", nx) - debug.Printf("+fx: %v \n", fx) - clo.names = append(clo.names, nx.Name) - clo.nxs = append(clo.nxs, nx) -} - -//func (clo *Closure) Pop() { -// clo.nxs = clo.nxs[:len(clo.nxs)-1] -// clo.Fxs = clo.Fxs[:len(clo.Fxs)-1] -//} - // In the case of a *FileSet, some declaration steps have to happen // in a restricted parallel way across all the files. // Anything predefined or preprocessed here get skipped during the Preprocess diff --git a/gnovm/pkg/gnolang/transcribe.go b/gnovm/pkg/gnolang/transcribe.go index 24d919ba6d5..843f67ff551 100644 --- a/gnovm/pkg/gnolang/transcribe.go +++ b/gnovm/pkg/gnolang/transcribe.go @@ -106,6 +106,89 @@ const ( TRANS_FILE_BODY ) +var closures []*Closure +var fxs []*FuncLitExpr + +func pushClosure(c *Closure) { + closures = append(closures, c) +} + +func popClosure() { + debug.Println("-c") + if len(closures) != 0 { + closures = closures[:len(closures)-1] + } +} + +func pushFxs(fx *FuncLitExpr) { + fxs = append(fxs, fx) +} + +func popFx() { + debug.Println("-fx") + if len(fxs) != 0 { + fxs = fxs[:len(fxs)-1] + } +} + +func dumpFxs() { + println("============Dump fxs===========") + println("len: ", len(fxs)) + for i, fx := range fxs { + fmt.Printf("fx[%d]: %v\n", i, fx) + } + println("============end===============") + println("\n") +} +func dumpClosures() { + println("============Dump closures=======") + println("len: ", len(closures)) + for _, c := range closures { + for i, name := range c.names { + fmt.Printf("name[%s]: %v\n", i, name) + } + for i, nx := range c.nxs { + fmt.Printf("nx[%s]: %v\n", i, nx) + } + } + println("============end===============") + println("\n") +} + +func currentClosure() *Closure { + if len(closures) == 0 { + return nil + } + return closures[len(closures)-1] +} + +func currentFx() *FuncLitExpr { + if len(fxs) == 0 { + return nil + } + return fxs[len(fxs)-1] +} + +type Closure struct { + names []Name + nxs []*NameExpr +} + +//func NewClosure() *Closure { +// return &Closure{} +//} + +func (clo *Closure) Fill(nx *NameExpr) { + debug.Printf("+nx: %v \n", nx) + clo.names = append(clo.names, nx.Name) + clo.nxs = append(clo.nxs, nx) +} + +//func (clo *Closure) Pop() { +// clo.nxs = clo.nxs[:len(clo.nxs)-1] +// clo.Fxs = clo.Fxs[:len(clo.Fxs)-1] +//} + // return: // - TRANS_CONTINUE to visit children recursively; // - TRANS_SKIP to break out of the @@ -149,19 +232,14 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc switch cnn := nn.(type) { case *NameExpr: debug.Printf("-----trans, nameExpr: %v \n", cnn) - //dumpClosures() - //dumpFxs() - // TODO: how to filter out unrelated namedExpr - // set current closure // TODO: do we need to filter out already define in current block currentClo := currentClosure() debug.Printf("currentClo: %v \n", currentClo) - if currentClo != nil { - currentClo.Push(cnn, currentFx()) + if currentClo != nil { // a closure to fill + currentClo.Fill(cnn) currentFx := currentFx() currentFx.Closure = *currentClo - dumpClosures() dumpFxs() } @@ -295,7 +373,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc debug.Println("---start trans funcLit body stmt") debug.Println("push target closure and fx") - pushClosure(NewClosure()) + pushClosure(&Closure{}) pushFxs(cnn) for idx := range cnn.Body { cnn.Body[idx] = transcribe(t, nns, TRANS_FUNCLIT_BODY, idx, cnn.Body[idx], &c).(Stmt) diff --git a/gnovm/tests/files/debug/1c.gno b/gnovm/tests/files/debug/1c.gno new file mode 100644 index 00000000000..e5c61c59089 --- /dev/null +++ b/gnovm/tests/files/debug/1c.gno @@ -0,0 +1,18 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 2; i++ { + f := func() int { + return i + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/files/debug/1d.gno b/gnovm/tests/files/debug/1d.gno new file mode 100644 index 00000000000..185961c741f --- /dev/null +++ b/gnovm/tests/files/debug/1d.gno @@ -0,0 +1,29 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 5; i++ { + x := i + f := func() int { + x := 5 + return x + } + println(x) + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 +// 5 +// 5 +// 5 +// 5 +// 5 diff --git a/gnovm/tests/files/debug/2.gno b/gnovm/tests/files/debug/2.gno new file mode 100644 index 00000000000..1a788d9e0b2 --- /dev/null +++ b/gnovm/tests/files/debug/2.gno @@ -0,0 +1,26 @@ +package main + +import "fmt" + +func bar() func() func() int { + x := 1 + + // First level of closure, modifies x + return func() func() int { + //x++ + // Second level of closure, returns x + return func() int { + return x + } + } +} + +func main() { + f := bar() // f is the first-level closure + g := f() // g is the second-level closure, x is incremented here + + fmt.Println(g()) // prints the value of x after being modified by the first-level closure +} + +// Output: +// 1 From 26b454e38219fc2f3ae4856caba3e2f735bbfc41 Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Thu, 4 Jan 2024 18:20:47 +0800 Subject: [PATCH 03/32] simplify --- gnovm/pkg/gnolang/transcribe.go | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/gnovm/pkg/gnolang/transcribe.go b/gnovm/pkg/gnolang/transcribe.go index 843f67ff551..5bde68629e7 100644 --- a/gnovm/pkg/gnolang/transcribe.go +++ b/gnovm/pkg/gnolang/transcribe.go @@ -233,13 +233,16 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc case *NameExpr: debug.Printf("-----trans, nameExpr: %v \n", cnn) // TODO: do we need to filter out already define in current block - currentClo := currentClosure() - debug.Printf("currentClo: %v \n", currentClo) + //currentClo := currentClosure() + //debug.Printf("currentClo: %v \n", currentClo) + currentFx := currentFx() + debug.Printf("currentFx: %v \n", currentFx) - if currentClo != nil { // a closure to fill - currentClo.Fill(cnn) - currentFx := currentFx() - currentFx.Closure = *currentClo + if currentFx != nil { // a closure to fill + clo := &Closure{} + clo.Fill(cnn) + //currentFx := currentFx() + currentFx.Closure = *clo dumpClosures() dumpFxs() } @@ -373,7 +376,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc debug.Println("---start trans funcLit body stmt") debug.Println("push target closure and fx") - pushClosure(&Closure{}) + //pushClosure(&Closure{}) pushFxs(cnn) for idx := range cnn.Body { cnn.Body[idx] = transcribe(t, nns, TRANS_FUNCLIT_BODY, idx, cnn.Body[idx], &c).(Stmt) @@ -391,7 +394,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } // defer pop debug.Printf("---done trans body \n") - popClosure() + //popClosure() popFx() case *FieldTypeExpr: cnn.Type = transcribe(t, nns, TRANS_FIELDTYPE_TYPE, 0, cnn.Type, &c).(Expr) From 8dc512c92f186a2901ffdf422e8827be134b4ae4 Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Sat, 6 Jan 2024 21:17:31 +0800 Subject: [PATCH 04/32] fixup --- gnovm/pkg/gnolang/nodes.go | 5 + gnovm/pkg/gnolang/op_call.go | 4 +- gnovm/pkg/gnolang/op_eval.go | 30 ----- gnovm/pkg/gnolang/op_exec.go | 2 + gnovm/pkg/gnolang/op_expressions.go | 29 ++++- gnovm/pkg/gnolang/preprocess.go | 2 +- gnovm/pkg/gnolang/transcribe.go | 179 +++++++++++++++++++------- gnovm/pkg/gnolang/values.go | 2 +- gnovm/tests/file_test.go | 3 +- gnovm/tests/files/debug/3.gno | 25 ++++ gnovm/tests/files/debug/3a.gno | 23 ++++ gnovm/tests/files/debug/3b.gno | 27 ++++ gnovm/tests/files/debug/recover4.gno | 25 ++++ gnovm/tests/files/debug/recover6.gno | 30 +++++ gnovm/tests/files/debug3/1.gno | 22 ++++ gnovm/tests/files/debug3/1a.gno | 22 ++++ gnovm/tests/files/debug3/1b.gno | 22 ++++ gnovm/tests/files/debug3/1c.gno | 18 +++ gnovm/tests/files/debug3/1d.gno | 29 +++++ gnovm/tests/files/debug3/2.gno | 26 ++++ gnovm/tests/files/debug3/closure0.gno | 17 +++ gnovm/tests/files/debug3/closure1.gno | 20 +++ gnovm/tests/files/debug3/closure2.gno | 28 ++++ gnovm/tests/files/debug3/closure3.gno | 23 ++++ gnovm/tests/files/debug3/closure4.gno | 23 ++++ gnovm/tests/files/debug3/closure5.gno | 23 ++++ gnovm/tests/files/debug3/closure6.gno | 23 ++++ gnovm/tests/files/debug3/closure7.gno | 28 ++++ gnovm/tests/files/debug3/closure8.gno | 10 ++ 29 files changed, 631 insertions(+), 89 deletions(-) create mode 100644 gnovm/tests/files/debug/3.gno create mode 100644 gnovm/tests/files/debug/3a.gno create mode 100644 gnovm/tests/files/debug/3b.gno create mode 100644 gnovm/tests/files/debug/recover4.gno create mode 100644 gnovm/tests/files/debug/recover6.gno create mode 100644 gnovm/tests/files/debug3/1.gno create mode 100644 gnovm/tests/files/debug3/1a.gno create mode 100644 gnovm/tests/files/debug3/1b.gno create mode 100644 gnovm/tests/files/debug3/1c.gno create mode 100644 gnovm/tests/files/debug3/1d.gno create mode 100644 gnovm/tests/files/debug3/2.gno create mode 100644 gnovm/tests/files/debug3/closure0.gno create mode 100644 gnovm/tests/files/debug3/closure1.gno create mode 100644 gnovm/tests/files/debug3/closure2.gno create mode 100644 gnovm/tests/files/debug3/closure3.gno create mode 100644 gnovm/tests/files/debug3/closure4.gno create mode 100644 gnovm/tests/files/debug3/closure5.gno create mode 100644 gnovm/tests/files/debug3/closure6.gno create mode 100644 gnovm/tests/files/debug3/closure7.gno create mode 100644 gnovm/tests/files/debug3/closure8.gno diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index c38823395fb..070807eb333 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -499,6 +499,11 @@ type FuncLitExpr struct { Closure Closure } +func (fx *FuncLitExpr) SetClosure(c Closure) { + debug.Printf("+++++SetClosure, c: %v \n", c) + fx.Closure = c +} + //type ClosureObject struct { // StaticBlock //} diff --git a/gnovm/pkg/gnolang/op_call.go b/gnovm/pkg/gnolang/op_call.go index 28a65f28a31..96e10400c33 100644 --- a/gnovm/pkg/gnolang/op_call.go +++ b/gnovm/pkg/gnolang/op_call.go @@ -66,6 +66,9 @@ func (m *Machine) doOpCall() { debug.Printf("-----got closure: %v ----- \n", clo) // update block vars using captured vars captures := fr.Func.Captures + if captures == nil { + debug.Println("nil captures") + } if captures != nil { debug.Printf("captures before call: %v, len(names): %d, len(values): %d \n", *captures, len(captures.names), len(captures.values)) names := clo.GetSource(m.Store).GetBlockNames() @@ -84,7 +87,6 @@ func (m *Machine) doOpCall() { } } // only need initial snapshot - // TODO: better way to zero it fr.Func.Captures = nil b := m.Alloc.NewBlock(fr.Func.GetSource(m.Store), clo) diff --git a/gnovm/pkg/gnolang/op_eval.go b/gnovm/pkg/gnolang/op_eval.go index 167aef4c250..790b69807e4 100644 --- a/gnovm/pkg/gnolang/op_eval.go +++ b/gnovm/pkg/gnolang/op_eval.go @@ -309,36 +309,6 @@ func (m *Machine) doOpEval() { debug.Println("-----FuncLitExpr") b := m.LastBlock() debug.Printf("b: %v \n", b) - //debug.Printf("x: %v, body: %v \n", x, x.GetBody()) - // - //bk := x.GetBlock() - //bs := x.GetBodyStmt() - //names := x.GetBlockNames() - //by := x.GetBody() - // TODO: capture - // 1. span slice for funcLitExpr in preprocess - // 2. set value in eval funcLitExpr - // 3. add interceptor to nameExpr evaluation process, namely, use captured vars - - // block structure - // for block - // funcLitBlock - - //debug.Printf("body:%v\n", by) - //debug.Printf("bodyStmt:%v \n", bs) - //debug.Printf("block:%v \n", bk) - //debug.Printf("names:%v \n", names) - - //bn := m.Alloc.NewBlock(x, m.LastBlock()) - //b.bodyStmt = bodyStmt{ - // Body: x.Body, - // BodyLen: len(x.Body), - // NextBodyIndex: -2, - //} - //debug.Printf("bn: %v \n", bn) - //m.PushBlock(bn) - //m.PushOp(OpPopBlock) - m.PushOp(OpFuncLit) m.PushOp(OpPreFuncLit) // evaluate func type diff --git a/gnovm/pkg/gnolang/op_exec.go b/gnovm/pkg/gnolang/op_exec.go index 55c28dccd6a..28328236c43 100644 --- a/gnovm/pkg/gnolang/op_exec.go +++ b/gnovm/pkg/gnolang/op_exec.go @@ -67,6 +67,7 @@ func (m *Machine) doOpExec(op Op) { case OpBody: bs := m.LastBlock().GetBodyStmt() if bs.NextBodyIndex == -2 { // init + debug.Println("---OpBody, init") bs.NumOps = m.NumOps bs.NumValues = m.NumValues bs.NumExprs = len(m.Exprs) @@ -74,6 +75,7 @@ func (m *Machine) doOpExec(op Op) { bs.NextBodyIndex = 0 } if bs.NextBodyIndex < bs.BodyLen { + debug.Println("---OpBody, index < body len") next := bs.Body[bs.NextBodyIndex] bs.NextBodyIndex++ // continue onto exec stmt. diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index 5e513541a01..3705ac22898 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -684,15 +684,32 @@ type closureObject struct { func (m *Machine) doOpPreFuncLit() { debug.Println("-----doOpPreFuncLit") x := m.PeekExpr(1).(*FuncLitExpr) + debug.Printf("funcLitExpr: %v \n", x) + //debug.Printf("pointer of x.Closure: %p \n", x.Closure) + debug.Printf("x.Closure: %v \n", &(x.Closure)) lb := m.LastBlock() b := m.Alloc.NewBlock(x, lb) m.PushBlock(b) m.PushOp(OpPopBlock) closure := x.Closure - for i, nx := range closure.nxs { - debug.Printf("closure[%s]: %v \n", i, nx) - m.PushExpr(nx) + debug.Printf("closure nxs len is: %d \n", len(closure.cnxs)) + for i, cnx := range closure.cnxs { + debug.Printf("closure[%d]: %v \n", i, cnx) + offset := cnx.offset + debug.Printf("offset of %s is %d \n", cnx.nx.Name, offset) + vp := cnx.nx.Path + debug.Printf("vp of nx[%s] is : %v \n", cnx.nx.Name, vp) + nvp := ValuePath{ + Name: cnx.nx.Name, + Depth: vp.Depth - offset + 1, + Index: vp.Index, + Type: vp.Type, + } + debug.Printf("nvp of nx[%s] is : %v \n", nvp.Name, nvp) + nnx := *cnx.nx + nnx.Path = nvp + m.PushExpr(&nnx) m.PushOp(OpEval) } } @@ -704,12 +721,12 @@ func (m *Machine) doOpFuncLit() { debug.Printf("lb: %v \n", lb) captured := &Captured{} - for _, nx := range x.Closure.nxs { - debug.Printf("range closures of : %s \n", string(nx.Name)) + for _, cnx := range x.Closure.cnxs { + debug.Printf("range closures of : %s \n", string(cnx.nx.Name)) v := *m.PopValue() if !v.IsUndefined() { // e.g. args of func. forward debug.Printf("capturing v: %v \n", v) - captured.names = append(captured.names, nx.Name) + captured.names = append(captured.names, cnx.nx.Name) captured.values = append(captured.values, v) } else { debug.Println("undefined, skip") diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 097c1d9ac52..83c9bfd8f63 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -290,7 +290,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // TRANS_BLOCK ----------------------- case *IfCaseStmt: - debugPP.Printf("-----IfCaseStmt-----") + debugPP.Println("-----IfCaseStmt-----") pushRealBlock(n, &last, &stack) // parent if statement. ifs := ns[len(ns)-1].(*IfStmt) diff --git a/gnovm/pkg/gnolang/transcribe.go b/gnovm/pkg/gnolang/transcribe.go index 5bde68629e7..fdfbec6d52a 100644 --- a/gnovm/pkg/gnolang/transcribe.go +++ b/gnovm/pkg/gnolang/transcribe.go @@ -106,17 +106,51 @@ const ( TRANS_FILE_BODY ) -var closures []*Closure +var closures = &Closures{} + +type Closures struct { + cs []*Closure +} + var fxs []*FuncLitExpr func pushClosure(c *Closure) { - closures = append(closures, c) + debug.Println("+clo") + debug.Println("before push closure") + dumpClosures() + closures.cs = append(closures.cs, c) + debug.Println("end push closure") + dumpClosures() } -func popClosure() { - debug.Println("-c") - if len(closures) != 0 { - closures = closures[:len(closures)-1] +func popClosure() *Closure { + debug.Println("-clo") + debug.Println("before pop, dump") + dumpClosures() + if len(closures.cs) == 0 { + return nil + } else { + c := closures.cs[len(closures.cs)-1] // get current + for _, cnx := range c.cnxs { // pop-> increase + debug.Printf("+1 \n") + cnx.offset += 1 + } + + closures.cs = closures.cs[:len(closures.cs)-1] // shrink + // copy poped to latest, if left at least one closure + currentClo := currentClosure() + debug.Printf("currentClo: %v \n", currentClo) + if currentClo != nil { // if last closure, just pop, no copy + // fill up current closure + for _, cnx := range c.cnxs { // trace back captured nxs + currentClo.Fill(*cnx) + } + } + debug.Println("after pop, dump") + dumpClosures() + + debug.Printf("c poped: %v, \n", c) + return c } } @@ -132,34 +166,34 @@ func popFx() { } func dumpFxs() { - println("============Dump fxs===========") - println("len: ", len(fxs)) - for i, fx := range fxs { - fmt.Printf("fx[%d]: %v\n", i, fx) + if debug { + println("============Dump fxs===========") + println("len: ", len(fxs)) + for i, fx := range fxs { + fmt.Printf("fx[%d]: %v\n", i, fx) + } + println("============end===============") + println("\n") } - println("============end===============") - println("\n") } func dumpClosures() { - println("============Dump closures=======") - println("len: ", len(closures)) - for _, c := range closures { - for i, name := range c.names { - fmt.Printf("name[%s]: %v\n", i, name) - } - for i, nx := range c.nxs { - fmt.Printf("nx[%s]: %v\n", i, nx) - } + if debug { + println("============Dump closures start=======") + debug.Printf("depth of closures: %d \n", len(closures.cs)) + for _, c := range closures.cs { + fmt.Printf("===>: %v \n", c) + } + println("============Dump closures end===============") + println("\n") } - println("============end===============") - println("\n") } func currentClosure() *Closure { - if len(closures) == 0 { + debug.Printf("currentClosure, len: %d \n", len(closures.cs)) + if len(closures.cs) == 0 { return nil } - return closures[len(closures)-1] + return closures.cs[len(closures.cs)-1] } func currentFx() *FuncLitExpr { @@ -169,26 +203,50 @@ func currentFx() *FuncLitExpr { return fxs[len(fxs)-1] } +type CapturedNx struct { + nx *NameExpr + offset uint8 // every captured nx has an offset, represents its distance to the funcLitExpr +} + +func (cnx *CapturedNx) String() string { + return fmt.Sprintf("nx is: %v, offset is: %d \n", cnx.nx, cnx.offset) +} + +// captured NameExpr type Closure struct { names []Name - nxs []*NameExpr + cnxs []*CapturedNx +} + +func (c *Closure) String() string { + var s string + s += "\n===========closure start============\n" + for i, n := range c.names { + s += fmt.Sprintf("names[%d] is: %s \n", i, string(n)) + } + for i, c := range c.cnxs { + s += fmt.Sprintf("cnxs[%d] is : [nx:%v, offset:%d] \n", i, c.nx, c.offset) + } + s += "===========closure end=============\n" + return s } //func NewClosure() *Closure { // return &Closure{} //} -func (clo *Closure) Fill(nx *NameExpr) { - debug.Printf("+nx: %v \n", nx) - clo.names = append(clo.names, nx.Name) - clo.nxs = append(clo.nxs, nx) +func (clo *Closure) Fill(cnx CapturedNx) { + debug.Printf("+nx: %v \n", cnx.nx) + for _, n := range clo.names { // filter out existed nx + if cnx.nx.Name == n { + debug.Println("exist, return") + return + } + } + clo.names = append(clo.names, cnx.nx.Name) + clo.cnxs = append(clo.cnxs, &cnx) } -//func (clo *Closure) Pop() { -// clo.nxs = clo.nxs[:len(clo.nxs)-1] -// clo.Fxs = clo.Fxs[:len(clo.Fxs)-1] -//} - // return: // - TRANS_CONTINUE to visit children recursively; // - TRANS_SKIP to break out of the @@ -233,16 +291,22 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc case *NameExpr: debug.Printf("-----trans, nameExpr: %v \n", cnn) // TODO: do we need to filter out already define in current block - //currentClo := currentClosure() - //debug.Printf("currentClo: %v \n", currentClo) - currentFx := currentFx() - debug.Printf("currentFx: %v \n", currentFx) - - if currentFx != nil { // a closure to fill - clo := &Closure{} - clo.Fill(cnn) - //currentFx := currentFx() - currentFx.Closure = *clo + + currentClo := currentClosure() + debug.Printf("currentClo: %v \n", currentClo) + + if currentClo != nil { // a closure to fill + if cnn.Path.Depth > 1 { // if local defined, no capture + cnx := CapturedNx{ + nx: cnn, + offset: 0, + } + currentClo.Fill(cnx) + } + //currentFx.Closure = clo + //for _, cnx := range currentClo.cnxs { + // cnx.offset = 0 // reset every cnx offset + //} dumpClosures() dumpFxs() } @@ -374,10 +438,10 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc cnn = cnn2.(*FuncLitExpr) } - debug.Println("---start trans funcLit body stmt") - debug.Println("push target closure and fx") - //pushClosure(&Closure{}) + debug.Println("---start trans funcLit body stmt, push initial closure and fx") + pushClosure(&Closure{}) pushFxs(cnn) + for idx := range cnn.Body { cnn.Body[idx] = transcribe(t, nns, TRANS_FUNCLIT_BODY, idx, cnn.Body[idx], &c).(Stmt) @@ -394,7 +458,14 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } // defer pop debug.Printf("---done trans body \n") - //popClosure() + // TODO: set fx.Closure, and level as well + debug.Println("funcLit pop c-----") + pc := popClosure() + //cnn.Closure = c + closure := *pc + cnn.SetClosure(closure) + debug.Printf("---done FuncLit trans, fx: %v, closure: %+v \n", cnn, cnn.Closure.String()) + popFx() case *FieldTypeExpr: cnn.Type = transcribe(t, nns, TRANS_FIELDTYPE_TYPE, 0, cnn.Type, &c).(Expr) @@ -569,6 +640,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } case *IfStmt: + debug.Println("-----trans, if stmt") // NOTE: like switch stmts, both if statements AND // contained cases visit with the TRANS_BLOCK stage, even // though during runtime only one block is created. @@ -579,6 +651,8 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*IfStmt) } + + pushClosure(&Closure{}) if cnn.Init != nil { cnn.Init = transcribe(t, nns, TRANS_IF_INIT, 0, cnn.Init, &c).(SimpleStmt) if isStopOrSkip(nc, c) { @@ -589,6 +663,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc if isStopOrSkip(nc, c) { return } + cnn.Then = *transcribe(t, nns, TRANS_IF_BODY, 0, &cnn.Then, &c).(*IfCaseStmt) if isStopOrSkip(nc, c) { return @@ -597,7 +672,9 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc if isStopOrSkip(nc, c) { return } + popClosure() case *IfCaseStmt: + debug.Printf("-----trans, (if---case) stmt: %v \n", cnn) cnn2, c2 := t(ns, ftype, index, cnn, TRANS_BLOCK) if isStopOrSkip(nc, c2) { nn = cnn2 @@ -605,6 +682,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*IfCaseStmt) } + //pushClosure(&Closure{}) for idx := range cnn.Body { cnn.Body[idx] = transcribe(t, nns, TRANS_IF_CASE_BODY, idx, cnn.Body[idx], &c).(Stmt) if isBreak(c) { @@ -613,6 +691,9 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } } + //debug.Println("if-case pop c-----") + + //popClosure() case *IncDecStmt: cnn.X = transcribe(t, nns, TRANS_INCDEC_X, 0, cnn.X, &c).(Expr) if isStopOrSkip(nc, c) { diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index ede203f0b1f..121280287d5 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -2333,7 +2333,7 @@ func (b *Block) GetParent(store Store) *Block { } func (b *Block) GetPointerToInt(store Store, index int) PointerValue { - debug.Printf("GetPointerToInt, index: %d \n", index) + debug.Printf("-----GetPointerToInt, index: %d \n", index) debug.Printf("b: %v \n", b) debug.Printf("len of values: %v \n", len(b.Values)) vv := fillValueTV(store, &b.Values[index]) diff --git a/gnovm/tests/file_test.go b/gnovm/tests/file_test.go index 091b1f8ffd0..04b5f14aec1 100644 --- a/gnovm/tests/file_test.go +++ b/gnovm/tests/file_test.go @@ -34,7 +34,8 @@ func TestFiles(t *testing.T) { func TestDebug(t *testing.T) { baseDir := filepath.Join(".", "files/debug") - runFileTests(t, baseDir, nil) + //runFileTests(t, baseDir, []string{"*_native*"}) + runFileTests(t, baseDir, []string{"*_stdlibs*"}, WithNativeLibs()) } func TestChallenges(t *testing.T) { diff --git a/gnovm/tests/files/debug/3.gno b/gnovm/tests/files/debug/3.gno new file mode 100644 index 00000000000..1ea6e013b0d --- /dev/null +++ b/gnovm/tests/files/debug/3.gno @@ -0,0 +1,25 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + f := func() int { + if true { + if true { + return x + } + } + return 0 + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/files/debug/3a.gno b/gnovm/tests/files/debug/3a.gno new file mode 100644 index 00000000000..2ce3df96097 --- /dev/null +++ b/gnovm/tests/files/debug/3a.gno @@ -0,0 +1,23 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + f := func() int { + if true { + return x + } + return 0 + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/files/debug/3b.gno b/gnovm/tests/files/debug/3b.gno new file mode 100644 index 00000000000..427c6f075f9 --- /dev/null +++ b/gnovm/tests/files/debug/3b.gno @@ -0,0 +1,27 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + y := 0 + f := func() int { + if true { + x += y + if true { + return x + } + } + return 0 + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/files/debug/recover4.gno b/gnovm/tests/files/debug/recover4.gno new file mode 100644 index 00000000000..5a6da4261a2 --- /dev/null +++ b/gnovm/tests/files/debug/recover4.gno @@ -0,0 +1,25 @@ +package main + +import "fmt" + +func div(a, b int) (result int) { + defer func() { + r := recover() + + fmt.Printf("r = %#v\n", r) + + if r != nil { + result = 0 + } + }() + + return a / b +} + +func main() { + println(div(30, 2)) +} + +// Output: +// r = +// 15 diff --git a/gnovm/tests/files/debug/recover6.gno b/gnovm/tests/files/debug/recover6.gno new file mode 100644 index 00000000000..0b304369764 --- /dev/null +++ b/gnovm/tests/files/debug/recover6.gno @@ -0,0 +1,30 @@ +package main + +import ( + "errors" +) + +func main() { + println(f(false)) + println(f(true)) +} + +func f(dopanic bool) (err error) { + defer func() { + if x := recover(); x != nil { + err = x.(error) + } + }() + q(dopanic) + return +} + +func q(dopanic bool) { + if dopanic { + panic(errors.New("wtf")) + } +} + +// Output: +// undefined +// wtf diff --git a/gnovm/tests/files/debug3/1.gno b/gnovm/tests/files/debug3/1.gno new file mode 100644 index 00000000000..be4631b10dc --- /dev/null +++ b/gnovm/tests/files/debug3/1.gno @@ -0,0 +1,22 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 5; i++ { + x := i + f := func() int { + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 4 +// 4 +// 4 +// 4 +// 4 diff --git a/gnovm/tests/files/debug3/1a.gno b/gnovm/tests/files/debug3/1a.gno new file mode 100644 index 00000000000..105f8c8e6b3 --- /dev/null +++ b/gnovm/tests/files/debug3/1a.gno @@ -0,0 +1,22 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 2; i++ { + x := i + y := 0 + f := func() int { + x += y + //x += 1 + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 1 +// 1 diff --git a/gnovm/tests/files/debug3/1b.gno b/gnovm/tests/files/debug3/1b.gno new file mode 100644 index 00000000000..06ff7975701 --- /dev/null +++ b/gnovm/tests/files/debug3/1b.gno @@ -0,0 +1,22 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 2; i++ { + x := i + y := 0 + f := func() int { + x += y + x += 1 + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 2 +// 3 diff --git a/gnovm/tests/files/debug3/1c.gno b/gnovm/tests/files/debug3/1c.gno new file mode 100644 index 00000000000..65c71cd14fb --- /dev/null +++ b/gnovm/tests/files/debug3/1c.gno @@ -0,0 +1,18 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 2; i++ { + f := func() int { + return i + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 2 +// 2 diff --git a/gnovm/tests/files/debug3/1d.gno b/gnovm/tests/files/debug3/1d.gno new file mode 100644 index 00000000000..185961c741f --- /dev/null +++ b/gnovm/tests/files/debug3/1d.gno @@ -0,0 +1,29 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 5; i++ { + x := i + f := func() int { + x := 5 + return x + } + println(x) + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 +// 5 +// 5 +// 5 +// 5 +// 5 diff --git a/gnovm/tests/files/debug3/2.gno b/gnovm/tests/files/debug3/2.gno new file mode 100644 index 00000000000..1a788d9e0b2 --- /dev/null +++ b/gnovm/tests/files/debug3/2.gno @@ -0,0 +1,26 @@ +package main + +import "fmt" + +func bar() func() func() int { + x := 1 + + // First level of closure, modifies x + return func() func() int { + //x++ + // Second level of closure, returns x + return func() int { + return x + } + } +} + +func main() { + f := bar() // f is the first-level closure + g := f() // g is the second-level closure, x is incremented here + + fmt.Println(g()) // prints the value of x after being modified by the first-level closure +} + +// Output: +// 1 diff --git a/gnovm/tests/files/debug3/closure0.gno b/gnovm/tests/files/debug3/closure0.gno new file mode 100644 index 00000000000..acc1abfd404 --- /dev/null +++ b/gnovm/tests/files/debug3/closure0.gno @@ -0,0 +1,17 @@ +package main + +type adder func(int, int) int + +func genAdd(k int) adder { + return func(i, j int) int { + return i + j + k + } +} + +func main() { + f := genAdd(5) + println(f(3, 4)) +} + +// Output: +// 12 diff --git a/gnovm/tests/files/debug3/closure1.gno b/gnovm/tests/files/debug3/closure1.gno new file mode 100644 index 00000000000..f0bbda7ca1e --- /dev/null +++ b/gnovm/tests/files/debug3/closure1.gno @@ -0,0 +1,20 @@ +package main + +type adder func(int, int) int + +func genAdd(k int) adder { + return func(i, j int) int { + return i + j + k + } +} + +func main() { + f := genAdd(5) + g := genAdd(8) + println(f(3, 4)) + println(g(3, 4)) +} + +// Output: +// 12 +// 15 diff --git a/gnovm/tests/files/debug3/closure2.gno b/gnovm/tests/files/debug3/closure2.gno new file mode 100644 index 00000000000..e43496b9f43 --- /dev/null +++ b/gnovm/tests/files/debug3/closure2.gno @@ -0,0 +1,28 @@ +package main + +func adder() func(int) int { + sum := 0 + return func(x int) int { + sum = sum + x + return sum + } +} + +func main() { + pos, neg := adder(), adder() + for i := 0; i < 10; i++ { + println(pos(i), neg(-2*i)) + } +} + +// Output: +// 0 0 +// 1 -2 +// 3 -6 +// 6 -12 +// 10 -20 +// 15 -30 +// 21 -42 +// 28 -56 +// 36 -72 +// 45 -90 diff --git a/gnovm/tests/files/debug3/closure3.gno b/gnovm/tests/files/debug3/closure3.gno new file mode 100644 index 00000000000..5e365a67cb4 --- /dev/null +++ b/gnovm/tests/files/debug3/closure3.gno @@ -0,0 +1,23 @@ +package main + +type T1 struct { + Name string +} + +func (t *T1) genAdd(k int) func(int) int { + return func(i int) int { + println(t.Name) + return i + k + } +} + +var t = &T1{"test"} + +func main() { + f := t.genAdd(4) + println(f(5)) +} + +// Output: +// test +// 9 diff --git a/gnovm/tests/files/debug3/closure4.gno b/gnovm/tests/files/debug3/closure4.gno new file mode 100644 index 00000000000..61c0a77f1ba --- /dev/null +++ b/gnovm/tests/files/debug3/closure4.gno @@ -0,0 +1,23 @@ +package main + +type T1 struct { + Name string +} + +func (t *T1) genAdd(k int) func(int) int { + return func(i int) int { + println(t.Name) + return i + k + } +} + +var t = T1{"test"} + +func main() { + f := t.genAdd(4) + println(f(5)) +} + +// Output: +// test +// 9 diff --git a/gnovm/tests/files/debug3/closure5.gno b/gnovm/tests/files/debug3/closure5.gno new file mode 100644 index 00000000000..e6d3c223607 --- /dev/null +++ b/gnovm/tests/files/debug3/closure5.gno @@ -0,0 +1,23 @@ +package main + +type T1 struct { + Name string +} + +func (t T1) genAdd(k int) func(int) int { + return func(i int) int { + println(t.Name) + return i + k + } +} + +var t = T1{"test"} + +func main() { + f := t.genAdd(4) + println(f(5)) +} + +// Output: +// test +// 9 diff --git a/gnovm/tests/files/debug3/closure6.gno b/gnovm/tests/files/debug3/closure6.gno new file mode 100644 index 00000000000..5e365a67cb4 --- /dev/null +++ b/gnovm/tests/files/debug3/closure6.gno @@ -0,0 +1,23 @@ +package main + +type T1 struct { + Name string +} + +func (t *T1) genAdd(k int) func(int) int { + return func(i int) int { + println(t.Name) + return i + k + } +} + +var t = &T1{"test"} + +func main() { + f := t.genAdd(4) + println(f(5)) +} + +// Output: +// test +// 9 diff --git a/gnovm/tests/files/debug3/closure7.gno b/gnovm/tests/files/debug3/closure7.gno new file mode 100644 index 00000000000..d4c0c96b0d5 --- /dev/null +++ b/gnovm/tests/files/debug3/closure7.gno @@ -0,0 +1,28 @@ +package main + +import ( + "fmt" +) + +type Config struct { + A string +} + +var conf *Config = &Config{} + +func SetConfig() func(*Config) { + return func(cf *Config) { + conf = cf + } +} + +func main() { + conf := &Config{ + A: "foo", + } + + fmt.Println(conf.A) +} + +// Output: +// foo diff --git a/gnovm/tests/files/debug3/closure8.gno b/gnovm/tests/files/debug3/closure8.gno new file mode 100644 index 00000000000..6ef1b43fca6 --- /dev/null +++ b/gnovm/tests/files/debug3/closure8.gno @@ -0,0 +1,10 @@ +package main + +var f = func(a int) int { return 2 + a } + +func main() { + println(f(3)) +} + +// Output: +// 5 From 0fd3de32b4efa59683b9095b058330d4f2b210ab Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Mon, 8 Jan 2024 16:23:19 +0800 Subject: [PATCH 05/32] fixup --- gnovm/pkg/gnolang/nodes.go | 4 +- gnovm/pkg/gnolang/op_expressions.go | 58 +++--- gnovm/pkg/gnolang/preprocess.go | 22 +- gnovm/pkg/gnolang/transcribe.go | 197 +++++++++++------- gnovm/tests/{files => }/debug/1.gno | 0 gnovm/tests/{files => }/debug/1a.gno | 0 gnovm/tests/{files => }/debug/1b.gno | 0 gnovm/tests/{files => }/debug/1c.gno | 0 gnovm/tests/{files => }/debug/1d.gno | 0 gnovm/tests/{files => }/debug/2.gno | 0 .../{files/debug3/2.gno => debug/2a.gno} | 4 +- gnovm/tests/{files => }/debug/3.gno | 0 gnovm/tests/{files => }/debug/3a.gno | 0 gnovm/tests/{files => }/debug/3b.gno | 0 .../{files/debug3/1c.gno => debug/4.gno} | 10 +- .../{files/debug3/1a.gno => debug/4a.gno} | 9 +- .../{files/debug3/1b.gno => debug/4b.gno} | 10 +- .../{files/debug3/1.gno => debug/4c.gno} | 15 +- gnovm/tests/{files => }/debug/closure0.gno | 0 gnovm/tests/{files => }/debug/closure1.gno | 0 gnovm/tests/{files => }/debug/closure2.gno | 0 gnovm/tests/{files => }/debug/closure3.gno | 0 gnovm/tests/{files => }/debug/closure4.gno | 0 gnovm/tests/{files => }/debug/closure5.gno | 0 gnovm/tests/{files => }/debug/closure6.gno | 0 gnovm/tests/{files => }/debug/closure7.gno | 0 gnovm/tests/{files => }/debug/closure8.gno | 0 gnovm/tests/{files => }/debug/recover4.gno | 0 gnovm/tests/{files => }/debug/recover6.gno | 0 gnovm/tests/file_test.go | 2 +- gnovm/tests/files/debug3/1d.gno | 29 --- gnovm/tests/files/debug3/closure0.gno | 17 -- gnovm/tests/files/debug3/closure1.gno | 20 -- gnovm/tests/files/debug3/closure2.gno | 28 --- gnovm/tests/files/debug3/closure3.gno | 23 -- gnovm/tests/files/debug3/closure4.gno | 23 -- gnovm/tests/files/debug3/closure5.gno | 23 -- gnovm/tests/files/debug3/closure6.gno | 23 -- gnovm/tests/files/debug3/closure7.gno | 28 --- gnovm/tests/files/debug3/closure8.gno | 10 - 40 files changed, 200 insertions(+), 355 deletions(-) rename gnovm/tests/{files => }/debug/1.gno (100%) rename gnovm/tests/{files => }/debug/1a.gno (100%) rename gnovm/tests/{files => }/debug/1b.gno (100%) rename gnovm/tests/{files => }/debug/1c.gno (100%) rename gnovm/tests/{files => }/debug/1d.gno (100%) rename gnovm/tests/{files => }/debug/2.gno (100%) rename gnovm/tests/{files/debug3/2.gno => debug/2a.gno} (97%) rename gnovm/tests/{files => }/debug/3.gno (100%) rename gnovm/tests/{files => }/debug/3a.gno (100%) rename gnovm/tests/{files => }/debug/3b.gno (100%) rename gnovm/tests/{files/debug3/1c.gno => debug/4.gno} (81%) rename gnovm/tests/{files/debug3/1a.gno => debug/4a.gno} (83%) rename gnovm/tests/{files/debug3/1b.gno => debug/4b.gno} (77%) rename gnovm/tests/{files/debug3/1.gno => debug/4c.gno} (64%) rename gnovm/tests/{files => }/debug/closure0.gno (100%) rename gnovm/tests/{files => }/debug/closure1.gno (100%) rename gnovm/tests/{files => }/debug/closure2.gno (100%) rename gnovm/tests/{files => }/debug/closure3.gno (100%) rename gnovm/tests/{files => }/debug/closure4.gno (100%) rename gnovm/tests/{files => }/debug/closure5.gno (100%) rename gnovm/tests/{files => }/debug/closure6.gno (100%) rename gnovm/tests/{files => }/debug/closure7.gno (100%) rename gnovm/tests/{files => }/debug/closure8.gno (100%) rename gnovm/tests/{files => }/debug/recover4.gno (100%) rename gnovm/tests/{files => }/debug/recover6.gno (100%) delete mode 100644 gnovm/tests/files/debug3/1d.gno delete mode 100644 gnovm/tests/files/debug3/closure0.gno delete mode 100644 gnovm/tests/files/debug3/closure1.gno delete mode 100644 gnovm/tests/files/debug3/closure2.gno delete mode 100644 gnovm/tests/files/debug3/closure3.gno delete mode 100644 gnovm/tests/files/debug3/closure4.gno delete mode 100644 gnovm/tests/files/debug3/closure5.gno delete mode 100644 gnovm/tests/files/debug3/closure6.gno delete mode 100644 gnovm/tests/files/debug3/closure7.gno delete mode 100644 gnovm/tests/files/debug3/closure8.gno diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index 070807eb333..9560d1be248 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -496,10 +496,10 @@ type FuncLitExpr struct { StaticBlock Type FuncTypeExpr // function type Body // function body - Closure Closure + Closure *Closure } -func (fx *FuncLitExpr) SetClosure(c Closure) { +func (fx *FuncLitExpr) SetClosure(c *Closure) { debug.Printf("+++++SetClosure, c: %v \n", c) fx.Closure = c } diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index 3705ac22898..28e047ba128 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -686,31 +686,33 @@ func (m *Machine) doOpPreFuncLit() { x := m.PeekExpr(1).(*FuncLitExpr) debug.Printf("funcLitExpr: %v \n", x) //debug.Printf("pointer of x.Closure: %p \n", x.Closure) - debug.Printf("x.Closure: %v \n", &(x.Closure)) + debug.Printf("x.Closure: %v \n", x.Closure) lb := m.LastBlock() b := m.Alloc.NewBlock(x, lb) m.PushBlock(b) m.PushOp(OpPopBlock) closure := x.Closure - debug.Printf("closure nxs len is: %d \n", len(closure.cnxs)) - for i, cnx := range closure.cnxs { - debug.Printf("closure[%d]: %v \n", i, cnx) - offset := cnx.offset - debug.Printf("offset of %s is %d \n", cnx.nx.Name, offset) - vp := cnx.nx.Path - debug.Printf("vp of nx[%s] is : %v \n", cnx.nx.Name, vp) - nvp := ValuePath{ - Name: cnx.nx.Name, - Depth: vp.Depth - offset + 1, - Index: vp.Index, - Type: vp.Type, + if closure != nil { + debug.Printf("closure nxs len is: %d \n", len(closure.cnxs)) + for i, cnx := range closure.cnxs { + debug.Printf("closure[%d]: %v \n", i, cnx) + offset := cnx.offset + debug.Printf("offset of %s is %d \n", cnx.nx.Name, offset) + vp := cnx.nx.Path + debug.Printf("vp of nx[%s] is : %v \n", cnx.nx.Name, vp) + nvp := ValuePath{ + Name: cnx.nx.Name, + Depth: vp.Depth - offset + 1, + Index: vp.Index, + Type: vp.Type, + } + debug.Printf("nvp of nx[%s] is : %v \n", nvp.Name, nvp) + nnx := *cnx.nx + nnx.Path = nvp + m.PushExpr(&nnx) + m.PushOp(OpEval) } - debug.Printf("nvp of nx[%s] is : %v \n", nvp.Name, nvp) - nnx := *cnx.nx - nnx.Path = nvp - m.PushExpr(&nnx) - m.PushOp(OpEval) } } @@ -721,15 +723,17 @@ func (m *Machine) doOpFuncLit() { debug.Printf("lb: %v \n", lb) captured := &Captured{} - for _, cnx := range x.Closure.cnxs { - debug.Printf("range closures of : %s \n", string(cnx.nx.Name)) - v := *m.PopValue() - if !v.IsUndefined() { // e.g. args of func. forward - debug.Printf("capturing v: %v \n", v) - captured.names = append(captured.names, cnx.nx.Name) - captured.values = append(captured.values, v) - } else { - debug.Println("undefined, skip") + if x.Closure != nil { + for _, cnx := range x.Closure.cnxs { + debug.Printf("range closures of : %s \n", string(cnx.nx.Name)) + v := *m.PopValue() + if !v.IsUndefined() { // e.g. args of func. forward + debug.Printf("capturing v: %v \n", v) + captured.names = append(captured.names, cnx.nx.Name) + captured.values = append(captured.values, v) + } else { + debug.Println("undefined, skip") + } } } debug.Printf("---captured: %v \n", captured) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 83c9bfd8f63..641263b735a 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -274,10 +274,14 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // TRANS_BLOCK ----------------------- case *BlockStmt: pushInitBlock(n, &last, &stack) + debug.Println("blockStmt push closure") + //pushClosure(&Closure{}) + //debug.Println("blockStmt pop closure") + //popClosure() // TRANS_BLOCK ----------------------- case *ForStmt: - debugPP.Println("-----ForStmt-----") + debug.Println("-----ForStmt-----") pushInitBlock(n, &last, &stack) // TRANS_BLOCK ----------------------- @@ -292,6 +296,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { case *IfCaseStmt: debugPP.Println("-----IfCaseStmt-----") pushRealBlock(n, &last, &stack) + //pushClosure(&Closure{}) // parent if statement. ifs := ns[len(ns)-1].(*IfStmt) // anything declared in ifs are copied. @@ -300,6 +305,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { debugPP.Printf("tv : %v, *tv: %v \n", tv, *tv) last.Define(n, *tv) } + //popClosure() // TRANS_BLOCK ----------------------- case *RangeStmt: @@ -367,6 +373,9 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { ft := evalStaticType(store, last, &n.Type).(*FuncType) // push func body block. pushInitBlock(n, &last, &stack) + + //pushClosure(&Closure{}) + //pushFxs(n) // define parameters in new block. for _, p := range ft.Params { debug.Println("---PP define params") @@ -384,7 +393,14 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { last.Define(Name(rn), anyValue(rf.Type)) } } - debug.Println("---PP funcLitExpr end---") + + //debug.Println("funcLit pop c-----") + //pc := popClosure() + ////cnn.Closure = c + //closure := *pc + //n.SetClosure(closure) + //debug.Printf("---done FuncLit trans, fx: %v, closure: %+v \n", n, n.Closure.String()) + //debug.Println("---PP funcLitExpr end---") // TRANS_BLOCK ----------------------- case *SelectCaseStmt: @@ -414,6 +430,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // TRANS_BLOCK ----------------------- case *SwitchClauseStmt: pushRealBlock(n, &last, &stack) + //pushClosure(&Closure{}) // parent switch statement. ss := ns[len(ns)-1].(*SwitchStmt) // anything declared in ss are copied, @@ -476,6 +493,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { n.Cases[i] = cx } } + //popClosure() // TRANS_BLOCK ----------------------- case *FuncDecl: diff --git a/gnovm/pkg/gnolang/transcribe.go b/gnovm/pkg/gnolang/transcribe.go index fdfbec6d52a..eab20802245 100644 --- a/gnovm/pkg/gnolang/transcribe.go +++ b/gnovm/pkg/gnolang/transcribe.go @@ -112,64 +112,92 @@ type Closures struct { cs []*Closure } -var fxs []*FuncLitExpr +var cnodes []Node func pushClosure(c *Closure) { - debug.Println("+clo") - debug.Println("before push closure") - dumpClosures() - closures.cs = append(closures.cs, c) - debug.Println("end push closure") - dumpClosures() -} - -func popClosure() *Closure { - debug.Println("-clo") - debug.Println("before pop, dump") - dumpClosures() - if len(closures.cs) == 0 { - return nil + if len(cnodes) > 0 { + debug.Println("+clo") + debug.Println("before push closure") + dumpClosures() + closures.cs = append(closures.cs, c) + debug.Println("end push closure") + dumpClosures() } else { - c := closures.cs[len(closures.cs)-1] // get current - for _, cnx := range c.cnxs { // pop-> increase - debug.Printf("+1 \n") - cnx.offset += 1 - } + debug.Println("no fx in stack, no need push closure") + } +} - closures.cs = closures.cs[:len(closures.cs)-1] // shrink - // copy poped to latest, if left at least one closure - currentClo := currentClosure() - debug.Printf("currentClo: %v \n", currentClo) - if currentClo != nil { // if last closure, just pop, no copy - // fill up current closure - for _, cnx := range c.cnxs { // trace back captured nxs - currentClo.Fill(*cnx) - } - } - debug.Println("after pop, dump") +func popClosure(copy bool) *Closure { + if len(cnodes) > 0 { + debug.Println("-clo") + debug.Println("before pop, dump") dumpClosures() + if len(closures.cs) == 0 { + return nil + } else { + c := closures.cs[len(closures.cs)-1] // get current + for _, cnx := range c.cnxs { // pop-> increase + debug.Printf("+1 \n") + cnx.offset += 1 + } + closures.cs = closures.cs[:len(closures.cs)-1] // shrink + + // copy poped to latest, if left at least one closure + if copy { + currentClo := currentClosure() + debug.Printf("currentClo: %v \n", currentClo) + if currentClo != nil { // if last closure, just pop, no copy + // fill up current closure + for _, cnx := range c.cnxs { // trace back captured nxs + currentClo.Fill(*cnx) + } + } + } + debug.Println("after pop, dump") + dumpClosures() - debug.Printf("c poped: %v, \n", c) - return c + debug.Printf("c poped: %v, \n", c) + return c + } + } else { + debug.Println("should not happen: no fx in stack, no need pop closure") + return nil } } -func pushFxs(fx *FuncLitExpr) { - fxs = append(fxs, fx) +func pushCnodes(cn Node) { + cnodes = append(cnodes, cn) } -func popFx() { +func popCnodes() { debug.Println("-fx") - if len(fxs) != 0 { - fxs = fxs[:len(fxs)-1] + if len(cnodes) != 0 { + cnodes = cnodes[:len(cnodes)-1] + } +} + +func peekCnodes(offset int) Node { + debug.Println(":fx") + if len(cnodes) >= (1 + offset) { + return cnodes[len(cnodes)-(1+offset)] + } + return nil +} + +func hasClosure() bool { + for _, c := range cnodes { + if _, ok := c.(*FuncLitExpr); ok { + return true + } } + return false } func dumpFxs() { if debug { println("============Dump fxs===========") - println("len: ", len(fxs)) - for i, fx := range fxs { + println("len: ", len(cnodes)) + for i, fx := range cnodes { fmt.Printf("fx[%d]: %v\n", i, fx) } println("============end===============") @@ -196,13 +224,6 @@ func currentClosure() *Closure { return closures.cs[len(closures.cs)-1] } -func currentFx() *FuncLitExpr { - if len(fxs) == 0 { - return nil - } - return fxs[len(fxs)-1] -} - type CapturedNx struct { nx *NameExpr offset uint8 // every captured nx has an offset, represents its distance to the funcLitExpr @@ -289,26 +310,23 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc // visit any children of n. switch cnn := nn.(type) { case *NameExpr: - debug.Printf("-----trans, nameExpr: %v \n", cnn) // TODO: do we need to filter out already define in current block - - currentClo := currentClosure() - debug.Printf("currentClo: %v \n", currentClo) - - if currentClo != nil { // a closure to fill - if cnn.Path.Depth > 1 { // if local defined, no capture - cnx := CapturedNx{ - nx: cnn, - offset: 0, + // if nested closure, do not copy! + if hasClosure() { + debug.Printf("-----trans, nameExpr: %v \n", cnn) + currentClo := currentClosure() + debug.Printf("currentClo: %v \n", currentClo) + if currentClo != nil { // a closure to fill + if cnn.Path.Depth > 1 { // if local defined, no capture + cnx := CapturedNx{ + nx: cnn, + offset: 0, + } + currentClo.Fill(cnx) } - currentClo.Fill(cnx) + dumpClosures() + dumpFxs() } - //currentFx.Closure = clo - //for _, cnx := range currentClo.cnxs { - // cnx.offset = 0 // reset every cnx offset - //} - dumpClosures() - dumpFxs() } case *BasicLitExpr: case *BinaryExpr: @@ -423,8 +441,8 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } case *FuncLitExpr: debug.Printf("-----trans, funcLitExpr: %v \n", cnn) - dumpClosures() dumpFxs() + dumpClosures() cnn.Type = *transcribe(t, nns, TRANS_FUNCLIT_TYPE, 0, &cnn.Type, &c).(*FuncTypeExpr) if isStopOrSkip(nc, c) { @@ -439,20 +457,24 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } debug.Println("---start trans funcLit body stmt, push initial closure and fx") + pushCnodes(cnn) pushClosure(&Closure{}) - pushFxs(cnn) + + debug.Printf("---stop or skip, pop and return \n") + fx := peekCnodes(1) + isCopy := true + if _, ok := fx.(*FuncLitExpr); ok { + isCopy = false + } for idx := range cnn.Body { cnn.Body[idx] = transcribe(t, nns, TRANS_FUNCLIT_BODY, idx, cnn.Body[idx], &c).(Stmt) - if isBreak(c) { break } else if isStopOrSkip(nc, c) { // pop before return - debug.Printf("---stop or skip, pop and return \n") - popClosure() - popFx() - + popClosure(isCopy) + popCnodes() return } } @@ -460,13 +482,13 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc debug.Printf("---done trans body \n") // TODO: set fx.Closure, and level as well debug.Println("funcLit pop c-----") - pc := popClosure() - //cnn.Closure = c - closure := *pc - cnn.SetClosure(closure) - debug.Printf("---done FuncLit trans, fx: %v, closure: %+v \n", cnn, cnn.Closure.String()) - popFx() + pc := popClosure(isCopy) + if pc != nil { + cnn.SetClosure(pc) + debug.Printf("---done FuncLit trans, fx: %v, closure: %+v \n", cnn, cnn.Closure.String()) + } + popCnodes() case *FieldTypeExpr: cnn.Type = transcribe(t, nns, TRANS_FIELDTYPE_TYPE, 0, cnn.Type, &c).(Expr) if isStopOrSkip(nc, c) { @@ -571,14 +593,17 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*BlockStmt) } + pushClosure(&Closure{}) for idx := range cnn.Body { cnn.Body[idx] = transcribe(t, nns, TRANS_BLOCK_BODY, idx, cnn.Body[idx], &c).(Stmt) if isBreak(c) { break } else if isStopOrSkip(nc, c) { + popClosure(true) return } } + popClosure(true) case *BranchStmt: case *DeclStmt: for idx := range cnn.Body { @@ -608,6 +633,8 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*ForStmt) } + + pushClosure(&Closure{}) if cnn.Init != nil { cnn.Init = transcribe(t, nns, TRANS_FOR_INIT, 0, cnn.Init, &c).(SimpleStmt) if isStopOrSkip(nc, c) { @@ -631,9 +658,11 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc if isBreak(c) { break } else if isStopOrSkip(nc, c) { + popClosure(true) return } } + popClosure(true) case *GoStmt: cnn.Call = *transcribe(t, nns, TRANS_GO_CALL, 0, &cnn.Call, &c).(*CallExpr) if isStopOrSkip(nc, c) { @@ -672,7 +701,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc if isStopOrSkip(nc, c) { return } - popClosure() + popClosure(true) case *IfCaseStmt: debug.Printf("-----trans, (if---case) stmt: %v \n", cnn) cnn2, c2 := t(ns, ftype, index, cnn, TRANS_BLOCK) @@ -707,19 +736,23 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*RangeStmt) } + pushClosure(&Closure{}) cnn.X = transcribe(t, nns, TRANS_RANGE_X, 0, cnn.X, &c).(Expr) if isStopOrSkip(nc, c) { + popClosure(true) return } if cnn.Key != nil { cnn.Key = transcribe(t, nns, TRANS_RANGE_KEY, 0, cnn.Key, &c).(Expr) if isStopOrSkip(nc, c) { + popClosure(true) return } } if cnn.Value != nil { cnn.Value = transcribe(t, nns, TRANS_RANGE_VALUE, 0, cnn.Value, &c).(Expr) if isStopOrSkip(nc, c) { + popClosure(true) return } } @@ -728,9 +761,11 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc if isBreak(c) { break } else if isStopOrSkip(nc, c) { + popClosure(true) return } } + popClosure(true) case *ReturnStmt: debug.Printf("-----trans, return stmt: %v \n", cnn) for idx := range cnn.Results { @@ -793,20 +828,24 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*SwitchStmt) } + pushClosure(&Closure{}) if cnn.Init != nil { cnn.Init = transcribe(t, nns, TRANS_SWITCH_INIT, 0, cnn.Init, &c).(SimpleStmt) if isStopOrSkip(nc, c) { + popClosure(true) return } } cnn.X = transcribe(t, nns, TRANS_SWITCH_X, 0, cnn.X, &c).(Expr) if isStopOrSkip(nc, c) { + popClosure(true) return } // NOTE: special block case for after .Init and .X. cnn2, c2 = t(ns, ftype, index, cnn, TRANS_BLOCK2) if isStopOrSkip(nc, c2) { nn = cnn2 + popClosure(true) return } else { cnn = cnn2.(*SwitchStmt) @@ -816,9 +855,11 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc if isBreak(c) { break } else if isStopOrSkip(nc, c) { + popClosure(true) return } } + popClosure(true) case *SwitchClauseStmt: // NOTE: unlike the select case, both switch // statements AND switch cases visit with the diff --git a/gnovm/tests/files/debug/1.gno b/gnovm/tests/debug/1.gno similarity index 100% rename from gnovm/tests/files/debug/1.gno rename to gnovm/tests/debug/1.gno diff --git a/gnovm/tests/files/debug/1a.gno b/gnovm/tests/debug/1a.gno similarity index 100% rename from gnovm/tests/files/debug/1a.gno rename to gnovm/tests/debug/1a.gno diff --git a/gnovm/tests/files/debug/1b.gno b/gnovm/tests/debug/1b.gno similarity index 100% rename from gnovm/tests/files/debug/1b.gno rename to gnovm/tests/debug/1b.gno diff --git a/gnovm/tests/files/debug/1c.gno b/gnovm/tests/debug/1c.gno similarity index 100% rename from gnovm/tests/files/debug/1c.gno rename to gnovm/tests/debug/1c.gno diff --git a/gnovm/tests/files/debug/1d.gno b/gnovm/tests/debug/1d.gno similarity index 100% rename from gnovm/tests/files/debug/1d.gno rename to gnovm/tests/debug/1d.gno diff --git a/gnovm/tests/files/debug/2.gno b/gnovm/tests/debug/2.gno similarity index 100% rename from gnovm/tests/files/debug/2.gno rename to gnovm/tests/debug/2.gno diff --git a/gnovm/tests/files/debug3/2.gno b/gnovm/tests/debug/2a.gno similarity index 97% rename from gnovm/tests/files/debug3/2.gno rename to gnovm/tests/debug/2a.gno index 1a788d9e0b2..45ad06c6945 100644 --- a/gnovm/tests/files/debug3/2.gno +++ b/gnovm/tests/debug/2a.gno @@ -7,7 +7,7 @@ func bar() func() func() int { // First level of closure, modifies x return func() func() int { - //x++ + x++ // Second level of closure, returns x return func() int { return x @@ -23,4 +23,4 @@ func main() { } // Output: -// 1 +// 2 diff --git a/gnovm/tests/files/debug/3.gno b/gnovm/tests/debug/3.gno similarity index 100% rename from gnovm/tests/files/debug/3.gno rename to gnovm/tests/debug/3.gno diff --git a/gnovm/tests/files/debug/3a.gno b/gnovm/tests/debug/3a.gno similarity index 100% rename from gnovm/tests/files/debug/3a.gno rename to gnovm/tests/debug/3a.gno diff --git a/gnovm/tests/files/debug/3b.gno b/gnovm/tests/debug/3b.gno similarity index 100% rename from gnovm/tests/files/debug/3b.gno rename to gnovm/tests/debug/3b.gno diff --git a/gnovm/tests/files/debug3/1c.gno b/gnovm/tests/debug/4.gno similarity index 81% rename from gnovm/tests/files/debug3/1c.gno rename to gnovm/tests/debug/4.gno index 65c71cd14fb..22e23dbff55 100644 --- a/gnovm/tests/files/debug3/1c.gno +++ b/gnovm/tests/debug/4.gno @@ -2,9 +2,13 @@ package main func main() { var fns []func() int + for i := 0; i < 2; i++ { + x := i f := func() int { - return i + { + return x + } } fns = append(fns, f) } @@ -14,5 +18,5 @@ func main() { } // Output: -// 2 -// 2 +// 1 +// 1 diff --git a/gnovm/tests/files/debug3/1a.gno b/gnovm/tests/debug/4a.gno similarity index 83% rename from gnovm/tests/files/debug3/1a.gno rename to gnovm/tests/debug/4a.gno index 105f8c8e6b3..a94a47ca039 100644 --- a/gnovm/tests/files/debug3/1a.gno +++ b/gnovm/tests/debug/4a.gno @@ -2,12 +2,13 @@ package main func main() { var fns []func() int + for i := 0; i < 2; i++ { x := i - y := 0 f := func() int { - x += y - //x += 1 + for i := 0; i < 1; i++ { + x++ + } return x } fns = append(fns, f) @@ -18,5 +19,3 @@ func main() { } // Output: -// 1 -// 1 diff --git a/gnovm/tests/files/debug3/1b.gno b/gnovm/tests/debug/4b.gno similarity index 77% rename from gnovm/tests/files/debug3/1b.gno rename to gnovm/tests/debug/4b.gno index 06ff7975701..0e616d3a91f 100644 --- a/gnovm/tests/files/debug3/1b.gno +++ b/gnovm/tests/debug/4b.gno @@ -2,12 +2,14 @@ package main func main() { var fns []func() int + for i := 0; i < 2; i++ { x := i - y := 0 + s := []int{1, 2} f := func() int { - x += y - x += 1 + for _, v := range s { + x += v + } return x } fns = append(fns, f) @@ -18,5 +20,3 @@ func main() { } // Output: -// 2 -// 3 diff --git a/gnovm/tests/files/debug3/1.gno b/gnovm/tests/debug/4c.gno similarity index 64% rename from gnovm/tests/files/debug3/1.gno rename to gnovm/tests/debug/4c.gno index be4631b10dc..fb80f4e8dcb 100644 --- a/gnovm/tests/files/debug3/1.gno +++ b/gnovm/tests/debug/4c.gno @@ -2,9 +2,17 @@ package main func main() { var fns []func() int - for i := 0; i < 5; i++ { + + for i := 0; i < 2; i++ { x := i + y := 1 f := func() int { + switch y { + case 1: + x += 1 + default: + x += 0 + } return x } fns = append(fns, f) @@ -15,8 +23,3 @@ func main() { } // Output: -// 4 -// 4 -// 4 -// 4 -// 4 diff --git a/gnovm/tests/files/debug/closure0.gno b/gnovm/tests/debug/closure0.gno similarity index 100% rename from gnovm/tests/files/debug/closure0.gno rename to gnovm/tests/debug/closure0.gno diff --git a/gnovm/tests/files/debug/closure1.gno b/gnovm/tests/debug/closure1.gno similarity index 100% rename from gnovm/tests/files/debug/closure1.gno rename to gnovm/tests/debug/closure1.gno diff --git a/gnovm/tests/files/debug/closure2.gno b/gnovm/tests/debug/closure2.gno similarity index 100% rename from gnovm/tests/files/debug/closure2.gno rename to gnovm/tests/debug/closure2.gno diff --git a/gnovm/tests/files/debug/closure3.gno b/gnovm/tests/debug/closure3.gno similarity index 100% rename from gnovm/tests/files/debug/closure3.gno rename to gnovm/tests/debug/closure3.gno diff --git a/gnovm/tests/files/debug/closure4.gno b/gnovm/tests/debug/closure4.gno similarity index 100% rename from gnovm/tests/files/debug/closure4.gno rename to gnovm/tests/debug/closure4.gno diff --git a/gnovm/tests/files/debug/closure5.gno b/gnovm/tests/debug/closure5.gno similarity index 100% rename from gnovm/tests/files/debug/closure5.gno rename to gnovm/tests/debug/closure5.gno diff --git a/gnovm/tests/files/debug/closure6.gno b/gnovm/tests/debug/closure6.gno similarity index 100% rename from gnovm/tests/files/debug/closure6.gno rename to gnovm/tests/debug/closure6.gno diff --git a/gnovm/tests/files/debug/closure7.gno b/gnovm/tests/debug/closure7.gno similarity index 100% rename from gnovm/tests/files/debug/closure7.gno rename to gnovm/tests/debug/closure7.gno diff --git a/gnovm/tests/files/debug/closure8.gno b/gnovm/tests/debug/closure8.gno similarity index 100% rename from gnovm/tests/files/debug/closure8.gno rename to gnovm/tests/debug/closure8.gno diff --git a/gnovm/tests/files/debug/recover4.gno b/gnovm/tests/debug/recover4.gno similarity index 100% rename from gnovm/tests/files/debug/recover4.gno rename to gnovm/tests/debug/recover4.gno diff --git a/gnovm/tests/files/debug/recover6.gno b/gnovm/tests/debug/recover6.gno similarity index 100% rename from gnovm/tests/files/debug/recover6.gno rename to gnovm/tests/debug/recover6.gno diff --git a/gnovm/tests/file_test.go b/gnovm/tests/file_test.go index 04b5f14aec1..9e9de5fa615 100644 --- a/gnovm/tests/file_test.go +++ b/gnovm/tests/file_test.go @@ -33,7 +33,7 @@ func TestFiles(t *testing.T) { } func TestDebug(t *testing.T) { - baseDir := filepath.Join(".", "files/debug") + baseDir := filepath.Join(".", "debug") //runFileTests(t, baseDir, []string{"*_native*"}) runFileTests(t, baseDir, []string{"*_stdlibs*"}, WithNativeLibs()) } diff --git a/gnovm/tests/files/debug3/1d.gno b/gnovm/tests/files/debug3/1d.gno deleted file mode 100644 index 185961c741f..00000000000 --- a/gnovm/tests/files/debug3/1d.gno +++ /dev/null @@ -1,29 +0,0 @@ -package main - -func main() { - var fns []func() int - for i := 0; i < 5; i++ { - x := i - f := func() int { - x := 5 - return x - } - println(x) - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 0 -// 1 -// 2 -// 3 -// 4 -// 5 -// 5 -// 5 -// 5 -// 5 diff --git a/gnovm/tests/files/debug3/closure0.gno b/gnovm/tests/files/debug3/closure0.gno deleted file mode 100644 index acc1abfd404..00000000000 --- a/gnovm/tests/files/debug3/closure0.gno +++ /dev/null @@ -1,17 +0,0 @@ -package main - -type adder func(int, int) int - -func genAdd(k int) adder { - return func(i, j int) int { - return i + j + k - } -} - -func main() { - f := genAdd(5) - println(f(3, 4)) -} - -// Output: -// 12 diff --git a/gnovm/tests/files/debug3/closure1.gno b/gnovm/tests/files/debug3/closure1.gno deleted file mode 100644 index f0bbda7ca1e..00000000000 --- a/gnovm/tests/files/debug3/closure1.gno +++ /dev/null @@ -1,20 +0,0 @@ -package main - -type adder func(int, int) int - -func genAdd(k int) adder { - return func(i, j int) int { - return i + j + k - } -} - -func main() { - f := genAdd(5) - g := genAdd(8) - println(f(3, 4)) - println(g(3, 4)) -} - -// Output: -// 12 -// 15 diff --git a/gnovm/tests/files/debug3/closure2.gno b/gnovm/tests/files/debug3/closure2.gno deleted file mode 100644 index e43496b9f43..00000000000 --- a/gnovm/tests/files/debug3/closure2.gno +++ /dev/null @@ -1,28 +0,0 @@ -package main - -func adder() func(int) int { - sum := 0 - return func(x int) int { - sum = sum + x - return sum - } -} - -func main() { - pos, neg := adder(), adder() - for i := 0; i < 10; i++ { - println(pos(i), neg(-2*i)) - } -} - -// Output: -// 0 0 -// 1 -2 -// 3 -6 -// 6 -12 -// 10 -20 -// 15 -30 -// 21 -42 -// 28 -56 -// 36 -72 -// 45 -90 diff --git a/gnovm/tests/files/debug3/closure3.gno b/gnovm/tests/files/debug3/closure3.gno deleted file mode 100644 index 5e365a67cb4..00000000000 --- a/gnovm/tests/files/debug3/closure3.gno +++ /dev/null @@ -1,23 +0,0 @@ -package main - -type T1 struct { - Name string -} - -func (t *T1) genAdd(k int) func(int) int { - return func(i int) int { - println(t.Name) - return i + k - } -} - -var t = &T1{"test"} - -func main() { - f := t.genAdd(4) - println(f(5)) -} - -// Output: -// test -// 9 diff --git a/gnovm/tests/files/debug3/closure4.gno b/gnovm/tests/files/debug3/closure4.gno deleted file mode 100644 index 61c0a77f1ba..00000000000 --- a/gnovm/tests/files/debug3/closure4.gno +++ /dev/null @@ -1,23 +0,0 @@ -package main - -type T1 struct { - Name string -} - -func (t *T1) genAdd(k int) func(int) int { - return func(i int) int { - println(t.Name) - return i + k - } -} - -var t = T1{"test"} - -func main() { - f := t.genAdd(4) - println(f(5)) -} - -// Output: -// test -// 9 diff --git a/gnovm/tests/files/debug3/closure5.gno b/gnovm/tests/files/debug3/closure5.gno deleted file mode 100644 index e6d3c223607..00000000000 --- a/gnovm/tests/files/debug3/closure5.gno +++ /dev/null @@ -1,23 +0,0 @@ -package main - -type T1 struct { - Name string -} - -func (t T1) genAdd(k int) func(int) int { - return func(i int) int { - println(t.Name) - return i + k - } -} - -var t = T1{"test"} - -func main() { - f := t.genAdd(4) - println(f(5)) -} - -// Output: -// test -// 9 diff --git a/gnovm/tests/files/debug3/closure6.gno b/gnovm/tests/files/debug3/closure6.gno deleted file mode 100644 index 5e365a67cb4..00000000000 --- a/gnovm/tests/files/debug3/closure6.gno +++ /dev/null @@ -1,23 +0,0 @@ -package main - -type T1 struct { - Name string -} - -func (t *T1) genAdd(k int) func(int) int { - return func(i int) int { - println(t.Name) - return i + k - } -} - -var t = &T1{"test"} - -func main() { - f := t.genAdd(4) - println(f(5)) -} - -// Output: -// test -// 9 diff --git a/gnovm/tests/files/debug3/closure7.gno b/gnovm/tests/files/debug3/closure7.gno deleted file mode 100644 index d4c0c96b0d5..00000000000 --- a/gnovm/tests/files/debug3/closure7.gno +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import ( - "fmt" -) - -type Config struct { - A string -} - -var conf *Config = &Config{} - -func SetConfig() func(*Config) { - return func(cf *Config) { - conf = cf - } -} - -func main() { - conf := &Config{ - A: "foo", - } - - fmt.Println(conf.A) -} - -// Output: -// foo diff --git a/gnovm/tests/files/debug3/closure8.gno b/gnovm/tests/files/debug3/closure8.gno deleted file mode 100644 index 6ef1b43fca6..00000000000 --- a/gnovm/tests/files/debug3/closure8.gno +++ /dev/null @@ -1,10 +0,0 @@ -package main - -var f = func(a int) int { return 2 + a } - -func main() { - println(f(3)) -} - -// Output: -// 5 From 0831b1a161c4ddc420315332080087e7af83b9c6 Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Mon, 8 Jan 2024 17:19:35 +0800 Subject: [PATCH 06/32] fixup --- gnovm/pkg/gnolang/transcribe.go | 259 ++++++++++++++++++-------------- gnovm/tests/debug/4.gno | 2 +- gnovm/tests/debug/4a.gno | 2 + gnovm/tests/debug/4b.gno | 2 + gnovm/tests/debug/4c.gno | 2 + 5 files changed, 152 insertions(+), 115 deletions(-) diff --git a/gnovm/pkg/gnolang/transcribe.go b/gnovm/pkg/gnolang/transcribe.go index eab20802245..2cc6ed8cb6f 100644 --- a/gnovm/pkg/gnolang/transcribe.go +++ b/gnovm/pkg/gnolang/transcribe.go @@ -106,109 +106,108 @@ const ( TRANS_FILE_BODY ) -var closures = &Closures{} +// var closures = &Closures{} +var CX *ClosureContext -type Closures struct { - cs []*Closure +func init() { + CX = &ClosureContext{} } -var cnodes []Node +type ClosureContext struct { + closures []*Closure + nodes []Node +} + +//var cnodes []Node -func pushClosure(c *Closure) { - if len(cnodes) > 0 { +func (cx *ClosureContext) hasClosure() bool { + for _, c := range cx.nodes { + if _, ok := c.(*FuncLitExpr); ok { + return true + } + } + return false +} + +func (cx *ClosureContext) push(n Node) bool { + cx.nodes = append(cx.nodes, n) + if cx.hasClosure() { debug.Println("+clo") debug.Println("before push closure") - dumpClosures() - closures.cs = append(closures.cs, c) + cx.dumpClosures() + cx.closures = append(cx.closures, &Closure{}) // push empty closure to be filled in debug.Println("end push closure") - dumpClosures() + cx.dumpClosures() + return true } else { debug.Println("no fx in stack, no need push closure") + return false } } -func popClosure(copy bool) *Closure { - if len(cnodes) > 0 { - debug.Println("-clo") - debug.Println("before pop, dump") - dumpClosures() - if len(closures.cs) == 0 { - return nil - } else { - c := closures.cs[len(closures.cs)-1] // get current - for _, cnx := range c.cnxs { // pop-> increase - debug.Printf("+1 \n") - cnx.offset += 1 - } - closures.cs = closures.cs[:len(closures.cs)-1] // shrink - - // copy poped to latest, if left at least one closure - if copy { - currentClo := currentClosure() - debug.Printf("currentClo: %v \n", currentClo) - if currentClo != nil { // if last closure, just pop, no copy - // fill up current closure - for _, cnx := range c.cnxs { // trace back captured nxs - currentClo.Fill(*cnx) - } - } - } - debug.Println("after pop, dump") - dumpClosures() +func (cx *ClosureContext) pop(copy bool) *Closure { + debug.Println("-clo") + debug.Println("before pop, dump") + cx.dumpClosures() + defer func() { + if len(cx.nodes) != 0 { + debug.Println("-node") + cx.nodes = cx.nodes[:len(cx.nodes)-1] + } + }() - debug.Printf("c poped: %v, \n", c) - return c - } - } else { - debug.Println("should not happen: no fx in stack, no need pop closure") + if len(cx.closures) == 0 { return nil - } -} - -func pushCnodes(cn Node) { - cnodes = append(cnodes, cn) -} + } else { + c := cx.closures[len(cx.closures)-1] // get current + for _, cnx := range c.cnxs { // pop-> increase offset + debug.Printf("+1 \n") + cnx.offset += 1 + } + cx.closures = cx.closures[:len(cx.closures)-1] // shrink -func popCnodes() { - debug.Println("-fx") - if len(cnodes) != 0 { - cnodes = cnodes[:len(cnodes)-1] + // copy poped to latest, if left at least one closure + if copy { + currentClo := cx.currentClosure() + debug.Printf("currentClo: %v \n", currentClo) + if currentClo != nil { // if last closure, just pop, no copy + // fill up current closure + for _, cnx := range c.cnxs { // trace back captured nxs + currentClo.Fill(*cnx) + } + } + } + debug.Println("after pop, dump") + cx.dumpClosures() + debug.Printf("c poped: %v, \n", c) + return c } } -func peekCnodes(offset int) Node { - debug.Println(":fx") - if len(cnodes) >= (1 + offset) { - return cnodes[len(cnodes)-(1+offset)] +func (cx *ClosureContext) peekNodes(offset int) Node { + debug.Println("c:node") + if len(cx.nodes) >= (1 + offset) { + return cx.nodes[len(cx.nodes)-(1+offset)] } return nil } -func hasClosure() bool { - for _, c := range cnodes { - if _, ok := c.(*FuncLitExpr); ok { - return true - } - } - return false -} - -func dumpFxs() { +func (cx *ClosureContext) dumpNodes() { if debug { println("============Dump fxs===========") - println("len: ", len(cnodes)) - for i, fx := range cnodes { - fmt.Printf("fx[%d]: %v\n", i, fx) + println("len: ", len(cx.nodes)) + for i, n := range cx.nodes { + fmt.Printf("node[%d]: %v\n", i, n) } println("============end===============") println("\n") } } -func dumpClosures() { +func (cx *ClosureContext) dumpClosures() { if debug { println("============Dump closures start=======") - debug.Printf("depth of closures: %d \n", len(closures.cs)) - for _, c := range closures.cs { + debug.Printf("depth of closures: %d \n", len(cx.closures)) + for _, c := range cx.closures { fmt.Printf("===>: %v \n", c) } println("============Dump closures end===============") @@ -216,12 +215,12 @@ func dumpClosures() { } } -func currentClosure() *Closure { - debug.Printf("currentClosure, len: %d \n", len(closures.cs)) - if len(closures.cs) == 0 { +func (cx *ClosureContext) currentClosure() *Closure { + debug.Printf("currentClosure, len: %d \n", len(cx.closures)) + if len(cx.closures) == 0 { return nil } - return closures.cs[len(closures.cs)-1] + return cx.closures[len(cx.closures)-1] } type CapturedNx struct { @@ -310,11 +309,12 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc // visit any children of n. switch cnn := nn.(type) { case *NameExpr: + debug.Printf("-----trans, nameExpr: %v \n", cnn) // TODO: do we need to filter out already define in current block // if nested closure, do not copy! - if hasClosure() { - debug.Printf("-----trans, nameExpr: %v \n", cnn) - currentClo := currentClosure() + if CX.hasClosure() { + debug.Printf("---has Closure") + currentClo := CX.currentClosure() debug.Printf("currentClo: %v \n", currentClo) if currentClo != nil { // a closure to fill if cnn.Path.Depth > 1 { // if local defined, no capture @@ -324,8 +324,8 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } currentClo.Fill(cnx) } - dumpClosures() - dumpFxs() + CX.dumpClosures() + CX.dumpNodes() } } case *BasicLitExpr: @@ -441,8 +441,8 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } case *FuncLitExpr: debug.Printf("-----trans, funcLitExpr: %v \n", cnn) - dumpFxs() - dumpClosures() + CX.dumpNodes() + CX.dumpClosures() cnn.Type = *transcribe(t, nns, TRANS_FUNCLIT_TYPE, 0, &cnn.Type, &c).(*FuncTypeExpr) if isStopOrSkip(nc, c) { @@ -457,13 +457,12 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } debug.Println("---start trans funcLit body stmt, push initial closure and fx") - pushCnodes(cnn) - pushClosure(&Closure{}) + pushed := CX.push(cnn) debug.Printf("---stop or skip, pop and return \n") - fx := peekCnodes(1) + node := CX.peekNodes(1) isCopy := true - if _, ok := fx.(*FuncLitExpr); ok { + if _, ok := node.(*FuncLitExpr); ok { isCopy = false } @@ -473,8 +472,9 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc break } else if isStopOrSkip(nc, c) { // pop before return - popClosure(isCopy) - popCnodes() + if pushed { + CX.pop(isCopy) + } return } } @@ -483,12 +483,13 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc // TODO: set fx.Closure, and level as well debug.Println("funcLit pop c-----") - pc := popClosure(isCopy) - if pc != nil { - cnn.SetClosure(pc) - debug.Printf("---done FuncLit trans, fx: %v, closure: %+v \n", cnn, cnn.Closure.String()) + if pushed { + pc := CX.pop(isCopy) + if pc != nil { + cnn.SetClosure(pc) + debug.Printf("---done FuncLit trans, fx: %v, closure: %+v \n", cnn, cnn.Closure.String()) + } } - popCnodes() case *FieldTypeExpr: cnn.Type = transcribe(t, nns, TRANS_FIELDTYPE_TYPE, 0, cnn.Type, &c).(Expr) if isStopOrSkip(nc, c) { @@ -593,17 +594,21 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*BlockStmt) } - pushClosure(&Closure{}) + pushed := CX.push(cnn) for idx := range cnn.Body { cnn.Body[idx] = transcribe(t, nns, TRANS_BLOCK_BODY, idx, cnn.Body[idx], &c).(Stmt) if isBreak(c) { break } else if isStopOrSkip(nc, c) { - popClosure(true) + if pushed { + CX.pop(true) + } return } } - popClosure(true) + if pushed { + CX.pop(true) + } case *BranchStmt: case *DeclStmt: for idx := range cnn.Body { @@ -634,7 +639,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc cnn = cnn2.(*ForStmt) } - pushClosure(&Closure{}) + pushed := CX.push(cnn) if cnn.Init != nil { cnn.Init = transcribe(t, nns, TRANS_FOR_INIT, 0, cnn.Init, &c).(SimpleStmt) if isStopOrSkip(nc, c) { @@ -658,11 +663,15 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc if isBreak(c) { break } else if isStopOrSkip(nc, c) { - popClosure(true) + if pushed { + CX.pop(true) + } return } } - popClosure(true) + if pushed { + CX.pop(true) + } case *GoStmt: cnn.Call = *transcribe(t, nns, TRANS_GO_CALL, 0, &cnn.Call, &c).(*CallExpr) if isStopOrSkip(nc, c) { @@ -681,7 +690,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc cnn = cnn2.(*IfStmt) } - pushClosure(&Closure{}) + pushed := CX.push(cnn) if cnn.Init != nil { cnn.Init = transcribe(t, nns, TRANS_IF_INIT, 0, cnn.Init, &c).(SimpleStmt) if isStopOrSkip(nc, c) { @@ -701,7 +710,9 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc if isStopOrSkip(nc, c) { return } - popClosure(true) + if pushed { + CX.pop(true) + } case *IfCaseStmt: debug.Printf("-----trans, (if---case) stmt: %v \n", cnn) cnn2, c2 := t(ns, ftype, index, cnn, TRANS_BLOCK) @@ -736,23 +747,29 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*RangeStmt) } - pushClosure(&Closure{}) + pushed := CX.push(cnn) cnn.X = transcribe(t, nns, TRANS_RANGE_X, 0, cnn.X, &c).(Expr) if isStopOrSkip(nc, c) { - popClosure(true) + if pushed { + CX.pop(true) + } return } if cnn.Key != nil { cnn.Key = transcribe(t, nns, TRANS_RANGE_KEY, 0, cnn.Key, &c).(Expr) if isStopOrSkip(nc, c) { - popClosure(true) + if pushed { + CX.pop(true) + } return } } if cnn.Value != nil { cnn.Value = transcribe(t, nns, TRANS_RANGE_VALUE, 0, cnn.Value, &c).(Expr) if isStopOrSkip(nc, c) { - popClosure(true) + if pushed { + CX.pop(true) + } return } } @@ -761,11 +778,15 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc if isBreak(c) { break } else if isStopOrSkip(nc, c) { - popClosure(true) + if pushed { + CX.pop(true) + } return } } - popClosure(true) + if pushed { + CX.pop(true) + } case *ReturnStmt: debug.Printf("-----trans, return stmt: %v \n", cnn) for idx := range cnn.Results { @@ -828,24 +849,30 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*SwitchStmt) } - pushClosure(&Closure{}) + pushed := CX.push(cnn) if cnn.Init != nil { cnn.Init = transcribe(t, nns, TRANS_SWITCH_INIT, 0, cnn.Init, &c).(SimpleStmt) if isStopOrSkip(nc, c) { - popClosure(true) + if pushed { + CX.pop(true) + } return } } cnn.X = transcribe(t, nns, TRANS_SWITCH_X, 0, cnn.X, &c).(Expr) if isStopOrSkip(nc, c) { - popClosure(true) + if pushed { + CX.pop(true) + } return } // NOTE: special block case for after .Init and .X. cnn2, c2 = t(ns, ftype, index, cnn, TRANS_BLOCK2) if isStopOrSkip(nc, c2) { nn = cnn2 - popClosure(true) + if pushed { + CX.pop(true) + } return } else { cnn = cnn2.(*SwitchStmt) @@ -855,11 +882,15 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc if isBreak(c) { break } else if isStopOrSkip(nc, c) { - popClosure(true) + if pushed { + CX.pop(true) + } return } } - popClosure(true) + if pushed { + CX.pop(true) + } case *SwitchClauseStmt: // NOTE: unlike the select case, both switch // statements AND switch cases visit with the diff --git a/gnovm/tests/debug/4.gno b/gnovm/tests/debug/4.gno index 22e23dbff55..44717e05cc0 100644 --- a/gnovm/tests/debug/4.gno +++ b/gnovm/tests/debug/4.gno @@ -18,5 +18,5 @@ func main() { } // Output: -// 1 +// 0 // 1 diff --git a/gnovm/tests/debug/4a.gno b/gnovm/tests/debug/4a.gno index a94a47ca039..1e3eb9fe815 100644 --- a/gnovm/tests/debug/4a.gno +++ b/gnovm/tests/debug/4a.gno @@ -19,3 +19,5 @@ func main() { } // Output: +// 1 +// 2 diff --git a/gnovm/tests/debug/4b.gno b/gnovm/tests/debug/4b.gno index 0e616d3a91f..4351ceaaf49 100644 --- a/gnovm/tests/debug/4b.gno +++ b/gnovm/tests/debug/4b.gno @@ -20,3 +20,5 @@ func main() { } // Output: +// 3 +// 4 diff --git a/gnovm/tests/debug/4c.gno b/gnovm/tests/debug/4c.gno index fb80f4e8dcb..3e42e207649 100644 --- a/gnovm/tests/debug/4c.gno +++ b/gnovm/tests/debug/4c.gno @@ -23,3 +23,5 @@ func main() { } // Output: +// 1 +// 2 From f331607a06d6b92d6c69ef208488d330656b87ad Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Mon, 8 Jan 2024 21:23:17 +0800 Subject: [PATCH 07/32] fixup --- gnovm/pkg/gnolang/machine.go | 2 +- gnovm/pkg/gnolang/op_exec.go | 3 +- gnovm/pkg/gnolang/preprocess.go | 4 +-- gnovm/pkg/gnolang/transcribe.go | 36 ++++++++++++++++++------ gnovm/pkg/gnolang/values.go | 5 ++++ gnovm/tests/debug/1e.gno | 12 ++++++++ gnovm/tests/debug/recover6a.gno | 40 +++++++++++++++++++++++++++ gnovm/tests/file_test.go | 4 +-- gnovm/tests/files/zrealm0.gno | 1 + gnovm/tests/files/zrealm1.gno | 1 + gnovm/tests/files/zrealm2.gno | 2 ++ gnovm/tests/files/zrealm3.gno | 2 ++ gnovm/tests/files/zrealm4.gno | 2 ++ gnovm/tests/files/zrealm5.gno | 2 ++ gnovm/tests/files/zrealm6.gno | 2 ++ gnovm/tests/files/zrealm7.gno | 2 ++ gnovm/tests/files/zrealm_avl0.gno | 2 ++ gnovm/tests/files/zrealm_avl1.gno | 2 ++ gnovm/tests/files/zrealm_natbind0.gno | 3 ++ gnovm/tests/files/zrealm_tests0.gno | 15 ++++++++++ 20 files changed, 127 insertions(+), 15 deletions(-) create mode 100644 gnovm/tests/debug/1e.gno create mode 100644 gnovm/tests/debug/recover6a.gno diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go index b359572690b..928b83e415f 100644 --- a/gnovm/pkg/gnolang/machine.go +++ b/gnovm/pkg/gnolang/machine.go @@ -718,7 +718,7 @@ func (m *Machine) Eval(x Expr) []TypedValue { // static types and values. func (m *Machine) EvalStatic(last BlockNode, x Expr) TypedValue { if debug { - m.Printf("Machine.EvalStatic(%v, %v)\n", last, x) + //m.Printf("Machine.EvalStatic(%v, %v)\n", last, x) } // X must have been preprocessed. if x.GetAttribute(ATTR_PREPROCESSED) == nil { diff --git a/gnovm/pkg/gnolang/op_exec.go b/gnovm/pkg/gnolang/op_exec.go index 28328236c43..ddb93284638 100644 --- a/gnovm/pkg/gnolang/op_exec.go +++ b/gnovm/pkg/gnolang/op_exec.go @@ -55,7 +55,7 @@ func (m *Machine) doOpExec(op Op) { if debug { debug.Printf("PEEK STMT: %v\n", s) debug.Printf("op: %v\n", op) - debug.Printf("%v\n", m) + //debug.Printf("%v\n", m) } // NOTE this could go in the switch statement, and we could @@ -83,6 +83,7 @@ func (m *Machine) doOpExec(op Op) { s = next goto EXEC_SWITCH } else { + debug.Println("---else") m.ForcePopOp() m.ForcePopStmt() return diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 641263b735a..288a6424dbb 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -294,7 +294,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // TRANS_BLOCK ----------------------- case *IfCaseStmt: - debugPP.Println("-----IfCaseStmt-----") + debug.Println("-----IfCaseStmt-----") pushRealBlock(n, &last, &stack) //pushClosure(&Closure{}) // parent if statement. @@ -302,7 +302,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // anything declared in ifs are copied. for _, n := range ifs.GetBlockNames() { tv := ifs.GetValueRef(nil, n) - debugPP.Printf("tv : %v, *tv: %v \n", tv, *tv) + debug.Printf("tv : %v, *tv: %v \n", tv, *tv) last.Define(n, *tv) } //popClosure() diff --git a/gnovm/pkg/gnolang/transcribe.go b/gnovm/pkg/gnolang/transcribe.go index 2cc6ed8cb6f..1feef8f3818 100644 --- a/gnovm/pkg/gnolang/transcribe.go +++ b/gnovm/pkg/gnolang/transcribe.go @@ -106,7 +106,6 @@ const ( TRANS_FILE_BODY ) -// var closures = &Closures{} var CX *ClosureContext func init() { @@ -118,12 +117,12 @@ type ClosureContext struct { nodes []Node } -//var cnodes []Node - func (cx *ClosureContext) hasClosure() bool { for _, c := range cx.nodes { if _, ok := c.(*FuncLitExpr); ok { return true + } else if _, ok := c.(*FuncDecl); ok { + return true } } return false @@ -147,8 +146,6 @@ func (cx *ClosureContext) push(n Node) bool { func (cx *ClosureContext) pop(copy bool) *Closure { debug.Println("-clo") - debug.Println("before pop, dump") - cx.dumpClosures() defer func() { if len(cx.nodes) != 0 { debug.Println("-node") @@ -166,10 +163,8 @@ func (cx *ClosureContext) pop(copy bool) *Closure { } cx.closures = cx.closures[:len(cx.closures)-1] // shrink - // copy poped to latest, if left at least one closure if copy { currentClo := cx.currentClosure() - debug.Printf("currentClo: %v \n", currentClo) if currentClo != nil { // if last closure, just pop, no copy // fill up current closure for _, cnx := range c.cnxs { // trace back captured nxs @@ -325,7 +320,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc currentClo.Fill(cnx) } CX.dumpClosures() - CX.dumpNodes() + //CX.dumpNodes() } } case *BasicLitExpr: @@ -441,7 +436,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } case *FuncLitExpr: debug.Printf("-----trans, funcLitExpr: %v \n", cnn) - CX.dumpNodes() + //CX.dumpNodes() CX.dumpClosures() cnn.Type = *transcribe(t, nns, TRANS_FUNCLIT_TYPE, 0, &cnn.Type, &c).(*FuncTypeExpr) @@ -816,6 +811,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*SelectCaseStmt) } + pushed := CX.push(cnn) cnn.Comm = transcribe(t, nns, TRANS_SELECTCASE_COMM, 0, cnn.Comm, &c).(Stmt) if isStopOrSkip(nc, c) { return @@ -825,9 +821,15 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc if isBreak(c) { break } else if isStopOrSkip(nc, c) { + if pushed { + CX.pop(true) + } return } } + if pushed { + CX.pop(true) + } case *SendStmt: cnn.Chan = transcribe(t, nns, TRANS_SEND_CHAN, 0, cnn.Chan, &c).(Expr) if isStopOrSkip(nc, c) { @@ -937,14 +939,22 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*FuncDecl) } + + pushed := CX.push(cnn) for idx := range cnn.Body { cnn.Body[idx] = transcribe(t, nns, TRANS_FUNC_BODY, idx, cnn.Body[idx], &c).(Stmt) if isBreak(c) { break } else if isStopOrSkip(nc, c) { + if pushed { + CX.pop(true) + } return } } + if pushed { + CX.pop(true) + } case *ImportDecl: // nothing to do case *ValueDecl: @@ -975,14 +985,22 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*FileNode) } + pushed := CX.push(cnn) + for idx := range cnn.Decls { cnn.Decls[idx] = transcribe(t, nns, TRANS_FILE_BODY, idx, cnn.Decls[idx], &c).(Decl) if isBreak(c) { break } else if isStopOrSkip(nc, c) { + if pushed { + CX.pop(true) + } return } } + if pushed { + CX.pop(true) + } case *ConstExpr, *constTypeExpr: // leaf nodes // These nodes get created by the preprocessor while // leaving the type expression of a composite lit, before diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index 121280287d5..9513e89c120 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -2366,6 +2366,11 @@ func (b *Block) GetPointerTo(store Store, path ValuePath) PointerValue { // 0, it implies that b == uverse, and the condition // would fail as if it were 1. for i := uint8(1); i < path.Depth; i++ { + //if b == nil { + // fmt.Printf("-----GetPointerTo, path : %v\n", path) + // fmt.Printf("b: %v \n", b) + // panic("------b nil, depth is:" + strconv.Itoa(int(path.Depth))) + //} b = b.GetParent(store) } return b.GetPointerToInt(store, int(path.Index)) diff --git a/gnovm/tests/debug/1e.gno b/gnovm/tests/debug/1e.gno new file mode 100644 index 00000000000..b3b2b2b0385 --- /dev/null +++ b/gnovm/tests/debug/1e.gno @@ -0,0 +1,12 @@ +package main + +func main() { + for i := 0; i < 5; i++ { + if i == 1 { + panic("error 1") + } + } +} + +// Error: +// error 1 diff --git a/gnovm/tests/debug/recover6a.gno b/gnovm/tests/debug/recover6a.gno new file mode 100644 index 00000000000..427fc4b74b9 --- /dev/null +++ b/gnovm/tests/debug/recover6a.gno @@ -0,0 +1,40 @@ +package main + +import "errors" + +//func p(s interface{}) { +// fmt.Printf("%T \n", s) +// if v, ok := s.(string); ok { +// println(v) +// panic(v) +// } else { +// println("---") +// } +//} + +type error interface { + Error() string +} + +// New returns an error that formats as the given text. +// Each call to New returns a distinct error value even if the text is identical. +func New(text string) error { + return &errorString{text} +} + +// errorString is a trivial implementation of error. +type errorString struct { + s string +} + +func (e *errorString) Error() string { + return e.s +} + +func main() { + //panic(New("wtf")) + panic(errors.New("wtf")) +} + +// Error: +// wtf diff --git a/gnovm/tests/file_test.go b/gnovm/tests/file_test.go index 9e9de5fa615..ae6c2c7b1f9 100644 --- a/gnovm/tests/file_test.go +++ b/gnovm/tests/file_test.go @@ -34,8 +34,8 @@ func TestFiles(t *testing.T) { func TestDebug(t *testing.T) { baseDir := filepath.Join(".", "debug") - //runFileTests(t, baseDir, []string{"*_native*"}) - runFileTests(t, baseDir, []string{"*_stdlibs*"}, WithNativeLibs()) + runFileTests(t, baseDir, []string{"*_native*"}) + //runFileTests(t, baseDir, []string{"*_stdlibs*"}, WithNativeLibs()) } func TestChallenges(t *testing.T) { diff --git a/gnovm/tests/files/zrealm0.gno b/gnovm/tests/files/zrealm0.gno index 7578781e503..7d8176cc1ec 100644 --- a/gnovm/tests/files/zrealm0.gno +++ b/gnovm/tests/files/zrealm0.gno @@ -48,6 +48,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, diff --git a/gnovm/tests/files/zrealm1.gno b/gnovm/tests/files/zrealm1.gno index d90c5e8621a..9b1066fa9dc 100644 --- a/gnovm/tests/files/zrealm1.gno +++ b/gnovm/tests/files/zrealm1.gno @@ -162,6 +162,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, diff --git a/gnovm/tests/files/zrealm2.gno b/gnovm/tests/files/zrealm2.gno index 67ba2f5a768..7f04d858d87 100644 --- a/gnovm/tests/files/zrealm2.gno +++ b/gnovm/tests/files/zrealm2.gno @@ -165,6 +165,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -201,6 +202,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, diff --git a/gnovm/tests/files/zrealm3.gno b/gnovm/tests/files/zrealm3.gno index da8a581375c..56995c30b5e 100644 --- a/gnovm/tests/files/zrealm3.gno +++ b/gnovm/tests/files/zrealm3.gno @@ -164,6 +164,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -200,6 +201,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, diff --git a/gnovm/tests/files/zrealm4.gno b/gnovm/tests/files/zrealm4.gno index dc3c48c774b..668630ada1c 100644 --- a/gnovm/tests/files/zrealm4.gno +++ b/gnovm/tests/files/zrealm4.gno @@ -106,6 +106,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -142,6 +143,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, diff --git a/gnovm/tests/files/zrealm5.gno b/gnovm/tests/files/zrealm5.gno index e65b089c18d..d90f2c1a4f1 100644 --- a/gnovm/tests/files/zrealm5.gno +++ b/gnovm/tests/files/zrealm5.gno @@ -177,6 +177,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -213,6 +214,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, diff --git a/gnovm/tests/files/zrealm6.gno b/gnovm/tests/files/zrealm6.gno index 20615fa7d39..8e0e02da325 100644 --- a/gnovm/tests/files/zrealm6.gno +++ b/gnovm/tests/files/zrealm6.gno @@ -249,6 +249,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -285,6 +286,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, diff --git a/gnovm/tests/files/zrealm7.gno b/gnovm/tests/files/zrealm7.gno index 9decb0dae10..7e841c3fdca 100644 --- a/gnovm/tests/files/zrealm7.gno +++ b/gnovm/tests/files/zrealm7.gno @@ -321,6 +321,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -357,6 +358,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, diff --git a/gnovm/tests/files/zrealm_avl0.gno b/gnovm/tests/files/zrealm_avl0.gno index e91788ac8eb..191bc41c0f9 100644 --- a/gnovm/tests/files/zrealm_avl0.gno +++ b/gnovm/tests/files/zrealm_avl0.gno @@ -259,6 +259,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -295,6 +296,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, diff --git a/gnovm/tests/files/zrealm_avl1.gno b/gnovm/tests/files/zrealm_avl1.gno index cdd56a5ad89..ab8a22124b7 100644 --- a/gnovm/tests/files/zrealm_avl1.gno +++ b/gnovm/tests/files/zrealm_avl1.gno @@ -283,6 +283,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -319,6 +320,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, diff --git a/gnovm/tests/files/zrealm_natbind0.gno b/gnovm/tests/files/zrealm_natbind0.gno index 60e0d448202..9e9a8afb196 100644 --- a/gnovm/tests/files/zrealm_natbind0.gno +++ b/gnovm/tests/files/zrealm_natbind0.gno @@ -53,6 +53,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -89,6 +90,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -145,6 +147,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, diff --git a/gnovm/tests/files/zrealm_tests0.gno b/gnovm/tests/files/zrealm_tests0.gno index 73d07f726eb..996f1e78349 100644 --- a/gnovm/tests/files/zrealm_tests0.gno +++ b/gnovm/tests/files/zrealm_tests0.gno @@ -235,6 +235,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": null, // "FileName": "tests.gno", // "IsMethod": true, @@ -337,6 +338,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -403,6 +405,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -459,6 +462,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -505,6 +509,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -561,6 +566,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -607,6 +613,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -653,6 +660,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -712,6 +720,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -761,6 +770,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -797,6 +807,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -833,6 +844,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -879,6 +891,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -935,6 +948,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -992,6 +1006,7 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", +// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, From 62003380c2cf5587086f4292af44c4635fa8a64d Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Tue, 9 Jan 2024 08:05:40 +0800 Subject: [PATCH 08/32] fixup --- gnovm/pkg/gnolang/op_call.go | 13 ++-- gnovm/pkg/gnolang/op_eval.go | 6 +- gnovm/pkg/gnolang/op_expressions.go | 31 +++++----- gnovm/pkg/gnolang/transcribe.go | 86 ++++++++++++++++++++------ gnovm/tests/debug/4d.gnoa | 33 ++++++++++ gnovm/tests/debug/4e.gno | 27 ++++++++ gnovm/tests/debug/zregexp_stdlibs.gnoa | 19 ++++++ 7 files changed, 173 insertions(+), 42 deletions(-) create mode 100644 gnovm/tests/debug/4d.gnoa create mode 100644 gnovm/tests/debug/4e.gno create mode 100644 gnovm/tests/debug/zregexp_stdlibs.gnoa diff --git a/gnovm/pkg/gnolang/op_call.go b/gnovm/pkg/gnolang/op_call.go index 96e10400c33..1659cb49f8c 100644 --- a/gnovm/pkg/gnolang/op_call.go +++ b/gnovm/pkg/gnolang/op_call.go @@ -52,7 +52,7 @@ func (m *Machine) doOpPrecall() { var gReturnStmt = &ReturnStmt{} func (m *Machine) doOpCall() { - debug.Println("-----doOpCall-----") + debugPP.Println("-----doOpCall-----") // NOTE: Frame won't be popped until the statement is complete, to // discard the correct number of results for func calls in ExprStmts. fr := m.LastFrame() @@ -62,24 +62,25 @@ func (m *Machine) doOpCall() { numParams := len(pts) isMethod := 0 // 1 if true // Create new block scope. + debugPP.Printf("fv is: %v \n", fv) clo := fr.Func.GetClosure(m.Store) - debug.Printf("-----got closure: %v ----- \n", clo) + debugPP.Printf("-----got closure: %v ----- \n", clo) // update block vars using captured vars captures := fr.Func.Captures if captures == nil { - debug.Println("nil captures") + debugPP.Println("nil captures") } if captures != nil { debug.Printf("captures before call: %v, len(names): %d, len(values): %d \n", *captures, len(captures.names), len(captures.values)) names := clo.GetSource(m.Store).GetBlockNames() - debug.Printf("names: %v \n", names) + debugPP.Printf("names: %v \n", names) for i1, n1 := range captures.names { var index int for i2, n2 := range names { if n1 == n2 { // match and replace index = i2 - debug.Printf("index of %s in target block is: %d \n", n1, index) - debug.Printf("target tv[%d] in captured values is :%v \n", i1, captures.values[i1]) + debugPP.Printf("index of %s in target block is: %d \n", n1, index) + debugPP.Printf("target tv[%d] in captured values is :%v \n", i1, captures.values[i1]) // replace lv values with index clo.UpdateValue(index, captures.values[i1]) } diff --git a/gnovm/pkg/gnolang/op_eval.go b/gnovm/pkg/gnolang/op_eval.go index 790b69807e4..b74638d5c67 100644 --- a/gnovm/pkg/gnolang/op_eval.go +++ b/gnovm/pkg/gnolang/op_eval.go @@ -18,6 +18,8 @@ func (m *Machine) doOpEval() { debug.Printf("EVAL: (%T) %v\n", x, x) // fmt.Println(m.String()) } + + debugPP.Printf("EVAL: (%T) %v\n", x, x) // This case moved out of switch for performance. // TODO: understand this better. if nx, ok := x.(*NameExpr); ok { @@ -306,9 +308,9 @@ func (m *Machine) doOpEval() { m.PushExpr(x.Type) m.PushOp(OpEval) case *FuncLitExpr: - debug.Println("-----FuncLitExpr") + debugPP.Println("-----FuncLitExpr") b := m.LastBlock() - debug.Printf("b: %v \n", b) + debugPP.Printf("b: %v \n", b) m.PushOp(OpFuncLit) m.PushOp(OpPreFuncLit) // evaluate func type diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index 28e047ba128..58895b5b24c 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -682,11 +682,11 @@ type closureObject struct { } func (m *Machine) doOpPreFuncLit() { - debug.Println("-----doOpPreFuncLit") + debugPP.Println("-----doOpPreFuncLit") x := m.PeekExpr(1).(*FuncLitExpr) - debug.Printf("funcLitExpr: %v \n", x) + debugPP.Printf("funcLitExpr: %v \n", x) //debug.Printf("pointer of x.Closure: %p \n", x.Closure) - debug.Printf("x.Closure: %v \n", x.Closure) + debugPP.Printf("x.Closure: %v \n", x.Closure) lb := m.LastBlock() b := m.Alloc.NewBlock(x, lb) m.PushBlock(b) @@ -694,20 +694,23 @@ func (m *Machine) doOpPreFuncLit() { closure := x.Closure if closure != nil { - debug.Printf("closure nxs len is: %d \n", len(closure.cnxs)) + debugPP.Printf("closure nxs len is: %d \n", len(closure.cnxs)) for i, cnx := range closure.cnxs { - debug.Printf("closure[%d]: %v \n", i, cnx) + debugPP.Printf("closure[%d]: %v \n", i, cnx) offset := cnx.offset - debug.Printf("offset of %s is %d \n", cnx.nx.Name, offset) + debugPP.Printf("offset of %s is %d \n", cnx.nx.Name, offset) vp := cnx.nx.Path - debug.Printf("vp of nx[%s] is : %v \n", cnx.nx.Name, vp) + debugPP.Printf("vp of nx[%s] is : %v \n", cnx.nx.Name, vp) + if (vp.Depth + 1) < offset { + panic("---incorrect offset for:" + cnx.nx.Name) + } nvp := ValuePath{ Name: cnx.nx.Name, Depth: vp.Depth - offset + 1, Index: vp.Index, Type: vp.Type, } - debug.Printf("nvp of nx[%s] is : %v \n", nvp.Name, nvp) + debugPP.Printf("nvp of nx[%s] is : %v \n", nvp.Name, nvp) nnx := *cnx.nx nnx.Path = nvp m.PushExpr(&nnx) @@ -717,26 +720,26 @@ func (m *Machine) doOpPreFuncLit() { } func (m *Machine) doOpFuncLit() { - debug.Println("-----doOpFuncLit") + debugPP.Println("-----doOpFuncLit") x := m.PopExpr().(*FuncLitExpr) lb := m.LastBlock() - debug.Printf("lb: %v \n", lb) + debugPP.Printf("lb: %v \n", lb) captured := &Captured{} if x.Closure != nil { for _, cnx := range x.Closure.cnxs { - debug.Printf("range closures of : %s \n", string(cnx.nx.Name)) + debugPP.Printf("range closures of : %s \n", string(cnx.nx.Name)) v := *m.PopValue() if !v.IsUndefined() { // e.g. args of func. forward - debug.Printf("capturing v: %v \n", v) + debugPP.Printf("capturing v: %v \n", v) captured.names = append(captured.names, cnx.nx.Name) captured.values = append(captured.values, v) } else { - debug.Println("undefined, skip") + debugPP.Println("undefined, skip") } } } - debug.Printf("---captured: %v \n", captured) + debugPP.Printf("---captured: %v \n", captured) ft := m.PopValue().V.(TypeValue).Type.(*FuncType) diff --git a/gnovm/pkg/gnolang/transcribe.go b/gnovm/pkg/gnolang/transcribe.go index 1feef8f3818..d8321d04762 100644 --- a/gnovm/pkg/gnolang/transcribe.go +++ b/gnovm/pkg/gnolang/transcribe.go @@ -109,21 +109,42 @@ const ( var CX *ClosureContext func init() { - CX = &ClosureContext{} + CX = &ClosureContext{localNx: make(map[Name]bool)} } type ClosureContext struct { closures []*Closure nodes []Node + ops []Word + localNx map[Name]bool +} + +func (cx *ClosureContext) pushOp(op Word) { + cx.ops = append(cx.ops, op) +} + +func (cx *ClosureContext) popOp() { + if len(cx.ops) != 0 { + cx.ops = cx.ops[:len(cx.ops)-1] + } +} + +func (cx *ClosureContext) currentOp() Word { + if len(cx.ops) != 0 { + return cx.ops[len(cx.ops)-1] + } else { + return ILLEGAL + } } func (cx *ClosureContext) hasClosure() bool { for _, c := range cx.nodes { if _, ok := c.(*FuncLitExpr); ok { return true - } else if _, ok := c.(*FuncDecl); ok { - return true } + //else if _, ok := c.(*FuncDecl); ok { + // return true + //} } return false } @@ -156,6 +177,9 @@ func (cx *ClosureContext) pop(copy bool) *Closure { if len(cx.closures) == 0 { return nil } else { + if len(cx.closures) == 1 { // last one, clean context + cx.localNx = make(map[Name]bool) + } c := cx.closures[len(cx.closures)-1] // get current for _, cnx := range c.cnxs { // pop-> increase offset debug.Printf("+1 \n") @@ -304,15 +328,24 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc // visit any children of n. switch cnn := nn.(type) { case *NameExpr: - debug.Printf("-----trans, nameExpr: %v \n", cnn) + debugPP.Printf("-----trans, nameExpr: %v \n", cnn) // TODO: do we need to filter out already define in current block - // if nested closure, do not copy! + if CX.hasClosure() { - debug.Printf("---has Closure") + debugPP.Println("---has Closure") + debugPP.Println("---currentOp: ", CX.currentOp()) + // always recording + if CX.currentOp() == DEFINE || CX.currentOp() == ASSIGN { + // record local defined + CX.localNx[cnn.Name] = true + } // if nested closure, do not copy! + currentClo := CX.currentClosure() - debug.Printf("currentClo: %v \n", currentClo) + debugPP.Printf("currentClo: %v \n", currentClo) if currentClo != nil { // a closure to fill - if cnn.Path.Depth > 1 { // if local defined, no capture + //if cnn.Path.Depth < 1 { // if local defined, no capture + if !CX.localNx[cnn.Name] { + debugPP.Printf("---capture: %v \n", cnn) cnx := CapturedNx{ nx: cnn, offset: 0, @@ -453,6 +486,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc debug.Println("---start trans funcLit body stmt, push initial closure and fx") pushed := CX.push(cnn) + //var pushed bool debug.Printf("---stop or skip, pop and return \n") node := CX.peekNodes(1) @@ -565,6 +599,9 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } case *AssignStmt: + if cnn.Op == DEFINE { + CX.pushOp(cnn.Op) + } for idx := range cnn.Lhs { cnn.Lhs[idx] = transcribe(t, nns, TRANS_ASSIGN_LHS, idx, cnn.Lhs[idx], &c).(Expr) if isBreak(c) { @@ -581,6 +618,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } } + CX.popOp() case *BlockStmt: cnn2, c2 := t(ns, ftype, index, cnn, TRANS_BLOCK) if isStopOrSkip(nc, c2) { @@ -606,6 +644,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } case *BranchStmt: case *DeclStmt: + //CX.pushOp(ASSIGN) for idx := range cnn.Body { cnn.Body[idx] = transcribe(t, nns, TRANS_DECL_BODY, idx, cnn.Body[idx], &c).(SimpleDeclStmt) if isBreak(c) { @@ -614,6 +653,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } } + //CX.popOp() case *DeferStmt: cnn.Call = *transcribe(t, nns, TRANS_DEFER_CALL, 0, &cnn.Call, &c).(*CallExpr) if isStopOrSkip(nc, c) { @@ -634,7 +674,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc cnn = cnn2.(*ForStmt) } - pushed := CX.push(cnn) if cnn.Init != nil { cnn.Init = transcribe(t, nns, TRANS_FOR_INIT, 0, cnn.Init, &c).(SimpleStmt) if isStopOrSkip(nc, c) { @@ -653,6 +692,9 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } } + + pushed := CX.push(cnn) + for idx := range cnn.Body { cnn.Body[idx] = transcribe(t, nns, TRANS_FOR_BODY, idx, cnn.Body[idx], &c).(Stmt) if isBreak(c) { @@ -685,7 +727,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc cnn = cnn2.(*IfStmt) } - pushed := CX.push(cnn) if cnn.Init != nil { cnn.Init = transcribe(t, nns, TRANS_IF_INIT, 0, cnn.Init, &c).(SimpleStmt) if isStopOrSkip(nc, c) { @@ -697,6 +738,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } + pushed := CX.push(cnn) cnn.Then = *transcribe(t, nns, TRANS_IF_BODY, 0, &cnn.Then, &c).(*IfCaseStmt) if isStopOrSkip(nc, c) { return @@ -742,32 +784,33 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*RangeStmt) } - pushed := CX.push(cnn) cnn.X = transcribe(t, nns, TRANS_RANGE_X, 0, cnn.X, &c).(Expr) if isStopOrSkip(nc, c) { - if pushed { - CX.pop(true) - } + //if pushed { + // CX.pop(true) + //} return } + if cnn.Key != nil { cnn.Key = transcribe(t, nns, TRANS_RANGE_KEY, 0, cnn.Key, &c).(Expr) if isStopOrSkip(nc, c) { - if pushed { - CX.pop(true) - } + //if pushed { + // CX.pop(true) + //} return } } if cnn.Value != nil { cnn.Value = transcribe(t, nns, TRANS_RANGE_VALUE, 0, cnn.Value, &c).(Expr) if isStopOrSkip(nc, c) { - if pushed { - CX.pop(true) - } + //if pushed { + // CX.pop(true) + //} return } } + pushed := CX.push(cnn) for idx := range cnn.Body { cnn.Body[idx] = transcribe(t, nns, TRANS_RANGE_BODY, idx, cnn.Body[idx], &c).(Stmt) if isBreak(c) { @@ -958,6 +1001,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc case *ImportDecl: // nothing to do case *ValueDecl: + CX.pushOp(ASSIGN) if cnn.Type != nil { cnn.Type = transcribe(t, nns, TRANS_VAR_TYPE, 0, cnn.Type, &c).(Expr) if isStopOrSkip(nc, c) { @@ -972,6 +1016,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } } + CX.popOp() case *TypeDecl: cnn.Type = transcribe(t, nns, TRANS_TYPE_TYPE, 0, cnn.Type, &c).(Expr) if isStopOrSkip(nc, c) { @@ -985,6 +1030,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*FileNode) } + pushed := CX.push(cnn) for idx := range cnn.Decls { diff --git a/gnovm/tests/debug/4d.gnoa b/gnovm/tests/debug/4d.gnoa new file mode 100644 index 00000000000..4a8c1cba970 --- /dev/null +++ b/gnovm/tests/debug/4d.gnoa @@ -0,0 +1,33 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + y := 1 + b := true + f := func() int { + switch y { + case 1: + if b { + z := 5 + for j := 0; j < z; j++ { + x += 1 + } + } + default: + x += 0 + } + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 5 +// 6 diff --git a/gnovm/tests/debug/4e.gno b/gnovm/tests/debug/4e.gno new file mode 100644 index 00000000000..17490510b26 --- /dev/null +++ b/gnovm/tests/debug/4e.gno @@ -0,0 +1,27 @@ +package main + +type queueOnePass struct { + sparse []uint32 + dense []uint32 + size, nextIndex uint32 +} + +func newQueue(size int) (q *queueOnePass) { + return &queueOnePass{ + sparse: make([]uint32, size), + dense: make([]uint32, size), + } +} +func main() { + var ( + visitQueue = newQueue(10) + ) + f := func() { + println(visitQueue.size) + } + + f() +} + +// Output: +// 0 diff --git a/gnovm/tests/debug/zregexp_stdlibs.gnoa b/gnovm/tests/debug/zregexp_stdlibs.gnoa new file mode 100644 index 00000000000..10bb6f937d3 --- /dev/null +++ b/gnovm/tests/debug/zregexp_stdlibs.gnoa @@ -0,0 +1,19 @@ +// MAXALLOC: 100000000 +// max total allocation of 100 mb. +package main + +import "regexp" + +var reName = regexp.MustCompile(`^[a-z]+[_a-z0-9]*$`) + +func main() { + for j := 0; j < 100; j++ { + if !(reName.MatchString("thisisatestname")) { + panic("error") + } + } + println(true) +} + +// Output: +// true From cf84a1908174e80a53a47c57fd49e4c142e686f3 Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Wed, 10 Jan 2024 18:44:48 +0800 Subject: [PATCH 09/32] fixup --- gnovm/pkg/gnolang/machine.go | 10 + gnovm/pkg/gnolang/op_assign.go | 69 +++++ gnovm/pkg/gnolang/op_call.go | 9 +- gnovm/pkg/gnolang/op_exec.go | 8 +- gnovm/pkg/gnolang/op_expressions.go | 44 +-- gnovm/pkg/gnolang/transcribe.go | 267 ++++++++++++++---- gnovm/pkg/gnolang/values.go | 29 +- gnovm/tests/debug/4d.gnoa | 33 --- gnovm/tests/debug/4f.gno | 20 ++ gnovm/tests/debug/4g.gno | 14 + gnovm/tests/debug/4h.gno | 25 ++ gnovm/tests/debug/4i.gno | 15 + gnovm/tests/debug/5.gno | 22 ++ gnovm/tests/debug/6.gno | 23 ++ gnovm/tests/debug/6a.gno | 24 ++ gnovm/tests/debug/7.gno | 25 ++ gnovm/tests/debug/7a.gno | 28 ++ ...egexp_stdlibs.gnoa => zregexp_stdlibs.gno} | 0 18 files changed, 559 insertions(+), 106 deletions(-) create mode 100644 gnovm/tests/debug/4f.gno create mode 100644 gnovm/tests/debug/4g.gno create mode 100644 gnovm/tests/debug/4h.gno create mode 100644 gnovm/tests/debug/4i.gno create mode 100644 gnovm/tests/debug/5.gno create mode 100644 gnovm/tests/debug/6.gno create mode 100644 gnovm/tests/debug/6a.gno create mode 100644 gnovm/tests/debug/7.gno create mode 100644 gnovm/tests/debug/7a.gno rename gnovm/tests/debug/{zregexp_stdlibs.gnoa => zregexp_stdlibs.gno} (100%) diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go index 928b83e415f..79d7b499e16 100644 --- a/gnovm/pkg/gnolang/machine.go +++ b/gnovm/pkg/gnolang/machine.go @@ -926,6 +926,8 @@ const ( OpMaybeNativeType Op = 0x79 // maybenative{X} /* Statement operators */ + OpPreAssign Op = 0x92 + OpPostAssign Op = 0x93 OpAssign Op = 0x80 // Lhs = Rhs OpAddAssign Op = 0x81 // Lhs += Rhs OpSubAssign Op = 0x82 // Lhs -= Rhs @@ -1061,7 +1063,9 @@ const ( OpCPUMaybeNativeType = 1 /* Statement operators */ + OpCPUPreAssign = 1 OpCPUAssign = 1 + OpCPUPostAssign = 1 OpCPUAddAssign = 1 OpCPUSubAssign = 1 OpCPUMulAssign = 1 @@ -1347,9 +1351,15 @@ func (m *Machine) Run() { m.incrCPU(OpCPUMaybeNativeType) m.doOpMaybeNativeType() /* Statement operators */ + case OpPreAssign: + m.incrCPU(OpCPUPreAssign) + m.doOpPreAssign() case OpAssign: m.incrCPU(OpCPUAssign) m.doOpAssign() + case OpPostAssign: + m.incrCPU(OpCPUPostAssign) + m.doOpPostAssign() case OpAddAssign: m.incrCPU(OpCPUAddAssign) m.doOpAddAssign() diff --git a/gnovm/pkg/gnolang/op_assign.go b/gnovm/pkg/gnolang/op_assign.go index 4388fc522ef..2134e63096c 100644 --- a/gnovm/pkg/gnolang/op_assign.go +++ b/gnovm/pkg/gnolang/op_assign.go @@ -30,12 +30,49 @@ func (m *Machine) doOpDefine() { } } +func (m *Machine) doOpPreAssign() { + debugPP.Println("---doOpPreAssign") + // check rhs type + s := m.PeekStmt1().(*AssignStmt) + + // TODO: this is expensive, should quick return + + var ln Name + if n, ok := s.Lhs[0].(*NameExpr); ok { + debugPP.Printf("name of lhs is: %s \n", n.Name) + ln = n.Name + } + + debugPP.Printf("s peeked: %v, len of RHS: %d \n", s, len(s.Rhs)) + // rhs is funcLitExpr, set a flag, to record lhs and use it update captured in postAssign + if fx, ok := s.Rhs[0].(*FuncLitExpr); ok { + debugPP.Printf("fx: %v, closure: %v \n", fx, fx.Closure) + if fx.Closure != nil { + if len(fx.Closure.names) > 0 { + for _, n := range fx.Closure.names { + if n == ln { // contains + //if fx.Closure.names[0] == ln { + debugPP.Println("-----recursive closure") + //fx.Closure.recursive = true + fx.Closure = nil // no support for recursive closure + } + } + } + } + } + //m.PushValue(typedString("x")) + + debugPP.Println("---end") +} + func (m *Machine) doOpAssign() { s := m.PopStmt().(*AssignStmt) + debugPP.Printf("---doOpAssign, s: %v \n", s) // Assign each value evaluated for Lhs. // NOTE: PopValues() returns a slice in // forward order, not the usual reverse. rvs := m.PopValues(len(s.Lhs)) + for i := len(s.Lhs) - 1; 0 <= i; i-- { // Pop lhs value and desired type. lv := m.PopAsPointer(s.Lhs[i]) @@ -49,6 +86,38 @@ func (m *Machine) doOpAssign() { } lv.Assign2(m.Alloc, m.Store, m.Realm, rvs[i], true) } + + //// after assign, get lhs, when recursive closure + //if len(s.Rhs) == 1 { + // // push evaluated v + // if fx, ok := s.Rhs[0].(*FuncLitExpr); ok { + // debugPP.Printf("---fx.closure: %v \n", fx.Closure) + // if fx.Closure.recursive { + // // is recursive, push value + // //m.PushValue(rvs[0]) + // rx := s.Rhs[0] + // // evaluate Rhs + // m.PushExpr(rx) + // m.PushOp(OpEval) + // //m.PushOp(OpFuncLit) // eval again to update captured nx + // } + // } else { + // //m.PushValue(typedString("x")) + // } + //} +} + +func (m *Machine) doOpPostAssign() { + debugPP.Println("---doOpPostAssign") + //// get lhs name and value + //x := m.PopExpr() + //// use this value to replace captured, in funcLit + //v := m.PopValue() + //debugPP.Printf("pop x: %v \n", x) + //debugPP.Printf("pop value: %v \n", v) + //if fx, ok := x.(*FuncLitExpr); ok { + // //fx.Closure + //} } func (m *Machine) doOpAddAssign() { diff --git a/gnovm/pkg/gnolang/op_call.go b/gnovm/pkg/gnolang/op_call.go index 1659cb49f8c..ae9fa288d76 100644 --- a/gnovm/pkg/gnolang/op_call.go +++ b/gnovm/pkg/gnolang/op_call.go @@ -62,7 +62,11 @@ func (m *Machine) doOpCall() { numParams := len(pts) isMethod := 0 // 1 if true // Create new block scope. - debugPP.Printf("fv is: %v \n", fv) + debugPP.Printf("fv is:---") + fv.dump() + debugPP.Printf("fv.captures is: %v \n", fv.Captures) + debugPP.Printf("fv.address is: %p \n", fv) + clo := fr.Func.GetClosure(m.Store) debugPP.Printf("-----got closure: %v ----- \n", clo) // update block vars using captured vars @@ -71,7 +75,7 @@ func (m *Machine) doOpCall() { debugPP.Println("nil captures") } if captures != nil { - debug.Printf("captures before call: %v, len(names): %d, len(values): %d \n", *captures, len(captures.names), len(captures.values)) + debugPP.Printf("captures before call: %v, len(names): %d, len(values): %d \n", captures, len(captures.names), len(captures.values)) names := clo.GetSource(m.Store).GetBlockNames() debugPP.Printf("names: %v \n", names) for i1, n1 := range captures.names { @@ -89,6 +93,7 @@ func (m *Machine) doOpCall() { } // only need initial snapshot fr.Func.Captures = nil + //fr.Func.Captures = Captured{} b := m.Alloc.NewBlock(fr.Func.GetSource(m.Store), clo) m.PushBlock(b) diff --git a/gnovm/pkg/gnolang/op_exec.go b/gnovm/pkg/gnolang/op_exec.go index ddb93284638..7dce99a5d11 100644 --- a/gnovm/pkg/gnolang/op_exec.go +++ b/gnovm/pkg/gnolang/op_exec.go @@ -438,10 +438,14 @@ EXEC_SWITCH: // TODO: add case log for debug switch cs := s.(type) { case *AssignStmt: - debug.Println("-----AssignStmt") + debugPP.Printf("-----AssignStmt: %v \n", cs) switch cs.Op { case ASSIGN: + // post assign, use value of lhs to update captured value, name as ID + //m.PushOp(OpPostAssign) m.PushOp(OpAssign) + // pre assign, check rhs is funcLitExpr + //m.PushOp(OpPreAssign) case ADD_ASSIGN: m.PushOp(OpAddAssign) case SUB_ASSIGN: @@ -479,6 +483,8 @@ EXEC_SWITCH: m.PushExpr(rx) m.PushOp(OpEval) } + m.PushOp(OpPreAssign) + if cs.Op != DEFINE { // For each Lhs, push eval operation if needed. for i := len(cs.Lhs) - 1; 0 <= i; i-- { diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index 58895b5b24c..5f8f2c49e90 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -677,10 +677,6 @@ func (m *Machine) doOpStructLit() { }) } -type closureObject struct { - data map[string]interface{} -} - func (m *Machine) doOpPreFuncLit() { debugPP.Println("-----doOpPreFuncLit") x := m.PeekExpr(1).(*FuncLitExpr) @@ -730,10 +726,19 @@ func (m *Machine) doOpFuncLit() { for _, cnx := range x.Closure.cnxs { debugPP.Printf("range closures of : %s \n", string(cnx.nx.Name)) v := *m.PopValue() - if !v.IsUndefined() { // e.g. args of func. forward + debugPP.Printf("v got is: %v \n", v) + + //c := *m.PopValue() + //debugPP.Printf("---c is: %v \n", c) + + if !v.IsUndefined() { // e.g. args of func. forward // TODO: consider this + //if v.V != nil { // TODO: consider this, this only happens when recursive closure debugPP.Printf("capturing v: %v \n", v) + debugPP.Printf("captured before update: \n %s \n", captured) captured.names = append(captured.names, cnx.nx.Name) captured.values = append(captured.values, v) + debugPP.Printf("captured after update: \n %s \n", captured) + //} } else { debugPP.Println("undefined, skip") } @@ -745,19 +750,26 @@ func (m *Machine) doOpFuncLit() { m.Alloc.AllocateFunc() + fv := &FuncValue{ + Type: ft, + IsMethod: false, + Source: x, + Name: "", + Closure: lb, + Captures: captured, + PkgPath: m.Package.PkgPath, + body: x.Body, + nativeBody: nil, + } + + debugPP.Printf("fv is:------") + fv.dump() + debugPP.Printf("fv.captures is: %v \n", fv.Captures) + debugPP.Printf("fv.address is: %p \n", fv) + m.PushValue(TypedValue{ T: ft, - V: &FuncValue{ - Type: ft, - IsMethod: false, - Source: x, - Name: "", - Closure: lb, - Captures: captured, - PkgPath: m.Package.PkgPath, - body: x.Body, - nativeBody: nil, - }}) + V: fv}) } func (m *Machine) doOpConvert() { diff --git a/gnovm/pkg/gnolang/transcribe.go b/gnovm/pkg/gnolang/transcribe.go index d8321d04762..f595b31fd93 100644 --- a/gnovm/pkg/gnolang/transcribe.go +++ b/gnovm/pkg/gnolang/transcribe.go @@ -106,17 +106,91 @@ const ( TRANS_FILE_BODY ) +// rules of closure capture +// closure captures namedExprs(nx) +// closure captures nx when: +// 1. defined outside the closure block, which implies nxs defined inside do not need to be captured, +// it's evaluated naturally. +// 2. nx defined outside closure block are not all captured. it's captured only when it's volatile +// e.g. a nx if mutated when the outside block is rangeStmt, for stmt, which dynamically +// mutating the target nx. namely, any case you can not get a final value of the nx, you should +// capture it. any time it's deemed final, no need to capture. + +// FLOW: +// 1. start capture work when peeking funcLitExpr +// 2. do the capture work when traversing to nameExpr, check `hasClosure` to determine if capture needed +// 3. exclude any nxs that is not eligible: +// a. defined locally +// this is something tricky +// one method is to give every nx an absolute level,so any nx can be compared by the `level` +// with the level of the closure block to determine if it's defined inside or outside of the +// closure block. +// a second method is by checking the operators of `define` and `assign`, to filter out locally +// defined nxs. TODO: is it doable? +// ###whitelist is a collect of names to be whitelisted while capturing nxs, it contains: +// 1. params and results of funcLitExpr, +// 2. LHS of := and = +// 3. how about RHS? rhs can be either literals, or nxs(locally or not), it is defined locally, +// it's bypassed by rule2. if is not local, capture it. so what we need is LHS. +// FLOW of whitelist: +// everytime encounter assign(assignStmt, init of if/range/for/switch stmt), push the whitelist +// map with key of operator(define, assign), with value of assignStruct +// while in traversing nx, first peek operator, the `check` the corresponding nx according to the +// proper num set previously, the number should be counted, if counts to `num`, pop the operator, +// implies an end for a assign/define stmt. +// what the `check` does is to put the name of nx into the names, which is used to filter nxs. + +// b. final nx. TODO: this left a todo +// it's can be done by traversing parent blocks, similar as `hasClosure`, to determine if there +// is volatile blocks outside + var CX *ClosureContext func init() { - CX = &ClosureContext{localNx: make(map[Name]bool)} + CX = &ClosureContext{whitelist: make(map[Name]bool)} +} + +// AssignOperand is metadata to describe how much (left)nxs is related to an assign/define operator +type AssignOperand struct { + num int // num of lhs + counter int // counter increased every time checked in traversing nx, if counter == num, mean it's resolved, and pop operator +} + +func (ao *AssignOperand) String() string { + var s string + s += fmt.Sprintf("assign operand num: %d \n", ao.num) + s += fmt.Sprintf("assign operand counter: %d \n", ao.counter) + return s +} + +type CNode struct { + name Name + n Node } type ClosureContext struct { closures []*Closure nodes []Node - ops []Word - localNx map[Name]bool + ops []Word // assign/define related logic + operands []*AssignOperand // assign/define related logic, per operator, e.g. a, b := 0, 1 + //rc []*RecursiveContext // detect cyclic, to find recursive closure, could converge with `nodes` + whitelist map[Name]bool // use to filter out nxs +} + +func (cx *ClosureContext) clearWhiteList() { + debugPP.Println("clear whitelist") + cx.whitelist = make(map[Name]bool) + //cx.popOp() + //cx.popOperand() +} + +func (cx *ClosureContext) dumpWhitelist() string { + var s string + s += "===whitelist=== \n" + for n, _ := range cx.whitelist { + s += fmt.Sprintf("name is: %v \n", n) + } + return s } func (cx *ClosureContext) pushOp(op Word) { @@ -129,7 +203,7 @@ func (cx *ClosureContext) popOp() { } } -func (cx *ClosureContext) currentOp() Word { +func (cx *ClosureContext) peekOp() Word { if len(cx.ops) != 0 { return cx.ops[len(cx.ops)-1] } else { @@ -137,20 +211,58 @@ func (cx *ClosureContext) currentOp() Word { } } +func (cx *ClosureContext) popOperand() { + if len(cx.operands) != 0 { + cx.operands = cx.operands[:len(cx.operands)-1] + } +} + +func (cx *ClosureContext) peekOperand() *AssignOperand { + if len(cx.operands) != 0 { + return cx.operands[len(cx.operands)-1] + } else { + return nil + } +} + +func (cx *ClosureContext) dumpOps() string { + var s string + s += "\n" + for _, o := range cx.ops { + s += fmt.Sprintf("op: %v \n", o) + } + return s +} + +// 1. has funcLitExpr +// 2. it's embedded in another volatile block, for/range stmt +// 2. no recursive closure(not support for now) func (cx *ClosureContext) hasClosure() bool { - for _, c := range cx.nodes { - if _, ok := c.(*FuncLitExpr); ok { + for _, cn := range cx.nodes { + if _, ok := cn.(*FuncLitExpr); ok { return true } //else if _, ok := c.(*FuncDecl); ok { // return true //} } + + // detect cyclic + // 1. encounter a nx, its type is funcLitExpr, name it t; how to get it? + // 2. compare t with parent node type, could be direct ancestor, or indirect + // namely, if two(or more) same funcLitExpr appears in stack, have cyclic + // which implies !hasClosure + return false } func (cx *ClosureContext) push(n Node) bool { + // push nodes stack + //cn := &CNode{ + // n: n, + //} cx.nodes = append(cx.nodes, n) + // push closure if cx.hasClosure() { debug.Println("+clo") debug.Println("before push closure") @@ -178,7 +290,7 @@ func (cx *ClosureContext) pop(copy bool) *Closure { return nil } else { if len(cx.closures) == 1 { // last one, clean context - cx.localNx = make(map[Name]bool) + cx.whitelist = make(map[Name]bool) } c := cx.closures[len(cx.closures)-1] // get current for _, cnx := range c.cnxs { // pop-> increase offset @@ -253,8 +365,9 @@ func (cnx *CapturedNx) String() string { // captured NameExpr type Closure struct { - names []Name - cnxs []*CapturedNx + names []Name + cnxs []*CapturedNx + recursive bool } func (c *Closure) String() string { @@ -329,31 +442,44 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc switch cnn := nn.(type) { case *NameExpr: debugPP.Printf("-----trans, nameExpr: %v \n", cnn) - // TODO: do we need to filter out already define in current block - if CX.hasClosure() { - debugPP.Println("---has Closure") - debugPP.Println("---currentOp: ", CX.currentOp()) - // always recording - if CX.currentOp() == DEFINE || CX.currentOp() == ASSIGN { - // record local defined - CX.localNx[cnn.Name] = true - } // if nested closure, do not copy! - - currentClo := CX.currentClosure() - debugPP.Printf("currentClo: %v \n", currentClo) - if currentClo != nil { // a closure to fill - //if cnn.Path.Depth < 1 { // if local defined, no capture - if !CX.localNx[cnn.Name] { + debugPP.Printf("---has Closure, check: %v \n", cnn) + debugPP.Println("---currentOp: ", CX.peekOp()) + debugPP.Println("---dump ops: ", CX.dumpOps()) + // recording names defined in closure as a whitelist + if CX.peekOp() == DEFINE || CX.peekOp() == ASSIGN { + ao := CX.peekOperand() + if ao != nil { // staff to do + debugPP.Printf("ao is: %v \n", ao) + // in scope of define/assign op, record to whitelist + ao.counter += 1 + // add nx to whitelist until resolved + CX.whitelist[cnn.Name] = true + debugPP.Println(CX.dumpWhitelist()) + if ao.counter == ao.num { // all resolved + //CX.clearWhiteList() + CX.popOp() + CX.popOperand() + } + } + } + // capture logic + // not exist in whitelist, capture + if _, ok := CX.whitelist[cnn.Name]; !ok { + debugPP.Printf("nx need capture: %s \n", string(cnn.Name)) + currentClo := CX.currentClosure() + debugPP.Printf("currentClo: %v \n", currentClo) + if currentClo != nil { // a closure to fill + //if cnn.Path.Depth < 1 { // if local defined, no capture debugPP.Printf("---capture: %v \n", cnn) cnx := CapturedNx{ nx: cnn, offset: 0, } currentClo.Fill(cnx) + CX.dumpClosures() + //CX.dumpNodes() } - CX.dumpClosures() - //CX.dumpNodes() } } case *BasicLitExpr: @@ -483,12 +609,18 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*FuncLitExpr) } - - debug.Println("---start trans funcLit body stmt, push initial closure and fx") + // TODO: get all param and result names to filter out captured nxs + // whitelist + for i, n := range cnn.Names { + debugPP.Printf("name[%d] in staticBlock is: %s \n", i, string(n)) + // put in whitelist, which is per traverse + CX.whitelist[n] = true + } + debugPP.Println("---start trans funcLit body stmt, push initial closure and fx") pushed := CX.push(cnn) //var pushed bool - debug.Printf("---stop or skip, pop and return \n") + debugPP.Printf("---stop or skip, pop and return \n") node := CX.peekNodes(1) isCopy := true if _, ok := node.(*FuncLitExpr); ok { @@ -508,15 +640,15 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } } // defer pop - debug.Printf("---done trans body \n") + debugPP.Printf("---done trans body \n") // TODO: set fx.Closure, and level as well - debug.Println("funcLit pop c-----") + debugPP.Println("funcLit pop c-----") if pushed { pc := CX.pop(isCopy) if pc != nil { cnn.SetClosure(pc) - debug.Printf("---done FuncLit trans, fx: %v, closure: %+v \n", cnn, cnn.Closure.String()) + debugPP.Printf("---done FuncLit trans, fx: %v, closure: %+v \n", cnn, cnn.Closure.String()) } } case *FieldTypeExpr: @@ -599,8 +731,14 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } case *AssignStmt: - if cnn.Op == DEFINE { - CX.pushOp(cnn.Op) + debugPP.Printf("---assignStmt: %v \n", cnn) + if CX.hasClosure() { + debugPP.Println("---push op and operands") + // push op(assign/define) and operands + CX.ops = append(CX.ops, cnn.Op) + ao := &AssignOperand{} + ao.num = len(cnn.Lhs) + CX.operands = append(CX.operands, ao) } for idx := range cnn.Lhs { cnn.Lhs[idx] = transcribe(t, nns, TRANS_ASSIGN_LHS, idx, cnn.Lhs[idx], &c).(Expr) @@ -618,7 +756,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } } - CX.popOp() case *BlockStmt: cnn2, c2 := t(ns, ftype, index, cnn, TRANS_BLOCK) if isStopOrSkip(nc, c2) { @@ -674,6 +811,8 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc cnn = cnn2.(*ForStmt) } + pushed := CX.push(cnn) + if cnn.Init != nil { cnn.Init = transcribe(t, nns, TRANS_FOR_INIT, 0, cnn.Init, &c).(SimpleStmt) if isStopOrSkip(nc, c) { @@ -693,8 +832,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } } - pushed := CX.push(cnn) - for idx := range cnn.Body { cnn.Body[idx] = transcribe(t, nns, TRANS_FOR_BODY, idx, cnn.Body[idx], &c).(Stmt) if isBreak(c) { @@ -727,6 +864,9 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc cnn = cnn2.(*IfStmt) } + pushed := CX.push(cnn) + + // nx in init is always treat defined locally if cnn.Init != nil { cnn.Init = transcribe(t, nns, TRANS_IF_INIT, 0, cnn.Init, &c).(SimpleStmt) if isStopOrSkip(nc, c) { @@ -738,7 +878,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } - pushed := CX.push(cnn) cnn.Then = *transcribe(t, nns, TRANS_IF_BODY, 0, &cnn.Then, &c).(*IfCaseStmt) if isStopOrSkip(nc, c) { return @@ -777,6 +916,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } case *RangeStmt: + debugPP.Printf("---range stmt: %v \n", cnn) cnn2, c2 := t(ns, ftype, index, cnn, TRANS_BLOCK) if isStopOrSkip(nc, c2) { nn = cnn2 @@ -784,33 +924,44 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*RangeStmt) } + + pushed := CX.push(cnn) + cnn.X = transcribe(t, nns, TRANS_RANGE_X, 0, cnn.X, &c).(Expr) if isStopOrSkip(nc, c) { - //if pushed { - // CX.pop(true) - //} + if pushed { + CX.pop(true) + } return } + if CX.hasClosure() { // TODO: do we need this? + debugPP.Println("---push op and operands") + // push op(assign/define) and operands + CX.ops = append(CX.ops, cnn.Op) + ao := &AssignOperand{} + ao.num = 2 // key, value + CX.operands = append(CX.operands, ao) + } if cnn.Key != nil { cnn.Key = transcribe(t, nns, TRANS_RANGE_KEY, 0, cnn.Key, &c).(Expr) if isStopOrSkip(nc, c) { - //if pushed { - // CX.pop(true) - //} + if pushed { + CX.pop(true) + } return } } if cnn.Value != nil { cnn.Value = transcribe(t, nns, TRANS_RANGE_VALUE, 0, cnn.Value, &c).(Expr) if isStopOrSkip(nc, c) { - //if pushed { - // CX.pop(true) - //} + if pushed { + CX.pop(true) + } return } } - pushed := CX.push(cnn) + for idx := range cnn.Body { cnn.Body[idx] = transcribe(t, nns, TRANS_RANGE_BODY, idx, cnn.Body[idx], &c).(Stmt) if isBreak(c) { @@ -883,6 +1034,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } case *SwitchStmt: + debugPP.Printf("---switchStmt: %v \n", cnn) // NOTE: unlike the select case, and like if stmts, both // switch statements AND contained cases visit with the // TRANS_BLOCK stage, even though during runtime only one @@ -894,7 +1046,15 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*SwitchStmt) } + + if cnn.IsTypeSwitch { + debugPP.Printf("is type switch, init is :%v \n", cnn.Init) + debugPP.Printf("is type switch, X is :%v \n", cnn.X) + debugPP.Printf("is type switch, varName is :%v \n", cnn.VarName) + CX.whitelist[cnn.VarName] = true + } pushed := CX.push(cnn) + if cnn.Init != nil { cnn.Init = transcribe(t, nns, TRANS_SWITCH_INIT, 0, cnn.Init, &c).(SimpleStmt) if isStopOrSkip(nc, c) { @@ -1001,7 +1161,16 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc case *ImportDecl: // nothing to do case *ValueDecl: - CX.pushOp(ASSIGN) + debugPP.Println("---value decl") + if CX.hasClosure() { + debugPP.Println("---push op and operands") + // push op(assign/define) and operands + CX.ops = append(CX.ops, ASSIGN) + ao := &AssignOperand{} + ao.num = len(cnn.NameExprs) + CX.operands = append(CX.operands, ao) + } + if cnn.Type != nil { cnn.Type = transcribe(t, nns, TRANS_VAR_TYPE, 0, cnn.Type, &c).(Expr) if isStopOrSkip(nc, c) { diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index 9513e89c120..ebbf6018efc 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -518,6 +518,19 @@ type Captured struct { values []TypedValue } +func (c *Captured) String() { + var s string + s += "\n" + for i, n := range c.names { + s += fmt.Sprintf("name[%d] is: %s: \n", i, n) + } + for i, v := range c.values { + s += fmt.Sprintf("value[%d] is: %v: \n", i, v) + } + s += "\n" + fmt.Println(s) +} + // ---------------------------------------- // FuncValue @@ -547,6 +560,12 @@ type FuncValue struct { nativeBody func(*Machine) // alternative to Body } +func (fv *FuncValue) dump() { + if debugPP { + fmt.Printf("funcValue, name: %s, captures: %s \n", fv.Name, fv.Captures) + } +} + func (fv *FuncValue) IsNative() bool { if fv.NativePkg == "" && fv.NativeName == "" { return false @@ -2333,9 +2352,9 @@ func (b *Block) GetParent(store Store) *Block { } func (b *Block) GetPointerToInt(store Store, index int) PointerValue { - debug.Printf("-----GetPointerToInt, index: %d \n", index) - debug.Printf("b: %v \n", b) - debug.Printf("len of values: %v \n", len(b.Values)) + debugPP.Printf("-----GetPointerToInt, index: %d \n", index) + debugPP.Printf("b: %v \n", b) + debugPP.Printf("len of values: %v \n", len(b.Values)) vv := fillValueTV(store, &b.Values[index]) return PointerValue{ TV: vv, @@ -2345,8 +2364,8 @@ func (b *Block) GetPointerToInt(store Store, index int) PointerValue { } func (b *Block) GetPointerTo(store Store, path ValuePath) PointerValue { - debug.Printf("-----GetPointerTo, path : %v\n", path) - debug.Printf("b: %v \n", b) + debugPP.Printf("-----GetPointerTo, path : %v\n", path) + debugPP.Printf("b: %v \n", b) if path.IsBlockBlankPath() { if debug { if path.Name != "_" { diff --git a/gnovm/tests/debug/4d.gnoa b/gnovm/tests/debug/4d.gnoa index 4a8c1cba970..e69de29bb2d 100644 --- a/gnovm/tests/debug/4d.gnoa +++ b/gnovm/tests/debug/4d.gnoa @@ -1,33 +0,0 @@ -package main - -func main() { - var fns []func() int - - for i := 0; i < 2; i++ { - x := i - y := 1 - b := true - f := func() int { - switch y { - case 1: - if b { - z := 5 - for j := 0; j < z; j++ { - x += 1 - } - } - default: - x += 0 - } - return x - } - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 5 -// 6 diff --git a/gnovm/tests/debug/4f.gno b/gnovm/tests/debug/4f.gno new file mode 100644 index 00000000000..60e327bd2b2 --- /dev/null +++ b/gnovm/tests/debug/4f.gno @@ -0,0 +1,20 @@ +package main + +func main() { + s := []int{1, 2} + + f := func() { + for i, v := range s { // TODO: exclude s, it's final + println(i) + println(v) + } + } + + f() +} + +// Output: +// 0 +// 1 +// 1 +// 2 diff --git a/gnovm/tests/debug/4g.gno b/gnovm/tests/debug/4g.gno new file mode 100644 index 00000000000..bf998c163d8 --- /dev/null +++ b/gnovm/tests/debug/4g.gno @@ -0,0 +1,14 @@ +package main + +func main() { + f := func(a int) bool { + println(a) + return true + } + + println(f(5)) +} + +// Output: +// 5 +// true diff --git a/gnovm/tests/debug/4h.gno b/gnovm/tests/debug/4h.gno new file mode 100644 index 00000000000..c9f1f6e97e8 --- /dev/null +++ b/gnovm/tests/debug/4h.gno @@ -0,0 +1,25 @@ +package main + +func main() { + s := 1 + f := func() { + i := 0 // no capture for i + var j = s // s should be captured, j not + k := s // s should be captured, k not + m, n := s, 0 + println(i) + println(j) + println(k) + println(m) + println(n) + } + + f() +} + +// Output: +// 0 +// 1 +// 1 +// 1 +// 0 diff --git a/gnovm/tests/debug/4i.gno b/gnovm/tests/debug/4i.gno new file mode 100644 index 00000000000..acf1b53b32a --- /dev/null +++ b/gnovm/tests/debug/4i.gno @@ -0,0 +1,15 @@ +package main + +func main() { + s := []int{1, 2} + + f := func() { + if len(s) == 2 { + println("ok") + } + } + f() +} + +// Output: +// ok diff --git a/gnovm/tests/debug/5.gno b/gnovm/tests/debug/5.gno new file mode 100644 index 00000000000..ece19dd87f6 --- /dev/null +++ b/gnovm/tests/debug/5.gno @@ -0,0 +1,22 @@ +package main + +import "fmt" + +func main() { + // Define a function that returns a closure + var recursiveFunc func(int) int + recursiveFunc = func(num int) int { + if num <= 0 { + return 1 + } + // Closure calling itself recursively + return num * recursiveFunc(num-1) + } + + // Use the recursive closure + result := recursiveFunc(5) + fmt.Println("Factorial of 5 is:", result) // Output: 120 +} + +// Output: +// Factorial of 5 is: 120 diff --git a/gnovm/tests/debug/6.gno b/gnovm/tests/debug/6.gno new file mode 100644 index 00000000000..d4823d55c2a --- /dev/null +++ b/gnovm/tests/debug/6.gno @@ -0,0 +1,23 @@ +package main + +import "fmt" + +func foo() (err error) { + defer func() { + if r := recover(); r != nil { + switch v := r.(type) { + case error: + err = v + default: + err = fmt.Errorf("%s", v) + } + } + }() + return +} + +func main() { + foo() +} + +// Output: diff --git a/gnovm/tests/debug/6a.gno b/gnovm/tests/debug/6a.gno new file mode 100644 index 00000000000..1acfa3a1f2c --- /dev/null +++ b/gnovm/tests/debug/6a.gno @@ -0,0 +1,24 @@ +package main + +import ( + "errors" +) + +func foo() (err error) { + y := 1 + defer func() { + if r := recover(); r != nil { + switch y { + case 1: + err = errors.New("xxx") + default: + err = nil + } + } + }() + return +} + +func main() { + foo() +} diff --git a/gnovm/tests/debug/7.gno b/gnovm/tests/debug/7.gno new file mode 100644 index 00000000000..d5f65b18c5d --- /dev/null +++ b/gnovm/tests/debug/7.gno @@ -0,0 +1,25 @@ +package main + +import "fmt" + +func main() { + + var fns []func(int) int + var recursiveFunc func(int) int + + for i := 0; i < 6; i++ { + recursiveFunc = func(num int) int { + if num <= 0 { + return 1 + } + return num * recursiveFunc(num-1) + } + fns = append(fns, recursiveFunc) + } + + for i, r := range fns { + result := r(i) + fmt.Printf("Factorial of %d is: %d \n", i, result) + } + +} diff --git a/gnovm/tests/debug/7a.gno b/gnovm/tests/debug/7a.gno new file mode 100644 index 00000000000..8f7ca50dd38 --- /dev/null +++ b/gnovm/tests/debug/7a.gno @@ -0,0 +1,28 @@ +package main + +import "fmt" + +// recursive closure does not capture +func main() { + + var fns []func(int) int + var recursiveFunc func(int) int + + for i := 0; i < 3; i++ { + recursiveFunc = func(num int) int { + x := i + if num <= 0 { + return 1 + } + println(x) + return num * recursiveFunc(num-1) + } + fns = append(fns, recursiveFunc) + } + + for i, r := range fns { + result := r(i) + fmt.Printf("Factorial of %d is: %d \n", i, result) + } + +} diff --git a/gnovm/tests/debug/zregexp_stdlibs.gnoa b/gnovm/tests/debug/zregexp_stdlibs.gno similarity index 100% rename from gnovm/tests/debug/zregexp_stdlibs.gnoa rename to gnovm/tests/debug/zregexp_stdlibs.gno From e4ec67b99a8a6fa6222606638b639fd4c50ac932 Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Thu, 11 Jan 2024 10:50:27 +0800 Subject: [PATCH 10/32] make test pass --- gnovm/pkg/gnolang/machine.go | 2 +- gnovm/pkg/gnolang/nodes.go | 1 - gnovm/pkg/gnolang/op_call.go | 8 +- gnovm/pkg/gnolang/op_exec.go | 2 +- gnovm/pkg/gnolang/op_expressions.go | 48 ++++++--- gnovm/pkg/gnolang/preprocess.go | 2 +- gnovm/pkg/gnolang/transcribe.go | 159 ++++++++++++++++++---------- gnovm/pkg/gnolang/values.go | 5 + gnovm/tests/debug/5a.gno | 24 +++++ gnovm/tests/debug/5b.gno | 44 ++++++++ gnovm/tests/debug/5b0.gno | 19 ++++ gnovm/tests/debug/5b1.gno | 26 +++++ 12 files changed, 262 insertions(+), 78 deletions(-) create mode 100644 gnovm/tests/debug/5a.gno create mode 100644 gnovm/tests/debug/5b.gno create mode 100644 gnovm/tests/debug/5b0.gno create mode 100644 gnovm/tests/debug/5b1.gno diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go index 79d7b499e16..7027bcf6180 100644 --- a/gnovm/pkg/gnolang/machine.go +++ b/gnovm/pkg/gnolang/machine.go @@ -747,7 +747,7 @@ func (m *Machine) EvalStatic(last BlockNode, x Expr) TypedValue { // static types of nodes. func (m *Machine) EvalStaticTypeOf(last BlockNode, x Expr) Type { if debug { - m.Printf("Machine.EvalStaticTypeOf(%v, %v)\n", last, x) + //m.Printf("Machine.EvalStaticTypeOf(%v, %v)\n", last, x) } // X must have been preprocessed. if x.GetAttribute(ATTR_PREPROCESSED) == nil { diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index 9560d1be248..dd09f1c63c7 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -1386,7 +1386,6 @@ func (x *PackageNode) PrepareNewValues(pv *PackageValue) []TypedValue { // copy function value and assign closure from package value. fv = fv.Copy(nilAllocator) fv.Closure = pv.fBlocksMap[fv.FileName] - debug.Printf("fv.Closure: %v \n", fv.Closure) if fv.Closure == nil { panic(fmt.Sprintf("file block missing for file %q", fv.FileName)) } diff --git a/gnovm/pkg/gnolang/op_call.go b/gnovm/pkg/gnolang/op_call.go index ae9fa288d76..e02f95106ae 100644 --- a/gnovm/pkg/gnolang/op_call.go +++ b/gnovm/pkg/gnolang/op_call.go @@ -62,10 +62,10 @@ func (m *Machine) doOpCall() { numParams := len(pts) isMethod := 0 // 1 if true // Create new block scope. - debugPP.Printf("fv is:---") - fv.dump() - debugPP.Printf("fv.captures is: %v \n", fv.Captures) - debugPP.Printf("fv.address is: %p \n", fv) + //debugPP.Printf("fv is:---") + //fv.dump() + //debugPP.Printf("fv.captures is: %v \n", fv.Captures) + //debugPP.Printf("fv.address is: %p \n", fv) clo := fr.Func.GetClosure(m.Store) debugPP.Printf("-----got closure: %v ----- \n", clo) diff --git a/gnovm/pkg/gnolang/op_exec.go b/gnovm/pkg/gnolang/op_exec.go index 7dce99a5d11..fe80ff354e7 100644 --- a/gnovm/pkg/gnolang/op_exec.go +++ b/gnovm/pkg/gnolang/op_exec.go @@ -483,7 +483,7 @@ EXEC_SWITCH: m.PushExpr(rx) m.PushOp(OpEval) } - m.PushOp(OpPreAssign) + //m.PushOp(OpPreAssign) if cs.Op != DEFINE { // For each Lhs, push eval operation if needed. diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index 5f8f2c49e90..4b4d616a882 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -87,6 +87,7 @@ func (m *Machine) doOpSelector() { } func (m *Machine) doOpSlice() { + debugPP.Println("-----doOpSlice") sx := m.PopExpr().(*SliceExpr) var low, high, max int = -1, -1, -1 // max @@ -105,6 +106,7 @@ func (m *Machine) doOpSlice() { } // slice base x xv := m.PopValue() + debugPP.Printf("---xv: %v \n", xv) // if a is a pointer to an array, a[low : high : max] is // shorthand for (*a)[low : high : max] if xv.T.Kind() == PointerKind && @@ -715,6 +717,14 @@ func (m *Machine) doOpPreFuncLit() { } } +func isZeroArray(arr [8]byte) bool { + for _, v := range arr { + if v != 0 { + return false + } + } + return true +} func (m *Machine) doOpFuncLit() { debugPP.Println("-----doOpFuncLit") x := m.PopExpr().(*FuncLitExpr) @@ -725,19 +735,31 @@ func (m *Machine) doOpFuncLit() { if x.Closure != nil { for _, cnx := range x.Closure.cnxs { debugPP.Printf("range closures of : %s \n", string(cnx.nx.Name)) - v := *m.PopValue() - debugPP.Printf("v got is: %v \n", v) + tv := *m.PopValue() + debugPP.Printf("tv got is: %+v, tv.T: %v, tv.V: %v, tv.N: %v \n", tv, tv.T, tv.V, tv.N) //c := *m.PopValue() //debugPP.Printf("---c is: %v \n", c) - if !v.IsUndefined() { // e.g. args of func. forward // TODO: consider this - //if v.V != nil { // TODO: consider this, this only happens when recursive closure - debugPP.Printf("capturing v: %v \n", v) - debugPP.Printf("captured before update: \n %s \n", captured) - captured.names = append(captured.names, cnx.nx.Name) - captured.values = append(captured.values, v) - debugPP.Printf("captured after update: \n %s \n", captured) + if !tv.IsUndefined() { // e.g. args of func. forward // TODO: consider this + pass := true + if _, ok := tv.T.(*FuncType); ok { + if tv.V == nil { + pass = false + } + } else { + if tv.T == nil && tv.V == nil && isZeroArray(tv.N) { + pass = false + } + } + if pass { + //if v.V != nil { // TODO: consider this, this only happens when recursive closure + debugPP.Printf("capturing v: %v \n", tv) + //debugPP.Printf("captured before update: \n %s \n", captured) + captured.names = append(captured.names, cnx.nx.Name) + captured.values = append(captured.values, tv) + //debugPP.Printf("captured after update: \n %s \n", captured) + } //} } else { debugPP.Println("undefined, skip") @@ -762,10 +784,10 @@ func (m *Machine) doOpFuncLit() { nativeBody: nil, } - debugPP.Printf("fv is:------") - fv.dump() - debugPP.Printf("fv.captures is: %v \n", fv.Captures) - debugPP.Printf("fv.address is: %p \n", fv) + //debugPP.Printf("fv is:------") + //fv.dump() + //debugPP.Printf("fv.captures is: %v \n", fv.Captures) + //debugPP.Printf("fv.address is: %p \n", fv) m.PushValue(TypedValue{ T: ft, diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 288a6424dbb..3f81129bbec 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -187,7 +187,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } }() if debug { - debug.Printf("Preprocess %s (%v) stage:%v\n", n.String(), reflect.TypeOf(n), stage) + //debug.Printf("Preprocess %s (%v) stage:%v\n", n.String(), reflect.TypeOf(n), stage) } switch stage { diff --git a/gnovm/pkg/gnolang/transcribe.go b/gnovm/pkg/gnolang/transcribe.go index f595b31fd93..31dff768e97 100644 --- a/gnovm/pkg/gnolang/transcribe.go +++ b/gnovm/pkg/gnolang/transcribe.go @@ -163,10 +163,10 @@ func (ao *AssignOperand) String() string { return s } -type CNode struct { - name Name - n Node -} +//type CNode struct { +// name Name +// n Node +//} type ClosureContext struct { closures []*Closure @@ -238,11 +238,17 @@ func (cx *ClosureContext) dumpOps() string { // 2. it's embedded in another volatile block, for/range stmt // 2. no recursive closure(not support for now) func (cx *ClosureContext) hasClosure() bool { - for _, cn := range cx.nodes { - if _, ok := cn.(*FuncLitExpr); ok { - return true - } - //else if _, ok := c.(*FuncDecl); ok { + loopBlock := false + for _, n := range cx.nodes { + switch n.(type) { + case *ForStmt, *RangeStmt: + loopBlock = true + case *FuncLitExpr: + return loopBlock // has loopBlock ahead + default: + // do nothing + } + //if _, ok := n.(*FuncLitExpr); ok { // return true //} } @@ -256,12 +262,23 @@ func (cx *ClosureContext) hasClosure() bool { return false } -func (cx *ClosureContext) push(n Node) bool { - // push nodes stack - //cn := &CNode{ - // n: n, - //} +func (cx *ClosureContext) pushNode(n Node) { + debugPP.Println("+++Cx push node") + debugPP.Println("+node") cx.nodes = append(cx.nodes, n) +} + +func (cx *ClosureContext) popNode() { + debugPP.Println("---Cx pop node") + if len(cx.nodes) != 0 { + debugPP.Println("-node") + cx.nodes = cx.nodes[:len(cx.nodes)-1] + debugPP.Printf("len of node: %d \n", len(cx.nodes)) + } +} + +func (cx *ClosureContext) pushClosure() bool { + debugPP.Println("+++Cx push closure") // push closure if cx.hasClosure() { debug.Println("+clo") @@ -277,15 +294,9 @@ func (cx *ClosureContext) push(n Node) bool { } } -func (cx *ClosureContext) pop(copy bool) *Closure { +func (cx *ClosureContext) popClosure(copy bool) *Closure { + debugPP.Println("---Cx pop") debug.Println("-clo") - defer func() { - if len(cx.nodes) != 0 { - debug.Println("-node") - cx.nodes = cx.nodes[:len(cx.nodes)-1] - } - }() - if len(cx.closures) == 0 { return nil } else { @@ -294,7 +305,7 @@ func (cx *ClosureContext) pop(copy bool) *Closure { } c := cx.closures[len(cx.closures)-1] // get current for _, cnx := range c.cnxs { // pop-> increase offset - debug.Printf("+1 \n") + debug.Printf("+1 \n") // mutate offset cnx.offset += 1 } cx.closures = cx.closures[:len(cx.closures)-1] // shrink @@ -481,6 +492,8 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc //CX.dumpNodes() } } + } else { + debugPP.Println("---no closure in place") } case *BasicLitExpr: case *BinaryExpr: @@ -617,7 +630,9 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc CX.whitelist[n] = true } debugPP.Println("---start trans funcLit body stmt, push initial closure and fx") - pushed := CX.push(cnn) + + CX.pushNode(cnn) + pushed := CX.pushClosure() //var pushed bool debugPP.Printf("---stop or skip, pop and return \n") @@ -634,8 +649,9 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else if isStopOrSkip(nc, c) { // pop before return if pushed { - CX.pop(isCopy) + CX.popClosure(isCopy) } + CX.popNode() return } } @@ -645,12 +661,13 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc debugPP.Println("funcLit pop c-----") if pushed { - pc := CX.pop(isCopy) + pc := CX.popClosure(isCopy) if pc != nil { cnn.SetClosure(pc) debugPP.Printf("---done FuncLit trans, fx: %v, closure: %+v \n", cnn, cnn.Closure.String()) } } + CX.popNode() case *FieldTypeExpr: cnn.Type = transcribe(t, nns, TRANS_FIELDTYPE_TYPE, 0, cnn.Type, &c).(Expr) if isStopOrSkip(nc, c) { @@ -764,21 +781,25 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*BlockStmt) } - pushed := CX.push(cnn) + CX.pushNode(cnn) + pushed := CX.pushClosure() for idx := range cnn.Body { cnn.Body[idx] = transcribe(t, nns, TRANS_BLOCK_BODY, idx, cnn.Body[idx], &c).(Stmt) if isBreak(c) { break } else if isStopOrSkip(nc, c) { if pushed { - CX.pop(true) + CX.popClosure(true) } + CX.popNode() return } } if pushed { - CX.pop(true) + CX.popClosure(true) } + CX.popNode() + case *BranchStmt: case *DeclStmt: //CX.pushOp(ASSIGN) @@ -811,7 +832,8 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc cnn = cnn2.(*ForStmt) } - pushed := CX.push(cnn) + CX.pushNode(cnn) + pushed := CX.pushClosure() if cnn.Init != nil { cnn.Init = transcribe(t, nns, TRANS_FOR_INIT, 0, cnn.Init, &c).(SimpleStmt) @@ -838,14 +860,16 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc break } else if isStopOrSkip(nc, c) { if pushed { - CX.pop(true) + CX.popClosure(true) } + CX.popNode() return } } if pushed { - CX.pop(true) + CX.popClosure(true) } + CX.popNode() case *GoStmt: cnn.Call = *transcribe(t, nns, TRANS_GO_CALL, 0, &cnn.Call, &c).(*CallExpr) if isStopOrSkip(nc, c) { @@ -864,7 +888,8 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc cnn = cnn2.(*IfStmt) } - pushed := CX.push(cnn) + CX.pushNode(cnn) + pushed := CX.pushClosure() // nx in init is always treat defined locally if cnn.Init != nil { @@ -887,8 +912,9 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } if pushed { - CX.pop(true) + CX.popClosure(true) } + CX.popNode() case *IfCaseStmt: debug.Printf("-----trans, (if---case) stmt: %v \n", cnn) cnn2, c2 := t(ns, ftype, index, cnn, TRANS_BLOCK) @@ -909,7 +935,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } //debug.Println("if-case pop c-----") - //popClosure() case *IncDecStmt: cnn.X = transcribe(t, nns, TRANS_INCDEC_X, 0, cnn.X, &c).(Expr) if isStopOrSkip(nc, c) { @@ -924,14 +949,15 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*RangeStmt) } - - pushed := CX.push(cnn) + CX.pushNode(cnn) + pushed := CX.pushClosure() cnn.X = transcribe(t, nns, TRANS_RANGE_X, 0, cnn.X, &c).(Expr) if isStopOrSkip(nc, c) { if pushed { - CX.pop(true) + CX.popClosure(true) } + CX.popNode() return } @@ -947,8 +973,9 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc cnn.Key = transcribe(t, nns, TRANS_RANGE_KEY, 0, cnn.Key, &c).(Expr) if isStopOrSkip(nc, c) { if pushed { - CX.pop(true) + CX.popClosure(true) } + CX.popNode() return } } @@ -956,8 +983,9 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc cnn.Value = transcribe(t, nns, TRANS_RANGE_VALUE, 0, cnn.Value, &c).(Expr) if isStopOrSkip(nc, c) { if pushed { - CX.pop(true) + CX.popClosure(true) } + CX.popNode() return } } @@ -968,14 +996,16 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc break } else if isStopOrSkip(nc, c) { if pushed { - CX.pop(true) + CX.popClosure(true) } + CX.popNode() return } } if pushed { - CX.pop(true) + CX.popClosure(true) } + CX.popNode() case *ReturnStmt: debug.Printf("-----trans, return stmt: %v \n", cnn) for idx := range cnn.Results { @@ -1005,7 +1035,8 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*SelectCaseStmt) } - pushed := CX.push(cnn) + CX.pushNode(cnn) + pushed := CX.pushClosure() cnn.Comm = transcribe(t, nns, TRANS_SELECTCASE_COMM, 0, cnn.Comm, &c).(Stmt) if isStopOrSkip(nc, c) { return @@ -1016,14 +1047,16 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc break } else if isStopOrSkip(nc, c) { if pushed { - CX.pop(true) + CX.popClosure(true) } + CX.popNode() return } } if pushed { - CX.pop(true) + CX.popClosure(true) } + CX.popNode() case *SendStmt: cnn.Chan = transcribe(t, nns, TRANS_SEND_CHAN, 0, cnn.Chan, &c).(Expr) if isStopOrSkip(nc, c) { @@ -1053,22 +1086,25 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc debugPP.Printf("is type switch, varName is :%v \n", cnn.VarName) CX.whitelist[cnn.VarName] = true } - pushed := CX.push(cnn) + CX.pushNode(cnn) + pushed := CX.pushClosure() if cnn.Init != nil { cnn.Init = transcribe(t, nns, TRANS_SWITCH_INIT, 0, cnn.Init, &c).(SimpleStmt) if isStopOrSkip(nc, c) { if pushed { - CX.pop(true) + CX.popClosure(true) } + CX.popNode() return } } cnn.X = transcribe(t, nns, TRANS_SWITCH_X, 0, cnn.X, &c).(Expr) if isStopOrSkip(nc, c) { if pushed { - CX.pop(true) + CX.popClosure(true) } + CX.popNode() return } // NOTE: special block case for after .Init and .X. @@ -1076,8 +1112,9 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc if isStopOrSkip(nc, c2) { nn = cnn2 if pushed { - CX.pop(true) + CX.popClosure(true) } + CX.popNode() return } else { cnn = cnn2.(*SwitchStmt) @@ -1088,14 +1125,16 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc break } else if isStopOrSkip(nc, c) { if pushed { - CX.pop(true) + CX.popClosure(true) } + CX.popNode() return } } if pushed { - CX.pop(true) + CX.popClosure(true) } + CX.popNode() case *SwitchClauseStmt: // NOTE: unlike the select case, both switch // statements AND switch cases visit with the @@ -1143,21 +1182,24 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc cnn = cnn2.(*FuncDecl) } - pushed := CX.push(cnn) + CX.pushNode(cnn) + pushed := CX.pushClosure() for idx := range cnn.Body { cnn.Body[idx] = transcribe(t, nns, TRANS_FUNC_BODY, idx, cnn.Body[idx], &c).(Stmt) if isBreak(c) { break } else if isStopOrSkip(nc, c) { if pushed { - CX.pop(true) + CX.popClosure(true) } + CX.popNode() return } } if pushed { - CX.pop(true) + CX.popClosure(true) } + CX.popNode() case *ImportDecl: // nothing to do case *ValueDecl: @@ -1200,7 +1242,8 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc cnn = cnn2.(*FileNode) } - pushed := CX.push(cnn) + CX.pushNode(cnn) + pushed := CX.pushClosure() for idx := range cnn.Decls { cnn.Decls[idx] = transcribe(t, nns, TRANS_FILE_BODY, idx, cnn.Decls[idx], &c).(Decl) @@ -1208,14 +1251,16 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc break } else if isStopOrSkip(nc, c) { if pushed { - CX.pop(true) + CX.popClosure(true) } + CX.popNode() return } } if pushed { - CX.pop(true) + CX.popClosure(true) } + CX.popNode() case *ConstExpr, *constTypeExpr: // leaf nodes // These nodes get created by the preprocessor while // leaving the type expression of a composite lit, before diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index ebbf6018efc..6704e5a7c4f 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -2352,6 +2352,11 @@ func (b *Block) GetParent(store Store) *Block { } func (b *Block) GetPointerToInt(store Store, index int) PointerValue { + //defer func() { + // if r := recover(); r != nil { + // debugPP.Printf("r: %v \n", r) + // } + //}() debugPP.Printf("-----GetPointerToInt, index: %d \n", index) debugPP.Printf("b: %v \n", b) debugPP.Printf("len of values: %v \n", len(b.Values)) diff --git a/gnovm/tests/debug/5a.gno b/gnovm/tests/debug/5a.gno new file mode 100644 index 00000000000..8345c9ed48d --- /dev/null +++ b/gnovm/tests/debug/5a.gno @@ -0,0 +1,24 @@ +package main + +import "fmt" + +func main() { + var recursiveFunc func(int) int + var recursiveFunc2 func(int) int + + recursiveFunc = func(num int) int { + recursiveFunc2 = recursiveFunc + + if num <= 0 { + return 1 + } + + return num * recursiveFunc2(num-1) + } + + result := recursiveFunc(5) + fmt.Println("Factorial of 5 is:", result) // Output: 120 +} + +// Output: +// Factorial of 5 is: 120 diff --git a/gnovm/tests/debug/5b.gno b/gnovm/tests/debug/5b.gno new file mode 100644 index 00000000000..9c030f2ba58 --- /dev/null +++ b/gnovm/tests/debug/5b.gno @@ -0,0 +1,44 @@ +package main + +import ( + "fmt" + "io" + "strings" +) + +func main() { + var mr io.Reader + var buf []byte + nread := 0 + withFooBar := func(tests func()) { + r1 := strings.NewReader("foo ") + //r2 := strings.NewReader("") + //r3 := strings.NewReader("bar") + //mr = io.MultiReader(r1, r2, r3) + mr = io.MultiReader(r1) + buf = make([]byte, 20) + tests() + } + expectRead := func(size int, expected string, eerr error) { + nread++ + n, gerr := mr.Read(buf[0:size]) + if n != len(expected) { + panic(fmt.Sprintf("#%d, expected %d bytes; got %d", + nread, len(expected), n)) + } + got := string(buf[0:n]) + if got != expected { + panic(fmt.Sprintf("#%d, expected %q; got %q", + nread, expected, got)) + } + if gerr != eerr { + panic(fmt.Sprintf("#%d, expected error %v; got %v", + nread, eerr, gerr)) + } + buf = buf[n:] + } + withFooBar(func() { + expectRead(5, "foo ", nil) + }) + +} diff --git a/gnovm/tests/debug/5b0.gno b/gnovm/tests/debug/5b0.gno new file mode 100644 index 00000000000..0e74e0ea9a9 --- /dev/null +++ b/gnovm/tests/debug/5b0.gno @@ -0,0 +1,19 @@ +package main + +func main() { + var buf []byte + + // when eval this, buf is still nil, + expectRead := func(size int, expected string, eerr error) { + b := buf[0:size] + println(b) + } + + // buf should be captured here, here it's volatile, not where it defined + // namely, the vp should point here, -> so mutate offset + withFooBar := func() { + buf = make([]byte, 20) + expectRead(5, "foo ", nil) + } + withFooBar() +} diff --git a/gnovm/tests/debug/5b1.gno b/gnovm/tests/debug/5b1.gno new file mode 100644 index 00000000000..9218ee16fa3 --- /dev/null +++ b/gnovm/tests/debug/5b1.gno @@ -0,0 +1,26 @@ +package main + +func main() { + var buf []byte + + // when eval this, buf is still nil, + expectRead := func(size int, expected string, eerr error) { + b := buf[0:size] + println(b) + } + + // buf should be captured here, here it's volatile, not where it defined + // namely, the vp should point here, -> so mutate offset + withFooBar := func() func() { + buf = make([]byte, 20) + return func() { + b := buf[0:4] + println(b) + } + } + withFooBar() + println("ok") +} + +// Output: +// ok From c874d1fe19413a7ce15269e8c654ba9480a82964 Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Mon, 22 Jan 2024 22:44:39 +0800 Subject: [PATCH 11/32] fixup --- gnovm/pkg/gnolang/nodes.go | 47 +++- gnovm/pkg/gnolang/op_call.go | 58 +++-- gnovm/pkg/gnolang/op_exec.go | 22 +- gnovm/pkg/gnolang/op_expressions.go | 204 +++++++++++++----- gnovm/pkg/gnolang/preprocess.go | 4 +- gnovm/pkg/gnolang/transcribe.go | 71 +++--- gnovm/pkg/gnolang/values.go | 11 + gnovm/tests/{debug => debug2}/1.gno | 0 gnovm/tests/{debug => debug2}/1a.gno | 0 gnovm/tests/{debug => debug2}/1b.gno | 0 gnovm/tests/{debug => debug2}/1c.gno | 0 gnovm/tests/{debug => debug2}/1d.gno | 0 gnovm/tests/{debug => debug2}/1e.gno | 0 gnovm/tests/{debug => debug2}/2.gno | 0 gnovm/tests/{debug => debug2}/2a.gno | 0 gnovm/tests/{debug => debug2}/3.gno | 0 gnovm/tests/{debug => debug2}/3a.gno | 0 gnovm/tests/{debug => debug2}/3b.gno | 0 gnovm/tests/{debug => debug2}/4.gno | 0 gnovm/tests/{debug => debug2}/4a.gno | 0 gnovm/tests/{debug => debug2}/4b.gno | 0 gnovm/tests/{debug => debug2}/4c.gno | 0 gnovm/tests/{debug => debug2}/4d.gnoa | 0 gnovm/tests/{debug => debug2}/4e.gno | 0 gnovm/tests/{debug => debug2}/4f.gno | 0 gnovm/tests/{debug => debug2}/4g.gno | 0 gnovm/tests/{debug => debug2}/4h.gno | 0 gnovm/tests/{debug => debug2}/4i.gno | 0 gnovm/tests/{debug => debug2}/5.gno | 0 gnovm/tests/{debug => debug2}/5a.gno | 0 gnovm/tests/{debug => debug2}/5b.gno | 0 gnovm/tests/{debug => debug2}/5b0.gno | 0 gnovm/tests/{debug => debug2}/5b1.gno | 0 gnovm/tests/{debug => debug2}/6.gno | 0 gnovm/tests/{debug => debug2}/6a.gno | 0 gnovm/tests/{debug => debug2}/7.gno | 0 gnovm/tests/{debug => debug2}/7a.gno | 0 gnovm/tests/{debug => debug2}/closure0.gno | 0 gnovm/tests/{debug => debug2}/closure1.gno | 0 gnovm/tests/{debug => debug2}/closure2.gno | 0 gnovm/tests/{debug => debug2}/closure3.gno | 0 gnovm/tests/{debug => debug2}/closure4.gno | 0 gnovm/tests/{debug => debug2}/closure5.gno | 0 gnovm/tests/{debug => debug2}/closure6.gno | 0 gnovm/tests/{debug => debug2}/closure7.gno | 0 gnovm/tests/{debug => debug2}/closure8.gno | 0 gnovm/tests/{debug => debug2}/recover4.gno | 0 gnovm/tests/{debug => debug2}/recover6.gno | 0 gnovm/tests/{debug => debug2}/recover6a.gno | 0 .../{debug => debug2}/zregexp_stdlibs.gno | 0 gnovm/tests/file.go | 14 ++ 51 files changed, 330 insertions(+), 101 deletions(-) rename gnovm/tests/{debug => debug2}/1.gno (100%) rename gnovm/tests/{debug => debug2}/1a.gno (100%) rename gnovm/tests/{debug => debug2}/1b.gno (100%) rename gnovm/tests/{debug => debug2}/1c.gno (100%) rename gnovm/tests/{debug => debug2}/1d.gno (100%) rename gnovm/tests/{debug => debug2}/1e.gno (100%) rename gnovm/tests/{debug => debug2}/2.gno (100%) rename gnovm/tests/{debug => debug2}/2a.gno (100%) rename gnovm/tests/{debug => debug2}/3.gno (100%) rename gnovm/tests/{debug => debug2}/3a.gno (100%) rename gnovm/tests/{debug => debug2}/3b.gno (100%) rename gnovm/tests/{debug => debug2}/4.gno (100%) rename gnovm/tests/{debug => debug2}/4a.gno (100%) rename gnovm/tests/{debug => debug2}/4b.gno (100%) rename gnovm/tests/{debug => debug2}/4c.gno (100%) rename gnovm/tests/{debug => debug2}/4d.gnoa (100%) rename gnovm/tests/{debug => debug2}/4e.gno (100%) rename gnovm/tests/{debug => debug2}/4f.gno (100%) rename gnovm/tests/{debug => debug2}/4g.gno (100%) rename gnovm/tests/{debug => debug2}/4h.gno (100%) rename gnovm/tests/{debug => debug2}/4i.gno (100%) rename gnovm/tests/{debug => debug2}/5.gno (100%) rename gnovm/tests/{debug => debug2}/5a.gno (100%) rename gnovm/tests/{debug => debug2}/5b.gno (100%) rename gnovm/tests/{debug => debug2}/5b0.gno (100%) rename gnovm/tests/{debug => debug2}/5b1.gno (100%) rename gnovm/tests/{debug => debug2}/6.gno (100%) rename gnovm/tests/{debug => debug2}/6a.gno (100%) rename gnovm/tests/{debug => debug2}/7.gno (100%) rename gnovm/tests/{debug => debug2}/7a.gno (100%) rename gnovm/tests/{debug => debug2}/closure0.gno (100%) rename gnovm/tests/{debug => debug2}/closure1.gno (100%) rename gnovm/tests/{debug => debug2}/closure2.gno (100%) rename gnovm/tests/{debug => debug2}/closure3.gno (100%) rename gnovm/tests/{debug => debug2}/closure4.gno (100%) rename gnovm/tests/{debug => debug2}/closure5.gno (100%) rename gnovm/tests/{debug => debug2}/closure6.gno (100%) rename gnovm/tests/{debug => debug2}/closure7.gno (100%) rename gnovm/tests/{debug => debug2}/closure8.gno (100%) rename gnovm/tests/{debug => debug2}/recover4.gno (100%) rename gnovm/tests/{debug => debug2}/recover6.gno (100%) rename gnovm/tests/{debug => debug2}/recover6a.gno (100%) rename gnovm/tests/{debug => debug2}/zregexp_stdlibs.gno (100%) diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index dd09f1c63c7..0e85fb9ae33 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -883,15 +883,17 @@ type SwitchClauseStmt struct { // NOTE: embedded in Block. type bodyStmt struct { Attributes - Body // for non-loop stmts - BodyLen int // for for-continue - NextBodyIndex int // init:-2, cond/elem:-1, body:0..., post:n - NumOps int // number of Ops, for goto - NumValues int // number of Values, for goto - NumExprs int // number of Exprs, for goto - NumStmts int // number of Stmts, for goto - Cond Expr // for ForStmt - Post Stmt // for ForStmt + Body // for non-loop stmts + BodyLen int // for for-continue + NextBodyIndex int // init:-2, cond/elem:-1, body:0..., post:n + NumOps int // number of Ops, for goto + NumValues int // number of Values, for goto + NumExprs int // number of Exprs, for goto + NumStmts int // number of Stmts, for goto + Cond Expr // for ForStmt + Post Stmt // for ForStmt + Ts *TimeSeriesBag + isLoop bool Active Stmt // for PopStmt() Key Expr // for RangeStmt Value Expr // for RangeStmt @@ -1498,6 +1500,33 @@ type StaticBlock struct { oldValues []oldValue } +func (sb *StaticBlock) String() { + fmt.Println("==============static block==============") + for i, t := range sb.Types { + fmt.Printf("types[%d] is %v \n", i, t) + } + fmt.Printf("numNames is %d \n", sb.NumNames) + for i, t := range sb.Names { + fmt.Printf("names[%d] is %v \n", i, t) + } + for i, t := range sb.Consts { + fmt.Printf("consts[%d] is %v \n", i, t) + } + for i, t := range sb.Externs { + fmt.Printf("externs[%d] is %v \n", i, t) + } + for i, t := range sb.oldValues { + fmt.Printf("oldValues[%d] is %v \n", i, t) + } + fmt.Printf("sb objectInfo %v \n", sb.GetObjectInfo()) + fmt.Printf("sb isEscaped %v \n", sb.GetIsEscaped()) + fmt.Printf("sb isTransient %v \n", sb.GetIsTransient()) + //fmt.Printf("sb getOwnder, getOwnerID %v \n", sb.GetOwner(), sb.GetOwnerID()) + fmt.Println("=================block==================") + fmt.Printf("block: %v \n", sb.Block) + fmt.Println("==================end===================") +} + type oldValue struct { idx uint16 value Value diff --git a/gnovm/pkg/gnolang/op_call.go b/gnovm/pkg/gnolang/op_call.go index e02f95106ae..a63ca206e9f 100644 --- a/gnovm/pkg/gnolang/op_call.go +++ b/gnovm/pkg/gnolang/op_call.go @@ -74,23 +74,57 @@ func (m *Machine) doOpCall() { if captures == nil { debugPP.Println("nil captures") } - if captures != nil { - debugPP.Printf("captures before call: %v, len(names): %d, len(values): %d \n", captures, len(captures.names), len(captures.values)) - names := clo.GetSource(m.Store).GetBlockNames() - debugPP.Printf("names: %v \n", names) - for i1, n1 := range captures.names { + //if captures != nil { + // debugPP.Printf("captures before call: %v, len(names): %d, len(values): %d \n", captures, len(captures.names), len(captures.values)) + // names := clo.GetSource(m.Store).GetBlockNames() + // debugPP.Printf("names: %v \n", names) + // for i1, n1 := range captures.names { + // var index int + // for i2, n2 := range names { + // if n1 == n2 { // match and replace + // index = i2 + // debugPP.Printf("index of %s in target block is: %d \n", n1, index) + // debugPP.Printf("target tv[%d] in captured values is :%v \n", i1, captures.values[i1]) + // // replace lv values with index + // clo.UpdateValue(index, captures.values[i1]) + // } + // } + // } + //} + debugPP.Println("---parse transient") + if fv.Ts != nil { + var end bool + for i, t := range fv.Ts.transient { + debugPP.Printf("Ts.transient[%d] name is %v, path: %v, len of values is: %v \n", i, t.nx.Name, t.nx.Path, len(t.values)) + for i, v := range t.values { + debugPP.Printf("values[%d] is %v \n", i, v) + } + //debugPP.Println("?????????nil", fv.Ts == nil) + + names := clo.GetSource(m.Store).GetBlockNames() var index int - for i2, n2 := range names { - if n1 == n2 { // match and replace - index = i2 - debugPP.Printf("index of %s in target block is: %d \n", n1, index) - debugPP.Printf("target tv[%d] in captured values is :%v \n", i1, captures.values[i1]) - // replace lv values with index - clo.UpdateValue(index, captures.values[i1]) + var match bool + for i, n := range names { + if n == t.nx.Name { + index = i + match = true + } + } + if match { + clo.UpdateValue(index, t.values[0]) + nvs := t.values[1:] + //debugPP.Println("nil????????", fv.Ts == nil) + fv.Ts.transient[i].values = nvs + if len(fv.Ts.transient[i].values) == 0 { // loop end + end = true } } } + if end { + fv.Ts = nil + } } + // only need initial snapshot fr.Func.Captures = nil //fr.Func.Captures = Captured{} diff --git a/gnovm/pkg/gnolang/op_exec.go b/gnovm/pkg/gnolang/op_exec.go index fe80ff354e7..78d9fcba54a 100644 --- a/gnovm/pkg/gnolang/op_exec.go +++ b/gnovm/pkg/gnolang/op_exec.go @@ -89,6 +89,7 @@ func (m *Machine) doOpExec(op Op) { return } case OpForLoop: + lb := m.LastBlock() bs := m.LastBlock().GetBodyStmt() // evaluate .Cond. if bs.NextBodyIndex == -2 { // init @@ -117,7 +118,25 @@ func (m *Machine) doOpExec(op Op) { bs.Active = next s = next goto EXEC_SWITCH - } else if bs.NextBodyIndex == bs.BodyLen { + } else if bs.NextBodyIndex == bs.BodyLen { // TODO: Update ts slice? + debugPP.Printf("---end of body, current block is: %v \n", m.LastBlock()) + // update fv.Capture if value changed + if bs.isLoop { + if bs.Ts != nil { + for i, t := range bs.Ts.transient { + debugPP.Printf("ts[%d] name is %v, path: %v \n", i, t.nx.Name, t.nx.Path) + nvp := lb.Source.GetPathForName(m.Store, t.nx.Name) + ptr := m.LastBlock().GetPointerTo(m.Store, nvp) + tv := ptr.Deref() + debugPP.Printf("--- new tv : %v \n", tv) + //t.value = tv + // set back + bs.Ts.transient[i].values = append(bs.Ts.transient[i].values, tv) + debugPP.Printf("len of Ts values is : %d \n", len(bs.Ts.transient[i].values)) + } + } + } + // (queue to) go back. if bs.Cond != nil { m.PushExpr(bs.Cond) @@ -517,6 +536,7 @@ EXEC_SWITCH: NextBodyIndex: -2, Cond: cs.Cond, Post: cs.Post, + isLoop: true, } m.PushBlock(b) m.PushOp(OpForLoop) diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index 4b4d616a882..773ece8b7ac 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -686,35 +686,61 @@ func (m *Machine) doOpPreFuncLit() { //debug.Printf("pointer of x.Closure: %p \n", x.Closure) debugPP.Printf("x.Closure: %v \n", x.Closure) lb := m.LastBlock() + debugPP.Printf("lb: %v \n", lb) + + // push block b := m.Alloc.NewBlock(x, lb) + debugPP.Printf("b: %v \n", b) + m.PushBlock(b) m.PushOp(OpPopBlock) - closure := x.Closure - if closure != nil { - debugPP.Printf("closure nxs len is: %d \n", len(closure.cnxs)) - for i, cnx := range closure.cnxs { - debugPP.Printf("closure[%d]: %v \n", i, cnx) - offset := cnx.offset - debugPP.Printf("offset of %s is %d \n", cnx.nx.Name, offset) - vp := cnx.nx.Path - debugPP.Printf("vp of nx[%s] is : %v \n", cnx.nx.Name, vp) - if (vp.Depth + 1) < offset { - panic("---incorrect offset for:" + cnx.nx.Name) - } - nvp := ValuePath{ - Name: cnx.nx.Name, - Depth: vp.Depth - offset + 1, - Index: vp.Index, - Type: vp.Type, - } - debugPP.Printf("nvp of nx[%s] is : %v \n", nvp.Name, nvp) - nnx := *cnx.nx - nnx.Path = nvp - m.PushExpr(&nnx) - m.PushOp(OpEval) + // get names + //x.StaticBlock.String() + + //debugPP.Printf("x.staticBlock.ExternNames: %v \n", x.StaticBlock.GetExternNames()) + for i, n := range x.GetExternNames() { + debugPP.Printf("x.staticBlock.ExternNames[%d] %v \n", i, n) + vp := x.GetPathForName(m.Store, n) + debugPP.Printf("%v's value path is: %v \n", n, vp) + if vp.Depth == 0 { + continue + } + debugPP.Println("-------got target of: ", n) + nx := &NameExpr{ + Name: n, + Path: vp, } + debugPP.Printf("nx is: %v \n", nx) + //m.PushExpr(nx) + //m.PushOp(OpEval) } + + //closure := x.Closure + //if closure != nil { + // debugPP.Printf("closure nxs len is: %d \n", len(closure.cnxs)) + // for i, cnx := range closure.cnxs { + // debugPP.Printf("closure[%d]: %v \n", i, cnx) + // offset := cnx.offset + // debugPP.Printf("offset of %s is %d \n", cnx.nx.Name, offset) + // vp := cnx.nx.Path + // debugPP.Printf("vp of nx[%s] is : %v \n", cnx.nx.Name, vp) + // if (vp.Depth + 1) < offset { + // panic("---incorrect offset for:" + cnx.nx.Name) + // } + // nvp := ValuePath{ + // Name: cnx.nx.Name, + // Depth: vp.Depth - offset + 1, + // Index: vp.Index, + // Type: vp.Type, + // } + // debugPP.Printf("nvp of nx[%s] is : %v \n", nvp.Name, nvp) + // nnx := *cnx.nx + // nnx.Path = nvp + // m.PushExpr(&nnx) + // m.PushOp(OpEval) + // } + //} } func isZeroArray(arr [8]byte) bool { @@ -728,45 +754,115 @@ func isZeroArray(arr [8]byte) bool { func (m *Machine) doOpFuncLit() { debugPP.Println("-----doOpFuncLit") x := m.PopExpr().(*FuncLitExpr) + debugPP.Printf("-----doOpFuncLit, x: %v \n", x) lb := m.LastBlock() debugPP.Printf("lb: %v \n", lb) - captured := &Captured{} - if x.Closure != nil { - for _, cnx := range x.Closure.cnxs { - debugPP.Printf("range closures of : %s \n", string(cnx.nx.Name)) - tv := *m.PopValue() - debugPP.Printf("tv got is: %+v, tv.T: %v, tv.V: %v, tv.N: %v \n", tv, tv.T, tv.V, tv.N) - - //c := *m.PopValue() - //debugPP.Printf("---c is: %v \n", c) - - if !tv.IsUndefined() { // e.g. args of func. forward // TODO: consider this - pass := true - if _, ok := tv.T.(*FuncType); ok { - if tv.V == nil { - pass = false - } - } else { - if tv.T == nil && tv.V == nil && isZeroArray(tv.N) { - pass = false - } + var tsb *TimeSeriesBag + // if loop block outside + if lb.GetBodyStmt().isLoop { // TODO: should recursive check + tsb = lb.GetBodyStmt().Ts + debugPP.Printf("tsb: %v \n", tsb) + if tsb == nil { + tsb = &TimeSeriesBag{} // init + } + + for _, n := range x.GetExternNames() { + vp := x.GetPathForName(m.Store, n) + debugPP.Printf("%v's value path is: %v \n", n, vp) + if vp.Depth == 0 { + continue + } + //vp.Depth -= 1 + // update tsb + if !tsb.isSealed { // only once + nx := &NameExpr{ + Name: n, + Path: vp, } - if pass { - //if v.V != nil { // TODO: consider this, this only happens when recursive closure - debugPP.Printf("capturing v: %v \n", tv) - //debugPP.Printf("captured before update: \n %s \n", captured) - captured.names = append(captured.names, cnx.nx.Name) - captured.values = append(captured.values, tv) - //debugPP.Printf("captured after update: \n %s \n", captured) + tst := Transient{ + nx: nx, } + //tsb := TimeSeriesBag{ + tsb.transient = append(tsb.transient, tst) //} - } else { - debugPP.Println("undefined, skip") + // set back + lb.GetBodyStmt().Ts = tsb + debugPP.Printf("address of Ts is : %p \n", lb.GetBodyStmt().Ts) } } + tsb.isSealed = true } - debugPP.Printf("---captured: %v \n", captured) + + //captured := &Captured{} + //for _, n := range x.GetExternNames() { + // vp := x.GetPathForName(m.Store, n) + // debugPP.Printf("%v's value path is: %v \n", n, vp) + // if vp.Depth == 0 { + // continue + // } + // tv := *m.PopValue() + // debugPP.Printf("tv got is: %+v, tv.T: %v, tv.V: %v, tv.N: %v \n", tv, tv.T, tv.V, tv.N) + // if !tv.IsUndefined() { // e.g. args of func. forward // TODO: consider this + // pass := true + // if _, ok := tv.T.(*FuncType); ok { + // if tv.V == nil { + // pass = false + // } + // } else { + // if tv.T == nil && tv.V == nil && isZeroArray(tv.N) { + // pass = false + // } + // } + // if pass { + // //if v.V != nil { // TODO: consider this, this only happens when recursive closure + // debugPP.Printf("capturing v: %v \n", tv) + // //debugPP.Printf("captured before update: \n %s \n", captured) + // captured.names = append(captured.names, n) + // captured.values = append(captured.values, tv) + // //debugPP.Printf("captured after update: \n %s \n", captured) + // } + // //} + // } else { + // debugPP.Println("undefined, skip") + // } + //} + + //captured := &Captured{} + //if x.Closure != nil { + // for _, cnx := range x.Closure.cnxs { + // debugPP.Printf("range closures of : %s \n", string(cnx.nx.Name)) + // tv := *m.PopValue() + // debugPP.Printf("tv got is: %+v, tv.T: %v, tv.V: %v, tv.N: %v \n", tv, tv.T, tv.V, tv.N) + // + // //c := *m.PopValue() + // //debugPP.Printf("---c is: %v \n", c) + // + // if !tv.IsUndefined() { // e.g. args of func. forward // TODO: consider this + // pass := true + // if _, ok := tv.T.(*FuncType); ok { + // if tv.V == nil { + // pass = false + // } + // } else { + // if tv.T == nil && tv.V == nil && isZeroArray(tv.N) { + // pass = false + // } + // } + // if pass { + // //if v.V != nil { // TODO: consider this, this only happens when recursive closure + // debugPP.Printf("capturing v: %v \n", tv) + // //debugPP.Printf("captured before update: \n %s \n", captured) + // captured.names = append(captured.names, cnx.nx.Name) + // captured.values = append(captured.values, tv) + // //debugPP.Printf("captured after update: \n %s \n", captured) + // } + // //} + // } else { + // debugPP.Println("undefined, skip") + // } + // } + //} ft := m.PopValue().V.(TypeValue).Type.(*FuncType) @@ -778,7 +874,7 @@ func (m *Machine) doOpFuncLit() { Source: x, Name: "", Closure: lb, - Captures: captured, + Ts: tsb, PkgPath: m.Package.PkgPath, body: x.Body, nativeBody: nil, diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 3f81129bbec..6f528c133b2 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -187,7 +187,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } }() if debug { - //debug.Printf("Preprocess %s (%v) stage:%v\n", n.String(), reflect.TypeOf(n), stage) + debug.Printf("Preprocess %s (%v) stage:%v\n", n.String(), reflect.TypeOf(n), stage) } switch stage { @@ -1986,6 +1986,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } func pushInitBlock(bn BlockNode, last *BlockNode, stack *[]BlockNode) { + debugPP.Printf("pushInitBlock : %v \n", bn) if !bn.IsInitialized() { bn.InitStaticBlock(bn, *last) } else { @@ -1997,6 +1998,7 @@ func pushInitBlock(bn BlockNode, last *BlockNode, stack *[]BlockNode) { if bn.GetStaticBlock().Source != bn { panic("expected the source of a block node to be itself") } + //bn.GetStaticBlock().String() *last = bn *stack = append(*stack, bn) } diff --git a/gnovm/pkg/gnolang/transcribe.go b/gnovm/pkg/gnolang/transcribe.go index 31dff768e97..2e0f87b7d41 100644 --- a/gnovm/pkg/gnolang/transcribe.go +++ b/gnovm/pkg/gnolang/transcribe.go @@ -169,6 +169,8 @@ func (ao *AssignOperand) String() string { //} type ClosureContext struct { + hasLoop bool + hasClo bool closures []*Closure nodes []Node ops []Word // assign/define related logic @@ -238,33 +240,46 @@ func (cx *ClosureContext) dumpOps() string { // 2. it's embedded in another volatile block, for/range stmt // 2. no recursive closure(not support for now) func (cx *ClosureContext) hasClosure() bool { - loopBlock := false - for _, n := range cx.nodes { - switch n.(type) { - case *ForStmt, *RangeStmt: - loopBlock = true - case *FuncLitExpr: - return loopBlock // has loopBlock ahead - default: - // do nothing - } - //if _, ok := n.(*FuncLitExpr); ok { - // return true - //} - } - + return cx.hasClo + //loopBlock := false + //for _, n := range cx.nodes { + // switch n.(type) { + // case *ForStmt, *RangeStmt: + // loopBlock = true + // case *FuncLitExpr: + // return loopBlock // has loopBlock ahead + // default: + // // do nothing + // } + // //if _, ok := n.(*FuncLitExpr); ok { + // // return true + // //} + //} + // // detect cyclic // 1. encounter a nx, its type is funcLitExpr, name it t; how to get it? // 2. compare t with parent node type, could be direct ancestor, or indirect // namely, if two(or more) same funcLitExpr appears in stack, have cyclic // which implies !hasClosure - return false + //return false } func (cx *ClosureContext) pushNode(n Node) { - debugPP.Println("+++Cx push node") - debugPP.Println("+node") + debugPP.Println("+++Cx push node, node++") + if !cx.hasLoop { + switch n.(type) { + case *ForStmt, *RangeStmt: + cx.hasLoop = true + default: + // do nothing + } + } else { // has loop + if _, ok := n.(*FuncLitExpr); ok { + cx.hasClo = true + } + } + cx.nodes = append(cx.nodes, n) } @@ -274,6 +289,10 @@ func (cx *ClosureContext) popNode() { debugPP.Println("-node") cx.nodes = cx.nodes[:len(cx.nodes)-1] debugPP.Printf("len of node: %d \n", len(cx.nodes)) + if len(cx.nodes) == 0 { // reset flag + cx.hasLoop = false + cx.hasClo = false + } } } @@ -306,7 +325,7 @@ func (cx *ClosureContext) popClosure(copy bool) *Closure { c := cx.closures[len(cx.closures)-1] // get current for _, cnx := range c.cnxs { // pop-> increase offset debug.Printf("+1 \n") // mutate offset - cnx.offset += 1 + cnx.offset += 1 // from bottom up } cx.closures = cx.closures[:len(cx.closures)-1] // shrink @@ -394,10 +413,6 @@ func (c *Closure) String() string { return s } -//func NewClosure() *Closure { -// return &Closure{} -//} - func (clo *Closure) Fill(cnx CapturedNx) { debug.Printf("+nx: %v \n", cnx.nx) for _, n := range clo.names { // filter out existed nx @@ -439,6 +454,7 @@ func Transcribe(n Node, t Transform) (nn Node) { } func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc *TransCtrl) (nn Node) { + debugPP.Printf("transcribe, n is: %v \n", n) // transcribe n on the way in. var c TransCtrl nn, c = t(ns, ftype, index, n, TRANS_ENTER) @@ -453,6 +469,12 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc switch cnn := nn.(type) { case *NameExpr: debugPP.Printf("-----trans, nameExpr: %v \n", cnn) + //deps := make(map[Name]struct{}) + //findDependentNames(cnn, deps) + //for k, v := range deps { + // debugPP.Printf("%s depends on %v \n", k, v) + //} + if CX.hasClosure() { debugPP.Printf("---has Closure, check: %v \n", cnn) debugPP.Println("---currentOp: ", CX.peekOp()) @@ -651,7 +673,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc if pushed { CX.popClosure(isCopy) } - CX.popNode() + //CX.popNode() return } } @@ -832,6 +854,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc cnn = cnn2.(*ForStmt) } + // after block structure is build CX.pushNode(cnn) pushed := CX.pushClosure() diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index 6704e5a7c4f..357a6e46adc 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -513,6 +513,16 @@ func (sv *StructValue) Copy(alloc *Allocator) *StructValue { return alloc.NewStruct(fields) } +type TimeSeriesBag struct { + isSealed bool + transient []Transient +} + +type Transient struct { + nx *NameExpr + values []TypedValue +} + type Captured struct { names []Name values []TypedValue @@ -551,6 +561,7 @@ type FuncValue struct { Name Name // name of function/method Closure Value // *Block or RefValue to closure (may be nil for file blocks; lazy) Captures *Captured + Ts *TimeSeriesBag FileName Name // file name where declared PkgPath string NativePkg string // for native bindings through NativeStore diff --git a/gnovm/tests/debug/1.gno b/gnovm/tests/debug2/1.gno similarity index 100% rename from gnovm/tests/debug/1.gno rename to gnovm/tests/debug2/1.gno diff --git a/gnovm/tests/debug/1a.gno b/gnovm/tests/debug2/1a.gno similarity index 100% rename from gnovm/tests/debug/1a.gno rename to gnovm/tests/debug2/1a.gno diff --git a/gnovm/tests/debug/1b.gno b/gnovm/tests/debug2/1b.gno similarity index 100% rename from gnovm/tests/debug/1b.gno rename to gnovm/tests/debug2/1b.gno diff --git a/gnovm/tests/debug/1c.gno b/gnovm/tests/debug2/1c.gno similarity index 100% rename from gnovm/tests/debug/1c.gno rename to gnovm/tests/debug2/1c.gno diff --git a/gnovm/tests/debug/1d.gno b/gnovm/tests/debug2/1d.gno similarity index 100% rename from gnovm/tests/debug/1d.gno rename to gnovm/tests/debug2/1d.gno diff --git a/gnovm/tests/debug/1e.gno b/gnovm/tests/debug2/1e.gno similarity index 100% rename from gnovm/tests/debug/1e.gno rename to gnovm/tests/debug2/1e.gno diff --git a/gnovm/tests/debug/2.gno b/gnovm/tests/debug2/2.gno similarity index 100% rename from gnovm/tests/debug/2.gno rename to gnovm/tests/debug2/2.gno diff --git a/gnovm/tests/debug/2a.gno b/gnovm/tests/debug2/2a.gno similarity index 100% rename from gnovm/tests/debug/2a.gno rename to gnovm/tests/debug2/2a.gno diff --git a/gnovm/tests/debug/3.gno b/gnovm/tests/debug2/3.gno similarity index 100% rename from gnovm/tests/debug/3.gno rename to gnovm/tests/debug2/3.gno diff --git a/gnovm/tests/debug/3a.gno b/gnovm/tests/debug2/3a.gno similarity index 100% rename from gnovm/tests/debug/3a.gno rename to gnovm/tests/debug2/3a.gno diff --git a/gnovm/tests/debug/3b.gno b/gnovm/tests/debug2/3b.gno similarity index 100% rename from gnovm/tests/debug/3b.gno rename to gnovm/tests/debug2/3b.gno diff --git a/gnovm/tests/debug/4.gno b/gnovm/tests/debug2/4.gno similarity index 100% rename from gnovm/tests/debug/4.gno rename to gnovm/tests/debug2/4.gno diff --git a/gnovm/tests/debug/4a.gno b/gnovm/tests/debug2/4a.gno similarity index 100% rename from gnovm/tests/debug/4a.gno rename to gnovm/tests/debug2/4a.gno diff --git a/gnovm/tests/debug/4b.gno b/gnovm/tests/debug2/4b.gno similarity index 100% rename from gnovm/tests/debug/4b.gno rename to gnovm/tests/debug2/4b.gno diff --git a/gnovm/tests/debug/4c.gno b/gnovm/tests/debug2/4c.gno similarity index 100% rename from gnovm/tests/debug/4c.gno rename to gnovm/tests/debug2/4c.gno diff --git a/gnovm/tests/debug/4d.gnoa b/gnovm/tests/debug2/4d.gnoa similarity index 100% rename from gnovm/tests/debug/4d.gnoa rename to gnovm/tests/debug2/4d.gnoa diff --git a/gnovm/tests/debug/4e.gno b/gnovm/tests/debug2/4e.gno similarity index 100% rename from gnovm/tests/debug/4e.gno rename to gnovm/tests/debug2/4e.gno diff --git a/gnovm/tests/debug/4f.gno b/gnovm/tests/debug2/4f.gno similarity index 100% rename from gnovm/tests/debug/4f.gno rename to gnovm/tests/debug2/4f.gno diff --git a/gnovm/tests/debug/4g.gno b/gnovm/tests/debug2/4g.gno similarity index 100% rename from gnovm/tests/debug/4g.gno rename to gnovm/tests/debug2/4g.gno diff --git a/gnovm/tests/debug/4h.gno b/gnovm/tests/debug2/4h.gno similarity index 100% rename from gnovm/tests/debug/4h.gno rename to gnovm/tests/debug2/4h.gno diff --git a/gnovm/tests/debug/4i.gno b/gnovm/tests/debug2/4i.gno similarity index 100% rename from gnovm/tests/debug/4i.gno rename to gnovm/tests/debug2/4i.gno diff --git a/gnovm/tests/debug/5.gno b/gnovm/tests/debug2/5.gno similarity index 100% rename from gnovm/tests/debug/5.gno rename to gnovm/tests/debug2/5.gno diff --git a/gnovm/tests/debug/5a.gno b/gnovm/tests/debug2/5a.gno similarity index 100% rename from gnovm/tests/debug/5a.gno rename to gnovm/tests/debug2/5a.gno diff --git a/gnovm/tests/debug/5b.gno b/gnovm/tests/debug2/5b.gno similarity index 100% rename from gnovm/tests/debug/5b.gno rename to gnovm/tests/debug2/5b.gno diff --git a/gnovm/tests/debug/5b0.gno b/gnovm/tests/debug2/5b0.gno similarity index 100% rename from gnovm/tests/debug/5b0.gno rename to gnovm/tests/debug2/5b0.gno diff --git a/gnovm/tests/debug/5b1.gno b/gnovm/tests/debug2/5b1.gno similarity index 100% rename from gnovm/tests/debug/5b1.gno rename to gnovm/tests/debug2/5b1.gno diff --git a/gnovm/tests/debug/6.gno b/gnovm/tests/debug2/6.gno similarity index 100% rename from gnovm/tests/debug/6.gno rename to gnovm/tests/debug2/6.gno diff --git a/gnovm/tests/debug/6a.gno b/gnovm/tests/debug2/6a.gno similarity index 100% rename from gnovm/tests/debug/6a.gno rename to gnovm/tests/debug2/6a.gno diff --git a/gnovm/tests/debug/7.gno b/gnovm/tests/debug2/7.gno similarity index 100% rename from gnovm/tests/debug/7.gno rename to gnovm/tests/debug2/7.gno diff --git a/gnovm/tests/debug/7a.gno b/gnovm/tests/debug2/7a.gno similarity index 100% rename from gnovm/tests/debug/7a.gno rename to gnovm/tests/debug2/7a.gno diff --git a/gnovm/tests/debug/closure0.gno b/gnovm/tests/debug2/closure0.gno similarity index 100% rename from gnovm/tests/debug/closure0.gno rename to gnovm/tests/debug2/closure0.gno diff --git a/gnovm/tests/debug/closure1.gno b/gnovm/tests/debug2/closure1.gno similarity index 100% rename from gnovm/tests/debug/closure1.gno rename to gnovm/tests/debug2/closure1.gno diff --git a/gnovm/tests/debug/closure2.gno b/gnovm/tests/debug2/closure2.gno similarity index 100% rename from gnovm/tests/debug/closure2.gno rename to gnovm/tests/debug2/closure2.gno diff --git a/gnovm/tests/debug/closure3.gno b/gnovm/tests/debug2/closure3.gno similarity index 100% rename from gnovm/tests/debug/closure3.gno rename to gnovm/tests/debug2/closure3.gno diff --git a/gnovm/tests/debug/closure4.gno b/gnovm/tests/debug2/closure4.gno similarity index 100% rename from gnovm/tests/debug/closure4.gno rename to gnovm/tests/debug2/closure4.gno diff --git a/gnovm/tests/debug/closure5.gno b/gnovm/tests/debug2/closure5.gno similarity index 100% rename from gnovm/tests/debug/closure5.gno rename to gnovm/tests/debug2/closure5.gno diff --git a/gnovm/tests/debug/closure6.gno b/gnovm/tests/debug2/closure6.gno similarity index 100% rename from gnovm/tests/debug/closure6.gno rename to gnovm/tests/debug2/closure6.gno diff --git a/gnovm/tests/debug/closure7.gno b/gnovm/tests/debug2/closure7.gno similarity index 100% rename from gnovm/tests/debug/closure7.gno rename to gnovm/tests/debug2/closure7.gno diff --git a/gnovm/tests/debug/closure8.gno b/gnovm/tests/debug2/closure8.gno similarity index 100% rename from gnovm/tests/debug/closure8.gno rename to gnovm/tests/debug2/closure8.gno diff --git a/gnovm/tests/debug/recover4.gno b/gnovm/tests/debug2/recover4.gno similarity index 100% rename from gnovm/tests/debug/recover4.gno rename to gnovm/tests/debug2/recover4.gno diff --git a/gnovm/tests/debug/recover6.gno b/gnovm/tests/debug2/recover6.gno similarity index 100% rename from gnovm/tests/debug/recover6.gno rename to gnovm/tests/debug2/recover6.gno diff --git a/gnovm/tests/debug/recover6a.gno b/gnovm/tests/debug2/recover6a.gno similarity index 100% rename from gnovm/tests/debug/recover6a.gno rename to gnovm/tests/debug2/recover6a.gno diff --git a/gnovm/tests/debug/zregexp_stdlibs.gno b/gnovm/tests/debug2/zregexp_stdlibs.gno similarity index 100% rename from gnovm/tests/debug/zregexp_stdlibs.gno rename to gnovm/tests/debug2/zregexp_stdlibs.gno diff --git a/gnovm/tests/file.go b/gnovm/tests/file.go index a021628a385..8cfee1f8d50 100644 --- a/gnovm/tests/file.go +++ b/gnovm/tests/file.go @@ -3,6 +3,7 @@ package tests import ( "bytes" "fmt" + "github.com/gnolang/gno/tm2/pkg/bft/types/time" "go/ast" "go/parser" "go/token" @@ -148,7 +149,10 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error { f.logger("RUN FILES & INIT") f.logger("========================================") } + if !gno.IsRealmPath(pkgPath) { + startTime := time.Now() + // simple case. pn := gno.NewPackageNode(pkgName, pkgPath, &gno.FileSet{}) pv := pn.NewPackage() @@ -162,7 +166,17 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error { f.logger("RUN MAIN") f.logger("========================================") } + endTime := time.Now() + elapsedTime := endTime.Sub(startTime) + fmt.Printf("Run Init took %s to execute\n", elapsedTime) + + startTime = time.Now() + m.RunMain() + endTime = time.Now() + elapsedTime = endTime.Sub(startTime) + + fmt.Printf("Run Main took %s to execute\n", elapsedTime) if f.logger != nil { f.logger("========================================") f.logger("RUN MAIN END") From 480832134e9b8faa3ab55415c3147168380420b0 Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Wed, 24 Jan 2024 00:00:38 +0800 Subject: [PATCH 12/32] fixup --- gnovm/pkg/gnolang/op_call.go | 83 ++++----- gnovm/pkg/gnolang/op_exec.go | 54 ++++-- gnovm/pkg/gnolang/op_expressions.go | 240 +++++++++++++++----------- gnovm/pkg/gnolang/values.go | 58 ++++++- gnovm/tests/debug/1.gno | 22 +++ gnovm/tests/debug/1a.gno | 22 +++ gnovm/tests/debug/1b.gno | 22 +++ gnovm/tests/debug/1c.gno | 18 ++ gnovm/tests/debug/1d.gno | 29 ++++ gnovm/tests/debug/1e.gno | 12 ++ gnovm/tests/debug/1f.gno | 53 ++++++ gnovm/tests/debug/1g.gno | 24 +++ gnovm/tests/debug/1h.gno | 30 ++++ gnovm/tests/debug/2.gno | 26 +++ gnovm/tests/debug/2a.gno | 26 +++ gnovm/tests/debug/3.gno | 25 +++ gnovm/tests/debug/3a.gno | 23 +++ gnovm/tests/debug/3b.gno | 27 +++ gnovm/tests/debug/4.gno | 22 +++ gnovm/tests/debug/4a.gno | 23 +++ gnovm/tests/debug/4b.gno | 24 +++ gnovm/tests/debug/4c.gno | 27 +++ gnovm/tests/debug/4d.gnoa | 0 gnovm/tests/debug/4e.gno | 27 +++ gnovm/tests/debug/4f.gno | 20 +++ gnovm/tests/debug/4g.gno | 14 ++ gnovm/tests/debug/4h.gno | 25 +++ gnovm/tests/debug/4i.gno | 15 ++ gnovm/tests/debug/5.gno | 22 +++ gnovm/tests/debug/5a.gno | 24 +++ gnovm/tests/debug/5b.gno | 46 +++++ gnovm/tests/debug/5b0.gno | 26 +++ gnovm/tests/debug/5b1.gno | 26 +++ gnovm/tests/debug/6.gno | 23 +++ gnovm/tests/debug/6a.gno | 24 +++ gnovm/tests/debug/7.gno | 32 ++++ gnovm/tests/debug/7a.gno | 36 ++++ gnovm/tests/debug/closure0.gno | 17 ++ gnovm/tests/debug/closure1.gno | 20 +++ gnovm/tests/debug/closure2.gno | 28 +++ gnovm/tests/debug/closure3.gno | 23 +++ gnovm/tests/debug/closure4.gno | 23 +++ gnovm/tests/debug/closure5.gno | 23 +++ gnovm/tests/debug/closure6.gno | 23 +++ gnovm/tests/debug/closure7.gno | 28 +++ gnovm/tests/debug/closure8.gno | 10 ++ gnovm/tests/debug/io2.gno | 21 +++ gnovm/tests/debug/recover4.gno | 25 +++ gnovm/tests/debug/recover6.gno | 30 ++++ gnovm/tests/debug/recover6a.gno | 40 +++++ gnovm/tests/debug/zregexp_stdlibs.gno | 19 ++ gnovm/tests/debug2/1f.gno | 53 ++++++ gnovm/tests/debug2/1g.gno | 24 +++ gnovm/tests/debug2/1h.gno | 30 ++++ gnovm/tests/debug2/5b.gno | 2 + gnovm/tests/debug2/5b0.gno | 9 +- gnovm/tests/debug2/7.gno | 9 +- gnovm/tests/debug2/7a.gno | 8 + gnovm/tests/debug2/io2.gno | 21 +++ 59 files changed, 1570 insertions(+), 166 deletions(-) create mode 100644 gnovm/tests/debug/1.gno create mode 100644 gnovm/tests/debug/1a.gno create mode 100644 gnovm/tests/debug/1b.gno create mode 100644 gnovm/tests/debug/1c.gno create mode 100644 gnovm/tests/debug/1d.gno create mode 100644 gnovm/tests/debug/1e.gno create mode 100644 gnovm/tests/debug/1f.gno create mode 100644 gnovm/tests/debug/1g.gno create mode 100644 gnovm/tests/debug/1h.gno create mode 100644 gnovm/tests/debug/2.gno create mode 100644 gnovm/tests/debug/2a.gno create mode 100644 gnovm/tests/debug/3.gno create mode 100644 gnovm/tests/debug/3a.gno create mode 100644 gnovm/tests/debug/3b.gno create mode 100644 gnovm/tests/debug/4.gno create mode 100644 gnovm/tests/debug/4a.gno create mode 100644 gnovm/tests/debug/4b.gno create mode 100644 gnovm/tests/debug/4c.gno create mode 100644 gnovm/tests/debug/4d.gnoa create mode 100644 gnovm/tests/debug/4e.gno create mode 100644 gnovm/tests/debug/4f.gno create mode 100644 gnovm/tests/debug/4g.gno create mode 100644 gnovm/tests/debug/4h.gno create mode 100644 gnovm/tests/debug/4i.gno create mode 100644 gnovm/tests/debug/5.gno create mode 100644 gnovm/tests/debug/5a.gno create mode 100644 gnovm/tests/debug/5b.gno create mode 100644 gnovm/tests/debug/5b0.gno create mode 100644 gnovm/tests/debug/5b1.gno create mode 100644 gnovm/tests/debug/6.gno create mode 100644 gnovm/tests/debug/6a.gno create mode 100644 gnovm/tests/debug/7.gno create mode 100644 gnovm/tests/debug/7a.gno create mode 100644 gnovm/tests/debug/closure0.gno create mode 100644 gnovm/tests/debug/closure1.gno create mode 100644 gnovm/tests/debug/closure2.gno create mode 100644 gnovm/tests/debug/closure3.gno create mode 100644 gnovm/tests/debug/closure4.gno create mode 100644 gnovm/tests/debug/closure5.gno create mode 100644 gnovm/tests/debug/closure6.gno create mode 100644 gnovm/tests/debug/closure7.gno create mode 100644 gnovm/tests/debug/closure8.gno create mode 100644 gnovm/tests/debug/io2.gno create mode 100644 gnovm/tests/debug/recover4.gno create mode 100644 gnovm/tests/debug/recover6.gno create mode 100644 gnovm/tests/debug/recover6a.gno create mode 100644 gnovm/tests/debug/zregexp_stdlibs.gno create mode 100644 gnovm/tests/debug2/1f.gno create mode 100644 gnovm/tests/debug2/1g.gno create mode 100644 gnovm/tests/debug2/1h.gno create mode 100644 gnovm/tests/debug2/io2.gno diff --git a/gnovm/pkg/gnolang/op_call.go b/gnovm/pkg/gnolang/op_call.go index a63ca206e9f..47f7b1bce14 100644 --- a/gnovm/pkg/gnolang/op_call.go +++ b/gnovm/pkg/gnolang/op_call.go @@ -61,11 +61,6 @@ func (m *Machine) doOpCall() { pts := ft.Params numParams := len(pts) isMethod := 0 // 1 if true - // Create new block scope. - //debugPP.Printf("fv is:---") - //fv.dump() - //debugPP.Printf("fv.captures is: %v \n", fv.Captures) - //debugPP.Printf("fv.address is: %p \n", fv) clo := fr.Func.GetClosure(m.Store) debugPP.Printf("-----got closure: %v ----- \n", clo) @@ -74,54 +69,48 @@ func (m *Machine) doOpCall() { if captures == nil { debugPP.Println("nil captures") } - //if captures != nil { - // debugPP.Printf("captures before call: %v, len(names): %d, len(values): %d \n", captures, len(captures.names), len(captures.values)) - // names := clo.GetSource(m.Store).GetBlockNames() - // debugPP.Printf("names: %v \n", names) - // for i1, n1 := range captures.names { - // var index int - // for i2, n2 := range names { - // if n1 == n2 { // match and replace - // index = i2 - // debugPP.Printf("index of %s in target block is: %d \n", n1, index) - // debugPP.Printf("target tv[%d] in captured values is :%v \n", i1, captures.values[i1]) - // // replace lv values with index - // clo.UpdateValue(index, captures.values[i1]) - // } - // } - // } - //} - debugPP.Println("---parse transient") - if fv.Ts != nil { + var targetB *Block + debugPP.Println("================parse transient for fv====================") + if fv.Tss != nil { var end bool - for i, t := range fv.Ts.transient { - debugPP.Printf("Ts.transient[%d] name is %v, path: %v, len of values is: %v \n", i, t.nx.Name, t.nx.Path, len(t.values)) - for i, v := range t.values { - debugPP.Printf("values[%d] is %v \n", i, v) - } - //debugPP.Println("?????????nil", fv.Ts == nil) + for i, bag := range fv.Tss { // each bag is for a certain level of block + debugPP.Printf("Level[%d] bag is : %v \n", i, bag) + for j, t := range bag.transient { // unpack vars belong to a specific block + if t != nil { - names := clo.GetSource(m.Store).GetBlockNames() - var index int - var match bool - for i, n := range names { - if n == t.nx.Name { - index = i - match = true - } - } - if match { - clo.UpdateValue(index, t.values[0]) - nvs := t.values[1:] - //debugPP.Println("nil????????", fv.Ts == nil) - fv.Ts.transient[i].values = nvs - if len(fv.Ts.transient[i].values) == 0 { // loop end - end = true + debugPP.Printf("Ts.transient[%d] name is %v, path: %v, len of values is: %v \n", j, t.nx.Name, t.nx.Path, len(t.values)) + for k, v := range t.values { + debugPP.Printf("values[%d] is %v \n", k, v) + } + + targetB = clo.GetBlockWithDepth(m.Store, t.nx.Path) // find target block using depth + // check if exists, should always be? + names := targetB.GetSource(m.Store).GetBlockNames() + var index int + var match bool + for l, n := range names { + if n == t.nx.Name { + debugPP.Printf("name: %s match \n", n) + index = l + match = true + break + } + } + if match { + debugPP.Println("===match") + targetB.UpdateValue(index, t.values[0]) + nvs := t.values[1:] + bag.transient[j].values = nvs + if len(bag.transient[j].values) == 0 { // loop end + bag.transient[j] = nil // empty a name and value + end = true + } + } } } } if end { - fv.Ts = nil + fv.Tss = nil } } diff --git a/gnovm/pkg/gnolang/op_exec.go b/gnovm/pkg/gnolang/op_exec.go index 78d9fcba54a..eb1f48127b9 100644 --- a/gnovm/pkg/gnolang/op_exec.go +++ b/gnovm/pkg/gnolang/op_exec.go @@ -118,21 +118,49 @@ func (m *Machine) doOpExec(op Op) { bs.Active = next s = next goto EXEC_SWITCH - } else if bs.NextBodyIndex == bs.BodyLen { // TODO: Update ts slice? - debugPP.Printf("---end of body, current block is: %v \n", m.LastBlock()) + } else if bs.NextBodyIndex == bs.BodyLen { + debugPP.Printf("---end of loop body, going to update value--- \n") // update fv.Capture if value changed - if bs.isLoop { - if bs.Ts != nil { + //if _, ok := findLoopBlockWithPath(m.Store, lb); ok { + if lb.GetBodyStmt().isLoop { + debugPP.Printf("---------isLoop, current block is: %v \n", m.LastBlock()) + if bs.Ts != nil && bs.Ts.isSealed { + debugPP.Printf("---addr of bs.Ts is: %p \n", &bs.Ts) + names := lb.Source.GetBlockNames() + var found bool for i, t := range bs.Ts.transient { - debugPP.Printf("ts[%d] name is %v, path: %v \n", i, t.nx.Name, t.nx.Path) - nvp := lb.Source.GetPathForName(m.Store, t.nx.Name) - ptr := m.LastBlock().GetPointerTo(m.Store, nvp) - tv := ptr.Deref() - debugPP.Printf("--- new tv : %v \n", tv) - //t.value = tv - // set back - bs.Ts.transient[i].values = append(bs.Ts.transient[i].values, tv) - debugPP.Printf("len of Ts values is : %d \n", len(bs.Ts.transient[i].values)) + debugPP.Printf("transient[%d] name is %v, path: %v \n", i, t.nx.Name, t.nx.Path) + // first check if name is in current block, it not, stop + for _, name := range names { + if t.nx.Name == name { + debugPP.Printf("found %s in current block: %v \n", t.nx.Name, lb) + found = true + } + } + if found { + nvp := lb.Source.GetPathForName(m.Store, t.nx.Name) + ptr := m.LastBlock().GetPointerTo(m.Store, nvp) + tv := ptr.Deref() + debugPP.Printf("--- new tv : %v \n", tv) + //t.value = tv + // set back + debugPP.Printf("before update, len of Ts values is : %d \n", len(bs.Ts.transient[i].values)) + + expandRatio := bs.Ts.transient[i].expandRatio + debugPP.Printf("--- expand ratio is: %d \n", expandRatio) + for j := int8(0); j < expandRatio; j++ { + bs.Ts.transient[i].values = append(bs.Ts.transient[i].values, tv) + } + debugPP.Printf("after update, len of Ts values is : %d \n", len(bs.Ts.transient[i].values)) + // update higher level if it is also a loop, repeat state. + upperBlock := findNearestLoopBlock(m.Store, lb) + debugPP.Printf("upperBlock is: %v \n", upperBlock) + if upperBlock != nil { + upperBlock.GetBodyStmt().Ts.setRatio(int8(len(bs.Ts.transient[i].values))) + } + } else { + debugPP.Printf("---not found %s in current block, b: %v \n", t.nx.Name, lb) + } } } } diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index 773ece8b7ac..75372004faa 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -3,6 +3,7 @@ package gnolang import ( "fmt" "reflect" + "sort" ) // OpBinary1 defined in op_binary.go @@ -751,118 +752,161 @@ func isZeroArray(arr [8]byte) bool { } return true } + +func findNearestLoopBlock(store Store, b *Block) *Block { + nb := b.GetParent(store) + for { + if nb != nil { + if nb.GetBodyStmt().isLoop { + return nb + } else { + nb = nb.GetParent(store) + continue + } + } else { + return nil + } + } +} + +// find nearest block is loop and contains name of n +func findLoopBlockWithPath(store Store, b *Block, nx *NameExpr) (*Block, bool, uint8) { + var gen uint8 = 1 + for i := uint8(1); i < nx.Path.Depth; i++ { // find target block at certain depth + b = b.GetParent(store) + gen++ + } + + debugPP.Printf("---b is: %v, \n", b) + if b.GetBodyStmt().isLoop { // is loop + names := b.GetSource(store).GetBlockNames() + for _, name := range names { + if nx.Name == name { // find n in this block + return b, true, gen + } + } + } + return nil, false, gen + + //debugPP.Printf("findLoopBlockWithPath for %s, b is: %v, \n", nx.Name, b) + //for { + // if b != nil { + // debugPP.Printf("b is: %v, \n", b) + // if b.GetBodyStmt().isLoop { // is loop + // names := b.GetSource(store).GetBlockNames() + // for _, name := range names { + // if n == name { // find n in this block + // return b, true + // } + // } + // // not match + // b = b.GetParent(store) + // continue + // } else { + // b = b.GetParent(store) + // continue + // } + // } else { + // return nil, false + // } + //} +} + func (m *Machine) doOpFuncLit() { - debugPP.Println("-----doOpFuncLit") x := m.PopExpr().(*FuncLitExpr) debugPP.Printf("-----doOpFuncLit, x: %v \n", x) lb := m.LastBlock() debugPP.Printf("lb: %v \n", lb) - var tsb *TimeSeriesBag - // if loop block outside - if lb.GetBodyStmt().isLoop { // TODO: should recursive check - tsb = lb.GetBodyStmt().Ts - debugPP.Printf("tsb: %v \n", tsb) - if tsb == nil { - tsb = &TimeSeriesBag{} // init + var captures []*NameExpr + for _, n := range x.GetExternNames() { + debugPP.Println("iterating extern names, n: ", n) + vp := x.GetPathForName(m.Store, n) + debugPP.Printf("%v's value path is: %v \n", n, vp) + if vp.Depth == 0 { + continue + } + vp.Depth -= 1 // from funcLit block not inside + nx := &NameExpr{ + Name: n, + Path: vp, } + captures = append(captures, nx) + } - for _, n := range x.GetExternNames() { - vp := x.GetPathForName(m.Store, n) - debugPP.Printf("%v's value path is: %v \n", n, vp) - if vp.Depth == 0 { - continue + sortDepth := func(i, j int) bool { + return int(captures[i].Path.Depth) < int(captures[j].Path.Depth) + } + sort.Slice(captures, sortDepth) + debugPP.Println("---done sort") + //for i, nx := range captures { + // debugPP.Printf("captures[%d] is : %v \n", i, nx.Name) + //} + + var isLoop bool + var loopBlock *Block + var bag *TimeSeriesBag + + var gen uint8 + var lastGen uint8 + //var changed bool + //var isSet bool + var isReuse bool + var Tss []*TimeSeriesBag + for i, nx := range captures { + debugPP.Printf("captures[%d] is : %v \n", i, nx.Name) + // start search block, in the order of small depth to big depth + loopBlock, isLoop, gen = findLoopBlockWithPath(m.Store, lb, nx) + if lastGen == 0 { + lastGen = gen + } else if gen != lastGen { + //changed = true + lastGen = gen + // set last state + if bag != nil { + bag.isSealed = true + Tss = append(Tss, bag) } - //vp.Depth -= 1 - // update tsb - if !tsb.isSealed { // only once - nx := &NameExpr{ - Name: n, - Path: vp, - } - tst := Transient{ - nx: nx, + debugPP.Printf("========packed bag for %v is: %s \n", loopBlock, bag) + //isSet = true + } + if isLoop { + bag = loopBlock.GetBodyStmt().Ts // get bag from loop block, fill name + debugPP.Printf("got initial bag from target loop block: %v \n", bag) + if bag == nil { + bag = &TimeSeriesBag{} // init + } + if !bag.isSealed { // only once + debugPP.Println("---not sealed---, should pack only once for n: ", nx.Name) + tst := &Transient{ + nx: nx, + expandRatio: 1, // default 1 } - //tsb := TimeSeriesBag{ - tsb.transient = append(tsb.transient, tst) + bag.transient = append(bag.transient, tst) + // set back to loop block properly + loopBlock.GetBodyStmt().Ts = bag + // seal this layer + //if changed { // mean end of a layer, into high layer + // //bag.isSealed = true + // //Tss = append(Tss, bag) + // changed = false //} - // set back - lb.GetBodyStmt().Ts = tsb - debugPP.Printf("address of Ts is : %p \n", lb.GetBodyStmt().Ts) + } else { // repack when iterates, this should be optimized + isReuse = true + Tss = append(Tss, bag) } } - tsb.isSealed = true + } + // tail set + if bag != nil && !isReuse { + bag.isSealed = true // set for the last bag + Tss = append(Tss, bag) // collect the last bag + debugPP.Printf("========packed bag for %v is: %s \n", loopBlock, bag) } - //captured := &Captured{} - //for _, n := range x.GetExternNames() { - // vp := x.GetPathForName(m.Store, n) - // debugPP.Printf("%v's value path is: %v \n", n, vp) - // if vp.Depth == 0 { - // continue - // } - // tv := *m.PopValue() - // debugPP.Printf("tv got is: %+v, tv.T: %v, tv.V: %v, tv.N: %v \n", tv, tv.T, tv.V, tv.N) - // if !tv.IsUndefined() { // e.g. args of func. forward // TODO: consider this - // pass := true - // if _, ok := tv.T.(*FuncType); ok { - // if tv.V == nil { - // pass = false - // } - // } else { - // if tv.T == nil && tv.V == nil && isZeroArray(tv.N) { - // pass = false - // } - // } - // if pass { - // //if v.V != nil { // TODO: consider this, this only happens when recursive closure - // debugPP.Printf("capturing v: %v \n", tv) - // //debugPP.Printf("captured before update: \n %s \n", captured) - // captured.names = append(captured.names, n) - // captured.values = append(captured.values, tv) - // //debugPP.Printf("captured after update: \n %s \n", captured) - // } - // //} - // } else { - // debugPP.Println("undefined, skip") - // } - //} - - //captured := &Captured{} - //if x.Closure != nil { - // for _, cnx := range x.Closure.cnxs { - // debugPP.Printf("range closures of : %s \n", string(cnx.nx.Name)) - // tv := *m.PopValue() - // debugPP.Printf("tv got is: %+v, tv.T: %v, tv.V: %v, tv.N: %v \n", tv, tv.T, tv.V, tv.N) - // - // //c := *m.PopValue() - // //debugPP.Printf("---c is: %v \n", c) - // - // if !tv.IsUndefined() { // e.g. args of func. forward // TODO: consider this - // pass := true - // if _, ok := tv.T.(*FuncType); ok { - // if tv.V == nil { - // pass = false - // } - // } else { - // if tv.T == nil && tv.V == nil && isZeroArray(tv.N) { - // pass = false - // } - // } - // if pass { - // //if v.V != nil { // TODO: consider this, this only happens when recursive closure - // debugPP.Printf("capturing v: %v \n", tv) - // //debugPP.Printf("captured before update: \n %s \n", captured) - // captured.names = append(captured.names, cnx.nx.Name) - // captured.values = append(captured.values, tv) - // //debugPP.Printf("captured after update: \n %s \n", captured) - // } - // //} - // } else { - // debugPP.Println("undefined, skip") - // } - // } - //} + for i, ts := range Tss { + debugPP.Printf("========Tss[%d] is: %s, addr: %p \n", i, ts, &ts) + } ft := m.PopValue().V.(TypeValue).Type.(*FuncType) @@ -874,7 +918,7 @@ func (m *Machine) doOpFuncLit() { Source: x, Name: "", Closure: lb, - Ts: tsb, + Tss: Tss, PkgPath: m.Package.PkgPath, body: x.Body, nativeBody: nil, diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index 357a6e46adc..d26c3d92ad9 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -1,7 +1,9 @@ package gnolang import ( + "crypto/sha256" "encoding/binary" + "encoding/hex" "fmt" "math" "math/big" @@ -515,12 +517,34 @@ func (sv *StructValue) Copy(alloc *Allocator) *StructValue { type TimeSeriesBag struct { isSealed bool - transient []Transient + transient []*Transient +} + +func (tsb *TimeSeriesBag) setRatio(r int8) { + for _, tt := range tsb.transient { + tt.expandRatio = r + } +} + +func (tsb *TimeSeriesBag) String() string { + var s string + s += "\n" + s += "==================time bag===================\n" + s += fmt.Sprintf("isSealed: %v \n", tsb.isSealed) + for i, t := range tsb.transient { + s += fmt.Sprintf("nx[%d]: %v \n", i, t.nx) + for j, v := range t.values { + s += fmt.Sprintf("values[%d]: %v \n", j, v) + } + } + s += "====================end======================\n" + return s } type Transient struct { - nx *NameExpr - values []TypedValue + nx *NameExpr + expandRatio int8 // TODO: determine proper type + values []TypedValue // one name with multi snapshot } type Captured struct { @@ -561,7 +585,7 @@ type FuncValue struct { Name Name // name of function/method Closure Value // *Block or RefValue to closure (may be nil for file blocks; lazy) Captures *Captured - Ts *TimeSeriesBag + Tss []*TimeSeriesBag FileName Name // file name where declared PkgPath string NativePkg string // for native bindings through NativeStore @@ -2294,6 +2318,21 @@ func NewBlock(source BlockNode, parent *Block) *Block { } } +func (b *Block) Hash() string { + // Convert the struct to a byte slice (serialization) + structBytes := []byte(fmt.Sprintf("%s%d", b.ObjectInfo, b.Source, b.Values, b.Parent, b.Blank, b.bodyStmt)) + + // Hash the byte slice using SHA-256 + hash := sha256.Sum256(structBytes) + + // Convert the hash to a hexadecimal string + hashHex := hex.EncodeToString(hash[:]) + + fmt.Println("Original struct:", b) + fmt.Println("Hashed value:", hashHex) + return hashHex +} + func (b *Block) UpdateValue(index int, tv TypedValue) { debug.Printf("-----UpdateValue, index: %d, tv: %v \n", index, tv) debug.Printf("b before update: %v \n", b) @@ -2316,8 +2355,8 @@ func (b *Block) StringIndented(indent string) string { } lines := []string{} lines = append(lines, - fmt.Sprintf("Block(ID:%v,Addr:%p,Source:%s,Parent:%p)", - b.ObjectInfo.ID, b, source, b.Parent)) // XXX Parent may be RefValue{}. + fmt.Sprintf("Block(ID:%v,HASH:%v,Addr:%p,Source:%s,Parent:%p)", + b.ObjectInfo.ID, b.ObjectInfo.Hash, b, source, b.Parent)) // XXX Parent may be RefValue{}. if b.Source != nil { if _, ok := b.Source.(RefNode); ok { lines = append(lines, @@ -2379,6 +2418,13 @@ func (b *Block) GetPointerToInt(store Store, index int) PointerValue { } } +func (b *Block) GetBlockWithDepth(store Store, path ValuePath) *Block { + for i := uint8(1); i < path.Depth; i++ { + b = b.GetParent(store) + } + return b +} + func (b *Block) GetPointerTo(store Store, path ValuePath) PointerValue { debugPP.Printf("-----GetPointerTo, path : %v\n", path) debugPP.Printf("b: %v \n", b) diff --git a/gnovm/tests/debug/1.gno b/gnovm/tests/debug/1.gno new file mode 100644 index 00000000000..7f4231373ea --- /dev/null +++ b/gnovm/tests/debug/1.gno @@ -0,0 +1,22 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 5; i++ { + x := i + f := func() int { + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 diff --git a/gnovm/tests/debug/1a.gno b/gnovm/tests/debug/1a.gno new file mode 100644 index 00000000000..4a2a7630f6d --- /dev/null +++ b/gnovm/tests/debug/1a.gno @@ -0,0 +1,22 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 2; i++ { + x := i + y := 0 + f := func() int { + x += y + //x += 1 + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/debug/1b.gno b/gnovm/tests/debug/1b.gno new file mode 100644 index 00000000000..0822368fe3a --- /dev/null +++ b/gnovm/tests/debug/1b.gno @@ -0,0 +1,22 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 2; i++ { + x := i + y := 0 + f := func() int { + x += y + x += 1 + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 1 +// 2 diff --git a/gnovm/tests/debug/1c.gno b/gnovm/tests/debug/1c.gno new file mode 100644 index 00000000000..e5c61c59089 --- /dev/null +++ b/gnovm/tests/debug/1c.gno @@ -0,0 +1,18 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 2; i++ { + f := func() int { + return i + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/debug/1d.gno b/gnovm/tests/debug/1d.gno new file mode 100644 index 00000000000..185961c741f --- /dev/null +++ b/gnovm/tests/debug/1d.gno @@ -0,0 +1,29 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 5; i++ { + x := i + f := func() int { + x := 5 + return x + } + println(x) + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 +// 5 +// 5 +// 5 +// 5 +// 5 diff --git a/gnovm/tests/debug/1e.gno b/gnovm/tests/debug/1e.gno new file mode 100644 index 00000000000..b3b2b2b0385 --- /dev/null +++ b/gnovm/tests/debug/1e.gno @@ -0,0 +1,12 @@ +package main + +func main() { + for i := 0; i < 5; i++ { + if i == 1 { + panic("error 1") + } + } +} + +// Error: +// error 1 diff --git a/gnovm/tests/debug/1f.gno b/gnovm/tests/debug/1f.gno new file mode 100644 index 00000000000..19722ec3ae6 --- /dev/null +++ b/gnovm/tests/debug/1f.gno @@ -0,0 +1,53 @@ +package main + +// 1. we can determine target vars by hasClosure pattern and externNames(without uverse). +// it can be a loopvar, or vars derived from loopvar, e.g. x := i; +// 2. now we need to capture every transient state of the target var; firstly we should +// eval the transient state, there are two ways around: +// a) eval funcLit, but the time doing this is hard to determine, namely, you have to eval +// before it leaves its using context. In some sense, you have to cover all cases when a funcValue +// is used, like assign, xxxLit, funcCall as an arg, etc. +// b) another way for this is to generate a `time series` values that the index is the loop index, +// e.g. in a for loop, we define a new var like x := i, we store the transient state of x per loop +// in the time series, which is a slice. This slice is used when the closure fv is called, +// replacing the var in default block.(func value have a slice of name indicates it's captured, when +// eval name in this slice, using the time series instead). +// this solution mainly differs that it eval target x independently with the closure funcLit, so it avoids +// the obsession to determine the eval order. this seems the right way. hmmm. + +//======================================================================================================== +// work flow 1.0, hard to do +// 1. determine loop var, whole logic depends on this, the key word is dynamic, it causes polymorphic. +// this should most be done via define(), // TODO: check it +// 2. in transcribe, find if loopvar is used somewhere, to identify pattern like x := i, what to do with +// other use cases? this brings complexity. This is wrong feel. +// we can generate the ts slice for every target var, e.g. x in this case. if two vars, two slices. so fv should +// reference a slice of slice. + +// work flow 2.0 +// 1. ignore loop var, assume all externNames apart from uverse is target captured var, this also needs to happen +// in transcribe, tag it, and can be checked everywhere it's appears in runtime, x := i, or var x int, etc. +// when eval funcLit, new a slice of slice and push value for further update. + +func main() { + var fns []func() int + for i := 0; i < 5; i++ { + //x := i // check if x is polymorphic, if yes, new a slice and set value by index + var x int // new a slice no init, this would be updated later, so the slice can be mutated after opFuncLit + f := func() int { + return x // set the slice to fv in + } + x = i // check if x is polymorphic, is yes, update slice assigned to fv before this, which is a reference. + fns = append(fns, f) // where should eval funcLit, where it assigned somewhere, or used in another func lit + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 diff --git a/gnovm/tests/debug/1g.gno b/gnovm/tests/debug/1g.gno new file mode 100644 index 00000000000..e7a6b64f1f3 --- /dev/null +++ b/gnovm/tests/debug/1g.gno @@ -0,0 +1,24 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 5; i++ { + x := i + { + f := func() int { + return x + } + fns = append(fns, f) + } + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 diff --git a/gnovm/tests/debug/1h.gno b/gnovm/tests/debug/1h.gno new file mode 100644 index 00000000000..addd660d04a --- /dev/null +++ b/gnovm/tests/debug/1h.gno @@ -0,0 +1,30 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 5; i++ { + x := i + for j := 0; j < 2; j++ { + y := j + f := func() int { + return x + y + } + fns = append(fns, f) + } + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 1 +// 2 +// 2 +// 3 +// 3 +// 4 +// 4 +// 5 diff --git a/gnovm/tests/debug/2.gno b/gnovm/tests/debug/2.gno new file mode 100644 index 00000000000..1a788d9e0b2 --- /dev/null +++ b/gnovm/tests/debug/2.gno @@ -0,0 +1,26 @@ +package main + +import "fmt" + +func bar() func() func() int { + x := 1 + + // First level of closure, modifies x + return func() func() int { + //x++ + // Second level of closure, returns x + return func() int { + return x + } + } +} + +func main() { + f := bar() // f is the first-level closure + g := f() // g is the second-level closure, x is incremented here + + fmt.Println(g()) // prints the value of x after being modified by the first-level closure +} + +// Output: +// 1 diff --git a/gnovm/tests/debug/2a.gno b/gnovm/tests/debug/2a.gno new file mode 100644 index 00000000000..45ad06c6945 --- /dev/null +++ b/gnovm/tests/debug/2a.gno @@ -0,0 +1,26 @@ +package main + +import "fmt" + +func bar() func() func() int { + x := 1 + + // First level of closure, modifies x + return func() func() int { + x++ + // Second level of closure, returns x + return func() int { + return x + } + } +} + +func main() { + f := bar() // f is the first-level closure + g := f() // g is the second-level closure, x is incremented here + + fmt.Println(g()) // prints the value of x after being modified by the first-level closure +} + +// Output: +// 2 diff --git a/gnovm/tests/debug/3.gno b/gnovm/tests/debug/3.gno new file mode 100644 index 00000000000..1ea6e013b0d --- /dev/null +++ b/gnovm/tests/debug/3.gno @@ -0,0 +1,25 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + f := func() int { + if true { + if true { + return x + } + } + return 0 + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/debug/3a.gno b/gnovm/tests/debug/3a.gno new file mode 100644 index 00000000000..2ce3df96097 --- /dev/null +++ b/gnovm/tests/debug/3a.gno @@ -0,0 +1,23 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + f := func() int { + if true { + return x + } + return 0 + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/debug/3b.gno b/gnovm/tests/debug/3b.gno new file mode 100644 index 00000000000..427c6f075f9 --- /dev/null +++ b/gnovm/tests/debug/3b.gno @@ -0,0 +1,27 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + y := 0 + f := func() int { + if true { + x += y + if true { + return x + } + } + return 0 + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/debug/4.gno b/gnovm/tests/debug/4.gno new file mode 100644 index 00000000000..44717e05cc0 --- /dev/null +++ b/gnovm/tests/debug/4.gno @@ -0,0 +1,22 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + f := func() int { + { + return x + } + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/debug/4a.gno b/gnovm/tests/debug/4a.gno new file mode 100644 index 00000000000..1e3eb9fe815 --- /dev/null +++ b/gnovm/tests/debug/4a.gno @@ -0,0 +1,23 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + f := func() int { + for i := 0; i < 1; i++ { + x++ + } + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 1 +// 2 diff --git a/gnovm/tests/debug/4b.gno b/gnovm/tests/debug/4b.gno new file mode 100644 index 00000000000..4351ceaaf49 --- /dev/null +++ b/gnovm/tests/debug/4b.gno @@ -0,0 +1,24 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + s := []int{1, 2} + f := func() int { + for _, v := range s { + x += v + } + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 3 +// 4 diff --git a/gnovm/tests/debug/4c.gno b/gnovm/tests/debug/4c.gno new file mode 100644 index 00000000000..3e42e207649 --- /dev/null +++ b/gnovm/tests/debug/4c.gno @@ -0,0 +1,27 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + y := 1 + f := func() int { + switch y { + case 1: + x += 1 + default: + x += 0 + } + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 1 +// 2 diff --git a/gnovm/tests/debug/4d.gnoa b/gnovm/tests/debug/4d.gnoa new file mode 100644 index 00000000000..e69de29bb2d diff --git a/gnovm/tests/debug/4e.gno b/gnovm/tests/debug/4e.gno new file mode 100644 index 00000000000..17490510b26 --- /dev/null +++ b/gnovm/tests/debug/4e.gno @@ -0,0 +1,27 @@ +package main + +type queueOnePass struct { + sparse []uint32 + dense []uint32 + size, nextIndex uint32 +} + +func newQueue(size int) (q *queueOnePass) { + return &queueOnePass{ + sparse: make([]uint32, size), + dense: make([]uint32, size), + } +} +func main() { + var ( + visitQueue = newQueue(10) + ) + f := func() { + println(visitQueue.size) + } + + f() +} + +// Output: +// 0 diff --git a/gnovm/tests/debug/4f.gno b/gnovm/tests/debug/4f.gno new file mode 100644 index 00000000000..60e327bd2b2 --- /dev/null +++ b/gnovm/tests/debug/4f.gno @@ -0,0 +1,20 @@ +package main + +func main() { + s := []int{1, 2} + + f := func() { + for i, v := range s { // TODO: exclude s, it's final + println(i) + println(v) + } + } + + f() +} + +// Output: +// 0 +// 1 +// 1 +// 2 diff --git a/gnovm/tests/debug/4g.gno b/gnovm/tests/debug/4g.gno new file mode 100644 index 00000000000..bf998c163d8 --- /dev/null +++ b/gnovm/tests/debug/4g.gno @@ -0,0 +1,14 @@ +package main + +func main() { + f := func(a int) bool { + println(a) + return true + } + + println(f(5)) +} + +// Output: +// 5 +// true diff --git a/gnovm/tests/debug/4h.gno b/gnovm/tests/debug/4h.gno new file mode 100644 index 00000000000..c9f1f6e97e8 --- /dev/null +++ b/gnovm/tests/debug/4h.gno @@ -0,0 +1,25 @@ +package main + +func main() { + s := 1 + f := func() { + i := 0 // no capture for i + var j = s // s should be captured, j not + k := s // s should be captured, k not + m, n := s, 0 + println(i) + println(j) + println(k) + println(m) + println(n) + } + + f() +} + +// Output: +// 0 +// 1 +// 1 +// 1 +// 0 diff --git a/gnovm/tests/debug/4i.gno b/gnovm/tests/debug/4i.gno new file mode 100644 index 00000000000..acf1b53b32a --- /dev/null +++ b/gnovm/tests/debug/4i.gno @@ -0,0 +1,15 @@ +package main + +func main() { + s := []int{1, 2} + + f := func() { + if len(s) == 2 { + println("ok") + } + } + f() +} + +// Output: +// ok diff --git a/gnovm/tests/debug/5.gno b/gnovm/tests/debug/5.gno new file mode 100644 index 00000000000..ece19dd87f6 --- /dev/null +++ b/gnovm/tests/debug/5.gno @@ -0,0 +1,22 @@ +package main + +import "fmt" + +func main() { + // Define a function that returns a closure + var recursiveFunc func(int) int + recursiveFunc = func(num int) int { + if num <= 0 { + return 1 + } + // Closure calling itself recursively + return num * recursiveFunc(num-1) + } + + // Use the recursive closure + result := recursiveFunc(5) + fmt.Println("Factorial of 5 is:", result) // Output: 120 +} + +// Output: +// Factorial of 5 is: 120 diff --git a/gnovm/tests/debug/5a.gno b/gnovm/tests/debug/5a.gno new file mode 100644 index 00000000000..8345c9ed48d --- /dev/null +++ b/gnovm/tests/debug/5a.gno @@ -0,0 +1,24 @@ +package main + +import "fmt" + +func main() { + var recursiveFunc func(int) int + var recursiveFunc2 func(int) int + + recursiveFunc = func(num int) int { + recursiveFunc2 = recursiveFunc + + if num <= 0 { + return 1 + } + + return num * recursiveFunc2(num-1) + } + + result := recursiveFunc(5) + fmt.Println("Factorial of 5 is:", result) // Output: 120 +} + +// Output: +// Factorial of 5 is: 120 diff --git a/gnovm/tests/debug/5b.gno b/gnovm/tests/debug/5b.gno new file mode 100644 index 00000000000..5f54cc2f95d --- /dev/null +++ b/gnovm/tests/debug/5b.gno @@ -0,0 +1,46 @@ +package main + +import ( + "fmt" + "io" + "strings" +) + +func main() { + var mr io.Reader + var buf []byte + nread := 0 + withFooBar := func(tests func()) { + r1 := strings.NewReader("foo ") + //r2 := strings.NewReader("") + //r3 := strings.NewReader("bar") + //mr = io.MultiReader(r1, r2, r3) + mr = io.MultiReader(r1) + buf = make([]byte, 20) + tests() + } + expectRead := func(size int, expected string, eerr error) { + nread++ + n, gerr := mr.Read(buf[0:size]) + if n != len(expected) { + panic(fmt.Sprintf("#%d, expected %d bytes; got %d", + nread, len(expected), n)) + } + got := string(buf[0:n]) + if got != expected { + panic(fmt.Sprintf("#%d, expected %q; got %q", + nread, expected, got)) + } + if gerr != eerr { + panic(fmt.Sprintf("#%d, expected error %v; got %v", + nread, eerr, gerr)) + } + buf = buf[n:] + } + withFooBar(func() { + expectRead(5, "foo ", nil) + }) + +} + +// Output: diff --git a/gnovm/tests/debug/5b0.gno b/gnovm/tests/debug/5b0.gno new file mode 100644 index 00000000000..4bfe864cc8e --- /dev/null +++ b/gnovm/tests/debug/5b0.gno @@ -0,0 +1,26 @@ +package main + +func main() { + var buf []byte + + // when eval this, buf is still nil, + expectRead := func(size int, expected string, err error) { + b := buf[0:size] + println(b) + println(len(buf)) + println(cap(buf)) + } + + // buf should be captured here, here it's volatile, not where it defined + // namely, the vp should point here, -> so mutate offset + withFooBar := func() { + buf = make([]byte, 20) + expectRead(5, "foo ", nil) + } + withFooBar() +} + +// Output: +// slice[0x0000000000] +// 20 +// 20 diff --git a/gnovm/tests/debug/5b1.gno b/gnovm/tests/debug/5b1.gno new file mode 100644 index 00000000000..9218ee16fa3 --- /dev/null +++ b/gnovm/tests/debug/5b1.gno @@ -0,0 +1,26 @@ +package main + +func main() { + var buf []byte + + // when eval this, buf is still nil, + expectRead := func(size int, expected string, eerr error) { + b := buf[0:size] + println(b) + } + + // buf should be captured here, here it's volatile, not where it defined + // namely, the vp should point here, -> so mutate offset + withFooBar := func() func() { + buf = make([]byte, 20) + return func() { + b := buf[0:4] + println(b) + } + } + withFooBar() + println("ok") +} + +// Output: +// ok diff --git a/gnovm/tests/debug/6.gno b/gnovm/tests/debug/6.gno new file mode 100644 index 00000000000..d4823d55c2a --- /dev/null +++ b/gnovm/tests/debug/6.gno @@ -0,0 +1,23 @@ +package main + +import "fmt" + +func foo() (err error) { + defer func() { + if r := recover(); r != nil { + switch v := r.(type) { + case error: + err = v + default: + err = fmt.Errorf("%s", v) + } + } + }() + return +} + +func main() { + foo() +} + +// Output: diff --git a/gnovm/tests/debug/6a.gno b/gnovm/tests/debug/6a.gno new file mode 100644 index 00000000000..1acfa3a1f2c --- /dev/null +++ b/gnovm/tests/debug/6a.gno @@ -0,0 +1,24 @@ +package main + +import ( + "errors" +) + +func foo() (err error) { + y := 1 + defer func() { + if r := recover(); r != nil { + switch y { + case 1: + err = errors.New("xxx") + default: + err = nil + } + } + }() + return +} + +func main() { + foo() +} diff --git a/gnovm/tests/debug/7.gno b/gnovm/tests/debug/7.gno new file mode 100644 index 00000000000..86c8fc445f0 --- /dev/null +++ b/gnovm/tests/debug/7.gno @@ -0,0 +1,32 @@ +package main + +import "fmt" + +func main() { + + var fns []func(int) int + var recursiveFunc func(int) int + + for i := 0; i < 6; i++ { + recursiveFunc = func(num int) int { + if num <= 0 { + return 1 + } + return num * recursiveFunc(num-1) + } + fns = append(fns, recursiveFunc) + } + + for i, r := range fns { + result := r(i) + fmt.Printf("Factorial of %d is: %d \n", i, result) + } +} + +// Output: +// Factorial of 0 is: 1 +// Factorial of 1 is: 1 +// Factorial of 2 is: 2 +// Factorial of 3 is: 6 +// Factorial of 4 is: 24 +// Factorial of 5 is: 120 diff --git a/gnovm/tests/debug/7a.gno b/gnovm/tests/debug/7a.gno new file mode 100644 index 00000000000..9ae40a43df7 --- /dev/null +++ b/gnovm/tests/debug/7a.gno @@ -0,0 +1,36 @@ +package main + +import "fmt" + +// recursive closure does not capture +func main() { + + var fns []func(int) int + var recursiveFunc func(int) int + + for i := 0; i < 3; i++ { + recursiveFunc = func(num int) int { + x := i + if num <= 0 { + return 1 + } + println(x) + return num * recursiveFunc(num-1) + } + fns = append(fns, recursiveFunc) + } + + for i, r := range fns { + result := r(i) + fmt.Printf("Factorial of %d is: %d \n", i, result) + } + +} + +// Output: +// Factorial of 0 is: 1 +// 2 +// Factorial of 1 is: 1 +// 2 +// 2 +// Factorial of 2 is: 2 diff --git a/gnovm/tests/debug/closure0.gno b/gnovm/tests/debug/closure0.gno new file mode 100644 index 00000000000..acc1abfd404 --- /dev/null +++ b/gnovm/tests/debug/closure0.gno @@ -0,0 +1,17 @@ +package main + +type adder func(int, int) int + +func genAdd(k int) adder { + return func(i, j int) int { + return i + j + k + } +} + +func main() { + f := genAdd(5) + println(f(3, 4)) +} + +// Output: +// 12 diff --git a/gnovm/tests/debug/closure1.gno b/gnovm/tests/debug/closure1.gno new file mode 100644 index 00000000000..f0bbda7ca1e --- /dev/null +++ b/gnovm/tests/debug/closure1.gno @@ -0,0 +1,20 @@ +package main + +type adder func(int, int) int + +func genAdd(k int) adder { + return func(i, j int) int { + return i + j + k + } +} + +func main() { + f := genAdd(5) + g := genAdd(8) + println(f(3, 4)) + println(g(3, 4)) +} + +// Output: +// 12 +// 15 diff --git a/gnovm/tests/debug/closure2.gno b/gnovm/tests/debug/closure2.gno new file mode 100644 index 00000000000..e43496b9f43 --- /dev/null +++ b/gnovm/tests/debug/closure2.gno @@ -0,0 +1,28 @@ +package main + +func adder() func(int) int { + sum := 0 + return func(x int) int { + sum = sum + x + return sum + } +} + +func main() { + pos, neg := adder(), adder() + for i := 0; i < 10; i++ { + println(pos(i), neg(-2*i)) + } +} + +// Output: +// 0 0 +// 1 -2 +// 3 -6 +// 6 -12 +// 10 -20 +// 15 -30 +// 21 -42 +// 28 -56 +// 36 -72 +// 45 -90 diff --git a/gnovm/tests/debug/closure3.gno b/gnovm/tests/debug/closure3.gno new file mode 100644 index 00000000000..5e365a67cb4 --- /dev/null +++ b/gnovm/tests/debug/closure3.gno @@ -0,0 +1,23 @@ +package main + +type T1 struct { + Name string +} + +func (t *T1) genAdd(k int) func(int) int { + return func(i int) int { + println(t.Name) + return i + k + } +} + +var t = &T1{"test"} + +func main() { + f := t.genAdd(4) + println(f(5)) +} + +// Output: +// test +// 9 diff --git a/gnovm/tests/debug/closure4.gno b/gnovm/tests/debug/closure4.gno new file mode 100644 index 00000000000..61c0a77f1ba --- /dev/null +++ b/gnovm/tests/debug/closure4.gno @@ -0,0 +1,23 @@ +package main + +type T1 struct { + Name string +} + +func (t *T1) genAdd(k int) func(int) int { + return func(i int) int { + println(t.Name) + return i + k + } +} + +var t = T1{"test"} + +func main() { + f := t.genAdd(4) + println(f(5)) +} + +// Output: +// test +// 9 diff --git a/gnovm/tests/debug/closure5.gno b/gnovm/tests/debug/closure5.gno new file mode 100644 index 00000000000..e6d3c223607 --- /dev/null +++ b/gnovm/tests/debug/closure5.gno @@ -0,0 +1,23 @@ +package main + +type T1 struct { + Name string +} + +func (t T1) genAdd(k int) func(int) int { + return func(i int) int { + println(t.Name) + return i + k + } +} + +var t = T1{"test"} + +func main() { + f := t.genAdd(4) + println(f(5)) +} + +// Output: +// test +// 9 diff --git a/gnovm/tests/debug/closure6.gno b/gnovm/tests/debug/closure6.gno new file mode 100644 index 00000000000..5e365a67cb4 --- /dev/null +++ b/gnovm/tests/debug/closure6.gno @@ -0,0 +1,23 @@ +package main + +type T1 struct { + Name string +} + +func (t *T1) genAdd(k int) func(int) int { + return func(i int) int { + println(t.Name) + return i + k + } +} + +var t = &T1{"test"} + +func main() { + f := t.genAdd(4) + println(f(5)) +} + +// Output: +// test +// 9 diff --git a/gnovm/tests/debug/closure7.gno b/gnovm/tests/debug/closure7.gno new file mode 100644 index 00000000000..d4c0c96b0d5 --- /dev/null +++ b/gnovm/tests/debug/closure7.gno @@ -0,0 +1,28 @@ +package main + +import ( + "fmt" +) + +type Config struct { + A string +} + +var conf *Config = &Config{} + +func SetConfig() func(*Config) { + return func(cf *Config) { + conf = cf + } +} + +func main() { + conf := &Config{ + A: "foo", + } + + fmt.Println(conf.A) +} + +// Output: +// foo diff --git a/gnovm/tests/debug/closure8.gno b/gnovm/tests/debug/closure8.gno new file mode 100644 index 00000000000..6ef1b43fca6 --- /dev/null +++ b/gnovm/tests/debug/closure8.gno @@ -0,0 +1,10 @@ +package main + +var f = func(a int) int { return 2 + a } + +func main() { + println(f(3)) +} + +// Output: +// 5 diff --git a/gnovm/tests/debug/io2.gno b/gnovm/tests/debug/io2.gno new file mode 100644 index 00000000000..24655f5040c --- /dev/null +++ b/gnovm/tests/debug/io2.gno @@ -0,0 +1,21 @@ +package main + +import ( + "fmt" + "io" + "log" + "strings" +) + +func main() { + r := strings.NewReader("Go is a general-purpose language designed with systems programming in mind.") + + b, err := io.ReadAll(r) + if err != nil { + log.Fatal(err) + } + fmt.Printf("%s", b) +} + +// Output: +// Go is a general-purpose language designed with systems programming in mind. diff --git a/gnovm/tests/debug/recover4.gno b/gnovm/tests/debug/recover4.gno new file mode 100644 index 00000000000..5a6da4261a2 --- /dev/null +++ b/gnovm/tests/debug/recover4.gno @@ -0,0 +1,25 @@ +package main + +import "fmt" + +func div(a, b int) (result int) { + defer func() { + r := recover() + + fmt.Printf("r = %#v\n", r) + + if r != nil { + result = 0 + } + }() + + return a / b +} + +func main() { + println(div(30, 2)) +} + +// Output: +// r = +// 15 diff --git a/gnovm/tests/debug/recover6.gno b/gnovm/tests/debug/recover6.gno new file mode 100644 index 00000000000..0b304369764 --- /dev/null +++ b/gnovm/tests/debug/recover6.gno @@ -0,0 +1,30 @@ +package main + +import ( + "errors" +) + +func main() { + println(f(false)) + println(f(true)) +} + +func f(dopanic bool) (err error) { + defer func() { + if x := recover(); x != nil { + err = x.(error) + } + }() + q(dopanic) + return +} + +func q(dopanic bool) { + if dopanic { + panic(errors.New("wtf")) + } +} + +// Output: +// undefined +// wtf diff --git a/gnovm/tests/debug/recover6a.gno b/gnovm/tests/debug/recover6a.gno new file mode 100644 index 00000000000..427fc4b74b9 --- /dev/null +++ b/gnovm/tests/debug/recover6a.gno @@ -0,0 +1,40 @@ +package main + +import "errors" + +//func p(s interface{}) { +// fmt.Printf("%T \n", s) +// if v, ok := s.(string); ok { +// println(v) +// panic(v) +// } else { +// println("---") +// } +//} + +type error interface { + Error() string +} + +// New returns an error that formats as the given text. +// Each call to New returns a distinct error value even if the text is identical. +func New(text string) error { + return &errorString{text} +} + +// errorString is a trivial implementation of error. +type errorString struct { + s string +} + +func (e *errorString) Error() string { + return e.s +} + +func main() { + //panic(New("wtf")) + panic(errors.New("wtf")) +} + +// Error: +// wtf diff --git a/gnovm/tests/debug/zregexp_stdlibs.gno b/gnovm/tests/debug/zregexp_stdlibs.gno new file mode 100644 index 00000000000..10bb6f937d3 --- /dev/null +++ b/gnovm/tests/debug/zregexp_stdlibs.gno @@ -0,0 +1,19 @@ +// MAXALLOC: 100000000 +// max total allocation of 100 mb. +package main + +import "regexp" + +var reName = regexp.MustCompile(`^[a-z]+[_a-z0-9]*$`) + +func main() { + for j := 0; j < 100; j++ { + if !(reName.MatchString("thisisatestname")) { + panic("error") + } + } + println(true) +} + +// Output: +// true diff --git a/gnovm/tests/debug2/1f.gno b/gnovm/tests/debug2/1f.gno new file mode 100644 index 00000000000..19722ec3ae6 --- /dev/null +++ b/gnovm/tests/debug2/1f.gno @@ -0,0 +1,53 @@ +package main + +// 1. we can determine target vars by hasClosure pattern and externNames(without uverse). +// it can be a loopvar, or vars derived from loopvar, e.g. x := i; +// 2. now we need to capture every transient state of the target var; firstly we should +// eval the transient state, there are two ways around: +// a) eval funcLit, but the time doing this is hard to determine, namely, you have to eval +// before it leaves its using context. In some sense, you have to cover all cases when a funcValue +// is used, like assign, xxxLit, funcCall as an arg, etc. +// b) another way for this is to generate a `time series` values that the index is the loop index, +// e.g. in a for loop, we define a new var like x := i, we store the transient state of x per loop +// in the time series, which is a slice. This slice is used when the closure fv is called, +// replacing the var in default block.(func value have a slice of name indicates it's captured, when +// eval name in this slice, using the time series instead). +// this solution mainly differs that it eval target x independently with the closure funcLit, so it avoids +// the obsession to determine the eval order. this seems the right way. hmmm. + +//======================================================================================================== +// work flow 1.0, hard to do +// 1. determine loop var, whole logic depends on this, the key word is dynamic, it causes polymorphic. +// this should most be done via define(), // TODO: check it +// 2. in transcribe, find if loopvar is used somewhere, to identify pattern like x := i, what to do with +// other use cases? this brings complexity. This is wrong feel. +// we can generate the ts slice for every target var, e.g. x in this case. if two vars, two slices. so fv should +// reference a slice of slice. + +// work flow 2.0 +// 1. ignore loop var, assume all externNames apart from uverse is target captured var, this also needs to happen +// in transcribe, tag it, and can be checked everywhere it's appears in runtime, x := i, or var x int, etc. +// when eval funcLit, new a slice of slice and push value for further update. + +func main() { + var fns []func() int + for i := 0; i < 5; i++ { + //x := i // check if x is polymorphic, if yes, new a slice and set value by index + var x int // new a slice no init, this would be updated later, so the slice can be mutated after opFuncLit + f := func() int { + return x // set the slice to fv in + } + x = i // check if x is polymorphic, is yes, update slice assigned to fv before this, which is a reference. + fns = append(fns, f) // where should eval funcLit, where it assigned somewhere, or used in another func lit + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 diff --git a/gnovm/tests/debug2/1g.gno b/gnovm/tests/debug2/1g.gno new file mode 100644 index 00000000000..e7a6b64f1f3 --- /dev/null +++ b/gnovm/tests/debug2/1g.gno @@ -0,0 +1,24 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 5; i++ { + x := i + { + f := func() int { + return x + } + fns = append(fns, f) + } + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 diff --git a/gnovm/tests/debug2/1h.gno b/gnovm/tests/debug2/1h.gno new file mode 100644 index 00000000000..addd660d04a --- /dev/null +++ b/gnovm/tests/debug2/1h.gno @@ -0,0 +1,30 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 5; i++ { + x := i + for j := 0; j < 2; j++ { + y := j + f := func() int { + return x + y + } + fns = append(fns, f) + } + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 1 +// 2 +// 2 +// 3 +// 3 +// 4 +// 4 +// 5 diff --git a/gnovm/tests/debug2/5b.gno b/gnovm/tests/debug2/5b.gno index 9c030f2ba58..5f54cc2f95d 100644 --- a/gnovm/tests/debug2/5b.gno +++ b/gnovm/tests/debug2/5b.gno @@ -42,3 +42,5 @@ func main() { }) } + +// Output: diff --git a/gnovm/tests/debug2/5b0.gno b/gnovm/tests/debug2/5b0.gno index 0e74e0ea9a9..4bfe864cc8e 100644 --- a/gnovm/tests/debug2/5b0.gno +++ b/gnovm/tests/debug2/5b0.gno @@ -4,9 +4,11 @@ func main() { var buf []byte // when eval this, buf is still nil, - expectRead := func(size int, expected string, eerr error) { + expectRead := func(size int, expected string, err error) { b := buf[0:size] println(b) + println(len(buf)) + println(cap(buf)) } // buf should be captured here, here it's volatile, not where it defined @@ -17,3 +19,8 @@ func main() { } withFooBar() } + +// Output: +// slice[0x0000000000] +// 20 +// 20 diff --git a/gnovm/tests/debug2/7.gno b/gnovm/tests/debug2/7.gno index d5f65b18c5d..86c8fc445f0 100644 --- a/gnovm/tests/debug2/7.gno +++ b/gnovm/tests/debug2/7.gno @@ -21,5 +21,12 @@ func main() { result := r(i) fmt.Printf("Factorial of %d is: %d \n", i, result) } - } + +// Output: +// Factorial of 0 is: 1 +// Factorial of 1 is: 1 +// Factorial of 2 is: 2 +// Factorial of 3 is: 6 +// Factorial of 4 is: 24 +// Factorial of 5 is: 120 diff --git a/gnovm/tests/debug2/7a.gno b/gnovm/tests/debug2/7a.gno index 8f7ca50dd38..66d5c3a8ddc 100644 --- a/gnovm/tests/debug2/7a.gno +++ b/gnovm/tests/debug2/7a.gno @@ -26,3 +26,11 @@ func main() { } } + +// Output: +// Factorial of 0 is: 1 +// 1 +// Factorial of 1 is: 1 +// 2 +// 2 +// Factorial of 2 is: 2 diff --git a/gnovm/tests/debug2/io2.gno b/gnovm/tests/debug2/io2.gno new file mode 100644 index 00000000000..24655f5040c --- /dev/null +++ b/gnovm/tests/debug2/io2.gno @@ -0,0 +1,21 @@ +package main + +import ( + "fmt" + "io" + "log" + "strings" +) + +func main() { + r := strings.NewReader("Go is a general-purpose language designed with systems programming in mind.") + + b, err := io.ReadAll(r) + if err != nil { + log.Fatal(err) + } + fmt.Printf("%s", b) +} + +// Output: +// Go is a general-purpose language designed with systems programming in mind. From d7b913ec7ec1fa8d4cfde47d6c4031b81eca5f8b Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Wed, 24 Jan 2024 17:40:05 +0800 Subject: [PATCH 13/32] fixup --- gnovm/Makefile | 2 + gnovm/pkg/gnolang/machine.go | 6 - gnovm/pkg/gnolang/nodes.go | 12 +- gnovm/pkg/gnolang/op_assign.go | 35 -- gnovm/pkg/gnolang/op_call.go | 63 +- gnovm/pkg/gnolang/op_eval.go | 2 +- gnovm/pkg/gnolang/op_exec.go | 77 +-- gnovm/pkg/gnolang/op_expressions.go | 146 +---- gnovm/pkg/gnolang/transcribe.go | 575 ++---------------- gnovm/pkg/gnolang/values.go | 17 +- gnovm/tests/debug/sort_search_efficiency.gno | 21 + .../debug/sort_search_efficiency_simple.gnoa | 25 + gnovm/tests/file.go | 17 +- gnovm/tests/files/zrealm0.gno | 1 + gnovm/tests/files/zrealm1.gno | 1 + gnovm/tests/files/zrealm2.gno | 2 + gnovm/tests/files/zrealm3.gno | 2 + gnovm/tests/files/zrealm4.gno | 2 + gnovm/tests/files/zrealm5.gno | 2 + gnovm/tests/files/zrealm6.gno | 2 + gnovm/tests/files/zrealm7.gno | 2 + gnovm/tests/files/zrealm_avl0.gno | 2 + gnovm/tests/files/zrealm_avl1.gno | 2 + gnovm/tests/files/zrealm_natbind0.gno | 3 + gnovm/tests/files/zrealm_tests0.gno | 15 + 25 files changed, 262 insertions(+), 772 deletions(-) create mode 100644 gnovm/tests/debug/sort_search_efficiency.gno create mode 100644 gnovm/tests/debug/sort_search_efficiency_simple.gnoa diff --git a/gnovm/Makefile b/gnovm/Makefile index cbe6802d32d..69ae3d2c20b 100644 --- a/gnovm/Makefile +++ b/gnovm/Makefile @@ -54,6 +54,7 @@ _test.gnolang: _test.gnolang.native _test.gnolang.stdlibs _test.gnolang.realm _t _test.gnolang.other:; go test tests/*.go -run "(TestFileStr|TestSelectors)" $(GOTEST_FLAGS) _test.gnolang.realm:; go test tests/*.go -run "TestFiles/^zrealm" $(GOTEST_FLAGS) _test.gnolang.pkg0:; go test tests/*.go -run "TestPackages/(bufio|crypto|encoding|errors|internal|io|math|sort|std|stdshim|strconv|strings|testing|unicode)" $(GOTEST_FLAGS) +#_test.gnolang.pkg0:; go test tests/*.go -run "TestPackages/(bufio|crypto|encoding|errors|internal|io|math|std|stdshim|strconv|strings|testing|unicode)" $(GOTEST_FLAGS) _test.gnolang.pkg1:; go test tests/*.go -run "TestPackages/regexp" $(GOTEST_FLAGS) _test.gnolang.pkg2:; go test tests/*.go -run "TestPackages/bytes" $(GOTEST_FLAGS) _test.gnolang.native:; go test tests/*.go -test.short -run "TestFilesNative/" $(GOTEST_FLAGS) @@ -61,6 +62,7 @@ _test.gnolang.stdlibs:; go test tests/*.go -test.short -run 'TestFiles$$/' _test.gnolang.native.sync:; go test tests/*.go -test.short -run "TestFilesNative/" --update-golden-tests $(GOTEST_FLAGS) _test.gnolang.stdlibs.sync:; go test tests/*.go -test.short -run 'TestFiles$$/' --update-golden-tests $(GOTEST_FLAGS) _test.gnolang.debug:; go test tests/*.go -test.short -run 'TestDebug$$/' --update-golden-tests $(GOTEST_FLAGS) +_test.gnolang.p:; go test tests/*.go -run "TestPackages/(sort)" $(GOTEST_FLAGS) ######################################## # Code gen diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go index 7027bcf6180..66390a84de0 100644 --- a/gnovm/pkg/gnolang/machine.go +++ b/gnovm/pkg/gnolang/machine.go @@ -1297,9 +1297,6 @@ func (m *Machine) Run() { case OpFuncLit: m.incrCPU(OpCPUFuncLit) m.doOpFuncLit() - case OpPreFuncLit: - m.incrCPU(OpCPUPreFuncLit) - m.doOpPreFuncLit() case OpMapLit: m.incrCPU(OpCPUMapLit) m.doOpMapLit() @@ -1351,9 +1348,6 @@ func (m *Machine) Run() { m.incrCPU(OpCPUMaybeNativeType) m.doOpMaybeNativeType() /* Statement operators */ - case OpPreAssign: - m.incrCPU(OpCPUPreAssign) - m.doOpPreAssign() case OpAssign: m.incrCPU(OpCPUAssign) m.doOpAssign() diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index 0e85fb9ae33..ea8c46129e3 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -494,14 +494,8 @@ type KeyValueExprs []KeyValueExpr type FuncLitExpr struct { Attributes StaticBlock - Type FuncTypeExpr // function type - Body // function body - Closure *Closure -} - -func (fx *FuncLitExpr) SetClosure(c *Closure) { - debug.Printf("+++++SetClosure, c: %v \n", c) - fx.Closure = c + Type FuncTypeExpr // function type + Body // function body } //type ClosureObject struct { @@ -892,7 +886,7 @@ type bodyStmt struct { NumStmts int // number of Stmts, for goto Cond Expr // for ForStmt Post Stmt // for ForStmt - Ts *TimeSeriesBag + Bag *TimeSeriesBag isLoop bool Active Stmt // for PopStmt() Key Expr // for RangeStmt diff --git a/gnovm/pkg/gnolang/op_assign.go b/gnovm/pkg/gnolang/op_assign.go index 2134e63096c..5ce75ee8dcd 100644 --- a/gnovm/pkg/gnolang/op_assign.go +++ b/gnovm/pkg/gnolang/op_assign.go @@ -30,41 +30,6 @@ func (m *Machine) doOpDefine() { } } -func (m *Machine) doOpPreAssign() { - debugPP.Println("---doOpPreAssign") - // check rhs type - s := m.PeekStmt1().(*AssignStmt) - - // TODO: this is expensive, should quick return - - var ln Name - if n, ok := s.Lhs[0].(*NameExpr); ok { - debugPP.Printf("name of lhs is: %s \n", n.Name) - ln = n.Name - } - - debugPP.Printf("s peeked: %v, len of RHS: %d \n", s, len(s.Rhs)) - // rhs is funcLitExpr, set a flag, to record lhs and use it update captured in postAssign - if fx, ok := s.Rhs[0].(*FuncLitExpr); ok { - debugPP.Printf("fx: %v, closure: %v \n", fx, fx.Closure) - if fx.Closure != nil { - if len(fx.Closure.names) > 0 { - for _, n := range fx.Closure.names { - if n == ln { // contains - //if fx.Closure.names[0] == ln { - debugPP.Println("-----recursive closure") - //fx.Closure.recursive = true - fx.Closure = nil // no support for recursive closure - } - } - } - } - } - //m.PushValue(typedString("x")) - - debugPP.Println("---end") -} - func (m *Machine) doOpAssign() { s := m.PopStmt().(*AssignStmt) debugPP.Printf("---doOpAssign, s: %v \n", s) diff --git a/gnovm/pkg/gnolang/op_call.go b/gnovm/pkg/gnolang/op_call.go index 47f7b1bce14..34b1133b33f 100644 --- a/gnovm/pkg/gnolang/op_call.go +++ b/gnovm/pkg/gnolang/op_call.go @@ -75,37 +75,44 @@ func (m *Machine) doOpCall() { var end bool for i, bag := range fv.Tss { // each bag is for a certain level of block debugPP.Printf("Level[%d] bag is : %v \n", i, bag) - for j, t := range bag.transient { // unpack vars belong to a specific block - if t != nil { + if bag != nil { + if bag.isSealed { + if bag != nil && !bag.isEmpty() { // NOTE: for some reasons won't pack value + for j, t := range bag.transient { // unpack vars belong to a specific block + if t != nil { + debugPP.Printf("Bag.transient[%d] name is %v, path: %v, len of values is: %v \n", j, t.nx.Name, t.nx.Path, len(t.values)) + for k, v := range t.values { + debugPP.Printf("values[%d] is %v \n", k, v) + } - debugPP.Printf("Ts.transient[%d] name is %v, path: %v, len of values is: %v \n", j, t.nx.Name, t.nx.Path, len(t.values)) - for k, v := range t.values { - debugPP.Printf("values[%d] is %v \n", k, v) - } - - targetB = clo.GetBlockWithDepth(m.Store, t.nx.Path) // find target block using depth - // check if exists, should always be? - names := targetB.GetSource(m.Store).GetBlockNames() - var index int - var match bool - for l, n := range names { - if n == t.nx.Name { - debugPP.Printf("name: %s match \n", n) - index = l - match = true - break - } - } - if match { - debugPP.Println("===match") - targetB.UpdateValue(index, t.values[0]) - nvs := t.values[1:] - bag.transient[j].values = nvs - if len(bag.transient[j].values) == 0 { // loop end - bag.transient[j] = nil // empty a name and value - end = true + targetB = clo.GetBlockWithDepth(m.Store, t.nx.Path) // find target block using depth + // check if exists, should always be? + names := targetB.GetSource(m.Store).GetBlockNames() + var index int + var match bool + for l, n := range names { + if n == t.nx.Name { + debugPP.Printf("name: %s match \n", n) + index = l + match = true + break + } + } + if match { + debugPP.Println("===match") + targetB.UpdateValue(index, t.values[0]) + nvs := t.values[1:] + bag.transient[j].values = nvs + if len(bag.transient[j].values) == 0 { // loop end + bag.transient[j] = nil // empty a name and value + end = true + } + } + } } } + } else { // not sealed will be tainted + fv.Tss[i].isTainted = true } } } diff --git a/gnovm/pkg/gnolang/op_eval.go b/gnovm/pkg/gnolang/op_eval.go index b74638d5c67..fe03645a735 100644 --- a/gnovm/pkg/gnolang/op_eval.go +++ b/gnovm/pkg/gnolang/op_eval.go @@ -312,7 +312,7 @@ func (m *Machine) doOpEval() { b := m.LastBlock() debugPP.Printf("b: %v \n", b) m.PushOp(OpFuncLit) - m.PushOp(OpPreFuncLit) + //m.PushOp(OpPreFuncLit) // evaluate func type m.PushExpr(&x.Type) m.PushOp(OpEval) diff --git a/gnovm/pkg/gnolang/op_exec.go b/gnovm/pkg/gnolang/op_exec.go index eb1f48127b9..59841a0b6ed 100644 --- a/gnovm/pkg/gnolang/op_exec.go +++ b/gnovm/pkg/gnolang/op_exec.go @@ -119,49 +119,56 @@ func (m *Machine) doOpExec(op Op) { s = next goto EXEC_SWITCH } else if bs.NextBodyIndex == bs.BodyLen { - debugPP.Printf("---end of loop body, going to update value--- \n") - // update fv.Capture if value changed - //if _, ok := findLoopBlockWithPath(m.Store, lb); ok { - if lb.GetBodyStmt().isLoop { + debugPP.Printf("---end of loop body, going to update value using current state--- \n") + if lb.GetBodyStmt().isLoop { // do we need this? debugPP.Printf("---------isLoop, current block is: %v \n", m.LastBlock()) - if bs.Ts != nil && bs.Ts.isSealed { - debugPP.Printf("---addr of bs.Ts is: %p \n", &bs.Ts) + debugPP.Printf("---------bag, %v \n", bs.Bag) + if bs.Bag != nil && bs.Bag.isFilled && !bs.Bag.isTainted { + debugPP.Printf("---addr of bs.Bag is: %p \n", &bs.Bag) names := lb.Source.GetBlockNames() var found bool - for i, t := range bs.Ts.transient { - debugPP.Printf("transient[%d] name is %v, path: %v \n", i, t.nx.Name, t.nx.Path) - // first check if name is in current block, it not, stop - for _, name := range names { - if t.nx.Name == name { - debugPP.Printf("found %s in current block: %v \n", t.nx.Name, lb) - found = true + var isSealed bool + for i, tt := range bs.Bag.transient { + if tt != nil { + debugPP.Printf("transient[%d] name is %v, path: %v \n", i, tt.nx.Name, tt.nx.Path) + // first check if name is in current block, it not, stop + for _, name := range names { + if tt.nx.Name == name { + debugPP.Printf("found %s in current block: %v \n", tt.nx.Name, lb) + found = true + } } - } - if found { - nvp := lb.Source.GetPathForName(m.Store, t.nx.Name) - ptr := m.LastBlock().GetPointerTo(m.Store, nvp) - tv := ptr.Deref() - debugPP.Printf("--- new tv : %v \n", tv) - //t.value = tv - // set back - debugPP.Printf("before update, len of Ts values is : %d \n", len(bs.Ts.transient[i].values)) + if found { + nvp := lb.Source.GetPathForName(m.Store, tt.nx.Name) + ptr := m.LastBlock().GetPointerTo(m.Store, nvp) + tv := ptr.Deref() + debugPP.Printf("--- new tv : %v \n", tv) + // set back + debugPP.Printf("before update, len of Bag values is : %d \n", len(bs.Bag.transient[i].values)) - expandRatio := bs.Ts.transient[i].expandRatio - debugPP.Printf("--- expand ratio is: %d \n", expandRatio) - for j := int8(0); j < expandRatio; j++ { - bs.Ts.transient[i].values = append(bs.Ts.transient[i].values, tv) - } - debugPP.Printf("after update, len of Ts values is : %d \n", len(bs.Ts.transient[i].values)) - // update higher level if it is also a loop, repeat state. - upperBlock := findNearestLoopBlock(m.Store, lb) - debugPP.Printf("upperBlock is: %v \n", upperBlock) - if upperBlock != nil { - upperBlock.GetBodyStmt().Ts.setRatio(int8(len(bs.Ts.transient[i].values))) + expandRatio := bs.Bag.transient[i].expandRatio + debugPP.Printf("--- expand ratio is: %d \n", expandRatio) + for j := int8(0); j < expandRatio; j++ { + bs.Bag.transient[i].values = append(bs.Bag.transient[i].values, tv) + } + debugPP.Printf("after update, len of Bag values is : %d \n", len(bs.Bag.transient[i].values)) + + // update higher level if it is also a loop, padding. + upperBlock := findNearestLoopBlock(m.Store, lb) + debugPP.Printf("upperBlock is: %v \n", upperBlock) + if upperBlock != nil && upperBlock.GetBodyStmt().Bag != nil { + upperBlock.GetBodyStmt().Bag.setRatio(int8(len(bs.Bag.transient[i].values))) + } + isSealed = true + } else { + debugPP.Printf("---not found %s in current block, b: %v \n", tt.nx.Name, lb) + isSealed = false } - } else { - debugPP.Printf("---not found %s in current block, b: %v \n", t.nx.Name, lb) } } + if isSealed { + bs.Bag.isSealed = true // seal for this bag of this block + } } } diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index 75372004faa..f084a777c91 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -680,70 +680,6 @@ func (m *Machine) doOpStructLit() { }) } -func (m *Machine) doOpPreFuncLit() { - debugPP.Println("-----doOpPreFuncLit") - x := m.PeekExpr(1).(*FuncLitExpr) - debugPP.Printf("funcLitExpr: %v \n", x) - //debug.Printf("pointer of x.Closure: %p \n", x.Closure) - debugPP.Printf("x.Closure: %v \n", x.Closure) - lb := m.LastBlock() - debugPP.Printf("lb: %v \n", lb) - - // push block - b := m.Alloc.NewBlock(x, lb) - debugPP.Printf("b: %v \n", b) - - m.PushBlock(b) - m.PushOp(OpPopBlock) - - // get names - //x.StaticBlock.String() - - //debugPP.Printf("x.staticBlock.ExternNames: %v \n", x.StaticBlock.GetExternNames()) - for i, n := range x.GetExternNames() { - debugPP.Printf("x.staticBlock.ExternNames[%d] %v \n", i, n) - vp := x.GetPathForName(m.Store, n) - debugPP.Printf("%v's value path is: %v \n", n, vp) - if vp.Depth == 0 { - continue - } - debugPP.Println("-------got target of: ", n) - nx := &NameExpr{ - Name: n, - Path: vp, - } - debugPP.Printf("nx is: %v \n", nx) - //m.PushExpr(nx) - //m.PushOp(OpEval) - } - - //closure := x.Closure - //if closure != nil { - // debugPP.Printf("closure nxs len is: %d \n", len(closure.cnxs)) - // for i, cnx := range closure.cnxs { - // debugPP.Printf("closure[%d]: %v \n", i, cnx) - // offset := cnx.offset - // debugPP.Printf("offset of %s is %d \n", cnx.nx.Name, offset) - // vp := cnx.nx.Path - // debugPP.Printf("vp of nx[%s] is : %v \n", cnx.nx.Name, vp) - // if (vp.Depth + 1) < offset { - // panic("---incorrect offset for:" + cnx.nx.Name) - // } - // nvp := ValuePath{ - // Name: cnx.nx.Name, - // Depth: vp.Depth - offset + 1, - // Index: vp.Index, - // Type: vp.Type, - // } - // debugPP.Printf("nvp of nx[%s] is : %v \n", nvp.Name, nvp) - // nnx := *cnx.nx - // nnx.Path = nvp - // m.PushExpr(&nnx) - // m.PushOp(OpEval) - // } - //} -} - func isZeroArray(arr [8]byte) bool { for _, v := range arr { if v != 0 { @@ -787,36 +723,13 @@ func findLoopBlockWithPath(store Store, b *Block, nx *NameExpr) (*Block, bool, u } } return nil, false, gen - - //debugPP.Printf("findLoopBlockWithPath for %s, b is: %v, \n", nx.Name, b) - //for { - // if b != nil { - // debugPP.Printf("b is: %v, \n", b) - // if b.GetBodyStmt().isLoop { // is loop - // names := b.GetSource(store).GetBlockNames() - // for _, name := range names { - // if n == name { // find n in this block - // return b, true - // } - // } - // // not match - // b = b.GetParent(store) - // continue - // } else { - // b = b.GetParent(store) - // continue - // } - // } else { - // return nil, false - // } - //} } func (m *Machine) doOpFuncLit() { x := m.PopExpr().(*FuncLitExpr) debugPP.Printf("-----doOpFuncLit, x: %v \n", x) lb := m.LastBlock() - debugPP.Printf("lb: %v \n", lb) + debugPP.Printf("last block: %v \n", lb) var captures []*NameExpr for _, n := range x.GetExternNames() { @@ -834,49 +747,60 @@ func (m *Machine) doOpFuncLit() { captures = append(captures, nx) } + // sort it so to traverse block in an order of inner to outer sortDepth := func(i, j int) bool { return int(captures[i].Path.Depth) < int(captures[j].Path.Depth) } sort.Slice(captures, sortDepth) debugPP.Println("---done sort") - //for i, nx := range captures { - // debugPP.Printf("captures[%d] is : %v \n", i, nx.Name) - //} - var isLoop bool + var isLoopBlock bool var loopBlock *Block var bag *TimeSeriesBag - var gen uint8 - var lastGen uint8 - //var changed bool - //var isSet bool + var lastGen, gen uint8 var isReuse bool var Tss []*TimeSeriesBag + // start search every captured var in target block via path + // 1. Tss is a slice of bag, one bag is for one loop block. + // 2. new a `bag` for every block, the bag has a flag of isFilled for reuse, + // and a []transient for vars in on loop block, that each transient contains an + // nameExpr, and a slice of values that is value of var in specific time slice. + // Tss -> []bag -> isFilled, []transient -> nx:nameExpr, values:[]TypedValue. + // XXX, Tss if for fv, which is the concrete entity of closure. + // in case for i:=0, i++; i<2{ x := i }, Tss will have one bag for the only loop block, + // and one nx for x, and 2 values of [0,1], fv will have 2 replica in this case, and they + // share the same loopBlock. + // there are some more complex situation like: + // for i:=0, i++; i<2{ x := i for j:=0, j++; j<2{y := j}}, + // in this case, there will be 4 replica of fv, and 3 loopBlock, 1 if for the outer loop, + // left 2 is the inner blocks, since as the outer for loop execute, will yield due num of + // sub blocks. + // an intuitive state for state of x, y is: [0,0], [0,1], [1,0], [1,1]. + // in the impl, the outer block has a bag of [0,0,1,1], and inner block 1, has a bag [0,1] + // and inner block 2 has a bag[0,1]. for i, nx := range captures { debugPP.Printf("captures[%d] is : %v \n", i, nx.Name) // start search block, in the order of small depth to big depth - loopBlock, isLoop, gen = findLoopBlockWithPath(m.Store, lb, nx) + loopBlock, isLoopBlock, gen = findLoopBlockWithPath(m.Store, lb, nx) if lastGen == 0 { lastGen = gen - } else if gen != lastGen { - //changed = true + } else if gen != lastGen { // if enter new level of block, means last block is all done, pack bag lastGen = gen // set last state - if bag != nil { - bag.isSealed = true + if bag != nil { // has something to pack + bag.isFilled = true Tss = append(Tss, bag) } debugPP.Printf("========packed bag for %v is: %s \n", loopBlock, bag) - //isSet = true } - if isLoop { - bag = loopBlock.GetBodyStmt().Ts // get bag from loop block, fill name + if isLoopBlock { + bag = loopBlock.GetBodyStmt().Bag // get bag from specific block, that is shared across current level of for/range loop debugPP.Printf("got initial bag from target loop block: %v \n", bag) if bag == nil { bag = &TimeSeriesBag{} // init } - if !bag.isSealed { // only once + if !bag.isFilled { // only once debugPP.Println("---not sealed---, should pack only once for n: ", nx.Name) tst := &Transient{ nx: nx, @@ -884,22 +808,16 @@ func (m *Machine) doOpFuncLit() { } bag.transient = append(bag.transient, tst) // set back to loop block properly - loopBlock.GetBodyStmt().Ts = bag - // seal this layer - //if changed { // mean end of a layer, into high layer - // //bag.isSealed = true - // //Tss = append(Tss, bag) - // changed = false - //} + loopBlock.GetBodyStmt().Bag = bag } else { // repack when iterates, this should be optimized - isReuse = true + isReuse = true // use isFilled instead Tss = append(Tss, bag) } } } // tail set if bag != nil && !isReuse { - bag.isSealed = true // set for the last bag + bag.isFilled = true // set for the last bag Tss = append(Tss, bag) // collect the last bag debugPP.Printf("========packed bag for %v is: %s \n", loopBlock, bag) } diff --git a/gnovm/pkg/gnolang/transcribe.go b/gnovm/pkg/gnolang/transcribe.go index 2e0f87b7d41..a5548b23253 100644 --- a/gnovm/pkg/gnolang/transcribe.go +++ b/gnovm/pkg/gnolang/transcribe.go @@ -106,325 +106,6 @@ const ( TRANS_FILE_BODY ) -// rules of closure capture -// closure captures namedExprs(nx) -// closure captures nx when: -// 1. defined outside the closure block, which implies nxs defined inside do not need to be captured, -// it's evaluated naturally. -// 2. nx defined outside closure block are not all captured. it's captured only when it's volatile -// e.g. a nx if mutated when the outside block is rangeStmt, for stmt, which dynamically -// mutating the target nx. namely, any case you can not get a final value of the nx, you should -// capture it. any time it's deemed final, no need to capture. - -// FLOW: -// 1. start capture work when peeking funcLitExpr -// 2. do the capture work when traversing to nameExpr, check `hasClosure` to determine if capture needed -// 3. exclude any nxs that is not eligible: -// a. defined locally -// this is something tricky -// one method is to give every nx an absolute level,so any nx can be compared by the `level` -// with the level of the closure block to determine if it's defined inside or outside of the -// closure block. -// a second method is by checking the operators of `define` and `assign`, to filter out locally -// defined nxs. TODO: is it doable? -// ###whitelist is a collect of names to be whitelisted while capturing nxs, it contains: -// 1. params and results of funcLitExpr, -// 2. LHS of := and = -// 3. how about RHS? rhs can be either literals, or nxs(locally or not), it is defined locally, -// it's bypassed by rule2. if is not local, capture it. so what we need is LHS. -// FLOW of whitelist: -// everytime encounter assign(assignStmt, init of if/range/for/switch stmt), push the whitelist -// map with key of operator(define, assign), with value of assignStruct -// while in traversing nx, first peek operator, the `check` the corresponding nx according to the -// proper num set previously, the number should be counted, if counts to `num`, pop the operator, -// implies an end for a assign/define stmt. -// what the `check` does is to put the name of nx into the names, which is used to filter nxs. - -// b. final nx. TODO: this left a todo -// it's can be done by traversing parent blocks, similar as `hasClosure`, to determine if there -// is volatile blocks outside - -var CX *ClosureContext - -func init() { - CX = &ClosureContext{whitelist: make(map[Name]bool)} -} - -// AssignOperand is metadata to describe how much (left)nxs is related to an assign/define operator -type AssignOperand struct { - num int // num of lhs - counter int // counter increased every time checked in traversing nx, if counter == num, mean it's resolved, and pop operator -} - -func (ao *AssignOperand) String() string { - var s string - s += fmt.Sprintf("assign operand num: %d \n", ao.num) - s += fmt.Sprintf("assign operand counter: %d \n", ao.counter) - return s -} - -//type CNode struct { -// name Name -// n Node -//} - -type ClosureContext struct { - hasLoop bool - hasClo bool - closures []*Closure - nodes []Node - ops []Word // assign/define related logic - operands []*AssignOperand // assign/define related logic, per operator, e.g. a, b := 0, 1 - //rc []*RecursiveContext // detect cyclic, to find recursive closure, could converge with `nodes` - whitelist map[Name]bool // use to filter out nxs -} - -func (cx *ClosureContext) clearWhiteList() { - debugPP.Println("clear whitelist") - cx.whitelist = make(map[Name]bool) - //cx.popOp() - //cx.popOperand() -} - -func (cx *ClosureContext) dumpWhitelist() string { - var s string - s += "===whitelist=== \n" - for n, _ := range cx.whitelist { - s += fmt.Sprintf("name is: %v \n", n) - } - return s -} - -func (cx *ClosureContext) pushOp(op Word) { - cx.ops = append(cx.ops, op) -} - -func (cx *ClosureContext) popOp() { - if len(cx.ops) != 0 { - cx.ops = cx.ops[:len(cx.ops)-1] - } -} - -func (cx *ClosureContext) peekOp() Word { - if len(cx.ops) != 0 { - return cx.ops[len(cx.ops)-1] - } else { - return ILLEGAL - } -} - -func (cx *ClosureContext) popOperand() { - if len(cx.operands) != 0 { - cx.operands = cx.operands[:len(cx.operands)-1] - } -} - -func (cx *ClosureContext) peekOperand() *AssignOperand { - if len(cx.operands) != 0 { - return cx.operands[len(cx.operands)-1] - } else { - return nil - } -} - -func (cx *ClosureContext) dumpOps() string { - var s string - s += "\n" - for _, o := range cx.ops { - s += fmt.Sprintf("op: %v \n", o) - } - return s -} - -// 1. has funcLitExpr -// 2. it's embedded in another volatile block, for/range stmt -// 2. no recursive closure(not support for now) -func (cx *ClosureContext) hasClosure() bool { - return cx.hasClo - //loopBlock := false - //for _, n := range cx.nodes { - // switch n.(type) { - // case *ForStmt, *RangeStmt: - // loopBlock = true - // case *FuncLitExpr: - // return loopBlock // has loopBlock ahead - // default: - // // do nothing - // } - // //if _, ok := n.(*FuncLitExpr); ok { - // // return true - // //} - //} - // - // detect cyclic - // 1. encounter a nx, its type is funcLitExpr, name it t; how to get it? - // 2. compare t with parent node type, could be direct ancestor, or indirect - // namely, if two(or more) same funcLitExpr appears in stack, have cyclic - // which implies !hasClosure - - //return false -} - -func (cx *ClosureContext) pushNode(n Node) { - debugPP.Println("+++Cx push node, node++") - if !cx.hasLoop { - switch n.(type) { - case *ForStmt, *RangeStmt: - cx.hasLoop = true - default: - // do nothing - } - } else { // has loop - if _, ok := n.(*FuncLitExpr); ok { - cx.hasClo = true - } - } - - cx.nodes = append(cx.nodes, n) -} - -func (cx *ClosureContext) popNode() { - debugPP.Println("---Cx pop node") - if len(cx.nodes) != 0 { - debugPP.Println("-node") - cx.nodes = cx.nodes[:len(cx.nodes)-1] - debugPP.Printf("len of node: %d \n", len(cx.nodes)) - if len(cx.nodes) == 0 { // reset flag - cx.hasLoop = false - cx.hasClo = false - } - } -} - -func (cx *ClosureContext) pushClosure() bool { - debugPP.Println("+++Cx push closure") - // push closure - if cx.hasClosure() { - debug.Println("+clo") - debug.Println("before push closure") - cx.dumpClosures() - cx.closures = append(cx.closures, &Closure{}) // push empty closure to be filled in - debug.Println("end push closure") - cx.dumpClosures() - return true - } else { - debug.Println("no fx in stack, no need push closure") - return false - } -} - -func (cx *ClosureContext) popClosure(copy bool) *Closure { - debugPP.Println("---Cx pop") - debug.Println("-clo") - if len(cx.closures) == 0 { - return nil - } else { - if len(cx.closures) == 1 { // last one, clean context - cx.whitelist = make(map[Name]bool) - } - c := cx.closures[len(cx.closures)-1] // get current - for _, cnx := range c.cnxs { // pop-> increase offset - debug.Printf("+1 \n") // mutate offset - cnx.offset += 1 // from bottom up - } - cx.closures = cx.closures[:len(cx.closures)-1] // shrink - - if copy { - currentClo := cx.currentClosure() - if currentClo != nil { // if last closure, just pop, no copy - // fill up current closure - for _, cnx := range c.cnxs { // trace back captured nxs - currentClo.Fill(*cnx) - } - } - } - debug.Println("after pop, dump") - cx.dumpClosures() - debug.Printf("c poped: %v, \n", c) - return c - } -} - -func (cx *ClosureContext) peekNodes(offset int) Node { - debug.Println("c:node") - if len(cx.nodes) >= (1 + offset) { - return cx.nodes[len(cx.nodes)-(1+offset)] - } - return nil -} - -func (cx *ClosureContext) dumpNodes() { - if debug { - println("============Dump fxs===========") - println("len: ", len(cx.nodes)) - for i, n := range cx.nodes { - fmt.Printf("node[%d]: %v\n", i, n) - } - println("============end===============") - println("\n") - } -} -func (cx *ClosureContext) dumpClosures() { - if debug { - println("============Dump closures start=======") - debug.Printf("depth of closures: %d \n", len(cx.closures)) - for _, c := range cx.closures { - fmt.Printf("===>: %v \n", c) - } - println("============Dump closures end===============") - println("\n") - } -} - -func (cx *ClosureContext) currentClosure() *Closure { - debug.Printf("currentClosure, len: %d \n", len(cx.closures)) - if len(cx.closures) == 0 { - return nil - } - return cx.closures[len(cx.closures)-1] -} - -type CapturedNx struct { - nx *NameExpr - offset uint8 // every captured nx has an offset, represents its distance to the funcLitExpr -} - -func (cnx *CapturedNx) String() string { - return fmt.Sprintf("nx is: %v, offset is: %d \n", cnx.nx, cnx.offset) -} - -// captured NameExpr -type Closure struct { - names []Name - cnxs []*CapturedNx - recursive bool -} - -func (c *Closure) String() string { - var s string - s += "\n===========closure start============\n" - for i, n := range c.names { - s += fmt.Sprintf("names[%d] is: %s \n", i, string(n)) - } - for i, c := range c.cnxs { - s += fmt.Sprintf("cnxs[%d] is : [nx:%v, offset:%d] \n", i, c.nx, c.offset) - } - s += "===========closure end=============\n" - return s -} - -func (clo *Closure) Fill(cnx CapturedNx) { - debug.Printf("+nx: %v \n", cnx.nx) - for _, n := range clo.names { // filter out existed nx - if cnx.nx.Name == n { - debug.Println("exist, return") - return - } - } - clo.names = append(clo.names, cnx.nx.Name) - clo.cnxs = append(clo.cnxs, &cnx) -} - // return: // - TRANS_CONTINUE to visit children recursively; // - TRANS_SKIP to break out of the @@ -475,48 +156,48 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc // debugPP.Printf("%s depends on %v \n", k, v) //} - if CX.hasClosure() { - debugPP.Printf("---has Closure, check: %v \n", cnn) - debugPP.Println("---currentOp: ", CX.peekOp()) - debugPP.Println("---dump ops: ", CX.dumpOps()) - // recording names defined in closure as a whitelist - if CX.peekOp() == DEFINE || CX.peekOp() == ASSIGN { - ao := CX.peekOperand() - if ao != nil { // staff to do - debugPP.Printf("ao is: %v \n", ao) - // in scope of define/assign op, record to whitelist - ao.counter += 1 - // add nx to whitelist until resolved - CX.whitelist[cnn.Name] = true - debugPP.Println(CX.dumpWhitelist()) - if ao.counter == ao.num { // all resolved - //CX.clearWhiteList() - CX.popOp() - CX.popOperand() - } - } - } - // capture logic - // not exist in whitelist, capture - if _, ok := CX.whitelist[cnn.Name]; !ok { - debugPP.Printf("nx need capture: %s \n", string(cnn.Name)) - currentClo := CX.currentClosure() - debugPP.Printf("currentClo: %v \n", currentClo) - if currentClo != nil { // a closure to fill - //if cnn.Path.Depth < 1 { // if local defined, no capture - debugPP.Printf("---capture: %v \n", cnn) - cnx := CapturedNx{ - nx: cnn, - offset: 0, - } - currentClo.Fill(cnx) - CX.dumpClosures() - //CX.dumpNodes() - } - } - } else { - debugPP.Println("---no closure in place") - } + //if CX.hasClosure() { + // debugPP.Printf("---has Closure, check: %v \n", cnn) + // debugPP.Println("---currentOp: ", CX.peekOp()) + // debugPP.Println("---dump ops: ", CX.dumpOps()) + // // recording names defined in closure as a whitelist + // if CX.peekOp() == DEFINE || CX.peekOp() == ASSIGN { + // ao := CX.peekOperand() + // if ao != nil { // staff to do + // debugPP.Printf("ao is: %v \n", ao) + // // in scope of define/assign op, record to whitelist + // ao.counter += 1 + // // add nx to whitelist until resolved + // CX.whitelist[cnn.Name] = true + // debugPP.Println(CX.dumpWhitelist()) + // if ao.counter == ao.num { // all resolved + // //CX.clearWhiteList() + // CX.popOp() + // CX.popOperand() + // } + // } + // } + // // capture logic + // // not exist in whitelist, capture + // if _, ok := CX.whitelist[cnn.Name]; !ok { + // debugPP.Printf("nx need capture: %s \n", string(cnn.Name)) + // currentClo := CX.currentClosure() + // debugPP.Printf("currentClo: %v \n", currentClo) + // if currentClo != nil { // a closure to fill + // //if cnn.Path.Depth < 1 { // if local defined, no capture + // debugPP.Printf("---capture: %v \n", cnn) + // cnx := CapturedNx{ + // nx: cnn, + // offset: 0, + // } + // currentClo.Fill(cnx) + // CX.dumpClosures() + // //CX.dumpNodes() + // } + // } + //} else { + // debugPP.Println("---no closure in place") + //} case *BasicLitExpr: case *BinaryExpr: cnn.Left = transcribe(t, nns, TRANS_BINARY_LEFT, 0, cnn.Left, &c).(Expr) // XXX wished this worked with nil. @@ -630,8 +311,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } case *FuncLitExpr: debug.Printf("-----trans, funcLitExpr: %v \n", cnn) - //CX.dumpNodes() - CX.dumpClosures() cnn.Type = *transcribe(t, nns, TRANS_FUNCLIT_TYPE, 0, &cnn.Type, &c).(*FuncTypeExpr) if isStopOrSkip(nc, c) { @@ -644,52 +323,14 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*FuncLitExpr) } - // TODO: get all param and result names to filter out captured nxs - // whitelist - for i, n := range cnn.Names { - debugPP.Printf("name[%d] in staticBlock is: %s \n", i, string(n)) - // put in whitelist, which is per traverse - CX.whitelist[n] = true - } - debugPP.Println("---start trans funcLit body stmt, push initial closure and fx") - - CX.pushNode(cnn) - pushed := CX.pushClosure() - //var pushed bool - - debugPP.Printf("---stop or skip, pop and return \n") - node := CX.peekNodes(1) - isCopy := true - if _, ok := node.(*FuncLitExpr); ok { - isCopy = false - } - for idx := range cnn.Body { cnn.Body[idx] = transcribe(t, nns, TRANS_FUNCLIT_BODY, idx, cnn.Body[idx], &c).(Stmt) if isBreak(c) { break } else if isStopOrSkip(nc, c) { - // pop before return - if pushed { - CX.popClosure(isCopy) - } - //CX.popNode() return } } - // defer pop - debugPP.Printf("---done trans body \n") - // TODO: set fx.Closure, and level as well - debugPP.Println("funcLit pop c-----") - - if pushed { - pc := CX.popClosure(isCopy) - if pc != nil { - cnn.SetClosure(pc) - debugPP.Printf("---done FuncLit trans, fx: %v, closure: %+v \n", cnn, cnn.Closure.String()) - } - } - CX.popNode() case *FieldTypeExpr: cnn.Type = transcribe(t, nns, TRANS_FIELDTYPE_TYPE, 0, cnn.Type, &c).(Expr) if isStopOrSkip(nc, c) { @@ -771,14 +412,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } case *AssignStmt: debugPP.Printf("---assignStmt: %v \n", cnn) - if CX.hasClosure() { - debugPP.Println("---push op and operands") - // push op(assign/define) and operands - CX.ops = append(CX.ops, cnn.Op) - ao := &AssignOperand{} - ao.num = len(cnn.Lhs) - CX.operands = append(CX.operands, ao) - } for idx := range cnn.Lhs { cnn.Lhs[idx] = transcribe(t, nns, TRANS_ASSIGN_LHS, idx, cnn.Lhs[idx], &c).(Expr) if isBreak(c) { @@ -803,25 +436,14 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*BlockStmt) } - CX.pushNode(cnn) - pushed := CX.pushClosure() for idx := range cnn.Body { cnn.Body[idx] = transcribe(t, nns, TRANS_BLOCK_BODY, idx, cnn.Body[idx], &c).(Stmt) if isBreak(c) { break } else if isStopOrSkip(nc, c) { - if pushed { - CX.popClosure(true) - } - CX.popNode() return } } - if pushed { - CX.popClosure(true) - } - CX.popNode() - case *BranchStmt: case *DeclStmt: //CX.pushOp(ASSIGN) @@ -854,10 +476,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc cnn = cnn2.(*ForStmt) } - // after block structure is build - CX.pushNode(cnn) - pushed := CX.pushClosure() - if cnn.Init != nil { cnn.Init = transcribe(t, nns, TRANS_FOR_INIT, 0, cnn.Init, &c).(SimpleStmt) if isStopOrSkip(nc, c) { @@ -882,17 +500,9 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc if isBreak(c) { break } else if isStopOrSkip(nc, c) { - if pushed { - CX.popClosure(true) - } - CX.popNode() return } } - if pushed { - CX.popClosure(true) - } - CX.popNode() case *GoStmt: cnn.Call = *transcribe(t, nns, TRANS_GO_CALL, 0, &cnn.Call, &c).(*CallExpr) if isStopOrSkip(nc, c) { @@ -911,9 +521,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc cnn = cnn2.(*IfStmt) } - CX.pushNode(cnn) - pushed := CX.pushClosure() - // nx in init is always treat defined locally if cnn.Init != nil { cnn.Init = transcribe(t, nns, TRANS_IF_INIT, 0, cnn.Init, &c).(SimpleStmt) @@ -934,10 +541,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc if isStopOrSkip(nc, c) { return } - if pushed { - CX.popClosure(true) - } - CX.popNode() case *IfCaseStmt: debug.Printf("-----trans, (if---case) stmt: %v \n", cnn) cnn2, c2 := t(ns, ftype, index, cnn, TRANS_BLOCK) @@ -972,43 +575,20 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*RangeStmt) } - CX.pushNode(cnn) - pushed := CX.pushClosure() - cnn.X = transcribe(t, nns, TRANS_RANGE_X, 0, cnn.X, &c).(Expr) if isStopOrSkip(nc, c) { - if pushed { - CX.popClosure(true) - } - CX.popNode() return } - if CX.hasClosure() { // TODO: do we need this? - debugPP.Println("---push op and operands") - // push op(assign/define) and operands - CX.ops = append(CX.ops, cnn.Op) - ao := &AssignOperand{} - ao.num = 2 // key, value - CX.operands = append(CX.operands, ao) - } if cnn.Key != nil { cnn.Key = transcribe(t, nns, TRANS_RANGE_KEY, 0, cnn.Key, &c).(Expr) if isStopOrSkip(nc, c) { - if pushed { - CX.popClosure(true) - } - CX.popNode() return } } if cnn.Value != nil { cnn.Value = transcribe(t, nns, TRANS_RANGE_VALUE, 0, cnn.Value, &c).(Expr) if isStopOrSkip(nc, c) { - if pushed { - CX.popClosure(true) - } - CX.popNode() return } } @@ -1018,17 +598,9 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc if isBreak(c) { break } else if isStopOrSkip(nc, c) { - if pushed { - CX.popClosure(true) - } - CX.popNode() return } } - if pushed { - CX.popClosure(true) - } - CX.popNode() case *ReturnStmt: debug.Printf("-----trans, return stmt: %v \n", cnn) for idx := range cnn.Results { @@ -1058,8 +630,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*SelectCaseStmt) } - CX.pushNode(cnn) - pushed := CX.pushClosure() cnn.Comm = transcribe(t, nns, TRANS_SELECTCASE_COMM, 0, cnn.Comm, &c).(Stmt) if isStopOrSkip(nc, c) { return @@ -1069,17 +639,9 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc if isBreak(c) { break } else if isStopOrSkip(nc, c) { - if pushed { - CX.popClosure(true) - } - CX.popNode() return } } - if pushed { - CX.popClosure(true) - } - CX.popNode() case *SendStmt: cnn.Chan = transcribe(t, nns, TRANS_SEND_CHAN, 0, cnn.Chan, &c).(Expr) if isStopOrSkip(nc, c) { @@ -1107,37 +669,22 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc debugPP.Printf("is type switch, init is :%v \n", cnn.Init) debugPP.Printf("is type switch, X is :%v \n", cnn.X) debugPP.Printf("is type switch, varName is :%v \n", cnn.VarName) - CX.whitelist[cnn.VarName] = true } - CX.pushNode(cnn) - pushed := CX.pushClosure() if cnn.Init != nil { cnn.Init = transcribe(t, nns, TRANS_SWITCH_INIT, 0, cnn.Init, &c).(SimpleStmt) if isStopOrSkip(nc, c) { - if pushed { - CX.popClosure(true) - } - CX.popNode() return } } cnn.X = transcribe(t, nns, TRANS_SWITCH_X, 0, cnn.X, &c).(Expr) if isStopOrSkip(nc, c) { - if pushed { - CX.popClosure(true) - } - CX.popNode() return } // NOTE: special block case for after .Init and .X. cnn2, c2 = t(ns, ftype, index, cnn, TRANS_BLOCK2) if isStopOrSkip(nc, c2) { nn = cnn2 - if pushed { - CX.popClosure(true) - } - CX.popNode() return } else { cnn = cnn2.(*SwitchStmt) @@ -1147,17 +694,9 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc if isBreak(c) { break } else if isStopOrSkip(nc, c) { - if pushed { - CX.popClosure(true) - } - CX.popNode() return } } - if pushed { - CX.popClosure(true) - } - CX.popNode() case *SwitchClauseStmt: // NOTE: unlike the select case, both switch // statements AND switch cases visit with the @@ -1205,36 +744,18 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc cnn = cnn2.(*FuncDecl) } - CX.pushNode(cnn) - pushed := CX.pushClosure() for idx := range cnn.Body { cnn.Body[idx] = transcribe(t, nns, TRANS_FUNC_BODY, idx, cnn.Body[idx], &c).(Stmt) if isBreak(c) { break } else if isStopOrSkip(nc, c) { - if pushed { - CX.popClosure(true) - } - CX.popNode() return } } - if pushed { - CX.popClosure(true) - } - CX.popNode() case *ImportDecl: // nothing to do case *ValueDecl: debugPP.Println("---value decl") - if CX.hasClosure() { - debugPP.Println("---push op and operands") - // push op(assign/define) and operands - CX.ops = append(CX.ops, ASSIGN) - ao := &AssignOperand{} - ao.num = len(cnn.NameExprs) - CX.operands = append(CX.operands, ao) - } if cnn.Type != nil { cnn.Type = transcribe(t, nns, TRANS_VAR_TYPE, 0, cnn.Type, &c).(Expr) @@ -1250,7 +771,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } } - CX.popOp() case *TypeDecl: cnn.Type = transcribe(t, nns, TRANS_TYPE_TYPE, 0, cnn.Type, &c).(Expr) if isStopOrSkip(nc, c) { @@ -1265,25 +785,14 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc cnn = cnn2.(*FileNode) } - CX.pushNode(cnn) - pushed := CX.pushClosure() - for idx := range cnn.Decls { cnn.Decls[idx] = transcribe(t, nns, TRANS_FILE_BODY, idx, cnn.Decls[idx], &c).(Decl) if isBreak(c) { break } else if isStopOrSkip(nc, c) { - if pushed { - CX.popClosure(true) - } - CX.popNode() return } } - if pushed { - CX.popClosure(true) - } - CX.popNode() case *ConstExpr, *constTypeExpr: // leaf nodes // These nodes get created by the preprocessor while // leaving the type expression of a composite lit, before diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index d26c3d92ad9..2fefe59115b 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -516,13 +516,26 @@ func (sv *StructValue) Copy(alloc *Allocator) *StructValue { } type TimeSeriesBag struct { + isFilled bool isSealed bool + isTainted bool transient []*Transient } +func (tsb *TimeSeriesBag) isEmpty() bool { + for _, tt := range tsb.transient { + if tt != nil && tt.values != nil && len(tt.values) != 0 { + return false + } + } + return true +} + func (tsb *TimeSeriesBag) setRatio(r int8) { for _, tt := range tsb.transient { - tt.expandRatio = r + if tt != nil { + tt.expandRatio = r + } } } @@ -530,7 +543,7 @@ func (tsb *TimeSeriesBag) String() string { var s string s += "\n" s += "==================time bag===================\n" - s += fmt.Sprintf("isSealed: %v \n", tsb.isSealed) + s += fmt.Sprintf("isFilled: %v \n", tsb.isFilled) for i, t := range tsb.transient { s += fmt.Sprintf("nx[%d]: %v \n", i, t.nx) for j, v := range t.values { diff --git a/gnovm/tests/debug/sort_search_efficiency.gno b/gnovm/tests/debug/sort_search_efficiency.gno new file mode 100644 index 00000000000..90362944ac5 --- /dev/null +++ b/gnovm/tests/debug/sort_search_efficiency.gno @@ -0,0 +1,21 @@ +package main + +func Search(n int, f func(int) bool) int { + f(1) + return 0 +} + +func main() { + for x := 0; x < 2; x++ { + count := 0 + println(" first: count: ", count) + Search(1, func(i int) bool { count++; return i >= x }) + println("second: count: ", count) + } +} + +// Output: +// first: count: 0 +// second: count: 1 +// first: count: 0 +// second: count: 1 diff --git a/gnovm/tests/debug/sort_search_efficiency_simple.gnoa b/gnovm/tests/debug/sort_search_efficiency_simple.gnoa new file mode 100644 index 00000000000..4c242461d9e --- /dev/null +++ b/gnovm/tests/debug/sort_search_efficiency_simple.gnoa @@ -0,0 +1,25 @@ +package main + +func Search(x, y int, f func(a, b int) int) int { + return f(x, y) +} + +func main() { + for x := 0; x < 10; x++ { + count := 0 + r := Search(x, count, func(a, b int) int { return a + b }) + println(r) + } +} + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 +// 5 +// 6 +// 7 +// 8 +// 9 diff --git a/gnovm/tests/file.go b/gnovm/tests/file.go index 8cfee1f8d50..fd99b0c3b6c 100644 --- a/gnovm/tests/file.go +++ b/gnovm/tests/file.go @@ -3,7 +3,6 @@ package tests import ( "bytes" "fmt" - "github.com/gnolang/gno/tm2/pkg/bft/types/time" "go/ast" "go/parser" "go/token" @@ -151,7 +150,7 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error { } if !gno.IsRealmPath(pkgPath) { - startTime := time.Now() + //startTime := time.Now() // simple case. pn := gno.NewPackageNode(pkgName, pkgPath, &gno.FileSet{}) @@ -166,17 +165,17 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error { f.logger("RUN MAIN") f.logger("========================================") } - endTime := time.Now() - elapsedTime := endTime.Sub(startTime) - fmt.Printf("Run Init took %s to execute\n", elapsedTime) + //endTime := time.Now() + //elapsedTime := endTime.Sub(startTime) + //fmt.Printf("Run Init took %s to execute\n", elapsedTime) - startTime = time.Now() + //startTime = time.Now() m.RunMain() - endTime = time.Now() - elapsedTime = endTime.Sub(startTime) + //endTime = time.Now() + //elapsedTime = endTime.Sub(startTime) - fmt.Printf("Run Main took %s to execute\n", elapsedTime) + //fmt.Printf("Run Main took %s to execute\n", elapsedTime) if f.logger != nil { f.logger("========================================") f.logger("RUN MAIN END") diff --git a/gnovm/tests/files/zrealm0.gno b/gnovm/tests/files/zrealm0.gno index 7d8176cc1ec..489fbbdf829 100644 --- a/gnovm/tests/files/zrealm0.gno +++ b/gnovm/tests/files/zrealm0.gno @@ -70,6 +70,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], diff --git a/gnovm/tests/files/zrealm1.gno b/gnovm/tests/files/zrealm1.gno index 9b1066fa9dc..d88bb1e27dd 100644 --- a/gnovm/tests/files/zrealm1.gno +++ b/gnovm/tests/files/zrealm1.gno @@ -184,6 +184,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], diff --git a/gnovm/tests/files/zrealm2.gno b/gnovm/tests/files/zrealm2.gno index 7f04d858d87..635c872a4dd 100644 --- a/gnovm/tests/files/zrealm2.gno +++ b/gnovm/tests/files/zrealm2.gno @@ -187,6 +187,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -224,6 +225,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], diff --git a/gnovm/tests/files/zrealm3.gno b/gnovm/tests/files/zrealm3.gno index 56995c30b5e..ca4fe03c577 100644 --- a/gnovm/tests/files/zrealm3.gno +++ b/gnovm/tests/files/zrealm3.gno @@ -186,6 +186,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -223,6 +224,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], diff --git a/gnovm/tests/files/zrealm4.gno b/gnovm/tests/files/zrealm4.gno index 668630ada1c..e9dc7ebfcc1 100644 --- a/gnovm/tests/files/zrealm4.gno +++ b/gnovm/tests/files/zrealm4.gno @@ -128,6 +128,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -165,6 +166,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], diff --git a/gnovm/tests/files/zrealm5.gno b/gnovm/tests/files/zrealm5.gno index d90f2c1a4f1..b2f14109ffd 100644 --- a/gnovm/tests/files/zrealm5.gno +++ b/gnovm/tests/files/zrealm5.gno @@ -199,6 +199,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -236,6 +237,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], diff --git a/gnovm/tests/files/zrealm6.gno b/gnovm/tests/files/zrealm6.gno index 8e0e02da325..6bfba4f7f45 100644 --- a/gnovm/tests/files/zrealm6.gno +++ b/gnovm/tests/files/zrealm6.gno @@ -271,6 +271,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -308,6 +309,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], diff --git a/gnovm/tests/files/zrealm7.gno b/gnovm/tests/files/zrealm7.gno index 7e841c3fdca..67d2ccf3687 100644 --- a/gnovm/tests/files/zrealm7.gno +++ b/gnovm/tests/files/zrealm7.gno @@ -343,6 +343,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -380,6 +381,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], diff --git a/gnovm/tests/files/zrealm_avl0.gno b/gnovm/tests/files/zrealm_avl0.gno index 191bc41c0f9..8c4637652cb 100644 --- a/gnovm/tests/files/zrealm_avl0.gno +++ b/gnovm/tests/files/zrealm_avl0.gno @@ -281,6 +281,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -318,6 +319,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], diff --git a/gnovm/tests/files/zrealm_avl1.gno b/gnovm/tests/files/zrealm_avl1.gno index ab8a22124b7..c760d1e529d 100644 --- a/gnovm/tests/files/zrealm_avl1.gno +++ b/gnovm/tests/files/zrealm_avl1.gno @@ -305,6 +305,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -342,6 +343,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], diff --git a/gnovm/tests/files/zrealm_natbind0.gno b/gnovm/tests/files/zrealm_natbind0.gno index 9e9a8afb196..af482361672 100644 --- a/gnovm/tests/files/zrealm_natbind0.gno +++ b/gnovm/tests/files/zrealm_natbind0.gno @@ -75,6 +75,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -112,6 +113,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -169,6 +171,7 @@ func main() { // "PkgPath": "std" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [ diff --git a/gnovm/tests/files/zrealm_tests0.gno b/gnovm/tests/files/zrealm_tests0.gno index 996f1e78349..972b61270db 100644 --- a/gnovm/tests/files/zrealm_tests0.gno +++ b/gnovm/tests/files/zrealm_tests0.gno @@ -253,6 +253,7 @@ func main() { // "PkgPath": "gno.land/r/demo/tests" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [ @@ -360,6 +361,7 @@ func main() { // "PkgPath": "gno.land/r/demo/tests" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [ @@ -427,6 +429,7 @@ func main() { // "PkgPath": "gno.land/r/demo/tests" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [ @@ -484,6 +487,7 @@ func main() { // "PkgPath": "gno.land/r/demo/tests" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -531,6 +535,7 @@ func main() { // "PkgPath": "gno.land/r/demo/tests" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -588,6 +593,7 @@ func main() { // "PkgPath": "gno.land/r/demo/tests" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -635,6 +641,7 @@ func main() { // "PkgPath": "gno.land/r/demo/tests" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -682,6 +689,7 @@ func main() { // "PkgPath": "gno.land/r/demo/tests" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -742,6 +750,7 @@ func main() { // "PkgPath": "gno.land/r/demo/tests" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [ @@ -792,6 +801,7 @@ func main() { // "PkgPath": "gno.land/r/demo/tests" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -829,6 +839,7 @@ func main() { // "PkgPath": "gno.land/r/demo/tests" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -866,6 +877,7 @@ func main() { // "PkgPath": "gno.land/r/demo/tests" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -913,6 +925,7 @@ func main() { // "PkgPath": "gno.land/r/demo/tests" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -970,6 +983,7 @@ func main() { // "PkgPath": "gno.land/r/demo/tests" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -1028,6 +1042,7 @@ func main() { // "PkgPath": "gno.land/r/demo/tests" // } // }, +// "Tss": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [ From 36f8345a9e74c06736522c5c0b360ddc1d5d564a Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Wed, 24 Jan 2024 18:21:02 +0800 Subject: [PATCH 14/32] range support --- gnovm/pkg/gnolang/op_exec.go | 118 ++++++++++-------- gnovm/tests/debug/10.gno | 21 ++++ gnovm/tests/debug/10a.gno | 20 +++ gnovm/tests/debug/10ab.gno | 19 +++ gnovm/tests/debug/10b.gno | 23 ++++ gnovm/tests/debug/10bb.gno | 22 ++++ gnovm/tests/debug2/7a.gno | 2 +- gnovm/tests/debug2/sort_search_efficiency.gno | 21 ++++ .../debug2/sort_search_efficiency_simple.gnoa | 25 ++++ 9 files changed, 217 insertions(+), 54 deletions(-) create mode 100644 gnovm/tests/debug/10.gno create mode 100644 gnovm/tests/debug/10a.gno create mode 100644 gnovm/tests/debug/10ab.gno create mode 100644 gnovm/tests/debug/10b.gno create mode 100644 gnovm/tests/debug/10bb.gno create mode 100644 gnovm/tests/debug2/sort_search_efficiency.gno create mode 100644 gnovm/tests/debug2/sort_search_efficiency_simple.gnoa diff --git a/gnovm/pkg/gnolang/op_exec.go b/gnovm/pkg/gnolang/op_exec.go index 59841a0b6ed..495eff6f3f9 100644 --- a/gnovm/pkg/gnolang/op_exec.go +++ b/gnovm/pkg/gnolang/op_exec.go @@ -43,6 +43,61 @@ SelectStmt -> */ +func updateCapturedValue(m *Machine, lb *Block) { + if lb.GetBodyStmt().isLoop { // do we need this? + bs := lb.GetBodyStmt() + debugPP.Printf("---------isLoop, current block is: %v \n", lb) + debugPP.Printf("---------bag, %v \n", bs.Bag) + if bs.Bag != nil && bs.Bag.isFilled && !bs.Bag.isTainted { + debugPP.Printf("---addr of bs.Bag is: %p \n", &bs.Bag) + names := lb.Source.GetBlockNames() + var found bool + var isSealed bool + for i, tt := range bs.Bag.transient { + if tt != nil { + debugPP.Printf("transient[%d] name is %v, path: %v \n", i, tt.nx.Name, tt.nx.Path) + // first check if name is in current block, it not, stop + for _, name := range names { + if tt.nx.Name == name { + debugPP.Printf("found %s in current block: %v \n", tt.nx.Name, lb) + found = true + } + } + if found { + nvp := lb.Source.GetPathForName(m.Store, tt.nx.Name) + ptr := m.LastBlock().GetPointerTo(m.Store, nvp) + tv := ptr.Deref() + debugPP.Printf("--- new tv : %v \n", tv) + // set back + debugPP.Printf("before update, len of Bag values is : %d \n", len(bs.Bag.transient[i].values)) + + expandRatio := bs.Bag.transient[i].expandRatio + debugPP.Printf("--- expand ratio is: %d \n", expandRatio) + for j := int8(0); j < expandRatio; j++ { + bs.Bag.transient[i].values = append(bs.Bag.transient[i].values, tv) + } + debugPP.Printf("after update, len of Bag values is : %d \n", len(bs.Bag.transient[i].values)) + + // update higher level if it is also a loop, padding. + upperBlock := findNearestLoopBlock(m.Store, lb) + debugPP.Printf("upperBlock is: %v \n", upperBlock) + if upperBlock != nil && upperBlock.GetBodyStmt().Bag != nil { + upperBlock.GetBodyStmt().Bag.setRatio(int8(len(bs.Bag.transient[i].values))) + } + isSealed = true + } else { + debugPP.Printf("---not found %s in current block, b: %v \n", tt.nx.Name, lb) + isSealed = false + } + } + } + if isSealed { + bs.Bag.isSealed = true // seal for this bag of this block + } + } + } +} + //---------------------------------------- // doOpExec // @@ -119,59 +174,8 @@ func (m *Machine) doOpExec(op Op) { s = next goto EXEC_SWITCH } else if bs.NextBodyIndex == bs.BodyLen { - debugPP.Printf("---end of loop body, going to update value using current state--- \n") - if lb.GetBodyStmt().isLoop { // do we need this? - debugPP.Printf("---------isLoop, current block is: %v \n", m.LastBlock()) - debugPP.Printf("---------bag, %v \n", bs.Bag) - if bs.Bag != nil && bs.Bag.isFilled && !bs.Bag.isTainted { - debugPP.Printf("---addr of bs.Bag is: %p \n", &bs.Bag) - names := lb.Source.GetBlockNames() - var found bool - var isSealed bool - for i, tt := range bs.Bag.transient { - if tt != nil { - debugPP.Printf("transient[%d] name is %v, path: %v \n", i, tt.nx.Name, tt.nx.Path) - // first check if name is in current block, it not, stop - for _, name := range names { - if tt.nx.Name == name { - debugPP.Printf("found %s in current block: %v \n", tt.nx.Name, lb) - found = true - } - } - if found { - nvp := lb.Source.GetPathForName(m.Store, tt.nx.Name) - ptr := m.LastBlock().GetPointerTo(m.Store, nvp) - tv := ptr.Deref() - debugPP.Printf("--- new tv : %v \n", tv) - // set back - debugPP.Printf("before update, len of Bag values is : %d \n", len(bs.Bag.transient[i].values)) - - expandRatio := bs.Bag.transient[i].expandRatio - debugPP.Printf("--- expand ratio is: %d \n", expandRatio) - for j := int8(0); j < expandRatio; j++ { - bs.Bag.transient[i].values = append(bs.Bag.transient[i].values, tv) - } - debugPP.Printf("after update, len of Bag values is : %d \n", len(bs.Bag.transient[i].values)) - - // update higher level if it is also a loop, padding. - upperBlock := findNearestLoopBlock(m.Store, lb) - debugPP.Printf("upperBlock is: %v \n", upperBlock) - if upperBlock != nil && upperBlock.GetBodyStmt().Bag != nil { - upperBlock.GetBodyStmt().Bag.setRatio(int8(len(bs.Bag.transient[i].values))) - } - isSealed = true - } else { - debugPP.Printf("---not found %s in current block, b: %v \n", tt.nx.Name, lb) - isSealed = false - } - } - } - if isSealed { - bs.Bag.isSealed = true // seal for this bag of this block - } - } - } - + debugPP.Printf("---for loop end of loop body, going to update value using current state--- \n") + updateCapturedValue(m, lb) // (queue to) go back. if bs.Cond != nil { m.PushExpr(bs.Cond) @@ -264,6 +268,9 @@ func (m *Machine) doOpExec(op Op) { s = next // switch on bs.Active goto EXEC_SWITCH } else if bs.NextBodyIndex == bs.BodyLen { + // update captured values + debugPP.Printf("---range array end of loop body, going to update value using current state--- \n") + updateCapturedValue(m, m.LastBlock()) if bs.ListIndex < bs.ListLen-1 { // set up next assign if needed. switch bs.Op { @@ -358,6 +365,8 @@ func (m *Machine) doOpExec(op Op) { s = next // switch on bs.Active goto EXEC_SWITCH } else if bs.NextBodyIndex == bs.BodyLen { + debugPP.Printf("---range of string end of loop body, going to update value using current state--- \n") + updateCapturedValue(m, m.LastBlock()) if bs.StrIndex < bs.StrLen { // set up next assign if needed. switch bs.Op { @@ -451,6 +460,8 @@ func (m *Machine) doOpExec(op Op) { s = next // switch on bs.Active goto EXEC_SWITCH } else if bs.NextBodyIndex == bs.BodyLen { + debugPP.Printf("---range of map, end of loop body, going to update value using current state--- \n") + updateCapturedValue(m, m.LastBlock()) nnext := bs.NextItem.Next if nnext == nil { // done with range. @@ -658,6 +669,7 @@ EXEC_SWITCH: Key: cs.Key, Value: cs.Value, Op: cs.Op, + isLoop: true, } m.PushBlock(b) // TODO: replace with "cs.Op". diff --git a/gnovm/tests/debug/10.gno b/gnovm/tests/debug/10.gno new file mode 100644 index 00000000000..8a472ffb410 --- /dev/null +++ b/gnovm/tests/debug/10.gno @@ -0,0 +1,21 @@ +package main + +func main() { + var fns []func() int + s := []int{1, 2, 3} + for i, _ := range s { + x := i + f := func() int { + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 2 diff --git a/gnovm/tests/debug/10a.gno b/gnovm/tests/debug/10a.gno new file mode 100644 index 00000000000..ee375bc719c --- /dev/null +++ b/gnovm/tests/debug/10a.gno @@ -0,0 +1,20 @@ +package main + +func main() { + var fns []func() int + m := map[string]int{"a": 1, "b": 2} + for _, v := range m { + x := v + f := func() int { + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 1 +// 2 diff --git a/gnovm/tests/debug/10ab.gno b/gnovm/tests/debug/10ab.gno new file mode 100644 index 00000000000..857cc11d7da --- /dev/null +++ b/gnovm/tests/debug/10ab.gno @@ -0,0 +1,19 @@ +package main + +func main() { + var fns []func() int + m := map[string]int{"a": 1, "b": 2} + for _, v := range m { + f := func() int { + return v + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 1 +// 2 diff --git a/gnovm/tests/debug/10b.gno b/gnovm/tests/debug/10b.gno new file mode 100644 index 00000000000..5d26a2c6dc0 --- /dev/null +++ b/gnovm/tests/debug/10b.gno @@ -0,0 +1,23 @@ +package main + +func main() { + var fns []func() int + s := "hello" + for i, _ := range s { + x := i + f := func() int { + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 diff --git a/gnovm/tests/debug/10bb.gno b/gnovm/tests/debug/10bb.gno new file mode 100644 index 00000000000..a7ddb99504f --- /dev/null +++ b/gnovm/tests/debug/10bb.gno @@ -0,0 +1,22 @@ +package main + +func main() { + var fns []func() int + s := "hello" + for i, _ := range s { + f := func() int { + return i + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 diff --git a/gnovm/tests/debug2/7a.gno b/gnovm/tests/debug2/7a.gno index 66d5c3a8ddc..9ae40a43df7 100644 --- a/gnovm/tests/debug2/7a.gno +++ b/gnovm/tests/debug2/7a.gno @@ -29,7 +29,7 @@ func main() { // Output: // Factorial of 0 is: 1 -// 1 +// 2 // Factorial of 1 is: 1 // 2 // 2 diff --git a/gnovm/tests/debug2/sort_search_efficiency.gno b/gnovm/tests/debug2/sort_search_efficiency.gno new file mode 100644 index 00000000000..90362944ac5 --- /dev/null +++ b/gnovm/tests/debug2/sort_search_efficiency.gno @@ -0,0 +1,21 @@ +package main + +func Search(n int, f func(int) bool) int { + f(1) + return 0 +} + +func main() { + for x := 0; x < 2; x++ { + count := 0 + println(" first: count: ", count) + Search(1, func(i int) bool { count++; return i >= x }) + println("second: count: ", count) + } +} + +// Output: +// first: count: 0 +// second: count: 1 +// first: count: 0 +// second: count: 1 diff --git a/gnovm/tests/debug2/sort_search_efficiency_simple.gnoa b/gnovm/tests/debug2/sort_search_efficiency_simple.gnoa new file mode 100644 index 00000000000..4c242461d9e --- /dev/null +++ b/gnovm/tests/debug2/sort_search_efficiency_simple.gnoa @@ -0,0 +1,25 @@ +package main + +func Search(x, y int, f func(a, b int) int) int { + return f(x, y) +} + +func main() { + for x := 0; x < 10; x++ { + count := 0 + r := Search(x, count, func(a, b int) int { return a + b }) + println(r) + } +} + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 +// 5 +// 6 +// 7 +// 8 +// 9 From 3fb64f3d7155517c0de8c996af971d5566e1e3fa Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Thu, 25 Jan 2024 13:59:28 +0800 Subject: [PATCH 15/32] fix index --- gnovm/pkg/gnolang/nodes.go | 20 +++++++++---------- gnovm/pkg/gnolang/op_call.go | 30 +++++++++++++++-------------- gnovm/pkg/gnolang/op_exec.go | 15 ++++++++------- gnovm/pkg/gnolang/op_expressions.go | 26 +++++++++++++++++-------- gnovm/pkg/gnolang/values.go | 18 ++++++++++++++++- gnovm/tests/debug/01.gno | 20 +++++++++++++++++++ gnovm/tests/debug/1h.gno | 8 +------- gnovm/tests/debug/7a.gno | 11 +++++++---- 8 files changed, 97 insertions(+), 51 deletions(-) create mode 100644 gnovm/tests/debug/01.gno diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index ea8c46129e3..8065e1e713a 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -877,16 +877,16 @@ type SwitchClauseStmt struct { // NOTE: embedded in Block. type bodyStmt struct { Attributes - Body // for non-loop stmts - BodyLen int // for for-continue - NextBodyIndex int // init:-2, cond/elem:-1, body:0..., post:n - NumOps int // number of Ops, for goto - NumValues int // number of Values, for goto - NumExprs int // number of Exprs, for goto - NumStmts int // number of Stmts, for goto - Cond Expr // for ForStmt - Post Stmt // for ForStmt - Bag *TimeSeriesBag + Body // for non-loop stmts + BodyLen int // for for-continue + NextBodyIndex int // init:-2, cond/elem:-1, body:0..., post:n + NumOps int // number of Ops, for goto + NumValues int // number of Values, for goto + NumExprs int // number of Exprs, for goto + NumStmts int // number of Stmts, for goto + Cond Expr // for ForStmt + Post Stmt // for ForStmt + Bag *TimeSeriesBag // a series of values of captured vars isLoop bool Active Stmt // for PopStmt() Key Expr // for RangeStmt diff --git a/gnovm/pkg/gnolang/op_call.go b/gnovm/pkg/gnolang/op_call.go index 34b1133b33f..73900d93095 100644 --- a/gnovm/pkg/gnolang/op_call.go +++ b/gnovm/pkg/gnolang/op_call.go @@ -72,8 +72,8 @@ func (m *Machine) doOpCall() { var targetB *Block debugPP.Println("================parse transient for fv====================") if fv.Tss != nil { - var end bool - for i, bag := range fv.Tss { // each bag is for a certain level of block + for i, loopData := range fv.Tss { // each bag is for a certain level of block + bag := loopData.bag debugPP.Printf("Level[%d] bag is : %v \n", i, bag) if bag != nil { if bag.isSealed { @@ -99,26 +99,28 @@ func (m *Machine) doOpCall() { } } if match { - debugPP.Println("===match") - targetB.UpdateValue(index, t.values[0]) - nvs := t.values[1:] - bag.transient[j].values = nvs - if len(bag.transient[j].values) == 0 { // loop end - bag.transient[j] = nil // empty a name and value - end = true - } + debugPP.Println("===match, cursor is:", loopData.index) + + targetB.UpdateValue(index, t.values[loopData.index]) + //nvs := t.values[1:] + //bag.transient[j].values = nvs + //if len(bag.transient[j].values) == 0 { // loop end + // bag.transient[j] = nil // empty a name and value + // end = true + //} } } } } } else { // not sealed will be tainted - fv.Tss[i].isTainted = true + debugPP.Println("---taint this var") + fv.Tss[i].bag.isTainted = true } } } - if end { - fv.Tss = nil - } + //if end { + fv.Tss = nil + //} } // only need initial snapshot diff --git a/gnovm/pkg/gnolang/op_exec.go b/gnovm/pkg/gnolang/op_exec.go index 495eff6f3f9..746382b8c30 100644 --- a/gnovm/pkg/gnolang/op_exec.go +++ b/gnovm/pkg/gnolang/op_exec.go @@ -71,19 +71,20 @@ func updateCapturedValue(m *Machine, lb *Block) { // set back debugPP.Printf("before update, len of Bag values is : %d \n", len(bs.Bag.transient[i].values)) - expandRatio := bs.Bag.transient[i].expandRatio + //expandRatio := bs.Bag.transient[i].expandRatio + expandRatio := bs.Bag.transient[i].cursor + 1 - len(bs.Bag.transient[i].values) // 2 - 0 debugPP.Printf("--- expand ratio is: %d \n", expandRatio) - for j := int8(0); j < expandRatio; j++ { + for j := 0; j < expandRatio; j++ { bs.Bag.transient[i].values = append(bs.Bag.transient[i].values, tv) } debugPP.Printf("after update, len of Bag values is : %d \n", len(bs.Bag.transient[i].values)) // update higher level if it is also a loop, padding. - upperBlock := findNearestLoopBlock(m.Store, lb) - debugPP.Printf("upperBlock is: %v \n", upperBlock) - if upperBlock != nil && upperBlock.GetBodyStmt().Bag != nil { - upperBlock.GetBodyStmt().Bag.setRatio(int8(len(bs.Bag.transient[i].values))) - } + //upperBlock := findNearestLoopBlock(m.Store, lb) + //debugPP.Printf("upperBlock is: %v \n", upperBlock) + //if upperBlock != nil && upperBlock.GetBodyStmt().Bag != nil { + // upperBlock.GetBodyStmt().Bag.setRatio(int8(len(bs.Bag.transient[i].values))) + //} isSealed = true } else { debugPP.Printf("---not found %s in current block, b: %v \n", tt.nx.Name, lb) diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index f084a777c91..d8c25dd225b 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -760,7 +760,8 @@ func (m *Machine) doOpFuncLit() { var lastGen, gen uint8 var isReuse bool - var Tss []*TimeSeriesBag + var Tss []*LoopData + //var Tss *LoopData // start search every captured var in target block via path // 1. Tss is a slice of bag, one bag is for one loop block. // 2. new a `bag` for every block, the bag has a flag of isFilled for reuse, @@ -783,14 +784,16 @@ func (m *Machine) doOpFuncLit() { debugPP.Printf("captures[%d] is : %v \n", i, nx.Name) // start search block, in the order of small depth to big depth loopBlock, isLoopBlock, gen = findLoopBlockWithPath(m.Store, lb, nx) + debugPP.Printf("loopBlock: %v, isLoopBlock: %v, gen: %v, cursor: %v \n", loopBlock, isLoopBlock, gen) if lastGen == 0 { lastGen = gen } else if gen != lastGen { // if enter new level of block, means last block is all done, pack bag lastGen = gen // set last state - if bag != nil { // has something to pack + if bag != nil && !isReuse { // has something to pack bag.isFilled = true - Tss = append(Tss, bag) + Tss = append(Tss, &LoopData{index: 0, bag: bag}) // pack per level of block + //Tss = append(Tss, bag) } debugPP.Printf("========packed bag for %v is: %s \n", loopBlock, bag) } @@ -805,25 +808,32 @@ func (m *Machine) doOpFuncLit() { tst := &Transient{ nx: nx, expandRatio: 1, // default 1 + cursor: 0, } bag.transient = append(bag.transient, tst) // set back to loop block properly loopBlock.GetBodyStmt().Bag = bag } else { // repack when iterates, this should be optimized isReuse = true // use isFilled instead - Tss = append(Tss, bag) + // get cursor by name + debugPP.Println("---reuse, current cursor is: ", bag.transient[bag.getIndexByName(nx.Name)].cursor) + bag.transient[bag.getIndexByName(nx.Name)].cursor++ // only inc in reuse, that is iteration + //Tss = append(Tss, bag) + // cursor indicates num of iterations of fv + Tss = append(Tss, &LoopData{index: bag.transient[bag.getIndexByName(nx.Name)].cursor, bag: bag}) } } } // tail set - if bag != nil && !isReuse { - bag.isFilled = true // set for the last bag - Tss = append(Tss, bag) // collect the last bag + if bag != nil && !bag.isFilled && !isReuse { + debugPP.Println("---tail pack") + bag.isFilled = true // set for the last bag + Tss = append(Tss, &LoopData{index: 0, bag: bag}) // collect the last bag debugPP.Printf("========packed bag for %v is: %s \n", loopBlock, bag) } for i, ts := range Tss { - debugPP.Printf("========Tss[%d] is: %s, addr: %p \n", i, ts, &ts) + debugPP.Printf("========Tss[%d] is: %s, addr: %p, index: %d \n", i, ts.bag, &ts.bag, ts.index) } ft := m.PopValue().V.(TypeValue).Type.(*FuncType) diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index 2fefe59115b..45f24d19339 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -522,6 +522,15 @@ type TimeSeriesBag struct { transient []*Transient } +func (tsb *TimeSeriesBag) getIndexByName(n Name) int { + for i, tt := range tsb.transient { + if n == tt.nx.Name { + return i + } + } + return -1 // never reach +} + func (tsb *TimeSeriesBag) isEmpty() bool { for _, tt := range tsb.transient { if tt != nil && tt.values != nil && len(tt.values) != 0 { @@ -549,6 +558,7 @@ func (tsb *TimeSeriesBag) String() string { for j, v := range t.values { s += fmt.Sprintf("values[%d]: %v \n", j, v) } + s += fmt.Sprintf("cursor: %v \n", t.cursor) } s += "====================end======================\n" return s @@ -558,6 +568,7 @@ type Transient struct { nx *NameExpr expandRatio int8 // TODO: determine proper type values []TypedValue // one name with multi snapshot + cursor int // cursor per fv to find value in time series } type Captured struct { @@ -598,7 +609,7 @@ type FuncValue struct { Name Name // name of function/method Closure Value // *Block or RefValue to closure (may be nil for file blocks; lazy) Captures *Captured - Tss []*TimeSeriesBag + Tss []*LoopData FileName Name // file name where declared PkgPath string NativePkg string // for native bindings through NativeStore @@ -608,6 +619,11 @@ type FuncValue struct { nativeBody func(*Machine) // alternative to Body } +type LoopData struct { + index int // index for fv to look for var + bag *TimeSeriesBag // per loop block +} + func (fv *FuncValue) dump() { if debugPP { fmt.Printf("funcValue, name: %s, captures: %s \n", fv.Name, fv.Captures) diff --git a/gnovm/tests/debug/01.gno b/gnovm/tests/debug/01.gno new file mode 100644 index 00000000000..82ca810b13c --- /dev/null +++ b/gnovm/tests/debug/01.gno @@ -0,0 +1,20 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 5; i++ { + x := i + f := func() int { + return x + } + fns = append(fns, f) + } + println(fns[3]()) + println(fns[2]()) + println(fns[0]()) +} + +// Output: +// 3 +// 2 +// 0 diff --git a/gnovm/tests/debug/1h.gno b/gnovm/tests/debug/1h.gno index addd660d04a..b3fc6026584 100644 --- a/gnovm/tests/debug/1h.gno +++ b/gnovm/tests/debug/1h.gno @@ -2,7 +2,7 @@ package main func main() { var fns []func() int - for i := 0; i < 5; i++ { + for i := 0; i < 2; i++ { x := i for j := 0; j < 2; j++ { y := j @@ -22,9 +22,3 @@ func main() { // 1 // 1 // 2 -// 2 -// 3 -// 3 -// 4 -// 4 -// 5 diff --git a/gnovm/tests/debug/7a.gno b/gnovm/tests/debug/7a.gno index 9ae40a43df7..edb62312a29 100644 --- a/gnovm/tests/debug/7a.gno +++ b/gnovm/tests/debug/7a.gno @@ -11,10 +11,10 @@ func main() { for i := 0; i < 3; i++ { recursiveFunc = func(num int) int { x := i + println("value of x: ", x) if num <= 0 { return 1 } - println(x) return num * recursiveFunc(num-1) } fns = append(fns, recursiveFunc) @@ -28,9 +28,12 @@ func main() { } // Output: +// value of x: 0 // Factorial of 0 is: 1 -// 2 +// value of x: 1 +// value of x: 2 // Factorial of 1 is: 1 -// 2 -// 2 +// value of x: 2 +// value of x: 2 +// value of x: 2 // Factorial of 2 is: 2 From aa542a3fc56a8e7b3570ebbc9fc19b3fa067a804 Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Thu, 25 Jan 2024 18:19:01 +0800 Subject: [PATCH 16/32] clean --- gnovm/pkg/gnolang/machine.go | 8 +- gnovm/pkg/gnolang/nodes.go | 8 +- gnovm/pkg/gnolang/op_assign.go | 2 +- gnovm/pkg/gnolang/op_call.go | 35 ++--- gnovm/pkg/gnolang/op_eval.go | 2 - gnovm/pkg/gnolang/op_exec.go | 36 ++--- gnovm/pkg/gnolang/op_expressions.go | 137 +++++++----------- gnovm/pkg/gnolang/preprocess.go | 24 +-- gnovm/pkg/gnolang/transcribe.go | 8 +- gnovm/pkg/gnolang/values.go | 134 +++++++---------- gnovm/tests/debug/4d.gnoa | 0 gnovm/tests/debug/6.gno | 23 --- gnovm/tests/debug/{2.gno => closure10.gno} | 0 gnovm/tests/debug/{2a.gno => closure10_a.gno} | 0 gnovm/tests/debug/{3.gno => closure11.gno} | 0 gnovm/tests/debug/{3a.gno => closure11_a.gno} | 0 gnovm/tests/debug/{3b.gno => closure11_b.gno} | 0 gnovm/tests/debug/{4.gno => closure12.gno} | 0 gnovm/tests/debug/{4a.gno => closure12_a.gno} | 0 gnovm/tests/debug/{4b.gno => closure12_b.gno} | 0 gnovm/tests/debug/{4c.gno => closure12_c.gno} | 0 gnovm/tests/debug/{4e.gno => closure12_e.gno} | 0 gnovm/tests/debug/{4f.gno => closure12_f.gno} | 0 gnovm/tests/debug/{4g.gno => closure12_g.gno} | 0 gnovm/tests/debug/{4h.gno => closure12_h.gno} | 0 gnovm/tests/debug/{4i.gno => closure12_i.gno} | 0 gnovm/tests/debug/{5.gno => closure13.gno} | 0 gnovm/tests/debug/{5a.gno => closure13_a.gno} | 0 gnovm/tests/debug/{5b.gno => closure13_b.gno} | 3 +- .../tests/debug/{5b0.gno => closure13_b0.gno} | 0 .../tests/debug/{5b1.gno => closure13_b1.gno} | 0 .../{debug2/6.gno => debug/closure14.gno} | 7 +- gnovm/tests/debug/{6a.gno => closure14_a.gno} | 9 +- gnovm/tests/debug/{7.gno => closure15.gno} | 0 gnovm/tests/debug/{7a.gno => closure15_a.gno} | 12 +- gnovm/tests/debug/{10.gno => closure16.gno} | 0 .../tests/debug/{10a.gno => closure16_a.gno} | 0 .../debug/{10ab.gno => closure16_a1.gno} | 0 .../tests/debug/{10b.gno => closure16_b.gno} | 0 .../debug/{10bb.gno => closure16_b1.gno} | 0 .../debug/{io2.gno => closure17_io2.gno} | 0 .../{recover4.gno => closure17_recover4.gno} | 0 .../{recover6.gno => closure17_recover6.gno} | 0 ...{recover6a.gno => closure17_recover6a.gno} | 0 ...o => closure17_sort_search_efficiency.gno} | 0 ...libs.gno => closure17_zregexp_stdlibs.gno} | 0 gnovm/tests/debug/{1.gno => closure9.gno} | 8 + .../{debug2/1d.gno => debug/closure9_a0.gno} | 18 +-- .../tests/debug/{01.gno => closure9_a02.gno} | 0 gnovm/tests/debug/closure9_a1.gno | 34 +++++ gnovm/tests/debug/{1b.gno => closure9_b.gno} | 0 gnovm/tests/debug/{1c.gno => closure9_c.gno} | 0 gnovm/tests/debug/{1d.gno => closure9_d.gno} | 0 gnovm/tests/debug/{1e.gno => closure9_e.gno} | 0 gnovm/tests/debug/{1f.gno => closure9_f.gno} | 0 gnovm/tests/debug/{1g.gno => closure9_g.gno} | 0 gnovm/tests/debug/{1h.gno => closure9_h.gno} | 6 + .../{debug2/1g.gno => debug/closure9_h_0.gno} | 16 +- .../{debug2/1h.gno => debug/closure9_i.gno} | 19 +-- gnovm/tests/debug/{1a.gno => closure9_j.gno} | 1 - .../debug/sort_search_efficiency_simple.gnoa | 25 ---- gnovm/tests/debug2/1.gno | 22 --- gnovm/tests/debug2/1a.gno | 22 --- gnovm/tests/debug2/1b.gno | 22 --- gnovm/tests/debug2/1c.gno | 18 --- gnovm/tests/debug2/1e.gno | 12 -- gnovm/tests/debug2/1f.gno | 53 ------- gnovm/tests/debug2/2.gno | 26 ---- gnovm/tests/debug2/2a.gno | 26 ---- gnovm/tests/debug2/3.gno | 25 ---- gnovm/tests/debug2/3a.gno | 23 --- gnovm/tests/debug2/3b.gno | 27 ---- gnovm/tests/debug2/4.gno | 22 --- gnovm/tests/debug2/4a.gno | 23 --- gnovm/tests/debug2/4b.gno | 24 --- gnovm/tests/debug2/4c.gno | 27 ---- gnovm/tests/debug2/4d.gnoa | 0 gnovm/tests/debug2/4e.gno | 27 ---- gnovm/tests/debug2/4f.gno | 20 --- gnovm/tests/debug2/4g.gno | 14 -- gnovm/tests/debug2/4h.gno | 25 ---- gnovm/tests/debug2/4i.gno | 15 -- gnovm/tests/debug2/5.gno | 22 --- gnovm/tests/debug2/5a.gno | 24 --- gnovm/tests/debug2/5b.gno | 46 ------ gnovm/tests/debug2/5b0.gno | 26 ---- gnovm/tests/debug2/5b1.gno | 26 ---- gnovm/tests/debug2/6a.gno | 24 --- gnovm/tests/debug2/7.gno | 32 ---- gnovm/tests/debug2/7a.gno | 36 ----- gnovm/tests/debug2/closure0.gno | 17 --- gnovm/tests/debug2/closure1.gno | 20 --- gnovm/tests/debug2/closure2.gno | 28 ---- gnovm/tests/debug2/closure3.gno | 23 --- gnovm/tests/debug2/closure4.gno | 23 --- gnovm/tests/debug2/closure5.gno | 23 --- gnovm/tests/debug2/closure6.gno | 23 --- gnovm/tests/debug2/closure7.gno | 28 ---- gnovm/tests/debug2/closure8.gno | 10 -- gnovm/tests/debug2/io2.gno | 21 --- gnovm/tests/debug2/recover4.gno | 25 ---- gnovm/tests/debug2/recover6.gno | 30 ---- gnovm/tests/debug2/recover6a.gno | 40 ----- gnovm/tests/debug2/sort_search_efficiency.gno | 21 --- .../debug2/sort_search_efficiency_simple.gnoa | 25 ---- gnovm/tests/debug2/zregexp_stdlibs.gno | 19 --- gnovm/tests/file.go | 16 +- gnovm/tests/file_test.go | 3 +- gnovm/tests/files/zrealm0.gno | 3 +- gnovm/tests/files/zrealm1.gno | 3 +- gnovm/tests/files/zrealm2.gno | 6 +- gnovm/tests/files/zrealm3.gno | 6 +- gnovm/tests/files/zrealm4.gno | 6 +- gnovm/tests/files/zrealm5.gno | 6 +- gnovm/tests/files/zrealm6.gno | 6 +- gnovm/tests/files/zrealm7.gno | 6 +- gnovm/tests/files/zrealm_avl0.gno | 6 +- gnovm/tests/files/zrealm_avl1.gno | 6 +- gnovm/tests/files/zrealm_natbind0.gno | 9 +- gnovm/tests/files/zrealm_tests0.gno | 45 ++---- 120 files changed, 301 insertions(+), 1486 deletions(-) delete mode 100644 gnovm/tests/debug/4d.gnoa delete mode 100644 gnovm/tests/debug/6.gno rename gnovm/tests/debug/{2.gno => closure10.gno} (100%) rename gnovm/tests/debug/{2a.gno => closure10_a.gno} (100%) rename gnovm/tests/debug/{3.gno => closure11.gno} (100%) rename gnovm/tests/debug/{3a.gno => closure11_a.gno} (100%) rename gnovm/tests/debug/{3b.gno => closure11_b.gno} (100%) rename gnovm/tests/debug/{4.gno => closure12.gno} (100%) rename gnovm/tests/debug/{4a.gno => closure12_a.gno} (100%) rename gnovm/tests/debug/{4b.gno => closure12_b.gno} (100%) rename gnovm/tests/debug/{4c.gno => closure12_c.gno} (100%) rename gnovm/tests/debug/{4e.gno => closure12_e.gno} (100%) rename gnovm/tests/debug/{4f.gno => closure12_f.gno} (100%) rename gnovm/tests/debug/{4g.gno => closure12_g.gno} (100%) rename gnovm/tests/debug/{4h.gno => closure12_h.gno} (100%) rename gnovm/tests/debug/{4i.gno => closure12_i.gno} (100%) rename gnovm/tests/debug/{5.gno => closure13.gno} (100%) rename gnovm/tests/debug/{5a.gno => closure13_a.gno} (100%) rename gnovm/tests/debug/{5b.gno => closure13_b.gno} (97%) rename gnovm/tests/debug/{5b0.gno => closure13_b0.gno} (100%) rename gnovm/tests/debug/{5b1.gno => closure13_b1.gno} (100%) rename gnovm/tests/{debug2/6.gno => debug/closure14.gno} (80%) rename gnovm/tests/debug/{6a.gno => closure14_a.gno} (73%) rename gnovm/tests/debug/{7.gno => closure15.gno} (100%) rename gnovm/tests/debug/{7a.gno => closure15_a.gno} (77%) rename gnovm/tests/debug/{10.gno => closure16.gno} (100%) rename gnovm/tests/debug/{10a.gno => closure16_a.gno} (100%) rename gnovm/tests/debug/{10ab.gno => closure16_a1.gno} (100%) rename gnovm/tests/debug/{10b.gno => closure16_b.gno} (100%) rename gnovm/tests/debug/{10bb.gno => closure16_b1.gno} (100%) rename gnovm/tests/debug/{io2.gno => closure17_io2.gno} (100%) rename gnovm/tests/debug/{recover4.gno => closure17_recover4.gno} (100%) rename gnovm/tests/debug/{recover6.gno => closure17_recover6.gno} (100%) rename gnovm/tests/debug/{recover6a.gno => closure17_recover6a.gno} (100%) rename gnovm/tests/debug/{sort_search_efficiency.gno => closure17_sort_search_efficiency.gno} (100%) rename gnovm/tests/debug/{zregexp_stdlibs.gno => closure17_zregexp_stdlibs.gno} (100%) rename gnovm/tests/debug/{1.gno => closure9.gno} (72%) rename gnovm/tests/{debug2/1d.gno => debug/closure9_a0.gno} (70%) rename gnovm/tests/debug/{01.gno => closure9_a02.gno} (100%) create mode 100644 gnovm/tests/debug/closure9_a1.gno rename gnovm/tests/debug/{1b.gno => closure9_b.gno} (100%) rename gnovm/tests/debug/{1c.gno => closure9_c.gno} (100%) rename gnovm/tests/debug/{1d.gno => closure9_d.gno} (100%) rename gnovm/tests/debug/{1e.gno => closure9_e.gno} (100%) rename gnovm/tests/debug/{1f.gno => closure9_f.gno} (100%) rename gnovm/tests/debug/{1g.gno => closure9_g.gno} (100%) rename gnovm/tests/debug/{1h.gno => closure9_h.gno} (88%) rename gnovm/tests/{debug2/1g.gno => debug/closure9_h_0.gno} (63%) rename gnovm/tests/{debug2/1h.gno => debug/closure9_i.gno} (75%) rename gnovm/tests/debug/{1a.gno => closure9_j.gno} (95%) delete mode 100644 gnovm/tests/debug/sort_search_efficiency_simple.gnoa delete mode 100644 gnovm/tests/debug2/1.gno delete mode 100644 gnovm/tests/debug2/1a.gno delete mode 100644 gnovm/tests/debug2/1b.gno delete mode 100644 gnovm/tests/debug2/1c.gno delete mode 100644 gnovm/tests/debug2/1e.gno delete mode 100644 gnovm/tests/debug2/1f.gno delete mode 100644 gnovm/tests/debug2/2.gno delete mode 100644 gnovm/tests/debug2/2a.gno delete mode 100644 gnovm/tests/debug2/3.gno delete mode 100644 gnovm/tests/debug2/3a.gno delete mode 100644 gnovm/tests/debug2/3b.gno delete mode 100644 gnovm/tests/debug2/4.gno delete mode 100644 gnovm/tests/debug2/4a.gno delete mode 100644 gnovm/tests/debug2/4b.gno delete mode 100644 gnovm/tests/debug2/4c.gno delete mode 100644 gnovm/tests/debug2/4d.gnoa delete mode 100644 gnovm/tests/debug2/4e.gno delete mode 100644 gnovm/tests/debug2/4f.gno delete mode 100644 gnovm/tests/debug2/4g.gno delete mode 100644 gnovm/tests/debug2/4h.gno delete mode 100644 gnovm/tests/debug2/4i.gno delete mode 100644 gnovm/tests/debug2/5.gno delete mode 100644 gnovm/tests/debug2/5a.gno delete mode 100644 gnovm/tests/debug2/5b.gno delete mode 100644 gnovm/tests/debug2/5b0.gno delete mode 100644 gnovm/tests/debug2/5b1.gno delete mode 100644 gnovm/tests/debug2/6a.gno delete mode 100644 gnovm/tests/debug2/7.gno delete mode 100644 gnovm/tests/debug2/7a.gno delete mode 100644 gnovm/tests/debug2/closure0.gno delete mode 100644 gnovm/tests/debug2/closure1.gno delete mode 100644 gnovm/tests/debug2/closure2.gno delete mode 100644 gnovm/tests/debug2/closure3.gno delete mode 100644 gnovm/tests/debug2/closure4.gno delete mode 100644 gnovm/tests/debug2/closure5.gno delete mode 100644 gnovm/tests/debug2/closure6.gno delete mode 100644 gnovm/tests/debug2/closure7.gno delete mode 100644 gnovm/tests/debug2/closure8.gno delete mode 100644 gnovm/tests/debug2/io2.gno delete mode 100644 gnovm/tests/debug2/recover4.gno delete mode 100644 gnovm/tests/debug2/recover6.gno delete mode 100644 gnovm/tests/debug2/recover6a.gno delete mode 100644 gnovm/tests/debug2/sort_search_efficiency.gno delete mode 100644 gnovm/tests/debug2/sort_search_efficiency_simple.gnoa delete mode 100644 gnovm/tests/debug2/zregexp_stdlibs.gno diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go index 66390a84de0..45cb7063043 100644 --- a/gnovm/pkg/gnolang/machine.go +++ b/gnovm/pkg/gnolang/machine.go @@ -718,7 +718,7 @@ func (m *Machine) Eval(x Expr) []TypedValue { // static types and values. func (m *Machine) EvalStatic(last BlockNode, x Expr) TypedValue { if debug { - //m.Printf("Machine.EvalStatic(%v, %v)\n", last, x) + // m.Printf("Machine.EvalStatic(%v, %v)\n", last, x) } // X must have been preprocessed. if x.GetAttribute(ATTR_PREPROCESSED) == nil { @@ -747,7 +747,7 @@ func (m *Machine) EvalStatic(last BlockNode, x Expr) TypedValue { // static types of nodes. func (m *Machine) EvalStaticTypeOf(last BlockNode, x Expr) Type { if debug { - //m.Printf("Machine.EvalStaticTypeOf(%v, %v)\n", last, x) + // m.Printf("Machine.EvalStaticTypeOf(%v, %v)\n", last, x) } // X must have been preprocessed. if x.GetAttribute(ATTR_PREPROCESSED) == nil { @@ -905,7 +905,6 @@ const ( OpStructLit Op = 0x50 // X{...} OpFuncLit Op = 0x51 // func(T){Body} OpConvert Op = 0x52 // Y(X) - OpPreFuncLit Op = 0x53 // /* Native operators */ OpArrayLitGoNative Op = 0x60 @@ -1041,7 +1040,6 @@ const ( OpCPUMapLit = 1 OpCPUStructLit = 1 OpCPUFuncLit = 1 - OpCPUPreFuncLit = 1 OpCPUConvert = 1 /* Native operators */ @@ -1637,7 +1635,7 @@ func (m *Machine) ReapValues(start int) []TypedValue { func (m *Machine) PushBlock(b *Block) { if debug { - //m.Println("+B") + // m.Println("+B") m.Printf("+B: %v \n", b) } m.Blocks = append(m.Blocks, b) diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index 8065e1e713a..1b948e697d7 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -886,7 +886,7 @@ type bodyStmt struct { NumStmts int // number of Stmts, for goto Cond Expr // for ForStmt Post Stmt // for ForStmt - Bag *TimeSeriesBag // a series of values of captured vars + LoopValuesBox *LoopValuesBox // a series of transient values of captured var generated as the iteration goes on isLoop bool Active Stmt // for PopStmt() Key Expr // for RangeStmt @@ -1515,7 +1515,7 @@ func (sb *StaticBlock) String() { fmt.Printf("sb objectInfo %v \n", sb.GetObjectInfo()) fmt.Printf("sb isEscaped %v \n", sb.GetIsEscaped()) fmt.Printf("sb isTransient %v \n", sb.GetIsTransient()) - //fmt.Printf("sb getOwnder, getOwnerID %v \n", sb.GetOwner(), sb.GetOwnerID()) + // fmt.Printf("sb getOwnder, getOwnerID %v \n", sb.GetOwner(), sb.GetOwnerID()) fmt.Println("=================block==================") fmt.Printf("block: %v \n", sb.Block) fmt.Println("==================end===================") @@ -1622,7 +1622,7 @@ func (sb *StaticBlock) GetParentNode(store Store) BlockNode { // Implements BlockNode. // As a side effect, notes externally defined names. func (sb *StaticBlock) GetPathForName(store Store, n Name) ValuePath { - //debug.Printf("-----GetPathForName: %v \n", n) + // debug.Printf("-----GetPathForName: %v \n", n) if n == "_" { return NewValuePathBlock(0, 0, "_") } @@ -1736,7 +1736,7 @@ func (sb *StaticBlock) GetStaticTypeOfAt(store Store, path ValuePath) Type { // Implements BlockNode. func (sb *StaticBlock) GetLocalIndex(n Name) (uint16, bool) { - //debug.Printf("GetLocalIndex: %v \n", n) + // debug.Printf("GetLocalIndex: %v \n", n) for i, name := range sb.Names { if name == n { if debug { diff --git a/gnovm/pkg/gnolang/op_assign.go b/gnovm/pkg/gnolang/op_assign.go index 5ce75ee8dcd..54acb0b2abb 100644 --- a/gnovm/pkg/gnolang/op_assign.go +++ b/gnovm/pkg/gnolang/op_assign.go @@ -7,7 +7,7 @@ func (m *Machine) doOpDefine() { // Define each value evaluated for Lhs. // NOTE: PopValues() returns a slice in // forward order, not the usual reverse. - //m.PopValue() + // m.PopValue() rvs := m.PopValues(len(s.Lhs)) debug.Printf("s:%v \n", s) debug.Printf("lhs:%v \n", s.Lhs) diff --git a/gnovm/pkg/gnolang/op_call.go b/gnovm/pkg/gnolang/op_call.go index 73900d93095..6115aef1ca4 100644 --- a/gnovm/pkg/gnolang/op_call.go +++ b/gnovm/pkg/gnolang/op_call.go @@ -65,26 +65,21 @@ func (m *Machine) doOpCall() { clo := fr.Func.GetClosure(m.Store) debugPP.Printf("-----got closure: %v ----- \n", clo) // update block vars using captured vars - captures := fr.Func.Captures - if captures == nil { - debugPP.Println("nil captures") - } var targetB *Block debugPP.Println("================parse transient for fv====================") - if fv.Tss != nil { - for i, loopData := range fv.Tss { // each bag is for a certain level of block - bag := loopData.bag - debugPP.Printf("Level[%d] bag is : %v \n", i, bag) + if fv.TransientLoopData != nil { + for i, loopData := range fv.TransientLoopData { // each LoopValuesBox is for a certain level of block + bag := loopData.loopValuesBox + debugPP.Printf("Level[%d] LoopValuesBox is : %v \n", i, bag) if bag != nil { if bag.isSealed { if bag != nil && !bag.isEmpty() { // NOTE: for some reasons won't pack value for j, t := range bag.transient { // unpack vars belong to a specific block if t != nil { - debugPP.Printf("Bag.transient[%d] name is %v, path: %v, len of values is: %v \n", j, t.nx.Name, t.nx.Path, len(t.values)) + debugPP.Printf("LoopValuesBox.transient[%d] name is %v, path: %v, len of values is: %v \n", j, t.nx.Name, t.nx.Path, len(t.values)) for k, v := range t.values { debugPP.Printf("values[%d] is %v \n", k, v) } - targetB = clo.GetBlockWithDepth(m.Store, t.nx.Path) // find target block using depth // check if exists, should always be? names := targetB.GetSource(m.Store).GetBlockNames() @@ -100,33 +95,21 @@ func (m *Machine) doOpCall() { } if match { debugPP.Println("===match, cursor is:", loopData.index) - + // update values in context with previously captured. targetB.UpdateValue(index, t.values[loopData.index]) - //nvs := t.values[1:] - //bag.transient[j].values = nvs - //if len(bag.transient[j].values) == 0 { // loop end - // bag.transient[j] = nil // empty a name and value - // end = true - //} } } } } - } else { // not sealed will be tainted + } else { // not sealed will be tainted, indicates it's not a closure. debugPP.Println("---taint this var") - fv.Tss[i].bag.isTainted = true + fv.TransientLoopData[i].loopValuesBox.isTainted = true } } } - //if end { - fv.Tss = nil - //} + fv.TransientLoopData = nil } - // only need initial snapshot - fr.Func.Captures = nil - //fr.Func.Captures = Captured{} - b := m.Alloc.NewBlock(fr.Func.GetSource(m.Store), clo) m.PushBlock(b) if fv.nativeBody == nil && fv.NativePkg != "" { diff --git a/gnovm/pkg/gnolang/op_eval.go b/gnovm/pkg/gnolang/op_eval.go index fe03645a735..617f8cd15b5 100644 --- a/gnovm/pkg/gnolang/op_eval.go +++ b/gnovm/pkg/gnolang/op_eval.go @@ -312,8 +312,6 @@ func (m *Machine) doOpEval() { b := m.LastBlock() debugPP.Printf("b: %v \n", b) m.PushOp(OpFuncLit) - //m.PushOp(OpPreFuncLit) - // evaluate func type m.PushExpr(&x.Type) m.PushOp(OpEval) case *ConstExpr: diff --git a/gnovm/pkg/gnolang/op_exec.go b/gnovm/pkg/gnolang/op_exec.go index 746382b8c30..618c68f1a94 100644 --- a/gnovm/pkg/gnolang/op_exec.go +++ b/gnovm/pkg/gnolang/op_exec.go @@ -47,13 +47,12 @@ func updateCapturedValue(m *Machine, lb *Block) { if lb.GetBodyStmt().isLoop { // do we need this? bs := lb.GetBodyStmt() debugPP.Printf("---------isLoop, current block is: %v \n", lb) - debugPP.Printf("---------bag, %v \n", bs.Bag) - if bs.Bag != nil && bs.Bag.isFilled && !bs.Bag.isTainted { - debugPP.Printf("---addr of bs.Bag is: %p \n", &bs.Bag) + debugPP.Printf("---------LoopValuesBox, %v \n", bs.LoopValuesBox) + if bs.LoopValuesBox != nil && bs.LoopValuesBox.isFilled && !bs.LoopValuesBox.isTainted { names := lb.Source.GetBlockNames() var found bool var isSealed bool - for i, tt := range bs.Bag.transient { + for i, tt := range bs.LoopValuesBox.transient { if tt != nil { debugPP.Printf("transient[%d] name is %v, path: %v \n", i, tt.nx.Name, tt.nx.Path) // first check if name is in current block, it not, stop @@ -69,22 +68,15 @@ func updateCapturedValue(m *Machine, lb *Block) { tv := ptr.Deref() debugPP.Printf("--- new tv : %v \n", tv) // set back - debugPP.Printf("before update, len of Bag values is : %d \n", len(bs.Bag.transient[i].values)) - - //expandRatio := bs.Bag.transient[i].expandRatio - expandRatio := bs.Bag.transient[i].cursor + 1 - len(bs.Bag.transient[i].values) // 2 - 0 + debugPP.Printf("before update, len of LoopValuesBox values is : %d \n", len(bs.LoopValuesBox.transient[i].values)) + // inner loops iterates twice while outer loop iterates once. + // it's an alignment for the values of out loop block. + expandRatio := bs.LoopValuesBox.transient[i].cursor + 1 - len(bs.LoopValuesBox.transient[i].values) // 2 - 0 debugPP.Printf("--- expand ratio is: %d \n", expandRatio) for j := 0; j < expandRatio; j++ { - bs.Bag.transient[i].values = append(bs.Bag.transient[i].values, tv) + bs.LoopValuesBox.transient[i].values = append(bs.LoopValuesBox.transient[i].values, tv) } - debugPP.Printf("after update, len of Bag values is : %d \n", len(bs.Bag.transient[i].values)) - - // update higher level if it is also a loop, padding. - //upperBlock := findNearestLoopBlock(m.Store, lb) - //debugPP.Printf("upperBlock is: %v \n", upperBlock) - //if upperBlock != nil && upperBlock.GetBodyStmt().Bag != nil { - // upperBlock.GetBodyStmt().Bag.setRatio(int8(len(bs.Bag.transient[i].values))) - //} + debugPP.Printf("after update, len of LoopValuesBox values is : %d \n", len(bs.LoopValuesBox.transient[i].values)) isSealed = true } else { debugPP.Printf("---not found %s in current block, b: %v \n", tt.nx.Name, lb) @@ -93,7 +85,7 @@ func updateCapturedValue(m *Machine, lb *Block) { } } if isSealed { - bs.Bag.isSealed = true // seal for this bag of this block + bs.LoopValuesBox.isSealed = true // seal for this LoopValuesBox of this block } } } @@ -111,7 +103,7 @@ func (m *Machine) doOpExec(op Op) { if debug { debug.Printf("PEEK STMT: %v\n", s) debug.Printf("op: %v\n", op) - //debug.Printf("%v\n", m) + // debug.Printf("%v\n", m) } // NOTE this could go in the switch statement, and we could @@ -508,10 +500,10 @@ EXEC_SWITCH: switch cs.Op { case ASSIGN: // post assign, use value of lhs to update captured value, name as ID - //m.PushOp(OpPostAssign) + // m.PushOp(OpPostAssign) m.PushOp(OpAssign) // pre assign, check rhs is funcLitExpr - //m.PushOp(OpPreAssign) + // m.PushOp(OpPreAssign) case ADD_ASSIGN: m.PushOp(OpAddAssign) case SUB_ASSIGN: @@ -549,7 +541,7 @@ EXEC_SWITCH: m.PushExpr(rx) m.PushOp(OpEval) } - //m.PushOp(OpPreAssign) + // m.PushOp(OpPreAssign) if cs.Op != DEFINE { // For each Lhs, push eval operation if needed. diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index d8c25dd225b..7126ced668c 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -680,31 +680,6 @@ func (m *Machine) doOpStructLit() { }) } -func isZeroArray(arr [8]byte) bool { - for _, v := range arr { - if v != 0 { - return false - } - } - return true -} - -func findNearestLoopBlock(store Store, b *Block) *Block { - nb := b.GetParent(store) - for { - if nb != nil { - if nb.GetBodyStmt().isLoop { - return nb - } else { - nb = nb.GetParent(store) - continue - } - } else { - return nil - } - } -} - // find nearest block is loop and contains name of n func findLoopBlockWithPath(store Store, b *Block, nx *NameExpr) (*Block, bool, uint8) { var gen uint8 = 1 @@ -739,7 +714,7 @@ func (m *Machine) doOpFuncLit() { if vp.Depth == 0 { continue } - vp.Depth -= 1 // from funcLit block not inside + vp.Depth -= 1 // from funcLit block nx := &NameExpr{ Name: n, Path: vp, @@ -754,22 +729,23 @@ func (m *Machine) doOpFuncLit() { sort.Slice(captures, sortDepth) debugPP.Println("---done sort") - var isLoopBlock bool - var loopBlock *Block - var bag *TimeSeriesBag - - var lastGen, gen uint8 - var isReuse bool - var Tss []*LoopData - //var Tss *LoopData + var ( + isLoopBlock bool + loopBlock *Block + lvBox *LoopValuesBox + lastGen, gen uint8 + isReuse bool + loopData []*LoopBlockData + ) + // var TransientLoopData *TransientLoopData // start search every captured var in target block via path - // 1. Tss is a slice of bag, one bag is for one loop block. - // 2. new a `bag` for every block, the bag has a flag of isFilled for reuse, + // 1. TransientLoopData is a slice of LoopValuesBox, one LoopValuesBox is for one loop block. + // 2. new a `LoopValuesBox` for every block, the LoopValuesBox has a flag of isFilled for reuse, // and a []transient for vars in on loop block, that each transient contains an // nameExpr, and a slice of values that is value of var in specific time slice. - // Tss -> []bag -> isFilled, []transient -> nx:nameExpr, values:[]TypedValue. - // XXX, Tss if for fv, which is the concrete entity of closure. - // in case for i:=0, i++; i<2{ x := i }, Tss will have one bag for the only loop block, + // TransientLoopData -> []LoopValuesBox -> isFilled, []transient -> nx:nameExpr, values:[]TypedValue. + // XXX, TransientLoopData if for fv, which is the concrete entity of closure. + // in case for i:=0, i++; i<2{ x := i }, TransientLoopData will have one LoopValuesBox for the only loop block, // and one nx for x, and 2 values of [0,1], fv will have 2 replica in this case, and they // share the same loopBlock. // there are some more complex situation like: @@ -778,8 +754,8 @@ func (m *Machine) doOpFuncLit() { // left 2 is the inner blocks, since as the outer for loop execute, will yield due num of // sub blocks. // an intuitive state for state of x, y is: [0,0], [0,1], [1,0], [1,1]. - // in the impl, the outer block has a bag of [0,0,1,1], and inner block 1, has a bag [0,1] - // and inner block 2 has a bag[0,1]. + // in the impl, the outer block has a LoopValuesBox of [0,0,1,1], and inner block 1, has a LoopValuesBox [0,1] + // and inner block 2 has a LoopValuesBox[0,1]. for i, nx := range captures { debugPP.Printf("captures[%d] is : %v \n", i, nx.Name) // start search block, in the order of small depth to big depth @@ -787,53 +763,50 @@ func (m *Machine) doOpFuncLit() { debugPP.Printf("loopBlock: %v, isLoopBlock: %v, gen: %v, cursor: %v \n", loopBlock, isLoopBlock, gen) if lastGen == 0 { lastGen = gen - } else if gen != lastGen { // if enter new level of block, means last block is all done, pack bag + } else if gen != lastGen { // if enter new level of block, means last block is all done, pack LoopValuesBox lastGen = gen // set last state - if bag != nil && !isReuse { // has something to pack - bag.isFilled = true - Tss = append(Tss, &LoopData{index: 0, bag: bag}) // pack per level of block - //Tss = append(Tss, bag) + if lvBox != nil && !isReuse { // has something to pack + lvBox.isFilled = true + loopData = append(loopData, &LoopBlockData{index: 0, loopValuesBox: lvBox}) // pack per level of block } - debugPP.Printf("========packed bag for %v is: %s \n", loopBlock, bag) + debugPP.Printf("========packed LoopValuesBox for %v is: %s \n", loopBlock, lvBox) } if isLoopBlock { - bag = loopBlock.GetBodyStmt().Bag // get bag from specific block, that is shared across current level of for/range loop - debugPP.Printf("got initial bag from target loop block: %v \n", bag) - if bag == nil { - bag = &TimeSeriesBag{} // init + lvBox = loopBlock.GetBodyStmt().LoopValuesBox // get LoopValuesBox from specific block, that is shared across current level of for/range loop + debugPP.Printf("got initial LoopValuesBox from target loop block: %v \n", lvBox) + if lvBox == nil { + lvBox = &LoopValuesBox{} // init } - if !bag.isFilled { // only once + if !lvBox.isFilled { // only once for further reuse debugPP.Println("---not sealed---, should pack only once for n: ", nx.Name) tst := &Transient{ - nx: nx, - expandRatio: 1, // default 1 - cursor: 0, + nx: nx, + cursor: 0, } - bag.transient = append(bag.transient, tst) + lvBox.transient = append(lvBox.transient, tst) // set back to loop block properly - loopBlock.GetBodyStmt().Bag = bag + loopBlock.GetBodyStmt().LoopValuesBox = lvBox } else { // repack when iterates, this should be optimized isReuse = true // use isFilled instead // get cursor by name - debugPP.Println("---reuse, current cursor is: ", bag.transient[bag.getIndexByName(nx.Name)].cursor) - bag.transient[bag.getIndexByName(nx.Name)].cursor++ // only inc in reuse, that is iteration - //Tss = append(Tss, bag) - // cursor indicates num of iterations of fv - Tss = append(Tss, &LoopData{index: bag.transient[bag.getIndexByName(nx.Name)].cursor, bag: bag}) + debugPP.Println("---reuse, current cursor is: ", lvBox.transient[lvBox.getIndexByName(nx.Name)].cursor) + lvBox.transient[lvBox.getIndexByName(nx.Name)].cursor++ // only inc in reuse, that is iteration + // cursor indicates num of iterations of fv + loopData = append(loopData, &LoopBlockData{index: lvBox.transient[lvBox.getIndexByName(nx.Name)].cursor, loopValuesBox: lvBox}) } } } - // tail set - if bag != nil && !bag.isFilled && !isReuse { + // set fill flag + if lvBox != nil && !lvBox.isFilled && !isReuse { debugPP.Println("---tail pack") - bag.isFilled = true // set for the last bag - Tss = append(Tss, &LoopData{index: 0, bag: bag}) // collect the last bag - debugPP.Printf("========packed bag for %v is: %s \n", loopBlock, bag) + lvBox.isFilled = true // set for the last LoopValuesBox + loopData = append(loopData, &LoopBlockData{index: 0, loopValuesBox: lvBox}) // collect the last LoopValuesBox + debugPP.Printf("========packed LoopValuesBox for %v is: %s \n", loopBlock, lvBox) } - for i, ts := range Tss { - debugPP.Printf("========Tss[%d] is: %s, addr: %p, index: %d \n", i, ts.bag, &ts.bag, ts.index) + for i, ts := range loopData { + debugPP.Printf("========TransientLoopData[%d] is: %s, addr: %p, index: %d \n", i, ts.loopValuesBox, &ts.loopValuesBox, ts.index) } ft := m.PopValue().V.(TypeValue).Type.(*FuncType) @@ -841,25 +814,21 @@ func (m *Machine) doOpFuncLit() { m.Alloc.AllocateFunc() fv := &FuncValue{ - Type: ft, - IsMethod: false, - Source: x, - Name: "", - Closure: lb, - Tss: Tss, - PkgPath: m.Package.PkgPath, - body: x.Body, - nativeBody: nil, + Type: ft, + IsMethod: false, + Source: x, + Name: "", + Closure: lb, + TransientLoopData: loopData, + PkgPath: m.Package.PkgPath, + body: x.Body, + nativeBody: nil, } - //debugPP.Printf("fv is:------") - //fv.dump() - //debugPP.Printf("fv.captures is: %v \n", fv.Captures) - //debugPP.Printf("fv.address is: %p \n", fv) - m.PushValue(TypedValue{ T: ft, - V: fv}) + V: fv, + }) } func (m *Machine) doOpConvert() { diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 6f528c133b2..0b2162d5199 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -275,9 +275,9 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { case *BlockStmt: pushInitBlock(n, &last, &stack) debug.Println("blockStmt push closure") - //pushClosure(&Closure{}) - //debug.Println("blockStmt pop closure") - //popClosure() + // pushClosure(&Closure{}) + // debug.Println("blockStmt pop closure") + // popClosure() // TRANS_BLOCK ----------------------- case *ForStmt: @@ -296,7 +296,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { case *IfCaseStmt: debug.Println("-----IfCaseStmt-----") pushRealBlock(n, &last, &stack) - //pushClosure(&Closure{}) + // pushClosure(&Closure{}) // parent if statement. ifs := ns[len(ns)-1].(*IfStmt) // anything declared in ifs are copied. @@ -305,7 +305,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { debug.Printf("tv : %v, *tv: %v \n", tv, *tv) last.Define(n, *tv) } - //popClosure() + // popClosure() // TRANS_BLOCK ----------------------- case *RangeStmt: @@ -374,8 +374,8 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // push func body block. pushInitBlock(n, &last, &stack) - //pushClosure(&Closure{}) - //pushFxs(n) + // pushClosure(&Closure{}) + // pushFxs(n) // define parameters in new block. for _, p := range ft.Params { debug.Println("---PP define params") @@ -430,7 +430,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // TRANS_BLOCK ----------------------- case *SwitchClauseStmt: pushRealBlock(n, &last, &stack) - //pushClosure(&Closure{}) + // pushClosure(&Closure{}) // parent switch statement. ss := ns[len(ns)-1].(*SwitchStmt) // anything declared in ss are copied, @@ -493,7 +493,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { n.Cases[i] = cx } } - //popClosure() + // popClosure() // TRANS_BLOCK ----------------------- case *FuncDecl: @@ -773,8 +773,8 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } } } - //popClosure() - //popFx() + // popClosure() + // popFx() // TRANS_LEAVE ----------------------- case *BasicLitExpr: @@ -1998,7 +1998,7 @@ func pushInitBlock(bn BlockNode, last *BlockNode, stack *[]BlockNode) { if bn.GetStaticBlock().Source != bn { panic("expected the source of a block node to be itself") } - //bn.GetStaticBlock().String() + // bn.GetStaticBlock().String() *last = bn *stack = append(*stack, bn) } diff --git a/gnovm/pkg/gnolang/transcribe.go b/gnovm/pkg/gnolang/transcribe.go index a5548b23253..a987e0c82ca 100644 --- a/gnovm/pkg/gnolang/transcribe.go +++ b/gnovm/pkg/gnolang/transcribe.go @@ -446,7 +446,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } case *BranchStmt: case *DeclStmt: - //CX.pushOp(ASSIGN) + // CX.pushOp(ASSIGN) for idx := range cnn.Body { cnn.Body[idx] = transcribe(t, nns, TRANS_DECL_BODY, idx, cnn.Body[idx], &c).(SimpleDeclStmt) if isBreak(c) { @@ -455,7 +455,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } } - //CX.popOp() + // CX.popOp() case *DeferStmt: cnn.Call = *transcribe(t, nns, TRANS_DEFER_CALL, 0, &cnn.Call, &c).(*CallExpr) if isStopOrSkip(nc, c) { @@ -550,7 +550,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*IfCaseStmt) } - //pushClosure(&Closure{}) + // pushClosure(&Closure{}) for idx := range cnn.Body { cnn.Body[idx] = transcribe(t, nns, TRANS_IF_CASE_BODY, idx, cnn.Body[idx], &c).(Stmt) if isBreak(c) { @@ -559,7 +559,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } } - //debug.Println("if-case pop c-----") + // debug.Println("if-case pop c-----") case *IncDecStmt: cnn.X = transcribe(t, nns, TRANS_INCDEC_X, 0, cnn.X, &c).(Expr) diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index 45f24d19339..36f4a300b84 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -515,14 +515,62 @@ func (sv *StructValue) Copy(alloc *Allocator) *StructValue { return alloc.NewStruct(fields) } -type TimeSeriesBag struct { +// ---------------------------------------- +// FuncValue + +// FuncValue.Type stores the method signature from the +// declaration, and has exact parameter/result names declared, +// whereas the TypedValue.T that contains at .V may not. (i.e. +// TypedValue.T doesn't care about parameter/result names, but +// the *FuncValue requires this for execution. +// In leu of FuncValue.Type, we could refer to FuncValue.Source +// or create a different field with param/result names, but +// *FuncType is already a suitable structure, and re-using +// makes construction TypedValue{T:*FuncType{},V:*FuncValue{}} +// faster. +type FuncValue struct { + Type Type // includes unbound receiver(s) + IsMethod bool // is an (unbound) method + Source BlockNode // for block mem allocation + Name Name // name of function/method + Closure Value // *Block or RefValue to closure (may be nil for file blocks; lazy) + TransientLoopData []*LoopBlockData + FileName Name // file name where declared + PkgPath string + NativePkg string // for native bindings through NativeStore + NativeName Name // not redundant with Name; this cannot be changed in userspace + + body []Stmt // function body + nativeBody func(*Machine) // alternative to Body +} + +// LoopBlockData is a wrap of LoopValuesBox, index indicates iteration number of fv. +// there are n replicas of fv generated as the iteration goes on, we have to identify +// which replica is executing thus find proper value in the box to update context(replay). +type LoopBlockData struct { // every fv points to n loopData + index int + loopValuesBox *LoopValuesBox // per loop block +} + +// Transient record all dynamic value for captured variable as iteration goes on. +// e.g. for i:=0;i<2;i++{func(){println(i)}, i will have a [0,1] values. +type Transient struct { + nx *NameExpr + values []TypedValue // one name with multi snapshot + cursor int // cursor per fv to find value in time series +} + +// LoopValuesBox stores a slice of transient values of captured vars dynamically +// generated as the iterations goes on. +// it is used for a closure execution. +type LoopValuesBox struct { isFilled bool isSealed bool isTainted bool transient []*Transient } -func (tsb *TimeSeriesBag) getIndexByName(n Name) int { +func (tsb *LoopValuesBox) getIndexByName(n Name) int { for i, tt := range tsb.transient { if n == tt.nx.Name { return i @@ -531,7 +579,7 @@ func (tsb *TimeSeriesBag) getIndexByName(n Name) int { return -1 // never reach } -func (tsb *TimeSeriesBag) isEmpty() bool { +func (tsb *LoopValuesBox) isEmpty() bool { for _, tt := range tsb.transient { if tt != nil && tt.values != nil && len(tt.values) != 0 { return false @@ -540,18 +588,10 @@ func (tsb *TimeSeriesBag) isEmpty() bool { return true } -func (tsb *TimeSeriesBag) setRatio(r int8) { - for _, tt := range tsb.transient { - if tt != nil { - tt.expandRatio = r - } - } -} - -func (tsb *TimeSeriesBag) String() string { +func (tsb *LoopValuesBox) String() string { var s string s += "\n" - s += "==================time bag===================\n" + s += "==================time LoopValuesBox===================\n" s += fmt.Sprintf("isFilled: %v \n", tsb.isFilled) for i, t := range tsb.transient { s += fmt.Sprintf("nx[%d]: %v \n", i, t.nx) @@ -564,72 +604,6 @@ func (tsb *TimeSeriesBag) String() string { return s } -type Transient struct { - nx *NameExpr - expandRatio int8 // TODO: determine proper type - values []TypedValue // one name with multi snapshot - cursor int // cursor per fv to find value in time series -} - -type Captured struct { - names []Name - values []TypedValue -} - -func (c *Captured) String() { - var s string - s += "\n" - for i, n := range c.names { - s += fmt.Sprintf("name[%d] is: %s: \n", i, n) - } - for i, v := range c.values { - s += fmt.Sprintf("value[%d] is: %v: \n", i, v) - } - s += "\n" - fmt.Println(s) -} - -// ---------------------------------------- -// FuncValue - -// FuncValue.Type stores the method signature from the -// declaration, and has exact parameter/result names declared, -// whereas the TypedValue.T that contains at .V may not. (i.e. -// TypedValue.T doesn't care about parameter/result names, but -// the *FuncValue requires this for execution. -// In leu of FuncValue.Type, we could refer to FuncValue.Source -// or create a different field with param/result names, but -// *FuncType is already a suitable structure, and re-using -// makes construction TypedValue{T:*FuncType{},V:*FuncValue{}} -// faster. -type FuncValue struct { - Type Type // includes unbound receiver(s) - IsMethod bool // is an (unbound) method - Source BlockNode // for block mem allocation - Name Name // name of function/method - Closure Value // *Block or RefValue to closure (may be nil for file blocks; lazy) - Captures *Captured - Tss []*LoopData - FileName Name // file name where declared - PkgPath string - NativePkg string // for native bindings through NativeStore - NativeName Name // not redundant with Name; this cannot be changed in userspace - - body []Stmt // function body - nativeBody func(*Machine) // alternative to Body -} - -type LoopData struct { - index int // index for fv to look for var - bag *TimeSeriesBag // per loop block -} - -func (fv *FuncValue) dump() { - if debugPP { - fmt.Printf("funcValue, name: %s, captures: %s \n", fv.Name, fv.Captures) - } -} - func (fv *FuncValue) IsNative() bool { if fv.NativePkg == "" && fv.NativeName == "" { return false @@ -2365,7 +2339,7 @@ func (b *Block) Hash() string { func (b *Block) UpdateValue(index int, tv TypedValue) { debug.Printf("-----UpdateValue, index: %d, tv: %v \n", index, tv) debug.Printf("b before update: %v \n", b) - for i, _ := range b.Values { + for i := range b.Values { if i == index { b.Values[i] = tv } diff --git a/gnovm/tests/debug/4d.gnoa b/gnovm/tests/debug/4d.gnoa deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/gnovm/tests/debug/6.gno b/gnovm/tests/debug/6.gno deleted file mode 100644 index d4823d55c2a..00000000000 --- a/gnovm/tests/debug/6.gno +++ /dev/null @@ -1,23 +0,0 @@ -package main - -import "fmt" - -func foo() (err error) { - defer func() { - if r := recover(); r != nil { - switch v := r.(type) { - case error: - err = v - default: - err = fmt.Errorf("%s", v) - } - } - }() - return -} - -func main() { - foo() -} - -// Output: diff --git a/gnovm/tests/debug/2.gno b/gnovm/tests/debug/closure10.gno similarity index 100% rename from gnovm/tests/debug/2.gno rename to gnovm/tests/debug/closure10.gno diff --git a/gnovm/tests/debug/2a.gno b/gnovm/tests/debug/closure10_a.gno similarity index 100% rename from gnovm/tests/debug/2a.gno rename to gnovm/tests/debug/closure10_a.gno diff --git a/gnovm/tests/debug/3.gno b/gnovm/tests/debug/closure11.gno similarity index 100% rename from gnovm/tests/debug/3.gno rename to gnovm/tests/debug/closure11.gno diff --git a/gnovm/tests/debug/3a.gno b/gnovm/tests/debug/closure11_a.gno similarity index 100% rename from gnovm/tests/debug/3a.gno rename to gnovm/tests/debug/closure11_a.gno diff --git a/gnovm/tests/debug/3b.gno b/gnovm/tests/debug/closure11_b.gno similarity index 100% rename from gnovm/tests/debug/3b.gno rename to gnovm/tests/debug/closure11_b.gno diff --git a/gnovm/tests/debug/4.gno b/gnovm/tests/debug/closure12.gno similarity index 100% rename from gnovm/tests/debug/4.gno rename to gnovm/tests/debug/closure12.gno diff --git a/gnovm/tests/debug/4a.gno b/gnovm/tests/debug/closure12_a.gno similarity index 100% rename from gnovm/tests/debug/4a.gno rename to gnovm/tests/debug/closure12_a.gno diff --git a/gnovm/tests/debug/4b.gno b/gnovm/tests/debug/closure12_b.gno similarity index 100% rename from gnovm/tests/debug/4b.gno rename to gnovm/tests/debug/closure12_b.gno diff --git a/gnovm/tests/debug/4c.gno b/gnovm/tests/debug/closure12_c.gno similarity index 100% rename from gnovm/tests/debug/4c.gno rename to gnovm/tests/debug/closure12_c.gno diff --git a/gnovm/tests/debug/4e.gno b/gnovm/tests/debug/closure12_e.gno similarity index 100% rename from gnovm/tests/debug/4e.gno rename to gnovm/tests/debug/closure12_e.gno diff --git a/gnovm/tests/debug/4f.gno b/gnovm/tests/debug/closure12_f.gno similarity index 100% rename from gnovm/tests/debug/4f.gno rename to gnovm/tests/debug/closure12_f.gno diff --git a/gnovm/tests/debug/4g.gno b/gnovm/tests/debug/closure12_g.gno similarity index 100% rename from gnovm/tests/debug/4g.gno rename to gnovm/tests/debug/closure12_g.gno diff --git a/gnovm/tests/debug/4h.gno b/gnovm/tests/debug/closure12_h.gno similarity index 100% rename from gnovm/tests/debug/4h.gno rename to gnovm/tests/debug/closure12_h.gno diff --git a/gnovm/tests/debug/4i.gno b/gnovm/tests/debug/closure12_i.gno similarity index 100% rename from gnovm/tests/debug/4i.gno rename to gnovm/tests/debug/closure12_i.gno diff --git a/gnovm/tests/debug/5.gno b/gnovm/tests/debug/closure13.gno similarity index 100% rename from gnovm/tests/debug/5.gno rename to gnovm/tests/debug/closure13.gno diff --git a/gnovm/tests/debug/5a.gno b/gnovm/tests/debug/closure13_a.gno similarity index 100% rename from gnovm/tests/debug/5a.gno rename to gnovm/tests/debug/closure13_a.gno diff --git a/gnovm/tests/debug/5b.gno b/gnovm/tests/debug/closure13_b.gno similarity index 97% rename from gnovm/tests/debug/5b.gno rename to gnovm/tests/debug/closure13_b.gno index 5f54cc2f95d..7d9d935652c 100644 --- a/gnovm/tests/debug/5b.gno +++ b/gnovm/tests/debug/closure13_b.gno @@ -40,7 +40,8 @@ func main() { withFooBar(func() { expectRead(5, "foo ", nil) }) - + println("ok") } // Output: +// ok diff --git a/gnovm/tests/debug/5b0.gno b/gnovm/tests/debug/closure13_b0.gno similarity index 100% rename from gnovm/tests/debug/5b0.gno rename to gnovm/tests/debug/closure13_b0.gno diff --git a/gnovm/tests/debug/5b1.gno b/gnovm/tests/debug/closure13_b1.gno similarity index 100% rename from gnovm/tests/debug/5b1.gno rename to gnovm/tests/debug/closure13_b1.gno diff --git a/gnovm/tests/debug2/6.gno b/gnovm/tests/debug/closure14.gno similarity index 80% rename from gnovm/tests/debug2/6.gno rename to gnovm/tests/debug/closure14.gno index d4823d55c2a..e6b89f9528a 100644 --- a/gnovm/tests/debug2/6.gno +++ b/gnovm/tests/debug/closure14.gno @@ -13,11 +13,14 @@ func foo() (err error) { } } }() - return + + panic("xxx") } func main() { - foo() + err := foo() + println(err.Error()) } // Output: +// xxx diff --git a/gnovm/tests/debug/6a.gno b/gnovm/tests/debug/closure14_a.gno similarity index 73% rename from gnovm/tests/debug/6a.gno rename to gnovm/tests/debug/closure14_a.gno index 1acfa3a1f2c..706e2bd7e8b 100644 --- a/gnovm/tests/debug/6a.gno +++ b/gnovm/tests/debug/closure14_a.gno @@ -10,15 +10,18 @@ func foo() (err error) { if r := recover(); r != nil { switch y { case 1: - err = errors.New("xxx") + err = errors.New("ok") default: err = nil } } }() - return + panic(y) } func main() { - foo() + println(foo()) } + +// Output: +// ok diff --git a/gnovm/tests/debug/7.gno b/gnovm/tests/debug/closure15.gno similarity index 100% rename from gnovm/tests/debug/7.gno rename to gnovm/tests/debug/closure15.gno diff --git a/gnovm/tests/debug/7a.gno b/gnovm/tests/debug/closure15_a.gno similarity index 77% rename from gnovm/tests/debug/7a.gno rename to gnovm/tests/debug/closure15_a.gno index edb62312a29..1983845bd81 100644 --- a/gnovm/tests/debug/7a.gno +++ b/gnovm/tests/debug/closure15_a.gno @@ -24,9 +24,19 @@ func main() { result := r(i) fmt.Printf("Factorial of %d is: %d \n", i, result) } - } +// Go Output: +// value of x: 3 +// Factorial of 0 is: 1 +// value of x: 3 +// value of x: 3 +// Factorial of 1 is: 1 +// value of x: 3 +// value of x: 3 +// value of x: 3 +// Factorial of 2 is: 2 + // Output: // value of x: 0 // Factorial of 0 is: 1 diff --git a/gnovm/tests/debug/10.gno b/gnovm/tests/debug/closure16.gno similarity index 100% rename from gnovm/tests/debug/10.gno rename to gnovm/tests/debug/closure16.gno diff --git a/gnovm/tests/debug/10a.gno b/gnovm/tests/debug/closure16_a.gno similarity index 100% rename from gnovm/tests/debug/10a.gno rename to gnovm/tests/debug/closure16_a.gno diff --git a/gnovm/tests/debug/10ab.gno b/gnovm/tests/debug/closure16_a1.gno similarity index 100% rename from gnovm/tests/debug/10ab.gno rename to gnovm/tests/debug/closure16_a1.gno diff --git a/gnovm/tests/debug/10b.gno b/gnovm/tests/debug/closure16_b.gno similarity index 100% rename from gnovm/tests/debug/10b.gno rename to gnovm/tests/debug/closure16_b.gno diff --git a/gnovm/tests/debug/10bb.gno b/gnovm/tests/debug/closure16_b1.gno similarity index 100% rename from gnovm/tests/debug/10bb.gno rename to gnovm/tests/debug/closure16_b1.gno diff --git a/gnovm/tests/debug/io2.gno b/gnovm/tests/debug/closure17_io2.gno similarity index 100% rename from gnovm/tests/debug/io2.gno rename to gnovm/tests/debug/closure17_io2.gno diff --git a/gnovm/tests/debug/recover4.gno b/gnovm/tests/debug/closure17_recover4.gno similarity index 100% rename from gnovm/tests/debug/recover4.gno rename to gnovm/tests/debug/closure17_recover4.gno diff --git a/gnovm/tests/debug/recover6.gno b/gnovm/tests/debug/closure17_recover6.gno similarity index 100% rename from gnovm/tests/debug/recover6.gno rename to gnovm/tests/debug/closure17_recover6.gno diff --git a/gnovm/tests/debug/recover6a.gno b/gnovm/tests/debug/closure17_recover6a.gno similarity index 100% rename from gnovm/tests/debug/recover6a.gno rename to gnovm/tests/debug/closure17_recover6a.gno diff --git a/gnovm/tests/debug/sort_search_efficiency.gno b/gnovm/tests/debug/closure17_sort_search_efficiency.gno similarity index 100% rename from gnovm/tests/debug/sort_search_efficiency.gno rename to gnovm/tests/debug/closure17_sort_search_efficiency.gno diff --git a/gnovm/tests/debug/zregexp_stdlibs.gno b/gnovm/tests/debug/closure17_zregexp_stdlibs.gno similarity index 100% rename from gnovm/tests/debug/zregexp_stdlibs.gno rename to gnovm/tests/debug/closure17_zregexp_stdlibs.gno diff --git a/gnovm/tests/debug/1.gno b/gnovm/tests/debug/closure9.gno similarity index 72% rename from gnovm/tests/debug/1.gno rename to gnovm/tests/debug/closure9.gno index 7f4231373ea..e3a4917211f 100644 --- a/gnovm/tests/debug/1.gno +++ b/gnovm/tests/debug/closure9.gno @@ -1,5 +1,6 @@ package main +// this is a fix, intuitive, and same with Go func main() { var fns []func() int for i := 0; i < 5; i++ { @@ -14,6 +15,13 @@ func main() { } } +// Go Output: +// 0 +// 1 +// 2 +// 3 +// 4 + // Output: // 0 // 1 diff --git a/gnovm/tests/debug2/1d.gno b/gnovm/tests/debug/closure9_a0.gno similarity index 70% rename from gnovm/tests/debug2/1d.gno rename to gnovm/tests/debug/closure9_a0.gno index 185961c741f..0f5ba6d6739 100644 --- a/gnovm/tests/debug2/1d.gno +++ b/gnovm/tests/debug/closure9_a0.gno @@ -1,14 +1,12 @@ package main +// this is a fix, intuitive, same with go loopVar experiment feature. func main() { var fns []func() int for i := 0; i < 5; i++ { - x := i f := func() int { - x := 5 - return x + return i } - println(x) fns = append(fns, f) } for _, fn := range fns { @@ -16,14 +14,16 @@ func main() { } } +// Go Output: +// 5 +// 5 +// 5 +// 5 +// 5 + // Output: // 0 // 1 // 2 // 3 // 4 -// 5 -// 5 -// 5 -// 5 -// 5 diff --git a/gnovm/tests/debug/01.gno b/gnovm/tests/debug/closure9_a02.gno similarity index 100% rename from gnovm/tests/debug/01.gno rename to gnovm/tests/debug/closure9_a02.gno diff --git a/gnovm/tests/debug/closure9_a1.gno b/gnovm/tests/debug/closure9_a1.gno new file mode 100644 index 00000000000..19951610e7e --- /dev/null +++ b/gnovm/tests/debug/closure9_a1.gno @@ -0,0 +1,34 @@ +package main + +// this still keeps consistent with go, that a variable out of loop block is not captured +// this is unintuitive(should capture something) +// TODO: leave it for discuss. + +func main() { + var fns []func() int + var x int + for i := 0; i < 5; i++ { + x = i + f := func() int { + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Go Output: +// 4 +// 4 +// 4 +// 4 +// 4 + +// Output: +// 4 +// 4 +// 4 +// 4 +// 4 diff --git a/gnovm/tests/debug/1b.gno b/gnovm/tests/debug/closure9_b.gno similarity index 100% rename from gnovm/tests/debug/1b.gno rename to gnovm/tests/debug/closure9_b.gno diff --git a/gnovm/tests/debug/1c.gno b/gnovm/tests/debug/closure9_c.gno similarity index 100% rename from gnovm/tests/debug/1c.gno rename to gnovm/tests/debug/closure9_c.gno diff --git a/gnovm/tests/debug/1d.gno b/gnovm/tests/debug/closure9_d.gno similarity index 100% rename from gnovm/tests/debug/1d.gno rename to gnovm/tests/debug/closure9_d.gno diff --git a/gnovm/tests/debug/1e.gno b/gnovm/tests/debug/closure9_e.gno similarity index 100% rename from gnovm/tests/debug/1e.gno rename to gnovm/tests/debug/closure9_e.gno diff --git a/gnovm/tests/debug/1f.gno b/gnovm/tests/debug/closure9_f.gno similarity index 100% rename from gnovm/tests/debug/1f.gno rename to gnovm/tests/debug/closure9_f.gno diff --git a/gnovm/tests/debug/1g.gno b/gnovm/tests/debug/closure9_g.gno similarity index 100% rename from gnovm/tests/debug/1g.gno rename to gnovm/tests/debug/closure9_g.gno diff --git a/gnovm/tests/debug/1h.gno b/gnovm/tests/debug/closure9_h.gno similarity index 88% rename from gnovm/tests/debug/1h.gno rename to gnovm/tests/debug/closure9_h.gno index b3fc6026584..23cb5ff9f40 100644 --- a/gnovm/tests/debug/1h.gno +++ b/gnovm/tests/debug/closure9_h.gno @@ -17,6 +17,12 @@ func main() { } } +// Go Output: +// 0 +// 1 +// 1 +// 2 + // Output: // 0 // 1 diff --git a/gnovm/tests/debug2/1g.gno b/gnovm/tests/debug/closure9_h_0.gno similarity index 63% rename from gnovm/tests/debug2/1g.gno rename to gnovm/tests/debug/closure9_h_0.gno index e7a6b64f1f3..d60fa8515a9 100644 --- a/gnovm/tests/debug2/1g.gno +++ b/gnovm/tests/debug/closure9_h_0.gno @@ -2,11 +2,10 @@ package main func main() { var fns []func() int - for i := 0; i < 5; i++ { - x := i - { + for i := 0; i < 2; i++ { + for j := 0; j < 2; j++ { f := func() int { - return x + return i + j } fns = append(fns, f) } @@ -16,9 +15,14 @@ func main() { } } +// Go Output: +// 4 +// 4 +// 4 +// 4 + // Output: // 0 // 1 +// 1 // 2 -// 3 -// 4 diff --git a/gnovm/tests/debug2/1h.gno b/gnovm/tests/debug/closure9_i.gno similarity index 75% rename from gnovm/tests/debug2/1h.gno rename to gnovm/tests/debug/closure9_i.gno index addd660d04a..42a4d11d862 100644 --- a/gnovm/tests/debug2/1h.gno +++ b/gnovm/tests/debug/closure9_i.gno @@ -2,8 +2,9 @@ package main func main() { var fns []func() int - for i := 0; i < 5; i++ { - x := i + var x int + for i := 0; i < 2; i++ { + x = i for j := 0; j < 2; j++ { y := j f := func() int { @@ -17,14 +18,14 @@ func main() { } } -// Output: -// 0 +// Go Output: // 1 +// 2 // 1 // 2 + +// Output: +// 1 +// 2 +// 1 // 2 -// 3 -// 3 -// 4 -// 4 -// 5 diff --git a/gnovm/tests/debug/1a.gno b/gnovm/tests/debug/closure9_j.gno similarity index 95% rename from gnovm/tests/debug/1a.gno rename to gnovm/tests/debug/closure9_j.gno index 4a2a7630f6d..926caf5090c 100644 --- a/gnovm/tests/debug/1a.gno +++ b/gnovm/tests/debug/closure9_j.gno @@ -7,7 +7,6 @@ func main() { y := 0 f := func() int { x += y - //x += 1 return x } fns = append(fns, f) diff --git a/gnovm/tests/debug/sort_search_efficiency_simple.gnoa b/gnovm/tests/debug/sort_search_efficiency_simple.gnoa deleted file mode 100644 index 4c242461d9e..00000000000 --- a/gnovm/tests/debug/sort_search_efficiency_simple.gnoa +++ /dev/null @@ -1,25 +0,0 @@ -package main - -func Search(x, y int, f func(a, b int) int) int { - return f(x, y) -} - -func main() { - for x := 0; x < 10; x++ { - count := 0 - r := Search(x, count, func(a, b int) int { return a + b }) - println(r) - } -} - -// Output: -// 0 -// 1 -// 2 -// 3 -// 4 -// 5 -// 6 -// 7 -// 8 -// 9 diff --git a/gnovm/tests/debug2/1.gno b/gnovm/tests/debug2/1.gno deleted file mode 100644 index 7f4231373ea..00000000000 --- a/gnovm/tests/debug2/1.gno +++ /dev/null @@ -1,22 +0,0 @@ -package main - -func main() { - var fns []func() int - for i := 0; i < 5; i++ { - x := i - f := func() int { - return x - } - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 0 -// 1 -// 2 -// 3 -// 4 diff --git a/gnovm/tests/debug2/1a.gno b/gnovm/tests/debug2/1a.gno deleted file mode 100644 index 4a2a7630f6d..00000000000 --- a/gnovm/tests/debug2/1a.gno +++ /dev/null @@ -1,22 +0,0 @@ -package main - -func main() { - var fns []func() int - for i := 0; i < 2; i++ { - x := i - y := 0 - f := func() int { - x += y - //x += 1 - return x - } - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 0 -// 1 diff --git a/gnovm/tests/debug2/1b.gno b/gnovm/tests/debug2/1b.gno deleted file mode 100644 index 0822368fe3a..00000000000 --- a/gnovm/tests/debug2/1b.gno +++ /dev/null @@ -1,22 +0,0 @@ -package main - -func main() { - var fns []func() int - for i := 0; i < 2; i++ { - x := i - y := 0 - f := func() int { - x += y - x += 1 - return x - } - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 1 -// 2 diff --git a/gnovm/tests/debug2/1c.gno b/gnovm/tests/debug2/1c.gno deleted file mode 100644 index e5c61c59089..00000000000 --- a/gnovm/tests/debug2/1c.gno +++ /dev/null @@ -1,18 +0,0 @@ -package main - -func main() { - var fns []func() int - for i := 0; i < 2; i++ { - f := func() int { - return i - } - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 0 -// 1 diff --git a/gnovm/tests/debug2/1e.gno b/gnovm/tests/debug2/1e.gno deleted file mode 100644 index b3b2b2b0385..00000000000 --- a/gnovm/tests/debug2/1e.gno +++ /dev/null @@ -1,12 +0,0 @@ -package main - -func main() { - for i := 0; i < 5; i++ { - if i == 1 { - panic("error 1") - } - } -} - -// Error: -// error 1 diff --git a/gnovm/tests/debug2/1f.gno b/gnovm/tests/debug2/1f.gno deleted file mode 100644 index 19722ec3ae6..00000000000 --- a/gnovm/tests/debug2/1f.gno +++ /dev/null @@ -1,53 +0,0 @@ -package main - -// 1. we can determine target vars by hasClosure pattern and externNames(without uverse). -// it can be a loopvar, or vars derived from loopvar, e.g. x := i; -// 2. now we need to capture every transient state of the target var; firstly we should -// eval the transient state, there are two ways around: -// a) eval funcLit, but the time doing this is hard to determine, namely, you have to eval -// before it leaves its using context. In some sense, you have to cover all cases when a funcValue -// is used, like assign, xxxLit, funcCall as an arg, etc. -// b) another way for this is to generate a `time series` values that the index is the loop index, -// e.g. in a for loop, we define a new var like x := i, we store the transient state of x per loop -// in the time series, which is a slice. This slice is used when the closure fv is called, -// replacing the var in default block.(func value have a slice of name indicates it's captured, when -// eval name in this slice, using the time series instead). -// this solution mainly differs that it eval target x independently with the closure funcLit, so it avoids -// the obsession to determine the eval order. this seems the right way. hmmm. - -//======================================================================================================== -// work flow 1.0, hard to do -// 1. determine loop var, whole logic depends on this, the key word is dynamic, it causes polymorphic. -// this should most be done via define(), // TODO: check it -// 2. in transcribe, find if loopvar is used somewhere, to identify pattern like x := i, what to do with -// other use cases? this brings complexity. This is wrong feel. -// we can generate the ts slice for every target var, e.g. x in this case. if two vars, two slices. so fv should -// reference a slice of slice. - -// work flow 2.0 -// 1. ignore loop var, assume all externNames apart from uverse is target captured var, this also needs to happen -// in transcribe, tag it, and can be checked everywhere it's appears in runtime, x := i, or var x int, etc. -// when eval funcLit, new a slice of slice and push value for further update. - -func main() { - var fns []func() int - for i := 0; i < 5; i++ { - //x := i // check if x is polymorphic, if yes, new a slice and set value by index - var x int // new a slice no init, this would be updated later, so the slice can be mutated after opFuncLit - f := func() int { - return x // set the slice to fv in - } - x = i // check if x is polymorphic, is yes, update slice assigned to fv before this, which is a reference. - fns = append(fns, f) // where should eval funcLit, where it assigned somewhere, or used in another func lit - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 0 -// 1 -// 2 -// 3 -// 4 diff --git a/gnovm/tests/debug2/2.gno b/gnovm/tests/debug2/2.gno deleted file mode 100644 index 1a788d9e0b2..00000000000 --- a/gnovm/tests/debug2/2.gno +++ /dev/null @@ -1,26 +0,0 @@ -package main - -import "fmt" - -func bar() func() func() int { - x := 1 - - // First level of closure, modifies x - return func() func() int { - //x++ - // Second level of closure, returns x - return func() int { - return x - } - } -} - -func main() { - f := bar() // f is the first-level closure - g := f() // g is the second-level closure, x is incremented here - - fmt.Println(g()) // prints the value of x after being modified by the first-level closure -} - -// Output: -// 1 diff --git a/gnovm/tests/debug2/2a.gno b/gnovm/tests/debug2/2a.gno deleted file mode 100644 index 45ad06c6945..00000000000 --- a/gnovm/tests/debug2/2a.gno +++ /dev/null @@ -1,26 +0,0 @@ -package main - -import "fmt" - -func bar() func() func() int { - x := 1 - - // First level of closure, modifies x - return func() func() int { - x++ - // Second level of closure, returns x - return func() int { - return x - } - } -} - -func main() { - f := bar() // f is the first-level closure - g := f() // g is the second-level closure, x is incremented here - - fmt.Println(g()) // prints the value of x after being modified by the first-level closure -} - -// Output: -// 2 diff --git a/gnovm/tests/debug2/3.gno b/gnovm/tests/debug2/3.gno deleted file mode 100644 index 1ea6e013b0d..00000000000 --- a/gnovm/tests/debug2/3.gno +++ /dev/null @@ -1,25 +0,0 @@ -package main - -func main() { - var fns []func() int - - for i := 0; i < 2; i++ { - x := i - f := func() int { - if true { - if true { - return x - } - } - return 0 - } - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 0 -// 1 diff --git a/gnovm/tests/debug2/3a.gno b/gnovm/tests/debug2/3a.gno deleted file mode 100644 index 2ce3df96097..00000000000 --- a/gnovm/tests/debug2/3a.gno +++ /dev/null @@ -1,23 +0,0 @@ -package main - -func main() { - var fns []func() int - - for i := 0; i < 2; i++ { - x := i - f := func() int { - if true { - return x - } - return 0 - } - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 0 -// 1 diff --git a/gnovm/tests/debug2/3b.gno b/gnovm/tests/debug2/3b.gno deleted file mode 100644 index 427c6f075f9..00000000000 --- a/gnovm/tests/debug2/3b.gno +++ /dev/null @@ -1,27 +0,0 @@ -package main - -func main() { - var fns []func() int - - for i := 0; i < 2; i++ { - x := i - y := 0 - f := func() int { - if true { - x += y - if true { - return x - } - } - return 0 - } - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 0 -// 1 diff --git a/gnovm/tests/debug2/4.gno b/gnovm/tests/debug2/4.gno deleted file mode 100644 index 44717e05cc0..00000000000 --- a/gnovm/tests/debug2/4.gno +++ /dev/null @@ -1,22 +0,0 @@ -package main - -func main() { - var fns []func() int - - for i := 0; i < 2; i++ { - x := i - f := func() int { - { - return x - } - } - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 0 -// 1 diff --git a/gnovm/tests/debug2/4a.gno b/gnovm/tests/debug2/4a.gno deleted file mode 100644 index 1e3eb9fe815..00000000000 --- a/gnovm/tests/debug2/4a.gno +++ /dev/null @@ -1,23 +0,0 @@ -package main - -func main() { - var fns []func() int - - for i := 0; i < 2; i++ { - x := i - f := func() int { - for i := 0; i < 1; i++ { - x++ - } - return x - } - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 1 -// 2 diff --git a/gnovm/tests/debug2/4b.gno b/gnovm/tests/debug2/4b.gno deleted file mode 100644 index 4351ceaaf49..00000000000 --- a/gnovm/tests/debug2/4b.gno +++ /dev/null @@ -1,24 +0,0 @@ -package main - -func main() { - var fns []func() int - - for i := 0; i < 2; i++ { - x := i - s := []int{1, 2} - f := func() int { - for _, v := range s { - x += v - } - return x - } - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 3 -// 4 diff --git a/gnovm/tests/debug2/4c.gno b/gnovm/tests/debug2/4c.gno deleted file mode 100644 index 3e42e207649..00000000000 --- a/gnovm/tests/debug2/4c.gno +++ /dev/null @@ -1,27 +0,0 @@ -package main - -func main() { - var fns []func() int - - for i := 0; i < 2; i++ { - x := i - y := 1 - f := func() int { - switch y { - case 1: - x += 1 - default: - x += 0 - } - return x - } - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 1 -// 2 diff --git a/gnovm/tests/debug2/4d.gnoa b/gnovm/tests/debug2/4d.gnoa deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/gnovm/tests/debug2/4e.gno b/gnovm/tests/debug2/4e.gno deleted file mode 100644 index 17490510b26..00000000000 --- a/gnovm/tests/debug2/4e.gno +++ /dev/null @@ -1,27 +0,0 @@ -package main - -type queueOnePass struct { - sparse []uint32 - dense []uint32 - size, nextIndex uint32 -} - -func newQueue(size int) (q *queueOnePass) { - return &queueOnePass{ - sparse: make([]uint32, size), - dense: make([]uint32, size), - } -} -func main() { - var ( - visitQueue = newQueue(10) - ) - f := func() { - println(visitQueue.size) - } - - f() -} - -// Output: -// 0 diff --git a/gnovm/tests/debug2/4f.gno b/gnovm/tests/debug2/4f.gno deleted file mode 100644 index 60e327bd2b2..00000000000 --- a/gnovm/tests/debug2/4f.gno +++ /dev/null @@ -1,20 +0,0 @@ -package main - -func main() { - s := []int{1, 2} - - f := func() { - for i, v := range s { // TODO: exclude s, it's final - println(i) - println(v) - } - } - - f() -} - -// Output: -// 0 -// 1 -// 1 -// 2 diff --git a/gnovm/tests/debug2/4g.gno b/gnovm/tests/debug2/4g.gno deleted file mode 100644 index bf998c163d8..00000000000 --- a/gnovm/tests/debug2/4g.gno +++ /dev/null @@ -1,14 +0,0 @@ -package main - -func main() { - f := func(a int) bool { - println(a) - return true - } - - println(f(5)) -} - -// Output: -// 5 -// true diff --git a/gnovm/tests/debug2/4h.gno b/gnovm/tests/debug2/4h.gno deleted file mode 100644 index c9f1f6e97e8..00000000000 --- a/gnovm/tests/debug2/4h.gno +++ /dev/null @@ -1,25 +0,0 @@ -package main - -func main() { - s := 1 - f := func() { - i := 0 // no capture for i - var j = s // s should be captured, j not - k := s // s should be captured, k not - m, n := s, 0 - println(i) - println(j) - println(k) - println(m) - println(n) - } - - f() -} - -// Output: -// 0 -// 1 -// 1 -// 1 -// 0 diff --git a/gnovm/tests/debug2/4i.gno b/gnovm/tests/debug2/4i.gno deleted file mode 100644 index acf1b53b32a..00000000000 --- a/gnovm/tests/debug2/4i.gno +++ /dev/null @@ -1,15 +0,0 @@ -package main - -func main() { - s := []int{1, 2} - - f := func() { - if len(s) == 2 { - println("ok") - } - } - f() -} - -// Output: -// ok diff --git a/gnovm/tests/debug2/5.gno b/gnovm/tests/debug2/5.gno deleted file mode 100644 index ece19dd87f6..00000000000 --- a/gnovm/tests/debug2/5.gno +++ /dev/null @@ -1,22 +0,0 @@ -package main - -import "fmt" - -func main() { - // Define a function that returns a closure - var recursiveFunc func(int) int - recursiveFunc = func(num int) int { - if num <= 0 { - return 1 - } - // Closure calling itself recursively - return num * recursiveFunc(num-1) - } - - // Use the recursive closure - result := recursiveFunc(5) - fmt.Println("Factorial of 5 is:", result) // Output: 120 -} - -// Output: -// Factorial of 5 is: 120 diff --git a/gnovm/tests/debug2/5a.gno b/gnovm/tests/debug2/5a.gno deleted file mode 100644 index 8345c9ed48d..00000000000 --- a/gnovm/tests/debug2/5a.gno +++ /dev/null @@ -1,24 +0,0 @@ -package main - -import "fmt" - -func main() { - var recursiveFunc func(int) int - var recursiveFunc2 func(int) int - - recursiveFunc = func(num int) int { - recursiveFunc2 = recursiveFunc - - if num <= 0 { - return 1 - } - - return num * recursiveFunc2(num-1) - } - - result := recursiveFunc(5) - fmt.Println("Factorial of 5 is:", result) // Output: 120 -} - -// Output: -// Factorial of 5 is: 120 diff --git a/gnovm/tests/debug2/5b.gno b/gnovm/tests/debug2/5b.gno deleted file mode 100644 index 5f54cc2f95d..00000000000 --- a/gnovm/tests/debug2/5b.gno +++ /dev/null @@ -1,46 +0,0 @@ -package main - -import ( - "fmt" - "io" - "strings" -) - -func main() { - var mr io.Reader - var buf []byte - nread := 0 - withFooBar := func(tests func()) { - r1 := strings.NewReader("foo ") - //r2 := strings.NewReader("") - //r3 := strings.NewReader("bar") - //mr = io.MultiReader(r1, r2, r3) - mr = io.MultiReader(r1) - buf = make([]byte, 20) - tests() - } - expectRead := func(size int, expected string, eerr error) { - nread++ - n, gerr := mr.Read(buf[0:size]) - if n != len(expected) { - panic(fmt.Sprintf("#%d, expected %d bytes; got %d", - nread, len(expected), n)) - } - got := string(buf[0:n]) - if got != expected { - panic(fmt.Sprintf("#%d, expected %q; got %q", - nread, expected, got)) - } - if gerr != eerr { - panic(fmt.Sprintf("#%d, expected error %v; got %v", - nread, eerr, gerr)) - } - buf = buf[n:] - } - withFooBar(func() { - expectRead(5, "foo ", nil) - }) - -} - -// Output: diff --git a/gnovm/tests/debug2/5b0.gno b/gnovm/tests/debug2/5b0.gno deleted file mode 100644 index 4bfe864cc8e..00000000000 --- a/gnovm/tests/debug2/5b0.gno +++ /dev/null @@ -1,26 +0,0 @@ -package main - -func main() { - var buf []byte - - // when eval this, buf is still nil, - expectRead := func(size int, expected string, err error) { - b := buf[0:size] - println(b) - println(len(buf)) - println(cap(buf)) - } - - // buf should be captured here, here it's volatile, not where it defined - // namely, the vp should point here, -> so mutate offset - withFooBar := func() { - buf = make([]byte, 20) - expectRead(5, "foo ", nil) - } - withFooBar() -} - -// Output: -// slice[0x0000000000] -// 20 -// 20 diff --git a/gnovm/tests/debug2/5b1.gno b/gnovm/tests/debug2/5b1.gno deleted file mode 100644 index 9218ee16fa3..00000000000 --- a/gnovm/tests/debug2/5b1.gno +++ /dev/null @@ -1,26 +0,0 @@ -package main - -func main() { - var buf []byte - - // when eval this, buf is still nil, - expectRead := func(size int, expected string, eerr error) { - b := buf[0:size] - println(b) - } - - // buf should be captured here, here it's volatile, not where it defined - // namely, the vp should point here, -> so mutate offset - withFooBar := func() func() { - buf = make([]byte, 20) - return func() { - b := buf[0:4] - println(b) - } - } - withFooBar() - println("ok") -} - -// Output: -// ok diff --git a/gnovm/tests/debug2/6a.gno b/gnovm/tests/debug2/6a.gno deleted file mode 100644 index 1acfa3a1f2c..00000000000 --- a/gnovm/tests/debug2/6a.gno +++ /dev/null @@ -1,24 +0,0 @@ -package main - -import ( - "errors" -) - -func foo() (err error) { - y := 1 - defer func() { - if r := recover(); r != nil { - switch y { - case 1: - err = errors.New("xxx") - default: - err = nil - } - } - }() - return -} - -func main() { - foo() -} diff --git a/gnovm/tests/debug2/7.gno b/gnovm/tests/debug2/7.gno deleted file mode 100644 index 86c8fc445f0..00000000000 --- a/gnovm/tests/debug2/7.gno +++ /dev/null @@ -1,32 +0,0 @@ -package main - -import "fmt" - -func main() { - - var fns []func(int) int - var recursiveFunc func(int) int - - for i := 0; i < 6; i++ { - recursiveFunc = func(num int) int { - if num <= 0 { - return 1 - } - return num * recursiveFunc(num-1) - } - fns = append(fns, recursiveFunc) - } - - for i, r := range fns { - result := r(i) - fmt.Printf("Factorial of %d is: %d \n", i, result) - } -} - -// Output: -// Factorial of 0 is: 1 -// Factorial of 1 is: 1 -// Factorial of 2 is: 2 -// Factorial of 3 is: 6 -// Factorial of 4 is: 24 -// Factorial of 5 is: 120 diff --git a/gnovm/tests/debug2/7a.gno b/gnovm/tests/debug2/7a.gno deleted file mode 100644 index 9ae40a43df7..00000000000 --- a/gnovm/tests/debug2/7a.gno +++ /dev/null @@ -1,36 +0,0 @@ -package main - -import "fmt" - -// recursive closure does not capture -func main() { - - var fns []func(int) int - var recursiveFunc func(int) int - - for i := 0; i < 3; i++ { - recursiveFunc = func(num int) int { - x := i - if num <= 0 { - return 1 - } - println(x) - return num * recursiveFunc(num-1) - } - fns = append(fns, recursiveFunc) - } - - for i, r := range fns { - result := r(i) - fmt.Printf("Factorial of %d is: %d \n", i, result) - } - -} - -// Output: -// Factorial of 0 is: 1 -// 2 -// Factorial of 1 is: 1 -// 2 -// 2 -// Factorial of 2 is: 2 diff --git a/gnovm/tests/debug2/closure0.gno b/gnovm/tests/debug2/closure0.gno deleted file mode 100644 index acc1abfd404..00000000000 --- a/gnovm/tests/debug2/closure0.gno +++ /dev/null @@ -1,17 +0,0 @@ -package main - -type adder func(int, int) int - -func genAdd(k int) adder { - return func(i, j int) int { - return i + j + k - } -} - -func main() { - f := genAdd(5) - println(f(3, 4)) -} - -// Output: -// 12 diff --git a/gnovm/tests/debug2/closure1.gno b/gnovm/tests/debug2/closure1.gno deleted file mode 100644 index f0bbda7ca1e..00000000000 --- a/gnovm/tests/debug2/closure1.gno +++ /dev/null @@ -1,20 +0,0 @@ -package main - -type adder func(int, int) int - -func genAdd(k int) adder { - return func(i, j int) int { - return i + j + k - } -} - -func main() { - f := genAdd(5) - g := genAdd(8) - println(f(3, 4)) - println(g(3, 4)) -} - -// Output: -// 12 -// 15 diff --git a/gnovm/tests/debug2/closure2.gno b/gnovm/tests/debug2/closure2.gno deleted file mode 100644 index e43496b9f43..00000000000 --- a/gnovm/tests/debug2/closure2.gno +++ /dev/null @@ -1,28 +0,0 @@ -package main - -func adder() func(int) int { - sum := 0 - return func(x int) int { - sum = sum + x - return sum - } -} - -func main() { - pos, neg := adder(), adder() - for i := 0; i < 10; i++ { - println(pos(i), neg(-2*i)) - } -} - -// Output: -// 0 0 -// 1 -2 -// 3 -6 -// 6 -12 -// 10 -20 -// 15 -30 -// 21 -42 -// 28 -56 -// 36 -72 -// 45 -90 diff --git a/gnovm/tests/debug2/closure3.gno b/gnovm/tests/debug2/closure3.gno deleted file mode 100644 index 5e365a67cb4..00000000000 --- a/gnovm/tests/debug2/closure3.gno +++ /dev/null @@ -1,23 +0,0 @@ -package main - -type T1 struct { - Name string -} - -func (t *T1) genAdd(k int) func(int) int { - return func(i int) int { - println(t.Name) - return i + k - } -} - -var t = &T1{"test"} - -func main() { - f := t.genAdd(4) - println(f(5)) -} - -// Output: -// test -// 9 diff --git a/gnovm/tests/debug2/closure4.gno b/gnovm/tests/debug2/closure4.gno deleted file mode 100644 index 61c0a77f1ba..00000000000 --- a/gnovm/tests/debug2/closure4.gno +++ /dev/null @@ -1,23 +0,0 @@ -package main - -type T1 struct { - Name string -} - -func (t *T1) genAdd(k int) func(int) int { - return func(i int) int { - println(t.Name) - return i + k - } -} - -var t = T1{"test"} - -func main() { - f := t.genAdd(4) - println(f(5)) -} - -// Output: -// test -// 9 diff --git a/gnovm/tests/debug2/closure5.gno b/gnovm/tests/debug2/closure5.gno deleted file mode 100644 index e6d3c223607..00000000000 --- a/gnovm/tests/debug2/closure5.gno +++ /dev/null @@ -1,23 +0,0 @@ -package main - -type T1 struct { - Name string -} - -func (t T1) genAdd(k int) func(int) int { - return func(i int) int { - println(t.Name) - return i + k - } -} - -var t = T1{"test"} - -func main() { - f := t.genAdd(4) - println(f(5)) -} - -// Output: -// test -// 9 diff --git a/gnovm/tests/debug2/closure6.gno b/gnovm/tests/debug2/closure6.gno deleted file mode 100644 index 5e365a67cb4..00000000000 --- a/gnovm/tests/debug2/closure6.gno +++ /dev/null @@ -1,23 +0,0 @@ -package main - -type T1 struct { - Name string -} - -func (t *T1) genAdd(k int) func(int) int { - return func(i int) int { - println(t.Name) - return i + k - } -} - -var t = &T1{"test"} - -func main() { - f := t.genAdd(4) - println(f(5)) -} - -// Output: -// test -// 9 diff --git a/gnovm/tests/debug2/closure7.gno b/gnovm/tests/debug2/closure7.gno deleted file mode 100644 index d4c0c96b0d5..00000000000 --- a/gnovm/tests/debug2/closure7.gno +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import ( - "fmt" -) - -type Config struct { - A string -} - -var conf *Config = &Config{} - -func SetConfig() func(*Config) { - return func(cf *Config) { - conf = cf - } -} - -func main() { - conf := &Config{ - A: "foo", - } - - fmt.Println(conf.A) -} - -// Output: -// foo diff --git a/gnovm/tests/debug2/closure8.gno b/gnovm/tests/debug2/closure8.gno deleted file mode 100644 index 6ef1b43fca6..00000000000 --- a/gnovm/tests/debug2/closure8.gno +++ /dev/null @@ -1,10 +0,0 @@ -package main - -var f = func(a int) int { return 2 + a } - -func main() { - println(f(3)) -} - -// Output: -// 5 diff --git a/gnovm/tests/debug2/io2.gno b/gnovm/tests/debug2/io2.gno deleted file mode 100644 index 24655f5040c..00000000000 --- a/gnovm/tests/debug2/io2.gno +++ /dev/null @@ -1,21 +0,0 @@ -package main - -import ( - "fmt" - "io" - "log" - "strings" -) - -func main() { - r := strings.NewReader("Go is a general-purpose language designed with systems programming in mind.") - - b, err := io.ReadAll(r) - if err != nil { - log.Fatal(err) - } - fmt.Printf("%s", b) -} - -// Output: -// Go is a general-purpose language designed with systems programming in mind. diff --git a/gnovm/tests/debug2/recover4.gno b/gnovm/tests/debug2/recover4.gno deleted file mode 100644 index 5a6da4261a2..00000000000 --- a/gnovm/tests/debug2/recover4.gno +++ /dev/null @@ -1,25 +0,0 @@ -package main - -import "fmt" - -func div(a, b int) (result int) { - defer func() { - r := recover() - - fmt.Printf("r = %#v\n", r) - - if r != nil { - result = 0 - } - }() - - return a / b -} - -func main() { - println(div(30, 2)) -} - -// Output: -// r = -// 15 diff --git a/gnovm/tests/debug2/recover6.gno b/gnovm/tests/debug2/recover6.gno deleted file mode 100644 index 0b304369764..00000000000 --- a/gnovm/tests/debug2/recover6.gno +++ /dev/null @@ -1,30 +0,0 @@ -package main - -import ( - "errors" -) - -func main() { - println(f(false)) - println(f(true)) -} - -func f(dopanic bool) (err error) { - defer func() { - if x := recover(); x != nil { - err = x.(error) - } - }() - q(dopanic) - return -} - -func q(dopanic bool) { - if dopanic { - panic(errors.New("wtf")) - } -} - -// Output: -// undefined -// wtf diff --git a/gnovm/tests/debug2/recover6a.gno b/gnovm/tests/debug2/recover6a.gno deleted file mode 100644 index 427fc4b74b9..00000000000 --- a/gnovm/tests/debug2/recover6a.gno +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import "errors" - -//func p(s interface{}) { -// fmt.Printf("%T \n", s) -// if v, ok := s.(string); ok { -// println(v) -// panic(v) -// } else { -// println("---") -// } -//} - -type error interface { - Error() string -} - -// New returns an error that formats as the given text. -// Each call to New returns a distinct error value even if the text is identical. -func New(text string) error { - return &errorString{text} -} - -// errorString is a trivial implementation of error. -type errorString struct { - s string -} - -func (e *errorString) Error() string { - return e.s -} - -func main() { - //panic(New("wtf")) - panic(errors.New("wtf")) -} - -// Error: -// wtf diff --git a/gnovm/tests/debug2/sort_search_efficiency.gno b/gnovm/tests/debug2/sort_search_efficiency.gno deleted file mode 100644 index 90362944ac5..00000000000 --- a/gnovm/tests/debug2/sort_search_efficiency.gno +++ /dev/null @@ -1,21 +0,0 @@ -package main - -func Search(n int, f func(int) bool) int { - f(1) - return 0 -} - -func main() { - for x := 0; x < 2; x++ { - count := 0 - println(" first: count: ", count) - Search(1, func(i int) bool { count++; return i >= x }) - println("second: count: ", count) - } -} - -// Output: -// first: count: 0 -// second: count: 1 -// first: count: 0 -// second: count: 1 diff --git a/gnovm/tests/debug2/sort_search_efficiency_simple.gnoa b/gnovm/tests/debug2/sort_search_efficiency_simple.gnoa deleted file mode 100644 index 4c242461d9e..00000000000 --- a/gnovm/tests/debug2/sort_search_efficiency_simple.gnoa +++ /dev/null @@ -1,25 +0,0 @@ -package main - -func Search(x, y int, f func(a, b int) int) int { - return f(x, y) -} - -func main() { - for x := 0; x < 10; x++ { - count := 0 - r := Search(x, count, func(a, b int) int { return a + b }) - println(r) - } -} - -// Output: -// 0 -// 1 -// 2 -// 3 -// 4 -// 5 -// 6 -// 7 -// 8 -// 9 diff --git a/gnovm/tests/debug2/zregexp_stdlibs.gno b/gnovm/tests/debug2/zregexp_stdlibs.gno deleted file mode 100644 index 10bb6f937d3..00000000000 --- a/gnovm/tests/debug2/zregexp_stdlibs.gno +++ /dev/null @@ -1,19 +0,0 @@ -// MAXALLOC: 100000000 -// max total allocation of 100 mb. -package main - -import "regexp" - -var reName = regexp.MustCompile(`^[a-z]+[_a-z0-9]*$`) - -func main() { - for j := 0; j < 100; j++ { - if !(reName.MatchString("thisisatestname")) { - panic("error") - } - } - println(true) -} - -// Output: -// true diff --git a/gnovm/tests/file.go b/gnovm/tests/file.go index fd99b0c3b6c..d2a5406c3b8 100644 --- a/gnovm/tests/file.go +++ b/gnovm/tests/file.go @@ -150,7 +150,7 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error { } if !gno.IsRealmPath(pkgPath) { - //startTime := time.Now() + // startTime := time.Now() // simple case. pn := gno.NewPackageNode(pkgName, pkgPath, &gno.FileSet{}) @@ -165,17 +165,17 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error { f.logger("RUN MAIN") f.logger("========================================") } - //endTime := time.Now() - //elapsedTime := endTime.Sub(startTime) - //fmt.Printf("Run Init took %s to execute\n", elapsedTime) + // endTime := time.Now() + // elapsedTime := endTime.Sub(startTime) + // fmt.Printf("Run Init took %s to execute\n", elapsedTime) - //startTime = time.Now() + // startTime = time.Now() m.RunMain() - //endTime = time.Now() - //elapsedTime = endTime.Sub(startTime) + // endTime = time.Now() + // elapsedTime = endTime.Sub(startTime) - //fmt.Printf("Run Main took %s to execute\n", elapsedTime) + // fmt.Printf("Run Main took %s to execute\n", elapsedTime) if f.logger != nil { f.logger("========================================") f.logger("RUN MAIN END") diff --git a/gnovm/tests/file_test.go b/gnovm/tests/file_test.go index ae6c2c7b1f9..10f7c19fef4 100644 --- a/gnovm/tests/file_test.go +++ b/gnovm/tests/file_test.go @@ -33,9 +33,10 @@ func TestFiles(t *testing.T) { } func TestDebug(t *testing.T) { + // baseDir := filepath.Join(".", "debug2") baseDir := filepath.Join(".", "debug") runFileTests(t, baseDir, []string{"*_native*"}) - //runFileTests(t, baseDir, []string{"*_stdlibs*"}, WithNativeLibs()) + // runFileTests(t, baseDir, []string{"*_stdlibs*"}, WithNativeLibs()) } func TestChallenges(t *testing.T) { diff --git a/gnovm/tests/files/zrealm0.gno b/gnovm/tests/files/zrealm0.gno index 489fbbdf829..487422282b7 100644 --- a/gnovm/tests/files/zrealm0.gno +++ b/gnovm/tests/files/zrealm0.gno @@ -48,7 +48,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -70,7 +69,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], diff --git a/gnovm/tests/files/zrealm1.gno b/gnovm/tests/files/zrealm1.gno index d88bb1e27dd..63075a77dcd 100644 --- a/gnovm/tests/files/zrealm1.gno +++ b/gnovm/tests/files/zrealm1.gno @@ -162,7 +162,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -184,7 +183,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], diff --git a/gnovm/tests/files/zrealm2.gno b/gnovm/tests/files/zrealm2.gno index 635c872a4dd..988d74b436f 100644 --- a/gnovm/tests/files/zrealm2.gno +++ b/gnovm/tests/files/zrealm2.gno @@ -165,7 +165,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -187,7 +186,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -203,7 +202,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -225,7 +223,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], diff --git a/gnovm/tests/files/zrealm3.gno b/gnovm/tests/files/zrealm3.gno index ca4fe03c577..72bb49f3560 100644 --- a/gnovm/tests/files/zrealm3.gno +++ b/gnovm/tests/files/zrealm3.gno @@ -164,7 +164,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -186,7 +185,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -202,7 +201,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -224,7 +222,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], diff --git a/gnovm/tests/files/zrealm4.gno b/gnovm/tests/files/zrealm4.gno index e9dc7ebfcc1..dd8cc23dd52 100644 --- a/gnovm/tests/files/zrealm4.gno +++ b/gnovm/tests/files/zrealm4.gno @@ -106,7 +106,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -128,7 +127,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -144,7 +143,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -166,7 +164,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], diff --git a/gnovm/tests/files/zrealm5.gno b/gnovm/tests/files/zrealm5.gno index b2f14109ffd..954ab2763e6 100644 --- a/gnovm/tests/files/zrealm5.gno +++ b/gnovm/tests/files/zrealm5.gno @@ -177,7 +177,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -199,7 +198,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -215,7 +214,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -237,7 +235,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], diff --git a/gnovm/tests/files/zrealm6.gno b/gnovm/tests/files/zrealm6.gno index 6bfba4f7f45..e40291dcff7 100644 --- a/gnovm/tests/files/zrealm6.gno +++ b/gnovm/tests/files/zrealm6.gno @@ -249,7 +249,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -271,7 +270,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -287,7 +286,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -309,7 +307,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], diff --git a/gnovm/tests/files/zrealm7.gno b/gnovm/tests/files/zrealm7.gno index 67d2ccf3687..20319e4f61b 100644 --- a/gnovm/tests/files/zrealm7.gno +++ b/gnovm/tests/files/zrealm7.gno @@ -321,7 +321,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -343,7 +342,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -359,7 +358,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -381,7 +379,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], diff --git a/gnovm/tests/files/zrealm_avl0.gno b/gnovm/tests/files/zrealm_avl0.gno index 8c4637652cb..acd3d3d5758 100644 --- a/gnovm/tests/files/zrealm_avl0.gno +++ b/gnovm/tests/files/zrealm_avl0.gno @@ -259,7 +259,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -281,7 +280,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -297,7 +296,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -319,7 +317,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], diff --git a/gnovm/tests/files/zrealm_avl1.gno b/gnovm/tests/files/zrealm_avl1.gno index c760d1e529d..77726610747 100644 --- a/gnovm/tests/files/zrealm_avl1.gno +++ b/gnovm/tests/files/zrealm_avl1.gno @@ -283,7 +283,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -305,7 +304,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -321,7 +320,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -343,7 +341,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], diff --git a/gnovm/tests/files/zrealm_natbind0.gno b/gnovm/tests/files/zrealm_natbind0.gno index af482361672..8b141fc14f4 100644 --- a/gnovm/tests/files/zrealm_natbind0.gno +++ b/gnovm/tests/files/zrealm_natbind0.gno @@ -53,7 +53,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -75,7 +74,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -91,7 +90,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -113,7 +111,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -149,7 +147,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -171,7 +168,7 @@ func main() { // "PkgPath": "std" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [ diff --git a/gnovm/tests/files/zrealm_tests0.gno b/gnovm/tests/files/zrealm_tests0.gno index 972b61270db..40362238489 100644 --- a/gnovm/tests/files/zrealm_tests0.gno +++ b/gnovm/tests/files/zrealm_tests0.gno @@ -235,7 +235,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": null, // "FileName": "tests.gno", // "IsMethod": true, @@ -253,7 +252,7 @@ func main() { // "PkgPath": "gno.land/r/demo/tests" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [ @@ -339,7 +338,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -361,7 +359,7 @@ func main() { // "PkgPath": "gno.land/r/demo/tests" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [ @@ -407,7 +405,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -429,7 +426,7 @@ func main() { // "PkgPath": "gno.land/r/demo/tests" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [ @@ -465,7 +462,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -487,7 +483,7 @@ func main() { // "PkgPath": "gno.land/r/demo/tests" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -513,7 +509,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -535,7 +530,7 @@ func main() { // "PkgPath": "gno.land/r/demo/tests" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -571,7 +566,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -593,7 +587,7 @@ func main() { // "PkgPath": "gno.land/r/demo/tests" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -619,7 +613,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -641,7 +634,7 @@ func main() { // "PkgPath": "gno.land/r/demo/tests" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -667,7 +660,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -689,7 +681,7 @@ func main() { // "PkgPath": "gno.land/r/demo/tests" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -728,7 +720,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -750,7 +741,7 @@ func main() { // "PkgPath": "gno.land/r/demo/tests" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [ @@ -779,7 +770,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -801,7 +791,7 @@ func main() { // "PkgPath": "gno.land/r/demo/tests" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -817,7 +807,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -839,7 +828,7 @@ func main() { // "PkgPath": "gno.land/r/demo/tests" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -855,7 +844,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -877,7 +865,7 @@ func main() { // "PkgPath": "gno.land/r/demo/tests" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -903,7 +891,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -925,7 +912,7 @@ func main() { // "PkgPath": "gno.land/r/demo/tests" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -961,7 +948,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -983,7 +969,7 @@ func main() { // "PkgPath": "gno.land/r/demo/tests" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -1020,7 +1006,6 @@ func main() { // }, // "V": { // "@type": "/gno.FuncValue", -// "Captures": null, // "Closure": { // "@type": "/gno.RefValue", // "Escaped": true, @@ -1042,7 +1027,7 @@ func main() { // "PkgPath": "gno.land/r/demo/tests" // } // }, -// "Tss": null, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [ From 494d998a0a7c17709f898121cbd7b66031221402 Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Fri, 26 Jan 2024 00:54:09 +0800 Subject: [PATCH 17/32] clean --- gnovm/pkg/gnolang/machine.go | 5 -- gnovm/pkg/gnolang/op_assign.go | 32 ------------ gnovm/pkg/gnolang/op_call.go | 71 +++++++++++---------------- gnovm/pkg/gnolang/op_exec.go | 50 +++++++------------ gnovm/pkg/gnolang/op_expressions.go | 75 ++++++++++++----------------- gnovm/pkg/gnolang/values.go | 10 ++-- 6 files changed, 82 insertions(+), 161 deletions(-) diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go index 45cb7063043..8455449ca2a 100644 --- a/gnovm/pkg/gnolang/machine.go +++ b/gnovm/pkg/gnolang/machine.go @@ -1061,9 +1061,7 @@ const ( OpCPUMaybeNativeType = 1 /* Statement operators */ - OpCPUPreAssign = 1 OpCPUAssign = 1 - OpCPUPostAssign = 1 OpCPUAddAssign = 1 OpCPUSubAssign = 1 OpCPUMulAssign = 1 @@ -1349,9 +1347,6 @@ func (m *Machine) Run() { case OpAssign: m.incrCPU(OpCPUAssign) m.doOpAssign() - case OpPostAssign: - m.incrCPU(OpCPUPostAssign) - m.doOpPostAssign() case OpAddAssign: m.incrCPU(OpCPUAddAssign) m.doOpAddAssign() diff --git a/gnovm/pkg/gnolang/op_assign.go b/gnovm/pkg/gnolang/op_assign.go index 54acb0b2abb..9c9e969ecf1 100644 --- a/gnovm/pkg/gnolang/op_assign.go +++ b/gnovm/pkg/gnolang/op_assign.go @@ -51,38 +51,6 @@ func (m *Machine) doOpAssign() { } lv.Assign2(m.Alloc, m.Store, m.Realm, rvs[i], true) } - - //// after assign, get lhs, when recursive closure - //if len(s.Rhs) == 1 { - // // push evaluated v - // if fx, ok := s.Rhs[0].(*FuncLitExpr); ok { - // debugPP.Printf("---fx.closure: %v \n", fx.Closure) - // if fx.Closure.recursive { - // // is recursive, push value - // //m.PushValue(rvs[0]) - // rx := s.Rhs[0] - // // evaluate Rhs - // m.PushExpr(rx) - // m.PushOp(OpEval) - // //m.PushOp(OpFuncLit) // eval again to update captured nx - // } - // } else { - // //m.PushValue(typedString("x")) - // } - //} -} - -func (m *Machine) doOpPostAssign() { - debugPP.Println("---doOpPostAssign") - //// get lhs name and value - //x := m.PopExpr() - //// use this value to replace captured, in funcLit - //v := m.PopValue() - //debugPP.Printf("pop x: %v \n", x) - //debugPP.Printf("pop value: %v \n", v) - //if fx, ok := x.(*FuncLitExpr); ok { - // //fx.Closure - //} } func (m *Machine) doOpAddAssign() { diff --git a/gnovm/pkg/gnolang/op_call.go b/gnovm/pkg/gnolang/op_call.go index 6115aef1ca4..4b07fcbb66f 100644 --- a/gnovm/pkg/gnolang/op_call.go +++ b/gnovm/pkg/gnolang/op_call.go @@ -8,10 +8,7 @@ import ( func (m *Machine) doOpPrecall() { cx := m.PopExpr().(*CallExpr) - - debug.Printf("-----doOpPrecall------callExpr :%v \n", cx) v := m.PeekValue(1 + cx.NumArgs).V - debug.Printf("-----doOpPrecall------v :%v \n", v) if debug { if v == nil { // This may happen due to an undefined uverse or @@ -22,9 +19,6 @@ func (m *Machine) doOpPrecall() { } switch fv := v.(type) { case *FuncValue: - debug.Printf("-----funcValue: %v------ \n", fv) - debug.Printf("-----body: %v------ \n", fv.GetBodyFromSource(m.Store)) - m.PushFrameCall(cx, fv, TypedValue{}) m.PushOp(OpCall) case *BoundMethodValue: @@ -62,49 +56,42 @@ func (m *Machine) doOpCall() { numParams := len(pts) isMethod := 0 // 1 if true - clo := fr.Func.GetClosure(m.Store) + clo := fr.Func.GetClosure(m.Store) // this is "static" debugPP.Printf("-----got closure: %v ----- \n", clo) // update block vars using captured vars - var targetB *Block + var loopBlock *Block // target loop block with values to be updated debugPP.Println("================parse transient for fv====================") - if fv.TransientLoopData != nil { + if fv.TransientLoopData != nil { // it captured a bundle of values for i, loopData := range fv.TransientLoopData { // each LoopValuesBox is for a certain level of block - bag := loopData.loopValuesBox - debugPP.Printf("Level[%d] LoopValuesBox is : %v \n", i, bag) - if bag != nil { - if bag.isSealed { - if bag != nil && !bag.isEmpty() { // NOTE: for some reasons won't pack value - for j, t := range bag.transient { // unpack vars belong to a specific block - if t != nil { - debugPP.Printf("LoopValuesBox.transient[%d] name is %v, path: %v, len of values is: %v \n", j, t.nx.Name, t.nx.Path, len(t.values)) - for k, v := range t.values { - debugPP.Printf("values[%d] is %v \n", k, v) - } - targetB = clo.GetBlockWithDepth(m.Store, t.nx.Path) // find target block using depth - // check if exists, should always be? - names := targetB.GetSource(m.Store).GetBlockNames() - var index int - var match bool - for l, n := range names { - if n == t.nx.Name { - debugPP.Printf("name: %s match \n", n) - index = l - match = true - break - } - } - if match { - debugPP.Println("===match, cursor is:", loopData.index) - // update values in context with previously captured. - targetB.UpdateValue(index, t.values[loopData.index]) - } - } + box := loopData.loopValuesBox + debugPP.Printf("Level[%d] LoopValuesBox is : %v \n", i, box) + if box.isSealed { + for j, t := range box.transient { // unpack vars belong to a specific block + debugPP.Printf("LoopValuesBox.transient[%d] name is %v, path: %v, len of values is: %v \n", j, t.nx.Name, t.nx.Path, len(t.values)) + for k, v := range t.values { + debugPP.Printf("values[%d] is %v \n", k, v) + } + loopBlock = clo.GetBlockWithDepth(m.Store, t.nx.Path) // find target block using depth + // check if exists, should always be? + names := loopBlock.GetSource(m.Store).GetBlockNames() + var index int + var match bool + for l, n := range names { + if n == t.nx.Name { + debugPP.Printf("name: %s match \n", n) + index = l + match = true + break } } - } else { // not sealed will be tainted, indicates it's not a closure. - debugPP.Println("---taint this var") - fv.TransientLoopData[i].loopValuesBox.isTainted = true + if match { + debugPP.Println("===match, cursor is:", loopData.index) + // update values in context with previously captured. + loopBlock.UpdateValue(index, t.values[loopData.index]) + } } + } else { // not sealed will be tainted, indicates not sealed with values when loopBlock ends, it's not a closure. + fv.TransientLoopData[i].loopValuesBox.isTainted = true } } fv.TransientLoopData = nil diff --git a/gnovm/pkg/gnolang/op_exec.go b/gnovm/pkg/gnolang/op_exec.go index 618c68f1a94..34619bc6d5f 100644 --- a/gnovm/pkg/gnolang/op_exec.go +++ b/gnovm/pkg/gnolang/op_exec.go @@ -46,46 +46,30 @@ SelectStmt -> func updateCapturedValue(m *Machine, lb *Block) { if lb.GetBodyStmt().isLoop { // do we need this? bs := lb.GetBodyStmt() - debugPP.Printf("---------isLoop, current block is: %v \n", lb) debugPP.Printf("---------LoopValuesBox, %v \n", bs.LoopValuesBox) if bs.LoopValuesBox != nil && bs.LoopValuesBox.isFilled && !bs.LoopValuesBox.isTainted { - names := lb.Source.GetBlockNames() - var found bool var isSealed bool for i, tt := range bs.LoopValuesBox.transient { - if tt != nil { - debugPP.Printf("transient[%d] name is %v, path: %v \n", i, tt.nx.Name, tt.nx.Path) - // first check if name is in current block, it not, stop - for _, name := range names { - if tt.nx.Name == name { - debugPP.Printf("found %s in current block: %v \n", tt.nx.Name, lb) - found = true - } - } - if found { - nvp := lb.Source.GetPathForName(m.Store, tt.nx.Name) - ptr := m.LastBlock().GetPointerTo(m.Store, nvp) - tv := ptr.Deref() - debugPP.Printf("--- new tv : %v \n", tv) - // set back - debugPP.Printf("before update, len of LoopValuesBox values is : %d \n", len(bs.LoopValuesBox.transient[i].values)) - // inner loops iterates twice while outer loop iterates once. - // it's an alignment for the values of out loop block. - expandRatio := bs.LoopValuesBox.transient[i].cursor + 1 - len(bs.LoopValuesBox.transient[i].values) // 2 - 0 - debugPP.Printf("--- expand ratio is: %d \n", expandRatio) - for j := 0; j < expandRatio; j++ { - bs.LoopValuesBox.transient[i].values = append(bs.LoopValuesBox.transient[i].values, tv) - } - debugPP.Printf("after update, len of LoopValuesBox values is : %d \n", len(bs.LoopValuesBox.transient[i].values)) - isSealed = true - } else { - debugPP.Printf("---not found %s in current block, b: %v \n", tt.nx.Name, lb) - isSealed = false - } + debugPP.Printf("transient[%d] name is %v, path: %v \n", i, tt.nx.Name, tt.nx.Path) + nvp := lb.Source.GetPathForName(m.Store, tt.nx.Name) + ptr := m.LastBlock().GetPointerTo(m.Store, nvp) + tv := ptr.Deref() + debugPP.Printf("--- new tv : %v \n", tv) + // update context use previously recorded value + debugPP.Printf("before update, len of LoopValuesBox values is : %d \n", len(bs.LoopValuesBox.transient[i].values)) + // inner loops iterates twice while outer loop iterates once. + // it's an alignment for the values of out loop block. + // TODO: hard to understand, more doc or e.g. + expandRatio := bs.LoopValuesBox.transient[i].cursor + 1 - len(bs.LoopValuesBox.transient[i].values) // 2 - 0 + debugPP.Printf("--- expand ratio is: %d \n", expandRatio) + for j := 0; j < expandRatio; j++ { + bs.LoopValuesBox.transient[i].values = append(bs.LoopValuesBox.transient[i].values, tv) } + debugPP.Printf("after update, len of LoopValuesBox values is : %d \n", len(bs.LoopValuesBox.transient[i].values)) + isSealed = true } if isSealed { - bs.LoopValuesBox.isSealed = true // seal for this LoopValuesBox of this block + bs.LoopValuesBox.isSealed = true // seal for this LoopValuesBox for closure execution. } } } diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index 7126ced668c..0bc155f12e3 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -702,19 +702,16 @@ func findLoopBlockWithPath(store Store, b *Block, nx *NameExpr) (*Block, bool, u func (m *Machine) doOpFuncLit() { x := m.PopExpr().(*FuncLitExpr) - debugPP.Printf("-----doOpFuncLit, x: %v \n", x) lb := m.LastBlock() - debugPP.Printf("last block: %v \n", lb) + debugPP.Printf("doOpFuncLit, x: %v, lb: %v \n", x, lb) var captures []*NameExpr for _, n := range x.GetExternNames() { - debugPP.Println("iterating extern names, n: ", n) vp := x.GetPathForName(m.Store, n) - debugPP.Printf("%v's value path is: %v \n", n, vp) - if vp.Depth == 0 { + if vp.Depth == 0 { // skip uverse name continue } - vp.Depth -= 1 // from funcLit block + vp.Depth -= 1 // from the perspective of funcLit block nx := &NameExpr{ Name: n, Path: vp, @@ -722,40 +719,31 @@ func (m *Machine) doOpFuncLit() { captures = append(captures, nx) } - // sort it so to traverse block in an order of inner to outer + // sort it so to traverse block in order from inner to outer sortDepth := func(i, j int) bool { return int(captures[i].Path.Depth) < int(captures[j].Path.Depth) } sort.Slice(captures, sortDepth) - debugPP.Println("---done sort") var ( isLoopBlock bool - loopBlock *Block - lvBox *LoopValuesBox + loopBlock *Block // e.g. a `for` block that is outside of funcLit block + lvBox *LoopValuesBox // container per block to store transient values for captured vars lastGen, gen uint8 isReuse bool - loopData []*LoopBlockData + loopData []*LoopBlockData // for fv to track all transient values ) - // var TransientLoopData *TransientLoopData - // start search every captured var in target block via path - // 1. TransientLoopData is a slice of LoopValuesBox, one LoopValuesBox is for one loop block. - // 2. new a `LoopValuesBox` for every block, the LoopValuesBox has a flag of isFilled for reuse, - // and a []transient for vars in on loop block, that each transient contains an - // nameExpr, and a slice of values that is value of var in specific time slice. - // TransientLoopData -> []LoopValuesBox -> isFilled, []transient -> nx:nameExpr, values:[]TypedValue. - // XXX, TransientLoopData if for fv, which is the concrete entity of closure. - // in case for i:=0, i++; i<2{ x := i }, TransientLoopData will have one LoopValuesBox for the only loop block, - // and one nx for x, and 2 values of [0,1], fv will have 2 replica in this case, and they - // share the same loopBlock. - // there are some more complex situation like: + + // when iterating goes on, funcValue will yield several replicas, each of which should capture a slice of + // transient values for a nameExpr. + // e.g. `for i:=0, i++; i<2{ x := i }`, the transient values for x should be [0 ,1], this is recorded and + // used when the funcLit is executed, namely, closure. + // more complex situation like: // for i:=0, i++; i<2{ x := i for j:=0, j++; j<2{y := j}}, - // in this case, there will be 4 replica of fv, and 3 loopBlock, 1 if for the outer loop, - // left 2 is the inner blocks, since as the outer for loop execute, will yield due num of - // sub blocks. - // an intuitive state for state of x, y is: [0,0], [0,1], [1,0], [1,1]. - // in the impl, the outer block has a LoopValuesBox of [0,0,1,1], and inner block 1, has a LoopValuesBox [0,1] - // and inner block 2 has a LoopValuesBox[0,1]. + // in this case, there will be 4 replica of fv, and 3 loopBlock. + // the transient state of x, y is: [0,0], [0,1], [1,0], [1,1]. + // the outer block will hold transient values of [0,0,1,1] for `x`, and inner block 1 will hold [0,1] for `y`, + // another inner block 2 holds [0,1] for y too. for i, nx := range captures { debugPP.Printf("captures[%d] is : %v \n", i, nx.Name) // start search block, in the order of small depth to big depth @@ -763,45 +751,44 @@ func (m *Machine) doOpFuncLit() { debugPP.Printf("loopBlock: %v, isLoopBlock: %v, gen: %v, cursor: %v \n", loopBlock, isLoopBlock, gen) if lastGen == 0 { lastGen = gen - } else if gen != lastGen { // if enter new level of block, means last block is all done, pack LoopValuesBox + } else if gen != lastGen { // if enter new level of block, pack last box lastGen = gen - // set last state if lvBox != nil && !isReuse { // has something to pack lvBox.isFilled = true - loopData = append(loopData, &LoopBlockData{index: 0, loopValuesBox: lvBox}) // pack per level of block + loopData = append(loopData, &LoopBlockData{index: 0, loopValuesBox: lvBox}) } debugPP.Printf("========packed LoopValuesBox for %v is: %s \n", loopBlock, lvBox) } if isLoopBlock { + // every loopBlock holds a loopValuesBox that contains a slice of transient value generated as the iterations goes on. + // funcValue references this loopValuesBox for future use(when closure executing) lvBox = loopBlock.GetBodyStmt().LoopValuesBox // get LoopValuesBox from specific block, that is shared across current level of for/range loop debugPP.Printf("got initial LoopValuesBox from target loop block: %v \n", lvBox) if lvBox == nil { lvBox = &LoopValuesBox{} // init } - if !lvBox.isFilled { // only once for further reuse - debugPP.Println("---not sealed---, should pack only once for n: ", nx.Name) + if !lvBox.isFilled { // for replicas of fv, the captured names are same, so fill only once for further reuse. + debugPP.Println("---not filled---") tst := &Transient{ nx: nx, - cursor: 0, + cursor: 0, // inc every iteration, implies sequence of a fv, and index of transient values. } lvBox.transient = append(lvBox.transient, tst) - // set back to loop block properly + // record in loop block loopBlock.GetBodyStmt().LoopValuesBox = lvBox - } else { // repack when iterates, this should be optimized + } else { // reuse last replica's. (in same block). isReuse = true // use isFilled instead // get cursor by name - debugPP.Println("---reuse, current cursor is: ", lvBox.transient[lvBox.getIndexByName(nx.Name)].cursor) - lvBox.transient[lvBox.getIndexByName(nx.Name)].cursor++ // only inc in reuse, that is iteration - // cursor indicates num of iterations of fv + lvBox.transient[lvBox.getIndexByName(nx.Name)].cursor++ // inc by iteration + // each fv may have n outer loopBlock, reference them all loopData = append(loopData, &LoopBlockData{index: lvBox.transient[lvBox.getIndexByName(nx.Name)].cursor, loopValuesBox: lvBox}) } } } - // set fill flag + // set as a deferred operation if lvBox != nil && !lvBox.isFilled && !isReuse { - debugPP.Println("---tail pack") - lvBox.isFilled = true // set for the last LoopValuesBox - loopData = append(loopData, &LoopBlockData{index: 0, loopValuesBox: lvBox}) // collect the last LoopValuesBox + lvBox.isFilled = true // set for the last LoopValuesBox of last loopBlock + loopData = append(loopData, &LoopBlockData{index: 0, loopValuesBox: lvBox}) debugPP.Printf("========packed LoopValuesBox for %v is: %s \n", loopBlock, lvBox) } diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index 36f4a300b84..b3d276c84f9 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -560,13 +560,13 @@ type Transient struct { cursor int // cursor per fv to find value in time series } -// LoopValuesBox stores a slice of transient values of captured vars dynamically -// generated as the iterations goes on. +// LoopValuesBox stores a slice of transient values of captured vars +// that generated as the iterations goes on. // it is used for a closure execution. type LoopValuesBox struct { - isFilled bool - isSealed bool - isTainted bool + isFilled bool // filled with names, no values + isSealed bool // filled with names and values + isTainted bool // with names but no values. for cases funcLit is called before loop block ends, makes it not a closure. transient []*Transient } From 4cb1710674447846a5ae827d25f6b9d4e64a5650 Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Fri, 26 Jan 2024 01:29:34 +0800 Subject: [PATCH 18/32] clean --- gnovm/pkg/gnolang/debug.go | 19 +---- gnovm/pkg/gnolang/frame.go | 2 +- gnovm/pkg/gnolang/machine.go | 2 +- gnovm/pkg/gnolang/nodes.go | 34 --------- gnovm/pkg/gnolang/op_assign.go | 1 - gnovm/pkg/gnolang/op_call.go | 17 ++--- gnovm/pkg/gnolang/op_eval.go | 4 -- gnovm/pkg/gnolang/op_exec.go | 11 --- gnovm/pkg/gnolang/op_expressions.go | 38 ++-------- gnovm/pkg/gnolang/preprocess.go | 31 -------- gnovm/pkg/gnolang/transcribe.go | 70 ------------------- gnovm/pkg/gnolang/values.go | 46 +++++------- gnovm/tests/files/closure10.gno | 26 +++++++ gnovm/tests/files/closure10_a.gno | 26 +++++++ gnovm/tests/files/closure11.gno | 25 +++++++ gnovm/tests/files/closure11_a.gno | 23 ++++++ gnovm/tests/files/closure11_b.gno | 27 +++++++ gnovm/tests/files/closure12.gno | 22 ++++++ gnovm/tests/files/closure12_a.gno | 23 ++++++ gnovm/tests/files/closure12_b.gno | 24 +++++++ gnovm/tests/files/closure12_c.gno | 27 +++++++ gnovm/tests/files/closure12_e.gno | 27 +++++++ gnovm/tests/files/closure12_f.gno | 20 ++++++ gnovm/tests/files/closure12_g.gno | 14 ++++ gnovm/tests/files/closure12_h.gno | 25 +++++++ gnovm/tests/files/closure12_i.gno | 15 ++++ gnovm/tests/files/closure13.gno | 22 ++++++ gnovm/tests/files/closure13_a.gno | 24 +++++++ gnovm/tests/files/closure13_b0.gno | 26 +++++++ gnovm/tests/files/closure13_b1.gno | 26 +++++++ gnovm/tests/files/closure13_b_stdlibs.gno | 47 +++++++++++++ gnovm/tests/files/closure14.gno | 26 +++++++ gnovm/tests/files/closure14_a.gno | 27 +++++++ gnovm/tests/files/closure15.gno | 32 +++++++++ gnovm/tests/files/closure15_a.gno | 49 +++++++++++++ gnovm/tests/files/closure16.gno | 21 ++++++ gnovm/tests/files/closure16_a.gno | 20 ++++++ gnovm/tests/files/closure16_a1.gno | 19 +++++ gnovm/tests/files/closure16_b.gno | 23 ++++++ gnovm/tests/files/closure16_b1.gno | 22 ++++++ gnovm/tests/files/closure17_io2.gno | 21 ++++++ gnovm/tests/files/closure17_recover4.gno | 25 +++++++ gnovm/tests/files/closure17_recover6.gno | 30 ++++++++ gnovm/tests/files/closure17_recover6a.gno | 40 +++++++++++ .../closure17_sort_search_efficiency.gno | 21 ++++++ .../tests/files/closure17_zregexp_stdlibs.gno | 19 +++++ gnovm/tests/files/closure9.gno | 30 ++++++++ gnovm/tests/files/closure9_a0.gno | 29 ++++++++ gnovm/tests/files/closure9_a02.gno | 20 ++++++ gnovm/tests/files/closure9_a1.gno | 34 +++++++++ gnovm/tests/files/closure9_b.gno | 22 ++++++ gnovm/tests/files/closure9_c.gno | 18 +++++ gnovm/tests/files/closure9_d.gno | 29 ++++++++ gnovm/tests/files/closure9_e.gno | 12 ++++ gnovm/tests/files/closure9_f.gno | 53 ++++++++++++++ gnovm/tests/files/closure9_g.gno | 24 +++++++ gnovm/tests/files/closure9_h.gno | 30 ++++++++ gnovm/tests/files/closure9_h_0.gno | 28 ++++++++ gnovm/tests/files/closure9_i.gno | 31 ++++++++ gnovm/tests/files/closure9_j.gno | 21 ++++++ 60 files changed, 1279 insertions(+), 241 deletions(-) create mode 100644 gnovm/tests/files/closure10.gno create mode 100644 gnovm/tests/files/closure10_a.gno create mode 100644 gnovm/tests/files/closure11.gno create mode 100644 gnovm/tests/files/closure11_a.gno create mode 100644 gnovm/tests/files/closure11_b.gno create mode 100644 gnovm/tests/files/closure12.gno create mode 100644 gnovm/tests/files/closure12_a.gno create mode 100644 gnovm/tests/files/closure12_b.gno create mode 100644 gnovm/tests/files/closure12_c.gno create mode 100644 gnovm/tests/files/closure12_e.gno create mode 100644 gnovm/tests/files/closure12_f.gno create mode 100644 gnovm/tests/files/closure12_g.gno create mode 100644 gnovm/tests/files/closure12_h.gno create mode 100644 gnovm/tests/files/closure12_i.gno create mode 100644 gnovm/tests/files/closure13.gno create mode 100644 gnovm/tests/files/closure13_a.gno create mode 100644 gnovm/tests/files/closure13_b0.gno create mode 100644 gnovm/tests/files/closure13_b1.gno create mode 100644 gnovm/tests/files/closure13_b_stdlibs.gno create mode 100644 gnovm/tests/files/closure14.gno create mode 100644 gnovm/tests/files/closure14_a.gno create mode 100644 gnovm/tests/files/closure15.gno create mode 100644 gnovm/tests/files/closure15_a.gno create mode 100644 gnovm/tests/files/closure16.gno create mode 100644 gnovm/tests/files/closure16_a.gno create mode 100644 gnovm/tests/files/closure16_a1.gno create mode 100644 gnovm/tests/files/closure16_b.gno create mode 100644 gnovm/tests/files/closure16_b1.gno create mode 100644 gnovm/tests/files/closure17_io2.gno create mode 100644 gnovm/tests/files/closure17_recover4.gno create mode 100644 gnovm/tests/files/closure17_recover6.gno create mode 100644 gnovm/tests/files/closure17_recover6a.gno create mode 100644 gnovm/tests/files/closure17_sort_search_efficiency.gno create mode 100644 gnovm/tests/files/closure17_zregexp_stdlibs.gno create mode 100644 gnovm/tests/files/closure9.gno create mode 100644 gnovm/tests/files/closure9_a0.gno create mode 100644 gnovm/tests/files/closure9_a02.gno create mode 100644 gnovm/tests/files/closure9_a1.gno create mode 100644 gnovm/tests/files/closure9_b.gno create mode 100644 gnovm/tests/files/closure9_c.gno create mode 100644 gnovm/tests/files/closure9_d.gno create mode 100644 gnovm/tests/files/closure9_e.gno create mode 100644 gnovm/tests/files/closure9_f.gno create mode 100644 gnovm/tests/files/closure9_g.gno create mode 100644 gnovm/tests/files/closure9_h.gno create mode 100644 gnovm/tests/files/closure9_h_0.gno create mode 100644 gnovm/tests/files/closure9_i.gno create mode 100644 gnovm/tests/files/closure9_j.gno diff --git a/gnovm/pkg/gnolang/debug.go b/gnovm/pkg/gnolang/debug.go index 33ad29bbaae..1d98a0f44c8 100644 --- a/gnovm/pkg/gnolang/debug.go +++ b/gnovm/pkg/gnolang/debug.go @@ -19,20 +19,17 @@ import ( // before calling debug.Println or debug.Printf. type ( - debugging bool - debugPreprocess bool + debugging bool ) // using a const is probably faster. // const debug debugging = true // or flip var ( - debug debugging = false - debugPP debugPreprocess = false + debug debugging = false ) func init() { debug = os.Getenv("DEBUG") == "1" - debugPP = os.Getenv("DEBUG_PP") == "1" if debug { go func() { // e.g. @@ -70,18 +67,6 @@ func (d debugging) Printf(format string, args ...interface{}) { } } -func (d debugPreprocess) Println(args ...interface{}) { - if d { - fmt.Println(append([]interface{}{"DEBUG:"}, args...)...) - } -} - -func (d debugPreprocess) Printf(format string, args ...interface{}) { - if d { - fmt.Printf("DEBUG: "+format, args...) - } -} - var derrors []string = nil // Instead of actually panic'ing, which messes with tests, errors are sometimes diff --git a/gnovm/pkg/gnolang/frame.go b/gnovm/pkg/gnolang/frame.go index f495cbe4d35..7f87fa26097 100644 --- a/gnovm/pkg/gnolang/frame.go +++ b/gnovm/pkg/gnolang/frame.go @@ -31,7 +31,7 @@ type Frame struct { func (fr Frame) String() string { if fr.Func != nil { return fmt.Sprintf("[FRAME FUNC:%v RECV:%s (%d args) %d/%d/%d/%d/%d LASTPKG:%s LASTRLM:%v]", - *fr.Func, + fr.Func, fr.Receiver, fr.NumArgs, fr.NumOps, diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go index 8455449ca2a..e41f264c06c 100644 --- a/gnovm/pkg/gnolang/machine.go +++ b/gnovm/pkg/gnolang/machine.go @@ -718,7 +718,7 @@ func (m *Machine) Eval(x Expr) []TypedValue { // static types and values. func (m *Machine) EvalStatic(last BlockNode, x Expr) TypedValue { if debug { - // m.Printf("Machine.EvalStatic(%v, %v)\n", last, x) + m.Printf("Machine.EvalStatic(%v, %v)\n", last, x) } // X must have been preprocessed. if x.GetAttribute(ATTR_PREPROCESSED) == nil { diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index 1b948e697d7..7a4f00b9a3d 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -498,10 +498,6 @@ type FuncLitExpr struct { Body // function body } -//type ClosureObject struct { -// StaticBlock -//} - // The preprocessor replaces const expressions // with *ConstExpr nodes. type ConstExpr struct { @@ -1354,7 +1350,6 @@ func (x *PackageNode) NewPackage() *PackageValue { // NOTE: declared methods do not get their closures set here. See // *DeclaredType.GetValueAt() which returns a filled copy. func (x *PackageNode) PrepareNewValues(pv *PackageValue) []TypedValue { - debug.Println("-----PrepareNewValues") if pv.PkgPath == "" { // nothing to prepare for throwaway packages. // TODO: double check to see if still relevant. @@ -1494,33 +1489,6 @@ type StaticBlock struct { oldValues []oldValue } -func (sb *StaticBlock) String() { - fmt.Println("==============static block==============") - for i, t := range sb.Types { - fmt.Printf("types[%d] is %v \n", i, t) - } - fmt.Printf("numNames is %d \n", sb.NumNames) - for i, t := range sb.Names { - fmt.Printf("names[%d] is %v \n", i, t) - } - for i, t := range sb.Consts { - fmt.Printf("consts[%d] is %v \n", i, t) - } - for i, t := range sb.Externs { - fmt.Printf("externs[%d] is %v \n", i, t) - } - for i, t := range sb.oldValues { - fmt.Printf("oldValues[%d] is %v \n", i, t) - } - fmt.Printf("sb objectInfo %v \n", sb.GetObjectInfo()) - fmt.Printf("sb isEscaped %v \n", sb.GetIsEscaped()) - fmt.Printf("sb isTransient %v \n", sb.GetIsTransient()) - // fmt.Printf("sb getOwnder, getOwnerID %v \n", sb.GetOwner(), sb.GetOwnerID()) - fmt.Println("=================block==================") - fmt.Printf("block: %v \n", sb.Block) - fmt.Println("==================end===================") -} - type oldValue struct { idx uint16 value Value @@ -1622,7 +1590,6 @@ func (sb *StaticBlock) GetParentNode(store Store) BlockNode { // Implements BlockNode. // As a side effect, notes externally defined names. func (sb *StaticBlock) GetPathForName(store Store, n Name) ValuePath { - // debug.Printf("-----GetPathForName: %v \n", n) if n == "_" { return NewValuePathBlock(0, 0, "_") } @@ -1736,7 +1703,6 @@ func (sb *StaticBlock) GetStaticTypeOfAt(store Store, path ValuePath) Type { // Implements BlockNode. func (sb *StaticBlock) GetLocalIndex(n Name) (uint16, bool) { - // debug.Printf("GetLocalIndex: %v \n", n) for i, name := range sb.Names { if name == n { if debug { diff --git a/gnovm/pkg/gnolang/op_assign.go b/gnovm/pkg/gnolang/op_assign.go index 9c9e969ecf1..55c952da295 100644 --- a/gnovm/pkg/gnolang/op_assign.go +++ b/gnovm/pkg/gnolang/op_assign.go @@ -32,7 +32,6 @@ func (m *Machine) doOpDefine() { func (m *Machine) doOpAssign() { s := m.PopStmt().(*AssignStmt) - debugPP.Printf("---doOpAssign, s: %v \n", s) // Assign each value evaluated for Lhs. // NOTE: PopValues() returns a slice in // forward order, not the usual reverse. diff --git a/gnovm/pkg/gnolang/op_call.go b/gnovm/pkg/gnolang/op_call.go index 4b07fcbb66f..6442c8d83d5 100644 --- a/gnovm/pkg/gnolang/op_call.go +++ b/gnovm/pkg/gnolang/op_call.go @@ -46,7 +46,6 @@ func (m *Machine) doOpPrecall() { var gReturnStmt = &ReturnStmt{} func (m *Machine) doOpCall() { - debugPP.Println("-----doOpCall-----") // NOTE: Frame won't be popped until the statement is complete, to // discard the correct number of results for func calls in ExprStmts. fr := m.LastFrame() @@ -57,19 +56,17 @@ func (m *Machine) doOpCall() { isMethod := 0 // 1 if true clo := fr.Func.GetClosure(m.Store) // this is "static" - debugPP.Printf("-----got closure: %v ----- \n", clo) // update block vars using captured vars - var loopBlock *Block // target loop block with values to be updated - debugPP.Println("================parse transient for fv====================") + var loopBlock *Block // target loop block with values to be updated if fv.TransientLoopData != nil { // it captured a bundle of values for i, loopData := range fv.TransientLoopData { // each LoopValuesBox is for a certain level of block box := loopData.loopValuesBox - debugPP.Printf("Level[%d] LoopValuesBox is : %v \n", i, box) if box.isSealed { - for j, t := range box.transient { // unpack vars belong to a specific block - debugPP.Printf("LoopValuesBox.transient[%d] name is %v, path: %v, len of values is: %v \n", j, t.nx.Name, t.nx.Path, len(t.values)) - for k, v := range t.values { - debugPP.Printf("values[%d] is %v \n", k, v) + for _, t := range box.transient { // unpack vars belong to a specific block + if debug { + for k, v := range t.values { + fmt.Printf("values[%d] is %v \n", k, v) + } } loopBlock = clo.GetBlockWithDepth(m.Store, t.nx.Path) // find target block using depth // check if exists, should always be? @@ -78,14 +75,12 @@ func (m *Machine) doOpCall() { var match bool for l, n := range names { if n == t.nx.Name { - debugPP.Printf("name: %s match \n", n) index = l match = true break } } if match { - debugPP.Println("===match, cursor is:", loopData.index) // update values in context with previously captured. loopBlock.UpdateValue(index, t.values[loopData.index]) } diff --git a/gnovm/pkg/gnolang/op_eval.go b/gnovm/pkg/gnolang/op_eval.go index 617f8cd15b5..bbefaedd862 100644 --- a/gnovm/pkg/gnolang/op_eval.go +++ b/gnovm/pkg/gnolang/op_eval.go @@ -19,7 +19,6 @@ func (m *Machine) doOpEval() { // fmt.Println(m.String()) } - debugPP.Printf("EVAL: (%T) %v\n", x, x) // This case moved out of switch for performance. // TODO: understand this better. if nx, ok := x.(*NameExpr); ok { @@ -308,9 +307,6 @@ func (m *Machine) doOpEval() { m.PushExpr(x.Type) m.PushOp(OpEval) case *FuncLitExpr: - debugPP.Println("-----FuncLitExpr") - b := m.LastBlock() - debugPP.Printf("b: %v \n", b) m.PushOp(OpFuncLit) m.PushExpr(&x.Type) m.PushOp(OpEval) diff --git a/gnovm/pkg/gnolang/op_exec.go b/gnovm/pkg/gnolang/op_exec.go index 34619bc6d5f..13d69fc6420 100644 --- a/gnovm/pkg/gnolang/op_exec.go +++ b/gnovm/pkg/gnolang/op_exec.go @@ -46,26 +46,20 @@ SelectStmt -> func updateCapturedValue(m *Machine, lb *Block) { if lb.GetBodyStmt().isLoop { // do we need this? bs := lb.GetBodyStmt() - debugPP.Printf("---------LoopValuesBox, %v \n", bs.LoopValuesBox) if bs.LoopValuesBox != nil && bs.LoopValuesBox.isFilled && !bs.LoopValuesBox.isTainted { var isSealed bool for i, tt := range bs.LoopValuesBox.transient { - debugPP.Printf("transient[%d] name is %v, path: %v \n", i, tt.nx.Name, tt.nx.Path) nvp := lb.Source.GetPathForName(m.Store, tt.nx.Name) ptr := m.LastBlock().GetPointerTo(m.Store, nvp) tv := ptr.Deref() - debugPP.Printf("--- new tv : %v \n", tv) // update context use previously recorded value - debugPP.Printf("before update, len of LoopValuesBox values is : %d \n", len(bs.LoopValuesBox.transient[i].values)) // inner loops iterates twice while outer loop iterates once. // it's an alignment for the values of out loop block. // TODO: hard to understand, more doc or e.g. expandRatio := bs.LoopValuesBox.transient[i].cursor + 1 - len(bs.LoopValuesBox.transient[i].values) // 2 - 0 - debugPP.Printf("--- expand ratio is: %d \n", expandRatio) for j := 0; j < expandRatio; j++ { bs.LoopValuesBox.transient[i].values = append(bs.LoopValuesBox.transient[i].values, tv) } - debugPP.Printf("after update, len of LoopValuesBox values is : %d \n", len(bs.LoopValuesBox.transient[i].values)) isSealed = true } if isSealed { @@ -151,7 +145,6 @@ func (m *Machine) doOpExec(op Op) { s = next goto EXEC_SWITCH } else if bs.NextBodyIndex == bs.BodyLen { - debugPP.Printf("---for loop end of loop body, going to update value using current state--- \n") updateCapturedValue(m, lb) // (queue to) go back. if bs.Cond != nil { @@ -246,7 +239,6 @@ func (m *Machine) doOpExec(op Op) { goto EXEC_SWITCH } else if bs.NextBodyIndex == bs.BodyLen { // update captured values - debugPP.Printf("---range array end of loop body, going to update value using current state--- \n") updateCapturedValue(m, m.LastBlock()) if bs.ListIndex < bs.ListLen-1 { // set up next assign if needed. @@ -342,7 +334,6 @@ func (m *Machine) doOpExec(op Op) { s = next // switch on bs.Active goto EXEC_SWITCH } else if bs.NextBodyIndex == bs.BodyLen { - debugPP.Printf("---range of string end of loop body, going to update value using current state--- \n") updateCapturedValue(m, m.LastBlock()) if bs.StrIndex < bs.StrLen { // set up next assign if needed. @@ -437,7 +428,6 @@ func (m *Machine) doOpExec(op Op) { s = next // switch on bs.Active goto EXEC_SWITCH } else if bs.NextBodyIndex == bs.BodyLen { - debugPP.Printf("---range of map, end of loop body, going to update value using current state--- \n") updateCapturedValue(m, m.LastBlock()) nnext := bs.NextItem.Next if nnext == nil { @@ -480,7 +470,6 @@ EXEC_SWITCH: // TODO: add case log for debug switch cs := s.(type) { case *AssignStmt: - debugPP.Printf("-----AssignStmt: %v \n", cs) switch cs.Op { case ASSIGN: // post assign, use value of lhs to update captured value, name as ID diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index 0bc155f12e3..aa595108b64 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -88,7 +88,6 @@ func (m *Machine) doOpSelector() { } func (m *Machine) doOpSlice() { - debugPP.Println("-----doOpSlice") sx := m.PopExpr().(*SliceExpr) var low, high, max int = -1, -1, -1 // max @@ -107,7 +106,6 @@ func (m *Machine) doOpSlice() { } // slice base x xv := m.PopValue() - debugPP.Printf("---xv: %v \n", xv) // if a is a pointer to an array, a[low : high : max] is // shorthand for (*a)[low : high : max] if xv.T.Kind() == PointerKind && @@ -680,31 +678,9 @@ func (m *Machine) doOpStructLit() { }) } -// find nearest block is loop and contains name of n -func findLoopBlockWithPath(store Store, b *Block, nx *NameExpr) (*Block, bool, uint8) { - var gen uint8 = 1 - for i := uint8(1); i < nx.Path.Depth; i++ { // find target block at certain depth - b = b.GetParent(store) - gen++ - } - - debugPP.Printf("---b is: %v, \n", b) - if b.GetBodyStmt().isLoop { // is loop - names := b.GetSource(store).GetBlockNames() - for _, name := range names { - if nx.Name == name { // find n in this block - return b, true, gen - } - } - } - return nil, false, gen -} - func (m *Machine) doOpFuncLit() { x := m.PopExpr().(*FuncLitExpr) lb := m.LastBlock() - debugPP.Printf("doOpFuncLit, x: %v, lb: %v \n", x, lb) - var captures []*NameExpr for _, n := range x.GetExternNames() { vp := x.GetPathForName(m.Store, n) @@ -744,11 +720,9 @@ func (m *Machine) doOpFuncLit() { // the transient state of x, y is: [0,0], [0,1], [1,0], [1,1]. // the outer block will hold transient values of [0,0,1,1] for `x`, and inner block 1 will hold [0,1] for `y`, // another inner block 2 holds [0,1] for y too. - for i, nx := range captures { - debugPP.Printf("captures[%d] is : %v \n", i, nx.Name) + for _, nx := range captures { // start search block, in the order of small depth to big depth loopBlock, isLoopBlock, gen = findLoopBlockWithPath(m.Store, lb, nx) - debugPP.Printf("loopBlock: %v, isLoopBlock: %v, gen: %v, cursor: %v \n", loopBlock, isLoopBlock, gen) if lastGen == 0 { lastGen = gen } else if gen != lastGen { // if enter new level of block, pack last box @@ -757,18 +731,15 @@ func (m *Machine) doOpFuncLit() { lvBox.isFilled = true loopData = append(loopData, &LoopBlockData{index: 0, loopValuesBox: lvBox}) } - debugPP.Printf("========packed LoopValuesBox for %v is: %s \n", loopBlock, lvBox) } if isLoopBlock { // every loopBlock holds a loopValuesBox that contains a slice of transient value generated as the iterations goes on. // funcValue references this loopValuesBox for future use(when closure executing) lvBox = loopBlock.GetBodyStmt().LoopValuesBox // get LoopValuesBox from specific block, that is shared across current level of for/range loop - debugPP.Printf("got initial LoopValuesBox from target loop block: %v \n", lvBox) if lvBox == nil { lvBox = &LoopValuesBox{} // init } if !lvBox.isFilled { // for replicas of fv, the captured names are same, so fill only once for further reuse. - debugPP.Println("---not filled---") tst := &Transient{ nx: nx, cursor: 0, // inc every iteration, implies sequence of a fv, and index of transient values. @@ -789,11 +760,12 @@ func (m *Machine) doOpFuncLit() { if lvBox != nil && !lvBox.isFilled && !isReuse { lvBox.isFilled = true // set for the last LoopValuesBox of last loopBlock loopData = append(loopData, &LoopBlockData{index: 0, loopValuesBox: lvBox}) - debugPP.Printf("========packed LoopValuesBox for %v is: %s \n", loopBlock, lvBox) } - for i, ts := range loopData { - debugPP.Printf("========TransientLoopData[%d] is: %s, addr: %p, index: %d \n", i, ts.loopValuesBox, &ts.loopValuesBox, ts.index) + if debug { + for i, ts := range loopData { + fmt.Printf("========TransientLoopData[%d] is: %s, addr: %p, index: %d \n", i, ts.loopValuesBox, &ts.loopValuesBox, ts.index) + } } ft := m.PopValue().V.(TypeValue).Type.(*FuncType) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 0b2162d5199..808ffd9894b 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -222,8 +222,6 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // TRANS_ENTER ----------------------- case *ImportDecl, *ValueDecl, *TypeDecl, *FuncDecl: - debug.Println("-----trans enter Decls") - debug.Printf("node: %v \n", n) // NOTE func decl usually must happen with a // file, and so last is usually a *FileNode, // but for testing convenience we allow @@ -274,14 +272,9 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // TRANS_BLOCK ----------------------- case *BlockStmt: pushInitBlock(n, &last, &stack) - debug.Println("blockStmt push closure") - // pushClosure(&Closure{}) - // debug.Println("blockStmt pop closure") - // popClosure() // TRANS_BLOCK ----------------------- case *ForStmt: - debug.Println("-----ForStmt-----") pushInitBlock(n, &last, &stack) // TRANS_BLOCK ----------------------- @@ -294,7 +287,6 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // TRANS_BLOCK ----------------------- case *IfCaseStmt: - debug.Println("-----IfCaseStmt-----") pushRealBlock(n, &last, &stack) // pushClosure(&Closure{}) // parent if statement. @@ -302,10 +294,8 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // anything declared in ifs are copied. for _, n := range ifs.GetBlockNames() { tv := ifs.GetValueRef(nil, n) - debug.Printf("tv : %v, *tv: %v \n", tv, *tv) last.Define(n, *tv) } - // popClosure() // TRANS_BLOCK ----------------------- case *RangeStmt: @@ -367,23 +357,16 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // TRANS_BLOCK ----------------------- case *FuncLitExpr: - debug.Println("---PP funcLitExpr") - // retrieve cached function type. ft := evalStaticType(store, last, &n.Type).(*FuncType) // push func body block. pushInitBlock(n, &last, &stack) - // pushClosure(&Closure{}) - // pushFxs(n) - // define parameters in new block. for _, p := range ft.Params { - debug.Println("---PP define params") last.Define(p.Name, anyValue(p.Type)) } // define results in new block. for i, rf := range ft.Results { - debug.Println("---PP define results") if 0 < len(rf.Name) { last.Define(rf.Name, anyValue(rf.Type)) } else { @@ -393,15 +376,6 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { last.Define(Name(rn), anyValue(rf.Type)) } } - - //debug.Println("funcLit pop c-----") - //pc := popClosure() - ////cnn.Closure = c - //closure := *pc - //n.SetClosure(closure) - //debug.Printf("---done FuncLit trans, fx: %v, closure: %+v \n", n, n.Closure.String()) - //debug.Println("---PP funcLitExpr end---") - // TRANS_BLOCK ----------------------- case *SelectCaseStmt: pushInitBlock(n, &last, &stack) @@ -663,9 +637,6 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { switch n := n.(type) { // TRANS_LEAVE ----------------------- case *NameExpr: - debug.Println("---PP NameExpr") - debug.Println("---PP NameExpr, ftype: ", ftype) - // Validity: check that name isn't reserved. if isReservedName(n.Name) { panic(fmt.Sprintf( @@ -1986,7 +1957,6 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } func pushInitBlock(bn BlockNode, last *BlockNode, stack *[]BlockNode) { - debugPP.Printf("pushInitBlock : %v \n", bn) if !bn.IsInitialized() { bn.InitStaticBlock(bn, *last) } else { @@ -3020,7 +2990,6 @@ func predefineNow2(store Store, last BlockNode, d Decl, m map[Name]struct{}) (De // must be called for name declarations within (non-file, // non-package) stmt bodies. func tryPredefine(store Store, last BlockNode, d Decl) (un Name) { - debug.Println("-----tryPredefine") if d.GetAttribute(ATTR_PREDEFINED) == true { panic("decl node already predefined!") } diff --git a/gnovm/pkg/gnolang/transcribe.go b/gnovm/pkg/gnolang/transcribe.go index a987e0c82ca..3f865badf2b 100644 --- a/gnovm/pkg/gnolang/transcribe.go +++ b/gnovm/pkg/gnolang/transcribe.go @@ -135,7 +135,6 @@ func Transcribe(n Node, t Transform) (nn Node) { } func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc *TransCtrl) (nn Node) { - debugPP.Printf("transcribe, n is: %v \n", n) // transcribe n on the way in. var c TransCtrl nn, c = t(ns, ftype, index, n, TRANS_ENTER) @@ -149,55 +148,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc // visit any children of n. switch cnn := nn.(type) { case *NameExpr: - debugPP.Printf("-----trans, nameExpr: %v \n", cnn) - //deps := make(map[Name]struct{}) - //findDependentNames(cnn, deps) - //for k, v := range deps { - // debugPP.Printf("%s depends on %v \n", k, v) - //} - - //if CX.hasClosure() { - // debugPP.Printf("---has Closure, check: %v \n", cnn) - // debugPP.Println("---currentOp: ", CX.peekOp()) - // debugPP.Println("---dump ops: ", CX.dumpOps()) - // // recording names defined in closure as a whitelist - // if CX.peekOp() == DEFINE || CX.peekOp() == ASSIGN { - // ao := CX.peekOperand() - // if ao != nil { // staff to do - // debugPP.Printf("ao is: %v \n", ao) - // // in scope of define/assign op, record to whitelist - // ao.counter += 1 - // // add nx to whitelist until resolved - // CX.whitelist[cnn.Name] = true - // debugPP.Println(CX.dumpWhitelist()) - // if ao.counter == ao.num { // all resolved - // //CX.clearWhiteList() - // CX.popOp() - // CX.popOperand() - // } - // } - // } - // // capture logic - // // not exist in whitelist, capture - // if _, ok := CX.whitelist[cnn.Name]; !ok { - // debugPP.Printf("nx need capture: %s \n", string(cnn.Name)) - // currentClo := CX.currentClosure() - // debugPP.Printf("currentClo: %v \n", currentClo) - // if currentClo != nil { // a closure to fill - // //if cnn.Path.Depth < 1 { // if local defined, no capture - // debugPP.Printf("---capture: %v \n", cnn) - // cnx := CapturedNx{ - // nx: cnn, - // offset: 0, - // } - // currentClo.Fill(cnx) - // CX.dumpClosures() - // //CX.dumpNodes() - // } - // } - //} else { - // debugPP.Println("---no closure in place") - //} case *BasicLitExpr: case *BinaryExpr: cnn.Left = transcribe(t, nns, TRANS_BINARY_LEFT, 0, cnn.Left, &c).(Expr) // XXX wished this worked with nil. @@ -310,8 +260,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc cnn.Elts[idx] = KeyValueExpr{Key: k, Value: v} } case *FuncLitExpr: - debug.Printf("-----trans, funcLitExpr: %v \n", cnn) - cnn.Type = *transcribe(t, nns, TRANS_FUNCLIT_TYPE, 0, &cnn.Type, &c).(*FuncTypeExpr) if isStopOrSkip(nc, c) { return @@ -411,7 +359,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } case *AssignStmt: - debugPP.Printf("---assignStmt: %v \n", cnn) for idx := range cnn.Lhs { cnn.Lhs[idx] = transcribe(t, nns, TRANS_ASSIGN_LHS, idx, cnn.Lhs[idx], &c).(Expr) if isBreak(c) { @@ -446,7 +393,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } case *BranchStmt: case *DeclStmt: - // CX.pushOp(ASSIGN) for idx := range cnn.Body { cnn.Body[idx] = transcribe(t, nns, TRANS_DECL_BODY, idx, cnn.Body[idx], &c).(SimpleDeclStmt) if isBreak(c) { @@ -455,7 +401,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } } - // CX.popOp() case *DeferStmt: cnn.Call = *transcribe(t, nns, TRANS_DEFER_CALL, 0, &cnn.Call, &c).(*CallExpr) if isStopOrSkip(nc, c) { @@ -509,7 +454,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } case *IfStmt: - debug.Println("-----trans, if stmt") // NOTE: like switch stmts, both if statements AND // contained cases visit with the TRANS_BLOCK stage, even // though during runtime only one block is created. @@ -542,7 +486,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } case *IfCaseStmt: - debug.Printf("-----trans, (if---case) stmt: %v \n", cnn) cnn2, c2 := t(ns, ftype, index, cnn, TRANS_BLOCK) if isStopOrSkip(nc, c2) { nn = cnn2 @@ -559,15 +502,12 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } } - // debug.Println("if-case pop c-----") - case *IncDecStmt: cnn.X = transcribe(t, nns, TRANS_INCDEC_X, 0, cnn.X, &c).(Expr) if isStopOrSkip(nc, c) { return } case *RangeStmt: - debugPP.Printf("---range stmt: %v \n", cnn) cnn2, c2 := t(ns, ftype, index, cnn, TRANS_BLOCK) if isStopOrSkip(nc, c2) { nn = cnn2 @@ -602,7 +542,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } } case *ReturnStmt: - debug.Printf("-----trans, return stmt: %v \n", cnn) for idx := range cnn.Results { cnn.Results[idx] = transcribe(t, nns, TRANS_RETURN_RESULT, idx, cnn.Results[idx], &c).(Expr) if isBreak(c) { @@ -652,7 +591,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } case *SwitchStmt: - debugPP.Printf("---switchStmt: %v \n", cnn) // NOTE: unlike the select case, and like if stmts, both // switch statements AND contained cases visit with the // TRANS_BLOCK stage, even though during runtime only one @@ -665,12 +603,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc cnn = cnn2.(*SwitchStmt) } - if cnn.IsTypeSwitch { - debugPP.Printf("is type switch, init is :%v \n", cnn.Init) - debugPP.Printf("is type switch, X is :%v \n", cnn.X) - debugPP.Printf("is type switch, varName is :%v \n", cnn.VarName) - } - if cnn.Init != nil { cnn.Init = transcribe(t, nns, TRANS_SWITCH_INIT, 0, cnn.Init, &c).(SimpleStmt) if isStopOrSkip(nc, c) { @@ -755,8 +687,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc case *ImportDecl: // nothing to do case *ValueDecl: - debugPP.Println("---value decl") - if cnn.Type != nil { cnn.Type = transcribe(t, nns, TRANS_VAR_TYPE, 0, cnn.Type, &c).(Expr) if isStopOrSkip(nc, c) { diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index b3d276c84f9..9051ad1b0f7 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -579,15 +579,6 @@ func (tsb *LoopValuesBox) getIndexByName(n Name) int { return -1 // never reach } -func (tsb *LoopValuesBox) isEmpty() bool { - for _, tt := range tsb.transient { - if tt != nil && tt.values != nil && len(tt.values) != 0 { - return false - } - } - return true -} - func (tsb *LoopValuesBox) String() string { var s string s += "\n" @@ -2337,14 +2328,11 @@ func (b *Block) Hash() string { } func (b *Block) UpdateValue(index int, tv TypedValue) { - debug.Printf("-----UpdateValue, index: %d, tv: %v \n", index, tv) - debug.Printf("b before update: %v \n", b) for i := range b.Values { if i == index { b.Values[i] = tv } } - debug.Printf("b after update: %v \n", b) } func (b *Block) String() string { @@ -2405,14 +2393,6 @@ func (b *Block) GetParent(store Store) *Block { } func (b *Block) GetPointerToInt(store Store, index int) PointerValue { - //defer func() { - // if r := recover(); r != nil { - // debugPP.Printf("r: %v \n", r) - // } - //}() - debugPP.Printf("-----GetPointerToInt, index: %d \n", index) - debugPP.Printf("b: %v \n", b) - debugPP.Printf("len of values: %v \n", len(b.Values)) vv := fillValueTV(store, &b.Values[index]) return PointerValue{ TV: vv, @@ -2429,8 +2409,6 @@ func (b *Block) GetBlockWithDepth(store Store, path ValuePath) *Block { } func (b *Block) GetPointerTo(store Store, path ValuePath) PointerValue { - debugPP.Printf("-----GetPointerTo, path : %v\n", path) - debugPP.Printf("b: %v \n", b) if path.IsBlockBlankPath() { if debug { if path.Name != "_" { @@ -2450,16 +2428,30 @@ func (b *Block) GetPointerTo(store Store, path ValuePath) PointerValue { // 0, it implies that b == uverse, and the condition // would fail as if it were 1. for i := uint8(1); i < path.Depth; i++ { - //if b == nil { - // fmt.Printf("-----GetPointerTo, path : %v\n", path) - // fmt.Printf("b: %v \n", b) - // panic("------b nil, depth is:" + strconv.Itoa(int(path.Depth))) - //} b = b.GetParent(store) } return b.GetPointerToInt(store, int(path.Index)) } +// find nearest block is loop and contains name of n +func findLoopBlockWithPath(store Store, b *Block, nx *NameExpr) (*Block, bool, uint8) { + var gen uint8 = 1 + for i := uint8(1); i < nx.Path.Depth; i++ { // find target block at certain depth + b = b.GetParent(store) + gen++ + } + + if b.GetBodyStmt().isLoop { // is loop + names := b.GetSource(store).GetBlockNames() + for _, name := range names { + if nx.Name == name { // find n in this block + return b, true, gen + } + } + } + return nil, false, gen +} + // Result is used has lhs for any assignments to "_". func (b *Block) GetBlankRef() *TypedValue { return &b.Blank diff --git a/gnovm/tests/files/closure10.gno b/gnovm/tests/files/closure10.gno new file mode 100644 index 00000000000..1a788d9e0b2 --- /dev/null +++ b/gnovm/tests/files/closure10.gno @@ -0,0 +1,26 @@ +package main + +import "fmt" + +func bar() func() func() int { + x := 1 + + // First level of closure, modifies x + return func() func() int { + //x++ + // Second level of closure, returns x + return func() int { + return x + } + } +} + +func main() { + f := bar() // f is the first-level closure + g := f() // g is the second-level closure, x is incremented here + + fmt.Println(g()) // prints the value of x after being modified by the first-level closure +} + +// Output: +// 1 diff --git a/gnovm/tests/files/closure10_a.gno b/gnovm/tests/files/closure10_a.gno new file mode 100644 index 00000000000..45ad06c6945 --- /dev/null +++ b/gnovm/tests/files/closure10_a.gno @@ -0,0 +1,26 @@ +package main + +import "fmt" + +func bar() func() func() int { + x := 1 + + // First level of closure, modifies x + return func() func() int { + x++ + // Second level of closure, returns x + return func() int { + return x + } + } +} + +func main() { + f := bar() // f is the first-level closure + g := f() // g is the second-level closure, x is incremented here + + fmt.Println(g()) // prints the value of x after being modified by the first-level closure +} + +// Output: +// 2 diff --git a/gnovm/tests/files/closure11.gno b/gnovm/tests/files/closure11.gno new file mode 100644 index 00000000000..1ea6e013b0d --- /dev/null +++ b/gnovm/tests/files/closure11.gno @@ -0,0 +1,25 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + f := func() int { + if true { + if true { + return x + } + } + return 0 + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/files/closure11_a.gno b/gnovm/tests/files/closure11_a.gno new file mode 100644 index 00000000000..2ce3df96097 --- /dev/null +++ b/gnovm/tests/files/closure11_a.gno @@ -0,0 +1,23 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + f := func() int { + if true { + return x + } + return 0 + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/files/closure11_b.gno b/gnovm/tests/files/closure11_b.gno new file mode 100644 index 00000000000..427c6f075f9 --- /dev/null +++ b/gnovm/tests/files/closure11_b.gno @@ -0,0 +1,27 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + y := 0 + f := func() int { + if true { + x += y + if true { + return x + } + } + return 0 + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/files/closure12.gno b/gnovm/tests/files/closure12.gno new file mode 100644 index 00000000000..44717e05cc0 --- /dev/null +++ b/gnovm/tests/files/closure12.gno @@ -0,0 +1,22 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + f := func() int { + { + return x + } + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/files/closure12_a.gno b/gnovm/tests/files/closure12_a.gno new file mode 100644 index 00000000000..1e3eb9fe815 --- /dev/null +++ b/gnovm/tests/files/closure12_a.gno @@ -0,0 +1,23 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + f := func() int { + for i := 0; i < 1; i++ { + x++ + } + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 1 +// 2 diff --git a/gnovm/tests/files/closure12_b.gno b/gnovm/tests/files/closure12_b.gno new file mode 100644 index 00000000000..4351ceaaf49 --- /dev/null +++ b/gnovm/tests/files/closure12_b.gno @@ -0,0 +1,24 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + s := []int{1, 2} + f := func() int { + for _, v := range s { + x += v + } + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 3 +// 4 diff --git a/gnovm/tests/files/closure12_c.gno b/gnovm/tests/files/closure12_c.gno new file mode 100644 index 00000000000..3e42e207649 --- /dev/null +++ b/gnovm/tests/files/closure12_c.gno @@ -0,0 +1,27 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + y := 1 + f := func() int { + switch y { + case 1: + x += 1 + default: + x += 0 + } + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 1 +// 2 diff --git a/gnovm/tests/files/closure12_e.gno b/gnovm/tests/files/closure12_e.gno new file mode 100644 index 00000000000..17490510b26 --- /dev/null +++ b/gnovm/tests/files/closure12_e.gno @@ -0,0 +1,27 @@ +package main + +type queueOnePass struct { + sparse []uint32 + dense []uint32 + size, nextIndex uint32 +} + +func newQueue(size int) (q *queueOnePass) { + return &queueOnePass{ + sparse: make([]uint32, size), + dense: make([]uint32, size), + } +} +func main() { + var ( + visitQueue = newQueue(10) + ) + f := func() { + println(visitQueue.size) + } + + f() +} + +// Output: +// 0 diff --git a/gnovm/tests/files/closure12_f.gno b/gnovm/tests/files/closure12_f.gno new file mode 100644 index 00000000000..60e327bd2b2 --- /dev/null +++ b/gnovm/tests/files/closure12_f.gno @@ -0,0 +1,20 @@ +package main + +func main() { + s := []int{1, 2} + + f := func() { + for i, v := range s { // TODO: exclude s, it's final + println(i) + println(v) + } + } + + f() +} + +// Output: +// 0 +// 1 +// 1 +// 2 diff --git a/gnovm/tests/files/closure12_g.gno b/gnovm/tests/files/closure12_g.gno new file mode 100644 index 00000000000..bf998c163d8 --- /dev/null +++ b/gnovm/tests/files/closure12_g.gno @@ -0,0 +1,14 @@ +package main + +func main() { + f := func(a int) bool { + println(a) + return true + } + + println(f(5)) +} + +// Output: +// 5 +// true diff --git a/gnovm/tests/files/closure12_h.gno b/gnovm/tests/files/closure12_h.gno new file mode 100644 index 00000000000..c9f1f6e97e8 --- /dev/null +++ b/gnovm/tests/files/closure12_h.gno @@ -0,0 +1,25 @@ +package main + +func main() { + s := 1 + f := func() { + i := 0 // no capture for i + var j = s // s should be captured, j not + k := s // s should be captured, k not + m, n := s, 0 + println(i) + println(j) + println(k) + println(m) + println(n) + } + + f() +} + +// Output: +// 0 +// 1 +// 1 +// 1 +// 0 diff --git a/gnovm/tests/files/closure12_i.gno b/gnovm/tests/files/closure12_i.gno new file mode 100644 index 00000000000..acf1b53b32a --- /dev/null +++ b/gnovm/tests/files/closure12_i.gno @@ -0,0 +1,15 @@ +package main + +func main() { + s := []int{1, 2} + + f := func() { + if len(s) == 2 { + println("ok") + } + } + f() +} + +// Output: +// ok diff --git a/gnovm/tests/files/closure13.gno b/gnovm/tests/files/closure13.gno new file mode 100644 index 00000000000..ece19dd87f6 --- /dev/null +++ b/gnovm/tests/files/closure13.gno @@ -0,0 +1,22 @@ +package main + +import "fmt" + +func main() { + // Define a function that returns a closure + var recursiveFunc func(int) int + recursiveFunc = func(num int) int { + if num <= 0 { + return 1 + } + // Closure calling itself recursively + return num * recursiveFunc(num-1) + } + + // Use the recursive closure + result := recursiveFunc(5) + fmt.Println("Factorial of 5 is:", result) // Output: 120 +} + +// Output: +// Factorial of 5 is: 120 diff --git a/gnovm/tests/files/closure13_a.gno b/gnovm/tests/files/closure13_a.gno new file mode 100644 index 00000000000..8345c9ed48d --- /dev/null +++ b/gnovm/tests/files/closure13_a.gno @@ -0,0 +1,24 @@ +package main + +import "fmt" + +func main() { + var recursiveFunc func(int) int + var recursiveFunc2 func(int) int + + recursiveFunc = func(num int) int { + recursiveFunc2 = recursiveFunc + + if num <= 0 { + return 1 + } + + return num * recursiveFunc2(num-1) + } + + result := recursiveFunc(5) + fmt.Println("Factorial of 5 is:", result) // Output: 120 +} + +// Output: +// Factorial of 5 is: 120 diff --git a/gnovm/tests/files/closure13_b0.gno b/gnovm/tests/files/closure13_b0.gno new file mode 100644 index 00000000000..4bfe864cc8e --- /dev/null +++ b/gnovm/tests/files/closure13_b0.gno @@ -0,0 +1,26 @@ +package main + +func main() { + var buf []byte + + // when eval this, buf is still nil, + expectRead := func(size int, expected string, err error) { + b := buf[0:size] + println(b) + println(len(buf)) + println(cap(buf)) + } + + // buf should be captured here, here it's volatile, not where it defined + // namely, the vp should point here, -> so mutate offset + withFooBar := func() { + buf = make([]byte, 20) + expectRead(5, "foo ", nil) + } + withFooBar() +} + +// Output: +// slice[0x0000000000] +// 20 +// 20 diff --git a/gnovm/tests/files/closure13_b1.gno b/gnovm/tests/files/closure13_b1.gno new file mode 100644 index 00000000000..9218ee16fa3 --- /dev/null +++ b/gnovm/tests/files/closure13_b1.gno @@ -0,0 +1,26 @@ +package main + +func main() { + var buf []byte + + // when eval this, buf is still nil, + expectRead := func(size int, expected string, eerr error) { + b := buf[0:size] + println(b) + } + + // buf should be captured here, here it's volatile, not where it defined + // namely, the vp should point here, -> so mutate offset + withFooBar := func() func() { + buf = make([]byte, 20) + return func() { + b := buf[0:4] + println(b) + } + } + withFooBar() + println("ok") +} + +// Output: +// ok diff --git a/gnovm/tests/files/closure13_b_stdlibs.gno b/gnovm/tests/files/closure13_b_stdlibs.gno new file mode 100644 index 00000000000..7d9d935652c --- /dev/null +++ b/gnovm/tests/files/closure13_b_stdlibs.gno @@ -0,0 +1,47 @@ +package main + +import ( + "fmt" + "io" + "strings" +) + +func main() { + var mr io.Reader + var buf []byte + nread := 0 + withFooBar := func(tests func()) { + r1 := strings.NewReader("foo ") + //r2 := strings.NewReader("") + //r3 := strings.NewReader("bar") + //mr = io.MultiReader(r1, r2, r3) + mr = io.MultiReader(r1) + buf = make([]byte, 20) + tests() + } + expectRead := func(size int, expected string, eerr error) { + nread++ + n, gerr := mr.Read(buf[0:size]) + if n != len(expected) { + panic(fmt.Sprintf("#%d, expected %d bytes; got %d", + nread, len(expected), n)) + } + got := string(buf[0:n]) + if got != expected { + panic(fmt.Sprintf("#%d, expected %q; got %q", + nread, expected, got)) + } + if gerr != eerr { + panic(fmt.Sprintf("#%d, expected error %v; got %v", + nread, eerr, gerr)) + } + buf = buf[n:] + } + withFooBar(func() { + expectRead(5, "foo ", nil) + }) + println("ok") +} + +// Output: +// ok diff --git a/gnovm/tests/files/closure14.gno b/gnovm/tests/files/closure14.gno new file mode 100644 index 00000000000..e6b89f9528a --- /dev/null +++ b/gnovm/tests/files/closure14.gno @@ -0,0 +1,26 @@ +package main + +import "fmt" + +func foo() (err error) { + defer func() { + if r := recover(); r != nil { + switch v := r.(type) { + case error: + err = v + default: + err = fmt.Errorf("%s", v) + } + } + }() + + panic("xxx") +} + +func main() { + err := foo() + println(err.Error()) +} + +// Output: +// xxx diff --git a/gnovm/tests/files/closure14_a.gno b/gnovm/tests/files/closure14_a.gno new file mode 100644 index 00000000000..706e2bd7e8b --- /dev/null +++ b/gnovm/tests/files/closure14_a.gno @@ -0,0 +1,27 @@ +package main + +import ( + "errors" +) + +func foo() (err error) { + y := 1 + defer func() { + if r := recover(); r != nil { + switch y { + case 1: + err = errors.New("ok") + default: + err = nil + } + } + }() + panic(y) +} + +func main() { + println(foo()) +} + +// Output: +// ok diff --git a/gnovm/tests/files/closure15.gno b/gnovm/tests/files/closure15.gno new file mode 100644 index 00000000000..86c8fc445f0 --- /dev/null +++ b/gnovm/tests/files/closure15.gno @@ -0,0 +1,32 @@ +package main + +import "fmt" + +func main() { + + var fns []func(int) int + var recursiveFunc func(int) int + + for i := 0; i < 6; i++ { + recursiveFunc = func(num int) int { + if num <= 0 { + return 1 + } + return num * recursiveFunc(num-1) + } + fns = append(fns, recursiveFunc) + } + + for i, r := range fns { + result := r(i) + fmt.Printf("Factorial of %d is: %d \n", i, result) + } +} + +// Output: +// Factorial of 0 is: 1 +// Factorial of 1 is: 1 +// Factorial of 2 is: 2 +// Factorial of 3 is: 6 +// Factorial of 4 is: 24 +// Factorial of 5 is: 120 diff --git a/gnovm/tests/files/closure15_a.gno b/gnovm/tests/files/closure15_a.gno new file mode 100644 index 00000000000..1983845bd81 --- /dev/null +++ b/gnovm/tests/files/closure15_a.gno @@ -0,0 +1,49 @@ +package main + +import "fmt" + +// recursive closure does not capture +func main() { + + var fns []func(int) int + var recursiveFunc func(int) int + + for i := 0; i < 3; i++ { + recursiveFunc = func(num int) int { + x := i + println("value of x: ", x) + if num <= 0 { + return 1 + } + return num * recursiveFunc(num-1) + } + fns = append(fns, recursiveFunc) + } + + for i, r := range fns { + result := r(i) + fmt.Printf("Factorial of %d is: %d \n", i, result) + } +} + +// Go Output: +// value of x: 3 +// Factorial of 0 is: 1 +// value of x: 3 +// value of x: 3 +// Factorial of 1 is: 1 +// value of x: 3 +// value of x: 3 +// value of x: 3 +// Factorial of 2 is: 2 + +// Output: +// value of x: 0 +// Factorial of 0 is: 1 +// value of x: 1 +// value of x: 2 +// Factorial of 1 is: 1 +// value of x: 2 +// value of x: 2 +// value of x: 2 +// Factorial of 2 is: 2 diff --git a/gnovm/tests/files/closure16.gno b/gnovm/tests/files/closure16.gno new file mode 100644 index 00000000000..8a472ffb410 --- /dev/null +++ b/gnovm/tests/files/closure16.gno @@ -0,0 +1,21 @@ +package main + +func main() { + var fns []func() int + s := []int{1, 2, 3} + for i, _ := range s { + x := i + f := func() int { + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 2 diff --git a/gnovm/tests/files/closure16_a.gno b/gnovm/tests/files/closure16_a.gno new file mode 100644 index 00000000000..ee375bc719c --- /dev/null +++ b/gnovm/tests/files/closure16_a.gno @@ -0,0 +1,20 @@ +package main + +func main() { + var fns []func() int + m := map[string]int{"a": 1, "b": 2} + for _, v := range m { + x := v + f := func() int { + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 1 +// 2 diff --git a/gnovm/tests/files/closure16_a1.gno b/gnovm/tests/files/closure16_a1.gno new file mode 100644 index 00000000000..857cc11d7da --- /dev/null +++ b/gnovm/tests/files/closure16_a1.gno @@ -0,0 +1,19 @@ +package main + +func main() { + var fns []func() int + m := map[string]int{"a": 1, "b": 2} + for _, v := range m { + f := func() int { + return v + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 1 +// 2 diff --git a/gnovm/tests/files/closure16_b.gno b/gnovm/tests/files/closure16_b.gno new file mode 100644 index 00000000000..5d26a2c6dc0 --- /dev/null +++ b/gnovm/tests/files/closure16_b.gno @@ -0,0 +1,23 @@ +package main + +func main() { + var fns []func() int + s := "hello" + for i, _ := range s { + x := i + f := func() int { + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 diff --git a/gnovm/tests/files/closure16_b1.gno b/gnovm/tests/files/closure16_b1.gno new file mode 100644 index 00000000000..a7ddb99504f --- /dev/null +++ b/gnovm/tests/files/closure16_b1.gno @@ -0,0 +1,22 @@ +package main + +func main() { + var fns []func() int + s := "hello" + for i, _ := range s { + f := func() int { + return i + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 diff --git a/gnovm/tests/files/closure17_io2.gno b/gnovm/tests/files/closure17_io2.gno new file mode 100644 index 00000000000..24655f5040c --- /dev/null +++ b/gnovm/tests/files/closure17_io2.gno @@ -0,0 +1,21 @@ +package main + +import ( + "fmt" + "io" + "log" + "strings" +) + +func main() { + r := strings.NewReader("Go is a general-purpose language designed with systems programming in mind.") + + b, err := io.ReadAll(r) + if err != nil { + log.Fatal(err) + } + fmt.Printf("%s", b) +} + +// Output: +// Go is a general-purpose language designed with systems programming in mind. diff --git a/gnovm/tests/files/closure17_recover4.gno b/gnovm/tests/files/closure17_recover4.gno new file mode 100644 index 00000000000..5a6da4261a2 --- /dev/null +++ b/gnovm/tests/files/closure17_recover4.gno @@ -0,0 +1,25 @@ +package main + +import "fmt" + +func div(a, b int) (result int) { + defer func() { + r := recover() + + fmt.Printf("r = %#v\n", r) + + if r != nil { + result = 0 + } + }() + + return a / b +} + +func main() { + println(div(30, 2)) +} + +// Output: +// r = +// 15 diff --git a/gnovm/tests/files/closure17_recover6.gno b/gnovm/tests/files/closure17_recover6.gno new file mode 100644 index 00000000000..0b304369764 --- /dev/null +++ b/gnovm/tests/files/closure17_recover6.gno @@ -0,0 +1,30 @@ +package main + +import ( + "errors" +) + +func main() { + println(f(false)) + println(f(true)) +} + +func f(dopanic bool) (err error) { + defer func() { + if x := recover(); x != nil { + err = x.(error) + } + }() + q(dopanic) + return +} + +func q(dopanic bool) { + if dopanic { + panic(errors.New("wtf")) + } +} + +// Output: +// undefined +// wtf diff --git a/gnovm/tests/files/closure17_recover6a.gno b/gnovm/tests/files/closure17_recover6a.gno new file mode 100644 index 00000000000..427fc4b74b9 --- /dev/null +++ b/gnovm/tests/files/closure17_recover6a.gno @@ -0,0 +1,40 @@ +package main + +import "errors" + +//func p(s interface{}) { +// fmt.Printf("%T \n", s) +// if v, ok := s.(string); ok { +// println(v) +// panic(v) +// } else { +// println("---") +// } +//} + +type error interface { + Error() string +} + +// New returns an error that formats as the given text. +// Each call to New returns a distinct error value even if the text is identical. +func New(text string) error { + return &errorString{text} +} + +// errorString is a trivial implementation of error. +type errorString struct { + s string +} + +func (e *errorString) Error() string { + return e.s +} + +func main() { + //panic(New("wtf")) + panic(errors.New("wtf")) +} + +// Error: +// wtf diff --git a/gnovm/tests/files/closure17_sort_search_efficiency.gno b/gnovm/tests/files/closure17_sort_search_efficiency.gno new file mode 100644 index 00000000000..90362944ac5 --- /dev/null +++ b/gnovm/tests/files/closure17_sort_search_efficiency.gno @@ -0,0 +1,21 @@ +package main + +func Search(n int, f func(int) bool) int { + f(1) + return 0 +} + +func main() { + for x := 0; x < 2; x++ { + count := 0 + println(" first: count: ", count) + Search(1, func(i int) bool { count++; return i >= x }) + println("second: count: ", count) + } +} + +// Output: +// first: count: 0 +// second: count: 1 +// first: count: 0 +// second: count: 1 diff --git a/gnovm/tests/files/closure17_zregexp_stdlibs.gno b/gnovm/tests/files/closure17_zregexp_stdlibs.gno new file mode 100644 index 00000000000..10bb6f937d3 --- /dev/null +++ b/gnovm/tests/files/closure17_zregexp_stdlibs.gno @@ -0,0 +1,19 @@ +// MAXALLOC: 100000000 +// max total allocation of 100 mb. +package main + +import "regexp" + +var reName = regexp.MustCompile(`^[a-z]+[_a-z0-9]*$`) + +func main() { + for j := 0; j < 100; j++ { + if !(reName.MatchString("thisisatestname")) { + panic("error") + } + } + println(true) +} + +// Output: +// true diff --git a/gnovm/tests/files/closure9.gno b/gnovm/tests/files/closure9.gno new file mode 100644 index 00000000000..e3a4917211f --- /dev/null +++ b/gnovm/tests/files/closure9.gno @@ -0,0 +1,30 @@ +package main + +// this is a fix, intuitive, and same with Go +func main() { + var fns []func() int + for i := 0; i < 5; i++ { + x := i + f := func() int { + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Go Output: +// 0 +// 1 +// 2 +// 3 +// 4 + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 diff --git a/gnovm/tests/files/closure9_a0.gno b/gnovm/tests/files/closure9_a0.gno new file mode 100644 index 00000000000..0f5ba6d6739 --- /dev/null +++ b/gnovm/tests/files/closure9_a0.gno @@ -0,0 +1,29 @@ +package main + +// this is a fix, intuitive, same with go loopVar experiment feature. +func main() { + var fns []func() int + for i := 0; i < 5; i++ { + f := func() int { + return i + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Go Output: +// 5 +// 5 +// 5 +// 5 +// 5 + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 diff --git a/gnovm/tests/files/closure9_a02.gno b/gnovm/tests/files/closure9_a02.gno new file mode 100644 index 00000000000..82ca810b13c --- /dev/null +++ b/gnovm/tests/files/closure9_a02.gno @@ -0,0 +1,20 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 5; i++ { + x := i + f := func() int { + return x + } + fns = append(fns, f) + } + println(fns[3]()) + println(fns[2]()) + println(fns[0]()) +} + +// Output: +// 3 +// 2 +// 0 diff --git a/gnovm/tests/files/closure9_a1.gno b/gnovm/tests/files/closure9_a1.gno new file mode 100644 index 00000000000..19951610e7e --- /dev/null +++ b/gnovm/tests/files/closure9_a1.gno @@ -0,0 +1,34 @@ +package main + +// this still keeps consistent with go, that a variable out of loop block is not captured +// this is unintuitive(should capture something) +// TODO: leave it for discuss. + +func main() { + var fns []func() int + var x int + for i := 0; i < 5; i++ { + x = i + f := func() int { + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Go Output: +// 4 +// 4 +// 4 +// 4 +// 4 + +// Output: +// 4 +// 4 +// 4 +// 4 +// 4 diff --git a/gnovm/tests/files/closure9_b.gno b/gnovm/tests/files/closure9_b.gno new file mode 100644 index 00000000000..0822368fe3a --- /dev/null +++ b/gnovm/tests/files/closure9_b.gno @@ -0,0 +1,22 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 2; i++ { + x := i + y := 0 + f := func() int { + x += y + x += 1 + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 1 +// 2 diff --git a/gnovm/tests/files/closure9_c.gno b/gnovm/tests/files/closure9_c.gno new file mode 100644 index 00000000000..e5c61c59089 --- /dev/null +++ b/gnovm/tests/files/closure9_c.gno @@ -0,0 +1,18 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 2; i++ { + f := func() int { + return i + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/files/closure9_d.gno b/gnovm/tests/files/closure9_d.gno new file mode 100644 index 00000000000..185961c741f --- /dev/null +++ b/gnovm/tests/files/closure9_d.gno @@ -0,0 +1,29 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 5; i++ { + x := i + f := func() int { + x := 5 + return x + } + println(x) + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 +// 5 +// 5 +// 5 +// 5 +// 5 diff --git a/gnovm/tests/files/closure9_e.gno b/gnovm/tests/files/closure9_e.gno new file mode 100644 index 00000000000..b3b2b2b0385 --- /dev/null +++ b/gnovm/tests/files/closure9_e.gno @@ -0,0 +1,12 @@ +package main + +func main() { + for i := 0; i < 5; i++ { + if i == 1 { + panic("error 1") + } + } +} + +// Error: +// error 1 diff --git a/gnovm/tests/files/closure9_f.gno b/gnovm/tests/files/closure9_f.gno new file mode 100644 index 00000000000..19722ec3ae6 --- /dev/null +++ b/gnovm/tests/files/closure9_f.gno @@ -0,0 +1,53 @@ +package main + +// 1. we can determine target vars by hasClosure pattern and externNames(without uverse). +// it can be a loopvar, or vars derived from loopvar, e.g. x := i; +// 2. now we need to capture every transient state of the target var; firstly we should +// eval the transient state, there are two ways around: +// a) eval funcLit, but the time doing this is hard to determine, namely, you have to eval +// before it leaves its using context. In some sense, you have to cover all cases when a funcValue +// is used, like assign, xxxLit, funcCall as an arg, etc. +// b) another way for this is to generate a `time series` values that the index is the loop index, +// e.g. in a for loop, we define a new var like x := i, we store the transient state of x per loop +// in the time series, which is a slice. This slice is used when the closure fv is called, +// replacing the var in default block.(func value have a slice of name indicates it's captured, when +// eval name in this slice, using the time series instead). +// this solution mainly differs that it eval target x independently with the closure funcLit, so it avoids +// the obsession to determine the eval order. this seems the right way. hmmm. + +//======================================================================================================== +// work flow 1.0, hard to do +// 1. determine loop var, whole logic depends on this, the key word is dynamic, it causes polymorphic. +// this should most be done via define(), // TODO: check it +// 2. in transcribe, find if loopvar is used somewhere, to identify pattern like x := i, what to do with +// other use cases? this brings complexity. This is wrong feel. +// we can generate the ts slice for every target var, e.g. x in this case. if two vars, two slices. so fv should +// reference a slice of slice. + +// work flow 2.0 +// 1. ignore loop var, assume all externNames apart from uverse is target captured var, this also needs to happen +// in transcribe, tag it, and can be checked everywhere it's appears in runtime, x := i, or var x int, etc. +// when eval funcLit, new a slice of slice and push value for further update. + +func main() { + var fns []func() int + for i := 0; i < 5; i++ { + //x := i // check if x is polymorphic, if yes, new a slice and set value by index + var x int // new a slice no init, this would be updated later, so the slice can be mutated after opFuncLit + f := func() int { + return x // set the slice to fv in + } + x = i // check if x is polymorphic, is yes, update slice assigned to fv before this, which is a reference. + fns = append(fns, f) // where should eval funcLit, where it assigned somewhere, or used in another func lit + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 diff --git a/gnovm/tests/files/closure9_g.gno b/gnovm/tests/files/closure9_g.gno new file mode 100644 index 00000000000..e7a6b64f1f3 --- /dev/null +++ b/gnovm/tests/files/closure9_g.gno @@ -0,0 +1,24 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 5; i++ { + x := i + { + f := func() int { + return x + } + fns = append(fns, f) + } + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 diff --git a/gnovm/tests/files/closure9_h.gno b/gnovm/tests/files/closure9_h.gno new file mode 100644 index 00000000000..23cb5ff9f40 --- /dev/null +++ b/gnovm/tests/files/closure9_h.gno @@ -0,0 +1,30 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 2; i++ { + x := i + for j := 0; j < 2; j++ { + y := j + f := func() int { + return x + y + } + fns = append(fns, f) + } + } + for _, fn := range fns { + println(fn()) + } +} + +// Go Output: +// 0 +// 1 +// 1 +// 2 + +// Output: +// 0 +// 1 +// 1 +// 2 diff --git a/gnovm/tests/files/closure9_h_0.gno b/gnovm/tests/files/closure9_h_0.gno new file mode 100644 index 00000000000..d60fa8515a9 --- /dev/null +++ b/gnovm/tests/files/closure9_h_0.gno @@ -0,0 +1,28 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 2; i++ { + for j := 0; j < 2; j++ { + f := func() int { + return i + j + } + fns = append(fns, f) + } + } + for _, fn := range fns { + println(fn()) + } +} + +// Go Output: +// 4 +// 4 +// 4 +// 4 + +// Output: +// 0 +// 1 +// 1 +// 2 diff --git a/gnovm/tests/files/closure9_i.gno b/gnovm/tests/files/closure9_i.gno new file mode 100644 index 00000000000..42a4d11d862 --- /dev/null +++ b/gnovm/tests/files/closure9_i.gno @@ -0,0 +1,31 @@ +package main + +func main() { + var fns []func() int + var x int + for i := 0; i < 2; i++ { + x = i + for j := 0; j < 2; j++ { + y := j + f := func() int { + return x + y + } + fns = append(fns, f) + } + } + for _, fn := range fns { + println(fn()) + } +} + +// Go Output: +// 1 +// 2 +// 1 +// 2 + +// Output: +// 1 +// 2 +// 1 +// 2 diff --git a/gnovm/tests/files/closure9_j.gno b/gnovm/tests/files/closure9_j.gno new file mode 100644 index 00000000000..926caf5090c --- /dev/null +++ b/gnovm/tests/files/closure9_j.gno @@ -0,0 +1,21 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 2; i++ { + x := i + y := 0 + f := func() int { + x += y + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 From a33899a8c1b97517f619a55cec8ba6800cea4e91 Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Fri, 26 Jan 2024 02:04:33 +0800 Subject: [PATCH 19/32] clean --- gnovm/tests/debug/closure0.gno | 17 ------ gnovm/tests/debug/closure1.gno | 20 ------- gnovm/tests/debug/closure10.gno | 26 --------- gnovm/tests/debug/closure10_a.gno | 26 --------- gnovm/tests/debug/closure11.gno | 25 --------- gnovm/tests/debug/closure11_a.gno | 23 -------- gnovm/tests/debug/closure11_b.gno | 27 ---------- gnovm/tests/debug/closure12.gno | 22 -------- gnovm/tests/debug/closure12_a.gno | 23 -------- gnovm/tests/debug/closure12_b.gno | 24 --------- gnovm/tests/debug/closure12_c.gno | 27 ---------- gnovm/tests/debug/closure12_e.gno | 27 ---------- gnovm/tests/debug/closure12_f.gno | 20 ------- gnovm/tests/debug/closure12_g.gno | 14 ----- gnovm/tests/debug/closure12_h.gno | 25 --------- gnovm/tests/debug/closure12_i.gno | 15 ------ gnovm/tests/debug/closure13.gno | 22 -------- gnovm/tests/debug/closure13_a.gno | 24 --------- gnovm/tests/debug/closure13_b.gno | 47 ---------------- gnovm/tests/debug/closure13_b0.gno | 26 --------- gnovm/tests/debug/closure13_b1.gno | 26 --------- gnovm/tests/debug/closure14.gno | 26 --------- gnovm/tests/debug/closure14_a.gno | 27 ---------- gnovm/tests/debug/closure15.gno | 32 ----------- gnovm/tests/debug/closure15_a.gno | 49 ----------------- gnovm/tests/debug/closure16.gno | 21 -------- gnovm/tests/debug/closure16_a.gno | 20 ------- gnovm/tests/debug/closure16_a1.gno | 19 ------- gnovm/tests/debug/closure16_b.gno | 23 -------- gnovm/tests/debug/closure16_b1.gno | 22 -------- gnovm/tests/debug/closure17_io2.gno | 21 -------- gnovm/tests/debug/closure17_recover4.gno | 25 --------- gnovm/tests/debug/closure17_recover6.gno | 30 ----------- gnovm/tests/debug/closure17_recover6a.gno | 40 -------------- .../closure17_sort_search_efficiency.gno | 21 -------- .../tests/debug/closure17_zregexp_stdlibs.gno | 19 ------- gnovm/tests/debug/closure2.gno | 28 ---------- gnovm/tests/debug/closure3.gno | 23 -------- gnovm/tests/debug/closure4.gno | 23 -------- gnovm/tests/debug/closure5.gno | 23 -------- gnovm/tests/debug/closure6.gno | 23 -------- gnovm/tests/debug/closure7.gno | 28 ---------- gnovm/tests/debug/closure8.gno | 10 ---- gnovm/tests/debug/closure9.gno | 30 ----------- gnovm/tests/debug/closure9_a0.gno | 29 ---------- gnovm/tests/debug/closure9_a02.gno | 20 ------- gnovm/tests/debug/closure9_a1.gno | 34 ------------ gnovm/tests/debug/closure9_b.gno | 22 -------- gnovm/tests/debug/closure9_c.gno | 18 ------- gnovm/tests/debug/closure9_d.gno | 29 ---------- gnovm/tests/debug/closure9_e.gno | 12 ----- gnovm/tests/debug/closure9_f.gno | 53 ------------------- gnovm/tests/debug/closure9_g.gno | 24 --------- gnovm/tests/debug/closure9_h.gno | 30 ----------- gnovm/tests/debug/closure9_h_0.gno | 28 ---------- gnovm/tests/debug/closure9_i.gno | 31 ----------- gnovm/tests/debug/closure9_j.gno | 21 -------- 57 files changed, 1440 deletions(-) delete mode 100644 gnovm/tests/debug/closure0.gno delete mode 100644 gnovm/tests/debug/closure1.gno delete mode 100644 gnovm/tests/debug/closure10.gno delete mode 100644 gnovm/tests/debug/closure10_a.gno delete mode 100644 gnovm/tests/debug/closure11.gno delete mode 100644 gnovm/tests/debug/closure11_a.gno delete mode 100644 gnovm/tests/debug/closure11_b.gno delete mode 100644 gnovm/tests/debug/closure12.gno delete mode 100644 gnovm/tests/debug/closure12_a.gno delete mode 100644 gnovm/tests/debug/closure12_b.gno delete mode 100644 gnovm/tests/debug/closure12_c.gno delete mode 100644 gnovm/tests/debug/closure12_e.gno delete mode 100644 gnovm/tests/debug/closure12_f.gno delete mode 100644 gnovm/tests/debug/closure12_g.gno delete mode 100644 gnovm/tests/debug/closure12_h.gno delete mode 100644 gnovm/tests/debug/closure12_i.gno delete mode 100644 gnovm/tests/debug/closure13.gno delete mode 100644 gnovm/tests/debug/closure13_a.gno delete mode 100644 gnovm/tests/debug/closure13_b.gno delete mode 100644 gnovm/tests/debug/closure13_b0.gno delete mode 100644 gnovm/tests/debug/closure13_b1.gno delete mode 100644 gnovm/tests/debug/closure14.gno delete mode 100644 gnovm/tests/debug/closure14_a.gno delete mode 100644 gnovm/tests/debug/closure15.gno delete mode 100644 gnovm/tests/debug/closure15_a.gno delete mode 100644 gnovm/tests/debug/closure16.gno delete mode 100644 gnovm/tests/debug/closure16_a.gno delete mode 100644 gnovm/tests/debug/closure16_a1.gno delete mode 100644 gnovm/tests/debug/closure16_b.gno delete mode 100644 gnovm/tests/debug/closure16_b1.gno delete mode 100644 gnovm/tests/debug/closure17_io2.gno delete mode 100644 gnovm/tests/debug/closure17_recover4.gno delete mode 100644 gnovm/tests/debug/closure17_recover6.gno delete mode 100644 gnovm/tests/debug/closure17_recover6a.gno delete mode 100644 gnovm/tests/debug/closure17_sort_search_efficiency.gno delete mode 100644 gnovm/tests/debug/closure17_zregexp_stdlibs.gno delete mode 100644 gnovm/tests/debug/closure2.gno delete mode 100644 gnovm/tests/debug/closure3.gno delete mode 100644 gnovm/tests/debug/closure4.gno delete mode 100644 gnovm/tests/debug/closure5.gno delete mode 100644 gnovm/tests/debug/closure6.gno delete mode 100644 gnovm/tests/debug/closure7.gno delete mode 100644 gnovm/tests/debug/closure8.gno delete mode 100644 gnovm/tests/debug/closure9.gno delete mode 100644 gnovm/tests/debug/closure9_a0.gno delete mode 100644 gnovm/tests/debug/closure9_a02.gno delete mode 100644 gnovm/tests/debug/closure9_a1.gno delete mode 100644 gnovm/tests/debug/closure9_b.gno delete mode 100644 gnovm/tests/debug/closure9_c.gno delete mode 100644 gnovm/tests/debug/closure9_d.gno delete mode 100644 gnovm/tests/debug/closure9_e.gno delete mode 100644 gnovm/tests/debug/closure9_f.gno delete mode 100644 gnovm/tests/debug/closure9_g.gno delete mode 100644 gnovm/tests/debug/closure9_h.gno delete mode 100644 gnovm/tests/debug/closure9_h_0.gno delete mode 100644 gnovm/tests/debug/closure9_i.gno delete mode 100644 gnovm/tests/debug/closure9_j.gno diff --git a/gnovm/tests/debug/closure0.gno b/gnovm/tests/debug/closure0.gno deleted file mode 100644 index acc1abfd404..00000000000 --- a/gnovm/tests/debug/closure0.gno +++ /dev/null @@ -1,17 +0,0 @@ -package main - -type adder func(int, int) int - -func genAdd(k int) adder { - return func(i, j int) int { - return i + j + k - } -} - -func main() { - f := genAdd(5) - println(f(3, 4)) -} - -// Output: -// 12 diff --git a/gnovm/tests/debug/closure1.gno b/gnovm/tests/debug/closure1.gno deleted file mode 100644 index f0bbda7ca1e..00000000000 --- a/gnovm/tests/debug/closure1.gno +++ /dev/null @@ -1,20 +0,0 @@ -package main - -type adder func(int, int) int - -func genAdd(k int) adder { - return func(i, j int) int { - return i + j + k - } -} - -func main() { - f := genAdd(5) - g := genAdd(8) - println(f(3, 4)) - println(g(3, 4)) -} - -// Output: -// 12 -// 15 diff --git a/gnovm/tests/debug/closure10.gno b/gnovm/tests/debug/closure10.gno deleted file mode 100644 index 1a788d9e0b2..00000000000 --- a/gnovm/tests/debug/closure10.gno +++ /dev/null @@ -1,26 +0,0 @@ -package main - -import "fmt" - -func bar() func() func() int { - x := 1 - - // First level of closure, modifies x - return func() func() int { - //x++ - // Second level of closure, returns x - return func() int { - return x - } - } -} - -func main() { - f := bar() // f is the first-level closure - g := f() // g is the second-level closure, x is incremented here - - fmt.Println(g()) // prints the value of x after being modified by the first-level closure -} - -// Output: -// 1 diff --git a/gnovm/tests/debug/closure10_a.gno b/gnovm/tests/debug/closure10_a.gno deleted file mode 100644 index 45ad06c6945..00000000000 --- a/gnovm/tests/debug/closure10_a.gno +++ /dev/null @@ -1,26 +0,0 @@ -package main - -import "fmt" - -func bar() func() func() int { - x := 1 - - // First level of closure, modifies x - return func() func() int { - x++ - // Second level of closure, returns x - return func() int { - return x - } - } -} - -func main() { - f := bar() // f is the first-level closure - g := f() // g is the second-level closure, x is incremented here - - fmt.Println(g()) // prints the value of x after being modified by the first-level closure -} - -// Output: -// 2 diff --git a/gnovm/tests/debug/closure11.gno b/gnovm/tests/debug/closure11.gno deleted file mode 100644 index 1ea6e013b0d..00000000000 --- a/gnovm/tests/debug/closure11.gno +++ /dev/null @@ -1,25 +0,0 @@ -package main - -func main() { - var fns []func() int - - for i := 0; i < 2; i++ { - x := i - f := func() int { - if true { - if true { - return x - } - } - return 0 - } - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 0 -// 1 diff --git a/gnovm/tests/debug/closure11_a.gno b/gnovm/tests/debug/closure11_a.gno deleted file mode 100644 index 2ce3df96097..00000000000 --- a/gnovm/tests/debug/closure11_a.gno +++ /dev/null @@ -1,23 +0,0 @@ -package main - -func main() { - var fns []func() int - - for i := 0; i < 2; i++ { - x := i - f := func() int { - if true { - return x - } - return 0 - } - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 0 -// 1 diff --git a/gnovm/tests/debug/closure11_b.gno b/gnovm/tests/debug/closure11_b.gno deleted file mode 100644 index 427c6f075f9..00000000000 --- a/gnovm/tests/debug/closure11_b.gno +++ /dev/null @@ -1,27 +0,0 @@ -package main - -func main() { - var fns []func() int - - for i := 0; i < 2; i++ { - x := i - y := 0 - f := func() int { - if true { - x += y - if true { - return x - } - } - return 0 - } - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 0 -// 1 diff --git a/gnovm/tests/debug/closure12.gno b/gnovm/tests/debug/closure12.gno deleted file mode 100644 index 44717e05cc0..00000000000 --- a/gnovm/tests/debug/closure12.gno +++ /dev/null @@ -1,22 +0,0 @@ -package main - -func main() { - var fns []func() int - - for i := 0; i < 2; i++ { - x := i - f := func() int { - { - return x - } - } - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 0 -// 1 diff --git a/gnovm/tests/debug/closure12_a.gno b/gnovm/tests/debug/closure12_a.gno deleted file mode 100644 index 1e3eb9fe815..00000000000 --- a/gnovm/tests/debug/closure12_a.gno +++ /dev/null @@ -1,23 +0,0 @@ -package main - -func main() { - var fns []func() int - - for i := 0; i < 2; i++ { - x := i - f := func() int { - for i := 0; i < 1; i++ { - x++ - } - return x - } - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 1 -// 2 diff --git a/gnovm/tests/debug/closure12_b.gno b/gnovm/tests/debug/closure12_b.gno deleted file mode 100644 index 4351ceaaf49..00000000000 --- a/gnovm/tests/debug/closure12_b.gno +++ /dev/null @@ -1,24 +0,0 @@ -package main - -func main() { - var fns []func() int - - for i := 0; i < 2; i++ { - x := i - s := []int{1, 2} - f := func() int { - for _, v := range s { - x += v - } - return x - } - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 3 -// 4 diff --git a/gnovm/tests/debug/closure12_c.gno b/gnovm/tests/debug/closure12_c.gno deleted file mode 100644 index 3e42e207649..00000000000 --- a/gnovm/tests/debug/closure12_c.gno +++ /dev/null @@ -1,27 +0,0 @@ -package main - -func main() { - var fns []func() int - - for i := 0; i < 2; i++ { - x := i - y := 1 - f := func() int { - switch y { - case 1: - x += 1 - default: - x += 0 - } - return x - } - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 1 -// 2 diff --git a/gnovm/tests/debug/closure12_e.gno b/gnovm/tests/debug/closure12_e.gno deleted file mode 100644 index 17490510b26..00000000000 --- a/gnovm/tests/debug/closure12_e.gno +++ /dev/null @@ -1,27 +0,0 @@ -package main - -type queueOnePass struct { - sparse []uint32 - dense []uint32 - size, nextIndex uint32 -} - -func newQueue(size int) (q *queueOnePass) { - return &queueOnePass{ - sparse: make([]uint32, size), - dense: make([]uint32, size), - } -} -func main() { - var ( - visitQueue = newQueue(10) - ) - f := func() { - println(visitQueue.size) - } - - f() -} - -// Output: -// 0 diff --git a/gnovm/tests/debug/closure12_f.gno b/gnovm/tests/debug/closure12_f.gno deleted file mode 100644 index 60e327bd2b2..00000000000 --- a/gnovm/tests/debug/closure12_f.gno +++ /dev/null @@ -1,20 +0,0 @@ -package main - -func main() { - s := []int{1, 2} - - f := func() { - for i, v := range s { // TODO: exclude s, it's final - println(i) - println(v) - } - } - - f() -} - -// Output: -// 0 -// 1 -// 1 -// 2 diff --git a/gnovm/tests/debug/closure12_g.gno b/gnovm/tests/debug/closure12_g.gno deleted file mode 100644 index bf998c163d8..00000000000 --- a/gnovm/tests/debug/closure12_g.gno +++ /dev/null @@ -1,14 +0,0 @@ -package main - -func main() { - f := func(a int) bool { - println(a) - return true - } - - println(f(5)) -} - -// Output: -// 5 -// true diff --git a/gnovm/tests/debug/closure12_h.gno b/gnovm/tests/debug/closure12_h.gno deleted file mode 100644 index c9f1f6e97e8..00000000000 --- a/gnovm/tests/debug/closure12_h.gno +++ /dev/null @@ -1,25 +0,0 @@ -package main - -func main() { - s := 1 - f := func() { - i := 0 // no capture for i - var j = s // s should be captured, j not - k := s // s should be captured, k not - m, n := s, 0 - println(i) - println(j) - println(k) - println(m) - println(n) - } - - f() -} - -// Output: -// 0 -// 1 -// 1 -// 1 -// 0 diff --git a/gnovm/tests/debug/closure12_i.gno b/gnovm/tests/debug/closure12_i.gno deleted file mode 100644 index acf1b53b32a..00000000000 --- a/gnovm/tests/debug/closure12_i.gno +++ /dev/null @@ -1,15 +0,0 @@ -package main - -func main() { - s := []int{1, 2} - - f := func() { - if len(s) == 2 { - println("ok") - } - } - f() -} - -// Output: -// ok diff --git a/gnovm/tests/debug/closure13.gno b/gnovm/tests/debug/closure13.gno deleted file mode 100644 index ece19dd87f6..00000000000 --- a/gnovm/tests/debug/closure13.gno +++ /dev/null @@ -1,22 +0,0 @@ -package main - -import "fmt" - -func main() { - // Define a function that returns a closure - var recursiveFunc func(int) int - recursiveFunc = func(num int) int { - if num <= 0 { - return 1 - } - // Closure calling itself recursively - return num * recursiveFunc(num-1) - } - - // Use the recursive closure - result := recursiveFunc(5) - fmt.Println("Factorial of 5 is:", result) // Output: 120 -} - -// Output: -// Factorial of 5 is: 120 diff --git a/gnovm/tests/debug/closure13_a.gno b/gnovm/tests/debug/closure13_a.gno deleted file mode 100644 index 8345c9ed48d..00000000000 --- a/gnovm/tests/debug/closure13_a.gno +++ /dev/null @@ -1,24 +0,0 @@ -package main - -import "fmt" - -func main() { - var recursiveFunc func(int) int - var recursiveFunc2 func(int) int - - recursiveFunc = func(num int) int { - recursiveFunc2 = recursiveFunc - - if num <= 0 { - return 1 - } - - return num * recursiveFunc2(num-1) - } - - result := recursiveFunc(5) - fmt.Println("Factorial of 5 is:", result) // Output: 120 -} - -// Output: -// Factorial of 5 is: 120 diff --git a/gnovm/tests/debug/closure13_b.gno b/gnovm/tests/debug/closure13_b.gno deleted file mode 100644 index 7d9d935652c..00000000000 --- a/gnovm/tests/debug/closure13_b.gno +++ /dev/null @@ -1,47 +0,0 @@ -package main - -import ( - "fmt" - "io" - "strings" -) - -func main() { - var mr io.Reader - var buf []byte - nread := 0 - withFooBar := func(tests func()) { - r1 := strings.NewReader("foo ") - //r2 := strings.NewReader("") - //r3 := strings.NewReader("bar") - //mr = io.MultiReader(r1, r2, r3) - mr = io.MultiReader(r1) - buf = make([]byte, 20) - tests() - } - expectRead := func(size int, expected string, eerr error) { - nread++ - n, gerr := mr.Read(buf[0:size]) - if n != len(expected) { - panic(fmt.Sprintf("#%d, expected %d bytes; got %d", - nread, len(expected), n)) - } - got := string(buf[0:n]) - if got != expected { - panic(fmt.Sprintf("#%d, expected %q; got %q", - nread, expected, got)) - } - if gerr != eerr { - panic(fmt.Sprintf("#%d, expected error %v; got %v", - nread, eerr, gerr)) - } - buf = buf[n:] - } - withFooBar(func() { - expectRead(5, "foo ", nil) - }) - println("ok") -} - -// Output: -// ok diff --git a/gnovm/tests/debug/closure13_b0.gno b/gnovm/tests/debug/closure13_b0.gno deleted file mode 100644 index 4bfe864cc8e..00000000000 --- a/gnovm/tests/debug/closure13_b0.gno +++ /dev/null @@ -1,26 +0,0 @@ -package main - -func main() { - var buf []byte - - // when eval this, buf is still nil, - expectRead := func(size int, expected string, err error) { - b := buf[0:size] - println(b) - println(len(buf)) - println(cap(buf)) - } - - // buf should be captured here, here it's volatile, not where it defined - // namely, the vp should point here, -> so mutate offset - withFooBar := func() { - buf = make([]byte, 20) - expectRead(5, "foo ", nil) - } - withFooBar() -} - -// Output: -// slice[0x0000000000] -// 20 -// 20 diff --git a/gnovm/tests/debug/closure13_b1.gno b/gnovm/tests/debug/closure13_b1.gno deleted file mode 100644 index 9218ee16fa3..00000000000 --- a/gnovm/tests/debug/closure13_b1.gno +++ /dev/null @@ -1,26 +0,0 @@ -package main - -func main() { - var buf []byte - - // when eval this, buf is still nil, - expectRead := func(size int, expected string, eerr error) { - b := buf[0:size] - println(b) - } - - // buf should be captured here, here it's volatile, not where it defined - // namely, the vp should point here, -> so mutate offset - withFooBar := func() func() { - buf = make([]byte, 20) - return func() { - b := buf[0:4] - println(b) - } - } - withFooBar() - println("ok") -} - -// Output: -// ok diff --git a/gnovm/tests/debug/closure14.gno b/gnovm/tests/debug/closure14.gno deleted file mode 100644 index e6b89f9528a..00000000000 --- a/gnovm/tests/debug/closure14.gno +++ /dev/null @@ -1,26 +0,0 @@ -package main - -import "fmt" - -func foo() (err error) { - defer func() { - if r := recover(); r != nil { - switch v := r.(type) { - case error: - err = v - default: - err = fmt.Errorf("%s", v) - } - } - }() - - panic("xxx") -} - -func main() { - err := foo() - println(err.Error()) -} - -// Output: -// xxx diff --git a/gnovm/tests/debug/closure14_a.gno b/gnovm/tests/debug/closure14_a.gno deleted file mode 100644 index 706e2bd7e8b..00000000000 --- a/gnovm/tests/debug/closure14_a.gno +++ /dev/null @@ -1,27 +0,0 @@ -package main - -import ( - "errors" -) - -func foo() (err error) { - y := 1 - defer func() { - if r := recover(); r != nil { - switch y { - case 1: - err = errors.New("ok") - default: - err = nil - } - } - }() - panic(y) -} - -func main() { - println(foo()) -} - -// Output: -// ok diff --git a/gnovm/tests/debug/closure15.gno b/gnovm/tests/debug/closure15.gno deleted file mode 100644 index 86c8fc445f0..00000000000 --- a/gnovm/tests/debug/closure15.gno +++ /dev/null @@ -1,32 +0,0 @@ -package main - -import "fmt" - -func main() { - - var fns []func(int) int - var recursiveFunc func(int) int - - for i := 0; i < 6; i++ { - recursiveFunc = func(num int) int { - if num <= 0 { - return 1 - } - return num * recursiveFunc(num-1) - } - fns = append(fns, recursiveFunc) - } - - for i, r := range fns { - result := r(i) - fmt.Printf("Factorial of %d is: %d \n", i, result) - } -} - -// Output: -// Factorial of 0 is: 1 -// Factorial of 1 is: 1 -// Factorial of 2 is: 2 -// Factorial of 3 is: 6 -// Factorial of 4 is: 24 -// Factorial of 5 is: 120 diff --git a/gnovm/tests/debug/closure15_a.gno b/gnovm/tests/debug/closure15_a.gno deleted file mode 100644 index 1983845bd81..00000000000 --- a/gnovm/tests/debug/closure15_a.gno +++ /dev/null @@ -1,49 +0,0 @@ -package main - -import "fmt" - -// recursive closure does not capture -func main() { - - var fns []func(int) int - var recursiveFunc func(int) int - - for i := 0; i < 3; i++ { - recursiveFunc = func(num int) int { - x := i - println("value of x: ", x) - if num <= 0 { - return 1 - } - return num * recursiveFunc(num-1) - } - fns = append(fns, recursiveFunc) - } - - for i, r := range fns { - result := r(i) - fmt.Printf("Factorial of %d is: %d \n", i, result) - } -} - -// Go Output: -// value of x: 3 -// Factorial of 0 is: 1 -// value of x: 3 -// value of x: 3 -// Factorial of 1 is: 1 -// value of x: 3 -// value of x: 3 -// value of x: 3 -// Factorial of 2 is: 2 - -// Output: -// value of x: 0 -// Factorial of 0 is: 1 -// value of x: 1 -// value of x: 2 -// Factorial of 1 is: 1 -// value of x: 2 -// value of x: 2 -// value of x: 2 -// Factorial of 2 is: 2 diff --git a/gnovm/tests/debug/closure16.gno b/gnovm/tests/debug/closure16.gno deleted file mode 100644 index 8a472ffb410..00000000000 --- a/gnovm/tests/debug/closure16.gno +++ /dev/null @@ -1,21 +0,0 @@ -package main - -func main() { - var fns []func() int - s := []int{1, 2, 3} - for i, _ := range s { - x := i - f := func() int { - return x - } - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 0 -// 1 -// 2 diff --git a/gnovm/tests/debug/closure16_a.gno b/gnovm/tests/debug/closure16_a.gno deleted file mode 100644 index ee375bc719c..00000000000 --- a/gnovm/tests/debug/closure16_a.gno +++ /dev/null @@ -1,20 +0,0 @@ -package main - -func main() { - var fns []func() int - m := map[string]int{"a": 1, "b": 2} - for _, v := range m { - x := v - f := func() int { - return x - } - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 1 -// 2 diff --git a/gnovm/tests/debug/closure16_a1.gno b/gnovm/tests/debug/closure16_a1.gno deleted file mode 100644 index 857cc11d7da..00000000000 --- a/gnovm/tests/debug/closure16_a1.gno +++ /dev/null @@ -1,19 +0,0 @@ -package main - -func main() { - var fns []func() int - m := map[string]int{"a": 1, "b": 2} - for _, v := range m { - f := func() int { - return v - } - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 1 -// 2 diff --git a/gnovm/tests/debug/closure16_b.gno b/gnovm/tests/debug/closure16_b.gno deleted file mode 100644 index 5d26a2c6dc0..00000000000 --- a/gnovm/tests/debug/closure16_b.gno +++ /dev/null @@ -1,23 +0,0 @@ -package main - -func main() { - var fns []func() int - s := "hello" - for i, _ := range s { - x := i - f := func() int { - return x - } - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 0 -// 1 -// 2 -// 3 -// 4 diff --git a/gnovm/tests/debug/closure16_b1.gno b/gnovm/tests/debug/closure16_b1.gno deleted file mode 100644 index a7ddb99504f..00000000000 --- a/gnovm/tests/debug/closure16_b1.gno +++ /dev/null @@ -1,22 +0,0 @@ -package main - -func main() { - var fns []func() int - s := "hello" - for i, _ := range s { - f := func() int { - return i - } - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 0 -// 1 -// 2 -// 3 -// 4 diff --git a/gnovm/tests/debug/closure17_io2.gno b/gnovm/tests/debug/closure17_io2.gno deleted file mode 100644 index 24655f5040c..00000000000 --- a/gnovm/tests/debug/closure17_io2.gno +++ /dev/null @@ -1,21 +0,0 @@ -package main - -import ( - "fmt" - "io" - "log" - "strings" -) - -func main() { - r := strings.NewReader("Go is a general-purpose language designed with systems programming in mind.") - - b, err := io.ReadAll(r) - if err != nil { - log.Fatal(err) - } - fmt.Printf("%s", b) -} - -// Output: -// Go is a general-purpose language designed with systems programming in mind. diff --git a/gnovm/tests/debug/closure17_recover4.gno b/gnovm/tests/debug/closure17_recover4.gno deleted file mode 100644 index 5a6da4261a2..00000000000 --- a/gnovm/tests/debug/closure17_recover4.gno +++ /dev/null @@ -1,25 +0,0 @@ -package main - -import "fmt" - -func div(a, b int) (result int) { - defer func() { - r := recover() - - fmt.Printf("r = %#v\n", r) - - if r != nil { - result = 0 - } - }() - - return a / b -} - -func main() { - println(div(30, 2)) -} - -// Output: -// r = -// 15 diff --git a/gnovm/tests/debug/closure17_recover6.gno b/gnovm/tests/debug/closure17_recover6.gno deleted file mode 100644 index 0b304369764..00000000000 --- a/gnovm/tests/debug/closure17_recover6.gno +++ /dev/null @@ -1,30 +0,0 @@ -package main - -import ( - "errors" -) - -func main() { - println(f(false)) - println(f(true)) -} - -func f(dopanic bool) (err error) { - defer func() { - if x := recover(); x != nil { - err = x.(error) - } - }() - q(dopanic) - return -} - -func q(dopanic bool) { - if dopanic { - panic(errors.New("wtf")) - } -} - -// Output: -// undefined -// wtf diff --git a/gnovm/tests/debug/closure17_recover6a.gno b/gnovm/tests/debug/closure17_recover6a.gno deleted file mode 100644 index 427fc4b74b9..00000000000 --- a/gnovm/tests/debug/closure17_recover6a.gno +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import "errors" - -//func p(s interface{}) { -// fmt.Printf("%T \n", s) -// if v, ok := s.(string); ok { -// println(v) -// panic(v) -// } else { -// println("---") -// } -//} - -type error interface { - Error() string -} - -// New returns an error that formats as the given text. -// Each call to New returns a distinct error value even if the text is identical. -func New(text string) error { - return &errorString{text} -} - -// errorString is a trivial implementation of error. -type errorString struct { - s string -} - -func (e *errorString) Error() string { - return e.s -} - -func main() { - //panic(New("wtf")) - panic(errors.New("wtf")) -} - -// Error: -// wtf diff --git a/gnovm/tests/debug/closure17_sort_search_efficiency.gno b/gnovm/tests/debug/closure17_sort_search_efficiency.gno deleted file mode 100644 index 90362944ac5..00000000000 --- a/gnovm/tests/debug/closure17_sort_search_efficiency.gno +++ /dev/null @@ -1,21 +0,0 @@ -package main - -func Search(n int, f func(int) bool) int { - f(1) - return 0 -} - -func main() { - for x := 0; x < 2; x++ { - count := 0 - println(" first: count: ", count) - Search(1, func(i int) bool { count++; return i >= x }) - println("second: count: ", count) - } -} - -// Output: -// first: count: 0 -// second: count: 1 -// first: count: 0 -// second: count: 1 diff --git a/gnovm/tests/debug/closure17_zregexp_stdlibs.gno b/gnovm/tests/debug/closure17_zregexp_stdlibs.gno deleted file mode 100644 index 10bb6f937d3..00000000000 --- a/gnovm/tests/debug/closure17_zregexp_stdlibs.gno +++ /dev/null @@ -1,19 +0,0 @@ -// MAXALLOC: 100000000 -// max total allocation of 100 mb. -package main - -import "regexp" - -var reName = regexp.MustCompile(`^[a-z]+[_a-z0-9]*$`) - -func main() { - for j := 0; j < 100; j++ { - if !(reName.MatchString("thisisatestname")) { - panic("error") - } - } - println(true) -} - -// Output: -// true diff --git a/gnovm/tests/debug/closure2.gno b/gnovm/tests/debug/closure2.gno deleted file mode 100644 index e43496b9f43..00000000000 --- a/gnovm/tests/debug/closure2.gno +++ /dev/null @@ -1,28 +0,0 @@ -package main - -func adder() func(int) int { - sum := 0 - return func(x int) int { - sum = sum + x - return sum - } -} - -func main() { - pos, neg := adder(), adder() - for i := 0; i < 10; i++ { - println(pos(i), neg(-2*i)) - } -} - -// Output: -// 0 0 -// 1 -2 -// 3 -6 -// 6 -12 -// 10 -20 -// 15 -30 -// 21 -42 -// 28 -56 -// 36 -72 -// 45 -90 diff --git a/gnovm/tests/debug/closure3.gno b/gnovm/tests/debug/closure3.gno deleted file mode 100644 index 5e365a67cb4..00000000000 --- a/gnovm/tests/debug/closure3.gno +++ /dev/null @@ -1,23 +0,0 @@ -package main - -type T1 struct { - Name string -} - -func (t *T1) genAdd(k int) func(int) int { - return func(i int) int { - println(t.Name) - return i + k - } -} - -var t = &T1{"test"} - -func main() { - f := t.genAdd(4) - println(f(5)) -} - -// Output: -// test -// 9 diff --git a/gnovm/tests/debug/closure4.gno b/gnovm/tests/debug/closure4.gno deleted file mode 100644 index 61c0a77f1ba..00000000000 --- a/gnovm/tests/debug/closure4.gno +++ /dev/null @@ -1,23 +0,0 @@ -package main - -type T1 struct { - Name string -} - -func (t *T1) genAdd(k int) func(int) int { - return func(i int) int { - println(t.Name) - return i + k - } -} - -var t = T1{"test"} - -func main() { - f := t.genAdd(4) - println(f(5)) -} - -// Output: -// test -// 9 diff --git a/gnovm/tests/debug/closure5.gno b/gnovm/tests/debug/closure5.gno deleted file mode 100644 index e6d3c223607..00000000000 --- a/gnovm/tests/debug/closure5.gno +++ /dev/null @@ -1,23 +0,0 @@ -package main - -type T1 struct { - Name string -} - -func (t T1) genAdd(k int) func(int) int { - return func(i int) int { - println(t.Name) - return i + k - } -} - -var t = T1{"test"} - -func main() { - f := t.genAdd(4) - println(f(5)) -} - -// Output: -// test -// 9 diff --git a/gnovm/tests/debug/closure6.gno b/gnovm/tests/debug/closure6.gno deleted file mode 100644 index 5e365a67cb4..00000000000 --- a/gnovm/tests/debug/closure6.gno +++ /dev/null @@ -1,23 +0,0 @@ -package main - -type T1 struct { - Name string -} - -func (t *T1) genAdd(k int) func(int) int { - return func(i int) int { - println(t.Name) - return i + k - } -} - -var t = &T1{"test"} - -func main() { - f := t.genAdd(4) - println(f(5)) -} - -// Output: -// test -// 9 diff --git a/gnovm/tests/debug/closure7.gno b/gnovm/tests/debug/closure7.gno deleted file mode 100644 index d4c0c96b0d5..00000000000 --- a/gnovm/tests/debug/closure7.gno +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import ( - "fmt" -) - -type Config struct { - A string -} - -var conf *Config = &Config{} - -func SetConfig() func(*Config) { - return func(cf *Config) { - conf = cf - } -} - -func main() { - conf := &Config{ - A: "foo", - } - - fmt.Println(conf.A) -} - -// Output: -// foo diff --git a/gnovm/tests/debug/closure8.gno b/gnovm/tests/debug/closure8.gno deleted file mode 100644 index 6ef1b43fca6..00000000000 --- a/gnovm/tests/debug/closure8.gno +++ /dev/null @@ -1,10 +0,0 @@ -package main - -var f = func(a int) int { return 2 + a } - -func main() { - println(f(3)) -} - -// Output: -// 5 diff --git a/gnovm/tests/debug/closure9.gno b/gnovm/tests/debug/closure9.gno deleted file mode 100644 index e3a4917211f..00000000000 --- a/gnovm/tests/debug/closure9.gno +++ /dev/null @@ -1,30 +0,0 @@ -package main - -// this is a fix, intuitive, and same with Go -func main() { - var fns []func() int - for i := 0; i < 5; i++ { - x := i - f := func() int { - return x - } - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Go Output: -// 0 -// 1 -// 2 -// 3 -// 4 - -// Output: -// 0 -// 1 -// 2 -// 3 -// 4 diff --git a/gnovm/tests/debug/closure9_a0.gno b/gnovm/tests/debug/closure9_a0.gno deleted file mode 100644 index 0f5ba6d6739..00000000000 --- a/gnovm/tests/debug/closure9_a0.gno +++ /dev/null @@ -1,29 +0,0 @@ -package main - -// this is a fix, intuitive, same with go loopVar experiment feature. -func main() { - var fns []func() int - for i := 0; i < 5; i++ { - f := func() int { - return i - } - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Go Output: -// 5 -// 5 -// 5 -// 5 -// 5 - -// Output: -// 0 -// 1 -// 2 -// 3 -// 4 diff --git a/gnovm/tests/debug/closure9_a02.gno b/gnovm/tests/debug/closure9_a02.gno deleted file mode 100644 index 82ca810b13c..00000000000 --- a/gnovm/tests/debug/closure9_a02.gno +++ /dev/null @@ -1,20 +0,0 @@ -package main - -func main() { - var fns []func() int - for i := 0; i < 5; i++ { - x := i - f := func() int { - return x - } - fns = append(fns, f) - } - println(fns[3]()) - println(fns[2]()) - println(fns[0]()) -} - -// Output: -// 3 -// 2 -// 0 diff --git a/gnovm/tests/debug/closure9_a1.gno b/gnovm/tests/debug/closure9_a1.gno deleted file mode 100644 index 19951610e7e..00000000000 --- a/gnovm/tests/debug/closure9_a1.gno +++ /dev/null @@ -1,34 +0,0 @@ -package main - -// this still keeps consistent with go, that a variable out of loop block is not captured -// this is unintuitive(should capture something) -// TODO: leave it for discuss. - -func main() { - var fns []func() int - var x int - for i := 0; i < 5; i++ { - x = i - f := func() int { - return x - } - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Go Output: -// 4 -// 4 -// 4 -// 4 -// 4 - -// Output: -// 4 -// 4 -// 4 -// 4 -// 4 diff --git a/gnovm/tests/debug/closure9_b.gno b/gnovm/tests/debug/closure9_b.gno deleted file mode 100644 index 0822368fe3a..00000000000 --- a/gnovm/tests/debug/closure9_b.gno +++ /dev/null @@ -1,22 +0,0 @@ -package main - -func main() { - var fns []func() int - for i := 0; i < 2; i++ { - x := i - y := 0 - f := func() int { - x += y - x += 1 - return x - } - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 1 -// 2 diff --git a/gnovm/tests/debug/closure9_c.gno b/gnovm/tests/debug/closure9_c.gno deleted file mode 100644 index e5c61c59089..00000000000 --- a/gnovm/tests/debug/closure9_c.gno +++ /dev/null @@ -1,18 +0,0 @@ -package main - -func main() { - var fns []func() int - for i := 0; i < 2; i++ { - f := func() int { - return i - } - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 0 -// 1 diff --git a/gnovm/tests/debug/closure9_d.gno b/gnovm/tests/debug/closure9_d.gno deleted file mode 100644 index 185961c741f..00000000000 --- a/gnovm/tests/debug/closure9_d.gno +++ /dev/null @@ -1,29 +0,0 @@ -package main - -func main() { - var fns []func() int - for i := 0; i < 5; i++ { - x := i - f := func() int { - x := 5 - return x - } - println(x) - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 0 -// 1 -// 2 -// 3 -// 4 -// 5 -// 5 -// 5 -// 5 -// 5 diff --git a/gnovm/tests/debug/closure9_e.gno b/gnovm/tests/debug/closure9_e.gno deleted file mode 100644 index b3b2b2b0385..00000000000 --- a/gnovm/tests/debug/closure9_e.gno +++ /dev/null @@ -1,12 +0,0 @@ -package main - -func main() { - for i := 0; i < 5; i++ { - if i == 1 { - panic("error 1") - } - } -} - -// Error: -// error 1 diff --git a/gnovm/tests/debug/closure9_f.gno b/gnovm/tests/debug/closure9_f.gno deleted file mode 100644 index 19722ec3ae6..00000000000 --- a/gnovm/tests/debug/closure9_f.gno +++ /dev/null @@ -1,53 +0,0 @@ -package main - -// 1. we can determine target vars by hasClosure pattern and externNames(without uverse). -// it can be a loopvar, or vars derived from loopvar, e.g. x := i; -// 2. now we need to capture every transient state of the target var; firstly we should -// eval the transient state, there are two ways around: -// a) eval funcLit, but the time doing this is hard to determine, namely, you have to eval -// before it leaves its using context. In some sense, you have to cover all cases when a funcValue -// is used, like assign, xxxLit, funcCall as an arg, etc. -// b) another way for this is to generate a `time series` values that the index is the loop index, -// e.g. in a for loop, we define a new var like x := i, we store the transient state of x per loop -// in the time series, which is a slice. This slice is used when the closure fv is called, -// replacing the var in default block.(func value have a slice of name indicates it's captured, when -// eval name in this slice, using the time series instead). -// this solution mainly differs that it eval target x independently with the closure funcLit, so it avoids -// the obsession to determine the eval order. this seems the right way. hmmm. - -//======================================================================================================== -// work flow 1.0, hard to do -// 1. determine loop var, whole logic depends on this, the key word is dynamic, it causes polymorphic. -// this should most be done via define(), // TODO: check it -// 2. in transcribe, find if loopvar is used somewhere, to identify pattern like x := i, what to do with -// other use cases? this brings complexity. This is wrong feel. -// we can generate the ts slice for every target var, e.g. x in this case. if two vars, two slices. so fv should -// reference a slice of slice. - -// work flow 2.0 -// 1. ignore loop var, assume all externNames apart from uverse is target captured var, this also needs to happen -// in transcribe, tag it, and can be checked everywhere it's appears in runtime, x := i, or var x int, etc. -// when eval funcLit, new a slice of slice and push value for further update. - -func main() { - var fns []func() int - for i := 0; i < 5; i++ { - //x := i // check if x is polymorphic, if yes, new a slice and set value by index - var x int // new a slice no init, this would be updated later, so the slice can be mutated after opFuncLit - f := func() int { - return x // set the slice to fv in - } - x = i // check if x is polymorphic, is yes, update slice assigned to fv before this, which is a reference. - fns = append(fns, f) // where should eval funcLit, where it assigned somewhere, or used in another func lit - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 0 -// 1 -// 2 -// 3 -// 4 diff --git a/gnovm/tests/debug/closure9_g.gno b/gnovm/tests/debug/closure9_g.gno deleted file mode 100644 index e7a6b64f1f3..00000000000 --- a/gnovm/tests/debug/closure9_g.gno +++ /dev/null @@ -1,24 +0,0 @@ -package main - -func main() { - var fns []func() int - for i := 0; i < 5; i++ { - x := i - { - f := func() int { - return x - } - fns = append(fns, f) - } - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 0 -// 1 -// 2 -// 3 -// 4 diff --git a/gnovm/tests/debug/closure9_h.gno b/gnovm/tests/debug/closure9_h.gno deleted file mode 100644 index 23cb5ff9f40..00000000000 --- a/gnovm/tests/debug/closure9_h.gno +++ /dev/null @@ -1,30 +0,0 @@ -package main - -func main() { - var fns []func() int - for i := 0; i < 2; i++ { - x := i - for j := 0; j < 2; j++ { - y := j - f := func() int { - return x + y - } - fns = append(fns, f) - } - } - for _, fn := range fns { - println(fn()) - } -} - -// Go Output: -// 0 -// 1 -// 1 -// 2 - -// Output: -// 0 -// 1 -// 1 -// 2 diff --git a/gnovm/tests/debug/closure9_h_0.gno b/gnovm/tests/debug/closure9_h_0.gno deleted file mode 100644 index d60fa8515a9..00000000000 --- a/gnovm/tests/debug/closure9_h_0.gno +++ /dev/null @@ -1,28 +0,0 @@ -package main - -func main() { - var fns []func() int - for i := 0; i < 2; i++ { - for j := 0; j < 2; j++ { - f := func() int { - return i + j - } - fns = append(fns, f) - } - } - for _, fn := range fns { - println(fn()) - } -} - -// Go Output: -// 4 -// 4 -// 4 -// 4 - -// Output: -// 0 -// 1 -// 1 -// 2 diff --git a/gnovm/tests/debug/closure9_i.gno b/gnovm/tests/debug/closure9_i.gno deleted file mode 100644 index 42a4d11d862..00000000000 --- a/gnovm/tests/debug/closure9_i.gno +++ /dev/null @@ -1,31 +0,0 @@ -package main - -func main() { - var fns []func() int - var x int - for i := 0; i < 2; i++ { - x = i - for j := 0; j < 2; j++ { - y := j - f := func() int { - return x + y - } - fns = append(fns, f) - } - } - for _, fn := range fns { - println(fn()) - } -} - -// Go Output: -// 1 -// 2 -// 1 -// 2 - -// Output: -// 1 -// 2 -// 1 -// 2 diff --git a/gnovm/tests/debug/closure9_j.gno b/gnovm/tests/debug/closure9_j.gno deleted file mode 100644 index 926caf5090c..00000000000 --- a/gnovm/tests/debug/closure9_j.gno +++ /dev/null @@ -1,21 +0,0 @@ -package main - -func main() { - var fns []func() int - for i := 0; i < 2; i++ { - x := i - y := 0 - f := func() int { - x += y - return x - } - fns = append(fns, f) - } - for _, fn := range fns { - println(fn()) - } -} - -// Output: -// 0 -// 1 From 465bb3bda3bac9080c2b93e84379fab56ee7c1cc Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Fri, 26 Jan 2024 08:26:23 +0800 Subject: [PATCH 20/32] fixup --- examples/gno.land/p/demo/avl/z_0_filetest.gno | 2 ++ examples/gno.land/p/demo/avl/z_1_filetest.gno | 2 ++ gnovm/Makefile | 2 -- gnovm/pkg/gnolang/debug.go | 8 ++------ gnovm/pkg/gnolang/machine.go | 3 --- gnovm/pkg/gnolang/op_assign.go | 5 ----- gnovm/pkg/gnolang/op_exec.go | 3 --- 7 files changed, 6 insertions(+), 19 deletions(-) diff --git a/examples/gno.land/p/demo/avl/z_0_filetest.gno b/examples/gno.land/p/demo/avl/z_0_filetest.gno index e91788ac8eb..acd3d3d5758 100644 --- a/examples/gno.land/p/demo/avl/z_0_filetest.gno +++ b/examples/gno.land/p/demo/avl/z_0_filetest.gno @@ -280,6 +280,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -316,6 +317,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], diff --git a/examples/gno.land/p/demo/avl/z_1_filetest.gno b/examples/gno.land/p/demo/avl/z_1_filetest.gno index cdd56a5ad89..77726610747 100644 --- a/examples/gno.land/p/demo/avl/z_1_filetest.gno +++ b/examples/gno.land/p/demo/avl/z_1_filetest.gno @@ -304,6 +304,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], @@ -340,6 +341,7 @@ func main() { // "PkgPath": "gno.land/r/test" // } // }, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], diff --git a/gnovm/Makefile b/gnovm/Makefile index 69ae3d2c20b..cbe6802d32d 100644 --- a/gnovm/Makefile +++ b/gnovm/Makefile @@ -54,7 +54,6 @@ _test.gnolang: _test.gnolang.native _test.gnolang.stdlibs _test.gnolang.realm _t _test.gnolang.other:; go test tests/*.go -run "(TestFileStr|TestSelectors)" $(GOTEST_FLAGS) _test.gnolang.realm:; go test tests/*.go -run "TestFiles/^zrealm" $(GOTEST_FLAGS) _test.gnolang.pkg0:; go test tests/*.go -run "TestPackages/(bufio|crypto|encoding|errors|internal|io|math|sort|std|stdshim|strconv|strings|testing|unicode)" $(GOTEST_FLAGS) -#_test.gnolang.pkg0:; go test tests/*.go -run "TestPackages/(bufio|crypto|encoding|errors|internal|io|math|std|stdshim|strconv|strings|testing|unicode)" $(GOTEST_FLAGS) _test.gnolang.pkg1:; go test tests/*.go -run "TestPackages/regexp" $(GOTEST_FLAGS) _test.gnolang.pkg2:; go test tests/*.go -run "TestPackages/bytes" $(GOTEST_FLAGS) _test.gnolang.native:; go test tests/*.go -test.short -run "TestFilesNative/" $(GOTEST_FLAGS) @@ -62,7 +61,6 @@ _test.gnolang.stdlibs:; go test tests/*.go -test.short -run 'TestFiles$$/' _test.gnolang.native.sync:; go test tests/*.go -test.short -run "TestFilesNative/" --update-golden-tests $(GOTEST_FLAGS) _test.gnolang.stdlibs.sync:; go test tests/*.go -test.short -run 'TestFiles$$/' --update-golden-tests $(GOTEST_FLAGS) _test.gnolang.debug:; go test tests/*.go -test.short -run 'TestDebug$$/' --update-golden-tests $(GOTEST_FLAGS) -_test.gnolang.p:; go test tests/*.go -run "TestPackages/(sort)" $(GOTEST_FLAGS) ######################################## # Code gen diff --git a/gnovm/pkg/gnolang/debug.go b/gnovm/pkg/gnolang/debug.go index 1d98a0f44c8..cb21da12ef2 100644 --- a/gnovm/pkg/gnolang/debug.go +++ b/gnovm/pkg/gnolang/debug.go @@ -18,15 +18,11 @@ import ( // so it is still faster to first check the truth value // before calling debug.Println or debug.Printf. -type ( - debugging bool -) +type debugging bool // using a const is probably faster. // const debug debugging = true // or flip -var ( - debug debugging = false -) +var debug debugging = false func init() { debug = os.Getenv("DEBUG") == "1" diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go index e41f264c06c..734c4cd06f9 100644 --- a/gnovm/pkg/gnolang/machine.go +++ b/gnovm/pkg/gnolang/machine.go @@ -925,8 +925,6 @@ const ( OpMaybeNativeType Op = 0x79 // maybenative{X} /* Statement operators */ - OpPreAssign Op = 0x92 - OpPostAssign Op = 0x93 OpAssign Op = 0x80 // Lhs = Rhs OpAddAssign Op = 0x81 // Lhs += Rhs OpSubAssign Op = 0x82 // Lhs -= Rhs @@ -1630,7 +1628,6 @@ func (m *Machine) ReapValues(start int) []TypedValue { func (m *Machine) PushBlock(b *Block) { if debug { - // m.Println("+B") m.Printf("+B: %v \n", b) } m.Blocks = append(m.Blocks, b) diff --git a/gnovm/pkg/gnolang/op_assign.go b/gnovm/pkg/gnolang/op_assign.go index 55c952da295..0c39c762c78 100644 --- a/gnovm/pkg/gnolang/op_assign.go +++ b/gnovm/pkg/gnolang/op_assign.go @@ -1,17 +1,12 @@ package gnolang func (m *Machine) doOpDefine() { - debug.Println("-----doOpDefine") s := m.PopStmt().(*AssignStmt) - debug.Printf("m.NumValues: %d \n", m.NumValues) // Define each value evaluated for Lhs. // NOTE: PopValues() returns a slice in // forward order, not the usual reverse. // m.PopValue() rvs := m.PopValues(len(s.Lhs)) - debug.Printf("s:%v \n", s) - debug.Printf("lhs:%v \n", s.Lhs) - debug.Printf("rvs:%v \n", rvs) lb := m.LastBlock() for i := 0; i < len(s.Lhs); i++ { // Get name and value of i'th term. diff --git a/gnovm/pkg/gnolang/op_exec.go b/gnovm/pkg/gnolang/op_exec.go index 13d69fc6420..303a655d6ff 100644 --- a/gnovm/pkg/gnolang/op_exec.go +++ b/gnovm/pkg/gnolang/op_exec.go @@ -93,7 +93,6 @@ func (m *Machine) doOpExec(op Op) { case OpBody: bs := m.LastBlock().GetBodyStmt() if bs.NextBodyIndex == -2 { // init - debug.Println("---OpBody, init") bs.NumOps = m.NumOps bs.NumValues = m.NumValues bs.NumExprs = len(m.Exprs) @@ -101,7 +100,6 @@ func (m *Machine) doOpExec(op Op) { bs.NextBodyIndex = 0 } if bs.NextBodyIndex < bs.BodyLen { - debug.Println("---OpBody, index < body len") next := bs.Body[bs.NextBodyIndex] bs.NextBodyIndex++ // continue onto exec stmt. @@ -109,7 +107,6 @@ func (m *Machine) doOpExec(op Op) { s = next goto EXEC_SWITCH } else { - debug.Println("---else") m.ForcePopOp() m.ForcePopStmt() return From 1544e3819d1bcb246cd234eb56f760d570595ec2 Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Fri, 26 Jan 2024 10:15:29 +0800 Subject: [PATCH 21/32] fixup --- .../gno/testdata/gno_test/realm_correct.txtar | 1 + .../gno/testdata/gno_test/realm_sync.txtar | 1 + gnovm/pkg/gnolang/preprocess.go | 8 +------ gnovm/pkg/gnolang/realm.go | 2 -- gnovm/pkg/gnolang/transcribe.go | 10 -------- gnovm/pkg/gnolang/types.go | 3 --- gnovm/pkg/gnolang/values.go | 23 ------------------- gnovm/tests/file.go | 13 ----------- 8 files changed, 3 insertions(+), 58 deletions(-) diff --git a/gnovm/cmd/gno/testdata/gno_test/realm_correct.txtar b/gnovm/cmd/gno/testdata/gno_test/realm_correct.txtar index f17f28055f2..7e33972267a 100644 --- a/gnovm/cmd/gno/testdata/gno_test/realm_correct.txtar +++ b/gnovm/cmd/gno/testdata/gno_test/realm_correct.txtar @@ -68,6 +68,7 @@ func main() { // "PkgPath": "gno.land/r/x" // } // }, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], diff --git a/gnovm/cmd/gno/testdata/gno_test/realm_sync.txtar b/gnovm/cmd/gno/testdata/gno_test/realm_sync.txtar index e8c643af0ba..ed38562ab09 100644 --- a/gnovm/cmd/gno/testdata/gno_test/realm_sync.txtar +++ b/gnovm/cmd/gno/testdata/gno_test/realm_sync.txtar @@ -83,6 +83,7 @@ func main() { // "PkgPath": "gno.land/r/x" // } // }, +// "TransientLoopData": null, // "Type": { // "@type": "/gno.FuncType", // "Params": [], diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 808ffd9894b..8fa9d0cb835 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -361,7 +361,6 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { ft := evalStaticType(store, last, &n.Type).(*FuncType) // push func body block. pushInitBlock(n, &last, &stack) - for _, p := range ft.Params { last.Define(p.Name, anyValue(p.Type)) } @@ -467,7 +466,6 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { n.Cases[i] = cx } } - // popClosure() // TRANS_BLOCK ----------------------- case *FuncDecl: @@ -744,9 +742,6 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } } } - // popClosure() - // popFx() - // TRANS_LEAVE ----------------------- case *BasicLitExpr: // Replace with *ConstExpr. @@ -1968,7 +1963,6 @@ func pushInitBlock(bn BlockNode, last *BlockNode, stack *[]BlockNode) { if bn.GetStaticBlock().Source != bn { panic("expected the source of a block node to be itself") } - // bn.GetStaticBlock().String() *last = bn *stack = append(*stack, bn) } @@ -1984,7 +1978,7 @@ func pushRealBlock(bn BlockNode, last *BlockNode, stack *[]BlockNode) { // anything declared in orig are copied. for _, n := range orig.GetBlockNames() { tv := orig.GetValueRef(nil, n) - bn.Define(n, *tv) // pointer, actually + bn.Define(n, *tv) } } diff --git a/gnovm/pkg/gnolang/realm.go b/gnovm/pkg/gnolang/realm.go index 1567859e2f6..d10a9280301 100644 --- a/gnovm/pkg/gnolang/realm.go +++ b/gnovm/pkg/gnolang/realm.go @@ -1053,7 +1053,6 @@ func copyTypeWithRefs(typ Type) Type { // Also checks for integrity of immediate children -- they must already be // persistent (real), and not dirty, or else this function panics. func copyValueWithRefs(parent Object, val Value) Value { - debug.Println("-----copyValueWithRefs") switch cv := val.(type) { case nil: return nil @@ -1130,7 +1129,6 @@ func copyValueWithRefs(parent Object, val Value) Value { if cv.Closure != nil { closure = toRefValue(parent, cv.Closure) } - debug.Printf("closure: %v \n", closure) // nativeBody funcs which don't come from NativeStore (and thus don't // have NativePkg/Name) can't be persisted, and should not be able // to get here anyway. diff --git a/gnovm/pkg/gnolang/transcribe.go b/gnovm/pkg/gnolang/transcribe.go index 3f865badf2b..3906497f276 100644 --- a/gnovm/pkg/gnolang/transcribe.go +++ b/gnovm/pkg/gnolang/transcribe.go @@ -420,7 +420,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*ForStmt) } - if cnn.Init != nil { cnn.Init = transcribe(t, nns, TRANS_FOR_INIT, 0, cnn.Init, &c).(SimpleStmt) if isStopOrSkip(nc, c) { @@ -439,7 +438,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } } - for idx := range cnn.Body { cnn.Body[idx] = transcribe(t, nns, TRANS_FOR_BODY, idx, cnn.Body[idx], &c).(Stmt) if isBreak(c) { @@ -464,8 +462,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*IfStmt) } - - // nx in init is always treat defined locally if cnn.Init != nil { cnn.Init = transcribe(t, nns, TRANS_IF_INIT, 0, cnn.Init, &c).(SimpleStmt) if isStopOrSkip(nc, c) { @@ -476,7 +472,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc if isStopOrSkip(nc, c) { return } - cnn.Then = *transcribe(t, nns, TRANS_IF_BODY, 0, &cnn.Then, &c).(*IfCaseStmt) if isStopOrSkip(nc, c) { return @@ -493,7 +488,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*IfCaseStmt) } - // pushClosure(&Closure{}) for idx := range cnn.Body { cnn.Body[idx] = transcribe(t, nns, TRANS_IF_CASE_BODY, idx, cnn.Body[idx], &c).(Stmt) if isBreak(c) { @@ -519,7 +513,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc if isStopOrSkip(nc, c) { return } - if cnn.Key != nil { cnn.Key = transcribe(t, nns, TRANS_RANGE_KEY, 0, cnn.Key, &c).(Expr) if isStopOrSkip(nc, c) { @@ -532,7 +525,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } } - for idx := range cnn.Body { cnn.Body[idx] = transcribe(t, nns, TRANS_RANGE_BODY, idx, cnn.Body[idx], &c).(Stmt) if isBreak(c) { @@ -675,7 +667,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*FuncDecl) } - for idx := range cnn.Body { cnn.Body[idx] = transcribe(t, nns, TRANS_FUNC_BODY, idx, cnn.Body[idx], &c).(Stmt) if isBreak(c) { @@ -714,7 +705,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*FileNode) } - for idx := range cnn.Decls { cnn.Decls[idx] = transcribe(t, nns, TRANS_FILE_BODY, idx, cnn.Decls[idx], &c).(Decl) if isBreak(c) { diff --git a/gnovm/pkg/gnolang/types.go b/gnovm/pkg/gnolang/types.go index 69e7d56e4e8..65bf7227458 100644 --- a/gnovm/pkg/gnolang/types.go +++ b/gnovm/pkg/gnolang/types.go @@ -1605,7 +1605,6 @@ func (dt *DeclaredType) FindEmbeddedFieldType(callerPath string, n Name, m map[T // runtime: *TV.GetPointerTo(path) // -> *DT.GetValueAt(path) func (dt *DeclaredType) GetValueAt(alloc *Allocator, store Store, path ValuePath) TypedValue { - debug.Printf("-----GetValueAt: %v \n", path) switch path.Type { case VPInterface: panic("should not happen") @@ -1617,9 +1616,7 @@ func (dt *DeclaredType) GetValueAt(alloc *Allocator, store Store, path ValuePath // Fill in *FV.Closure. ft := mtv.T fv := mtv.V.(*FuncValue).Copy(alloc) - debug.Printf("fv: %v \n", fv) fv.Closure = fv.GetClosure(store) - debug.Printf("closure: %v \n", fv.Closure) return TypedValue{T: ft, V: fv} } else { panic("DeclaredType.GetValueAt() expects depth == 0") diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index 9051ad1b0f7..d24a7365cd5 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -1,9 +1,7 @@ package gnolang import ( - "crypto/sha256" "encoding/binary" - "encoding/hex" "fmt" "math" "math/big" @@ -666,10 +664,8 @@ func (fv *FuncValue) GetPackage(store Store) *PackageValue { // file-level declared methods and functions. For those, caller // should set .Closure manually after *FuncValue.Copy(). func (fv *FuncValue) GetClosure(store Store) *Block { - debug.Println("-----GetClosure") switch cv := fv.Closure.(type) { case nil: - debug.Println("-----nil") if fv.FileName == "" { return nil } @@ -680,12 +676,10 @@ func (fv *FuncValue) GetClosure(store Store) *Block { } return fb case RefValue: - debug.Printf("-----RefValue, cv.ObjectID: %v \n", cv.ObjectID) block := store.GetObject(cv.ObjectID).(*Block) fv.Closure = block return block case *Block: - debug.Println("-----block") return cv default: panic("should not happen") @@ -1615,7 +1609,6 @@ func (tv *TypedValue) ComputeMapKey(store Store, omitType bool) MapKey { // cu: convert untyped after assignment. pass false // for const definitions, but true for all else. func (tv *TypedValue) Assign(alloc *Allocator, tv2 TypedValue, cu bool) { - debug.Printf("Assign, tv:%v, tv2:%v \n", tv, tv2) if debug { if tv.T == DataByteType { // assignment to data byte types should only @@ -1629,7 +1622,6 @@ func (tv *TypedValue) Assign(alloc *Allocator, tv2 TypedValue, cu bool) { } } *tv = tv2.Copy(alloc) - debug.Printf("*tv: %+v \n", *tv) if cu && isUntyped(tv.T) { ConvertUntypedTo(tv, defaultTypeOf(tv.T)) } @@ -2312,21 +2304,6 @@ func NewBlock(source BlockNode, parent *Block) *Block { } } -func (b *Block) Hash() string { - // Convert the struct to a byte slice (serialization) - structBytes := []byte(fmt.Sprintf("%s%d", b.ObjectInfo, b.Source, b.Values, b.Parent, b.Blank, b.bodyStmt)) - - // Hash the byte slice using SHA-256 - hash := sha256.Sum256(structBytes) - - // Convert the hash to a hexadecimal string - hashHex := hex.EncodeToString(hash[:]) - - fmt.Println("Original struct:", b) - fmt.Println("Hashed value:", hashHex) - return hashHex -} - func (b *Block) UpdateValue(index int, tv TypedValue) { for i := range b.Values { if i == index { diff --git a/gnovm/tests/file.go b/gnovm/tests/file.go index d2a5406c3b8..a021628a385 100644 --- a/gnovm/tests/file.go +++ b/gnovm/tests/file.go @@ -148,10 +148,7 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error { f.logger("RUN FILES & INIT") f.logger("========================================") } - if !gno.IsRealmPath(pkgPath) { - // startTime := time.Now() - // simple case. pn := gno.NewPackageNode(pkgName, pkgPath, &gno.FileSet{}) pv := pn.NewPackage() @@ -165,17 +162,7 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error { f.logger("RUN MAIN") f.logger("========================================") } - // endTime := time.Now() - // elapsedTime := endTime.Sub(startTime) - // fmt.Printf("Run Init took %s to execute\n", elapsedTime) - - // startTime = time.Now() - m.RunMain() - // endTime = time.Now() - // elapsedTime = endTime.Sub(startTime) - - // fmt.Printf("Run Main took %s to execute\n", elapsedTime) if f.logger != nil { f.logger("========================================") f.logger("RUN MAIN END") From c57a0f2a9101e9e1a82ac77eeb24c23e63f46af4 Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Fri, 26 Jan 2024 14:04:16 +0800 Subject: [PATCH 22/32] fixup --- .../testdata/gno_test/realm_incorrect.txtar | 2 +- gnovm/pkg/gnolang/op_expressions.go | 8 +++--- gnovm/pkg/gnolang/preprocess.go | 27 +++++++++---------- gnovm/tests/files/closure_9a.gno | 17 ++++++++++++ 4 files changed, 35 insertions(+), 19 deletions(-) create mode 100644 gnovm/tests/files/closure_9a.gno diff --git a/gnovm/cmd/gno/testdata/gno_test/realm_incorrect.txtar b/gnovm/cmd/gno/testdata/gno_test/realm_incorrect.txtar index 38794a3e645..96bb56c4110 100644 --- a/gnovm/cmd/gno/testdata/gno_test/realm_incorrect.txtar +++ b/gnovm/cmd/gno/testdata/gno_test/realm_incorrect.txtar @@ -7,7 +7,7 @@ stderr '=== RUN file/x_filetest.gno' stderr 'panic: fail on x_filetest.gno: diff:' stderr '--- Expected' stderr '\+\+\+ Actual' -stderr '@@ -1 \+1,66 @@' +stderr '@@ -1 \+1,67 @@' stderr '-xxx' stderr '\+switchrealm\["gno.land/r/x"\]' diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index aa595108b64..0ea6a0b7bed 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -706,7 +706,7 @@ func (m *Machine) doOpFuncLit() { loopBlock *Block // e.g. a `for` block that is outside of funcLit block lvBox *LoopValuesBox // container per block to store transient values for captured vars lastGen, gen uint8 - isReuse bool + isFilled bool loopData []*LoopBlockData // for fv to track all transient values ) @@ -727,7 +727,7 @@ func (m *Machine) doOpFuncLit() { lastGen = gen } else if gen != lastGen { // if enter new level of block, pack last box lastGen = gen - if lvBox != nil && !isReuse { // has something to pack + if lvBox != nil && !isFilled { // has something to pack lvBox.isFilled = true loopData = append(loopData, &LoopBlockData{index: 0, loopValuesBox: lvBox}) } @@ -748,7 +748,7 @@ func (m *Machine) doOpFuncLit() { // record in loop block loopBlock.GetBodyStmt().LoopValuesBox = lvBox } else { // reuse last replica's. (in same block). - isReuse = true // use isFilled instead + isFilled = true // use isFilled instead // get cursor by name lvBox.transient[lvBox.getIndexByName(nx.Name)].cursor++ // inc by iteration // each fv may have n outer loopBlock, reference them all @@ -757,7 +757,7 @@ func (m *Machine) doOpFuncLit() { } } // set as a deferred operation - if lvBox != nil && !lvBox.isFilled && !isReuse { + if lvBox != nil && !lvBox.isFilled && !isFilled { lvBox.isFilled = true // set for the last LoopValuesBox of last loopBlock loopData = append(loopData, &LoopBlockData{index: 0, loopValuesBox: lvBox}) } diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 8fa9d0cb835..bf47105d781 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -288,7 +288,6 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // TRANS_BLOCK ----------------------- case *IfCaseStmt: pushRealBlock(n, &last, &stack) - // pushClosure(&Closure{}) // parent if statement. ifs := ns[len(ns)-1].(*IfStmt) // anything declared in ifs are copied. @@ -403,7 +402,6 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // TRANS_BLOCK ----------------------- case *SwitchClauseStmt: pushRealBlock(n, &last, &stack) - // pushClosure(&Closure{}) // parent switch statement. ss := ns[len(ns)-1].(*SwitchStmt) // anything declared in ss are copied, @@ -711,8 +709,8 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { cx := evalConst(store, last, n) // built-in functions must be called. if !cx.IsUndefined() && - cx.T.Kind() == FuncKind && - ftype != TRANS_CALL_FUNC { + cx.T.Kind() == FuncKind && + ftype != TRANS_CALL_FUNC { panic(fmt.Sprintf( "use of builtin %s not in function call", n.Name)) @@ -742,6 +740,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } } } + // TRANS_LEAVE ----------------------- case *BasicLitExpr: // Replace with *ConstExpr. @@ -1353,8 +1352,8 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // Case 1: If receiver is pointer type but n.X is // not: if rcvr != nil && - rcvr.Kind() == PointerKind && - nxt2.Kind() != PointerKind { + rcvr.Kind() == PointerKind && + nxt2.Kind() != PointerKind { // Go spec: "If x is addressable and &x's // method set contains m, x.m() is shorthand // for (&x).m()" @@ -1386,8 +1385,8 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { )) } } else if len(tr) > 0 && - tr[len(tr)-1].IsDerefType() && - nxt2.Kind() != PointerKind { + tr[len(tr)-1].IsDerefType() && + nxt2.Kind() != PointerKind { // Case 2: If tr[0] is deref type, but xt // is not pointer type, replace n.X with // &RefExpr{X: n.X}. @@ -1526,7 +1525,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { if len(n.Lhs) != len(cft.Results) { panic(fmt.Sprintf( "assignment mismatch: "+ - "%d variables but %s returns %d values", + "%d variables but %s returns %d values", len(n.Lhs), cx.Func.String(), len(cft.Results))) } for i, lx := range n.Lhs { @@ -1595,7 +1594,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { if len(n.Lhs) != len(cft.Results) { panic(fmt.Sprintf( "assignment mismatch: "+ - "%d variables but %s returns %d values", + "%d variables but %s returns %d values", len(n.Lhs), cx.Func.String(), len(cft.Results))) } case *TypeAssertExpr: @@ -1834,7 +1833,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // evaluate typed value for static definition. for i, vx := range n.Values { if cx, ok := vx.(*ConstExpr); ok && - !cx.TypedValue.IsUndefined() { + !cx.TypedValue.IsUndefined() { if n.Const { // const _ = : static block should contain value tvs[i] = cx.TypedValue @@ -2220,7 +2219,7 @@ func funcOf(last BlockNode) (BlockNode, *FuncTypeExpr) { } func findGotoLabel(last BlockNode, label Name) ( - bn BlockNode, depth uint8, bodyIdx int, + bn BlockNode, depth uint8, bodyIdx int, ) { for { switch cbn := last.(type) { @@ -2471,8 +2470,8 @@ func checkType(xt Type, dt Type, autoNative bool) { if ddt, ok := dt.(*DeclaredType); ok { // types must match exactly. if !dxt.sealed && !ddt.sealed && - dxt.PkgPath == ddt.PkgPath && - dxt.Name == ddt.Name { // not yet sealed + dxt.PkgPath == ddt.PkgPath && + dxt.Name == ddt.Name { // not yet sealed return // ok } else if dxt.TypeID() == ddt.TypeID() { return // ok diff --git a/gnovm/tests/files/closure_9a.gno b/gnovm/tests/files/closure_9a.gno new file mode 100644 index 00000000000..3bfa5f181d0 --- /dev/null +++ b/gnovm/tests/files/closure_9a.gno @@ -0,0 +1,17 @@ +package main + +func main() { + var fns []func() + for _, v := range []int{1, 2, 3} { + x := v*100 + v + fns = append(fns, func() { println(x) }) + } + for _, fn := range fns { + fn() + } +} + +// Output: +// 101 +// 202 +// 303 From f4e98da40b750eb307d45ab98b1a1f7fac9b4767 Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Fri, 26 Jan 2024 14:14:52 +0800 Subject: [PATCH 23/32] fixup --- gnovm/pkg/gnolang/machine.go | 2 +- gnovm/pkg/gnolang/op_expressions.go | 10 +++++----- gnovm/pkg/gnolang/preprocess.go | 24 ++++++++++++------------ 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go index 734c4cd06f9..802d2cd85c2 100644 --- a/gnovm/pkg/gnolang/machine.go +++ b/gnovm/pkg/gnolang/machine.go @@ -747,7 +747,7 @@ func (m *Machine) EvalStatic(last BlockNode, x Expr) TypedValue { // static types of nodes. func (m *Machine) EvalStaticTypeOf(last BlockNode, x Expr) Type { if debug { - // m.Printf("Machine.EvalStaticTypeOf(%v, %v)\n", last, x) + m.Printf("Machine.EvalStaticTypeOf(%v, %v)\n", last, x) } // X must have been preprocessed. if x.GetAttribute(ATTR_PREPROCESSED) == nil { diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index 0ea6a0b7bed..8298d7a6721 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -681,7 +681,7 @@ func (m *Machine) doOpStructLit() { func (m *Machine) doOpFuncLit() { x := m.PopExpr().(*FuncLitExpr) lb := m.LastBlock() - var captures []*NameExpr + captures := make([]*NameExpr, 0) for _, n := range x.GetExternNames() { vp := x.GetPathForName(m.Store, n) if vp.Depth == 0 { // skip uverse name @@ -706,7 +706,7 @@ func (m *Machine) doOpFuncLit() { loopBlock *Block // e.g. a `for` block that is outside of funcLit block lvBox *LoopValuesBox // container per block to store transient values for captured vars lastGen, gen uint8 - isFilled bool + isReuse bool loopData []*LoopBlockData // for fv to track all transient values ) @@ -727,7 +727,7 @@ func (m *Machine) doOpFuncLit() { lastGen = gen } else if gen != lastGen { // if enter new level of block, pack last box lastGen = gen - if lvBox != nil && !isFilled { // has something to pack + if lvBox != nil && !isReuse { // has something to pack lvBox.isFilled = true loopData = append(loopData, &LoopBlockData{index: 0, loopValuesBox: lvBox}) } @@ -748,7 +748,7 @@ func (m *Machine) doOpFuncLit() { // record in loop block loopBlock.GetBodyStmt().LoopValuesBox = lvBox } else { // reuse last replica's. (in same block). - isFilled = true // use isFilled instead + isReuse = true // use isFilled instead // get cursor by name lvBox.transient[lvBox.getIndexByName(nx.Name)].cursor++ // inc by iteration // each fv may have n outer loopBlock, reference them all @@ -757,7 +757,7 @@ func (m *Machine) doOpFuncLit() { } } // set as a deferred operation - if lvBox != nil && !lvBox.isFilled && !isFilled { + if lvBox != nil && !lvBox.isFilled && !isReuse { lvBox.isFilled = true // set for the last LoopValuesBox of last loopBlock loopData = append(loopData, &LoopBlockData{index: 0, loopValuesBox: lvBox}) } diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index bf47105d781..f809a6af439 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -709,8 +709,8 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { cx := evalConst(store, last, n) // built-in functions must be called. if !cx.IsUndefined() && - cx.T.Kind() == FuncKind && - ftype != TRANS_CALL_FUNC { + cx.T.Kind() == FuncKind && + ftype != TRANS_CALL_FUNC { panic(fmt.Sprintf( "use of builtin %s not in function call", n.Name)) @@ -1352,8 +1352,8 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // Case 1: If receiver is pointer type but n.X is // not: if rcvr != nil && - rcvr.Kind() == PointerKind && - nxt2.Kind() != PointerKind { + rcvr.Kind() == PointerKind && + nxt2.Kind() != PointerKind { // Go spec: "If x is addressable and &x's // method set contains m, x.m() is shorthand // for (&x).m()" @@ -1385,8 +1385,8 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { )) } } else if len(tr) > 0 && - tr[len(tr)-1].IsDerefType() && - nxt2.Kind() != PointerKind { + tr[len(tr)-1].IsDerefType() && + nxt2.Kind() != PointerKind { // Case 2: If tr[0] is deref type, but xt // is not pointer type, replace n.X with // &RefExpr{X: n.X}. @@ -1525,7 +1525,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { if len(n.Lhs) != len(cft.Results) { panic(fmt.Sprintf( "assignment mismatch: "+ - "%d variables but %s returns %d values", + "%d variables but %s returns %d values", len(n.Lhs), cx.Func.String(), len(cft.Results))) } for i, lx := range n.Lhs { @@ -1594,7 +1594,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { if len(n.Lhs) != len(cft.Results) { panic(fmt.Sprintf( "assignment mismatch: "+ - "%d variables but %s returns %d values", + "%d variables but %s returns %d values", len(n.Lhs), cx.Func.String(), len(cft.Results))) } case *TypeAssertExpr: @@ -1833,7 +1833,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // evaluate typed value for static definition. for i, vx := range n.Values { if cx, ok := vx.(*ConstExpr); ok && - !cx.TypedValue.IsUndefined() { + !cx.TypedValue.IsUndefined() { if n.Const { // const _ = : static block should contain value tvs[i] = cx.TypedValue @@ -2219,7 +2219,7 @@ func funcOf(last BlockNode) (BlockNode, *FuncTypeExpr) { } func findGotoLabel(last BlockNode, label Name) ( - bn BlockNode, depth uint8, bodyIdx int, + bn BlockNode, depth uint8, bodyIdx int, ) { for { switch cbn := last.(type) { @@ -2470,8 +2470,8 @@ func checkType(xt Type, dt Type, autoNative bool) { if ddt, ok := dt.(*DeclaredType); ok { // types must match exactly. if !dxt.sealed && !ddt.sealed && - dxt.PkgPath == ddt.PkgPath && - dxt.Name == ddt.Name { // not yet sealed + dxt.PkgPath == ddt.PkgPath && + dxt.Name == ddt.Name { // not yet sealed return // ok } else if dxt.TypeID() == ddt.TypeID() { return // ok From 1630655a4ca8b057d5dbee75781d5d1089a3b4eb Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Fri, 26 Jan 2024 14:27:13 +0800 Subject: [PATCH 24/32] fix lint --- gnovm/pkg/gnolang/op_expressions.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index 8298d7a6721..1ca9716de97 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -764,7 +764,7 @@ func (m *Machine) doOpFuncLit() { if debug { for i, ts := range loopData { - fmt.Printf("========TransientLoopData[%d] is: %s, addr: %p, index: %d \n", i, ts.loopValuesBox, &ts.loopValuesBox, ts.index) + fmt.Printf("========TransientLoopData[%d] is: %s, index: %d \n", i, ts.loopValuesBox, ts.index) } } From 90c629d7ae8443bba493517299fd2da65404c2e4 Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Wed, 31 Jan 2024 17:12:27 +0800 Subject: [PATCH 25/32] tidy --- gnovm/pkg/gnolang/op_exec.go | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/gnovm/pkg/gnolang/op_exec.go b/gnovm/pkg/gnolang/op_exec.go index 303a655d6ff..6f240c13cc0 100644 --- a/gnovm/pkg/gnolang/op_exec.go +++ b/gnovm/pkg/gnolang/op_exec.go @@ -80,8 +80,7 @@ func (m *Machine) doOpExec(op Op) { s := m.PeekStmt(1) // TODO: PeekStmt1()? if debug { debug.Printf("PEEK STMT: %v\n", s) - debug.Printf("op: %v\n", op) - // debug.Printf("%v\n", m) + debug.Printf("%v\n", m) } // NOTE this could go in the switch statement, and we could @@ -464,16 +463,11 @@ EXEC_SWITCH: if debug { debug.Printf("EXEC: %v\n", s) } - // TODO: add case log for debug switch cs := s.(type) { case *AssignStmt: switch cs.Op { case ASSIGN: - // post assign, use value of lhs to update captured value, name as ID - // m.PushOp(OpPostAssign) m.PushOp(OpAssign) - // pre assign, check rhs is funcLitExpr - // m.PushOp(OpPreAssign) case ADD_ASSIGN: m.PushOp(OpAddAssign) case SUB_ASSIGN: @@ -511,8 +505,6 @@ EXEC_SWITCH: m.PushExpr(rx) m.PushOp(OpEval) } - // m.PushOp(OpPreAssign) - if cs.Op != DEFINE { // For each Lhs, push eval operation if needed. for i := len(cs.Lhs) - 1; 0 <= i; i-- { @@ -533,12 +525,8 @@ EXEC_SWITCH: m.PushExpr(cs.X) m.PushOp(OpEval) case *ForStmt: - debug.Println("-----ForStmt") m.PushFrameBasic(cs) - debug.Printf("cs: %v \n", cs) - debug.Printf("parent: %v \n", m.LastBlock()) b := m.Alloc.NewBlock(cs, m.LastBlock()) - debug.Printf("b: %v \n", b) b.bodyStmt = bodyStmt{ Body: cs.Body, BodyLen: len(cs.Body), From 3af55bbeead71c612b94f60a374b4ee29d2f069d Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Wed, 31 Jan 2024 17:18:41 +0800 Subject: [PATCH 26/32] rm unused comment --- gnovm/pkg/gnolang/op_expressions.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index 1ca9716de97..591c9885157 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -748,7 +748,7 @@ func (m *Machine) doOpFuncLit() { // record in loop block loopBlock.GetBodyStmt().LoopValuesBox = lvBox } else { // reuse last replica's. (in same block). - isReuse = true // use isFilled instead + isReuse = true // get cursor by name lvBox.transient[lvBox.getIndexByName(nx.Name)].cursor++ // inc by iteration // each fv may have n outer loopBlock, reference them all From 925e9ddd539d667980ebb1cd371636ee57b98a1b Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Tue, 20 Feb 2024 23:21:31 +0800 Subject: [PATCH 27/32] move closure pattern identification to preprocess as PoC; trying to fix goto with label case --- gnovm/pkg/gnolang/machine.go | 2 +- gnovm/pkg/gnolang/nodes.go | 9 ++ gnovm/pkg/gnolang/op_exec.go | 68 +++++++---- gnovm/pkg/gnolang/op_expressions.go | 183 ++++++++++++++++------------ gnovm/pkg/gnolang/preprocess.go | 139 ++++++++++++++++++++- gnovm/pkg/gnolang/transcribe.go | 6 + gnovm/pkg/gnolang/values.go | 38 +++++- gnovm/tests/debug/closure9.gno | 26 ++++ gnovm/tests/debug2/1.gno | 29 +++++ gnovm/tests/debug2/1a.gnoa | 23 ++++ gnovm/tests/debug2/1b.gno | 28 +++++ gnovm/tests/debug2/2.gno | 34 ++++++ gnovm/tests/debug2/3.gno | 25 ++++ gnovm/tests/debug2/4.gno | 26 ++++ gnovm/tests/debug2/5.gno | 33 +++++ 15 files changed, 558 insertions(+), 111 deletions(-) create mode 100644 gnovm/tests/debug/closure9.gno create mode 100644 gnovm/tests/debug2/1.gno create mode 100644 gnovm/tests/debug2/1a.gnoa create mode 100644 gnovm/tests/debug2/1b.gno create mode 100644 gnovm/tests/debug2/2.gno create mode 100644 gnovm/tests/debug2/3.gno create mode 100644 gnovm/tests/debug2/4.gno create mode 100644 gnovm/tests/debug2/5.gno diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go index 802d2cd85c2..91cbc0b0c21 100644 --- a/gnovm/pkg/gnolang/machine.go +++ b/gnovm/pkg/gnolang/machine.go @@ -718,7 +718,7 @@ func (m *Machine) Eval(x Expr) []TypedValue { // static types and values. func (m *Machine) EvalStatic(last BlockNode, x Expr) TypedValue { if debug { - m.Printf("Machine.EvalStatic(%v, %v)\n", last, x) + //m.Printf("Machine.EvalStatic(%v, %v)\n", last, x) } // X must have been preprocessed. if x.GetAttribute(ATTR_PREPROCESSED) == nil { diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index 7a4f00b9a3d..59d028ef3ac 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -870,6 +870,14 @@ type SwitchClauseStmt struct { // ---------------------------------------- // bodyStmt (persistent) +// this assumes that a goto stmt to less liners forms an implicit loop +// if an funcLitExpr embeded, do capture. +type LoopBody struct { + isLoop bool + start int // line of label start + end int // line of goto stmt +} + // NOTE: embedded in Block. type bodyStmt struct { Attributes @@ -884,6 +892,7 @@ type bodyStmt struct { Post Stmt // for ForStmt LoopValuesBox *LoopValuesBox // a series of transient values of captured var generated as the iteration goes on isLoop bool + loopBody *LoopBody Active Stmt // for PopStmt() Key Expr // for RangeStmt Value Expr // for RangeStmt diff --git a/gnovm/pkg/gnolang/op_exec.go b/gnovm/pkg/gnolang/op_exec.go index 6f240c13cc0..8b758f50b92 100644 --- a/gnovm/pkg/gnolang/op_exec.go +++ b/gnovm/pkg/gnolang/op_exec.go @@ -44,29 +44,34 @@ SelectStmt -> */ func updateCapturedValue(m *Machine, lb *Block) { - if lb.GetBodyStmt().isLoop { // do we need this? - bs := lb.GetBodyStmt() - if bs.LoopValuesBox != nil && bs.LoopValuesBox.isFilled && !bs.LoopValuesBox.isTainted { - var isSealed bool - for i, tt := range bs.LoopValuesBox.transient { - nvp := lb.Source.GetPathForName(m.Store, tt.nx.Name) - ptr := m.LastBlock().GetPointerTo(m.Store, nvp) - tv := ptr.Deref() - // update context use previously recorded value - // inner loops iterates twice while outer loop iterates once. - // it's an alignment for the values of out loop block. - // TODO: hard to understand, more doc or e.g. - expandRatio := bs.LoopValuesBox.transient[i].cursor + 1 - len(bs.LoopValuesBox.transient[i].values) // 2 - 0 - for j := 0; j < expandRatio; j++ { - bs.LoopValuesBox.transient[i].values = append(bs.LoopValuesBox.transient[i].values, tv) - } - isSealed = true - } - if isSealed { - bs.LoopValuesBox.isSealed = true // seal for this LoopValuesBox for closure execution. + debug.Println("---updateCapturedValue") + //if lb.GetBodyStmt().isLoop { // do we need this? + //lb.bodyStmt = lb.Source.GetStaticBlock().bodyStmt + bs := lb.GetBodyStmt() + //bs := lb.Source.GetStaticBlock().GetBodyStmt() + if bs.LoopValuesBox != nil && bs.LoopValuesBox.isFilled && !bs.LoopValuesBox.isTainted { + var isSealed bool + for i, tt := range bs.LoopValuesBox.transient { + nvp := lb.Source.GetPathForName(m.Store, tt.nx.Name) + ptr := m.LastBlock().GetPointerTo(m.Store, nvp) + tv := ptr.Deref() + debug.Printf("---transient value is: %v \n", tv) + // update context use previously recorded value + // inner loops iterates twice while outer loop iterates once. + // it's an alignment for the values of out loop block. + // TODO: hard to understand, more doc or e.g. + expandRatio := bs.LoopValuesBox.transient[i].cursor + 1 - len(bs.LoopValuesBox.transient[i].values) // 2 - 0 + for j := 0; j < expandRatio; j++ { + bs.LoopValuesBox.transient[i].values = append(bs.LoopValuesBox.transient[i].values, tv) } + isSealed = true + } + if isSealed { + bs.LoopValuesBox.isSealed = true // seal for this LoopValuesBox for closure execution. } + debug.Printf("---lvBox after updateValue is: %v \n", bs.LoopValuesBox) } + //} } //---------------------------------------- @@ -77,10 +82,11 @@ func updateCapturedValue(m *Machine, lb *Block) { // operation is that the value of the expression is pushed onto the stack. func (m *Machine) doOpExec(op Op) { + debug.Printf("---doOpExec, op: %v \n", op) s := m.PeekStmt(1) // TODO: PeekStmt1()? if debug { debug.Printf("PEEK STMT: %v\n", s) - debug.Printf("%v\n", m) + //debug.Printf("%v\n", m) } // NOTE this could go in the switch statement, and we could @@ -141,6 +147,7 @@ func (m *Machine) doOpExec(op Op) { s = next goto EXEC_SWITCH } else if bs.NextBodyIndex == bs.BodyLen { + // exiting loop updateCapturedValue(m, lb) // (queue to) go back. if bs.Cond != nil { @@ -533,7 +540,7 @@ EXEC_SWITCH: NextBodyIndex: -2, Cond: cs.Cond, Post: cs.Post, - isLoop: true, + loopBody: &LoopBody{isLoop: true}, } m.PushBlock(b) m.PushOp(OpForLoop) @@ -549,6 +556,7 @@ EXEC_SWITCH: m.PushOp(OpExec) } case *IfStmt: + debug.Printf("---IfStmt: %v \n", cs) b := m.Alloc.NewBlock(cs, m.LastBlock()) m.PushBlock(b) m.PushOp(OpPopBlock) @@ -620,7 +628,8 @@ EXEC_SWITCH: Key: cs.Key, Value: cs.Value, Op: cs.Op, - isLoop: true, + loopBody: &LoopBody{isLoop: true}, + //isLoop: true, } m.PushBlock(b) // TODO: replace with "cs.Op". @@ -654,6 +663,7 @@ EXEC_SWITCH: m.PushExpr(cs.X) m.PushOp(OpEval) case *BranchStmt: + debug.Printf("---BranchStmt, cs: %v \n", cs) switch cs.Op { case BREAK: // Pop frames until for/range @@ -697,18 +707,29 @@ EXEC_SWITCH: } } case GOTO: + debug.Println("---GOTO ", cs.String()) for i := uint8(0); i < cs.Depth; i++ { m.PopBlock() } last := m.LastBlock() + debug.Printf("lb: %v \n", last) + debug.Printf("lb.Source: %v \n", last.Source) + debug.Printf("lb.Source: %T \n", last.Source) bs := last.GetBodyStmt() + debug.Printf("lb.Source isLoop? : %t \n", last.Source.GetStaticBlock().bodyStmt.isLoop) + debug.Printf("bs.isLoop: %v, addr: %p \n", bs.isLoop, &bs) + m.NumOps = bs.NumOps m.NumValues = bs.NumValues m.Exprs = m.Exprs[:bs.NumExprs] m.Stmts = m.Stmts[:bs.NumStmts] bs.NextBodyIndex = cs.BodyIndex + //bs.isLoop = true bs.Active = bs.Body[cs.BodyIndex] // prefill + updateCapturedValue(m, last) + case FALLTHROUGH: + debug.Println("---FALLTHROUGH") ss, ok := m.LastFrame().Source.(*SwitchStmt) if !ok { // fallthrough is only allowed in a switch statement @@ -803,6 +824,7 @@ EXEC_SWITCH: m.PushStmt(cs.Init) } case *BlockStmt: + debug.Println("---BlockStmt") b := m.Alloc.NewBlock(cs, m.LastBlock()) m.PushBlock(b) m.PushOp(OpPopBlock) diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index 591c9885157..a5208e15adc 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -3,7 +3,6 @@ package gnolang import ( "fmt" "reflect" - "sort" ) // OpBinary1 defined in op_binary.go @@ -678,90 +677,116 @@ func (m *Machine) doOpStructLit() { }) } +// TODO: migrate loopdata from sb(BlockNode) to b(*Block) func (m *Machine) doOpFuncLit() { x := m.PopExpr().(*FuncLitExpr) + debug.Printf("---doOpFuncLit, x: %v \n", x) + debug.Printf("---doOpFuncLit, get label: %v, getLine: %d \n", x.GetLabel(), x.GetLine()) + lb := m.LastBlock() - captures := make([]*NameExpr, 0) - for _, n := range x.GetExternNames() { - vp := x.GetPathForName(m.Store, n) - if vp.Depth == 0 { // skip uverse name - continue - } - vp.Depth -= 1 // from the perspective of funcLit block - nx := &NameExpr{ - Name: n, - Path: vp, - } - captures = append(captures, nx) - } + debug.Printf("---doOpFuncLit, lb.Source.sb.bs.lvBox: %v \n", lb.Source.GetStaticBlock().bodyStmt.LoopValuesBox) - // sort it so to traverse block in order from inner to outer - sortDepth := func(i, j int) bool { - return int(captures[i].Path.Depth) < int(captures[j].Path.Depth) - } - sort.Slice(captures, sortDepth) - - var ( - isLoopBlock bool - loopBlock *Block // e.g. a `for` block that is outside of funcLit block - lvBox *LoopValuesBox // container per block to store transient values for captured vars - lastGen, gen uint8 - isReuse bool - loopData []*LoopBlockData // for fv to track all transient values - ) - - // when iterating goes on, funcValue will yield several replicas, each of which should capture a slice of - // transient values for a nameExpr. - // e.g. `for i:=0, i++; i<2{ x := i }`, the transient values for x should be [0 ,1], this is recorded and - // used when the funcLit is executed, namely, closure. - // more complex situation like: - // for i:=0, i++; i<2{ x := i for j:=0, j++; j<2{y := j}}, - // in this case, there will be 4 replica of fv, and 3 loopBlock. - // the transient state of x, y is: [0,0], [0,1], [1,0], [1,1]. - // the outer block will hold transient values of [0,0,1,1] for `x`, and inner block 1 will hold [0,1] for `y`, - // another inner block 2 holds [0,1] for y too. - for _, nx := range captures { - // start search block, in the order of small depth to big depth - loopBlock, isLoopBlock, gen = findLoopBlockWithPath(m.Store, lb, nx) - if lastGen == 0 { - lastGen = gen - } else if gen != lastGen { // if enter new level of block, pack last box - lastGen = gen - if lvBox != nil && !isReuse { // has something to pack - lvBox.isFilled = true - loopData = append(loopData, &LoopBlockData{index: 0, loopValuesBox: lvBox}) - } - } - if isLoopBlock { - // every loopBlock holds a loopValuesBox that contains a slice of transient value generated as the iterations goes on. - // funcValue references this loopValuesBox for future use(when closure executing) - lvBox = loopBlock.GetBodyStmt().LoopValuesBox // get LoopValuesBox from specific block, that is shared across current level of for/range loop - if lvBox == nil { - lvBox = &LoopValuesBox{} // init - } - if !lvBox.isFilled { // for replicas of fv, the captured names are same, so fill only once for further reuse. - tst := &Transient{ - nx: nx, - cursor: 0, // inc every iteration, implies sequence of a fv, and index of transient values. - } - lvBox.transient = append(lvBox.transient, tst) - // record in loop block - loopBlock.GetBodyStmt().LoopValuesBox = lvBox - } else { // reuse last replica's. (in same block). - isReuse = true - // get cursor by name - lvBox.transient[lvBox.getIndexByName(nx.Name)].cursor++ // inc by iteration - // each fv may have n outer loopBlock, reference them all - loopData = append(loopData, &LoopBlockData{index: lvBox.transient[lvBox.getIndexByName(nx.Name)].cursor, loopValuesBox: lvBox}) - } - } - } - // set as a deferred operation - if lvBox != nil && !lvBox.isFilled && !isReuse { - lvBox.isFilled = true // set for the last LoopValuesBox of last loopBlock - loopData = append(loopData, &LoopBlockData{index: 0, loopValuesBox: lvBox}) + var lvBox *LoopValuesBox // container per block to store transient values for captured vars + var loopData []*LoopBlockData // for fv to track all transient values + + lvBox = lb.Source.GetStaticBlock().bodyStmt.LoopValuesBox + // copy to block + lb.bodyStmt.LoopValuesBox = lvBox + + for _, tst := range lvBox.transient { + tst.cursor++ // increase cursor of every nameExpr } + // each fv may have n outer loopBlock, reference them all + loopData = append(loopData, &LoopBlockData{index: lvBox.transient[0].cursor, loopValuesBox: lvBox}) + + //captures := make([]*NameExpr, 0) + //for _, n := range x.GetExternNames() { + // vp := x.GetPathForName(m.Store, n) + // if vp.Depth == 0 { // skip uverse name + // continue + // } + // vp.Depth -= 1 // from the perspective of funcLit block + // nx := &NameExpr{ + // Name: n, + // Path: vp, + // } + // captures = append(captures, nx) + //} + // + //// sort it so to traverse block in order from inner to outer + //sortDepth := func(i, j int) bool { + // return int(captures[i].Path.Depth) < int(captures[j].Path.Depth) + //} + //sort.Slice(captures, sortDepth) + // + //var ( + // isLoopBlock bool + // loopBlock *Block // e.g. a `for` block that is outside of funcLit block + // lvBox *LoopValuesBox // container per block to store transient values for captured vars + // lastGen, gen uint8 + // isReuse bool + // loopData []*LoopBlockData // for fv to track all transient values + //) + // + //// when iterating goes on, funcValue will yield several replicas, each of which should capture a slice of + //// transient values for a nameExpr. + //// e.g. `for i:=0, i++; i<2{ x := i }`, the transient values for x should be [0 ,1], this is recorded and + //// used when the funcLit is executed, namely, closure. + //// more complex situation like: + //// for i:=0, i++; i<2{ x := i for j:=0, j++; j<2{y := j}}, + //// in this case, there will be 4 replica of fv, and 3 loopBlock. + //// the transient state of x, y is: [0,0], [0,1], [1,0], [1,1]. + //// the outer block will hold transient values of [0,0,1,1] for `x`, and inner block 1 will hold [0,1] for `y`, + //// another inner block 2 holds [0,1] for y too. + //for _, nx := range captures { + // // start search block, in the order of small depth to big depth + // loopBlock, isLoopBlock, gen = findLoopBlockWithPath(m.Store, lb, nx, x.GetLine()) + // debug.Printf("nx name: %v, nx path: %v, isLoopBlock: %t \n", nx.Name, nx.Path, isLoopBlock) + // debug.Printf("loopBlock: %v \n", loopBlock) + // if lastGen == 0 { + // lastGen = gen + // } else if gen != lastGen { // if enter new level of block, pack last box + // lastGen = gen + // if lvBox != nil && !isReuse { // has something to pack + // lvBox.isFilled = true + // loopData = append(loopData, &LoopBlockData{index: 0, loopValuesBox: lvBox}) + // } + // } + // if isLoopBlock { + // // every loopBlock holds a loopValuesBox that contains a slice of transient value generated as the iterations goes on. + // // funcValue references this loopValuesBox for future use(when closure executing) + // lvBox = loopBlock.GetBodyStmt().LoopValuesBox // get LoopValuesBox from specific block, that is shared across current level of for/range loop + // if lvBox == nil { + // lvBox = &LoopValuesBox{} // init + // } + // if !lvBox.isFilled { // for replicas of fv, the captured names are same, so fill only once for further reuse. + // debug.Printf("---fill nx: %v \n", nx) + // tst := &Transient{ + // nx: nx, + // cursor: 0, // inc every iteration, implies sequence of a fv, and index of transient values. + // } + // lvBox.transient = append(lvBox.transient, tst) + // // record in loop block + // loopBlock.GetBodyStmt().LoopValuesBox = lvBox + // } else { // reuse last replica's. (in same block). + // isReuse = true + // // get cursor by name + // debug.Printf("reuse by else loop in same block, nx.Name: %s \n", nx.Name) + // debug.Printf("index of x is: %d \n", lvBox.getIndexByName(nx.Name)) + // lvBox.transient[lvBox.getIndexByName(nx.Name)].cursor++ // inc by iteration + // // each fv may have n outer loopBlock, reference them all + // loopData = append(loopData, &LoopBlockData{index: lvBox.transient[lvBox.getIndexByName(nx.Name)].cursor, loopValuesBox: lvBox}) + // } + // } + //} + + //// set as a deferred operation + //if lvBox != nil && !lvBox.isFilled && !isReuse { + // lvBox.isFilled = true // set for the last LoopValuesBox of last loopBlock + // loopData = append(loopData, &LoopBlockData{index: 0, loopValuesBox: lvBox}) + //} + if debug { for i, ts := range loopData { fmt.Printf("========TransientLoopData[%d] is: %s, index: %d \n", i, ts.loopValuesBox, ts.index) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index f809a6af439..507e8b3bd95 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -4,6 +4,7 @@ import ( "fmt" "math/big" "reflect" + "sort" "github.com/gnolang/gno/tm2/pkg/errors" ) @@ -94,12 +95,56 @@ func PredefineFileSet(store Store, pn *PackageNode, fset *FileSet) { } } +func dumpStack(stack []BlockNode) { + println("---dumpStack, height of stack is: ", len(stack)) + for i, bn := range stack { + println("=================================") + fmt.Printf("blockNode[%d] type is: %T, value is: %v \n", i, bn, bn) + println("=================================") + } +} + +func getBlockNodeAt(stack []BlockNode, offset int) BlockNode { + debug.Println("---getParentBlockNode") + if len(stack) == 0 { + return nil + } else { + return stack[len(stack)-1-offset] + } +} + +// find nearest block is loop and contains name of n +func findLoopStaticBlockAndPath(stack []BlockNode, nx *NameExpr) (BlockNode, bool, uint8) { + debug.Printf("---findLoopStaticBlockAndPath, nx: %v \n", nx) + var gen uint8 = 1 // which level it is + var sb BlockNode + // nav to target level + for i := uint8(0); i < nx.Path.Depth+1; i++ { // find target block at certain depth + sb = getBlockNodeAt(stack, int(i)) + gen++ + } + + debug.Printf("---got target sb: %v \n", sb) + // find name + if sb.GetStaticBlock().bodyStmt.loopBody.isLoop { + names := sb.GetBlockNames() + for _, name := range names { + if nx.Name == name { // find n in this block + return sb, true, gen + } + } + } + return nil, false, gen +} + // This counter ensures (during testing) that certain functions // (like ConvertUntypedTo() for bigints and strings) // are only called during the preprocessing stage. // It is a counter because Preprocess() is recursive. var preprocessing int +//var stack []BlockNode = make([]BlockNode, 0, 32) + // Preprocess n whose parent block node is ctx. If any names // are defined in another file, generally you must call // PredefineFileSet() on the whole fileset first before calling @@ -150,8 +195,8 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { SetNodeLocations(pkgPath, fileName, fn) } - // create stack of BlockNodes. var stack []BlockNode = make([]BlockNode, 0, 32) + // create stack of BlockNodes. var last BlockNode = ctx lastpn := packageOf(last) stack = append(stack, last) @@ -276,6 +321,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // TRANS_BLOCK ----------------------- case *ForStmt: pushInitBlock(n, &last, &stack) + n.bodyStmt.loopBody = &LoopBody{isLoop: true} // TRANS_BLOCK ----------------------- case *IfStmt: @@ -740,6 +786,82 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } } } + case *FuncLitExpr: + debug.Printf("---trans_leave, funcLitExpr: %v \n", n) + // prepare captured nameExprs + captures := make([]*NameExpr, 0) + for _, name := range n.GetExternNames() { + vp := n.GetPathForName(store, name) + if vp.Depth == 0 { // skip uverse name + continue + } + vp.Depth -= 1 // from the perspective of funcLit block + nx := &NameExpr{ + Name: name, + Path: vp, + } + captures = append(captures, nx) + } + + // sort it so to traverse block in order from inner to outer + sortDepth := func(i, j int) bool { + return int(captures[i].Path.Depth) < int(captures[j].Path.Depth) + } + sort.Slice(captures, sortDepth) + + // loop related vars + var ( + isLoopBlock bool + loopBlock BlockNode // e.g. a `for` block that is outside of funcLit block + lvBox *LoopValuesBox // container per block to store transient values for captured vars + lastGen, gen uint8 + isReuse bool + loopData []*LoopBlockData // referenced by fv to track all transient values + ) + + // when iterating goes on, funcValue will yield several replicas, each of which should capture a slice of + // transient values for a nameExpr. + // e.g. `for i:=0, i++; i<2{ x := i }`, the transient values for x should be [0 ,1], this is recorded and + // used when the funcLit is executed, namely, closure. + // more complex situation like: + // for i:=0, i++; i<2{ x := i for j:=0, j++; j<2{y := j}}, + // in this case, there will be 4 replica of fv, and 3 loopBlock. + // the transient state of x, y is: [0,0], [0,1], [1,0], [1,1]. + // the outer block will hold transient values of [0,0,1,1] for `x`, and inner block 1 will hold [0,1] for `y`, + // another inner block 2 holds [0,1] for y too. + for _, nx := range captures { + // start search block, in the order of small depth to big depth + loopBlock, isLoopBlock, gen = findLoopStaticBlockAndPath(stack, nx) + debug.Printf("nx name: %v, nx path: %v, isLoopBlock: %t \n", nx.Name, nx.Path, isLoopBlock) + debug.Printf("loopBlock: %v \n", loopBlock) + debug.Printf("depth: %v \n", gen) + if lastGen == 0 { + lastGen = gen + } else if gen != lastGen { // if enter new level of block, pack last box + lastGen = gen + if lvBox != nil && !isReuse { // has something to pack + lvBox.isFilled = true + loopData = append(loopData, &LoopBlockData{index: 0, loopValuesBox: lvBox}) + } + } + if isLoopBlock { + // every loopBlock holds a loopValuesBox that contains a slice of transient value generated as the iterations goes on. + // funcValue references this loopValuesBox for future use(when closure executing) + lvBox = loopBlock.GetStaticBlock().GetBodyStmt().LoopValuesBox // get LoopValuesBox from specific block, that is shared across current level of for/range loop + if lvBox == nil { + lvBox = &LoopValuesBox{} // init + } + debug.Printf("---fill nx: %v \n", nx) + tst := &Transient{ + nx: nx, + cursor: -1, // inc every iteration, implies sequence of a fv, and index of transient values. + } + lvBox.transient = append(lvBox.transient, tst) + lvBox.isFilled = true + // record in loop block + loopBlock.GetStaticBlock().GetBodyStmt().LoopValuesBox = lvBox + } + } // TRANS_LEAVE ----------------------- case *BasicLitExpr: @@ -1634,9 +1756,13 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { case BREAK: case CONTINUE: case GOTO: - _, depth, index := findGotoLabel(last, n.Label) + debug.Printf("---branchStmt, n.Label: %v , n.Line: %d \n", n.GetLabel(), n.GetLine()) + _, depth, index, line := findGotoLabel(last, n.Label) n.Depth = depth n.BodyIndex = index + debug.Println("depth:, index:, line:", depth, index, line) + debug.Printf("---BranchStmt, last: %v, %T \n", last, last) + last.GetStaticBlock().SetLoopBody(line, n.GetLine()) case FALLTHROUGH: if swchC, ok := last.(*SwitchClauseStmt); ok { // last is a switch clause, find its index in the switch and assign @@ -2219,8 +2345,9 @@ func funcOf(last BlockNode) (BlockNode, *FuncTypeExpr) { } func findGotoLabel(last BlockNode, label Name) ( - bn BlockNode, depth uint8, bodyIdx int, + bn BlockNode, depth uint8, bodyIdx int, line int, ) { + var ls Stmt // labeled stmt for { switch cbn := last.(type) { case *IfStmt, *SwitchStmt: @@ -2232,9 +2359,10 @@ func findGotoLabel(last BlockNode, label Name) ( panic("unexpected package blocknode") case *FuncLitExpr, *FuncDecl: body := cbn.GetBody() - _, bodyIdx = body.GetLabeledStmt(label) + ls, bodyIdx = body.GetLabeledStmt(label) if bodyIdx != -1 { bn = cbn + line = ls.GetLine() return } else { panic(fmt.Sprintf( @@ -2243,9 +2371,10 @@ func findGotoLabel(last BlockNode, label Name) ( } case *BlockStmt, *ForStmt, *IfCaseStmt, *RangeStmt, *SelectCaseStmt, *SwitchClauseStmt: body := cbn.GetBody() - _, bodyIdx = body.GetLabeledStmt(label) + ls, bodyIdx = body.GetLabeledStmt(label) if bodyIdx != -1 { bn = cbn + line = ls.GetLine() return } else { last = cbn.GetParentNode(nil) diff --git a/gnovm/pkg/gnolang/transcribe.go b/gnovm/pkg/gnolang/transcribe.go index 3906497f276..bc9638326fa 100644 --- a/gnovm/pkg/gnolang/transcribe.go +++ b/gnovm/pkg/gnolang/transcribe.go @@ -260,6 +260,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc cnn.Elts[idx] = KeyValueExpr{Key: k, Value: v} } case *FuncLitExpr: + debug.Printf("---transcribe, funcLitExpr: %v \n", cnn) cnn.Type = *transcribe(t, nns, TRANS_FUNCLIT_TYPE, 0, &cnn.Type, &c).(*FuncTypeExpr) if isStopOrSkip(nc, c) { return @@ -271,6 +272,7 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*FuncLitExpr) } + for idx := range cnn.Body { cnn.Body[idx] = transcribe(t, nns, TRANS_FUNCLIT_BODY, idx, cnn.Body[idx], &c).(Stmt) if isBreak(c) { @@ -279,6 +281,10 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } } + // identify closure patterns + ns := cnn.GetExternNames() + debug.Printf("---transcribe, externs: %v \n", ns) + //printStack() case *FieldTypeExpr: cnn.Type = transcribe(t, nns, TRANS_FIELDTYPE_TYPE, 0, cnn.Type, &c).(Expr) if isStopOrSkip(nc, c) { diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index d24a7365cd5..4a869a3b3c6 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -569,6 +569,7 @@ type LoopValuesBox struct { } func (tsb *LoopValuesBox) getIndexByName(n Name) int { + debug.Printf("---getIndexByName, target n is: %s, len of transient is: %d \n", n, len(tsb.transient)) for i, tt := range tsb.transient { if n == tt.nx.Name { return i @@ -580,7 +581,7 @@ func (tsb *LoopValuesBox) getIndexByName(n Name) int { func (tsb *LoopValuesBox) String() string { var s string s += "\n" - s += "==================time LoopValuesBox===================\n" + s += "==================LoopValuesBox===================\n" s += fmt.Sprintf("isFilled: %v \n", tsb.isFilled) for i, t := range tsb.transient { s += fmt.Sprintf("nx[%d]: %v \n", i, t.nx) @@ -2304,6 +2305,14 @@ func NewBlock(source BlockNode, parent *Block) *Block { } } +func (b *Block) SetLoopBody(start, end int) { + debug.Printf("---SetIsLoop, bs: %v, bs.Addr: %p \n", b.bodyStmt, &b.bodyStmt) + if b.bodyStmt.loopBody == nil { + b.bodyStmt.loopBody = &LoopBody{isLoop: true, start: start, end: end} + } + debug.Printf("---SetIsLoop, bs: %v, bs.Addr: %p \n", b.bodyStmt, &b.bodyStmt) +} + func (b *Block) UpdateValue(index int, tv TypedValue) { for i := range b.Values { if i == index { @@ -2411,14 +2420,37 @@ func (b *Block) GetPointerTo(store Store, path ValuePath) PointerValue { } // find nearest block is loop and contains name of n -func findLoopBlockWithPath(store Store, b *Block, nx *NameExpr) (*Block, bool, uint8) { +func findLoopBlockWithPath(store Store, b *Block, nx *NameExpr, fLine int) (*Block, bool, uint8) { + debug.Printf("---findLoopBlockWithPath, b: %v \n", b) + debug.Printf("---findLoopBlockWithPath, b.bs: %v \n", b.bodyStmt) + debug.Printf("---findLoopBlockWithPath, b.Source: %v \n", b.Source) + debug.Printf("---findLoopBlockWithPath, b.Source.bs: %v \n", b.Source.GetStaticBlock().bodyStmt) var gen uint8 = 1 for i := uint8(1); i < nx.Path.Depth; i++ { // find target block at certain depth b = b.GetParent(store) gen++ } - if b.GetBodyStmt().isLoop { // is loop + debug.Printf("---findLoopBlockWithPath, fLine: %v \n", fLine) + debug.Printf("---findLoopBlockWithPath, loopBody: %v \n", b.Source.GetStaticBlock().bodyStmt.loopBody) + //debug.Printf("---findLoopBlockWithPath, start: %v \n", b.Source.GetStaticBlock().bodyStmt.loopBody.start) + //debug.Printf("---findLoopBlockWithPath, end: %v \n", b.Source.GetStaticBlock().bodyStmt.loopBody.end) + + var isImplicitLoop bool + // tmp, if static not nil, means some work done in preprocess, using static + // TODO: all staff in preprocess + if b.Source.GetStaticBlock().bodyStmt.loopBody != nil { + if b.Source.GetStaticBlock().bodyStmt.loopBody.isLoop { + debug.Println("---is potential loop") + if b.Source.GetStaticBlock().bodyStmt.loopBody.start < fLine && fLine < b.Source.GetStaticBlock().bodyStmt.loopBody.end { + debug.Println("---is implicit loop") + isImplicitLoop = true + } + } + } + + if b.GetBodyStmt().loopBody.isLoop || isImplicitLoop { // is loop + //if b.GetBodyStmt().isLoop || isImplicitLoop { // is loop names := b.GetSource(store).GetBlockNames() for _, name := range names { if nx.Name == name { // find n in this block diff --git a/gnovm/tests/debug/closure9.gno b/gnovm/tests/debug/closure9.gno new file mode 100644 index 00000000000..0de5fc8292e --- /dev/null +++ b/gnovm/tests/debug/closure9.gno @@ -0,0 +1,26 @@ +package main + +// this is a fix, intuitive, and same with Go +func main() { + var fns []func() int + for i := 0; i < 3; i++ { + x := i + f := func() int { + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Go Output: +// 0 +// 1 +// 2 + +// Output: +// 0 +// 1 +// 2 diff --git a/gnovm/tests/debug2/1.gno b/gnovm/tests/debug2/1.gno new file mode 100644 index 00000000000..77db6f19c8a --- /dev/null +++ b/gnovm/tests/debug2/1.gno @@ -0,0 +1,29 @@ +package main + +func main() { + var y, counter int + var f []func() + defer func() { + for _, ff := range f { + ff() + } + }() + + // this is actually a implicit for loop +LABEL_1: + if counter == 2 { + return + } + x := y + f = append(f, func() { println(x) }) + y++ + counter++ + goto LABEL_1 // this is the edge condition, break? continue? +} + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 diff --git a/gnovm/tests/debug2/1a.gnoa b/gnovm/tests/debug2/1a.gnoa new file mode 100644 index 00000000000..eaef398debe --- /dev/null +++ b/gnovm/tests/debug2/1a.gnoa @@ -0,0 +1,23 @@ +package main + +func main() { + var y int + var f []func() + + defer func() { + for _, ff := range f { + ff() + } + }() + + for i := 0; i < 3; i++ { + x := y + f = append(f, func() { println(x) }) + y++ + } +} + +// Output: +// 0 +// 1 +// 2 diff --git a/gnovm/tests/debug2/1b.gno b/gnovm/tests/debug2/1b.gno new file mode 100644 index 00000000000..3aee77fa232 --- /dev/null +++ b/gnovm/tests/debug2/1b.gno @@ -0,0 +1,28 @@ +package main + +// invalid goto +func main() { + var y, counter int + var f []func() + defer func() { + for _, ff := range f { + ff() + } + }() + + // this is actually a implicit for loop +LABEL_1: + if counter == 5 { + goto LABEL_2 + } + x := y + f = append(f, func() { println(x) }) + y++ + counter++ + goto LABEL_1 // this is the edge condition, break? continue? + +LABEL_2: + println("end") +} + +// Output: diff --git a/gnovm/tests/debug2/2.gno b/gnovm/tests/debug2/2.gno new file mode 100644 index 00000000000..31d25cf6cb7 --- /dev/null +++ b/gnovm/tests/debug2/2.gno @@ -0,0 +1,34 @@ +package main + +// this is a fix, intuitive, and same with Go +func main() { + var fns []func() int + //defer func() { + // for _, fn := range fns { + // println(fn()) + // } + //}() + + for i := 0; i < 5; i++ { + //defer func() { + // for _, fn := range fns { + // println(fn()) + // } + //}() + x := i + f := func() int { + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 diff --git a/gnovm/tests/debug2/3.gno b/gnovm/tests/debug2/3.gno new file mode 100644 index 00000000000..4fffeb4fea0 --- /dev/null +++ b/gnovm/tests/debug2/3.gno @@ -0,0 +1,25 @@ +package main + +// this is a fix, intuitive, and same with Go +func main() { + var fns []func() int + + for i := 0; i < 5; i++ { + x := i + f := func() int { + return x + } + fns = append(fns, f) + x += 1 + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 1 +// 2 +// 3 +// 4 +// 5 diff --git a/gnovm/tests/debug2/4.gno b/gnovm/tests/debug2/4.gno new file mode 100644 index 00000000000..3835db7d114 --- /dev/null +++ b/gnovm/tests/debug2/4.gno @@ -0,0 +1,26 @@ +package main + +func main() { + var y, counter int + var f []func() + defer func() { + for _, ff := range f { + ff() + } + }() + + // this is actually a implicit for loop +FOO_LABEL: + if counter == 5 { + //for _, ff := range f { + // ff() + //} + return + } + x := y + f = append(f, func() { println(x) }) + x += 1 + y++ + counter++ + goto FOO_LABEL // this is the edge condition, break? continue? +} diff --git a/gnovm/tests/debug2/5.gno b/gnovm/tests/debug2/5.gno new file mode 100644 index 00000000000..d0989412170 --- /dev/null +++ b/gnovm/tests/debug2/5.gno @@ -0,0 +1,33 @@ +package main + +import "fmt" + +func main() { + var counter int + var fns []func() + + defer func() { + for _, f := range fns { + f() + } + }() + +LABEL_1: + if counter >= 2 { + goto LABEL_2 // Jump to LABEL_2 once counter is 2 or more + } + + // capture + fns = append(fns, func() { println(counter) }) + counter++ + goto LABEL_1 // Go back to the beginning of the loop + +LABEL_2: + fmt.Println("Exited loop with counter at:", counter) + // Continue with the rest of the program +} + +// Output: +// Exited loop with counter at: 2 +// 1 +// 2 From 629b9d731401f6ba5acfdaf1837d256a6147d45a Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Thu, 22 Feb 2024 23:57:05 +0800 Subject: [PATCH 28/32] hacky proprocess --- gnovm/Makefile | 1 + gnovm/pkg/gnolang/machine.go | 7 + gnovm/pkg/gnolang/nodes.go | 6 +- gnovm/pkg/gnolang/op_assign.go | 4 + gnovm/pkg/gnolang/op_call.go | 11 +- gnovm/pkg/gnolang/op_exec.go | 18 +- gnovm/pkg/gnolang/op_expressions.go | 123 ++---- gnovm/pkg/gnolang/preprocess.go | 161 ++++++-- gnovm/pkg/gnolang/transcribe.go | 11 +- gnovm/pkg/gnolang/values.go | 15 +- gnovm/tests/debug/1.gno | 26 ++ gnovm/tests/debug/3.gno | 25 ++ gnovm/tests/debug/5.gno | 33 ++ gnovm/tests/debug2/1.gno | 3 - gnovm/tests/debug2/closure0.gno | 17 + gnovm/tests/debug2/closure11.gno | 25 ++ gnovm/tests/debug2/closure11_b.gno | 27 ++ gnovm/tests/debug2/closure12_a.gno | 23 ++ gnovm/tests/debug2/closure15_a.gno | 50 +++ gnovm/tests/debug2/closure16.gno | 21 + .../closure17_sort_search_efficiency.gno | 21 + gnovm/tests/{debug => debug2}/closure9.gno | 0 gnovm/tests/debug2/closure9_g.gno | 24 ++ gnovm/tests/debug2/closure9_h.gno | 30 ++ gnovm/tests/debug2/closure9_i.gno | 31 ++ gnovm/tests/debug2/foo1155.gno | 25 ++ gnovm/tests/debug2/inc.gno | 10 + gnovm/tests/debug2/sort_search_exhausted.gno | 20 + gnovm/tests/debug2/zrealm_avl1.gno | 379 ++++++++++++++++++ 29 files changed, 995 insertions(+), 152 deletions(-) create mode 100644 gnovm/tests/debug/1.gno create mode 100644 gnovm/tests/debug/3.gno create mode 100644 gnovm/tests/debug/5.gno create mode 100644 gnovm/tests/debug2/closure0.gno create mode 100644 gnovm/tests/debug2/closure11.gno create mode 100644 gnovm/tests/debug2/closure11_b.gno create mode 100644 gnovm/tests/debug2/closure12_a.gno create mode 100644 gnovm/tests/debug2/closure15_a.gno create mode 100644 gnovm/tests/debug2/closure16.gno create mode 100644 gnovm/tests/debug2/closure17_sort_search_efficiency.gno rename gnovm/tests/{debug => debug2}/closure9.gno (100%) create mode 100644 gnovm/tests/debug2/closure9_g.gno create mode 100644 gnovm/tests/debug2/closure9_h.gno create mode 100644 gnovm/tests/debug2/closure9_i.gno create mode 100644 gnovm/tests/debug2/foo1155.gno create mode 100644 gnovm/tests/debug2/inc.gno create mode 100644 gnovm/tests/debug2/sort_search_exhausted.gno create mode 100644 gnovm/tests/debug2/zrealm_avl1.gno diff --git a/gnovm/Makefile b/gnovm/Makefile index cbe6802d32d..8c6bac373c0 100644 --- a/gnovm/Makefile +++ b/gnovm/Makefile @@ -62,6 +62,7 @@ _test.gnolang.native.sync:; go test tests/*.go -test.short -run "TestFilesNativ _test.gnolang.stdlibs.sync:; go test tests/*.go -test.short -run 'TestFiles$$/' --update-golden-tests $(GOTEST_FLAGS) _test.gnolang.debug:; go test tests/*.go -test.short -run 'TestDebug$$/' --update-golden-tests $(GOTEST_FLAGS) +_test.gnolang.sort:; go test tests/*.go -run "TestPackages/(sort)" $(GOTEST_FLAGS) ######################################## # Code gen # TODO: move _dev.stringer to go:generate instructions, simplify generate diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go index 91cbc0b0c21..8214eb49d69 100644 --- a/gnovm/pkg/gnolang/machine.go +++ b/gnovm/pkg/gnolang/machine.go @@ -1649,6 +1649,13 @@ func (m *Machine) LastBlock() *Block { return m.Blocks[len(m.Blocks)-1] } +func (m *Machine) GetBlockAt(index int) *Block { + if index > len(m.Blocks)-1 { + panic("no block at this index, should not happen") + } + return m.Blocks[len(m.Blocks)-1-index] +} + // Pushes a frame with one less statement. func (m *Machine) PushFrameBasic(s Stmt) { label := s.GetLabel() diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index 59d028ef3ac..7085e4f6ba5 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -494,8 +494,10 @@ type KeyValueExprs []KeyValueExpr type FuncLitExpr struct { Attributes StaticBlock - Type FuncTypeExpr // function type - Body // function body + Type FuncTypeExpr // function type + Body // function body + RefLoopBlockNodes []BlockNode + RefIndices []int } // The preprocessor replaces const expressions diff --git a/gnovm/pkg/gnolang/op_assign.go b/gnovm/pkg/gnolang/op_assign.go index 0c39c762c78..3bdcb8265b4 100644 --- a/gnovm/pkg/gnolang/op_assign.go +++ b/gnovm/pkg/gnolang/op_assign.go @@ -1,7 +1,9 @@ package gnolang func (m *Machine) doOpDefine() { + debug.Println("---doOpDefine") s := m.PopStmt().(*AssignStmt) + debug.Printf("---s: %v \n", s) // Define each value evaluated for Lhs. // NOTE: PopValues() returns a slice in // forward order, not the usual reverse. @@ -26,7 +28,9 @@ func (m *Machine) doOpDefine() { } func (m *Machine) doOpAssign() { + debug.Println("---doOpAssign") s := m.PopStmt().(*AssignStmt) + debug.Printf("---s: %v \n", s) // Assign each value evaluated for Lhs. // NOTE: PopValues() returns a slice in // forward order, not the usual reverse. diff --git a/gnovm/pkg/gnolang/op_call.go b/gnovm/pkg/gnolang/op_call.go index 6442c8d83d5..7915bbca6e5 100644 --- a/gnovm/pkg/gnolang/op_call.go +++ b/gnovm/pkg/gnolang/op_call.go @@ -46,6 +46,7 @@ func (m *Machine) doOpPrecall() { var gReturnStmt = &ReturnStmt{} func (m *Machine) doOpCall() { + debug.Println("---doOpCall") // NOTE: Frame won't be popped until the statement is complete, to // discard the correct number of results for func calls in ExprStmts. fr := m.LastFrame() @@ -54,18 +55,22 @@ func (m *Machine) doOpCall() { pts := ft.Params numParams := len(pts) isMethod := 0 // 1 if true + debug.Printf("---fv: %v \n", fv) clo := fr.Func.GetClosure(m.Store) // this is "static" // update block vars using captured vars + var loopBlock *Block // target loop block with values to be updated if fv.TransientLoopData != nil { // it captured a bundle of values for i, loopData := range fv.TransientLoopData { // each LoopValuesBox is for a certain level of block box := loopData.loopValuesBox if box.isSealed { + debug.Println("---isSealed, do update context") for _, t := range box.transient { // unpack vars belong to a specific block if debug { + debug.Printf("---len of values: %d \n", len(t.values)) for k, v := range t.values { - fmt.Printf("values[%d] is %v \n", k, v) + debug.Printf("values[%d] is %v \n", k, v) } } loopBlock = clo.GetBlockWithDepth(m.Store, t.nx.Path) // find target block using depth @@ -86,12 +91,16 @@ func (m *Machine) doOpCall() { } } } else { // not sealed will be tainted, indicates not sealed with values when loopBlock ends, it's not a closure. + debug.Println("---not sealed, it's tainted") + debug.Printf("---tainted box is: %v \n", fv.TransientLoopData[i].loopValuesBox) fv.TransientLoopData[i].loopValuesBox.isTainted = true } } fv.TransientLoopData = nil } + //debug.Println("---no closure ...") + b := m.Alloc.NewBlock(fr.Func.GetSource(m.Store), clo) m.PushBlock(b) if fv.nativeBody == nil && fv.NativePkg != "" { diff --git a/gnovm/pkg/gnolang/op_exec.go b/gnovm/pkg/gnolang/op_exec.go index 8b758f50b92..4e98b2ba534 100644 --- a/gnovm/pkg/gnolang/op_exec.go +++ b/gnovm/pkg/gnolang/op_exec.go @@ -44,18 +44,23 @@ SelectStmt -> */ func updateCapturedValue(m *Machine, lb *Block) { - debug.Println("---updateCapturedValue") - //if lb.GetBodyStmt().isLoop { // do we need this? - //lb.bodyStmt = lb.Source.GetStaticBlock().bodyStmt + debug.Println("---op_exec, updateCapturedValue") bs := lb.GetBodyStmt() - //bs := lb.Source.GetStaticBlock().GetBodyStmt() + + debug.Printf("---op_exec, lvBox: %v \n", bs.LoopValuesBox) + + //if bs.LoopValuesBox != nil { + // debug.Printf("---op_exec, lvBox.isFilled: %v \n", bs.LoopValuesBox.isFilled) + // debug.Printf("---op_exec, lvBox.isTainted: %v \n", bs.LoopValuesBox.isTainted) + //} + if bs.LoopValuesBox != nil && bs.LoopValuesBox.isFilled && !bs.LoopValuesBox.isTainted { var isSealed bool for i, tt := range bs.LoopValuesBox.transient { nvp := lb.Source.GetPathForName(m.Store, tt.nx.Name) ptr := m.LastBlock().GetPointerTo(m.Store, nvp) tv := ptr.Deref() - debug.Printf("---transient value is: %v \n", tv) + debug.Printf("---transient value for %s is: %v \n", tt.nx.Name, tv) // update context use previously recorded value // inner loops iterates twice while outer loop iterates once. // it's an alignment for the values of out loop block. @@ -67,11 +72,11 @@ func updateCapturedValue(m *Machine, lb *Block) { isSealed = true } if isSealed { + debug.Printf("---seal for: %v \n", bs.LoopValuesBox) bs.LoopValuesBox.isSealed = true // seal for this LoopValuesBox for closure execution. } debug.Printf("---lvBox after updateValue is: %v \n", bs.LoopValuesBox) } - //} } //---------------------------------------- @@ -148,6 +153,7 @@ func (m *Machine) doOpExec(op Op) { goto EXEC_SWITCH } else if bs.NextBodyIndex == bs.BodyLen { // exiting loop + debug.Printf("---exiting for loop of bs: %v \n", lb.bodyStmt) updateCapturedValue(m, lb) // (queue to) go back. if bs.Cond != nil { diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index a5208e15adc..f246e9ee22d 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -677,116 +677,41 @@ func (m *Machine) doOpStructLit() { }) } -// TODO: migrate loopdata from sb(BlockNode) to b(*Block) func (m *Machine) doOpFuncLit() { x := m.PopExpr().(*FuncLitExpr) debug.Printf("---doOpFuncLit, x: %v \n", x) debug.Printf("---doOpFuncLit, get label: %v, getLine: %d \n", x.GetLabel(), x.GetLine()) lb := m.LastBlock() - debug.Printf("---doOpFuncLit, lb.Source.sb.bs.lvBox: %v \n", lb.Source.GetStaticBlock().bodyStmt.LoopValuesBox) - var lvBox *LoopValuesBox // container per block to store transient values for captured vars var loopData []*LoopBlockData // for fv to track all transient values + //var refLoopBlocks []*Block + var lvBox *LoopValuesBox // container per block to store transient values for captured vars + for index, refBn := range x.RefLoopBlockNodes { + debug.Printf("---refBn: %v \n", refBn) + // find counterpart block of refBn + lvBox = refBn.GetStaticBlock().bodyStmt.LoopValuesBox + targetRealBlock := m.GetBlockAt(x.RefIndices[index] - 1) + debug.Printf("---targetBlock: %v \n", targetRealBlock) + debug.Printf("---doOpFuncLit, lvBox: %v \n", lvBox) + + if lvBox != nil && lvBox.isFilled { + debug.Println("---copy to real block") + // copy to block + targetRealBlock.bodyStmt.LoopValuesBox = lvBox + + for _, tst := range lvBox.transient { + tst.cursor++ // increase cursor of every nameExpr + debug.Println("---cursor: ", tst.cursor) + } - lvBox = lb.Source.GetStaticBlock().bodyStmt.LoopValuesBox - // copy to block - lb.bodyStmt.LoopValuesBox = lvBox - - for _, tst := range lvBox.transient { - tst.cursor++ // increase cursor of every nameExpr + lvBox.isSealed = false // reset every time + // each fv may have n outer loopBlock, reference them all + loopData = append(loopData, &LoopBlockData{index: lvBox.transient[0].cursor, loopValuesBox: lvBox}) + } + //refLoopBlocks = append(refLoopBlocks, targetRealBlock) } - // each fv may have n outer loopBlock, reference them all - loopData = append(loopData, &LoopBlockData{index: lvBox.transient[0].cursor, loopValuesBox: lvBox}) - - //captures := make([]*NameExpr, 0) - //for _, n := range x.GetExternNames() { - // vp := x.GetPathForName(m.Store, n) - // if vp.Depth == 0 { // skip uverse name - // continue - // } - // vp.Depth -= 1 // from the perspective of funcLit block - // nx := &NameExpr{ - // Name: n, - // Path: vp, - // } - // captures = append(captures, nx) - //} - // - //// sort it so to traverse block in order from inner to outer - //sortDepth := func(i, j int) bool { - // return int(captures[i].Path.Depth) < int(captures[j].Path.Depth) - //} - //sort.Slice(captures, sortDepth) - // - //var ( - // isLoopBlock bool - // loopBlock *Block // e.g. a `for` block that is outside of funcLit block - // lvBox *LoopValuesBox // container per block to store transient values for captured vars - // lastGen, gen uint8 - // isReuse bool - // loopData []*LoopBlockData // for fv to track all transient values - //) - // - //// when iterating goes on, funcValue will yield several replicas, each of which should capture a slice of - //// transient values for a nameExpr. - //// e.g. `for i:=0, i++; i<2{ x := i }`, the transient values for x should be [0 ,1], this is recorded and - //// used when the funcLit is executed, namely, closure. - //// more complex situation like: - //// for i:=0, i++; i<2{ x := i for j:=0, j++; j<2{y := j}}, - //// in this case, there will be 4 replica of fv, and 3 loopBlock. - //// the transient state of x, y is: [0,0], [0,1], [1,0], [1,1]. - //// the outer block will hold transient values of [0,0,1,1] for `x`, and inner block 1 will hold [0,1] for `y`, - //// another inner block 2 holds [0,1] for y too. - //for _, nx := range captures { - // // start search block, in the order of small depth to big depth - // loopBlock, isLoopBlock, gen = findLoopBlockWithPath(m.Store, lb, nx, x.GetLine()) - // debug.Printf("nx name: %v, nx path: %v, isLoopBlock: %t \n", nx.Name, nx.Path, isLoopBlock) - // debug.Printf("loopBlock: %v \n", loopBlock) - // if lastGen == 0 { - // lastGen = gen - // } else if gen != lastGen { // if enter new level of block, pack last box - // lastGen = gen - // if lvBox != nil && !isReuse { // has something to pack - // lvBox.isFilled = true - // loopData = append(loopData, &LoopBlockData{index: 0, loopValuesBox: lvBox}) - // } - // } - // if isLoopBlock { - // // every loopBlock holds a loopValuesBox that contains a slice of transient value generated as the iterations goes on. - // // funcValue references this loopValuesBox for future use(when closure executing) - // lvBox = loopBlock.GetBodyStmt().LoopValuesBox // get LoopValuesBox from specific block, that is shared across current level of for/range loop - // if lvBox == nil { - // lvBox = &LoopValuesBox{} // init - // } - // if !lvBox.isFilled { // for replicas of fv, the captured names are same, so fill only once for further reuse. - // debug.Printf("---fill nx: %v \n", nx) - // tst := &Transient{ - // nx: nx, - // cursor: 0, // inc every iteration, implies sequence of a fv, and index of transient values. - // } - // lvBox.transient = append(lvBox.transient, tst) - // // record in loop block - // loopBlock.GetBodyStmt().LoopValuesBox = lvBox - // } else { // reuse last replica's. (in same block). - // isReuse = true - // // get cursor by name - // debug.Printf("reuse by else loop in same block, nx.Name: %s \n", nx.Name) - // debug.Printf("index of x is: %d \n", lvBox.getIndexByName(nx.Name)) - // lvBox.transient[lvBox.getIndexByName(nx.Name)].cursor++ // inc by iteration - // // each fv may have n outer loopBlock, reference them all - // loopData = append(loopData, &LoopBlockData{index: lvBox.transient[lvBox.getIndexByName(nx.Name)].cursor, loopValuesBox: lvBox}) - // } - // } - //} - - //// set as a deferred operation - //if lvBox != nil && !lvBox.isFilled && !isReuse { - // lvBox.isFilled = true // set for the last LoopValuesBox of last loopBlock - // loopData = append(loopData, &LoopBlockData{index: 0, loopValuesBox: lvBox}) - //} - if debug { for i, ts := range loopData { fmt.Printf("========TransientLoopData[%d] is: %s, index: %d \n", i, ts.loopValuesBox, ts.index) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 507e8b3bd95..e190698df39 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -96,41 +96,64 @@ func PredefineFileSet(store Store, pn *PackageNode, fset *FileSet) { } func dumpStack(stack []BlockNode) { - println("---dumpStack, height of stack is: ", len(stack)) - for i, bn := range stack { - println("=================================") - fmt.Printf("blockNode[%d] type is: %T, value is: %v \n", i, bn, bn) - println("=================================") + if debug { + println("---dumpStack, height of stack is: ", len(stack)) + for i, bn := range stack { + println("=================================") + fmt.Printf("blockNode[%d] type is: %T, value is: %v \n", i, bn, bn) + println("=================================") + } + } +} + +func dumpSnare(snare []BlockNode) { + if debug { + println("---dumpSnare, height of stack is: ", len(snare)) + for i, bn := range snare { + println("=================================") + fmt.Printf("blockNode[%d] type is: %T, value is: %v \n", i, bn, bn) + println("=================================") + } } } -func getBlockNodeAt(stack []BlockNode, offset int) BlockNode { +func getBlockNodeAt(stack []BlockNode, n int) BlockNode { debug.Println("---getParentBlockNode") - if len(stack) == 0 { + if n > len(stack)-1 { return nil } else { - return stack[len(stack)-1-offset] + return stack[len(stack)-1-n] } } // find nearest block is loop and contains name of n func findLoopStaticBlockAndPath(stack []BlockNode, nx *NameExpr) (BlockNode, bool, uint8) { debug.Printf("---findLoopStaticBlockAndPath, nx: %v \n", nx) + dumpStack(stack) var gen uint8 = 1 // which level it is - var sb BlockNode + var bn BlockNode // nav to target level for i := uint8(0); i < nx.Path.Depth+1; i++ { // find target block at certain depth - sb = getBlockNodeAt(stack, int(i)) + bn = getBlockNodeAt(stack, int(i)) gen++ + if bn == nil { + return nil, false, gen + } } - debug.Printf("---got target sb: %v \n", sb) - // find name - if sb.GetStaticBlock().bodyStmt.loopBody.isLoop { - names := sb.GetBlockNames() - for _, name := range names { - if nx.Name == name { // find n in this block - return sb, true, gen + debug.Printf("---got target bn: %v \n", bn) + debug.Printf("---got target bn type: %T \n", bn) + debug.Printf("---got target loopBody: %v \n", bn.GetStaticBlock().bodyStmt.loopBody) + + // tmp, if static not nil, means some work done in preprocess, using static + if bn.GetStaticBlock().bodyStmt.loopBody != nil { + // find name + if bn.GetStaticBlock().bodyStmt.loopBody.isLoop { + names := bn.GetBlockNames() + for _, name := range names { + if nx.Name == name { // find n in this block + return bn, true, gen + } } } } @@ -195,6 +218,9 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { SetNodeLocations(pkgPath, fileName, fn) } + // for analysis on closure as a special case + var snare []BlockNode = make([]BlockNode, 0, 32) + var stack []BlockNode = make([]BlockNode, 0, 32) // create stack of BlockNodes. var last BlockNode = ctx @@ -399,6 +425,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } } } + n.bodyStmt.loopBody = &LoopBody{isLoop: true} // TRANS_BLOCK ----------------------- case *FuncLitExpr: @@ -420,6 +447,9 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { last.Define(Name(rn), anyValue(rf.Type)) } } + // catch funcLit block + snare = append(snare, last) + // TRANS_BLOCK ----------------------- case *SelectCaseStmt: pushInitBlock(n, &last, &stack) @@ -788,11 +818,11 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } case *FuncLitExpr: debug.Printf("---trans_leave, funcLitExpr: %v \n", n) - // prepare captured nameExprs + // prepare captured named symbols captures := make([]*NameExpr, 0) for _, name := range n.GetExternNames() { vp := n.GetPathForName(store, name) - if vp.Depth == 0 { // skip uverse name + if vp.Depth == 0 { // skip uverse continue } vp.Depth -= 1 // from the perspective of funcLit block @@ -815,8 +845,6 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { loopBlock BlockNode // e.g. a `for` block that is outside of funcLit block lvBox *LoopValuesBox // container per block to store transient values for captured vars lastGen, gen uint8 - isReuse bool - loopData []*LoopBlockData // referenced by fv to track all transient values ) // when iterating goes on, funcValue will yield several replicas, each of which should capture a slice of @@ -830,36 +858,44 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // the outer block will hold transient values of [0,0,1,1] for `x`, and inner block 1 will hold [0,1] for `y`, // another inner block 2 holds [0,1] for y too. for _, nx := range captures { + debug.Printf("---loop captures, nx name: %v, nx path: %v \n", nx.Name, nx.Path) // start search block, in the order of small depth to big depth loopBlock, isLoopBlock, gen = findLoopStaticBlockAndPath(stack, nx) - debug.Printf("nx name: %v, nx path: %v, isLoopBlock: %t \n", nx.Name, nx.Path, isLoopBlock) + debug.Printf("isLoopBlock: %v \n", isLoopBlock) debug.Printf("loopBlock: %v \n", loopBlock) debug.Printf("depth: %v \n", gen) if lastGen == 0 { lastGen = gen } else if gen != lastGen { // if enter new level of block, pack last box lastGen = gen - if lvBox != nil && !isReuse { // has something to pack + if lvBox != nil { // xxx, need this? lvBox.isFilled = true - loopData = append(loopData, &LoopBlockData{index: 0, loopValuesBox: lvBox}) } } + // prepare lvBox if isLoopBlock { - // every loopBlock holds a loopValuesBox that contains a slice of transient value generated as the iterations goes on. - // funcValue references this loopValuesBox for future use(when closure executing) lvBox = loopBlock.GetStaticBlock().GetBodyStmt().LoopValuesBox // get LoopValuesBox from specific block, that is shared across current level of for/range loop + //} if lvBox == nil { - lvBox = &LoopValuesBox{} // init + lvBox = &LoopValuesBox{} } - debug.Printf("---fill nx: %v \n", nx) tst := &Transient{ nx: nx, cursor: -1, // inc every iteration, implies sequence of a fv, and index of transient values. } lvBox.transient = append(lvBox.transient, tst) + + // got a loop block + //if isLoopBlock { // got target block + debug.Printf("---fill nx: %v \n", nx) lvBox.isFilled = true // record in loop block loopBlock.GetStaticBlock().GetBodyStmt().LoopValuesBox = lvBox + // set ref loop block nodes + n.RefLoopBlockNodes = append(n.RefLoopBlockNodes, loopBlock) + n.RefIndices = append(n.RefIndices, int(nx.Path.Depth)) + //} + // set for potentially implicit loop block, goto label } } @@ -1756,13 +1792,73 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { case BREAK: case CONTINUE: case GOTO: - debug.Printf("---branchStmt, n.Label: %v , n.Line: %d \n", n.GetLabel(), n.GetLine()) - _, depth, index, line := findGotoLabel(last, n.Label) + debug.Printf("---branchStmt, n.Label: %v, n.Line: %d \n", n.GetLabel(), n.GetLine()) + _, depth, index, labelLine := findGotoLabel(last, n.Label) n.Depth = depth n.BodyIndex = index - debug.Println("depth:, index:, line:", depth, index, line) + debug.Println("depth:, index:, labelLine:", depth, index, labelLine) debug.Printf("---BranchStmt, last: %v, %T \n", last, last) - last.GetStaticBlock().SetLoopBody(line, n.GetLine()) + + debug.Println("---going to fine funcLitExpr blockNode") + dumpSnare(snare) + + if labelLine < n.GetLine() { // only jmp to previous line make it loop? + for i := len(snare) - 1; i >= 0; i-- { + if fx, ok := snare[i].(*FuncLitExpr); ok { + // do staff on fx, copy x here + debug.Printf("---fx: %v, at line of: %d \n", fx, fx.GetLine()) + // do staff with fx + // funcLit in implicit loop block + if labelLine < fx.GetLine() && fx.GetLine() < n.GetLine() { + debug.Println("---implicit pattern found!") + + // fill fx + captures := make([]*NameExpr, 0) + for _, name := range fx.GetExternNames() { + vp := fx.GetPathForName(store, name) + if vp.Depth == 0 { // skip uverse + continue + } + vp.Depth -= 1 // from the perspective of funcLit block + nx := &NameExpr{ + Name: name, + Path: vp, + } + captures = append(captures, nx) + } + + // find target nx + lvBox := &LoopValuesBox{} + for _, nx := range captures { + names := last.GetBlockNames() + for _, name := range names { + if nx.Name == name { // find nx in this block + tst := &Transient{ + nx: nx, + cursor: -1, // inc every iteration, implies sequence of a fv, and index of transient values. + } + lvBox.transient = append(lvBox.transient, tst) + lvBox.isFilled = true + } + } + } + // set state + if lvBox.isFilled { + last.GetStaticBlock().GetBodyStmt().LoopValuesBox = lvBox + fx.RefLoopBlockNodes = append(fx.RefLoopBlockNodes, last) + // set index for ref blocks + for _, tst := range lvBox.transient { + fx.RefIndices = append(fx.RefIndices, int(tst.nx.Path.Depth)) + } + } + } + } + } + + // tag implicit block + last.GetStaticBlock().SetLoopBody(labelLine, n.GetLine()) + snare = snare[:0] + } case FALLTHROUGH: if swchC, ok := last.(*SwitchClauseStmt); ok { // last is a switch clause, find its index in the switch and assign @@ -2061,6 +2157,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // finalization. if _, ok := n.(BlockNode); ok { // Pop block. + debug.Printf("---Pop blockNode: %v \n", stack[len(stack)-1]) stack = stack[:len(stack)-1] last = stack[len(stack)-1] return n, TRANS_CONTINUE diff --git a/gnovm/pkg/gnolang/transcribe.go b/gnovm/pkg/gnolang/transcribe.go index bc9638326fa..2d187def6fd 100644 --- a/gnovm/pkg/gnolang/transcribe.go +++ b/gnovm/pkg/gnolang/transcribe.go @@ -281,10 +281,6 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } } - // identify closure patterns - ns := cnn.GetExternNames() - debug.Printf("---transcribe, externs: %v \n", ns) - //printStack() case *FieldTypeExpr: cnn.Type = transcribe(t, nns, TRANS_FIELDTYPE_TYPE, 0, cnn.Type, &c).(Expr) if isStopOrSkip(nc, c) { @@ -398,6 +394,13 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } } case *BranchStmt: + //cnn2, c2 := t(ns, ftype, index, cnn, TRANS_BLOCK) + //if isStopOrSkip(nc, c2) { + // nn = cnn2 + // return + //} else { + // cnn = cnn2.(*BranchStmt) + //} case *DeclStmt: for idx := range cnn.Body { cnn.Body[idx] = transcribe(t, nns, TRANS_DECL_BODY, idx, cnn.Body[idx], &c).(SimpleDeclStmt) diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index 4a869a3b3c6..d7875b47a37 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -2305,15 +2305,20 @@ func NewBlock(source BlockNode, parent *Block) *Block { } } -func (b *Block) SetLoopBody(start, end int) { - debug.Printf("---SetIsLoop, bs: %v, bs.Addr: %p \n", b.bodyStmt, &b.bodyStmt) - if b.bodyStmt.loopBody == nil { - b.bodyStmt.loopBody = &LoopBody{isLoop: true, start: start, end: end} +func (sb *StaticBlock) SetLoopBody(start, end int) { + debug.Printf("---SetLoopBody, sb.Addr: %p \n", sb) + debug.Printf("---SetLoopBody, bs: %v, bs.Addr: %p \n", sb.bodyStmt, &sb.bodyStmt) + debug.Printf("---SetLoopBody, start: %d, end: %d \n", start, end) + if sb.bodyStmt.loopBody == nil { + debug.Println("---loopBody is nil, set value") + sb.bodyStmt.loopBody = &LoopBody{isLoop: true, start: start, end: end} } - debug.Printf("---SetIsLoop, bs: %v, bs.Addr: %p \n", b.bodyStmt, &b.bodyStmt) + debug.Printf("---SetLoopBody, bs: %v, bs.Addr: %p \n", sb.bodyStmt, &sb.bodyStmt) } func (b *Block) UpdateValue(index int, tv TypedValue) { + debug.Printf("---UpdateValue, index: %d \n", index) + debug.Printf("---UpdateValue, block: %v \n", b) for i := range b.Values { if i == index { b.Values[i] = tv diff --git a/gnovm/tests/debug/1.gno b/gnovm/tests/debug/1.gno new file mode 100644 index 00000000000..47bf386f606 --- /dev/null +++ b/gnovm/tests/debug/1.gno @@ -0,0 +1,26 @@ +package main + +func main() { + var y, counter int + var f []func() + defer func() { + for _, ff := range f { + ff() + } + }() + + // this is actually a implicit for loop +LABEL_1: + if counter == 2 { + return + } + x := y + f = append(f, func() { println(x) }) + y++ + counter++ + goto LABEL_1 // this is the edge condition, break? continue? +} + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/debug/3.gno b/gnovm/tests/debug/3.gno new file mode 100644 index 00000000000..4fffeb4fea0 --- /dev/null +++ b/gnovm/tests/debug/3.gno @@ -0,0 +1,25 @@ +package main + +// this is a fix, intuitive, and same with Go +func main() { + var fns []func() int + + for i := 0; i < 5; i++ { + x := i + f := func() int { + return x + } + fns = append(fns, f) + x += 1 + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 1 +// 2 +// 3 +// 4 +// 5 diff --git a/gnovm/tests/debug/5.gno b/gnovm/tests/debug/5.gno new file mode 100644 index 00000000000..d0989412170 --- /dev/null +++ b/gnovm/tests/debug/5.gno @@ -0,0 +1,33 @@ +package main + +import "fmt" + +func main() { + var counter int + var fns []func() + + defer func() { + for _, f := range fns { + f() + } + }() + +LABEL_1: + if counter >= 2 { + goto LABEL_2 // Jump to LABEL_2 once counter is 2 or more + } + + // capture + fns = append(fns, func() { println(counter) }) + counter++ + goto LABEL_1 // Go back to the beginning of the loop + +LABEL_2: + fmt.Println("Exited loop with counter at:", counter) + // Continue with the rest of the program +} + +// Output: +// Exited loop with counter at: 2 +// 1 +// 2 diff --git a/gnovm/tests/debug2/1.gno b/gnovm/tests/debug2/1.gno index 77db6f19c8a..47bf386f606 100644 --- a/gnovm/tests/debug2/1.gno +++ b/gnovm/tests/debug2/1.gno @@ -24,6 +24,3 @@ LABEL_1: // Output: // 0 // 1 -// 2 -// 3 -// 4 diff --git a/gnovm/tests/debug2/closure0.gno b/gnovm/tests/debug2/closure0.gno new file mode 100644 index 00000000000..acc1abfd404 --- /dev/null +++ b/gnovm/tests/debug2/closure0.gno @@ -0,0 +1,17 @@ +package main + +type adder func(int, int) int + +func genAdd(k int) adder { + return func(i, j int) int { + return i + j + k + } +} + +func main() { + f := genAdd(5) + println(f(3, 4)) +} + +// Output: +// 12 diff --git a/gnovm/tests/debug2/closure11.gno b/gnovm/tests/debug2/closure11.gno new file mode 100644 index 00000000000..1ea6e013b0d --- /dev/null +++ b/gnovm/tests/debug2/closure11.gno @@ -0,0 +1,25 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + f := func() int { + if true { + if true { + return x + } + } + return 0 + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/debug2/closure11_b.gno b/gnovm/tests/debug2/closure11_b.gno new file mode 100644 index 00000000000..427c6f075f9 --- /dev/null +++ b/gnovm/tests/debug2/closure11_b.gno @@ -0,0 +1,27 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + y := 0 + f := func() int { + if true { + x += y + if true { + return x + } + } + return 0 + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/debug2/closure12_a.gno b/gnovm/tests/debug2/closure12_a.gno new file mode 100644 index 00000000000..1e3eb9fe815 --- /dev/null +++ b/gnovm/tests/debug2/closure12_a.gno @@ -0,0 +1,23 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + f := func() int { + for i := 0; i < 1; i++ { + x++ + } + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 1 +// 2 diff --git a/gnovm/tests/debug2/closure15_a.gno b/gnovm/tests/debug2/closure15_a.gno new file mode 100644 index 00000000000..a30f558350e --- /dev/null +++ b/gnovm/tests/debug2/closure15_a.gno @@ -0,0 +1,50 @@ +package main + +import "fmt" + +// recursive closure does not capture +func main() { + + var fns []func(int) int + var recursiveFunc func(int) int + + for i := 0; i < 3; i++ { + recursiveFunc = func(num int) int { + x := i + println("value of x: ", x) + if num <= 0 { + return 1 + } + return num * recursiveFunc(num-1) + } + + fns = append(fns, recursiveFunc) + } + + for i, r := range fns { + result := r(i) + fmt.Printf("Factorial of %d is: %d \n", i, result) + } +} + +// Go Output: +// value of x: 3 +// Factorial of 0 is: 1 +// value of x: 3 +// value of x: 3 +// Factorial of 1 is: 1 +// value of x: 3 +// value of x: 3 +// value of x: 3 +// Factorial of 2 is: 2 + +// Output: +// value of x: 0 +// Factorial of 0 is: 1 +// value of x: 1 +// value of x: 2 +// Factorial of 1 is: 1 +// value of x: 2 +// value of x: 2 +// value of x: 2 +// Factorial of 2 is: 2 diff --git a/gnovm/tests/debug2/closure16.gno b/gnovm/tests/debug2/closure16.gno new file mode 100644 index 00000000000..8a472ffb410 --- /dev/null +++ b/gnovm/tests/debug2/closure16.gno @@ -0,0 +1,21 @@ +package main + +func main() { + var fns []func() int + s := []int{1, 2, 3} + for i, _ := range s { + x := i + f := func() int { + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 2 diff --git a/gnovm/tests/debug2/closure17_sort_search_efficiency.gno b/gnovm/tests/debug2/closure17_sort_search_efficiency.gno new file mode 100644 index 00000000000..90362944ac5 --- /dev/null +++ b/gnovm/tests/debug2/closure17_sort_search_efficiency.gno @@ -0,0 +1,21 @@ +package main + +func Search(n int, f func(int) bool) int { + f(1) + return 0 +} + +func main() { + for x := 0; x < 2; x++ { + count := 0 + println(" first: count: ", count) + Search(1, func(i int) bool { count++; return i >= x }) + println("second: count: ", count) + } +} + +// Output: +// first: count: 0 +// second: count: 1 +// first: count: 0 +// second: count: 1 diff --git a/gnovm/tests/debug/closure9.gno b/gnovm/tests/debug2/closure9.gno similarity index 100% rename from gnovm/tests/debug/closure9.gno rename to gnovm/tests/debug2/closure9.gno diff --git a/gnovm/tests/debug2/closure9_g.gno b/gnovm/tests/debug2/closure9_g.gno new file mode 100644 index 00000000000..bd8acbe02a0 --- /dev/null +++ b/gnovm/tests/debug2/closure9_g.gno @@ -0,0 +1,24 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 5; i++ { + x := i + { // another block + f := func() int { + return x + } + fns = append(fns, f) + } + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 diff --git a/gnovm/tests/debug2/closure9_h.gno b/gnovm/tests/debug2/closure9_h.gno new file mode 100644 index 00000000000..23cb5ff9f40 --- /dev/null +++ b/gnovm/tests/debug2/closure9_h.gno @@ -0,0 +1,30 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 2; i++ { + x := i + for j := 0; j < 2; j++ { + y := j + f := func() int { + return x + y + } + fns = append(fns, f) + } + } + for _, fn := range fns { + println(fn()) + } +} + +// Go Output: +// 0 +// 1 +// 1 +// 2 + +// Output: +// 0 +// 1 +// 1 +// 2 diff --git a/gnovm/tests/debug2/closure9_i.gno b/gnovm/tests/debug2/closure9_i.gno new file mode 100644 index 00000000000..42a4d11d862 --- /dev/null +++ b/gnovm/tests/debug2/closure9_i.gno @@ -0,0 +1,31 @@ +package main + +func main() { + var fns []func() int + var x int + for i := 0; i < 2; i++ { + x = i + for j := 0; j < 2; j++ { + y := j + f := func() int { + return x + y + } + fns = append(fns, f) + } + } + for _, fn := range fns { + println(fn()) + } +} + +// Go Output: +// 1 +// 2 +// 1 +// 2 + +// Output: +// 1 +// 2 +// 1 +// 2 diff --git a/gnovm/tests/debug2/foo1155.gno b/gnovm/tests/debug2/foo1155.gno new file mode 100644 index 00000000000..bd3d5e16a3b --- /dev/null +++ b/gnovm/tests/debug2/foo1155.gno @@ -0,0 +1,25 @@ +package main + +func BalanceOf(i int) int { + return 100 +} + +func main() { + bar := 100 + for i, tc := range []struct { + name string + expected interface{} + fn func() interface{} + }{ + {"BalanceOf(admin, tid1)", 100, func() interface{} { return BalanceOf(bar) }}, + } { + println("hey") + println(i) + println(tc) + } +} + +// Output: +// hey +// 0 +// struct{("BalanceOf(admin, tid1)" string),(100 int),( func()( interface{}))} diff --git a/gnovm/tests/debug2/inc.gno b/gnovm/tests/debug2/inc.gno new file mode 100644 index 00000000000..b45aa0569e3 --- /dev/null +++ b/gnovm/tests/debug2/inc.gno @@ -0,0 +1,10 @@ +package main + +func main() { + x := 1 + x++ + println(x) +} + +// Output: +// 2 diff --git a/gnovm/tests/debug2/sort_search_exhausted.gno b/gnovm/tests/debug2/sort_search_exhausted.gno new file mode 100644 index 00000000000..e44a9bf0967 --- /dev/null +++ b/gnovm/tests/debug2/sort_search_exhausted.gno @@ -0,0 +1,20 @@ +package main + +func Search(n int, f func(int) bool) int { + i, j := 0, n + for i := 0; i < j; i++ { + h := int(uint(i+j) >> 1) // avoid overflow when computing h + f(h) + } + return i +} + +func main() { + for size := 0; size < 2; size++ { + for targ := 0; targ <= size; targ++ { + Search(size, func(i int) bool { return i >= targ }) + } + } +} + +// Output: diff --git a/gnovm/tests/debug2/zrealm_avl1.gno b/gnovm/tests/debug2/zrealm_avl1.gno new file mode 100644 index 00000000000..462ff521447 --- /dev/null +++ b/gnovm/tests/debug2/zrealm_avl1.gno @@ -0,0 +1,379 @@ +// PKGPATH: gno.land/r/test +package main + +import ( + "gno.land/p/demo/avl" +) + +var node *avl.Node + +func init() { + node = avl.NewNode("key0", "value0") + node, _ = node.Set("key1", "value1") +} + +func main() { + var updated bool + node, updated = node.Set("key2", "value2") + // println(node, updated) + println(updated, node.Size()) +} + +// Output: +// false 3 + +// Realm: +// switchrealm["gno.land/r/test"] +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:9]={ +// "Fields": [ +// { +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "16" +// }, +// "V": { +// "@type": "/gno.StringValue", +// "value": "key2" +// } +// }, +// { +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "16" +// }, +// "V": { +// "@type": "/gno.StringValue", +// "value": "value2" +// } +// }, +// { +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "64" +// } +// }, +// { +// "N": "AQAAAAAAAAA=", +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "32" +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// } +// } +// ], +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8", +// "RefCount": "1" +// } +// } +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:8]={ +// "Fields": [ +// { +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "16" +// }, +// "V": { +// "@type": "/gno.StringValue", +// "value": "key2" +// } +// }, +// {}, +// { +// "N": "AQAAAAAAAAA=", +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "64" +// } +// }, +// { +// "N": "AgAAAAAAAAA=", +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "32" +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": null, +// "Index": "0", +// "TV": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "7a8a63e17a567d7b0891ac89d5cd90072a73787d", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" +// } +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": null, +// "Index": "0", +// "TV": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "ab5a297f4eb033d88bdf1677f4dc151ccb9fde9f", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9" +// } +// } +// } +// } +// ], +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", +// "RefCount": "1" +// } +// } +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:7]={ +// "Fields": [ +// { +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "16" +// }, +// "V": { +// "@type": "/gno.StringValue", +// "value": "key1" +// } +// }, +// {}, +// { +// "N": "AgAAAAAAAAA=", +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "64" +// } +// }, +// { +// "N": "AwAAAAAAAAA=", +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "32" +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": null, +// "Index": "0", +// "TV": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "627e8e517e7ae5db0f3b753e2a32b607989198b6", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5" +// } +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": null, +// "Index": "0", +// "TV": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "fe8afd501233fb95375016199f0443b3c6ab1fbc", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8" +// } +// } +// } +// } +// ], +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", +// "RefCount": "1" +// } +// } +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ +// "Blank": {}, +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", +// "IsEscaped": true, +// "ModTime": "6", +// "RefCount": "2" +// }, +// "Parent": null, +// "Source": { +// "@type": "/gno.RefNode", +// "BlockNode": null, +// "Location": { +// "File": "", +// "Line": "0", +// "Nonce": "0", +// "PkgPath": "gno.land/r/test" +// } +// }, +// "Values": [ +// { +// "T": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// }, +// "V": { +// "@type": "/gno.FuncValue", +// "Closure": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" +// }, +// "FileName": "main.gno", +// "IsMethod": false, +// "Name": "init.0", +// "NativeName": "", +// "NativePkg": "", +// "PkgPath": "gno.land/r/test", +// "Source": { +// "@type": "/gno.RefNode", +// "BlockNode": null, +// "Location": { +// "File": "main.gno", +// "Line": "10", +// "Nonce": "0", +// "PkgPath": "gno.land/r/test" +// } +// }, +// "TransientLoopData": null, +// "Type": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// }, +// "V": { +// "@type": "/gno.FuncValue", +// "Closure": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" +// }, +// "FileName": "main.gno", +// "IsMethod": false, +// "Name": "main", +// "NativeName": "", +// "NativePkg": "", +// "PkgPath": "gno.land/r/test", +// "Source": { +// "@type": "/gno.RefNode", +// "BlockNode": null, +// "Location": { +// "File": "main.gno", +// "Line": "15", +// "Nonce": "0", +// "PkgPath": "gno.land/r/test" +// } +// }, +// "TransientLoopData": null, +// "Type": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": null, +// "Index": "0", +// "TV": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "c5eefc40ed065461b4a920c1349ed734ffdead8f", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7" +// } +// } +// } +// } +// ] +// } +// d[a8ada09dee16d791fd406d629fe29bb0ed084a30:4] From 6b29a90f505f4f23ff4a7aa8f50f2135148e0f44 Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Fri, 23 Feb 2024 16:30:49 +0800 Subject: [PATCH 29/32] simplify doOpFunc logic --- gnovm/pkg/gnolang/alloc.go | 1 + gnovm/pkg/gnolang/machine.go | 1 + gnovm/pkg/gnolang/nodes.go | 20 +++++++- gnovm/pkg/gnolang/op_call.go | 7 +++ gnovm/pkg/gnolang/op_exec.go | 10 ++-- gnovm/pkg/gnolang/op_expressions.go | 76 +++++++++++++++++++---------- gnovm/pkg/gnolang/preprocess.go | 35 ++++++++----- gnovm/pkg/gnolang/values.go | 2 + 8 files changed, 110 insertions(+), 42 deletions(-) diff --git a/gnovm/pkg/gnolang/alloc.go b/gnovm/pkg/gnolang/alloc.go index a83f8102a2b..f19c6b5b8d5 100644 --- a/gnovm/pkg/gnolang/alloc.go +++ b/gnovm/pkg/gnolang/alloc.go @@ -275,6 +275,7 @@ func (alloc *Allocator) NewMap(size int) *MapValue { } func (alloc *Allocator) NewBlock(source BlockNode, parent *Block) *Block { + debug.Printf("---NewBlock, source: %v, parent: %v \n", source, parent) alloc.AllocateBlock(int64(source.GetNumNames())) return NewBlock(source, parent) } diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go index 8214eb49d69..b7076226250 100644 --- a/gnovm/pkg/gnolang/machine.go +++ b/gnovm/pkg/gnolang/machine.go @@ -1649,6 +1649,7 @@ func (m *Machine) LastBlock() *Block { return m.Blocks[len(m.Blocks)-1] } +// todo: values.2395 func (m *Machine) GetBlockAt(index int) *Block { if index > len(m.Blocks)-1 { panic("no block at this index, should not happen") diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index 7085e4f6ba5..51cf1a06418 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -498,6 +498,7 @@ type FuncLitExpr struct { Body // function body RefLoopBlockNodes []BlockNode RefIndices []int + LoopData []*LoopBlockData } // The preprocessor replaces const expressions @@ -1515,6 +1516,7 @@ func (sb *StaticBlock) revertToOld() { // Implements BlockNode func (sb *StaticBlock) InitStaticBlock(source BlockNode, parent BlockNode) { + debug.Printf("---init static block, source: %v, parent: %v \n", source, parent) if sb.Names != nil || sb.Block.Source != nil { panic("StaticBlock already initialized") } @@ -1525,7 +1527,7 @@ func (sb *StaticBlock) InitStaticBlock(source BlockNode, parent BlockNode) { Parent: nil, } } else { - sb.Block = Block{ + sb.Block = Block{ // build Hierarchical structure in preprocess stage Source: source, Values: nil, Parent: parent.GetStaticBlock().GetBlock(), @@ -1538,6 +1540,22 @@ func (sb *StaticBlock) InitStaticBlock(source BlockNode, parent BlockNode) { return } +func (sb *StaticBlock) dump() { + debug.Println("==============dump staticBlock==============") + for i, n := range sb.Names { + debug.Printf("name[%d] is : %v \n", i, n) + } + for i, c := range sb.Consts { + debug.Printf("const[%d] is : %v \n", i, c) + } + + debug.Printf("block.Source: %v \n", sb.Block.Source) + debug.Printf("block.Parent: %v \n", sb.Block.Parent) + debug.Printf("block: %v \n", sb.Block.String()) + + debug.Println("==============end==============") +} + // Implements BlockNode. func (sb *StaticBlock) IsInitialized() bool { return sb.Block.Source != nil diff --git a/gnovm/pkg/gnolang/op_call.go b/gnovm/pkg/gnolang/op_call.go index 7915bbca6e5..8cf6341d8d9 100644 --- a/gnovm/pkg/gnolang/op_call.go +++ b/gnovm/pkg/gnolang/op_call.go @@ -62,6 +62,13 @@ func (m *Machine) doOpCall() { var loopBlock *Block // target loop block with values to be updated if fv.TransientLoopData != nil { // it captured a bundle of values + debug.Printf("---doOpCall, addr of x.LoopData: %p \n", fv.TransientLoopData) + + if debug { + for i, lbd := range fv.TransientLoopData { + fmt.Printf("========doOpCall, TransientLoopData[%d] is: %s, index: %d \n", i, lbd.loopValuesBox, lbd.index) + } + } for i, loopData := range fv.TransientLoopData { // each LoopValuesBox is for a certain level of block box := loopData.loopValuesBox if box.isSealed { diff --git a/gnovm/pkg/gnolang/op_exec.go b/gnovm/pkg/gnolang/op_exec.go index 4e98b2ba534..951d2d58eef 100644 --- a/gnovm/pkg/gnolang/op_exec.go +++ b/gnovm/pkg/gnolang/op_exec.go @@ -45,6 +45,7 @@ SelectStmt -> func updateCapturedValue(m *Machine, lb *Block) { debug.Println("---op_exec, updateCapturedValue") + lb.GetBodyStmt().LoopValuesBox = lb.Source.GetStaticBlock().GetBodyStmt().LoopValuesBox bs := lb.GetBodyStmt() debug.Printf("---op_exec, lvBox: %v \n", bs.LoopValuesBox) @@ -538,15 +539,18 @@ EXEC_SWITCH: m.PushExpr(cs.X) m.PushOp(OpEval) case *ForStmt: + debug.Printf("---ForStmt: %v \n", cs) + debug.Printf("---ForStmt.sb.bs: %v \n", cs.GetStaticBlock().bodyStmt.loopBody) m.PushFrameBasic(cs) - b := m.Alloc.NewBlock(cs, m.LastBlock()) + // do the copy from preprocess block to runtime block with NewBlock + b := m.Alloc.NewBlock(cs, m.LastBlock()) // runtime block b.bodyStmt = bodyStmt{ Body: cs.Body, BodyLen: len(cs.Body), NextBodyIndex: -2, Cond: cs.Cond, Post: cs.Post, - loopBody: &LoopBody{isLoop: true}, + loopBody: cs.GetStaticBlock().bodyStmt.loopBody, // copy from preprocess block } m.PushBlock(b) m.PushOp(OpForLoop) @@ -634,7 +638,7 @@ EXEC_SWITCH: Key: cs.Key, Value: cs.Value, Op: cs.Op, - loopBody: &LoopBody{isLoop: true}, + loopBody: cs.GetStaticBlock().bodyStmt.loopBody, //isLoop: true, } m.PushBlock(b) diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index f246e9ee22d..e760504d00f 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -685,38 +685,62 @@ func (m *Machine) doOpFuncLit() { lb := m.LastBlock() var loopData []*LoopBlockData // for fv to track all transient values - //var refLoopBlocks []*Block - var lvBox *LoopValuesBox // container per block to store transient values for captured vars - for index, refBn := range x.RefLoopBlockNodes { - debug.Printf("---refBn: %v \n", refBn) - // find counterpart block of refBn - lvBox = refBn.GetStaticBlock().bodyStmt.LoopValuesBox - targetRealBlock := m.GetBlockAt(x.RefIndices[index] - 1) - debug.Printf("---targetBlock: %v \n", targetRealBlock) - debug.Printf("---doOpFuncLit, lvBox: %v \n", lvBox) - if lvBox != nil && lvBox.isFilled { - debug.Println("---copy to real block") - // copy to block - targetRealBlock.bodyStmt.LoopValuesBox = lvBox - - for _, tst := range lvBox.transient { - tst.cursor++ // increase cursor of every nameExpr - debug.Println("---cursor: ", tst.cursor) - } - - lvBox.isSealed = false // reset every time - // each fv may have n outer loopBlock, reference them all - loopData = append(loopData, &LoopBlockData{index: lvBox.transient[0].cursor, loopValuesBox: lvBox}) + // mutate state of loop data for control + for _, lbd := range x.LoopData { + var cursor int + for _, tst := range lbd.loopValuesBox.transient { + tst.cursor++ // increase cursor of every nameExpr + cursor = tst.cursor + debug.Println("---cursor: ", cursor) } - //refLoopBlocks = append(refLoopBlocks, targetRealBlock) + // unpack, mutate, pack + lvBox := lbd.loopValuesBox + lvBox.isSealed = false // reset per loop + //lbd.index = lbd.loopValuesBox.transient[0].cursor + debug.Printf("---lvBox: %v \n", lbd.loopValuesBox) + // update + loopData = append(loopData, &LoopBlockData{index: cursor, loopValuesBox: lvBox}) } - if debug { - for i, ts := range loopData { - fmt.Printf("========TransientLoopData[%d] is: %s, index: %d \n", i, ts.loopValuesBox, ts.index) + for i, lbd := range x.LoopData { + fmt.Printf("========doOpFuncLit, TransientLoopData[%d] is: %s, index: %d \n", i, lbd.loopValuesBox, lbd.index) } } + debug.Printf("---doOpFuncLit, addr of x.LoopData: %p \n", x.LoopData) + // + //var loopData []*LoopBlockData // for fv to track all transient values + ////var refLoopBlocks []*Block + //var lvBox *LoopValuesBox // container per block to store transient values for captured vars + //for index, refBn := range x.RefLoopBlockNodes { + // debug.Printf("---refBn: %v \n", refBn) + // // find counterpart block of refBn + // lvBox = refBn.GetStaticBlock().bodyStmt.LoopValuesBox + // targetRealBlock := m.GetBlockAt(x.RefIndices[index] - 1) + // debug.Printf("---targetBlock: %v \n", targetRealBlock) + // debug.Printf("---doOpFuncLit, lvBox: %v \n", lvBox) + // + // if lvBox != nil && lvBox.isFilled { + // debug.Println("---copy to real block") + // // copy to block + // targetRealBlock.bodyStmt.LoopValuesBox = lvBox + // + // for _, tst := range lvBox.transient { + // tst.cursor++ // increase cursor of every nameExpr + // debug.Println("---cursor: ", tst.cursor) + // } + // + // lvBox.isSealed = false // reset per loop + // // each fv may have n outer loopBlock, reference them all + // loopData = append(loopData, &LoopBlockData{index: lvBox.transient[0].cursor, loopValuesBox: lvBox}) + // } + //} + // + //if debug { + // for i, ts := range loopData { + // fmt.Printf("========TransientLoopData[%d] is: %s, index: %d \n", i, ts.loopValuesBox, ts.index) + // } + //} ft := m.PopValue().V.(TypeValue).Type.(*FuncType) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index e190698df39..16a491a9526 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -346,8 +346,11 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // TRANS_BLOCK ----------------------- case *ForStmt: + debug.Println("---for stmt") pushInitBlock(n, &last, &stack) n.bodyStmt.loopBody = &LoopBody{isLoop: true} + debug.Printf("---sb of ForStmt: %v \n", n.GetStaticBlock()) + n.GetStaticBlock().dump() // TRANS_BLOCK ----------------------- case *IfStmt: @@ -845,6 +848,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { loopBlock BlockNode // e.g. a `for` block that is outside of funcLit block lvBox *LoopValuesBox // container per block to store transient values for captured vars lastGen, gen uint8 + loopData []*LoopBlockData // for fv to track all transient values ) // when iterating goes on, funcValue will yield several replicas, each of which should capture a slice of @@ -875,7 +879,6 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // prepare lvBox if isLoopBlock { lvBox = loopBlock.GetStaticBlock().GetBodyStmt().LoopValuesBox // get LoopValuesBox from specific block, that is shared across current level of for/range loop - //} if lvBox == nil { lvBox = &LoopValuesBox{} } @@ -885,17 +888,20 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } lvBox.transient = append(lvBox.transient, tst) - // got a loop block - //if isLoopBlock { // got target block debug.Printf("---fill nx: %v \n", nx) lvBox.isFilled = true // record in loop block loopBlock.GetStaticBlock().GetBodyStmt().LoopValuesBox = lvBox // set ref loop block nodes - n.RefLoopBlockNodes = append(n.RefLoopBlockNodes, loopBlock) - n.RefIndices = append(n.RefIndices, int(nx.Path.Depth)) - //} - // set for potentially implicit loop block, goto label + //n.RefLoopBlockNodes = append(n.RefLoopBlockNodes, loopBlock) + //n.RefIndices = append(n.RefIndices, int(nx.Path.Depth)) + loopData = append(loopData, &LoopBlockData{index: lvBox.transient[0].cursor, loopValuesBox: lvBox}) + n.LoopData = loopData // this will be copied to fv + } + if debug { + for i, ts := range loopData { + fmt.Printf("========TransientLoopData[%d] is: %s, index: %d \n", i, ts.loopValuesBox, ts.index) + } } } @@ -1802,6 +1808,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { debug.Println("---going to fine funcLitExpr blockNode") dumpSnare(snare) + var loopData []*LoopBlockData if labelLine < n.GetLine() { // only jmp to previous line make it loop? for i := len(snare) - 1; i >= 0; i-- { if fx, ok := snare[i].(*FuncLitExpr); ok { @@ -1845,11 +1852,14 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // set state if lvBox.isFilled { last.GetStaticBlock().GetBodyStmt().LoopValuesBox = lvBox - fx.RefLoopBlockNodes = append(fx.RefLoopBlockNodes, last) - // set index for ref blocks - for _, tst := range lvBox.transient { - fx.RefIndices = append(fx.RefIndices, int(tst.nx.Path.Depth)) - } + loopData = append(loopData, &LoopBlockData{index: lvBox.transient[0].cursor, loopValuesBox: lvBox}) + fx.LoopData = loopData + + //fx.RefLoopBlockNodes = append(fx.RefLoopBlockNodes, last) + //// set index for ref blocks + //for _, tst := range lvBox.transient { + // fx.RefIndices = append(fx.RefIndices, int(tst.nx.Path.Depth)) + //} } } } @@ -2174,6 +2184,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } func pushInitBlock(bn BlockNode, last *BlockNode, stack *[]BlockNode) { + debug.Printf("---pushInitBlock, source: %v, \n parent: %v \n", bn, *last) if !bn.IsInitialized() { bn.InitStaticBlock(bn, *last) } else { diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index d7875b47a37..c028c85141d 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -583,6 +583,7 @@ func (tsb *LoopValuesBox) String() string { s += "\n" s += "==================LoopValuesBox===================\n" s += fmt.Sprintf("isFilled: %v \n", tsb.isFilled) + s += fmt.Sprintf("isSealed: %v \n", tsb.isSealed) for i, t := range tsb.transient { s += fmt.Sprintf("nx[%d]: %v \n", i, t.nx) for j, v := range t.values { @@ -2318,6 +2319,7 @@ func (sb *StaticBlock) SetLoopBody(start, end int) { func (b *Block) UpdateValue(index int, tv TypedValue) { debug.Printf("---UpdateValue, index: %d \n", index) + debug.Printf("---UpdateValue, tv: %v \n", tv) debug.Printf("---UpdateValue, block: %v \n", b) for i := range b.Values { if i == index { From 0e8264013e06bdaf9140b9ea33ca5845699d1434 Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Fri, 23 Feb 2024 19:20:53 +0800 Subject: [PATCH 30/32] comment & clean unused logic --- gnovm/pkg/gnolang/machine.go | 8 -- gnovm/pkg/gnolang/nodes.go | 71 +++++++++------ gnovm/pkg/gnolang/op_call.go | 2 +- gnovm/pkg/gnolang/op_exec.go | 13 ++- gnovm/pkg/gnolang/op_expressions.go | 47 ++-------- gnovm/pkg/gnolang/preprocess.go | 134 +++++++++++++--------------- gnovm/pkg/gnolang/values.go | 53 ----------- gnovm/tests/debug/defer.gno | 31 +++++++ 8 files changed, 147 insertions(+), 212 deletions(-) create mode 100644 gnovm/tests/debug/defer.gno diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go index b7076226250..91cbc0b0c21 100644 --- a/gnovm/pkg/gnolang/machine.go +++ b/gnovm/pkg/gnolang/machine.go @@ -1649,14 +1649,6 @@ func (m *Machine) LastBlock() *Block { return m.Blocks[len(m.Blocks)-1] } -// todo: values.2395 -func (m *Machine) GetBlockAt(index int) *Block { - if index > len(m.Blocks)-1 { - panic("no block at this index, should not happen") - } - return m.Blocks[len(m.Blocks)-1-index] -} - // Pushes a frame with one less statement. func (m *Machine) PushFrameBasic(s Stmt) { label := s.GetLabel() diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index 51cf1a06418..19949d31db6 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -496,9 +496,23 @@ type FuncLitExpr struct { StaticBlock Type FuncTypeExpr // function type Body // function body - RefLoopBlockNodes []BlockNode - RefIndices []int - LoopData []*LoopBlockData + TransientLoopData []*LoopBlockData +} + +func (fx *FuncLitExpr) GetCapturedNxs(store Store) (captures []*NameExpr) { + for _, name := range fx.GetExternNames() { + vp := fx.GetPathForName(store, name) + if vp.Depth == 0 { // skip uverse + continue + } + vp.Depth -= 1 // from the perspective of funcLit block + nx := &NameExpr{ + Name: name, + Path: vp, + } + captures = append(captures, nx) + } + return } // The preprocessor replaces const expressions @@ -873,9 +887,10 @@ type SwitchClauseStmt struct { // ---------------------------------------- // bodyStmt (persistent) -// this assumes that a goto stmt to less liners forms an implicit loop -// if an funcLitExpr embeded, do capture. -type LoopBody struct { +// this assumes that a goto stmt to label ahead +// forms an implicit loop. +// if an funcLitExpr embeded, do capture staff. +type ImplicitLoopBlock struct { isLoop bool start int // line of label start end int // line of goto stmt @@ -884,28 +899,28 @@ type LoopBody struct { // NOTE: embedded in Block. type bodyStmt struct { Attributes - Body // for non-loop stmts - BodyLen int // for for-continue - NextBodyIndex int // init:-2, cond/elem:-1, body:0..., post:n - NumOps int // number of Ops, for goto - NumValues int // number of Values, for goto - NumExprs int // number of Exprs, for goto - NumStmts int // number of Stmts, for goto - Cond Expr // for ForStmt - Post Stmt // for ForStmt - LoopValuesBox *LoopValuesBox // a series of transient values of captured var generated as the iteration goes on - isLoop bool - loopBody *LoopBody - Active Stmt // for PopStmt() - Key Expr // for RangeStmt - Value Expr // for RangeStmt - Op Word // for RangeStmt - ListLen int // for RangeStmt only - ListIndex int // for RangeStmt only - NextItem *MapListItem // fpr RangeStmt w/ maps only - StrLen int // for RangeStmt w/ strings only - StrIndex int // for RangeStmt w/ strings only - NextRune rune // for RangeStmt w/ strings only + Body // for non-loop stmts + BodyLen int // for for-continue + NextBodyIndex int // init:-2, cond/elem:-1, body:0..., post:n + NumOps int // number of Ops, for goto + NumValues int // number of Values, for goto + NumExprs int // number of Exprs, for goto + NumStmts int // number of Stmts, for goto + Cond Expr // for ForStmt + Post Stmt // for ForStmt + LoopValuesBox *LoopValuesBox // a series of transient values of captured var generated as the iteration goes on + isLoop bool + implicitLoopBlock *ImplicitLoopBlock + Active Stmt // for PopStmt() + Key Expr // for RangeStmt + Value Expr // for RangeStmt + Op Word // for RangeStmt + ListLen int // for RangeStmt only + ListIndex int // for RangeStmt only + NextItem *MapListItem // fpr RangeStmt w/ maps only + StrLen int // for RangeStmt w/ strings only + StrIndex int // for RangeStmt w/ strings only + NextRune rune // for RangeStmt w/ strings only } func (x *bodyStmt) PopActiveStmt() (as Stmt) { diff --git a/gnovm/pkg/gnolang/op_call.go b/gnovm/pkg/gnolang/op_call.go index 8cf6341d8d9..fa2a9373874 100644 --- a/gnovm/pkg/gnolang/op_call.go +++ b/gnovm/pkg/gnolang/op_call.go @@ -58,8 +58,8 @@ func (m *Machine) doOpCall() { debug.Printf("---fv: %v \n", fv) clo := fr.Func.GetClosure(m.Store) // this is "static" - // update block vars using captured vars + // update block vars using captured vars var loopBlock *Block // target loop block with values to be updated if fv.TransientLoopData != nil { // it captured a bundle of values debug.Printf("---doOpCall, addr of x.LoopData: %p \n", fv.TransientLoopData) diff --git a/gnovm/pkg/gnolang/op_exec.go b/gnovm/pkg/gnolang/op_exec.go index 951d2d58eef..e3b80aff2d0 100644 --- a/gnovm/pkg/gnolang/op_exec.go +++ b/gnovm/pkg/gnolang/op_exec.go @@ -56,7 +56,7 @@ func updateCapturedValue(m *Machine, lb *Block) { //} if bs.LoopValuesBox != nil && bs.LoopValuesBox.isFilled && !bs.LoopValuesBox.isTainted { - var isSealed bool + var isSeal bool for i, tt := range bs.LoopValuesBox.transient { nvp := lb.Source.GetPathForName(m.Store, tt.nx.Name) ptr := m.LastBlock().GetPointerTo(m.Store, nvp) @@ -70,9 +70,9 @@ func updateCapturedValue(m *Machine, lb *Block) { for j := 0; j < expandRatio; j++ { bs.LoopValuesBox.transient[i].values = append(bs.LoopValuesBox.transient[i].values, tv) } - isSealed = true + isSeal = true } - if isSealed { + if isSeal { debug.Printf("---seal for: %v \n", bs.LoopValuesBox) bs.LoopValuesBox.isSealed = true // seal for this LoopValuesBox for closure execution. } @@ -540,7 +540,7 @@ EXEC_SWITCH: m.PushOp(OpEval) case *ForStmt: debug.Printf("---ForStmt: %v \n", cs) - debug.Printf("---ForStmt.sb.bs: %v \n", cs.GetStaticBlock().bodyStmt.loopBody) + //debug.Printf("---ForStmt.sb.bs: %v \n", cs.GetStaticBlock().bodyStmt.implicitLoopBlock) m.PushFrameBasic(cs) // do the copy from preprocess block to runtime block with NewBlock b := m.Alloc.NewBlock(cs, m.LastBlock()) // runtime block @@ -550,7 +550,7 @@ EXEC_SWITCH: NextBodyIndex: -2, Cond: cs.Cond, Post: cs.Post, - loopBody: cs.GetStaticBlock().bodyStmt.loopBody, // copy from preprocess block + //implicitLoopBlock: cs.GetStaticBlock().bodyStmt.implicitLoopBlock, // copy from preprocess block } m.PushBlock(b) m.PushOp(OpForLoop) @@ -638,8 +638,7 @@ EXEC_SWITCH: Key: cs.Key, Value: cs.Value, Op: cs.Op, - loopBody: cs.GetStaticBlock().bodyStmt.loopBody, - //isLoop: true, + //implicitLoopBlock: cs.GetStaticBlock().bodyStmt.implicitLoopBlock, } m.PushBlock(b) // TODO: replace with "cs.Op". diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index e760504d00f..7a36e7539b8 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -684,10 +684,9 @@ func (m *Machine) doOpFuncLit() { lb := m.LastBlock() - var loopData []*LoopBlockData // for fv to track all transient values - - // mutate state of loop data for control - for _, lbd := range x.LoopData { + var transientLoopData []*LoopBlockData // for fv to track all transient values + // mutate state of for control + for _, lbd := range x.TransientLoopData { var cursor int for _, tst := range lbd.loopValuesBox.transient { tst.cursor++ // increase cursor of every nameExpr @@ -700,47 +699,13 @@ func (m *Machine) doOpFuncLit() { //lbd.index = lbd.loopValuesBox.transient[0].cursor debug.Printf("---lvBox: %v \n", lbd.loopValuesBox) // update - loopData = append(loopData, &LoopBlockData{index: cursor, loopValuesBox: lvBox}) + transientLoopData = append(transientLoopData, &LoopBlockData{index: cursor, loopValuesBox: lvBox}) } if debug { - for i, lbd := range x.LoopData { + for i, lbd := range x.TransientLoopData { fmt.Printf("========doOpFuncLit, TransientLoopData[%d] is: %s, index: %d \n", i, lbd.loopValuesBox, lbd.index) } } - debug.Printf("---doOpFuncLit, addr of x.LoopData: %p \n", x.LoopData) - // - //var loopData []*LoopBlockData // for fv to track all transient values - ////var refLoopBlocks []*Block - //var lvBox *LoopValuesBox // container per block to store transient values for captured vars - //for index, refBn := range x.RefLoopBlockNodes { - // debug.Printf("---refBn: %v \n", refBn) - // // find counterpart block of refBn - // lvBox = refBn.GetStaticBlock().bodyStmt.LoopValuesBox - // targetRealBlock := m.GetBlockAt(x.RefIndices[index] - 1) - // debug.Printf("---targetBlock: %v \n", targetRealBlock) - // debug.Printf("---doOpFuncLit, lvBox: %v \n", lvBox) - // - // if lvBox != nil && lvBox.isFilled { - // debug.Println("---copy to real block") - // // copy to block - // targetRealBlock.bodyStmt.LoopValuesBox = lvBox - // - // for _, tst := range lvBox.transient { - // tst.cursor++ // increase cursor of every nameExpr - // debug.Println("---cursor: ", tst.cursor) - // } - // - // lvBox.isSealed = false // reset per loop - // // each fv may have n outer loopBlock, reference them all - // loopData = append(loopData, &LoopBlockData{index: lvBox.transient[0].cursor, loopValuesBox: lvBox}) - // } - //} - // - //if debug { - // for i, ts := range loopData { - // fmt.Printf("========TransientLoopData[%d] is: %s, index: %d \n", i, ts.loopValuesBox, ts.index) - // } - //} ft := m.PopValue().V.(TypeValue).Type.(*FuncType) @@ -752,7 +717,7 @@ func (m *Machine) doOpFuncLit() { Source: x, Name: "", Closure: lb, - TransientLoopData: loopData, + TransientLoopData: transientLoopData, PkgPath: m.Package.PkgPath, body: x.Body, nativeBody: nil, diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 16a491a9526..0abb6b9c006 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -118,7 +118,6 @@ func dumpSnare(snare []BlockNode) { } func getBlockNodeAt(stack []BlockNode, n int) BlockNode { - debug.Println("---getParentBlockNode") if n > len(stack)-1 { return nil } else { @@ -126,11 +125,14 @@ func getBlockNodeAt(stack []BlockNode, n int) BlockNode { } } -// find nearest block is loop and contains name of n +// find block node that is loop block which contains name of nx.Name +// the target block node will ref to proper nxs wrapped in a container +// XXX, Note that in preprocess handling, the ref is stored on bn.StaticBlock.Block.BodyStmt +// can bn.StaticBlock.Block be reused in runtime rather than a new one? func findLoopStaticBlockAndPath(stack []BlockNode, nx *NameExpr) (BlockNode, bool, uint8) { debug.Printf("---findLoopStaticBlockAndPath, nx: %v \n", nx) dumpStack(stack) - var gen uint8 = 1 // which level it is + var gen uint8 = 1 // depth of blockNode var bn BlockNode // nav to target level for i := uint8(0); i < nx.Path.Depth+1; i++ { // find target block at certain depth @@ -143,12 +145,12 @@ func findLoopStaticBlockAndPath(stack []BlockNode, nx *NameExpr) (BlockNode, boo debug.Printf("---got target bn: %v \n", bn) debug.Printf("---got target bn type: %T \n", bn) - debug.Printf("---got target loopBody: %v \n", bn.GetStaticBlock().bodyStmt.loopBody) + debug.Printf("---got target implicitLoopBlock: %v \n", bn.GetStaticBlock().bodyStmt.implicitLoopBlock) // tmp, if static not nil, means some work done in preprocess, using static - if bn.GetStaticBlock().bodyStmt.loopBody != nil { + if bn.GetStaticBlock().bodyStmt.implicitLoopBlock != nil { // find name - if bn.GetStaticBlock().bodyStmt.loopBody.isLoop { + if bn.GetStaticBlock().bodyStmt.implicitLoopBlock.isLoop { names := bn.GetBlockNames() for _, name := range names { if nx.Name == name { // find n in this block @@ -348,7 +350,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { case *ForStmt: debug.Println("---for stmt") pushInitBlock(n, &last, &stack) - n.bodyStmt.loopBody = &LoopBody{isLoop: true} + n.bodyStmt.implicitLoopBlock = &ImplicitLoopBlock{isLoop: true} debug.Printf("---sb of ForStmt: %v \n", n.GetStaticBlock()) n.GetStaticBlock().dump() @@ -428,7 +430,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } } } - n.bodyStmt.loopBody = &LoopBody{isLoop: true} + n.bodyStmt.implicitLoopBlock = &ImplicitLoopBlock{isLoop: true} // TRANS_BLOCK ----------------------- case *FuncLitExpr: @@ -822,21 +824,9 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { case *FuncLitExpr: debug.Printf("---trans_leave, funcLitExpr: %v \n", n) // prepare captured named symbols - captures := make([]*NameExpr, 0) - for _, name := range n.GetExternNames() { - vp := n.GetPathForName(store, name) - if vp.Depth == 0 { // skip uverse - continue - } - vp.Depth -= 1 // from the perspective of funcLit block - nx := &NameExpr{ - Name: name, - Path: vp, - } - captures = append(captures, nx) - } - + captures := n.GetCapturedNxs(store) // sort it so to traverse block in order from inner to outer + // this is for the case of more than one loop block surrounded sortDepth := func(i, j int) bool { return int(captures[i].Path.Depth) < int(captures[j].Path.Depth) } @@ -844,13 +834,16 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // loop related vars var ( - isLoopBlock bool - loopBlock BlockNode // e.g. a `for` block that is outside of funcLit block - lvBox *LoopValuesBox // container per block to store transient values for captured vars - lastGen, gen uint8 - loopData []*LoopBlockData // for fv to track all transient values + isLoopBlock bool + loopBlockNode BlockNode // e.g. a `for` block that surround funcLit block + lvBox *LoopValuesBox // container per block, to store transient values for captured vars + lastGen, gen uint8 ) + // ref captured nx to corresponding loop block, + // it could be a for, range block, or implicit + // loop block with the pattern of goto label + // when iterating goes on, funcValue will yield several replicas, each of which should capture a slice of // transient values for a nameExpr. // e.g. `for i:=0, i++; i<2{ x := i }`, the transient values for x should be [0 ,1], this is recorded and @@ -861,46 +854,48 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // the transient state of x, y is: [0,0], [0,1], [1,0], [1,1]. // the outer block will hold transient values of [0,0,1,1] for `x`, and inner block 1 will hold [0,1] for `y`, // another inner block 2 holds [0,1] for y too. + for _, nx := range captures { debug.Printf("---loop captures, nx name: %v, nx path: %v \n", nx.Name, nx.Path) // start search block, in the order of small depth to big depth - loopBlock, isLoopBlock, gen = findLoopStaticBlockAndPath(stack, nx) + loopBlockNode, isLoopBlock, gen = findLoopStaticBlockAndPath(stack, nx) debug.Printf("isLoopBlock: %v \n", isLoopBlock) - debug.Printf("loopBlock: %v \n", loopBlock) + debug.Printf("loopBlockNode: %v \n", loopBlockNode) debug.Printf("depth: %v \n", gen) if lastGen == 0 { lastGen = gen } else if gen != lastGen { // if enter new level of block, pack last box lastGen = gen - if lvBox != nil { // xxx, need this? + if lvBox != nil { // closure14.gno lvBox.isFilled = true } } // prepare lvBox if isLoopBlock { - lvBox = loopBlock.GetStaticBlock().GetBodyStmt().LoopValuesBox // get LoopValuesBox from specific block, that is shared across current level of for/range loop - if lvBox == nil { + lvBox = loopBlockNode.GetStaticBlock().GetBodyStmt().LoopValuesBox + if lvBox == nil { // XXX, closure11_b.gno lvBox = &LoopValuesBox{} } tst := &Transient{ nx: nx, - cursor: -1, // inc every iteration, implies sequence of a fv, and index of transient values. + cursor: -1, // inc every iteration, and index of transient values, also used as sequence of a fv(s). } lvBox.transient = append(lvBox.transient, tst) + lvBox.isFilled = true debug.Printf("---fill nx: %v \n", nx) - lvBox.isFilled = true - // record in loop block - loopBlock.GetStaticBlock().GetBodyStmt().LoopValuesBox = lvBox - // set ref loop block nodes - //n.RefLoopBlockNodes = append(n.RefLoopBlockNodes, loopBlock) - //n.RefIndices = append(n.RefIndices, int(nx.Path.Depth)) - loopData = append(loopData, &LoopBlockData{index: lvBox.transient[0].cursor, loopValuesBox: lvBox}) - n.LoopData = loopData // this will be copied to fv + // ref box from loopBlockNode + loopBlockNode.GetStaticBlock().GetBodyStmt().LoopValuesBox = lvBox + // ref loop block nodes from funcLitExpr, and copy to funcValue later + // so a funcLitExpr will ref to more than one loopBlockNode, and share + // loopValueBox[i] with them, when a loop block switch scope in op_exec, + // the transient state of nx will be recorded to loopBlock(runtime), and + // funcValue, and to replay when doOpCall. + n.TransientLoopData = append(n.TransientLoopData, &LoopBlockData{index: lvBox.transient[0].cursor, loopValuesBox: lvBox}) } if debug { - for i, ts := range loopData { - fmt.Printf("========TransientLoopData[%d] is: %s, index: %d \n", i, ts.loopValuesBox, ts.index) + for i, ts := range n.TransientLoopData { + fmt.Printf("========preprocess funcLitExpr, TransientLoopData[%d] is: %s, index: %d \n", i, ts.loopValuesBox, ts.index) } } } @@ -1798,41 +1793,37 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { case BREAK: case CONTINUE: case GOTO: - debug.Printf("---branchStmt, n.Label: %v, n.Line: %d \n", n.GetLabel(), n.GetLine()) _, depth, index, labelLine := findGotoLabel(last, n.Label) n.Depth = depth n.BodyIndex = index + gotoLine := n.GetLine() + + debug.Printf("---branchStmt, n.Label: %v, n.Line: %d \n", n.GetLabel(), n.GetLine()) debug.Println("depth:, index:, labelLine:", depth, index, labelLine) debug.Printf("---BranchStmt, last: %v, %T \n", last, last) - - debug.Println("---going to fine funcLitExpr blockNode") + debug.Println("---going to find funcLitExpr blockNode") dumpSnare(snare) - var loopData []*LoopBlockData - if labelLine < n.GetLine() { // only jmp to previous line make it loop? + // XXX, Hacky way for special case of a goto label forms an implicit loop block + // especially when to goto stmt is after the label, and has + // funcLitExpr embedded in this block. + // XXX, Note that in this case, all logic about capture happens here since + // funcLitExpr is already traversed without knowing if there is + // an outer loopBlock. + // and due to the face blockNode in []stack in popped every time the node + // is done preprocess, so a specified stack call []snare is used to catch + // this pattern. can this be improved? + if labelLine < gotoLine { // only jmp to previous line make it loop? for i := len(snare) - 1; i >= 0; i-- { if fx, ok := snare[i].(*FuncLitExpr); ok { // do staff on fx, copy x here debug.Printf("---fx: %v, at line of: %d \n", fx, fx.GetLine()) // do staff with fx // funcLit in implicit loop block - if labelLine < fx.GetLine() && fx.GetLine() < n.GetLine() { + if labelLine < fx.GetLine() && fx.GetLine() < gotoLine { debug.Println("---implicit pattern found!") - - // fill fx - captures := make([]*NameExpr, 0) - for _, name := range fx.GetExternNames() { - vp := fx.GetPathForName(store, name) - if vp.Depth == 0 { // skip uverse - continue - } - vp.Depth -= 1 // from the perspective of funcLit block - nx := &NameExpr{ - Name: name, - Path: vp, - } - captures = append(captures, nx) - } + // prepare captured nxs + captures := fx.GetCapturedNxs(store) // find target nx lvBox := &LoopValuesBox{} @@ -1849,24 +1840,19 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } } } - // set state + // ref blockNode to lvBox + // ref fx to blockNodes + // record transient values in lvBox when op_exec + // replay in doOpCall if lvBox.isFilled { last.GetStaticBlock().GetBodyStmt().LoopValuesBox = lvBox - loopData = append(loopData, &LoopBlockData{index: lvBox.transient[0].cursor, loopValuesBox: lvBox}) - fx.LoopData = loopData - - //fx.RefLoopBlockNodes = append(fx.RefLoopBlockNodes, last) - //// set index for ref blocks - //for _, tst := range lvBox.transient { - // fx.RefIndices = append(fx.RefIndices, int(tst.nx.Path.Depth)) - //} + fx.TransientLoopData = append(fx.TransientLoopData, &LoopBlockData{index: lvBox.transient[0].cursor, loopValuesBox: lvBox}) } } } } // tag implicit block - last.GetStaticBlock().SetLoopBody(labelLine, n.GetLine()) snare = snare[:0] } case FALLTHROUGH: diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index c028c85141d..a7a532c11db 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -2306,17 +2306,6 @@ func NewBlock(source BlockNode, parent *Block) *Block { } } -func (sb *StaticBlock) SetLoopBody(start, end int) { - debug.Printf("---SetLoopBody, sb.Addr: %p \n", sb) - debug.Printf("---SetLoopBody, bs: %v, bs.Addr: %p \n", sb.bodyStmt, &sb.bodyStmt) - debug.Printf("---SetLoopBody, start: %d, end: %d \n", start, end) - if sb.bodyStmt.loopBody == nil { - debug.Println("---loopBody is nil, set value") - sb.bodyStmt.loopBody = &LoopBody{isLoop: true, start: start, end: end} - } - debug.Printf("---SetLoopBody, bs: %v, bs.Addr: %p \n", sb.bodyStmt, &sb.bodyStmt) -} - func (b *Block) UpdateValue(index int, tv TypedValue) { debug.Printf("---UpdateValue, index: %d \n", index) debug.Printf("---UpdateValue, tv: %v \n", tv) @@ -2426,48 +2415,6 @@ func (b *Block) GetPointerTo(store Store, path ValuePath) PointerValue { return b.GetPointerToInt(store, int(path.Index)) } -// find nearest block is loop and contains name of n -func findLoopBlockWithPath(store Store, b *Block, nx *NameExpr, fLine int) (*Block, bool, uint8) { - debug.Printf("---findLoopBlockWithPath, b: %v \n", b) - debug.Printf("---findLoopBlockWithPath, b.bs: %v \n", b.bodyStmt) - debug.Printf("---findLoopBlockWithPath, b.Source: %v \n", b.Source) - debug.Printf("---findLoopBlockWithPath, b.Source.bs: %v \n", b.Source.GetStaticBlock().bodyStmt) - var gen uint8 = 1 - for i := uint8(1); i < nx.Path.Depth; i++ { // find target block at certain depth - b = b.GetParent(store) - gen++ - } - - debug.Printf("---findLoopBlockWithPath, fLine: %v \n", fLine) - debug.Printf("---findLoopBlockWithPath, loopBody: %v \n", b.Source.GetStaticBlock().bodyStmt.loopBody) - //debug.Printf("---findLoopBlockWithPath, start: %v \n", b.Source.GetStaticBlock().bodyStmt.loopBody.start) - //debug.Printf("---findLoopBlockWithPath, end: %v \n", b.Source.GetStaticBlock().bodyStmt.loopBody.end) - - var isImplicitLoop bool - // tmp, if static not nil, means some work done in preprocess, using static - // TODO: all staff in preprocess - if b.Source.GetStaticBlock().bodyStmt.loopBody != nil { - if b.Source.GetStaticBlock().bodyStmt.loopBody.isLoop { - debug.Println("---is potential loop") - if b.Source.GetStaticBlock().bodyStmt.loopBody.start < fLine && fLine < b.Source.GetStaticBlock().bodyStmt.loopBody.end { - debug.Println("---is implicit loop") - isImplicitLoop = true - } - } - } - - if b.GetBodyStmt().loopBody.isLoop || isImplicitLoop { // is loop - //if b.GetBodyStmt().isLoop || isImplicitLoop { // is loop - names := b.GetSource(store).GetBlockNames() - for _, name := range names { - if nx.Name == name { // find n in this block - return b, true, gen - } - } - } - return nil, false, gen -} - // Result is used has lhs for any assignments to "_". func (b *Block) GetBlankRef() *TypedValue { return &b.Blank diff --git a/gnovm/tests/debug/defer.gno b/gnovm/tests/debug/defer.gno new file mode 100644 index 00000000000..de3ef60359e --- /dev/null +++ b/gnovm/tests/debug/defer.gno @@ -0,0 +1,31 @@ +package main + +func main() { + var fns []func() int + + println("start for loop") + for i := 0; i < 2; i++ { + defer func() { + println("defer") + for _, fn := range fns { + println(fn()) + } + }() + x := i + f := func() int { + return x + } + fns = append(fns, f) + } + println("end for loop") +} + +// Output: +// start for loop +// end +// defer +// 0 +// 1 +// defer +// 1 +// 1 From f39c80ec44afdec2d926c8260f5f85745cbd18ce Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Mon, 26 Feb 2024 00:22:10 +0800 Subject: [PATCH 31/32] comment --- gnovm/tests/debug/defer.gno | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/gnovm/tests/debug/defer.gno b/gnovm/tests/debug/defer.gno index de3ef60359e..219888548ac 100644 --- a/gnovm/tests/debug/defer.gno +++ b/gnovm/tests/debug/defer.gno @@ -1,20 +1,30 @@ package main +// explain: +// in the 1st loop, fns has a fn capture x with value 0, +// and a defer func is placed with refs to fns(it's not captured in face), +// in the 2nd loop, fn captured x with value 1, and fn appended to fns. +// so here, the first defer will print, 0 , 1. +// still in the 2nd loop, a second defer is placed, with fn[0] and fn[1] +// both capture the x in context, whose value is 1, so the second defer +// will print 1, 1 func main() { var fns []func() int println("start for loop") for i := 0; i < 2; i++ { - defer func() { + defer func() { // possible resource leak? println("defer") for _, fn := range fns { println(fn()) } }() + x := i f := func() int { return x } + fns = append(fns, f) } println("end for loop") @@ -22,7 +32,7 @@ func main() { // Output: // start for loop -// end +// end for loop // defer // 0 // 1 From dc615117ae827cbf09637778ba93b6992708ca41 Mon Sep 17 00:00:00 2001 From: ltzMaxwell Date: Mon, 26 Feb 2024 22:18:56 +0800 Subject: [PATCH 32/32] goto, break --- gnovm/pkg/gnolang/nodes.go | 46 +++++++++---------- gnovm/pkg/gnolang/op_exec.go | 18 +++++--- gnovm/pkg/gnolang/op_expressions.go | 2 +- gnovm/pkg/gnolang/preprocess.go | 16 +++---- gnovm/pkg/gnolang/values.go | 15 ++++++ gnovm/tests/debug/{3.gno => 3_break.gno} | 5 +- gnovm/tests/debug/3_continue.gno | 26 +++++++++++ gnovm/tests/debug/3_goto.gno | 27 +++++++++++ gnovm/tests/debug/5.gno | 33 ------------- .../tests/{debug/1.gno => debug2/1a_goto.gno} | 5 +- gnovm/tests/debug2/1b.gno | 6 +++ gnovm/tests/debug2/1b_goto.gno | 36 +++++++++++++++ gnovm/tests/debug2/declare.gno | 8 ++++ gnovm/tests/{debug => debug2}/defer.gno | 0 gnovm/tests/debug2/return.gno | 9 ++++ gnovm/tests/debug2/shadow.gno | 16 +++++++ 16 files changed, 192 insertions(+), 76 deletions(-) rename gnovm/tests/debug/{3.gno => 3_break.gno} (91%) create mode 100644 gnovm/tests/debug/3_continue.gno create mode 100644 gnovm/tests/debug/3_goto.gno delete mode 100644 gnovm/tests/debug/5.gno rename gnovm/tests/{debug/1.gno => debug2/1a_goto.gno} (85%) create mode 100644 gnovm/tests/debug2/1b_goto.gno create mode 100644 gnovm/tests/debug2/declare.gno rename gnovm/tests/{debug => debug2}/defer.gno (100%) create mode 100644 gnovm/tests/debug2/return.gno create mode 100644 gnovm/tests/debug2/shadow.gno diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index 19949d31db6..aa683a22cef 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -890,7 +890,7 @@ type SwitchClauseStmt struct { // this assumes that a goto stmt to label ahead // forms an implicit loop. // if an funcLitExpr embeded, do capture staff. -type ImplicitLoopBlock struct { +type LoopBlockAttr struct { isLoop bool start int // line of label start end int // line of goto stmt @@ -899,28 +899,28 @@ type ImplicitLoopBlock struct { // NOTE: embedded in Block. type bodyStmt struct { Attributes - Body // for non-loop stmts - BodyLen int // for for-continue - NextBodyIndex int // init:-2, cond/elem:-1, body:0..., post:n - NumOps int // number of Ops, for goto - NumValues int // number of Values, for goto - NumExprs int // number of Exprs, for goto - NumStmts int // number of Stmts, for goto - Cond Expr // for ForStmt - Post Stmt // for ForStmt - LoopValuesBox *LoopValuesBox // a series of transient values of captured var generated as the iteration goes on - isLoop bool - implicitLoopBlock *ImplicitLoopBlock - Active Stmt // for PopStmt() - Key Expr // for RangeStmt - Value Expr // for RangeStmt - Op Word // for RangeStmt - ListLen int // for RangeStmt only - ListIndex int // for RangeStmt only - NextItem *MapListItem // fpr RangeStmt w/ maps only - StrLen int // for RangeStmt w/ strings only - StrIndex int // for RangeStmt w/ strings only - NextRune rune // for RangeStmt w/ strings only + Body // for non-loop stmts + BodyLen int // for for-continue + NextBodyIndex int // init:-2, cond/elem:-1, body:0..., post:n + NumOps int // number of Ops, for goto + NumValues int // number of Values, for goto + NumExprs int // number of Exprs, for goto + NumStmts int // number of Stmts, for goto + Cond Expr // for ForStmt + Post Stmt // for ForStmt + LoopValuesBox *LoopValuesBox // a series of transient values of captured var generated as the iteration goes on + isLoop bool + loopBlockAttr *LoopBlockAttr + Active Stmt // for PopStmt() + Key Expr // for RangeStmt + Value Expr // for RangeStmt + Op Word // for RangeStmt + ListLen int // for RangeStmt only + ListIndex int // for RangeStmt only + NextItem *MapListItem // fpr RangeStmt w/ maps only + StrLen int // for RangeStmt w/ strings only + StrIndex int // for RangeStmt w/ strings only + NextRune rune // for RangeStmt w/ strings only } func (x *bodyStmt) PopActiveStmt() (as Stmt) { diff --git a/gnovm/pkg/gnolang/op_exec.go b/gnovm/pkg/gnolang/op_exec.go index e3b80aff2d0..7ccf9edf798 100644 --- a/gnovm/pkg/gnolang/op_exec.go +++ b/gnovm/pkg/gnolang/op_exec.go @@ -44,7 +44,7 @@ SelectStmt -> */ func updateCapturedValue(m *Machine, lb *Block) { - debug.Println("---op_exec, updateCapturedValue") + debug.Printf("---op_exec, updateCapturedValue, lb: %v \n", lb) lb.GetBodyStmt().LoopValuesBox = lb.Source.GetStaticBlock().GetBodyStmt().LoopValuesBox bs := lb.GetBodyStmt() @@ -59,7 +59,7 @@ func updateCapturedValue(m *Machine, lb *Block) { var isSeal bool for i, tt := range bs.LoopValuesBox.transient { nvp := lb.Source.GetPathForName(m.Store, tt.nx.Name) - ptr := m.LastBlock().GetPointerTo(m.Store, nvp) + ptr := lb.GetPointerTo(m.Store, nvp) tv := ptr.Deref() debug.Printf("---transient value for %s is: %v \n", tt.nx.Name, tv) // update context use previously recorded value @@ -540,7 +540,7 @@ EXEC_SWITCH: m.PushOp(OpEval) case *ForStmt: debug.Printf("---ForStmt: %v \n", cs) - //debug.Printf("---ForStmt.sb.bs: %v \n", cs.GetStaticBlock().bodyStmt.implicitLoopBlock) + //debug.Printf("---ForStmt.sb.bs: %v \n", cs.GetStaticBlock().bodyStmt.loopBlockAttr) m.PushFrameBasic(cs) // do the copy from preprocess block to runtime block with NewBlock b := m.Alloc.NewBlock(cs, m.LastBlock()) // runtime block @@ -550,7 +550,7 @@ EXEC_SWITCH: NextBodyIndex: -2, Cond: cs.Cond, Post: cs.Post, - //implicitLoopBlock: cs.GetStaticBlock().bodyStmt.implicitLoopBlock, // copy from preprocess block + loopBlockAttr: cs.GetStaticBlock().bodyStmt.loopBlockAttr, // copy from preprocess block } m.PushBlock(b) m.PushOp(OpForLoop) @@ -638,7 +638,7 @@ EXEC_SWITCH: Key: cs.Key, Value: cs.Value, Op: cs.Op, - //implicitLoopBlock: cs.GetStaticBlock().bodyStmt.implicitLoopBlock, + loopBlockAttr: cs.GetStaticBlock().bodyStmt.loopBlockAttr, } m.PushBlock(b) // TODO: replace with "cs.Op". @@ -675,6 +675,8 @@ EXEC_SWITCH: debug.Printf("---BranchStmt, cs: %v \n", cs) switch cs.Op { case BREAK: + last := m.LastBlock() + updateCapturedValue(m, last.GetNearestEnclosingLoopBlock(m.Store)) // Pop frames until for/range // statement (which matches // label, if labeled), and reset. @@ -692,6 +694,7 @@ EXEC_SWITCH: m.PopFrame() } } + case CONTINUE: // TODO document for { @@ -717,6 +720,9 @@ EXEC_SWITCH: } case GOTO: debug.Println("---GOTO ", cs.String()) + lb := m.LastBlock() + updateCapturedValue(m, lb.GetNearestEnclosingLoopBlock(m.Store)) + for i := uint8(0); i < cs.Depth; i++ { m.PopBlock() } @@ -735,7 +741,7 @@ EXEC_SWITCH: bs.NextBodyIndex = cs.BodyIndex //bs.isLoop = true bs.Active = bs.Body[cs.BodyIndex] // prefill - updateCapturedValue(m, last) + //updateCapturedValue(m, last) case FALLTHROUGH: debug.Println("---FALLTHROUGH") diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index 7a36e7539b8..aa69cdc7796 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -702,7 +702,7 @@ func (m *Machine) doOpFuncLit() { transientLoopData = append(transientLoopData, &LoopBlockData{index: cursor, loopValuesBox: lvBox}) } if debug { - for i, lbd := range x.TransientLoopData { + for i, lbd := range transientLoopData { fmt.Printf("========doOpFuncLit, TransientLoopData[%d] is: %s, index: %d \n", i, lbd.loopValuesBox, lbd.index) } } diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 0abb6b9c006..63792896f2d 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -129,8 +129,8 @@ func getBlockNodeAt(stack []BlockNode, n int) BlockNode { // the target block node will ref to proper nxs wrapped in a container // XXX, Note that in preprocess handling, the ref is stored on bn.StaticBlock.Block.BodyStmt // can bn.StaticBlock.Block be reused in runtime rather than a new one? -func findLoopStaticBlockAndPath(stack []BlockNode, nx *NameExpr) (BlockNode, bool, uint8) { - debug.Printf("---findLoopStaticBlockAndPath, nx: %v \n", nx) +func findLoopBlockNodeAndPath(stack []BlockNode, nx *NameExpr) (BlockNode, bool, uint8) { + debug.Printf("---findLoopBlockNodeAndPath, nx: %v \n", nx) dumpStack(stack) var gen uint8 = 1 // depth of blockNode var bn BlockNode @@ -145,12 +145,12 @@ func findLoopStaticBlockAndPath(stack []BlockNode, nx *NameExpr) (BlockNode, boo debug.Printf("---got target bn: %v \n", bn) debug.Printf("---got target bn type: %T \n", bn) - debug.Printf("---got target implicitLoopBlock: %v \n", bn.GetStaticBlock().bodyStmt.implicitLoopBlock) + debug.Printf("---got target loopBlockAttr: %v \n", bn.GetStaticBlock().bodyStmt.loopBlockAttr) // tmp, if static not nil, means some work done in preprocess, using static - if bn.GetStaticBlock().bodyStmt.implicitLoopBlock != nil { + if bn.GetStaticBlock().bodyStmt.loopBlockAttr != nil { // find name - if bn.GetStaticBlock().bodyStmt.implicitLoopBlock.isLoop { + if bn.GetStaticBlock().bodyStmt.loopBlockAttr.isLoop { names := bn.GetBlockNames() for _, name := range names { if nx.Name == name { // find n in this block @@ -350,7 +350,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { case *ForStmt: debug.Println("---for stmt") pushInitBlock(n, &last, &stack) - n.bodyStmt.implicitLoopBlock = &ImplicitLoopBlock{isLoop: true} + n.bodyStmt.loopBlockAttr = &LoopBlockAttr{isLoop: true} debug.Printf("---sb of ForStmt: %v \n", n.GetStaticBlock()) n.GetStaticBlock().dump() @@ -430,7 +430,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } } } - n.bodyStmt.implicitLoopBlock = &ImplicitLoopBlock{isLoop: true} + n.bodyStmt.loopBlockAttr = &LoopBlockAttr{isLoop: true} // TRANS_BLOCK ----------------------- case *FuncLitExpr: @@ -858,7 +858,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { for _, nx := range captures { debug.Printf("---loop captures, nx name: %v, nx path: %v \n", nx.Name, nx.Path) // start search block, in the order of small depth to big depth - loopBlockNode, isLoopBlock, gen = findLoopStaticBlockAndPath(stack, nx) + loopBlockNode, isLoopBlock, gen = findLoopBlockNodeAndPath(stack, nx) debug.Printf("isLoopBlock: %v \n", isLoopBlock) debug.Printf("loopBlockNode: %v \n", loopBlockNode) debug.Printf("depth: %v \n", gen) diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index a7a532c11db..0e62649ae19 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -2390,6 +2390,21 @@ func (b *Block) GetBlockWithDepth(store Store, path ValuePath) *Block { return b } +func (b *Block) GetNearestEnclosingLoopBlock(store Store) *Block { + debug.Printf("---GetNearestEnclosingLoopBlock, b: %v \n", b) + b = b.GetParent(store) + debug.Printf("--- b: %v \n", b) + debug.Printf("--- b.bodyStmt: %v \n", b.bodyStmt) + debug.Printf("--- b.bodyStmt: %v \n", b.bodyStmt) + for b != nil { + if b.bodyStmt.loopBlockAttr.isLoop { + return b + } + b = b.GetParent(store) + } + return nil +} + func (b *Block) GetPointerTo(store Store, path ValuePath) PointerValue { if path.IsBlockBlankPath() { if debug { diff --git a/gnovm/tests/debug/3.gno b/gnovm/tests/debug/3_break.gno similarity index 91% rename from gnovm/tests/debug/3.gno rename to gnovm/tests/debug/3_break.gno index 4fffeb4fea0..0a2a2a5831f 100644 --- a/gnovm/tests/debug/3.gno +++ b/gnovm/tests/debug/3_break.gno @@ -11,6 +11,9 @@ func main() { } fns = append(fns, f) x += 1 + if i == 2 { + break + } } for _, fn := range fns { println(fn()) @@ -21,5 +24,3 @@ func main() { // 1 // 2 // 3 -// 4 -// 5 diff --git a/gnovm/tests/debug/3_continue.gno b/gnovm/tests/debug/3_continue.gno new file mode 100644 index 00000000000..d0b8b48e7db --- /dev/null +++ b/gnovm/tests/debug/3_continue.gno @@ -0,0 +1,26 @@ +package main + +// this is a fix, intuitive, and same with Go +func main() { + var fns []func() int + + for i := 0; i < 5; i++ { + x := i + if i <= 2 { + continue + } + + f := func() int { + return x + } + fns = append(fns, f) + x += 1 + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 4 +// 5 diff --git a/gnovm/tests/debug/3_goto.gno b/gnovm/tests/debug/3_goto.gno new file mode 100644 index 00000000000..3f8b29bfd66 --- /dev/null +++ b/gnovm/tests/debug/3_goto.gno @@ -0,0 +1,27 @@ +package main + +// this is a fix, intuitive, and same with Go +func main() { + var fns []func() int + + for i := 0; i < 5; i++ { + x := i + f := func() int { + return x + } + fns = append(fns, f) + x += 1 + if i == 2 { + goto END + } + } +END: + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 1 +// 2 +// 3 diff --git a/gnovm/tests/debug/5.gno b/gnovm/tests/debug/5.gno deleted file mode 100644 index d0989412170..00000000000 --- a/gnovm/tests/debug/5.gno +++ /dev/null @@ -1,33 +0,0 @@ -package main - -import "fmt" - -func main() { - var counter int - var fns []func() - - defer func() { - for _, f := range fns { - f() - } - }() - -LABEL_1: - if counter >= 2 { - goto LABEL_2 // Jump to LABEL_2 once counter is 2 or more - } - - // capture - fns = append(fns, func() { println(counter) }) - counter++ - goto LABEL_1 // Go back to the beginning of the loop - -LABEL_2: - fmt.Println("Exited loop with counter at:", counter) - // Continue with the rest of the program -} - -// Output: -// Exited loop with counter at: 2 -// 1 -// 2 diff --git a/gnovm/tests/debug/1.gno b/gnovm/tests/debug2/1a_goto.gno similarity index 85% rename from gnovm/tests/debug/1.gno rename to gnovm/tests/debug2/1a_goto.gno index 47bf386f606..903e611fc95 100644 --- a/gnovm/tests/debug/1.gno +++ b/gnovm/tests/debug2/1a_goto.gno @@ -14,13 +14,12 @@ LABEL_1: if counter == 2 { return } - x := y - f = append(f, func() { println(x) }) + f = append(f, func() { println(y) }) y++ counter++ goto LABEL_1 // this is the edge condition, break? continue? } // Output: -// 0 // 1 +// 2 diff --git a/gnovm/tests/debug2/1b.gno b/gnovm/tests/debug2/1b.gno index 3aee77fa232..cc934ed4bf1 100644 --- a/gnovm/tests/debug2/1b.gno +++ b/gnovm/tests/debug2/1b.gno @@ -26,3 +26,9 @@ LABEL_2: } // Output: +// end +// 0 +// 1 +// 2 +// 3 +// 4 diff --git a/gnovm/tests/debug2/1b_goto.gno b/gnovm/tests/debug2/1b_goto.gno new file mode 100644 index 00000000000..8e309758aa6 --- /dev/null +++ b/gnovm/tests/debug2/1b_goto.gno @@ -0,0 +1,36 @@ +package main + +func main() { + var y, counter int + + println(&y) + + var f []func() *int + + defer func() { + for _, ff := range f { + println(ff()) + println(*ff()) + } + }() + + // this is actually a implicit for loop +LABEL_1: + if counter == 2 { + return + } + f = append(f, func() *int { + //x := &y + return &y + }) + y++ + counter++ + goto LABEL_1 // this is the edge condition, break? continue? +} + +// Output: +// &0x14000283f80.(*int) +// &0x14000283f80.(*int) +// 1 +// &0x14000283f80.(*int) +// 2 diff --git a/gnovm/tests/debug2/declare.gno b/gnovm/tests/debug2/declare.gno new file mode 100644 index 00000000000..761c3029a16 --- /dev/null +++ b/gnovm/tests/debug2/declare.gno @@ -0,0 +1,8 @@ +package main + +func main() { + println(a) + a := 0 +} + +// Output: diff --git a/gnovm/tests/debug/defer.gno b/gnovm/tests/debug2/defer.gno similarity index 100% rename from gnovm/tests/debug/defer.gno rename to gnovm/tests/debug2/defer.gno diff --git a/gnovm/tests/debug2/return.gno b/gnovm/tests/debug2/return.gno new file mode 100644 index 00000000000..6435588e080 --- /dev/null +++ b/gnovm/tests/debug2/return.gno @@ -0,0 +1,9 @@ +package main + +func main() { + a := 0 + return + println(a) +} + +// Output: diff --git a/gnovm/tests/debug2/shadow.gno b/gnovm/tests/debug2/shadow.gno new file mode 100644 index 00000000000..1b2d40eacdf --- /dev/null +++ b/gnovm/tests/debug2/shadow.gno @@ -0,0 +1,16 @@ +package main + +func main() { + var x int + + if true { + x += 2 + println("inner x: ", x) + } + + println("outer x:", x) +} + +// Output: +// inner x: 2 +// outer x: 2