Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(gnovm): solution for correctly capturing loop externs #1818

Closed
wants to merge 57 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
a0e7830
initial commit
jaekwon Mar 15, 2024
fe362a1
fix name and comments for isFuncLitInLoop
jaekwon Mar 16, 2024
2eab0d3
...
jaekwon Mar 16, 2024
3c5603c
reuse isNodeInLoop
jaekwon Mar 16, 2024
ccb5f27
...
jaekwon Mar 16, 2024
107252d
also check externs against outer loops
jaekwon Mar 16, 2024
d28f7b6
fix build errors
jaekwon Mar 16, 2024
fba2746
move pop block for block nodes upon leave to defer stmt
jaekwon Mar 16, 2024
6ada528
fix preprocess logic to get proper valuepath, make test pass
ltzmaxwell Mar 19, 2024
8c0207d
add comment
ltzmaxwell Mar 20, 2024
eea6789
initial flow, make for pass
ltzmaxwell Mar 21, 2024
3ebccfc
conditional loop var logic
ltzmaxwell Mar 21, 2024
c4fd01f
make range work
ltzmaxwell Mar 22, 2024
f1a8711
clean
ltzmaxwell Mar 25, 2024
2a6508c
add todo
ltzmaxwell Mar 25, 2024
93aff1e
add todo and new test
ltzmaxwell Mar 25, 2024
2e7eca2
clean
ltzmaxwell Mar 25, 2024
f6406d8
fix goto
ltzmaxwell Mar 25, 2024
483a10c
more goto case
ltzmaxwell Mar 25, 2024
c3c4a20
more goto, test pass
ltzmaxwell Mar 26, 2024
d7e4b2a
match parent block, only reProcess once
ltzmaxwell Mar 27, 2024
94ebf4f
tidy
ltzmaxwell Mar 28, 2024
f859825
clear old label, add new, make complex goto work
ltzmaxwell Mar 29, 2024
a399b3f
fixup
ltzmaxwell Mar 29, 2024
1421c43
fixup
ltzmaxwell Mar 29, 2024
4534c1c
file level multi node
ltzmaxwell Mar 30, 2024
3093a85
test pass
ltzmaxwell Mar 31, 2024
00e96ea
fix embeded in for loop
ltzmaxwell Mar 31, 2024
9b17edb
unify transform logic for all loops
ltzmaxwell Mar 31, 2024
31e5fc0
all reProcess in func level, test pass
ltzmaxwell Apr 1, 2024
2ba78bb
more test & clean
ltzmaxwell Apr 2, 2024
d6765c2
clean
ltzmaxwell Apr 2, 2024
433a12c
clean and refactor transformer
ltzmaxwell Apr 3, 2024
47248af
set node location for new wrapped node
ltzmaxwell Apr 22, 2024
47a7aee
fixup
ltzmaxwell Apr 24, 2024
5a093cb
fixup
ltzmaxwell Apr 24, 2024
01320e2
termination
ltzmaxwell Apr 24, 2024
3b067fa
comment
ltzmaxwell Apr 24, 2024
1cbd823
clean debug infos
ltzmaxwell Apr 24, 2024
fbf0f58
clean
ltzmaxwell Apr 24, 2024
2f67b74
clean test files
ltzmaxwell Apr 25, 2024
09f7569
reorder test files
ltzmaxwell Apr 25, 2024
e4e33e6
clean
ltzmaxwell Apr 25, 2024
87fc8d6
fixup
ltzmaxwell Apr 25, 2024
4790110
comment
ltzmaxwell Apr 25, 2024
960a625
clear
ltzmaxwell Apr 27, 2024
cd59316
clean test files
ltzmaxwell Apr 29, 2024
ad9c0f7
merge master
ltzmaxwell May 15, 2024
72de9da
fix lint
ltzmaxwell May 15, 2024
63abe8c
clear
ltzmaxwell May 15, 2024
c57498d
rm panic
ltzmaxwell May 16, 2024
0830d7d
improve residual handling
ltzmaxwell Jun 11, 2024
46367e1
tune
ltzmaxwell Jun 11, 2024
25d66a0
generalize preprocess state
ltzmaxwell Jun 11, 2024
26c69a1
helper for state reset
ltzmaxwell Jun 11, 2024
5b0e3a8
comments
ltzmaxwell Jun 13, 2024
72c7c9a
add more test
ltzmaxwell Jun 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions gnovm/pkg/gnolang/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,12 @@ func Return(results ...Expr) *ReturnStmt {
}
}

func BlockS(body Body) *BlockStmt {
return &BlockStmt{
Body: body,
}
}

func Continue(label interface{}) *BranchStmt {
return &BranchStmt{
Op: CONTINUE,
Expand Down Expand Up @@ -795,6 +801,17 @@ func SIf(cond bool, then_, else_ Stmt) Stmt {
}
}

func InjectStmts(lvs *CapturedLoopVariables) []Stmt {
stmts := []Stmt{}
for _, lv := range lvs.loopVars {
lhs := Nx(lv)
rhs := Nx(lv)
as := A(lhs, ":=", rhs)
stmts = append(stmts, as)
}
return stmts
}

// ----------------------------------------
// chop functions

Expand Down
10 changes: 5 additions & 5 deletions gnovm/pkg/gnolang/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ func (m *Machine) PreprocessAllFilesAndSaveBlockNodes() {
PredefineFileSet(m.Store, pn, fset)
for _, fn := range fset.Files {
// Save Types to m.Store (while preprocessing).
fn = Preprocess(m.Store, pn, fn).(*FileNode)
fn = Preprocess(m.Store, pn, fn, PHASE_VARLOOP).(*FileNode)
// Save BlockNodes to m.Store.
SaveBlockNodes(m.Store, fn)
}
Expand Down Expand Up @@ -539,7 +539,7 @@ func (m *Machine) runFiles(fns ...*FileNode) {
// runtime package value via PrepareNewValues. Then,
// non-constant var declarations and file-level imports
// are re-set in runDeclaration(,true).
fn = Preprocess(m.Store, pn, fn).(*FileNode)
fn = Preprocess(m.Store, pn, fn, PHASE_VARLOOP).(*FileNode)
if debug {
debug.Printf("PREPROCESSED FILE: %v\n", fn)
}
Expand Down Expand Up @@ -726,7 +726,7 @@ func (m *Machine) Eval(x Expr) []TypedValue {
// x already creates its own scope.
}
// Preprocess x.
x = Preprocess(m.Store, last, x).(Expr)
x = Preprocess(m.Store, last, x, PHASE_CORE).(Expr)
// Evaluate x.
start := m.NumValues
m.PushOp(OpHalt)
Expand Down Expand Up @@ -798,7 +798,7 @@ func (m *Machine) EvalStaticTypeOf(last BlockNode, x Expr) Type {

func (m *Machine) RunStatement(s Stmt) {
sn := m.LastBlock().GetSource(m.Store)
s = Preprocess(m.Store, sn, s).(Stmt)
s = Preprocess(m.Store, sn, s, PHASE_CORE).(Stmt)
m.PushOp(OpHalt)
m.PushStmt(s)
m.PushOp(OpExec)
Expand All @@ -815,7 +815,7 @@ func (m *Machine) RunDeclaration(d Decl) {
// Preprocess input using package block. There should only
// be one block right now, and it's a *PackageNode.
pn := m.LastBlock().GetSource(m.Store).(*PackageNode)
d = Preprocess(m.Store, pn, d).(Decl)
d = Preprocess(m.Store, pn, d, PHASE_CORE).(Decl)
// do not SaveBlockNodes(m.Store, d).
pn.PrepareNewValues(m.Package)
m.runDeclaration(d)
Expand Down
64 changes: 63 additions & 1 deletion gnovm/pkg/gnolang/nodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,7 @@ type ForStmt struct {
Cond Expr // condition; or nil
Post Stmt // post iteration (simple) statement; or nil
Body
LoopVars *CapturedLoopVariables
}

type GoStmt struct {
Expand Down Expand Up @@ -817,6 +818,7 @@ type RangeStmt struct {
IsMap bool // if X is map type
IsString bool // if X is string type
IsArrayPtr bool // if X is array-pointer type
LoopVars *CapturedLoopVariables
}

type ReturnStmt struct {
Expand Down Expand Up @@ -870,6 +872,12 @@ type SwitchClauseStmt struct {
// ----------------------------------------
// bodyStmt (persistent)

// loopVar of for/range-loop can be captured or not,
// recored the captured ones.
type CapturedLoopVariables struct {
loopVars []Name
}

// NOTE: embedded in Block.
type bodyStmt struct {
Attributes
Expand Down Expand Up @@ -1403,7 +1411,7 @@ func (x *PackageNode) DefineNative(n Name, ps, rs FieldTypeExprs, native func(*M
}

fd := FuncD(n, ps, rs, nil)
fd = Preprocess(nil, x, fd).(*FuncDecl)
fd = Preprocess(nil, x, fd, PHASE_CORE).(*FuncDecl)
ft := evalStaticType(nil, x, &fd.Type).(*FuncType)
if debug {
if ft == nil {
Expand Down Expand Up @@ -1459,6 +1467,7 @@ type BlockNode interface {
GetNumNames() uint16
GetParentNode(Store) BlockNode
GetPathForName(Store, Name) ValuePath
GetLoopNodeForName(Store, Name) BlockNode
GetIsConst(Store, Name) bool
GetLocalIndex(Name) (uint16, bool)
GetValueRef(Store, Name) *TypedValue
Expand Down Expand Up @@ -1500,6 +1509,19 @@ func (sb *StaticBlock) revertToOld() {
sb.oldValues = nil
}

func (sb *StaticBlock) reset() {
sb.Block = Block{
Source: nil,
Values: nil,
Parent: nil,
}
sb.NumNames = 0
sb.Names = nil
sb.Consts = nil
sb.Externs = nil
return
}

// Implements BlockNode
func (sb *StaticBlock) InitStaticBlock(source BlockNode, parent BlockNode) {
if sb.Names != nil || sb.Block.Source != nil {
Expand Down Expand Up @@ -1557,6 +1579,7 @@ func (sb *StaticBlock) GetBlockNames() (ns []Name) {
}

// Implements BlockNode.
// NOTE: Extern names may also be local, if declared later.
func (sb *StaticBlock) GetExternNames() (ns []Name) {
return sb.Externs // copy?
}
Expand Down Expand Up @@ -1598,6 +1621,8 @@ func (sb *StaticBlock) GetPathForName(store Store, n Name) ValuePath {
}
// Register as extern.
// NOTE: uverse names are externs too.
// NOTE: if a name is later declared in this block later, it is both an
// extern name with depth > 1, as well as local name with depth == 1.
if !isFile(sb.GetSource(store)) {
sb.GetStaticBlock().addExternName(n)
}
Expand Down Expand Up @@ -1626,6 +1651,43 @@ func (sb *StaticBlock) GetPathForName(store Store, n Name) ValuePath {
panic(fmt.Sprintf("name %s not declared", n))
}

// Get loop blockNode where a name is declared, return nil if not exist.
func (sb *StaticBlock) GetLoopNodeForName(store Store, n Name) BlockNode {
if n == "_" {
return nil
}
parent := sb.GetParentNode(store)
path := parent.GetPathForName(store, n)
if path.Type != VPBlock {
return nil
}

// NOTE: path.Depth == 1 means it's in bn.
bn := sb.GetSource(store)
for i := 1; i <= int(path.Depth); i++ {
bn = bn.GetParentNode(store)
}

if !isLoopNode(store, bn) {
return nil
}

return bn
}

func isLoopNode(store Store, bn BlockNode) bool {
if bn != nil {
switch bn.(type) {
case *ForStmt:
return true
case *RangeStmt:
return true
default:
}
}
return false
}

// Returns whether a name defined here in in ancestry is a const.
// This is not the same as whether a name's static type is
// untyped -- as in c := a == b, a name may be an untyped non-const.
Expand Down
Loading