diff --git a/runtime/common/computationkind.go b/runtime/common/computationkind.go index 90b96e858e..7574127cb5 100644 --- a/runtime/common/computationkind.go +++ b/runtime/common/computationkind.go @@ -57,7 +57,7 @@ const ( ComputationKindCreateArrayValue ComputationKindTransferArrayValue ComputationKindDestroyArrayValue - _ + ComputationKindIterateArrayValue _ _ _ diff --git a/runtime/common/computationkind_string.go b/runtime/common/computationkind_string.go index 9ed621d72f..dd2a4b27f5 100644 --- a/runtime/common/computationkind_string.go +++ b/runtime/common/computationkind_string.go @@ -18,6 +18,7 @@ func _() { _ = x[ComputationKindCreateArrayValue-1025] _ = x[ComputationKindTransferArrayValue-1026] _ = x[ComputationKindDestroyArrayValue-1027] + _ = x[ComputationKindIterateArrayValue-1028] _ = x[ComputationKindCreateDictionaryValue-1040] _ = x[ComputationKindTransferDictionaryValue-1041] _ = x[ComputationKindDestroyDictionaryValue-1042] @@ -33,7 +34,7 @@ const ( _ComputationKind_name_0 = "Unknown" _ComputationKind_name_1 = "StatementLoopFunctionInvocation" _ComputationKind_name_2 = "CreateCompositeValueTransferCompositeValueDestroyCompositeValue" - _ComputationKind_name_3 = "CreateArrayValueTransferArrayValueDestroyArrayValue" + _ComputationKind_name_3 = "CreateArrayValueTransferArrayValueDestroyArrayValueIterateArrayValue" _ComputationKind_name_4 = "CreateDictionaryValueTransferDictionaryValueDestroyDictionaryValue" _ComputationKind_name_5 = "EncodeValue" _ComputationKind_name_6 = "STDLIBPanicSTDLIBAssertSTDLIBUnsafeRandom" @@ -43,7 +44,7 @@ const ( var ( _ComputationKind_index_1 = [...]uint8{0, 9, 13, 31} _ComputationKind_index_2 = [...]uint8{0, 20, 42, 63} - _ComputationKind_index_3 = [...]uint8{0, 16, 34, 51} + _ComputationKind_index_3 = [...]uint8{0, 16, 34, 51, 68} _ComputationKind_index_4 = [...]uint8{0, 21, 44, 66} _ComputationKind_index_6 = [...]uint8{0, 11, 23, 41} _ComputationKind_index_7 = [...]uint8{0, 21, 40} @@ -59,7 +60,7 @@ func (i ComputationKind) String() string { case 1010 <= i && i <= 1012: i -= 1010 return _ComputationKind_name_2[_ComputationKind_index_2[i]:_ComputationKind_index_2[i+1]] - case 1025 <= i && i <= 1027: + case 1025 <= i && i <= 1028: i -= 1025 return _ComputationKind_name_3[_ComputationKind_index_3[i]:_ComputationKind_index_3[i+1]] case 1040 <= i && i <= 1042: diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 4e34139977..45f7593aac 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -2996,6 +2996,9 @@ func (v *ArrayValue) Reverse( return nil } + // Meter computation for iterating the array. + interpreter.ReportComputation(common.ComputationKindIterateArrayValue, 1) + value := v.Get(interpreter, locationRange, index) index-- @@ -3046,6 +3049,9 @@ func (v *ArrayValue) Filter( var value Value for { + // Meter computation for iterating the array. + interpreter.ReportComputation(common.ComputationKindIterateArrayValue, 1) + atreeValue, err := iterator.Next() if err != nil { panic(errors.NewExternalError(err)) @@ -3139,6 +3145,9 @@ func (v *ArrayValue) Map( uint64(v.Count()), func() Value { + // Meter computation for iterating the array. + interpreter.ReportComputation(common.ComputationKindIterateArrayValue, 1) + atreeValue, err := iterator.Next() if err != nil { panic(errors.NewExternalError(err)) diff --git a/runtime/interpreter/value_string.go b/runtime/interpreter/value_string.go index de8f342297..4922cc5ab2 100644 --- a/runtime/interpreter/value_string.go +++ b/runtime/interpreter/value_string.go @@ -132,6 +132,10 @@ func stringFunctionJoin(invocation Invocation) Value { first := true stringArray.Iterate(inter, func(element Value) (resume bool) { + + // Meter computation for iterating the array. + inter.ReportComputation(common.ComputationKindIterateArrayValue, 1) + // Add separator if !first { // Construct directly instead of using NewStringMemoryUsage to avoid diff --git a/runtime/tests/interpreter/metering_test.go b/runtime/tests/interpreter/metering_test.go index 8929ebc74b..a2fd7ab596 100644 --- a/runtime/tests/interpreter/metering_test.go +++ b/runtime/tests/interpreter/metering_test.go @@ -397,3 +397,118 @@ func TestInterpretFunctionInvocationHandler(t *testing.T) { occurrences, ) } + +func TestInterpretArrayFunctionsComputationMetering(t *testing.T) { + + t.Parallel() + + t.Run("reverse", func(t *testing.T) { + t.Parallel() + + computationMeteredValues := make(map[common.ComputationKind]uint) + inter, err := parseCheckAndInterpretWithOptions(t, ` + fun main() { + let x = [1, 2, 3] + let y = x.reverse() + }`, + ParseCheckAndInterpretOptions{ + Config: &interpreter.Config{ + OnMeterComputation: func(compKind common.ComputationKind, intensity uint) { + computationMeteredValues[compKind] += intensity + }, + }, + }, + ) + require.NoError(t, err) + + _, err = inter.Invoke("main") + require.NoError(t, err) + + assert.Equal(t, uint(3), computationMeteredValues[common.ComputationKindIterateArrayValue]) + }) + + t.Run("map", func(t *testing.T) { + t.Parallel() + + computationMeteredValues := make(map[common.ComputationKind]uint) + inter, err := parseCheckAndInterpretWithOptions(t, ` + fun main() { + let x = [1, 2, 3, 4] + let trueForEven = fun (_ x: Int): Bool { + return x % 2 == 0 + } + let y = x.map(trueForEven) + }`, + ParseCheckAndInterpretOptions{ + Config: &interpreter.Config{ + OnMeterComputation: func(compKind common.ComputationKind, intensity uint) { + computationMeteredValues[compKind] += intensity + }, + }, + }, + ) + require.NoError(t, err) + + _, err = inter.Invoke("main") + require.NoError(t, err) + + assert.Equal(t, uint(5), computationMeteredValues[common.ComputationKindIterateArrayValue]) + }) + + t.Run("filter", func(t *testing.T) { + t.Parallel() + + computationMeteredValues := make(map[common.ComputationKind]uint) + inter, err := parseCheckAndInterpretWithOptions(t, ` + fun main() { + let x = [1, 2, 3, 4, 5] + let onlyEven = fun (_ x: Int): Bool { + return x % 2 == 0 + } + let y = x.filter(onlyEven) + }`, + ParseCheckAndInterpretOptions{ + Config: &interpreter.Config{ + OnMeterComputation: func(compKind common.ComputationKind, intensity uint) { + computationMeteredValues[compKind] += intensity + }, + }, + }, + ) + require.NoError(t, err) + + _, err = inter.Invoke("main") + require.NoError(t, err) + + assert.Equal(t, uint(6), computationMeteredValues[common.ComputationKindIterateArrayValue]) + }) +} + +func TestInterpretStdlibComputationMetering(t *testing.T) { + + t.Parallel() + + t.Run("string join", func(t *testing.T) { + t.Parallel() + + computationMeteredValues := make(map[common.ComputationKind]uint) + inter, err := parseCheckAndInterpretWithOptions(t, ` + fun main() { + let s = String.join(["one", "two", "three", "four"], separator: ", ") + }`, + ParseCheckAndInterpretOptions{ + Config: &interpreter.Config{ + OnMeterComputation: func(compKind common.ComputationKind, intensity uint) { + computationMeteredValues[compKind] += intensity + }, + }, + }, + ) + require.NoError(t, err) + + _, err = inter.Invoke("main") + require.NoError(t, err) + + assert.Equal(t, uint(4), computationMeteredValues[common.ComputationKindIterateArrayValue]) + }) +}