Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions gnovm/pkg/gnolang/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,9 @@ func (ds *defaultStore) GetPackage(pkgPath string, isImport bool) *PackageValue
oid := ObjectIDFromPkgPath(pkgPath)
if oo, exists := ds.cacheObjects[oid]; exists {
pv := oo.(*PackageValue)
if pv.fBlocksMap == nil {
pv.deriveFBlocksMap(ds)
}
return pv
}
// else, load package.
Expand Down
106 changes: 106 additions & 0 deletions gnovm/pkg/gnolang/values_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,112 @@ func (m *mockTypedValueStruct) DeepFill(store Store) Value {
return m
}

// TestFileBlockMissingPanic tests the panic that occurs when a function's file block
// is missing from the package's fBlocksMap
func TestFileBlockMissingPanic(t *testing.T) {
// Create a minimal store
store := NewStore(nil, nil, nil)

// Create a package value
pv := &PackageValue{
ObjectInfo: ObjectInfo{
ID: ObjectID{
PkgID: PkgID{},
},
},
PkgName: "testpkg",
PkgPath: "gno.land/r/test/testpkg",
FNames: []string{"file1.gno"}, // Note: file2.gno is missing
FBlocks: []Value{&Block{}},
}

// Initialize fBlocksMap but don't add file2.gno
pv.fBlocksMap = make(map[string]*Block)
pv.fBlocksMap["file1.gno"] = &Block{}

// Store the package
store.SetCachePackage(pv)

// Create a function that claims to be from file2.gno
fv := &FuncValue{
PkgPath: "gno.land/r/test/testpkg",
FileName: "file2.gno", // This file is not in fBlocksMap
Parent: nil,
IsClosure: false,
}

// Test that GetParent panics with the expected message
defer func() {
if r := recover(); r != nil {
expected := `file block missing for file "file2.gno"`
if r != expected {
t.Errorf("Expected panic message %q, got %q", expected, r)
}
} else {
t.Error("Expected panic but didn't get one")
}
}()

// This should panic
fv.GetParent(store)
}

// TestFileBlockMissingWithInconsistentState tests a scenario where FNames/FBlocks
// arrays are inconsistent with fBlocksMap, simulating a package deployment issue
func TestFileBlockMissingWithInconsistentState(t *testing.T) {
// Create a minimal store
store := NewStore(nil, nil, nil)

// Create a package value where a function exists but its file block is missing
pv := &PackageValue{
ObjectInfo: ObjectInfo{
ID: ObjectID{
PkgID: PkgID{},
},
},
PkgName: "mypkg",
PkgPath: "gno.land/r/test/mypkg",
// FNames lists three files
FNames: []string{"main.gno", "types.gno", "utils.gno"},
// But FBlocks only has two blocks (simulating deployment issue)
FBlocks: []Value{&Block{}, &Block{}},
}

// deriveFBlocksMap would fail here because FNames and FBlocks lengths don't match
// So we manually create an incomplete fBlocksMap
pv.fBlocksMap = make(map[string]*Block)
pv.fBlocksMap["main.gno"] = &Block{}
pv.fBlocksMap["types.gno"] = &Block{}
// utils.gno is missing from fBlocksMap

// Store the package
store.SetCachePackage(pv)

// Create a function that's from the missing file
fv := &FuncValue{
PkgPath: "gno.land/r/test/mypkg",
FileName: "utils.gno", // This file is in FNames but not in fBlocksMap
Parent: nil,
IsClosure: false,
Name: "HelperFunc",
}

// Test that GetParent panics with the expected message
defer func() {
if r := recover(); r != nil {
expected := `file block missing for file "utils.gno"`
if r != expected {
t.Errorf("Expected panic message %q, got %q", expected, r)
}
} else {
t.Error("Expected panic but didn't get one")
}
}()

// This should panic
fv.GetParent(store)
}

func TestGetLengthPanic(t *testing.T) {
tests := []struct {
name string
Expand Down