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): handle loop variables #2429

Merged
merged 67 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from 64 commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
ae8c44e
...
jaekwon Jun 23, 2024
3c46e27
fix more tests
jaekwon Jun 23, 2024
f4306ed
fix tests
jaekwon Jun 23, 2024
5cc2494
...
jaekwon Jun 24, 2024
31b2263
update golden tests
jaekwon Jun 24, 2024
f1d7c3e
...
jaekwon Jun 24, 2024
492f103
...
jaekwon Jun 24, 2024
31bbcb6
remove predefine call; update comments
jaekwon Jun 26, 2024
53c47e5
first commit
jaekwon Jun 25, 2024
ffb2e76
findGotoLoopStmts, set attrs
ltzmaxwell Jun 25, 2024
e23141c
findLoopEscapedNames
ltzmaxwell Jun 25, 2024
883cc6a
fixup
ltzmaxwell Jun 25, 2024
2931579
comment
ltzmaxwell Jun 25, 2024
6b440e0
inner transcribe for annotating goto loop exprs
jaekwon Jun 26, 2024
e9e4454
...
jaekwon Jun 26, 2024
ea07aee
NameExprTypeDefine -> NameExprTypeLoopDefine
jaekwon Jun 29, 2024
59a51d3
...
jaekwon Jun 29, 2024
c771f9c
add 2 phases to find actual uasge, and demote if necessary
jaekwon Jun 30, 2024
b49630e
code cleanup w/ doRecover; pushInitBlock in findLoop*; etc
jaekwon Jun 30, 2024
c3b3edf
...
jaekwon Jun 30, 2024
e42a561
make compile
jaekwon Jun 30, 2024
3892c47
fix tests
jaekwon Jun 30, 2024
ae9158e
fix test
jaekwon Jun 30, 2024
d552d74
Loop -> Heap
jaekwon Jun 30, 2024
30386a8
add a failing test; fix, don't mark as heap defined until label
jaekwon Jul 1, 2024
a6d81ed
fix tests
jaekwon Jul 1, 2024
f8a0769
add two more tests
jaekwon Jul 1, 2024
ff4224e
trascribe ValueDecl LHS too
jaekwon Jul 1, 2024
7beaf3e
add failing test for var decl
jaekwon Jul 1, 2024
b33736f
don't use line numbers for logic; NameExprTypeDefine format change
jaekwon Jul 1, 2024
def501e
fix gotoloop filter; add more tests
jaekwon Jul 2, 2024
dc05e87
print heap captures from FuncLitExpr
jaekwon Jul 2, 2024
9405775
Add shims for GetPointerToHeapDefine etc
jaekwon Jul 2, 2024
a93e7c6
Add GetPointerToMaybeHeapUse
jaekwon Jul 2, 2024
df9554f
add captures; define ~name
jaekwon Jul 3, 2024
f6b076f
add HeapItemType/Kind
jaekwon Jul 3, 2024
a26d987
addHeapCapture refactor, atomic add
jaekwon Jul 3, 2024
e3ad91f
...
jaekwon Jul 3, 2024
c9eeed2
fix(gnovm): follow up works on loop scope(#2429) (#2440)
ltzmaxwell Jul 25, 2024
b44a4e5
remove unused xxx
ltzmaxwell Jul 26, 2024
1e04c97
merge master
ltzmaxwell Jul 26, 2024
0a5df97
revert support to range loopvar
ltzmaxwell Jul 27, 2024
c98af3c
make debugger test pass, but it should be fixed
ltzmaxwell Jul 27, 2024
f3e7d61
fix debugger
ltzmaxwell Jul 28, 2024
7313720
realm
ltzmaxwell Jul 29, 2024
9b7fe06
fix linter
ltzmaxwell Jul 29, 2024
ba5a1b8
clean
ltzmaxwell Jul 29, 2024
315096b
clean
ltzmaxwell Jul 29, 2024
401154f
fixup
ltzmaxwell Aug 15, 2024
a36641c
use blankIdentifier
ltzmaxwell Aug 15, 2024
8ed0250
merge master
ltzmaxwell Aug 15, 2024
409a92f
format
ltzmaxwell Aug 15, 2024
e5282fa
Update gnovm/pkg/gnolang/nodes.go
ltzmaxwell Sep 12, 2024
6bbfde5
fixup
ltzmaxwell Oct 7, 2024
0d3cdde
Update gnovm/pkg/gnolang/nodes.go
ltzmaxwell Oct 7, 2024
ab465c7
fixup
ltzmaxwell Oct 7, 2024
00730f5
merge master
ltzmaxwell Oct 18, 2024
757479b
fix line
ltzmaxwell Oct 18, 2024
888e3a1
Update gnovm/pkg/gnolang/nodes.go
ltzmaxwell Oct 18, 2024
f75d5c8
Update gnovm/pkg/gnolang/nodes.go
ltzmaxwell Oct 18, 2024
ac85d2b
Update gnovm/pkg/gnolang/nodes.go
ltzmaxwell Oct 18, 2024
d96704a
fixup
ltzmaxwell Oct 18, 2024
607e163
fixup
ltzmaxwell Oct 18, 2024
e4c78b6
fixup
ltzmaxwell Oct 18, 2024
c300486
fixup
ltzmaxwell Oct 21, 2024
798556a
fixup
ltzmaxwell Oct 22, 2024
1cbc993
fixup
ltzmaxwell Oct 22, 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
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

! stdout .+
stderr 'panic: unknown import path net \[recovered\]'
stderr ' panic: gno.land/r/\w{8}/contract.gno:3:1: unknown import path net'
stderr ' panic: gno.land/r/\w{8}/contract.gno:3:8: unknown import path net'

gno test -v --with-native-fallback .

Expand Down
4 changes: 2 additions & 2 deletions gnovm/cmd/gno/testdata/gno_test/unknow_lib.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@

! stdout .+
stderr 'panic: unknown import path foobarbaz \[recovered\]'
stderr ' panic: gno.land/r/\w{8}/contract.gno:3:1: unknown import path foobarbaz'
stderr ' panic: gno.land/r/\w{8}/contract.gno:3:8: unknown import path foobarbaz'

! gno test -v --with-native-fallback .

! stdout .+
stderr 'panic: unknown import path foobarbaz \[recovered\]'
stderr ' panic: gno.land/r/\w{8}/contract.gno:3:1: unknown import path foobarbaz'
stderr ' panic: gno.land/r/\w{8}/contract.gno:3:8: unknown import path foobarbaz'

-- contract.gno --
package contract
Expand Down
14 changes: 9 additions & 5 deletions gnovm/pkg/gnolang/debugger.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,17 +257,21 @@ func debugUpdateLocation(m *Machine) {
for i := nx - 1; i >= 0; i-- {
expr := m.Exprs[i]
if l := expr.GetLine(); l > 0 {
m.Debugger.loc.Line = l
m.Debugger.loc.Column = expr.GetColumn()
if col := expr.GetColumn(); col > 0 {
ltzmaxwell marked this conversation as resolved.
Show resolved Hide resolved
m.Debugger.loc.Line = l
m.Debugger.loc.Column = expr.GetColumn()
}
return
}
}

if len(m.Stmts) > 0 {
if stmt := m.PeekStmt1(); stmt != nil {
if l := stmt.GetLine(); l > 0 {
m.Debugger.loc.Line = l
m.Debugger.loc.Column = stmt.GetColumn()
if col := stmt.GetColumn(); col > 0 {
m.Debugger.loc.Line = l
m.Debugger.loc.Column = stmt.GetColumn()
}
return
}
}
Expand Down Expand Up @@ -648,7 +652,7 @@ func debugEvalExpr(m *Machine, node ast.Node) (tv TypedValue, err error) {
return tv, fmt.Errorf("invalid selector: %s", n.Sel.Name)
}
for _, vp := range tr {
x = x.GetPointerTo(m.Alloc, m.Store, vp).Deref()
x = x.GetPointerToFromTV(m.Alloc, m.Store, vp).Deref()
}
return x, nil
case *ast.IndexExpr:
Expand Down
2 changes: 1 addition & 1 deletion gnovm/pkg/gnolang/debugger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func TestDebug(t *testing.T) {
{in: "p \"xxxx\"\n", out: `("xxxx" string)`},
{in: "si\n", out: "sample.gno:14"},
{in: "s\ns\n", out: `=> 14: var global = "test"`},
{in: "s\n\n", out: "=> 33: num := 5"},
{in: "s\n\n\n", out: "=> 33: num := 5"},
mvertes marked this conversation as resolved.
Show resolved Hide resolved
{in: "foo", out: "command not available: foo"},
{in: "\n\n", out: "dbg> "},
{in: "#\n", out: "dbg> "},
Expand Down
6 changes: 4 additions & 2 deletions gnovm/pkg/gnolang/go2gno.go
Original file line number Diff line number Diff line change
Expand Up @@ -754,11 +754,13 @@ func toDecls(fs *token.FileSet, gd *ast.GenDecl) (ds Decls) {
name := toName(s.Name)
tipe := toExpr(fs, s.Type)
alias := s.Assign != 0
ds = append(ds, &TypeDecl{
td := &TypeDecl{
NameExpr: NameExpr{Name: name},
Type: tipe,
IsAlias: alias,
})
}
setLoc(fs, s.Pos(), td)
ds = append(ds, td)
case *ast.ValueSpec:
if gd.Tok == token.CONST {
var names []NameExpr
Expand Down
9 changes: 5 additions & 4 deletions gnovm/pkg/gnolang/kind_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 14 additions & 16 deletions gnovm/pkg/gnolang/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -699,7 +699,7 @@
}
}
// if dep already in loopfindr, abort.
if hasName(dep, loopfindr) {
if slices.Contains(loopfindr, dep) {
if _, ok := (*depdecl).(*FuncDecl); ok {
// recursive function dependencies
// are OK with func decls.
Expand Down Expand Up @@ -2112,15 +2112,25 @@
func (m *Machine) PopAsPointer(lx Expr) PointerValue {
switch lx := lx.(type) {
case *NameExpr:
lb := m.LastBlock()
return lb.GetPointerTo(m.Store, lx.Path)
switch lx.Type {
case NameExprTypeNormal:
lb := m.LastBlock()
return lb.GetPointerTo(m.Store, lx.Path)
case NameExprTypeHeapUse:
lb := m.LastBlock()
return lb.GetPointerToHeapUse(m.Store, lx.Path)
case NameExprTypeHeapClosure:
panic("should not happen")
default:
panic("unexpected NameExpr in PopAsPointer")

Check warning on line 2125 in gnovm/pkg/gnolang/machine.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/machine.go#L2122-L2125

Added lines #L2122 - L2125 were not covered by tests
}
case *IndexExpr:
iv := m.PopValue()
xv := m.PopValue()
return xv.GetPointerAtIndex(m.Alloc, m.Store, iv)
case *SelectorExpr:
xv := m.PopValue()
return xv.GetPointerTo(m.Alloc, m.Store, lx.Path)
return xv.GetPointerToFromTV(m.Alloc, m.Store, lx.Path)
case *StarExpr:
ptr := m.PopValue().V.(PointerValue)
return ptr
Expand Down Expand Up @@ -2348,15 +2358,3 @@

return builder.String()
}

//----------------------------------------
// utility

func hasName(n Name, ns []Name) bool {
for _, n2 := range ns {
if n == n2 {
return true
}
}
return false
}
120 changes: 85 additions & 35 deletions gnovm/pkg/gnolang/nodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,26 @@
// even after preprocessing. Temporary attributes (e.g. those
// for preprocessing) are stored in .data.

type GnoAttribute string

const (
ATTR_PREPROCESSED GnoAttribute = "ATTR_PREPROCESSED"
ATTR_PREDEFINED GnoAttribute = "ATTR_PREDEFINED"
ATTR_TYPE_VALUE GnoAttribute = "ATTR_TYPE_VALUE"
ATTR_TYPEOF_VALUE GnoAttribute = "ATTR_TYPEOF_VALUE"
ATTR_IOTA GnoAttribute = "ATTR_IOTA"
ATTR_LOCATIONED GnoAttribute = "ATTR_LOCATIONE" // XXX DELETE
ATTR_GOTOLOOP_STMT GnoAttribute = "ATTR_GOTOLOOP_STMT" // XXX delete?
ATTR_LOOP_DEFINES GnoAttribute = "ATTR_LOOP_DEFINES" // []Name defined within loops.
ATTR_LOOP_USES GnoAttribute = "ATTR_LOOP_USES" // []Name loop defines actually used.
ATTR_SHIFT_RHS GnoAttribute = "ATTR_SHIFT_RHS"
)

type Attributes struct {
Line int
Column int
Label Name
data map[interface{}]interface{} // not persisted
data map[GnoAttribute]interface{} // not persisted
}

func (attr *Attributes) GetLine() int {
Expand All @@ -176,22 +191,31 @@
attr.Label = label
}

func (attr *Attributes) HasAttribute(key interface{}) bool {
func (attr *Attributes) HasAttribute(key GnoAttribute) bool {

Check warning on line 194 in gnovm/pkg/gnolang/nodes.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L194

Added line #L194 was not covered by tests
_, ok := attr.data[key]
return ok
}

func (attr *Attributes) GetAttribute(key interface{}) interface{} {
// GnoAttribute must not be user provided / arbitrary,
// otherwise will create potential exploits.
func (attr *Attributes) GetAttribute(key GnoAttribute) interface{} {
return attr.data[key]
}

func (attr *Attributes) SetAttribute(key interface{}, value interface{}) {
func (attr *Attributes) SetAttribute(key GnoAttribute, value interface{}) {
if attr.data == nil {
attr.data = make(map[interface{}]interface{})
attr.data = make(map[GnoAttribute]interface{})
}
attr.data[key] = value
}

func (attr *Attributes) DelAttribute(key GnoAttribute) {
if debug && attr.data == nil {
panic("should not happen, attribute is expected to be non-empty.")

Check warning on line 214 in gnovm/pkg/gnolang/nodes.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L214

Added line #L214 was not covered by tests
}
delete(attr.data, key)
}

// ----------------------------------------
// Node

Expand All @@ -205,9 +229,10 @@
SetColumn(int)
GetLabel() Name
SetLabel(Name)
HasAttribute(key interface{}) bool
GetAttribute(key interface{}) interface{}
SetAttribute(key interface{}, value interface{})
HasAttribute(key GnoAttribute) bool
GetAttribute(key GnoAttribute) interface{}
SetAttribute(key GnoAttribute, value interface{})
DelAttribute(key GnoAttribute)
}

// non-pointer receiver to help make immutable.
Expand Down Expand Up @@ -367,11 +392,22 @@
_ Expr = &ConstExpr{}
)

type NameExprType int

const (
NameExprTypeNormal NameExprType = iota // default
NameExprTypeDefine // when defining normally
NameExprTypeHeapDefine // when defining escaped name in loop
NameExprTypeHeapUse // when above used in non-define lhs/rhs
NameExprTypeHeapClosure // when closure captures name
)

type NameExpr struct {
Attributes
// TODO rename .Path's to .ValuePaths.
Path ValuePath // set by preprocessor.
Name
Type NameExprType
}

type NameExprs []NameExpr
Expand Down Expand Up @@ -498,8 +534,9 @@
type FuncLitExpr struct {
Attributes
StaticBlock
Type FuncTypeExpr // function type
Body // function body
Type FuncTypeExpr // function type
Body // function body
HeapCaptures NameExprs // filled in findLoopUses1
}

// The preprocessor replaces const expressions
Expand Down Expand Up @@ -580,11 +617,15 @@
named := false
for i, ftx := range ftxz {
if i == 0 {
named = ftx.Name != ""
if ftx.Name == "" || isHiddenResultVariable(string(ftx.Name)) {
named = false
} else {
named = true
}
} else {
if named && ftx.Name == "" {
panic("[]FieldTypeExpr has inconsistent namedness (starts named)")
} else if !named && ftx.Name != "" {
} else if !named && (ftx.Name != "" || !isHiddenResultVariable(string(ftx.Name))) {
panic("[]FieldTypeExpr has inconsistent namedness (starts unnamed)")
}
}
Expand Down Expand Up @@ -1483,6 +1524,7 @@
GetNumNames() uint16
GetParentNode(Store) BlockNode
GetPathForName(Store, Name) ValuePath
GetBlockNodeForPath(Store, ValuePath) BlockNode
GetIsConst(Store, Name) bool
GetLocalIndex(Name) (uint16, bool)
GetValueRef(Store, Name, bool) *TypedValue
Expand Down Expand Up @@ -1582,6 +1624,8 @@
}

// Implements BlockNode.
// NOTE: Extern names may also be local, if declared after usage as an extern
// (thus shadowing the extern name).
func (sb *StaticBlock) GetExternNames() (ns []Name) {
return sb.Externs // copy?
}
Expand Down Expand Up @@ -1623,6 +1667,9 @@
}
// Register as extern.
// NOTE: uverse names are externs too.
// NOTE: externs may also be shadowed later in the block. Thus, usages
// before the declaration will have depth > 1; following it, depth == 1,
// matching the two different identifiers they refer to.
if !isFile(sb.GetSource(store)) {
sb.GetStaticBlock().addExternName(n)
}
Expand Down Expand Up @@ -1651,6 +1698,21 @@
panic(fmt.Sprintf("name %s not declared", n))
}

// Get the containing block node for node with path relative to this containing block.
func (sb *StaticBlock) GetBlockNodeForPath(store Store, path ValuePath) BlockNode {
ltzmaxwell marked this conversation as resolved.
Show resolved Hide resolved
if path.Type != VPBlock {
panic("expected block type value path but got " + path.Type.String())

Check warning on line 1704 in gnovm/pkg/gnolang/nodes.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L1704

Added line #L1704 was not covered by tests
}

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

return bn
}

// Returns whether a name defined here in in ancestry is a const.
// This is not the same as whether a name's static type is
// untyped -- as in c := a == b, a name may be an untyped non-const.
Expand Down Expand Up @@ -1707,21 +1769,12 @@
// Implements BlockNode.
func (sb *StaticBlock) GetStaticTypeOfAt(store Store, path ValuePath) Type {
if debug {
if path.Type != VPBlock {
panic("should not happen")
}
if path.Depth == 0 {
panic("should not happen")
}
}
for {
if path.Depth == 1 {
return sb.Types[path.Index]
} else {
sb = sb.GetParentNode(store).GetStaticBlock()
path.Depth -= 1
}
}
bn := sb.GetBlockNodeForPath(store, path)
return bn.GetStaticBlock().Types[path.Index]
}

// Implements BlockNode.
Expand Down Expand Up @@ -2109,18 +2162,6 @@
return i
}

type GnoAttribute string

const (
ATTR_PREPROCESSED GnoAttribute = "ATTR_PREPROCESSED"
ATTR_PREDEFINED GnoAttribute = "ATTR_PREDEFINED"
ATTR_TYPE_VALUE GnoAttribute = "ATTR_TYPE_VALUE"
ATTR_TYPEOF_VALUE GnoAttribute = "ATTR_TYPEOF_VALUE"
ATTR_IOTA GnoAttribute = "ATTR_IOTA"
ATTR_LOCATIONED GnoAttribute = "ATTR_LOCATIONED"
ATTR_SHIFT_RHS GnoAttribute = "ATTR_SHIFT_RHS"
)

var rePkgName = regexp.MustCompile(`^[a-z][a-z0-9_]+$`)

// TODO: consider length restrictions.
Expand All @@ -2130,3 +2171,12 @@
panic(fmt.Sprintf("cannot create package with invalid name %q", name))
}
}

const hiddenResultVariable = ".res_"

func isHiddenResultVariable(name string) bool {
if strings.HasPrefix(name, hiddenResultVariable) {
return true
}

Check warning on line 2180 in gnovm/pkg/gnolang/nodes.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/nodes.go#L2179-L2180

Added lines #L2179 - L2180 were not covered by tests
return false
}
Loading
Loading