From 1d2616094fff8ce2f1b6a97e2fd5128d8f014a8e Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Mon, 5 Jul 2021 15:42:14 +0530 Subject: [PATCH 1/4] Infer static type from arrays/dictionaries --- runtime/convertValues.go | 39 +++++++++++++++++++++++++++++------ runtime/convertValues_test.go | 30 --------------------------- 2 files changed, 33 insertions(+), 36 deletions(-) diff --git a/runtime/convertValues.go b/runtime/convertValues.go index fa0d157354..3bd0a8144d 100644 --- a/runtime/convertValues.go +++ b/runtime/convertValues.go @@ -435,10 +435,9 @@ func importOptionalValue(inter *interpreter.Interpreter, v cadence.Optional, exp func importArrayValue(inter *interpreter.Interpreter, v cadence.Array, expectedType sema.Type) *interpreter.ArrayValue { values := make([]interpreter.Value, len(v.Values)) - var arrayType sema.ArrayType var elementType sema.Type - if expectedArrayType, ok := expectedType.(sema.ArrayType); ok { - arrayType = expectedArrayType + arrayType, ok := expectedType.(sema.ArrayType) + if ok { elementType = arrayType.ElementType(false) } @@ -449,6 +448,17 @@ func importArrayValue(inter *interpreter.Interpreter, v cadence.Array, expectedT var staticArrayType interpreter.ArrayStaticType if arrayType != nil { staticArrayType = interpreter.ConvertSemaArrayTypeToStaticArrayType(arrayType) + } else { + types := make([]sema.Type, len(v.Values)) + + for i, value := range values { + types[i] = inter.ConvertStaticToSemaType(value.StaticType()) + } + + elementSuperType := sema.LeastCommonSuperType(types...) + staticArrayType = interpreter.VariableSizedStaticType{ + Type: interpreter.ConvertSemaToStaticType(elementSuperType), + } } return interpreter.NewArrayValueUnownedNonCopying(staticArrayType, values...) @@ -461,11 +471,11 @@ func importDictionaryValue( ) *interpreter.DictionaryValue { keysAndValues := make([]interpreter.Value, len(v.Pairs)*2) - var dictionaryType *sema.DictionaryType var keyType sema.Type var valueType sema.Type - if expectedDictionaryType, ok := expectedType.(*sema.DictionaryType); ok { - dictionaryType = expectedDictionaryType + + dictionaryType, ok := expectedType.(*sema.DictionaryType) + if ok { keyType = dictionaryType.KeyType valueType = dictionaryType.ValueType } @@ -478,6 +488,23 @@ func importDictionaryValue( var dictionaryStaticType interpreter.DictionaryStaticType if dictionaryType != nil { dictionaryStaticType = interpreter.ConvertSemaDictionaryTypeToStaticDictionaryType(dictionaryType) + } else { + size := len(v.Pairs) + keyTypes := make([]sema.Type, size) + valueTypes := make([]sema.Type, size) + + for i := 0; i < size; i++ { + keyTypes[i] = inter.ConvertStaticToSemaType(keysAndValues[i*2].StaticType()) + valueTypes[i] = inter.ConvertStaticToSemaType(keysAndValues[i*2+1].StaticType()) + } + + keySuperType := sema.LeastCommonSuperType(keyTypes...) + valueSuperType := sema.LeastCommonSuperType(valueTypes...) + + dictionaryStaticType = interpreter.DictionaryStaticType{ + KeyType: interpreter.ConvertSemaToStaticType(keySuperType), + ValueType: interpreter.ConvertSemaToStaticType(valueSuperType), + } } return interpreter.NewDictionaryValueUnownedNonCopying( diff --git a/runtime/convertValues_test.go b/runtime/convertValues_test.go index 311b984e83..d548b2699f 100644 --- a/runtime/convertValues_test.go +++ b/runtime/convertValues_test.go @@ -2768,21 +2768,6 @@ func TestStaticTypeAvailability(t *testing.T) { }, } - // TODO: type must be inferred, and shouldn't panic - defer func() { - r := recover() - - err, isError := r.(error) - require.True(t, isError) - require.Error(t, err) - - assert.Contains( - t, - err.Error(), - "invalid static type for argument: 0", - ) - }() - _, err := executeTestScript(t, script, structValue) require.NoError(t, err) }) @@ -2823,21 +2808,6 @@ func TestStaticTypeAvailability(t *testing.T) { }, } - // TODO: type must be inferred, and shouldn't panic - defer func() { - r := recover() - - err, isError := r.(error) - require.True(t, isError) - require.Error(t, err) - - assert.Contains( - t, - err.Error(), - "invalid static type for argument: 0", - ) - }() - _, err := executeTestScript(t, script, structValue) require.NoError(t, err) }) From bbba3191e719a31151fa3e5faf08c98f468a00f3 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Mon, 5 Jul 2021 16:01:15 +0530 Subject: [PATCH 2/4] Add tests for inferring static type from imported arrays/dictionaries --- runtime/convertValues_test.go | 132 ++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/runtime/convertValues_test.go b/runtime/convertValues_test.go index d548b2699f..ca1717f529 100644 --- a/runtime/convertValues_test.go +++ b/runtime/convertValues_test.go @@ -1837,6 +1837,59 @@ func TestImportExportArrayValue(t *testing.T) { actual, ) }) + + t.Run("import nested array with broader expected type", func(t *testing.T) { + + t.Parallel() + + value := cadence.NewArray([]cadence.Value{ + cadence.NewArray([]cadence.Value{ + cadence.NewInt8(4), + cadence.NewInt8(3), + }), + cadence.NewArray([]cadence.Value{ + cadence.NewInt8(42), + cadence.NewInt8(54), + }), + }) + + inter, err := interpreter.NewInterpreter( + nil, + utils.TestLocation, + ) + require.NoError(t, err) + + actual := importValue( + inter, + value, + sema.AnyStructType, + ) + + assert.Equal(t, + interpreter.NewArrayValueUnownedNonCopying( + interpreter.VariableSizedStaticType{ + Type: interpreter.VariableSizedStaticType{ + Type: interpreter.PrimitiveStaticTypeInt8, + }, + }, + interpreter.NewArrayValueUnownedNonCopying( + interpreter.VariableSizedStaticType{ + Type: interpreter.PrimitiveStaticTypeInt8, + }, + interpreter.Int8Value(4), + interpreter.Int8Value(3), + ), + interpreter.NewArrayValueUnownedNonCopying( + interpreter.VariableSizedStaticType{ + Type: interpreter.PrimitiveStaticTypeInt8, + }, + interpreter.Int8Value(42), + interpreter.Int8Value(54), + ), + ), + actual, + ) + }) } func TestImportExportDictionaryValue(t *testing.T) { @@ -1950,6 +2003,85 @@ func TestImportExportDictionaryValue(t *testing.T) { actual, ) }) + + t.Run("import nested dictionary with broader expected type", func(t *testing.T) { + + t.Parallel() + + value := cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.NewString("a"), + Value: cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.NewInt(1), + Value: cadence.NewInt(100), + }, + { + Key: cadence.NewInt(2), + Value: cadence.NewString("hello"), + }, + }), + }, + { + Key: cadence.NewString("b"), + Value: cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.NewInt(1), + Value: cadence.NewString("foo"), + }, + { + Key: cadence.NewInt(2), + Value: cadence.NewInt(50), + }, + }), + }, + }) + + inter, err := interpreter.NewInterpreter( + nil, + utils.TestLocation, + ) + require.NoError(t, err) + + actual := importValue( + inter, + value, + sema.AnyStructType, + ) + + assert.Equal(t, + interpreter.NewDictionaryValueUnownedNonCopying( + interpreter.DictionaryStaticType{ + KeyType: interpreter.PrimitiveStaticTypeString, + ValueType: interpreter.DictionaryStaticType{ + KeyType: interpreter.PrimitiveStaticTypeInt, + ValueType: interpreter.PrimitiveStaticTypeAnyStruct, + }, + }, + + interpreter.NewStringValue("a"), + interpreter.NewDictionaryValueUnownedNonCopying( + interpreter.DictionaryStaticType{ + KeyType: interpreter.PrimitiveStaticTypeInt, + ValueType: interpreter.PrimitiveStaticTypeAnyStruct, + }, + interpreter.NewIntValueFromInt64(1), interpreter.NewIntValueFromInt64(100), + interpreter.NewIntValueFromInt64(2), interpreter.NewStringValue("hello"), + ), + + interpreter.NewStringValue("b"), + interpreter.NewDictionaryValueUnownedNonCopying( + interpreter.DictionaryStaticType{ + KeyType: interpreter.PrimitiveStaticTypeInt, + ValueType: interpreter.PrimitiveStaticTypeAnyStruct, + }, + interpreter.NewIntValueFromInt64(1), interpreter.NewStringValue("foo"), + interpreter.NewIntValueFromInt64(2), interpreter.NewIntValueFromInt64(50), + ), + ), + actual, + ) + }) } func TestStringValueImport(t *testing.T) { From 91503dd1176757a9277d00b9b0cf8f62295c013a Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Fri, 12 Nov 2021 12:54:39 -0800 Subject: [PATCH 3/4] Validate invalid types for imported containers --- runtime/convertValues.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/runtime/convertValues.go b/runtime/convertValues.go index c7154d5aa7..6ecf6e04c5 100644 --- a/runtime/convertValues.go +++ b/runtime/convertValues.go @@ -674,6 +674,10 @@ func importArrayValue( } elementSuperType := sema.LeastCommonSuperType(types...) + if elementSuperType == sema.InvalidType { + return nil, fmt.Errorf("cannot import array: elements does not belong to the same type") + } + staticArrayType = interpreter.VariableSizedStaticType{ Type: interpreter.ConvertSemaToStaticType(elementSuperType), } @@ -751,6 +755,10 @@ func importDictionaryValue( ) } + if valueSuperType == sema.InvalidType { + return nil, fmt.Errorf("cannot import dictionary: values does not belong to the same type") + } + dictionaryStaticType = interpreter.DictionaryStaticType{ KeyType: interpreter.ConvertSemaToStaticType(keySuperType), ValueType: interpreter.ConvertSemaToStaticType(valueSuperType), From 17c0f76a3af0e4be9d57673bfb0097e97b28584c Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Thu, 18 Nov 2021 12:26:32 -0800 Subject: [PATCH 4/4] Fix typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- runtime/convertValues.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/convertValues.go b/runtime/convertValues.go index 6ecf6e04c5..43204b53ce 100644 --- a/runtime/convertValues.go +++ b/runtime/convertValues.go @@ -675,7 +675,7 @@ func importArrayValue( elementSuperType := sema.LeastCommonSuperType(types...) if elementSuperType == sema.InvalidType { - return nil, fmt.Errorf("cannot import array: elements does not belong to the same type") + return nil, fmt.Errorf("cannot import array: elements do not belong to the same type") } staticArrayType = interpreter.VariableSizedStaticType{