From cf404c621c1a679511690f5cda2fa4013c0fa620 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Mon, 19 Aug 2024 12:33:19 +0900 Subject: [PATCH 1/8] handle PointerValue in TypeValue's GetLength --- gnovm/pkg/gnolang/uverse_test.go | 55 ++++++++++++++++++++++++++++++-- gnovm/pkg/gnolang/values.go | 5 +++ gnovm/tests/files/len1.gno | 10 ++++++ 3 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 gnovm/tests/files/len1.gno diff --git a/gnovm/pkg/gnolang/uverse_test.go b/gnovm/pkg/gnolang/uverse_test.go index 7a6c0567e45..e84f2ee66bf 100644 --- a/gnovm/pkg/gnolang/uverse_test.go +++ b/gnovm/pkg/gnolang/uverse_test.go @@ -4,14 +4,14 @@ import ( "testing" ) -type printlnTestCases struct { +type uverseTestCases struct { name string code string expected string } func TestIssue1337PrintNilSliceAsUndefined(t *testing.T) { - test := []printlnTestCases{ + test := []uverseTestCases{ { name: "print empty slice", code: `package test @@ -158,3 +158,54 @@ func TestIssue1337PrintNilSliceAsUndefined(t *testing.T) { }) } } + +func TestIssue2707PointerSliceAsParamInLen(t *testing.T) { + tests := []uverseTestCases{ + { + name: "pointer slice as param in len", + code: ` +package test + +func main() { + exp := [...]string{"HELLO"} + x := len(&exp) + println(x) +} + `, + expected: "1\n", + }, + { + name: "len of array", + code: ` +package test + +func main() { + exp := [...]string{"HELLO", "WORLD"} + println(len(exp)) +} + `, + expected: "2\n", + }, + { + name: "len of pointer to array", + code: ` +package test + +func main() { + exp := [...]int{1, 2, 3, 4, 5} + ptr := &exp + println(len(ptr)) +} + `, + expected: "5\n", + }, + } + + for _, tc := range tests { + m := NewMachine("test", nil) + n := MustParseFile("main.go", tc.code) + m.RunFiles(n) + m.RunMain() + assertOutput(t, tc.code, tc.expected) + } +} diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index 5da7c15bb05..0e2a378b855 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -2149,6 +2149,11 @@ func (tv *TypedValue) GetLength() int { return cv.GetLength() case *NativeValue: return cv.Value.Len() + case PointerValue: + if av, ok := cv.TV.V.(*ArrayValue); ok { + return av.GetLength() + } + panic(fmt.Sprintf("unexpected pointer value for len(): %s", tv.T.String())) default: panic(fmt.Sprintf("unexpected type for len(): %s", tv.T.String())) diff --git a/gnovm/tests/files/len1.gno b/gnovm/tests/files/len1.gno new file mode 100644 index 00000000000..f627fba190f --- /dev/null +++ b/gnovm/tests/files/len1.gno @@ -0,0 +1,10 @@ +package main + +func main() { + exp := [...]string{"HELLO"} + x := len(&exp) + println(x) +} + +// Output: +// 1 From a089650e36358a276596dde0a8b26cf3af61c249 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Mon, 19 Aug 2024 14:47:05 +0900 Subject: [PATCH 2/8] cap --- gnovm/pkg/gnolang/uverse_test.go | 64 ++++++++++++++++++++++++++++++++ gnovm/pkg/gnolang/values.go | 5 +++ gnovm/tests/files/cap1.gno | 9 +++++ 3 files changed, 78 insertions(+) create mode 100644 gnovm/tests/files/cap1.gno diff --git a/gnovm/pkg/gnolang/uverse_test.go b/gnovm/pkg/gnolang/uverse_test.go index e84f2ee66bf..1ee27822bfa 100644 --- a/gnovm/pkg/gnolang/uverse_test.go +++ b/gnovm/pkg/gnolang/uverse_test.go @@ -209,3 +209,67 @@ func main() { assertOutput(t, tc.code, tc.expected) } } + +func TestGetCapacityPointerSlice(t *testing.T) { + tests := []struct { + name string + code string + expected string + }{ + { + name: "cap of pointer to array", + code: ` +package test + +func main() { + exp := [...]string{"HELLO"} + x := cap(&exp) + println(x) +}`, + expected: "1\n", + }, + { + name: "cap of array", + code: ` +package test + +func main() { + exp := [...]int{1, 2, 3, 4, 5} + println(cap(exp)) +}`, + expected: "5\n", + }, + { + name: "cap of slice", + code: ` +package test + +func main() { + slice := make([]int, 3, 5) + println(cap(slice)) +}`, + expected: "5\n", + }, + { + name: "cap of nil slice", + code: ` +package test + +func main() { + var slice []int + println(cap(slice)) +}`, + expected: "0\n", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + m := NewMachine("test", nil) + n := MustParseFile("main.go", tc.code) + m.RunFiles(n) + m.RunMain() + assertOutput(t, tc.code, tc.expected) + }) + } +} diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index 0e2a378b855..b96f69a9652 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -2183,6 +2183,11 @@ func (tv *TypedValue) GetCapacity() int { return cv.GetCapacity() case *NativeValue: return cv.Value.Cap() + case PointerValue: + if av, ok := cv.TV.V.(*ArrayValue); ok { + return av.GetCapacity() + } + panic(fmt.Sprintf("unexpected pointer value for cap(): %s", tv.T.String())) default: panic(fmt.Sprintf("unexpected type for cap(): %s", tv.T.String())) diff --git a/gnovm/tests/files/cap1.gno b/gnovm/tests/files/cap1.gno new file mode 100644 index 00000000000..a834c17b474 --- /dev/null +++ b/gnovm/tests/files/cap1.gno @@ -0,0 +1,9 @@ +package main + +func main() { + exp := [...]int{1, 2, 3, 4, 5} + println(cap(exp)) +} + +// Output: +// 5 From 96a512c02cf5a6a6e69ad930288230673af96bd2 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Tue, 20 Aug 2024 11:02:36 +0900 Subject: [PATCH 3/8] handle nil array pointer --- gnovm/pkg/gnolang/uverse_test.go | 139 +++++++++++++++++++++++++++++-- gnovm/pkg/gnolang/values.go | 26 ++++-- gnovm/tests/files/len2.gno | 12 +++ 3 files changed, 163 insertions(+), 14 deletions(-) create mode 100644 gnovm/tests/files/len2.gno diff --git a/gnovm/pkg/gnolang/uverse_test.go b/gnovm/pkg/gnolang/uverse_test.go index 1ee27822bfa..5c9f5183272 100644 --- a/gnovm/pkg/gnolang/uverse_test.go +++ b/gnovm/pkg/gnolang/uverse_test.go @@ -199,6 +199,46 @@ func main() { `, expected: "5\n", }, + { + name: "nil array pointer", + code: ` +package test + +func main() { + printLen(nil) +} + +func printLen(arr *[2]int) { + println(len(arr)) +}`, + expected: "2\n", + }, + { + name: "cap of nil pointer to array", + code: ` +package test + +func main() { + var arr *[3]string + println(cap(arr)) +}`, + expected: "3\n", + }, + { + name: "len and cap of nil pointer to array as function parameter", + code: ` +package test + +func main() { + printLenCap(nil) +} + +func printLenCap(arr *[4]float64) { + println(len(arr)) + println(cap(arr)) +}`, + expected: "4\n4\n", + }, } for _, tc := range tests { @@ -211,11 +251,7 @@ func main() { } func TestGetCapacityPointerSlice(t *testing.T) { - tests := []struct { - name string - code string - expected string - }{ + tests := []uverseTestCases{ { name: "cap of pointer to array", code: ` @@ -261,6 +297,99 @@ func main() { }`, expected: "0\n", }, + { + name: "cap of nil array pointer", + code: ` +package test + +func main() { + printCap(nil) +} + +func printCap(arr *[2]int) { + println(cap(arr)) +}`, + expected: "2\n", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + m := NewMachine("test", nil) + n := MustParseFile("main.go", tc.code) + m.RunFiles(n) + m.RunMain() + assertOutput(t, tc.code, tc.expected) + }) + } +} + +func TestGetCapacityNilValue(t *testing.T) { + tests := []uverseTestCases{ + { + name: "cap of nil array", + code: ` +package test + +func main() { + var arr [5]int + println(cap(&arr)) + var nilArr *[5]int + println(cap(nilArr)) +}`, + expected: "5\n5\n", + }, + { + name: "cap of nil slice", + code: ` +package test + +func main() { + var slice []int + println(cap(slice)) +}`, + expected: "0\n", + }, + { + name: "cap of nil array in function", + code: ` +package test + +func main() { + printCap(nil) +} + +func printCap(arr *[3]string) { + println(cap(arr)) +}`, + expected: "3\n", + }, + { + name: "cap of different nil array types", + code: ` +package test + +func main() { + var nilIntArr *[4]int + var nilFloatArr *[6]float64 + var nilStringArr *[2]string + println(cap(nilIntArr)) + println(cap(nilFloatArr)) + println(cap(nilStringArr)) +}`, + expected: "4\n6\n2\n", + }, + { + name: "cap of nil multidimensional array", + code: ` +package test + +func main() { + var nilMultiArr *[2][3]int + println(cap(nilMultiArr)) +}`, + expected: "2\n", + }, } for _, tc := range tests { diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index b96f69a9652..a533e62a843 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -2132,6 +2132,11 @@ func (tv *TypedValue) GetLength() int { return bt.Len case *SliceType: return 0 + case *PointerType: + if at, ok := bt.Elt.(*ArrayType); ok { + return at.Len + } + panic(fmt.Sprintf("unexpected pointer type for len(): %s", tv.T.String())) default: panic(fmt.Sprintf( "unexpected type for len(): %s", @@ -2162,17 +2167,20 @@ func (tv *TypedValue) GetLength() int { func (tv *TypedValue) GetCapacity() int { if tv.V == nil { - if debug { - // assert acceptable type. - switch baseOf(tv.T).(type) { - // strings have no capacity. - case *ArrayType: - case *SliceType: - default: - panic("should not happen") + // assert acceptable type. + switch bt := baseOf(tv.T).(type) { + // strings have no capacity. + case *ArrayType: + case *SliceType: + return 0 + case *PointerType: + if at, ok := bt.Elt.(*ArrayType); ok { + return at.Len } + panic(fmt.Sprintf("unexpected pointer type for cap(): %s", tv.T.String())) + default: + panic(fmt.Sprintf("unexpected nil type for cap(): %s", tv.T.String())) } - return 0 } switch cv := tv.V.(type) { case StringValue: diff --git a/gnovm/tests/files/len2.gno b/gnovm/tests/files/len2.gno new file mode 100644 index 00000000000..8b24c755041 --- /dev/null +++ b/gnovm/tests/files/len2.gno @@ -0,0 +1,12 @@ +package main + +func main() { + printLen(nil) +} + +func printLen(arr *[2]int) { + println(len(arr)) +} + +// Output: +// 2 From af7364186e2aea805b41350320a477f371da3a57 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Fri, 13 Sep 2024 18:05:45 +0900 Subject: [PATCH 4/8] update test --- gnovm/pkg/gnolang/values_test.go | 70 ++++++++++++++++++++++++++++++++ gnovm/tests/files/cap1.gno | 7 ++-- gnovm/tests/files/cap2.gno | 9 ++++ gnovm/tests/files/cap3.gno | 9 ++++ gnovm/tests/files/cap4.gno | 9 ++++ gnovm/tests/files/cap5.gno | 12 ++++++ gnovm/tests/files/len2.gno | 7 +--- gnovm/tests/files/len3.gno | 10 +++++ gnovm/tests/files/len4.gno | 12 ++++++ gnovm/tests/files/len5.gno | 9 ++++ gnovm/tests/files/len6.gno | 14 +++++++ 11 files changed, 160 insertions(+), 8 deletions(-) create mode 100644 gnovm/pkg/gnolang/values_test.go create mode 100644 gnovm/tests/files/cap2.gno create mode 100644 gnovm/tests/files/cap3.gno create mode 100644 gnovm/tests/files/cap4.gno create mode 100644 gnovm/tests/files/cap5.gno create mode 100644 gnovm/tests/files/len3.gno create mode 100644 gnovm/tests/files/len4.gno create mode 100644 gnovm/tests/files/len5.gno create mode 100644 gnovm/tests/files/len6.gno diff --git a/gnovm/pkg/gnolang/values_test.go b/gnovm/pkg/gnolang/values_test.go new file mode 100644 index 00000000000..9116a114414 --- /dev/null +++ b/gnovm/pkg/gnolang/values_test.go @@ -0,0 +1,70 @@ +package gnolang + +import ( + "fmt" + "testing" +) + +type mockTypedValueStruct struct { + field int +} + +func (m *mockTypedValueStruct) assertValue() {} + +func (m *mockTypedValueStruct) String() string { + return fmt.Sprintf("MockTypedValueStruct(%d)", m.field) +} + +func TestGetLengthPanic(t *testing.T) { + tests := []struct { + name string + tv TypedValue + expected string + }{ + { + name: "NonArrayPointer", + tv: TypedValue{ + T: &PointerType{Elt: &StructType{}}, + V: PointerValue{ + TV: &TypedValue{ + T: &StructType{}, + V: &mockTypedValueStruct{field: 42}, + }, + }, + }, + expected: "unexpected pointer value for len(): *struct{}", + }, + { + name: "UnexpectedType", + tv: TypedValue{ + T: &StructType{}, + V: &mockTypedValueStruct{field: 42}, + }, + expected: "unexpected type for len(): struct{}", + }, + { + name: "UnexpectedPointerType", + tv: TypedValue{ + T: &PointerType{Elt: &StructType{}}, + V: nil, + }, + expected: "unexpected pointer type for len(): *struct{}", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Errorf("the code did not panic") + } else { + if r != tt.expected { + t.Errorf("expected panic message to be %q, got %q", tt.expected, r) + } + } + }() + + tt.tv.GetLength() + }) + } +} diff --git a/gnovm/tests/files/cap1.gno b/gnovm/tests/files/cap1.gno index a834c17b474..e382357ca11 100644 --- a/gnovm/tests/files/cap1.gno +++ b/gnovm/tests/files/cap1.gno @@ -1,9 +1,10 @@ package main func main() { - exp := [...]int{1, 2, 3, 4, 5} - println(cap(exp)) + exp := [...]string{"HELLO"} + x := cap(&exp) + println(x) } // Output: -// 5 +// 1 diff --git a/gnovm/tests/files/cap2.gno b/gnovm/tests/files/cap2.gno new file mode 100644 index 00000000000..a834c17b474 --- /dev/null +++ b/gnovm/tests/files/cap2.gno @@ -0,0 +1,9 @@ +package main + +func main() { + exp := [...]int{1, 2, 3, 4, 5} + println(cap(exp)) +} + +// Output: +// 5 diff --git a/gnovm/tests/files/cap3.gno b/gnovm/tests/files/cap3.gno new file mode 100644 index 00000000000..c5b323338d8 --- /dev/null +++ b/gnovm/tests/files/cap3.gno @@ -0,0 +1,9 @@ +package main + +func main() { + slice := make([]int, 3, 5) + println(cap(slice)) +} + +// Output: +// 5 diff --git a/gnovm/tests/files/cap4.gno b/gnovm/tests/files/cap4.gno new file mode 100644 index 00000000000..758001358fa --- /dev/null +++ b/gnovm/tests/files/cap4.gno @@ -0,0 +1,9 @@ +package main + +func main() { + var slice []int + println(cap(slice)) +} + +// Output: +// 0 diff --git a/gnovm/tests/files/cap5.gno b/gnovm/tests/files/cap5.gno new file mode 100644 index 00000000000..ce2b6be2c42 --- /dev/null +++ b/gnovm/tests/files/cap5.gno @@ -0,0 +1,12 @@ +package main + +func main() { + printCap(nil) +} + +func printCap(arr *[2]int) { + println(cap(arr)) +} + +// Output: +// 2 diff --git a/gnovm/tests/files/len2.gno b/gnovm/tests/files/len2.gno index 8b24c755041..377ff01851f 100644 --- a/gnovm/tests/files/len2.gno +++ b/gnovm/tests/files/len2.gno @@ -1,11 +1,8 @@ package main func main() { - printLen(nil) -} - -func printLen(arr *[2]int) { - println(len(arr)) + exp := [...]string{"HELLO", "WORLD"} + println(len(exp)) } // Output: diff --git a/gnovm/tests/files/len3.gno b/gnovm/tests/files/len3.gno new file mode 100644 index 00000000000..89fe863b20b --- /dev/null +++ b/gnovm/tests/files/len3.gno @@ -0,0 +1,10 @@ +package main + +func main() { + exp := [...]int{1, 2, 3, 4, 5} + ptr := &exp + println(len(ptr)) +} + +// Output: +// 5 diff --git a/gnovm/tests/files/len4.gno b/gnovm/tests/files/len4.gno new file mode 100644 index 00000000000..8b24c755041 --- /dev/null +++ b/gnovm/tests/files/len4.gno @@ -0,0 +1,12 @@ +package main + +func main() { + printLen(nil) +} + +func printLen(arr *[2]int) { + println(len(arr)) +} + +// Output: +// 2 diff --git a/gnovm/tests/files/len5.gno b/gnovm/tests/files/len5.gno new file mode 100644 index 00000000000..7daf3c2cc07 --- /dev/null +++ b/gnovm/tests/files/len5.gno @@ -0,0 +1,9 @@ +package main + +func main() { + var arr *[3]string + println(cap(arr)) +} + +// Output: +// 3 diff --git a/gnovm/tests/files/len6.gno b/gnovm/tests/files/len6.gno new file mode 100644 index 00000000000..9656f65e08d --- /dev/null +++ b/gnovm/tests/files/len6.gno @@ -0,0 +1,14 @@ +package main + +func main() { + printLenCap(nil) +} + +func printLenCap(arr *[4]float64) { + println(len(arr)) + println(cap(arr)) +} + +// Output: +// 4 +// 4 From afc3891527eee43a16e72f600fbb840a0c3e4e6d Mon Sep 17 00:00:00 2001 From: Morgan Bazalgette Date: Fri, 13 Sep 2024 15:40:26 +0200 Subject: [PATCH 5/8] remove duplicate tests with filetests --- gnovm/pkg/gnolang/uverse_test.go | 165 ------------------------------- 1 file changed, 165 deletions(-) diff --git a/gnovm/pkg/gnolang/uverse_test.go b/gnovm/pkg/gnolang/uverse_test.go index 5c9f5183272..f417845ec29 100644 --- a/gnovm/pkg/gnolang/uverse_test.go +++ b/gnovm/pkg/gnolang/uverse_test.go @@ -159,171 +159,6 @@ func TestIssue1337PrintNilSliceAsUndefined(t *testing.T) { } } -func TestIssue2707PointerSliceAsParamInLen(t *testing.T) { - tests := []uverseTestCases{ - { - name: "pointer slice as param in len", - code: ` -package test - -func main() { - exp := [...]string{"HELLO"} - x := len(&exp) - println(x) -} - `, - expected: "1\n", - }, - { - name: "len of array", - code: ` -package test - -func main() { - exp := [...]string{"HELLO", "WORLD"} - println(len(exp)) -} - `, - expected: "2\n", - }, - { - name: "len of pointer to array", - code: ` -package test - -func main() { - exp := [...]int{1, 2, 3, 4, 5} - ptr := &exp - println(len(ptr)) -} - `, - expected: "5\n", - }, - { - name: "nil array pointer", - code: ` -package test - -func main() { - printLen(nil) -} - -func printLen(arr *[2]int) { - println(len(arr)) -}`, - expected: "2\n", - }, - { - name: "cap of nil pointer to array", - code: ` -package test - -func main() { - var arr *[3]string - println(cap(arr)) -}`, - expected: "3\n", - }, - { - name: "len and cap of nil pointer to array as function parameter", - code: ` -package test - -func main() { - printLenCap(nil) -} - -func printLenCap(arr *[4]float64) { - println(len(arr)) - println(cap(arr)) -}`, - expected: "4\n4\n", - }, - } - - for _, tc := range tests { - m := NewMachine("test", nil) - n := MustParseFile("main.go", tc.code) - m.RunFiles(n) - m.RunMain() - assertOutput(t, tc.code, tc.expected) - } -} - -func TestGetCapacityPointerSlice(t *testing.T) { - tests := []uverseTestCases{ - { - name: "cap of pointer to array", - code: ` -package test - -func main() { - exp := [...]string{"HELLO"} - x := cap(&exp) - println(x) -}`, - expected: "1\n", - }, - { - name: "cap of array", - code: ` -package test - -func main() { - exp := [...]int{1, 2, 3, 4, 5} - println(cap(exp)) -}`, - expected: "5\n", - }, - { - name: "cap of slice", - code: ` -package test - -func main() { - slice := make([]int, 3, 5) - println(cap(slice)) -}`, - expected: "5\n", - }, - { - name: "cap of nil slice", - code: ` -package test - -func main() { - var slice []int - println(cap(slice)) -}`, - expected: "0\n", - }, - { - name: "cap of nil array pointer", - code: ` -package test - -func main() { - printCap(nil) -} - -func printCap(arr *[2]int) { - println(cap(arr)) -}`, - expected: "2\n", - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - m := NewMachine("test", nil) - n := MustParseFile("main.go", tc.code) - m.RunFiles(n) - m.RunMain() - assertOutput(t, tc.code, tc.expected) - }) - } -} - func TestGetCapacityNilValue(t *testing.T) { tests := []uverseTestCases{ { From caff9dfe5112a2a8415cddf2ad0f3e0264d9cc50 Mon Sep 17 00:00:00 2001 From: Morgan Bazalgette Date: Fri, 13 Sep 2024 16:16:17 +0200 Subject: [PATCH 6/8] move tests; add some more --- gnovm/pkg/gnolang/uverse_test.go | 79 -------------------------------- gnovm/pkg/gnolang/values.go | 15 +++--- gnovm/tests/files/cap10.gno | 8 ++++ gnovm/tests/files/cap6.gno | 31 +++++++++++++ gnovm/tests/files/cap7.gno | 9 ++++ gnovm/tests/files/cap8.gno | 9 ++++ gnovm/tests/files/cap9.gno | 9 ++++ gnovm/tests/files/len7.gno | 8 ++++ gnovm/tests/files/len8.gno | 10 ++++ 9 files changed, 91 insertions(+), 87 deletions(-) create mode 100644 gnovm/tests/files/cap10.gno create mode 100644 gnovm/tests/files/cap6.gno create mode 100644 gnovm/tests/files/cap7.gno create mode 100644 gnovm/tests/files/cap8.gno create mode 100644 gnovm/tests/files/cap9.gno create mode 100644 gnovm/tests/files/len7.gno create mode 100644 gnovm/tests/files/len8.gno diff --git a/gnovm/pkg/gnolang/uverse_test.go b/gnovm/pkg/gnolang/uverse_test.go index f417845ec29..76961b70ccb 100644 --- a/gnovm/pkg/gnolang/uverse_test.go +++ b/gnovm/pkg/gnolang/uverse_test.go @@ -158,82 +158,3 @@ func TestIssue1337PrintNilSliceAsUndefined(t *testing.T) { }) } } - -func TestGetCapacityNilValue(t *testing.T) { - tests := []uverseTestCases{ - { - name: "cap of nil array", - code: ` -package test - -func main() { - var arr [5]int - println(cap(&arr)) - var nilArr *[5]int - println(cap(nilArr)) -}`, - expected: "5\n5\n", - }, - { - name: "cap of nil slice", - code: ` -package test - -func main() { - var slice []int - println(cap(slice)) -}`, - expected: "0\n", - }, - { - name: "cap of nil array in function", - code: ` -package test - -func main() { - printCap(nil) -} - -func printCap(arr *[3]string) { - println(cap(arr)) -}`, - expected: "3\n", - }, - { - name: "cap of different nil array types", - code: ` -package test - -func main() { - var nilIntArr *[4]int - var nilFloatArr *[6]float64 - var nilStringArr *[2]string - println(cap(nilIntArr)) - println(cap(nilFloatArr)) - println(cap(nilStringArr)) -}`, - expected: "4\n6\n2\n", - }, - { - name: "cap of nil multidimensional array", - code: ` -package test - -func main() { - var nilMultiArr *[2][3]int - println(cap(nilMultiArr)) -}`, - expected: "2\n", - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - m := NewMachine("test", nil) - n := MustParseFile("main.go", tc.code) - m.RunFiles(n) - m.RunMain() - assertOutput(t, tc.code, tc.expected) - }) - } -} diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index a533e62a843..443e8b88ff7 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -2125,7 +2125,7 @@ func (tv *TypedValue) GetLength() int { switch bt := baseOf(tv.T).(type) { case PrimitiveType: if bt != StringType { - panic("should not happen") + panic(fmt.Sprintf("unexpected type for len(): %s", tv.T.String())) } return 0 case *ArrayType: @@ -2136,7 +2136,7 @@ func (tv *TypedValue) GetLength() int { if at, ok := bt.Elt.(*ArrayType); ok { return at.Len } - panic(fmt.Sprintf("unexpected pointer type for len(): %s", tv.T.String())) + panic(fmt.Sprintf("unexpected type for len(): %s", tv.T.String())) default: panic(fmt.Sprintf( "unexpected type for len(): %s", @@ -2158,7 +2158,7 @@ func (tv *TypedValue) GetLength() int { if av, ok := cv.TV.V.(*ArrayValue); ok { return av.GetLength() } - panic(fmt.Sprintf("unexpected pointer value for len(): %s", tv.T.String())) + panic(fmt.Sprintf("unexpected type for len(): %s", tv.T.String())) default: panic(fmt.Sprintf("unexpected type for len(): %s", tv.T.String())) @@ -2171,20 +2171,19 @@ func (tv *TypedValue) GetCapacity() int { switch bt := baseOf(tv.T).(type) { // strings have no capacity. case *ArrayType: + return bt.Len case *SliceType: return 0 case *PointerType: if at, ok := bt.Elt.(*ArrayType); ok { return at.Len } - panic(fmt.Sprintf("unexpected pointer type for cap(): %s", tv.T.String())) + panic(fmt.Sprintf("unexpected type for cap(): %s", tv.T.String())) default: - panic(fmt.Sprintf("unexpected nil type for cap(): %s", tv.T.String())) + panic(fmt.Sprintf("unexpected type for cap(): %s", tv.T.String())) } } switch cv := tv.V.(type) { - case StringValue: - return len(string(cv)) case *ArrayValue: return cv.GetCapacity() case *SliceValue: @@ -2195,7 +2194,7 @@ func (tv *TypedValue) GetCapacity() int { if av, ok := cv.TV.V.(*ArrayValue); ok { return av.GetCapacity() } - panic(fmt.Sprintf("unexpected pointer value for cap(): %s", tv.T.String())) + panic(fmt.Sprintf("unexpected type for cap(): %s", tv.T.String())) default: panic(fmt.Sprintf("unexpected type for cap(): %s", tv.T.String())) diff --git a/gnovm/tests/files/cap10.gno b/gnovm/tests/files/cap10.gno new file mode 100644 index 00000000000..a76c723f77a --- /dev/null +++ b/gnovm/tests/files/cap10.gno @@ -0,0 +1,8 @@ +package main + +func main() { + println("cap", cap(struct{ A int }{})) +} + +// Error: +// unexpected type for cap(): struct{A int} diff --git a/gnovm/tests/files/cap6.gno b/gnovm/tests/files/cap6.gno new file mode 100644 index 00000000000..182279b7ec6 --- /dev/null +++ b/gnovm/tests/files/cap6.gno @@ -0,0 +1,31 @@ +package main + +func main() { + var arr [5]int + var nilArr *[5]int + var nilSlice []int + var nilArr2 *[8]struct{ A, B int } + var nilMatrix *[2][3]int + + println("cap(arr): ", cap(arr)) + println("cap(&arr): ", cap(&arr)) + println("cap(nilArr): ", cap(nilArr)) + println("cap(nilSlice): ", cap(nilSlice)) + println("cap(nilArr2): ", cap(nilArr2)) + println("cap(nilMatrix):", cap(nilMatrix)) + + printCap(nil) +} + +func printCap(arr *[3]string) { + println("printCap: ", cap(arr)) +} + +// Output: +// cap(arr): 5 +// cap(&arr): 5 +// cap(nilArr): 5 +// cap(nilSlice): 0 +// cap(nilArr2): 8 +// cap(nilMatrix): 2 +// printCap: 3 diff --git a/gnovm/tests/files/cap7.gno b/gnovm/tests/files/cap7.gno new file mode 100644 index 00000000000..73e2f11c147 --- /dev/null +++ b/gnovm/tests/files/cap7.gno @@ -0,0 +1,9 @@ +package main + +func main() { + var s string + println("cap", cap(s)) +} + +// Error: +// unexpected type for cap(): string diff --git a/gnovm/tests/files/cap8.gno b/gnovm/tests/files/cap8.gno new file mode 100644 index 00000000000..7fe9b48e28b --- /dev/null +++ b/gnovm/tests/files/cap8.gno @@ -0,0 +1,9 @@ +package main + +func main() { + var i *int + println("cap", cap(i)) +} + +// Error: +// unexpected type for cap(): *int diff --git a/gnovm/tests/files/cap9.gno b/gnovm/tests/files/cap9.gno new file mode 100644 index 00000000000..b7aad6037b4 --- /dev/null +++ b/gnovm/tests/files/cap9.gno @@ -0,0 +1,9 @@ +package main + +func main() { + i := new(int) + println("cap", cap(i)) +} + +// Error: +// unexpected type for cap(): *int diff --git a/gnovm/tests/files/len7.gno b/gnovm/tests/files/len7.gno new file mode 100644 index 00000000000..5deccdbf331 --- /dev/null +++ b/gnovm/tests/files/len7.gno @@ -0,0 +1,8 @@ +package main + +func main() { + println(len(new(int))) +} + +// Error: +// unexpected type for len(): *int diff --git a/gnovm/tests/files/len8.gno b/gnovm/tests/files/len8.gno new file mode 100644 index 00000000000..6ca5a6ae8fa --- /dev/null +++ b/gnovm/tests/files/len8.gno @@ -0,0 +1,10 @@ +package main + +func main() { + println(len(struct { + A, B int + }{})) +} + +// Error: +// unexpected type for len(): struct{A int;B int} From 2c95a7d4a1f1814b23aed6beebf938ce3146e9a3 Mon Sep 17 00:00:00 2001 From: Morgan Bazalgette Date: Fri, 13 Sep 2024 16:51:07 +0200 Subject: [PATCH 7/8] use GetLength in GetSlice for strings and arrays --- gnovm/pkg/gnolang/values.go | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index 443e8b88ff7..bbf77bf19c7 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -2217,13 +2217,13 @@ func (tv *TypedValue) GetSlice(alloc *Allocator, low, high int) TypedValue { "invalid slice index %d > %d", low, high)) } - if tv.GetCapacity() < high { - panic(fmt.Sprintf( - "slice bounds out of range [%d:%d] with capacity %d", - low, high, tv.GetCapacity())) - } switch t := baseOf(tv.T).(type) { case PrimitiveType: + if tv.GetLength() < high { + panic(fmt.Sprintf( + "slice bounds out of range [%d:%d] with string length %d", + low, high, tv.GetLength())) + } if t == StringType || t == UntypedStringType { return TypedValue{ T: tv.T, @@ -2232,6 +2232,11 @@ func (tv *TypedValue) GetSlice(alloc *Allocator, low, high int) TypedValue { } panic("non-string primitive type cannot be sliced") case *ArrayType: + if tv.GetLength() < high { + panic(fmt.Sprintf( + "slice bounds out of range [%d:%d] with array length %d", + low, high, tv.GetLength())) + } av := tv.V.(*ArrayValue) st := alloc.NewType(&SliceType{ Elt: t.Elt, @@ -2247,6 +2252,11 @@ func (tv *TypedValue) GetSlice(alloc *Allocator, low, high int) TypedValue { ), } case *SliceType: + if tv.GetCapacity() < high { + panic(fmt.Sprintf( + "slice bounds out of range [%d:%d] with capacity %d", + low, high, tv.GetCapacity())) + } if tv.V == nil { if low != 0 || high != 0 { panic("nil slice index out of range") From 044054ddfabcaf581dbc9bb64e46f88b848b1b37 Mon Sep 17 00:00:00 2001 From: Morgan Bazalgette Date: Fri, 13 Sep 2024 17:13:51 +0200 Subject: [PATCH 8/8] fixup --- gnovm/pkg/gnolang/values_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gnovm/pkg/gnolang/values_test.go b/gnovm/pkg/gnolang/values_test.go index 9116a114414..ce6edd0a2f9 100644 --- a/gnovm/pkg/gnolang/values_test.go +++ b/gnovm/pkg/gnolang/values_test.go @@ -32,7 +32,7 @@ func TestGetLengthPanic(t *testing.T) { }, }, }, - expected: "unexpected pointer value for len(): *struct{}", + expected: "unexpected type for len(): *struct{}", }, { name: "UnexpectedType", @@ -48,7 +48,7 @@ func TestGetLengthPanic(t *testing.T) { T: &PointerType{Elt: &StructType{}}, V: nil, }, - expected: "unexpected pointer type for len(): *struct{}", + expected: "unexpected type for len(): *struct{}", }, }