From c046e280e29b9964b5a4a31e2479eb1707c648e6 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Tue, 1 Aug 2023 14:03:46 -0400 Subject: [PATCH 01/40] include interface conformances when computing supported entitlements for interfaces --- runtime/sema/type.go | 4 +++- runtime/tests/checker/entitlements_test.go | 27 ++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 2e665098dc..28d3b34157 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -4932,7 +4932,9 @@ func (t *InterfaceType) SupportedEntitlements() (set *EntitlementOrderedSet) { }) } }) - // TODO: include inherited entitlements + t.EffectiveInterfaceConformanceSet().ForEach(func(it *InterfaceType) { + set.SetAll(it.SupportedEntitlements()) + }) t.supportedEntitlements = set return set diff --git a/runtime/tests/checker/entitlements_test.go b/runtime/tests/checker/entitlements_test.go index aee60d9740..17dd09ede7 100644 --- a/runtime/tests/checker/entitlements_test.go +++ b/runtime/tests/checker/entitlements_test.go @@ -4772,6 +4772,33 @@ func TestCheckEntitlementConditions(t *testing.T) { assert.NoError(t, err) }) + + t.Run("result value inherited interface entitlement resource", func(t *testing.T) { + t.Parallel() + _, err := ParseAndCheck(t, ` + entitlement X + entitlement Y + resource interface I { + access(X) view fun foo(): Bool { + return true + } + } + resource interface J: I { + access(Y) view fun bar(): Bool { + return true + } + } + fun bar(r: @{J}): @{J} { + post { + result.foo(): "" + result.bar(): "" + } + return <-r + } + `) + + assert.NoError(t, err) + }) } func TestCheckEntitledWriteAndMutateNotAllowed(t *testing.T) { From a42e1dd4be43123f0eb8b069b1664af82f3c88a0 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Thu, 3 Aug 2023 09:32:35 -0400 Subject: [PATCH 02/40] compute entitled types --- runtime/sema/type.go | 37 +++++++++++++++++++++++++++++++++++++ runtime/sema/type_test.go | 7 +++++++ 2 files changed, 44 insertions(+) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 2e665098dc..90562f4778 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -7655,6 +7655,43 @@ func (t *EntitlementMapType) Resolve(_ *TypeParameterTypeOrderedMap) Type { return t } +// Converts its input to an entitled type according to the following rules: +// * `ConvertToEntitledType(&T) ---> auth(Entitlements(T)) &T` +// * `ConvertToEntitledType(Capability) ---> Capability` +// * `ConvertToEntitledType(T) ---> T` +// where Entitlements(I) is defined as the result of T.SupportedEntitlements() +func ConvertToEntitledType(memoryGauge common.MemoryGauge, t Type) Type { + switch t := t.(type) { + case *ReferenceType: + switch t.Authorization { + case UnauthorizedAccess: + innerType := ConvertToEntitledType(memoryGauge, t.Type) + auth := UnauthorizedAccess + if entitlementSupportingType, ok := innerType.(EntitlementSupportingType); ok { + supportedEntitlements := entitlementSupportingType.SupportedEntitlements() + if supportedEntitlements.Len() > 0 { + auth = EntitlementSetAccess{ + SetKind: Conjunction, + Entitlements: supportedEntitlements, + } + } + } + return NewReferenceType( + memoryGauge, + innerType, + auth, + ) + // type is already entitled + default: + return t + } + case *CapabilityType: + return NewCapabilityType(memoryGauge, ConvertToEntitledType(memoryGauge, t.BorrowType)) + default: + return t + } +} + var NativeCompositeTypes = map[string]*CompositeType{} func init() { diff --git a/runtime/sema/type_test.go b/runtime/sema/type_test.go index 2552d49fca..5c6bc5b4fd 100644 --- a/runtime/sema/type_test.go +++ b/runtime/sema/type_test.go @@ -2021,3 +2021,10 @@ func TestMapType(t *testing.T) { require.True(t, outputFunction.Parameters[0].TypeAnnotation.Type.(*GenericType).TypeParameter == outputFunction.TypeParameters[0]) }) } + +func TestConvertToEntitledType(t *testing.T) { + + t.Parallel() + + entitlementE := NewEntitlementType() +} From dbc80387a26a197484a982d30ad313d015e1a4c5 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Thu, 3 Aug 2023 11:28:20 -0400 Subject: [PATCH 03/40] tests for convert to entitled type --- runtime/sema/type.go | 3 + runtime/sema/type_test.go | 327 +++++++++++++++++++++++++++++++++++++- 2 files changed, 329 insertions(+), 1 deletion(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index c8efc5abf4..fe1ecc81ec 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -7660,6 +7660,7 @@ func (t *EntitlementMapType) Resolve(_ *TypeParameterTypeOrderedMap) Type { // Converts its input to an entitled type according to the following rules: // * `ConvertToEntitledType(&T) ---> auth(Entitlements(T)) &T` // * `ConvertToEntitledType(Capability) ---> Capability` +// * `ConvertToEntitledType(T?) ---> ConvertToEntitledType(T)? // * `ConvertToEntitledType(T) ---> T` // where Entitlements(I) is defined as the result of T.SupportedEntitlements() func ConvertToEntitledType(memoryGauge common.MemoryGauge, t Type) Type { @@ -7687,6 +7688,8 @@ func ConvertToEntitledType(memoryGauge common.MemoryGauge, t Type) Type { default: return t } + case *OptionalType: + return NewOptionalType(memoryGauge, ConvertToEntitledType(memoryGauge, t.Type)) case *CapabilityType: return NewCapabilityType(memoryGauge, ConvertToEntitledType(memoryGauge, t.BorrowType)) default: diff --git a/runtime/sema/type_test.go b/runtime/sema/type_test.go index 5c6bc5b4fd..549a8ccd41 100644 --- a/runtime/sema/type_test.go +++ b/runtime/sema/type_test.go @@ -2026,5 +2026,330 @@ func TestConvertToEntitledType(t *testing.T) { t.Parallel() - entitlementE := NewEntitlementType() + testLocation := common.StringLocation("test") + + entitlementE := NewEntitlementType(nil, testLocation, "E") + entitlementF := NewEntitlementType(nil, testLocation, "F") + entitlementG := NewEntitlementType(nil, testLocation, "G") + + eAccess := NewEntitlementSetAccess([]*EntitlementType{entitlementE}, Conjunction) + fAccess := NewEntitlementSetAccess([]*EntitlementType{entitlementF}, Conjunction) + eOrFAccess := NewEntitlementSetAccess([]*EntitlementType{entitlementE, entitlementF}, Disjunction) + eAndFAccess := NewEntitlementSetAccess([]*EntitlementType{entitlementE, entitlementF}, Conjunction) + eAndGAccess := NewEntitlementSetAccess([]*EntitlementType{entitlementE, entitlementG}, Conjunction) + eFAndGAccess := NewEntitlementSetAccess([]*EntitlementType{entitlementE, entitlementF, entitlementG}, Conjunction) + + mapM := NewEntitlementMapType(nil, testLocation, "M") + mapM.Relations = []EntitlementRelation{ + { + Input: entitlementE, + Output: entitlementF, + }, + { + Input: entitlementF, + Output: entitlementG, + }, + } + mapAccess := NewEntitlementMapAccess(mapM) + + compositeStructWithOnlyE := &CompositeType{ + Location: testLocation, + Identifier: "S", + Kind: common.CompositeKindStructure, + Members: &StringMemberOrderedMap{}, + } + compositeStructWithOnlyE.Members.Set( + "foo", + NewFieldMember(nil, compositeStructWithOnlyE, eAccess, ast.VariableKindConstant, "foo", IntType, ""), + ) + + compositeResourceWithOnlyF := &CompositeType{ + Location: testLocation, + Identifier: "R", + Kind: common.CompositeKindResource, + Members: &StringMemberOrderedMap{}, + } + compositeResourceWithOnlyF.Members.Set( + "bar", + NewFieldMember(nil, compositeResourceWithOnlyF, fAccess, ast.VariableKindConstant, "bar", IntType, ""), + ) + compositeResourceWithOnlyF.Members.Set( + "baz", + NewFieldMember(nil, compositeResourceWithOnlyF, fAccess, ast.VariableKindConstant, "baz", compositeStructWithOnlyE, ""), + ) + + compositeResourceWithEOrF := &CompositeType{ + Location: testLocation, + Identifier: "R", + Kind: common.CompositeKindResource, + Members: &StringMemberOrderedMap{}, + } + compositeResourceWithEOrF.Members.Set( + "qux", + NewFieldMember(nil, compositeResourceWithEOrF, eOrFAccess, ast.VariableKindConstant, "qux", IntType, ""), + ) + + compositeTwoFields := &CompositeType{ + Location: testLocation, + Identifier: "S", + Kind: common.CompositeKindStructure, + Members: &StringMemberOrderedMap{}, + } + compositeTwoFields.Members.Set( + "foo", + NewFieldMember(nil, compositeTwoFields, eAccess, ast.VariableKindConstant, "foo", IntType, ""), + ) + compositeTwoFields.Members.Set( + "bar", + NewFieldMember(nil, compositeTwoFields, fAccess, ast.VariableKindConstant, "bar", IntType, ""), + ) + + interfaceTypeWithEAndG := &InterfaceType{ + Location: testLocation, + Identifier: "I", + CompositeKind: common.CompositeKindResource, + Members: &StringMemberOrderedMap{}, + } + interfaceTypeWithEAndG.Members.Set( + "foo", + NewFunctionMember(nil, interfaceTypeWithEAndG, eAndGAccess, "foo", &FunctionType{}, ""), + ) + + interfaceTypeInheriting := &InterfaceType{ + Location: testLocation, + Identifier: "J", + CompositeKind: common.CompositeKindResource, + Members: &StringMemberOrderedMap{}, + ExplicitInterfaceConformances: []*InterfaceType{interfaceTypeWithEAndG}, + } + + compositeTypeInheriting := &CompositeType{ + Location: testLocation, + Identifier: "RI", + Kind: common.CompositeKindResource, + Members: &StringMemberOrderedMap{}, + ExplicitInterfaceConformances: []*InterfaceType{interfaceTypeInheriting}, + } + + compositeTypeWithMap := &CompositeType{ + Location: testLocation, + Identifier: "RI", + Kind: common.CompositeKindResource, + Members: &StringMemberOrderedMap{}, + } + compositeTypeWithMap.Members.Set( + "foo", + NewFunctionMember(nil, compositeTypeWithMap, mapAccess, "foo", &FunctionType{}, ""), + ) + + interfaceTypeWithMap := &InterfaceType{ + Location: testLocation, + Identifier: "RI", + CompositeKind: common.CompositeKindResource, + Members: &StringMemberOrderedMap{}, + } + interfaceTypeWithMap.Members.Set( + "foo", + NewFunctionMember(nil, interfaceTypeWithMap, mapAccess, "foo", &FunctionType{}, ""), + ) + + compositeTypeWithCapField := &CompositeType{ + Location: testLocation, + Identifier: "RI", + Kind: common.CompositeKindResource, + Members: &StringMemberOrderedMap{}, + } + compositeTypeWithCapField.Members.Set( + "foo", + NewFieldMember( + nil, compositeTypeWithCapField, UnauthorizedAccess, ast.VariableKindConstant, "foo", + NewCapabilityType(nil, + NewReferenceType(nil, interfaceTypeInheriting, UnauthorizedAccess), + ), + "", + ), + ) + + interfaceTypeWithCapField := &InterfaceType{ + Location: testLocation, + Identifier: "RI", + CompositeKind: common.CompositeKindResource, + Members: &StringMemberOrderedMap{}, + } + interfaceTypeWithCapField.Members.Set( + "foo", + NewFieldMember( + nil, interfaceTypeWithCapField, UnauthorizedAccess, ast.VariableKindConstant, "foo", + NewCapabilityType(nil, + NewReferenceType(nil, interfaceTypeInheriting, UnauthorizedAccess), + ), + "", + ), + ) + + interfaceTypeInheritingCapField := &InterfaceType{ + Location: testLocation, + Identifier: "J", + CompositeKind: common.CompositeKindResource, + Members: &StringMemberOrderedMap{}, + ExplicitInterfaceConformances: []*InterfaceType{interfaceTypeWithCapField}, + } + + compositeTypeInheritingCapField := &CompositeType{ + Location: testLocation, + Identifier: "RI", + Kind: common.CompositeKindResource, + Members: &StringMemberOrderedMap{}, + ExplicitInterfaceConformances: []*InterfaceType{interfaceTypeInheritingCapField}, + } + + tests := []struct { + Input Type + Output Type + Name string + }{ + { + Input: NewReferenceType(nil, IntType, UnauthorizedAccess), + Output: NewReferenceType(nil, IntType, UnauthorizedAccess), + Name: "int", + }, + { + Input: NewReferenceType(nil, &FunctionType{}, UnauthorizedAccess), + Output: NewReferenceType(nil, &FunctionType{}, UnauthorizedAccess), + Name: "function", + }, + { + Input: NewReferenceType(nil, compositeStructWithOnlyE, UnauthorizedAccess), + Output: NewReferenceType(nil, compositeStructWithOnlyE, eAccess), + Name: "composite E", + }, + { + Input: NewReferenceType(nil, compositeResourceWithOnlyF, UnauthorizedAccess), + Output: NewReferenceType(nil, compositeResourceWithOnlyF, fAccess), + Name: "composite F", + }, + { + Input: NewReferenceType(nil, compositeResourceWithEOrF, UnauthorizedAccess), + Output: NewReferenceType(nil, compositeResourceWithEOrF, eAndFAccess), + Name: "composite E or F", + }, + { + Input: NewReferenceType(nil, compositeTwoFields, UnauthorizedAccess), + Output: NewReferenceType(nil, compositeTwoFields, eAndFAccess), + Name: "composite E and F", + }, + { + Input: NewReferenceType(nil, interfaceTypeWithEAndG, UnauthorizedAccess), + Output: NewReferenceType(nil, interfaceTypeWithEAndG, eAndGAccess), + Name: "interface E and G", + }, + { + Input: NewReferenceType(nil, interfaceTypeInheriting, UnauthorizedAccess), + Output: NewReferenceType(nil, interfaceTypeInheriting, eAndGAccess), + Name: "interface inheritance", + }, + { + Input: NewReferenceType(nil, compositeTypeInheriting, UnauthorizedAccess), + Output: NewReferenceType(nil, compositeTypeInheriting, eAndGAccess), + Name: "composite inheritance", + }, + { + Input: NewReferenceType(nil, compositeTypeWithMap, UnauthorizedAccess), + Output: NewReferenceType(nil, compositeTypeWithMap, eAndFAccess), + Name: "composite map", + }, + { + Input: NewReferenceType(nil, interfaceTypeWithMap, UnauthorizedAccess), + Output: NewReferenceType(nil, interfaceTypeWithMap, eAndFAccess), + Name: "interface map", + }, + { + Input: NewReferenceType(nil, NewCapabilityType(nil, NewReferenceType(nil, compositeTypeWithMap, UnauthorizedAccess)), UnauthorizedAccess), + Output: NewReferenceType(nil, NewCapabilityType(nil, NewReferenceType(nil, compositeTypeWithMap, eAndFAccess)), UnauthorizedAccess), + Name: "reference to capability", + }, + { + Input: NewReferenceType(nil, NewIntersectionType(nil, []*InterfaceType{interfaceTypeInheriting, interfaceTypeWithMap}), UnauthorizedAccess), + Output: NewReferenceType(nil, NewIntersectionType(nil, []*InterfaceType{interfaceTypeInheriting, interfaceTypeWithMap}), eFAndGAccess), + Name: "intersection", + }, + // no change + { + Input: NewReferenceType(nil, compositeTypeWithCapField, UnauthorizedAccess), + Output: NewReferenceType(nil, compositeTypeWithCapField, UnauthorizedAccess), + Name: "composite with capability field", + }, + // no change + { + Input: NewReferenceType(nil, interfaceTypeWithCapField, UnauthorizedAccess), + Output: NewReferenceType(nil, interfaceTypeWithCapField, UnauthorizedAccess), + Name: "interface with capability field", + }, + // no change + { + Input: NewReferenceType(nil, compositeTypeInheritingCapField, UnauthorizedAccess), + Output: NewReferenceType(nil, compositeTypeInheritingCapField, UnauthorizedAccess), + Name: "composite inheriting capability field", + }, + // no change + { + Input: NewReferenceType(nil, interfaceTypeInheritingCapField, UnauthorizedAccess), + Output: NewReferenceType(nil, interfaceTypeInheritingCapField, UnauthorizedAccess), + Name: "interface inheriting capability field", + }, + } + + // create capability versions of all the existing tests + for _, test := range tests { + var capabilityTest struct { + Input Type + Output Type + Name string + } + capabilityTest.Input = NewCapabilityType(nil, test.Input) + capabilityTest.Output = NewCapabilityType(nil, test.Output) + capabilityTest.Name = "capability " + test.Name + + tests = append(tests, capabilityTest) + } + + // create optional versions of all the existing tests + for _, test := range tests { + var optionalTest struct { + Input Type + Output Type + Name string + } + optionalTest.Input = NewOptionalType(nil, test.Input) + optionalTest.Output = NewOptionalType(nil, test.Output) + optionalTest.Name = "optional " + test.Name + + tests = append(tests, optionalTest) + } + + var compareTypesRecursively func(t *testing.T, expected Type, actual Type) + compareTypesRecursively = func(t *testing.T, expected Type, actual Type) { + require.IsType(t, expected, actual) + + switch expected := expected.(type) { + case *ReferenceType: + actual := actual.(*ReferenceType) + require.IsType(t, expected.Authorization, actual.Authorization) + require.True(t, expected.Authorization.Equal(actual.Authorization)) + compareTypesRecursively(t, expected.Type, actual.Type) + case *OptionalType: + actual := actual.(*OptionalType) + compareTypesRecursively(t, expected.Type, actual.Type) + case *CapabilityType: + actual := actual.(*CapabilityType) + compareTypesRecursively(t, expected.BorrowType, actual.BorrowType) + } + } + + for _, test := range tests { + t.Run(test.Name, func(t *testing.T) { + compareTypesRecursively(t, ConvertToEntitledType(nil, test.Input), test.Output) + }) + } + } From e5f4d470d99fadd10c58bce7ebd71447f6543f83 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Thu, 3 Aug 2023 11:33:39 -0400 Subject: [PATCH 04/40] add todo --- runtime/sema/type_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/runtime/sema/type_test.go b/runtime/sema/type_test.go index 549a8ccd41..2cab0cd633 100644 --- a/runtime/sema/type_test.go +++ b/runtime/sema/type_test.go @@ -2297,6 +2297,7 @@ func TestConvertToEntitledType(t *testing.T) { Output: NewReferenceType(nil, interfaceTypeInheritingCapField, UnauthorizedAccess), Name: "interface inheriting capability field", }, + // TODO: add tests for array and dictionary entitlements once the mutability changes are merged } // create capability versions of all the existing tests From 63c655095e804186c9accd939bac62b88bf81b71 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Mon, 7 Aug 2023 13:16:48 -0400 Subject: [PATCH 05/40] basic conversion of values --- runtime/interpreter/interpreter.go | 47 +++++++++ runtime/interpreter/value_test.go | 158 +++++++++++++++++++++++++++++ 2 files changed, 205 insertions(+) diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index ddc18c2a05..72b2458b28 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -5022,6 +5022,53 @@ func (interpreter *Interpreter) MustConvertStaticAuthorizationToSemaAccess(auth return access } +func (interpreter *Interpreter) ConvertValueToEntitlements(v Value) { + semaType := interpreter.MustSemaTypeOfValue(v) + entitledType := sema.ConvertToEntitledType(interpreter, semaType) + + switch v := v.(type) { + case *EphemeralReferenceValue: + entitledReferenceType := entitledType.(*sema.ReferenceType) + staticAuthorization := ConvertSemaAccesstoStaticAuthorization(interpreter, entitledReferenceType.Authorization) + v.Authorization = staticAuthorization + v.BorrowedType = entitledReferenceType.Type + interpreter.ConvertValueToEntitlements(v.Value) + case *StorageReferenceValue: + entitledReferenceType := entitledType.(*sema.ReferenceType) + staticAuthorization := ConvertSemaAccesstoStaticAuthorization(interpreter, entitledReferenceType.Authorization) + v.Authorization = staticAuthorization + v.BorrowedType = entitledReferenceType.Type + // TODO: how to convert stored value + // interpreter.convertValueToEntitlements(v.Value) + case *SomeValue: + interpreter.ConvertValueToEntitlements(v.value) + case *CompositeValue: + // convert all the fields of this composite value to entitlements + v.Walk(interpreter, interpreter.ConvertValueToEntitlements) + case *ArrayValue: + entitledArrayType := entitledType.(sema.ArrayType) + v.semaType = entitledArrayType + v.Type = ConvertSemaArrayTypeToStaticArrayType(interpreter, entitledArrayType) + // convert all the elements of this array value to entitlements + v.Walk(interpreter, interpreter.ConvertValueToEntitlements) + case *DictionaryValue: + entitledDictionaryType := entitledType.(*sema.DictionaryType) + v.semaType = entitledDictionaryType + v.Type = ConvertSemaDictionaryTypeToStaticDictionaryType(interpreter, entitledDictionaryType) + // convert all the elements of this array value to entitlements + v.Walk(interpreter, interpreter.ConvertValueToEntitlements) + // capabilities should just have their borrow type updated; + // we will update their underlying value when the capability is borrowed + case *PathCapabilityValue: + entitledCapabilityValue := entitledType.(*sema.CapabilityType) + v.BorrowType = ConvertSemaToStaticType(interpreter, entitledCapabilityValue.BorrowType) + case *IDCapabilityValue: + entitledCapabilityValue := entitledType.(*sema.CapabilityType) + v.BorrowType = ConvertSemaToStaticType(interpreter, entitledCapabilityValue.BorrowType) + } + +} + func (interpreter *Interpreter) getElaboration(location common.Location) *sema.Elaboration { // Ensure the program for this location is loaded, diff --git a/runtime/interpreter/value_test.go b/runtime/interpreter/value_test.go index 5d332d1fad..1678b17753 100644 --- a/runtime/interpreter/value_test.go +++ b/runtime/interpreter/value_test.go @@ -4533,3 +4533,161 @@ func TestValue_ConformsToStaticType(t *testing.T) { }) } + +func TestConvertToEntitledValue(t *testing.T) { + t.Parallel() + + var uuid uint64 + + storage := newUnmeteredInMemoryStorage() + + code := ` + access(all) entitlement E + access(all) entitlement F + access(all) entitlement G + + access(all) entitlement mapping M { + E -> F + F -> G + } + + access(all) struct S { + access(E) let eField: Int + access(F) let fField: String + init() { + self.eField = 0 + self.fField = "" + } + } + + access(all) resource R { + access(E, G) let egField: Int + init() { + self.egField = 0 + } + } + + access(all) resource Nested { + access(E | F) let efField: @R + init() { + self.efField <- create R() + } + destroy() { + destroy self.efField + } + } + + access(all) fun makeS(): S { + return S() + } + + access(all) fun makeR(): @R { + return <- create R() + } + + access(all) fun makeNested(): @Nested { + return <- create Nested() + } + ` + checker, err := checkerUtils.ParseAndCheckWithOptions(t, + code, + checkerUtils.ParseAndCheckOptions{}, + ) + + require.NoError(t, err) + + inter, err := NewInterpreter( + ProgramFromChecker(checker), + checker.Location, + &Config{ + Storage: storage, + UUIDHandler: func() (uint64, error) { + uuid++ + return uuid, nil + }, + }, + ) + + require.NoError(t, err) + + err = inter.Interpret() + require.NoError(t, err) + + rValue, err := inter.Invoke("makeR") + require.NoError(t, err) + sValue, err := inter.Invoke("makeS") + require.NoError(t, err) + nestedValue, err := inter.Invoke("makeNested") + require.NoError(t, err) + + tests := []struct { + Input Value + Output Value + Name string + }{ + { + Input: rValue, + Output: rValue, + Name: "R", + }, + { + Input: sValue, + Output: sValue, + Name: "S", + }, + { + Input: nestedValue, + Output: nestedValue, + Name: "Nested", + }, + { + Input: NewEphemeralReferenceValue(inter, UnauthorizedAccess, sValue, inter.MustSemaTypeOfValue(sValue)), + Output: NewEphemeralReferenceValue( + inter, + NewEntitlementSetAuthorization( + inter, + []common.TypeID{"S.test.E", "S.test.F"}, + sema.Conjunction, + ), + sValue, + inter.MustSemaTypeOfValue(sValue), + ), + Name: "&S", + }, + { + Input: NewEphemeralReferenceValue(inter, UnauthorizedAccess, rValue, inter.MustSemaTypeOfValue(rValue)), + Output: NewEphemeralReferenceValue( + inter, + NewEntitlementSetAuthorization( + inter, + []common.TypeID{"S.test.E", "S.test.G"}, + sema.Conjunction, + ), + rValue, + inter.MustSemaTypeOfValue(rValue), + ), + Name: "&R", + }, + { + Input: NewEphemeralReferenceValue(inter, UnauthorizedAccess, nestedValue, inter.MustSemaTypeOfValue(nestedValue)), + Output: NewEphemeralReferenceValue( + inter, + NewEntitlementSetAuthorization( + inter, + []common.TypeID{"S.test.E", "S.test.F"}, + sema.Conjunction, + ), + nestedValue, + inter.MustSemaTypeOfValue(nestedValue), + ), + Name: "&Nested", + }, + } + + for _, test := range tests { + t.Run(test.Name, func(t *testing.T) { + inter.ConvertValueToEntitlements(test.Input) + require.Equal(t, test.Input, test.Output) + }) + } +} From 079195517bf91d9921ff929b22d781cbc4d9dc69 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Mon, 7 Aug 2023 14:18:06 -0400 Subject: [PATCH 06/40] tests for arrays and dicts of references --- runtime/interpreter/value_test.go | 124 +++++++++++++++++++++++++++++- 1 file changed, 123 insertions(+), 1 deletion(-) diff --git a/runtime/interpreter/value_test.go b/runtime/interpreter/value_test.go index 1678b17753..66b809ce95 100644 --- a/runtime/interpreter/value_test.go +++ b/runtime/interpreter/value_test.go @@ -4541,6 +4541,8 @@ func TestConvertToEntitledValue(t *testing.T) { storage := newUnmeteredInMemoryStorage() + testAddress := common.MustBytesToAddress([]byte{0x1}) + code := ` access(all) entitlement E access(all) entitlement F @@ -4654,6 +4656,67 @@ func TestConvertToEntitledValue(t *testing.T) { ), Name: "&S", }, + { + Input: NewArrayValue( + inter, + EmptyLocationRange, + NewVariableSizedStaticType(inter, NewReferenceStaticType(inter, UnauthorizedAccess, sValue.StaticType(inter))), + testAddress, + NewEphemeralReferenceValue(inter, UnauthorizedAccess, sValue, inter.MustSemaTypeOfValue(sValue)), + ), + Output: NewArrayValue( + inter, + EmptyLocationRange, + NewVariableSizedStaticType( + inter, + NewReferenceStaticType(inter, + UnauthorizedAccess, + sValue.StaticType(inter), + ), + ), + testAddress, + NewEphemeralReferenceValue( + inter, + NewEntitlementSetAuthorization( + inter, + []common.TypeID{"S.test.E", "S.test.F"}, + sema.Conjunction, + ), + sValue, + inter.MustSemaTypeOfValue(sValue), + ), + ), + Name: "[&S]", + }, + { + Input: NewDictionaryValue( + inter, + EmptyLocationRange, + NewDictionaryStaticType(inter, PrimitiveStaticTypeInt, NewReferenceStaticType(inter, UnauthorizedAccess, sValue.StaticType(inter))), + NewIntValueFromInt64(inter, 0), + NewEphemeralReferenceValue(inter, UnauthorizedAccess, sValue, inter.MustSemaTypeOfValue(sValue)), + ), + Output: NewDictionaryValue( + inter, + EmptyLocationRange, + NewDictionaryStaticType(inter, PrimitiveStaticTypeInt, NewReferenceStaticType(inter, + UnauthorizedAccess, + sValue.StaticType(inter), + )), + NewIntValueFromInt64(inter, 0), + NewEphemeralReferenceValue( + inter, + NewEntitlementSetAuthorization( + inter, + []common.TypeID{"S.test.E", "S.test.F"}, + sema.Conjunction, + ), + sValue, + inter.MustSemaTypeOfValue(sValue), + ), + ), + Name: "{Int: &S}", + }, { Input: NewEphemeralReferenceValue(inter, UnauthorizedAccess, rValue, inter.MustSemaTypeOfValue(rValue)), Output: NewEphemeralReferenceValue( @@ -4668,6 +4731,32 @@ func TestConvertToEntitledValue(t *testing.T) { ), Name: "&R", }, + { + Input: NewArrayValue( + inter, + EmptyLocationRange, + NewVariableSizedStaticType(inter, NewReferenceStaticType(inter, UnauthorizedAccess, rValue.StaticType(inter))), + testAddress, + NewEphemeralReferenceValue(inter, UnauthorizedAccess, rValue, inter.MustSemaTypeOfValue(rValue)), + ), + Output: NewArrayValue( + inter, + EmptyLocationRange, + NewVariableSizedStaticType(inter, NewReferenceStaticType(inter, UnauthorizedAccess, rValue.StaticType(inter))), + testAddress, + NewEphemeralReferenceValue( + inter, + NewEntitlementSetAuthorization( + inter, + []common.TypeID{"S.test.E", "S.test.G"}, + sema.Conjunction, + ), + rValue, + inter.MustSemaTypeOfValue(rValue), + ), + ), + Name: "[&R]", + }, { Input: NewEphemeralReferenceValue(inter, UnauthorizedAccess, nestedValue, inter.MustSemaTypeOfValue(nestedValue)), Output: NewEphemeralReferenceValue( @@ -4682,12 +4771,45 @@ func TestConvertToEntitledValue(t *testing.T) { ), Name: "&Nested", }, + { + Input: NewArrayValue( + inter, + EmptyLocationRange, + NewVariableSizedStaticType(inter, NewReferenceStaticType(inter, UnauthorizedAccess, nestedValue.StaticType(inter))), + testAddress, + NewEphemeralReferenceValue(inter, UnauthorizedAccess, nestedValue, inter.MustSemaTypeOfValue(nestedValue)), + ), + Output: NewArrayValue( + inter, + EmptyLocationRange, + NewVariableSizedStaticType(inter, NewReferenceStaticType(inter, UnauthorizedAccess, nestedValue.StaticType(inter))), + testAddress, + NewEphemeralReferenceValue( + inter, + NewEntitlementSetAuthorization( + inter, + []common.TypeID{"S.test.E", "S.test.F"}, + sema.Conjunction, + ), + nestedValue, + inter.MustSemaTypeOfValue(nestedValue), + ), + ), + Name: "[&Nested]", + }, + + // TODO: after mutability entitlements, add tests for references to arrays and dictionaries } for _, test := range tests { t.Run(test.Name, func(t *testing.T) { inter.ConvertValueToEntitlements(test.Input) - require.Equal(t, test.Input, test.Output) + switch input := test.Input.(type) { + case EquatableValue: + require.True(t, input.Equal(inter, EmptyLocationRange, test.Output)) + default: + require.Equal(t, test.Input, test.Output) + } }) } } From 1c0e20d3d685be4237db6907a6eea66fe758f5fe Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Tue, 8 Aug 2023 13:38:23 -0400 Subject: [PATCH 07/40] add test for capability values --- runtime/interpreter/value_test.go | 47 ++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/runtime/interpreter/value_test.go b/runtime/interpreter/value_test.go index 66b809ce95..972d42643c 100644 --- a/runtime/interpreter/value_test.go +++ b/runtime/interpreter/value_test.go @@ -4797,7 +4797,52 @@ func TestConvertToEntitledValue(t *testing.T) { ), Name: "[&Nested]", }, - + { + Input: NewIDCapabilityValue( + inter, + 0, + NewAddressValue(inter, testAddress), + NewReferenceStaticType(inter, UnauthorizedAccess, sValue.StaticType(inter)), + ), + Output: NewIDCapabilityValue( + inter, + 0, + NewAddressValue(inter, testAddress), + NewReferenceStaticType( + inter, + NewEntitlementSetAuthorization( + inter, + []common.TypeID{"S.test.E", "S.test.F"}, + sema.Conjunction, + ), + sValue.StaticType(inter), + ), + ), + Name: "Capability<&S>", + }, + { + Input: NewIDCapabilityValue( + inter, + 0, + NewAddressValue(inter, testAddress), + NewReferenceStaticType(inter, UnauthorizedAccess, rValue.StaticType(inter)), + ), + Output: NewIDCapabilityValue( + inter, + 0, + NewAddressValue(inter, testAddress), + NewReferenceStaticType( + inter, + NewEntitlementSetAuthorization( + inter, + []common.TypeID{"S.test.E", "S.test.G"}, + sema.Conjunction, + ), + rValue.StaticType(inter), + ), + ), + Name: "Capability<&R>", + }, // TODO: after mutability entitlements, add tests for references to arrays and dictionaries } From b031af972511c199b0f368807d4cb09508500852 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Tue, 8 Aug 2023 14:26:11 -0400 Subject: [PATCH 08/40] convert runtime type values --- runtime/interpreter/interpreter.go | 30 ++++++++++++++++++++++++------ runtime/interpreter/value_test.go | 20 ++++++++++++++++---- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 72b2458b28..bad850ae3a 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -5022,7 +5022,10 @@ func (interpreter *Interpreter) MustConvertStaticAuthorizationToSemaAccess(auth return access } -func (interpreter *Interpreter) ConvertValueToEntitlements(v Value) { +// Converts the input value into a version compatible with the new entitlements feature, +// with the same members/operations accessible on any references as would have been accessible in the past. +// Modifies the input `v` in place, and also returns a copy of the value +func (interpreter *Interpreter) ConvertValueToEntitlements(v Value) Value { semaType := interpreter.MustSemaTypeOfValue(v) entitledType := sema.ConvertToEntitledType(interpreter, semaType) @@ -5038,25 +5041,24 @@ func (interpreter *Interpreter) ConvertValueToEntitlements(v Value) { staticAuthorization := ConvertSemaAccesstoStaticAuthorization(interpreter, entitledReferenceType.Authorization) v.Authorization = staticAuthorization v.BorrowedType = entitledReferenceType.Type - // TODO: how to convert stored value - // interpreter.convertValueToEntitlements(v.Value) + // stored value is not converted; instead we will convert it upon load case *SomeValue: interpreter.ConvertValueToEntitlements(v.value) case *CompositeValue: // convert all the fields of this composite value to entitlements - v.Walk(interpreter, interpreter.ConvertValueToEntitlements) + v.Walk(interpreter, func(v Value) { interpreter.ConvertValueToEntitlements(v) }) case *ArrayValue: entitledArrayType := entitledType.(sema.ArrayType) v.semaType = entitledArrayType v.Type = ConvertSemaArrayTypeToStaticArrayType(interpreter, entitledArrayType) // convert all the elements of this array value to entitlements - v.Walk(interpreter, interpreter.ConvertValueToEntitlements) + v.Walk(interpreter, func(v Value) { interpreter.ConvertValueToEntitlements(v) }) case *DictionaryValue: entitledDictionaryType := entitledType.(*sema.DictionaryType) v.semaType = entitledDictionaryType v.Type = ConvertSemaDictionaryTypeToStaticDictionaryType(interpreter, entitledDictionaryType) // convert all the elements of this array value to entitlements - v.Walk(interpreter, interpreter.ConvertValueToEntitlements) + v.Walk(interpreter, func(v Value) { interpreter.ConvertValueToEntitlements(v) }) // capabilities should just have their borrow type updated; // we will update their underlying value when the capability is borrowed case *PathCapabilityValue: @@ -5065,8 +5067,24 @@ func (interpreter *Interpreter) ConvertValueToEntitlements(v Value) { case *IDCapabilityValue: entitledCapabilityValue := entitledType.(*sema.CapabilityType) v.BorrowType = ConvertSemaToStaticType(interpreter, entitledCapabilityValue.BorrowType) + case TypeValue: + if v.Type == nil { + return v + } + // convert the static type of the value + return NewTypeValue( + interpreter, + ConvertSemaToStaticType( + interpreter, + sema.ConvertToEntitledType( + interpreter, + interpreter.MustConvertStaticToSemaType(v.Type), + ), + ), + ) } + return v } func (interpreter *Interpreter) getElaboration(location common.Location) *sema.Elaboration { diff --git a/runtime/interpreter/value_test.go b/runtime/interpreter/value_test.go index 972d42643c..d8767d8961 100644 --- a/runtime/interpreter/value_test.go +++ b/runtime/interpreter/value_test.go @@ -4846,14 +4846,26 @@ func TestConvertToEntitledValue(t *testing.T) { // TODO: after mutability entitlements, add tests for references to arrays and dictionaries } + for _, test := range tests { + var runtimeTypeTest struct { + Input Value + Output Value + Name string + } + runtimeTypeTest.Input = NewTypeValue(nil, test.Input.StaticType(inter)) + runtimeTypeTest.Output = NewTypeValue(nil, test.Output.StaticType(inter)) + runtimeTypeTest.Name = "runtime type " + test.Name + + tests = append(tests, runtimeTypeTest) + } + for _, test := range tests { t.Run(test.Name, func(t *testing.T) { - inter.ConvertValueToEntitlements(test.Input) - switch input := test.Input.(type) { + switch output := inter.ConvertValueToEntitlements(test.Input).(type) { case EquatableValue: - require.True(t, input.Equal(inter, EmptyLocationRange, test.Output)) + require.True(t, output.Equal(inter, EmptyLocationRange, test.Output)) default: - require.Equal(t, test.Input, test.Output) + require.Equal(t, output, test.Output) } }) } From bfed26b2973d49593700497ccd0efae848e726ca Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Wed, 9 Aug 2023 11:50:06 -0400 Subject: [PATCH 09/40] make TypeValue a pointer type --- runtime/convertValues.go | 6 +- runtime/convertValues_test.go | 6 +- runtime/interpreter/decode.go | 2 +- runtime/interpreter/encoding_test.go | 8 +- runtime/interpreter/interpreter.go | 26 +++-- runtime/interpreter/value.go | 66 +++++------ runtime/interpreter/value_test.go | 104 ++++++++++++++++-- runtime/interpreter/visitor.go | 6 +- runtime/stdlib/test_emulatorbackend.go | 2 +- runtime/tests/interpreter/account_test.go | 4 +- runtime/tests/interpreter/attachments_test.go | 4 +- runtime/tests/interpreter/interpreter_test.go | 14 +-- runtime/tests/interpreter/metatype_test.go | 30 ++--- runtime/tests/interpreter/runtimetype_test.go | 70 ++++++------ runtime/tests/interpreter/string_test.go | 8 +- 15 files changed, 221 insertions(+), 135 deletions(-) diff --git a/runtime/convertValues.go b/runtime/convertValues.go index 82a2caa0c7..3c0a7ecdb2 100644 --- a/runtime/convertValues.go +++ b/runtime/convertValues.go @@ -231,7 +231,7 @@ func exportValueWithInterpreter( return exportAccountLinkValue(inter), nil case interpreter.PathValue: return exportPathValue(inter, v) - case interpreter.TypeValue: + case *interpreter.TypeValue: return exportTypeValue(v, inter), nil case *interpreter.IDCapabilityValue: return exportIDCapabilityValue(v, inter) @@ -634,7 +634,7 @@ func exportPathValue(gauge common.MemoryGauge, v interpreter.PathValue) (cadence ) } -func exportTypeValue(v interpreter.TypeValue, inter *interpreter.Interpreter) cadence.TypeValue { +func exportTypeValue(v *interpreter.TypeValue, inter *interpreter.Interpreter) cadence.TypeValue { var typ sema.Type if v.Type != nil { typ = inter.MustConvertStaticToSemaType(v.Type) @@ -1130,7 +1130,7 @@ func (i valueImporter) importPathValue(v cadence.Path) interpreter.PathValue { ) } -func (i valueImporter) importTypeValue(v cadence.Type) (interpreter.TypeValue, error) { +func (i valueImporter) importTypeValue(v cadence.Type) (*interpreter.TypeValue, error) { inter := i.inter typ := ImportType(inter, v) diff --git a/runtime/convertValues_test.go b/runtime/convertValues_test.go index 4db6b7e620..62e8ae4a06 100644 --- a/runtime/convertValues_test.go +++ b/runtime/convertValues_test.go @@ -889,7 +889,7 @@ func TestImportValue(t *testing.T) { { label: "Type()", value: cadence.NewTypeValue(cadence.IntType{}), - expected: interpreter.TypeValue{Type: interpreter.PrimitiveStaticTypeInt}, + expected: &interpreter.TypeValue{Type: interpreter.PrimitiveStaticTypeInt}, }, } { test(tt) @@ -2070,7 +2070,7 @@ func TestExportTypeValue(t *testing.T) { t.Parallel() - value := interpreter.TypeValue{ + value := &interpreter.TypeValue{ Type: nil, } actual, err := exportValueWithInterpreter( @@ -2117,7 +2117,7 @@ func TestExportTypeValue(t *testing.T) { inter := newTestInterpreter(t) inter.Program = interpreter.ProgramFromChecker(checker) - ty := interpreter.TypeValue{ + ty := &interpreter.TypeValue{ Type: &interpreter.IntersectionStaticType{ Types: []interpreter.InterfaceStaticType{ { diff --git a/runtime/interpreter/decode.go b/runtime/interpreter/decode.go index 5c8647ec58..55f4a9fd6d 100644 --- a/runtime/interpreter/decode.go +++ b/runtime/interpreter/decode.go @@ -1330,7 +1330,7 @@ func (d StorableDecoder) decodePublishedValue() (*PublishedValue, error) { return NewPublishedValue(d.memoryGauge, addressValue, capabilityValue), nil } -func (d StorableDecoder) decodeType() (TypeValue, error) { +func (d StorableDecoder) decodeType() (*TypeValue, error) { const expectedLength = encodedTypeValueTypeLength arraySize, err := d.decoder.DecodeArrayHead() diff --git a/runtime/interpreter/encoding_test.go b/runtime/interpreter/encoding_test.go index 3d57ba229e..75567e76f7 100644 --- a/runtime/interpreter/encoding_test.go +++ b/runtime/interpreter/encoding_test.go @@ -4383,7 +4383,7 @@ func TestEncodeDecodeTypeValue(t *testing.T) { t.Parallel() - value := TypeValue{ + value := &TypeValue{ Type: ConvertSemaToPrimitiveStaticType(nil, sema.BoolType), } @@ -4410,7 +4410,7 @@ func TestEncodeDecodeTypeValue(t *testing.T) { t.Parallel() - value := TypeValue{ + value := &TypeValue{ Type: ConvertSemaToPrimitiveStaticType(nil, sema.IntType), } @@ -4437,7 +4437,7 @@ func TestEncodeDecodeTypeValue(t *testing.T) { t.Parallel() - value := TypeValue{ + value := &TypeValue{ Type: nil, } @@ -4468,7 +4468,7 @@ func TestEncodeDecodeTypeValue(t *testing.T) { maxInlineElementSize := atree.MaxInlineArrayElementSize identifier := strings.Repeat("x", int(maxInlineElementSize+1)) - expected := TypeValue{ + expected := &TypeValue{ Type: NewCompositeStaticTypeComputeTypeID( nil, common.AddressLocation{}, diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index bad850ae3a..079eaf49f7 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -3442,12 +3442,12 @@ func init() { } func dictionaryTypeFunction(invocation Invocation) Value { - keyTypeValue, ok := invocation.Arguments[0].(TypeValue) + keyTypeValue, ok := invocation.Arguments[0].(*TypeValue) if !ok { panic(errors.NewUnreachableError()) } - valueTypeValue, ok := invocation.Arguments[1].(TypeValue) + valueTypeValue, ok := invocation.Arguments[1].(*TypeValue) if !ok { panic(errors.NewUnreachableError()) } @@ -3480,7 +3480,7 @@ func referenceTypeFunction(invocation Invocation) Value { panic(errors.NewUnreachableError()) } - typeValue, ok := invocation.Arguments[1].(TypeValue) + typeValue, ok := invocation.Arguments[1].(*TypeValue) if !ok { panic(errors.NewUnreachableError()) } @@ -3577,7 +3577,7 @@ func functionTypeFunction(invocation Invocation) Value { panic(errors.NewUnreachableError()) } - typeValue, ok := invocation.Arguments[1].(TypeValue) + typeValue, ok := invocation.Arguments[1].(*TypeValue) if !ok { panic(errors.NewUnreachableError()) } @@ -3589,7 +3589,7 @@ func functionTypeFunction(invocation Invocation) Value { if parameterCount > 0 { parameterTypes = make([]sema.Parameter, 0, parameterCount) parameters.Iterate(interpreter, func(param Value) bool { - semaType := interpreter.MustConvertStaticToSemaType(param.(TypeValue).Type) + semaType := interpreter.MustConvertStaticToSemaType(param.(*TypeValue).Type) parameterTypes = append( parameterTypes, sema.Parameter{ @@ -3768,7 +3768,7 @@ var runtimeTypeConstructors = []runtimeTypeConstructor{ converter: NewUnmeteredHostFunctionValue( sema.OptionalTypeFunctionType, func(invocation Invocation) Value { - typeValue, ok := invocation.Arguments[0].(TypeValue) + typeValue, ok := invocation.Arguments[0].(*TypeValue) if !ok { panic(errors.NewUnreachableError()) } @@ -3788,7 +3788,7 @@ var runtimeTypeConstructors = []runtimeTypeConstructor{ converter: NewUnmeteredHostFunctionValue( sema.VariableSizedArrayTypeFunctionType, func(invocation Invocation) Value { - typeValue, ok := invocation.Arguments[0].(TypeValue) + typeValue, ok := invocation.Arguments[0].(*TypeValue) if !ok { panic(errors.NewUnreachableError()) } @@ -3809,7 +3809,7 @@ var runtimeTypeConstructors = []runtimeTypeConstructor{ converter: NewUnmeteredHostFunctionValue( sema.ConstantSizedArrayTypeFunctionType, func(invocation Invocation) Value { - typeValue, ok := invocation.Arguments[0].(TypeValue) + typeValue, ok := invocation.Arguments[0].(*TypeValue) if !ok { panic(errors.NewUnreachableError()) } @@ -3835,7 +3835,7 @@ var runtimeTypeConstructors = []runtimeTypeConstructor{ converter: NewUnmeteredHostFunctionValue( sema.CapabilityTypeFunctionType, func(invocation Invocation) Value { - typeValue, ok := invocation.Arguments[0].(TypeValue) + typeValue, ok := invocation.Arguments[0].(*TypeValue) if !ok { panic(errors.NewUnreachableError()) } @@ -5043,7 +5043,9 @@ func (interpreter *Interpreter) ConvertValueToEntitlements(v Value) Value { v.BorrowedType = entitledReferenceType.Type // stored value is not converted; instead we will convert it upon load case *SomeValue: - interpreter.ConvertValueToEntitlements(v.value) + v.value = interpreter.ConvertValueToEntitlements(v.value) + // reset the storable, to be recomputed on next access + v.valueStorable = nil case *CompositeValue: // convert all the fields of this composite value to entitlements v.Walk(interpreter, func(v Value) { interpreter.ConvertValueToEntitlements(v) }) @@ -5067,7 +5069,7 @@ func (interpreter *Interpreter) ConvertValueToEntitlements(v Value) Value { case *IDCapabilityValue: entitledCapabilityValue := entitledType.(*sema.CapabilityType) v.BorrowType = ConvertSemaToStaticType(interpreter, entitledCapabilityValue.BorrowType) - case TypeValue: + case *TypeValue: if v.Type == nil { return v } @@ -5330,7 +5332,7 @@ func (interpreter *Interpreter) isInstanceFunction(self Value) *HostFunctionValu interpreter := invocation.Interpreter firstArgument := invocation.Arguments[0] - typeValue, ok := firstArgument.(TypeValue) + typeValue, ok := firstArgument.(*TypeValue) if !ok { panic(errors.NewUnreachableError()) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 8e9715da6b..535d11c7b9 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -303,44 +303,44 @@ type TypeValue struct { Type StaticType } -var EmptyTypeValue = TypeValue{} +var EmptyTypeValue = &TypeValue{} -var _ Value = TypeValue{} -var _ atree.Storable = TypeValue{} -var _ EquatableValue = TypeValue{} -var _ MemberAccessibleValue = TypeValue{} +var _ Value = &TypeValue{} +var _ atree.Storable = &TypeValue{} +var _ EquatableValue = &TypeValue{} +var _ MemberAccessibleValue = &TypeValue{} -func NewUnmeteredTypeValue(t StaticType) TypeValue { - return TypeValue{Type: t} +func NewUnmeteredTypeValue(t StaticType) *TypeValue { + return &TypeValue{Type: t} } func NewTypeValue( memoryGauge common.MemoryGauge, staticType StaticType, -) TypeValue { +) *TypeValue { common.UseMemory(memoryGauge, common.TypeValueMemoryUsage) return NewUnmeteredTypeValue(staticType) } -func (TypeValue) isValue() {} +func (*TypeValue) isValue() {} -func (v TypeValue) Accept(interpreter *Interpreter, visitor Visitor) { +func (v *TypeValue) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitTypeValue(interpreter, v) } -func (TypeValue) Walk(_ *Interpreter, _ func(Value)) { +func (*TypeValue) Walk(_ *Interpreter, _ func(Value)) { // NO-OP } -func (TypeValue) StaticType(interpreter *Interpreter) StaticType { +func (*TypeValue) StaticType(interpreter *Interpreter) StaticType { return NewPrimitiveStaticType(interpreter, PrimitiveStaticTypeMetaType) } -func (TypeValue) IsImportable(_ *Interpreter) bool { +func (*TypeValue) IsImportable(_ *Interpreter) bool { return sema.MetaType.Importable } -func (v TypeValue) String() string { +func (v *TypeValue) String() string { var typeString string staticType := v.Type if staticType != nil { @@ -350,11 +350,11 @@ func (v TypeValue) String() string { return format.TypeValue(typeString) } -func (v TypeValue) RecursiveString(_ SeenReferences) string { +func (v *TypeValue) RecursiveString(_ SeenReferences) string { return v.String() } -func (v TypeValue) MeteredString(memoryGauge common.MemoryGauge, _ SeenReferences) string { +func (v *TypeValue) MeteredString(memoryGauge common.MemoryGauge, _ SeenReferences) string { common.UseMemory(memoryGauge, common.TypeValueStringMemoryUsage) var typeString string @@ -365,8 +365,8 @@ func (v TypeValue) MeteredString(memoryGauge common.MemoryGauge, _ SeenReference return format.TypeValue(typeString) } -func (v TypeValue) Equal(_ *Interpreter, _ LocationRange, other Value) bool { - otherTypeValue, ok := other.(TypeValue) +func (v *TypeValue) Equal(_ *Interpreter, _ LocationRange, other Value) bool { + otherTypeValue, ok := other.(*TypeValue) if !ok { return false } @@ -383,7 +383,7 @@ func (v TypeValue) Equal(_ *Interpreter, _ LocationRange, other Value) bool { return staticType.Equal(otherStaticType) } -func (v TypeValue) GetMember(interpreter *Interpreter, _ LocationRange, name string) Value { +func (v *TypeValue) GetMember(interpreter *Interpreter, _ LocationRange, name string) Value { switch name { case sema.MetaTypeIdentifierFieldName: var typeID string @@ -407,7 +407,7 @@ func (v TypeValue) GetMember(interpreter *Interpreter, _ LocationRange, name str interpreter := invocation.Interpreter staticType := v.Type - otherTypeValue, ok := invocation.Arguments[0].(TypeValue) + otherTypeValue, ok := invocation.Arguments[0].(*TypeValue) if !ok { panic(errors.NewUnreachableError()) } @@ -430,17 +430,17 @@ func (v TypeValue) GetMember(interpreter *Interpreter, _ LocationRange, name str return nil } -func (TypeValue) RemoveMember(_ *Interpreter, _ LocationRange, _ string) Value { +func (*TypeValue) RemoveMember(_ *Interpreter, _ LocationRange, _ string) Value { // Types have no removable members (fields / functions) panic(errors.NewUnreachableError()) } -func (TypeValue) SetMember(_ *Interpreter, _ LocationRange, _ string, _ Value) bool { +func (*TypeValue) SetMember(_ *Interpreter, _ LocationRange, _ string, _ Value) bool { // Types have no settable members (fields / functions) panic(errors.NewUnreachableError()) } -func (v TypeValue) ConformsToStaticType( +func (v *TypeValue) ConformsToStaticType( _ *Interpreter, _ LocationRange, _ TypeConformanceResults, @@ -448,7 +448,7 @@ func (v TypeValue) ConformsToStaticType( return true } -func (v TypeValue) Storable( +func (v *TypeValue) Storable( storage atree.SlabStorage, address atree.Address, maxInlineSize uint64, @@ -461,15 +461,15 @@ func (v TypeValue) Storable( ) } -func (TypeValue) NeedsStoreTo(_ atree.Address) bool { +func (*TypeValue) NeedsStoreTo(_ atree.Address) bool { return false } -func (TypeValue) IsResourceKinded(_ *Interpreter) bool { +func (*TypeValue) IsResourceKinded(_ *Interpreter) bool { return false } -func (v TypeValue) Transfer( +func (v *TypeValue) Transfer( interpreter *Interpreter, _ LocationRange, _ atree.Address, @@ -483,30 +483,30 @@ func (v TypeValue) Transfer( return v } -func (v TypeValue) Clone(_ *Interpreter) Value { +func (v *TypeValue) Clone(_ *Interpreter) Value { return v } -func (TypeValue) DeepRemove(_ *Interpreter) { +func (*TypeValue) DeepRemove(_ *Interpreter) { // NO-OP } -func (v TypeValue) ByteSize() uint32 { +func (v *TypeValue) ByteSize() uint32 { return mustStorableSize(v) } -func (v TypeValue) StoredValue(_ atree.SlabStorage) (atree.Value, error) { +func (v *TypeValue) StoredValue(_ atree.SlabStorage) (atree.Value, error) { return v, nil } -func (TypeValue) ChildStorables() []atree.Storable { +func (*TypeValue) ChildStorables() []atree.Storable { return nil } // HashInput returns a byte slice containing: // - HashInputTypeType (1 byte) // - type id (n bytes) -func (v TypeValue) HashInput(interpreter *Interpreter, _ LocationRange, scratch []byte) []byte { +func (v *TypeValue) HashInput(interpreter *Interpreter, _ LocationRange, scratch []byte) []byte { typeID := interpreter.MustConvertStaticToSemaType(v.Type).ID() length := 1 + len(typeID) diff --git a/runtime/interpreter/value_test.go b/runtime/interpreter/value_test.go index d8767d8961..6cd05e056b 100644 --- a/runtime/interpreter/value_test.go +++ b/runtime/interpreter/value_test.go @@ -2329,12 +2329,12 @@ func TestTypeValue_Equal(t *testing.T) { inter := newTestInterpreter(t) require.True(t, - TypeValue{ + (&TypeValue{ Type: PrimitiveStaticTypeString, - }.Equal( + }).Equal( inter, EmptyLocationRange, - TypeValue{ + &TypeValue{ Type: PrimitiveStaticTypeString, }, ), @@ -2348,12 +2348,12 @@ func TestTypeValue_Equal(t *testing.T) { inter := newTestInterpreter(t) require.False(t, - TypeValue{ + (&TypeValue{ Type: PrimitiveStaticTypeString, - }.Equal( + }).Equal( inter, EmptyLocationRange, - TypeValue{ + &TypeValue{ Type: PrimitiveStaticTypeInt, }, ), @@ -2367,9 +2367,9 @@ func TestTypeValue_Equal(t *testing.T) { inter := newTestInterpreter(t) require.False(t, - TypeValue{ + (&TypeValue{ Type: PrimitiveStaticTypeString, - }.Equal( + }).Equal( inter, EmptyLocationRange, NewUnmeteredStringValue("String"), @@ -4688,6 +4688,43 @@ func TestConvertToEntitledValue(t *testing.T) { ), Name: "[&S]", }, + { + Input: NewArrayValue( + inter, + EmptyLocationRange, + NewVariableSizedStaticType(inter, PrimitiveStaticTypeMetaType), + testAddress, + NewTypeValue( + inter, + NewEphemeralReferenceValue( + inter, + UnauthorizedAccess, + sValue, + inter.MustSemaTypeOfValue(sValue), + ).StaticType(inter), + ), + ), + Output: NewArrayValue( + inter, + EmptyLocationRange, + NewVariableSizedStaticType(inter, PrimitiveStaticTypeMetaType), + testAddress, + NewTypeValue( + inter, + NewEphemeralReferenceValue( + inter, + NewEntitlementSetAuthorization( + inter, + []common.TypeID{"S.test.E", "S.test.F"}, + sema.Conjunction, + ), + sValue, + inter.MustSemaTypeOfValue(sValue), + ).StaticType(inter), + ), + ), + Name: "[Type]", + }, { Input: NewDictionaryValue( inter, @@ -4717,6 +4754,40 @@ func TestConvertToEntitledValue(t *testing.T) { ), Name: "{Int: &S}", }, + { + Input: NewDictionaryValue( + inter, + EmptyLocationRange, + NewDictionaryStaticType(inter, PrimitiveStaticTypeInt, PrimitiveStaticTypeMetaType), + NewIntValueFromInt64(inter, 0), + NewTypeValue( + inter, + NewEphemeralReferenceValue( + inter, + UnauthorizedAccess, + sValue, + inter.MustSemaTypeOfValue(sValue), + ).StaticType(inter), + ), + ), + Output: NewDictionaryValue( + inter, + EmptyLocationRange, + NewDictionaryStaticType(inter, PrimitiveStaticTypeInt, PrimitiveStaticTypeMetaType), + NewIntValueFromInt64(inter, 0), + NewTypeValue(inter, + NewEphemeralReferenceValue( + inter, + NewEntitlementSetAuthorization( + inter, + []common.TypeID{"S.test.E", "S.test.F"}, + sema.Conjunction, + ), sValue, inter.MustSemaTypeOfValue(sValue), + ).StaticType(inter), + ), + ), + Name: "{Int: Type}", + }, { Input: NewEphemeralReferenceValue(inter, UnauthorizedAccess, rValue, inter.MustSemaTypeOfValue(rValue)), Output: NewEphemeralReferenceValue( @@ -4852,13 +4923,26 @@ func TestConvertToEntitledValue(t *testing.T) { Output Value Name string } - runtimeTypeTest.Input = NewTypeValue(nil, test.Input.StaticType(inter)) - runtimeTypeTest.Output = NewTypeValue(nil, test.Output.StaticType(inter)) + runtimeTypeTest.Input = NewTypeValue(inter, test.Input.Clone(inter).StaticType(inter)) + runtimeTypeTest.Output = NewTypeValue(inter, test.Output.Clone(inter).StaticType(inter)) runtimeTypeTest.Name = "runtime type " + test.Name tests = append(tests, runtimeTypeTest) } + for _, test := range tests { + var optionalValueTest struct { + Input Value + Output Value + Name string + } + optionalValueTest.Input = NewSomeValueNonCopying(inter, test.Input.Clone(inter)) + optionalValueTest.Output = NewSomeValueNonCopying(inter, test.Output.Clone(inter)) + optionalValueTest.Name = "optional " + test.Name + + tests = append(tests, optionalValueTest) + } + for _, test := range tests { t.Run(test.Name, func(t *testing.T) { switch output := inter.ConvertValueToEntitlements(test.Input).(type) { diff --git a/runtime/interpreter/visitor.go b/runtime/interpreter/visitor.go index 587a899865..c760d4a2aa 100644 --- a/runtime/interpreter/visitor.go +++ b/runtime/interpreter/visitor.go @@ -20,7 +20,7 @@ package interpreter type Visitor interface { VisitSimpleCompositeValue(interpreter *Interpreter, value *SimpleCompositeValue) - VisitTypeValue(interpreter *Interpreter, value TypeValue) + VisitTypeValue(interpreter *Interpreter, value *TypeValue) VisitVoidValue(interpreter *Interpreter, value VoidValue) VisitBoolValue(interpreter *Interpreter, value BoolValue) VisitStringValue(interpreter *Interpreter, value *StringValue) @@ -71,7 +71,7 @@ type Visitor interface { type EmptyVisitor struct { SimpleCompositeValueVisitor func(interpreter *Interpreter, value *SimpleCompositeValue) - TypeValueVisitor func(interpreter *Interpreter, value TypeValue) + TypeValueVisitor func(interpreter *Interpreter, value *TypeValue) VoidValueVisitor func(interpreter *Interpreter, value VoidValue) BoolValueVisitor func(interpreter *Interpreter, value BoolValue) CharacterValueVisitor func(interpreter *Interpreter, value CharacterValue) @@ -129,7 +129,7 @@ func (v EmptyVisitor) VisitSimpleCompositeValue(interpreter *Interpreter, value v.SimpleCompositeValueVisitor(interpreter, value) } -func (v EmptyVisitor) VisitTypeValue(interpreter *Interpreter, value TypeValue) { +func (v EmptyVisitor) VisitTypeValue(interpreter *Interpreter, value *TypeValue) { if v.TypeValueVisitor == nil { return } diff --git a/runtime/stdlib/test_emulatorbackend.go b/runtime/stdlib/test_emulatorbackend.go index 86e9db2038..46af82c6b8 100644 --- a/runtime/stdlib/test_emulatorbackend.go +++ b/runtime/stdlib/test_emulatorbackend.go @@ -646,7 +646,7 @@ func (t *testEmulatorBackendType) newEventsFunction( // Do nothing case *interpreter.SomeValue: innerValue := value.InnerValue(invocation.Interpreter, invocation.LocationRange) - typeValue, ok := innerValue.(interpreter.TypeValue) + typeValue, ok := innerValue.(*interpreter.TypeValue) if !ok { panic(errors.NewUnreachableError()) } diff --git a/runtime/tests/interpreter/account_test.go b/runtime/tests/interpreter/account_test.go index c301e9c9a2..ca84f2c75e 100644 --- a/runtime/tests/interpreter/account_test.go +++ b/runtime/tests/interpreter/account_test.go @@ -327,7 +327,7 @@ func TestInterpretAuthAccount_type(t *testing.T) { require.NoError(t, err) require.Equal(t, interpreter.NewUnmeteredSomeValueNonCopying( - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.CompositeStaticType{ Location: TestLocation, QualifiedIdentifier: "R", @@ -350,7 +350,7 @@ func TestInterpretAuthAccount_type(t *testing.T) { require.NoError(t, err) require.Equal(t, interpreter.NewUnmeteredSomeValueNonCopying( - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.CompositeStaticType{ Location: TestLocation, QualifiedIdentifier: "S", diff --git a/runtime/tests/interpreter/attachments_test.go b/runtime/tests/interpreter/attachments_test.go index bc9cde98d1..596005a72d 100644 --- a/runtime/tests/interpreter/attachments_test.go +++ b/runtime/tests/interpreter/attachments_test.go @@ -1835,8 +1835,8 @@ func TestInterpretAttachmentsRuntimeType(t *testing.T) { a, err := inter.Invoke("test") require.NoError(t, err) - require.IsType(t, interpreter.TypeValue{}, a) - require.Equal(t, "S.test.A", a.(interpreter.TypeValue).Type.String()) + require.IsType(t, &interpreter.TypeValue{}, a) + require.Equal(t, "S.test.A", a.(*interpreter.TypeValue).Type.String()) }) } diff --git a/runtime/tests/interpreter/interpreter_test.go b/runtime/tests/interpreter/interpreter_test.go index a24e7873ef..48966efe28 100644 --- a/runtime/tests/interpreter/interpreter_test.go +++ b/runtime/tests/interpreter/interpreter_test.go @@ -9986,8 +9986,8 @@ func TestHostFunctionStaticType(t *testing.T) { value.StaticType(inter), ) - require.IsType(t, interpreter.TypeValue{}, value) - typeValue := value.(interpreter.TypeValue) + require.IsType(t, &interpreter.TypeValue{}, value) + typeValue := value.(*interpreter.TypeValue) assert.Equal(t, interpreter.PrimitiveStaticTypeInt8, typeValue.Type) }) @@ -10043,7 +10043,7 @@ func TestInterpretArrayTypeInference(t *testing.T) { AssertValuesEqual( t, inter, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.VariableSizedStaticType{ Type: interpreter.PrimitiveStaticTypeAnyStruct, }, @@ -10068,7 +10068,7 @@ func TestInterpretArrayTypeInference(t *testing.T) { AssertValuesEqual( t, inter, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.VariableSizedStaticType{ Type: interpreter.PrimitiveStaticTypeInt, }, @@ -10425,7 +10425,7 @@ func TestInterpretCastingBoxing(t *testing.T) { require.Equal( t, interpreter.NewUnmeteredSomeValueNonCopying( - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.PrimitiveStaticTypeInt, }, ), @@ -10447,7 +10447,7 @@ func TestInterpretCastingBoxing(t *testing.T) { require.Equal( t, interpreter.NewUnmeteredSomeValueNonCopying( - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.PrimitiveStaticTypeInt, }, ), @@ -10469,7 +10469,7 @@ func TestInterpretCastingBoxing(t *testing.T) { require.Equal( t, interpreter.NewUnmeteredSomeValueNonCopying( - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.PrimitiveStaticTypeInt, }, ), diff --git a/runtime/tests/interpreter/metatype_test.go b/runtime/tests/interpreter/metatype_test.go index 8534f1c927..3975e315d6 100644 --- a/runtime/tests/interpreter/metatype_test.go +++ b/runtime/tests/interpreter/metatype_test.go @@ -124,7 +124,7 @@ func TestInterpretMetaTypeEquality(t *testing.T) { valueDeclaration := stdlib.StandardLibraryValue{ Name: "unknownType", Type: sema.MetaType, - Value: interpreter.TypeValue{ + Value: &interpreter.TypeValue{ Type: nil, }, Kind: common.DeclarationKindConstant, @@ -167,7 +167,7 @@ func TestInterpretMetaTypeEquality(t *testing.T) { { Name: "unknownType1", Type: sema.MetaType, - Value: interpreter.TypeValue{ + Value: &interpreter.TypeValue{ Type: nil, }, Kind: common.DeclarationKindConstant, @@ -175,7 +175,7 @@ func TestInterpretMetaTypeEquality(t *testing.T) { { Name: "unknownType2", Type: sema.MetaType, - Value: interpreter.TypeValue{ + Value: &interpreter.TypeValue{ Type: nil, }, Kind: common.DeclarationKindConstant, @@ -264,7 +264,7 @@ func TestInterpretMetaTypeIdentifier(t *testing.T) { { Name: "unknownType", Type: sema.MetaType, - Value: interpreter.TypeValue{ + Value: &interpreter.TypeValue{ Type: nil, }, Kind: common.DeclarationKindConstant, @@ -397,7 +397,7 @@ func TestInterpretIsInstance(t *testing.T) { valueDeclaration := stdlib.StandardLibraryValue{ Name: "unknownType", Type: sema.MetaType, - Value: interpreter.TypeValue{ + Value: &interpreter.TypeValue{ Type: nil, }, Kind: common.DeclarationKindConstant, @@ -537,7 +537,7 @@ func TestInterpretIsSubtype(t *testing.T) { valueDeclaration := stdlib.StandardLibraryValue{ Name: "unknownType", Type: sema.MetaType, - Value: interpreter.TypeValue{ + Value: &interpreter.TypeValue{ Type: nil, }, Kind: common.DeclarationKindConstant, @@ -585,7 +585,7 @@ func TestInterpretGetType(t *testing.T) { return "abc".getType() } `, - result: interpreter.TypeValue{ + result: &interpreter.TypeValue{ Type: interpreter.PrimitiveStaticTypeString, }, }, @@ -596,7 +596,7 @@ func TestInterpretGetType(t *testing.T) { return (1).getType() } `, - result: interpreter.TypeValue{ + result: &interpreter.TypeValue{ Type: interpreter.PrimitiveStaticTypeInt, }, }, @@ -612,7 +612,7 @@ func TestInterpretGetType(t *testing.T) { return res } `, - result: interpreter.TypeValue{ + result: &interpreter.TypeValue{ Type: interpreter.NewCompositeStaticTypeComputeTypeID(nil, TestLocation, "R"), }, }, @@ -630,7 +630,7 @@ func TestInterpretGetType(t *testing.T) { return optRef.getType() } `, - result: interpreter.TypeValue{ + result: &interpreter.TypeValue{ Type: interpreter.OptionalStaticType{ Type: interpreter.ReferenceStaticType{ Authorization: interpreter.NewEntitlementSetAuthorization(nil, []common.TypeID{"S.test.X"}, sema.Conjunction), @@ -653,7 +653,7 @@ func TestInterpretGetType(t *testing.T) { return optRef.getType() } `, - result: interpreter.TypeValue{ + result: &interpreter.TypeValue{ Type: interpreter.OptionalStaticType{ Type: interpreter.ReferenceStaticType{ // Reference was converted @@ -677,7 +677,7 @@ func TestInterpretGetType(t *testing.T) { return optRef.getType() } `, - result: interpreter.TypeValue{ + result: &interpreter.TypeValue{ Type: interpreter.OptionalStaticType{ Type: interpreter.ReferenceStaticType{ Authorization: interpreter.NewEntitlementSetAuthorization(nil, []common.TypeID{"S.test.X"}, sema.Conjunction), @@ -703,7 +703,7 @@ func TestInterpretGetType(t *testing.T) { return optRef.getType() } `, - result: interpreter.TypeValue{ + result: &interpreter.TypeValue{ Type: interpreter.OptionalStaticType{ Type: interpreter.ReferenceStaticType{ // Reference was converted @@ -730,7 +730,7 @@ func TestInterpretGetType(t *testing.T) { return optRef.getType() } `, - result: interpreter.TypeValue{ + result: &interpreter.TypeValue{ Type: interpreter.OptionalStaticType{ Type: interpreter.ReferenceStaticType{ Authorization: interpreter.NewEntitlementSetAuthorization(nil, []common.TypeID{"S.test.X"}, sema.Conjunction), @@ -746,7 +746,7 @@ func TestInterpretGetType(t *testing.T) { return [1, 3].getType() } `, - result: interpreter.TypeValue{ + result: &interpreter.TypeValue{ Type: interpreter.VariableSizedStaticType{ Type: interpreter.PrimitiveStaticTypeInt, }, diff --git a/runtime/tests/interpreter/runtimetype_test.go b/runtime/tests/interpreter/runtimetype_test.go index 18a9c4862e..cc43d2066c 100644 --- a/runtime/tests/interpreter/runtimetype_test.go +++ b/runtime/tests/interpreter/runtimetype_test.go @@ -45,7 +45,7 @@ func TestInterpretOptionalType(t *testing.T) { `) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.OptionalStaticType{ Type: interpreter.PrimitiveStaticTypeString, }, @@ -54,7 +54,7 @@ func TestInterpretOptionalType(t *testing.T) { ) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.OptionalStaticType{ Type: interpreter.PrimitiveStaticTypeInt, }, @@ -63,7 +63,7 @@ func TestInterpretOptionalType(t *testing.T) { ) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.OptionalStaticType{ Type: interpreter.CompositeStaticType{ Location: utils.TestLocation, @@ -76,7 +76,7 @@ func TestInterpretOptionalType(t *testing.T) { ) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.OptionalStaticType{ Type: interpreter.OptionalStaticType{ Type: interpreter.PrimitiveStaticTypeString, @@ -108,7 +108,7 @@ func TestInterpretVariableSizedArrayType(t *testing.T) { `) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.VariableSizedStaticType{ Type: interpreter.PrimitiveStaticTypeString, }, @@ -117,7 +117,7 @@ func TestInterpretVariableSizedArrayType(t *testing.T) { ) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.VariableSizedStaticType{ Type: interpreter.PrimitiveStaticTypeInt, }, @@ -126,7 +126,7 @@ func TestInterpretVariableSizedArrayType(t *testing.T) { ) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.VariableSizedStaticType{ Type: interpreter.CompositeStaticType{ Location: utils.TestLocation, @@ -139,7 +139,7 @@ func TestInterpretVariableSizedArrayType(t *testing.T) { ) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.VariableSizedStaticType{ Type: interpreter.VariableSizedStaticType{ Type: interpreter.PrimitiveStaticTypeString, @@ -170,7 +170,7 @@ func TestInterpretConstantSizedArrayType(t *testing.T) { `) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.ConstantSizedStaticType{ Type: interpreter.PrimitiveStaticTypeString, Size: int64(10), @@ -180,7 +180,7 @@ func TestInterpretConstantSizedArrayType(t *testing.T) { ) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.ConstantSizedStaticType{ Type: interpreter.PrimitiveStaticTypeInt, Size: int64(5), @@ -190,7 +190,7 @@ func TestInterpretConstantSizedArrayType(t *testing.T) { ) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.ConstantSizedStaticType{ Type: interpreter.CompositeStaticType{ Location: utils.TestLocation, @@ -204,7 +204,7 @@ func TestInterpretConstantSizedArrayType(t *testing.T) { ) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.ConstantSizedStaticType{ Type: interpreter.ConstantSizedStaticType{ Type: interpreter.PrimitiveStaticTypeString, @@ -240,7 +240,7 @@ func TestInterpretDictionaryType(t *testing.T) { `) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.DictionaryStaticType{ KeyType: interpreter.PrimitiveStaticTypeString, ValueType: interpreter.PrimitiveStaticTypeInt, @@ -250,7 +250,7 @@ func TestInterpretDictionaryType(t *testing.T) { ) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.DictionaryStaticType{ KeyType: interpreter.PrimitiveStaticTypeInt, ValueType: interpreter.PrimitiveStaticTypeString, @@ -260,7 +260,7 @@ func TestInterpretDictionaryType(t *testing.T) { ) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.DictionaryStaticType{ ValueType: interpreter.CompositeStaticType{ Location: utils.TestLocation, @@ -274,7 +274,7 @@ func TestInterpretDictionaryType(t *testing.T) { ) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.DictionaryStaticType{ ValueType: interpreter.DictionaryStaticType{ KeyType: interpreter.PrimitiveStaticTypeString, @@ -320,7 +320,7 @@ func TestInterpretCompositeType(t *testing.T) { `) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.CompositeStaticType{ QualifiedIdentifier: "R", Location: utils.TestLocation, @@ -331,7 +331,7 @@ func TestInterpretCompositeType(t *testing.T) { ) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.CompositeStaticType{ QualifiedIdentifier: "S", Location: utils.TestLocation, @@ -357,7 +357,7 @@ func TestInterpretCompositeType(t *testing.T) { ) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.CompositeStaticType{ QualifiedIdentifier: "F", Location: utils.TestLocation, @@ -368,7 +368,7 @@ func TestInterpretCompositeType(t *testing.T) { ) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.CompositeStaticType{ QualifiedIdentifier: "PublicKey", Location: nil, @@ -379,7 +379,7 @@ func TestInterpretCompositeType(t *testing.T) { ) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.CompositeStaticType{ QualifiedIdentifier: "HashAlgorithm", Location: nil, @@ -406,7 +406,7 @@ func TestInterpretInterfaceType(t *testing.T) { `) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.InterfaceStaticType{ QualifiedIdentifier: "R", Location: utils.TestLocation, @@ -416,7 +416,7 @@ func TestInterpretInterfaceType(t *testing.T) { ) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.InterfaceStaticType{ QualifiedIdentifier: "S", Location: utils.TestLocation, @@ -449,7 +449,7 @@ func TestInterpretFunctionType(t *testing.T) { `) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.FunctionStaticType{ Type: &sema.FunctionType{ Parameters: []sema.Parameter{ @@ -465,7 +465,7 @@ func TestInterpretFunctionType(t *testing.T) { ) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.FunctionStaticType{ Type: &sema.FunctionType{ Parameters: []sema.Parameter{ @@ -480,7 +480,7 @@ func TestInterpretFunctionType(t *testing.T) { ) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.FunctionStaticType{ Type: &sema.FunctionType{ ReturnTypeAnnotation: sema.StringTypeAnnotation, @@ -513,7 +513,7 @@ func TestInterpretReferenceType(t *testing.T) { `) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.ReferenceStaticType{ ReferencedType: interpreter.CompositeStaticType{ QualifiedIdentifier: "R", @@ -527,7 +527,7 @@ func TestInterpretReferenceType(t *testing.T) { ) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.ReferenceStaticType{ ReferencedType: interpreter.PrimitiveStaticTypeString, Authorization: interpreter.UnauthorizedAccess, @@ -537,7 +537,7 @@ func TestInterpretReferenceType(t *testing.T) { ) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.ReferenceStaticType{ ReferencedType: interpreter.CompositeStaticType{ QualifiedIdentifier: "S", @@ -590,7 +590,7 @@ func TestInterpretIntersectionType(t *testing.T) { `) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: &interpreter.IntersectionStaticType{ Types: []interpreter.InterfaceStaticType{ { @@ -609,7 +609,7 @@ func TestInterpretIntersectionType(t *testing.T) { ) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: &interpreter.IntersectionStaticType{ Types: []interpreter.InterfaceStaticType{ { @@ -628,7 +628,7 @@ func TestInterpretIntersectionType(t *testing.T) { ) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: &interpreter.IntersectionStaticType{ Types: []interpreter.InterfaceStaticType{ { @@ -677,7 +677,7 @@ func TestInterpretCapabilityType(t *testing.T) { `) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.CapabilityStaticType{ BorrowType: interpreter.ReferenceStaticType{ ReferencedType: interpreter.PrimitiveStaticTypeString, @@ -689,7 +689,7 @@ func TestInterpretCapabilityType(t *testing.T) { ) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.CapabilityStaticType{ BorrowType: interpreter.ReferenceStaticType{ ReferencedType: interpreter.PrimitiveStaticTypeInt, @@ -701,7 +701,7 @@ func TestInterpretCapabilityType(t *testing.T) { ) assert.Equal(t, - interpreter.TypeValue{ + &interpreter.TypeValue{ Type: interpreter.CapabilityStaticType{ BorrowType: interpreter.ReferenceStaticType{ ReferencedType: interpreter.CompositeStaticType{ diff --git a/runtime/tests/interpreter/string_test.go b/runtime/tests/interpreter/string_test.go index 44d3f0ee16..826b02f5eb 100644 --- a/runtime/tests/interpreter/string_test.go +++ b/runtime/tests/interpreter/string_test.go @@ -342,7 +342,7 @@ func TestInterpretStringAccess(t *testing.T) { require.NoError(t, err) require.Equal(t, - interpreter.TypeValue{Type: interpreter.PrimitiveStaticTypeCharacter}, + &interpreter.TypeValue{Type: interpreter.PrimitiveStaticTypeCharacter}, result, ) } @@ -362,7 +362,7 @@ func TestInterpretCharacterLiteralType(t *testing.T) { require.NoError(t, err) require.Equal(t, - interpreter.TypeValue{Type: interpreter.PrimitiveStaticTypeCharacter}, + &interpreter.TypeValue{Type: interpreter.PrimitiveStaticTypeCharacter}, result, ) } @@ -382,7 +382,7 @@ func TestInterpretOneCharacterStringLiteralType(t *testing.T) { require.NoError(t, err) require.Equal(t, - interpreter.TypeValue{Type: interpreter.PrimitiveStaticTypeString}, + &interpreter.TypeValue{Type: interpreter.PrimitiveStaticTypeString}, result, ) } @@ -402,7 +402,7 @@ func TestInterpretCharacterLiteralTypeNoAnnotation(t *testing.T) { require.NoError(t, err) require.Equal(t, - interpreter.TypeValue{Type: interpreter.PrimitiveStaticTypeString}, + &interpreter.TypeValue{Type: interpreter.PrimitiveStaticTypeString}, result, ) } From bb650486581336c41d78e8c0a8ab5a5fd28cddef Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Wed, 9 Aug 2023 11:54:11 -0400 Subject: [PATCH 10/40] fix tests --- runtime/interpreter/interpreter.go | 25 ++++++++++--------------- runtime/interpreter/value_test.go | 7 ++++--- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 079eaf49f7..eb58ae9ac1 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -5024,8 +5024,8 @@ func (interpreter *Interpreter) MustConvertStaticAuthorizationToSemaAccess(auth // Converts the input value into a version compatible with the new entitlements feature, // with the same members/operations accessible on any references as would have been accessible in the past. -// Modifies the input `v` in place, and also returns a copy of the value -func (interpreter *Interpreter) ConvertValueToEntitlements(v Value) Value { +// Modifies the input `v` in place +func (interpreter *Interpreter) ConvertValueToEntitlements(v Value) { semaType := interpreter.MustSemaTypeOfValue(v) entitledType := sema.ConvertToEntitledType(interpreter, semaType) @@ -5043,24 +5043,24 @@ func (interpreter *Interpreter) ConvertValueToEntitlements(v Value) Value { v.BorrowedType = entitledReferenceType.Type // stored value is not converted; instead we will convert it upon load case *SomeValue: - v.value = interpreter.ConvertValueToEntitlements(v.value) + interpreter.ConvertValueToEntitlements(v.value) // reset the storable, to be recomputed on next access v.valueStorable = nil case *CompositeValue: // convert all the fields of this composite value to entitlements - v.Walk(interpreter, func(v Value) { interpreter.ConvertValueToEntitlements(v) }) + v.Walk(interpreter, interpreter.ConvertValueToEntitlements) case *ArrayValue: entitledArrayType := entitledType.(sema.ArrayType) v.semaType = entitledArrayType v.Type = ConvertSemaArrayTypeToStaticArrayType(interpreter, entitledArrayType) // convert all the elements of this array value to entitlements - v.Walk(interpreter, func(v Value) { interpreter.ConvertValueToEntitlements(v) }) + v.Walk(interpreter, interpreter.ConvertValueToEntitlements) case *DictionaryValue: entitledDictionaryType := entitledType.(*sema.DictionaryType) v.semaType = entitledDictionaryType v.Type = ConvertSemaDictionaryTypeToStaticDictionaryType(interpreter, entitledDictionaryType) // convert all the elements of this array value to entitlements - v.Walk(interpreter, func(v Value) { interpreter.ConvertValueToEntitlements(v) }) + v.Walk(interpreter, interpreter.ConvertValueToEntitlements) // capabilities should just have their borrow type updated; // we will update their underlying value when the capability is borrowed case *PathCapabilityValue: @@ -5071,22 +5071,17 @@ func (interpreter *Interpreter) ConvertValueToEntitlements(v Value) Value { v.BorrowType = ConvertSemaToStaticType(interpreter, entitledCapabilityValue.BorrowType) case *TypeValue: if v.Type == nil { - return v + return } // convert the static type of the value - return NewTypeValue( + v.Type = ConvertSemaToStaticType( interpreter, - ConvertSemaToStaticType( + sema.ConvertToEntitledType( interpreter, - sema.ConvertToEntitledType( - interpreter, - interpreter.MustConvertStaticToSemaType(v.Type), - ), + interpreter.MustConvertStaticToSemaType(v.Type), ), ) } - - return v } func (interpreter *Interpreter) getElaboration(location common.Location) *sema.Elaboration { diff --git a/runtime/interpreter/value_test.go b/runtime/interpreter/value_test.go index 6cd05e056b..113ca7e7de 100644 --- a/runtime/interpreter/value_test.go +++ b/runtime/interpreter/value_test.go @@ -4945,11 +4945,12 @@ func TestConvertToEntitledValue(t *testing.T) { for _, test := range tests { t.Run(test.Name, func(t *testing.T) { - switch output := inter.ConvertValueToEntitlements(test.Input).(type) { + inter.ConvertValueToEntitlements(test.Input) + switch input := test.Input.(type) { case EquatableValue: - require.True(t, output.Equal(inter, EmptyLocationRange, test.Output)) + require.True(t, input.Equal(inter, EmptyLocationRange, test.Output)) default: - require.Equal(t, output, test.Output) + require.Equal(t, input, test.Output) } }) } From fa700044f42d6f9df7a3421a96dc12524659df07 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Wed, 9 Aug 2023 13:42:25 -0400 Subject: [PATCH 11/40] removed old capability value --- runtime/interpreter/interpreter.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 7ae4511983..ef394a4395 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -4565,9 +4565,6 @@ func (interpreter *Interpreter) ConvertValueToEntitlements(v Value) { v.Walk(interpreter, interpreter.ConvertValueToEntitlements) // capabilities should just have their borrow type updated; // we will update their underlying value when the capability is borrowed - case *PathCapabilityValue: - entitledCapabilityValue := entitledType.(*sema.CapabilityType) - v.BorrowType = ConvertSemaToStaticType(interpreter, entitledCapabilityValue.BorrowType) case *IDCapabilityValue: entitledCapabilityValue := entitledType.(*sema.CapabilityType) v.BorrowType = ConvertSemaToStaticType(interpreter, entitledCapabilityValue.BorrowType) From ba63ef145e486e91b2e04eddc35179f6fa25eb5a Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Thu, 16 Nov 2023 13:31:51 -0500 Subject: [PATCH 12/40] add entitlements migration --- migrations/entitlements/migration.go | 82 +++++ migrations/entitlements/migration_test.go | 360 ++++++++++++++++++++++ 2 files changed, 442 insertions(+) create mode 100644 migrations/entitlements/migration.go create mode 100644 migrations/entitlements/migration_test.go diff --git a/migrations/entitlements/migration.go b/migrations/entitlements/migration.go new file mode 100644 index 0000000000..0c01c24b8e --- /dev/null +++ b/migrations/entitlements/migration.go @@ -0,0 +1,82 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package entitlements + +import ( + "github.com/onflow/cadence/migrations" + "github.com/onflow/cadence/runtime/interpreter" + "github.com/onflow/cadence/runtime/sema" +) + +type EntitlementsMigration struct{} + +var _ migrations.Migration = EntitlementsMigration{} + +func NewAccountTypeMigration() EntitlementsMigration { + return EntitlementsMigration{} +} + +func (EntitlementsMigration) Name() string { + return "AccountTypeMigration" +} + +// Converts its input to an entitled type according to the following rules: +// * `ConvertToEntitledType(&T) ---> auth(Entitlements(T)) &T` +// * `ConvertToEntitledType(Capability) ---> Capability` +// * `ConvertToEntitledType(T?) ---> ConvertToEntitledType(T)? +// * `ConvertToEntitledType(T) ---> T` +// where Entitlements(I) is defined as the result of T.SupportedEntitlements() +func ConvertToEntitledType(t sema.Type) sema.Type { + switch t := t.(type) { + case *sema.ReferenceType: + switch t.Authorization { + case sema.UnauthorizedAccess: + innerType := ConvertToEntitledType(t.Type) + auth := sema.UnauthorizedAccess + if entitlementSupportingType, ok := innerType.(sema.EntitlementSupportingType); ok { + supportedEntitlements := entitlementSupportingType.SupportedEntitlements() + if supportedEntitlements.Len() > 0 { + auth = sema.EntitlementSetAccess{ + SetKind: sema.Conjunction, + Entitlements: supportedEntitlements, + } + } + } + return sema.NewReferenceType( + nil, + auth, + innerType, + ) + // type is already entitled + default: + return t + } + case *sema.OptionalType: + return sema.NewOptionalType(nil, ConvertToEntitledType(t.Type)) + case *sema.CapabilityType: + return sema.NewCapabilityType(nil, ConvertToEntitledType(t.BorrowType)) + default: + return t + } +} + +func (EntitlementsMigration) Migrate(value interpreter.Value) (newValue interpreter.Value) { + + return nil +} diff --git a/migrations/entitlements/migration_test.go b/migrations/entitlements/migration_test.go new file mode 100644 index 0000000000..2df398025b --- /dev/null +++ b/migrations/entitlements/migration_test.go @@ -0,0 +1,360 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package entitlements + +import ( + "testing" + + "github.com/onflow/cadence/runtime/ast" + "github.com/onflow/cadence/runtime/common" + "github.com/stretchr/testify/require" +) + +func TestConvertToEntitledType(t *testing.T) { + + t.Parallel() + + testLocation := common.StringLocation("test") + + entitlementE := NewEntitlementType(nil, testLocation, "E") + entitlementF := NewEntitlementType(nil, testLocation, "F") + entitlementG := NewEntitlementType(nil, testLocation, "G") + + eAccess := NewEntitlementSetAccess([]*EntitlementType{entitlementE}, Conjunction) + fAccess := NewEntitlementSetAccess([]*EntitlementType{entitlementF}, Conjunction) + eOrFAccess := NewEntitlementSetAccess([]*EntitlementType{entitlementE, entitlementF}, Disjunction) + eAndFAccess := NewEntitlementSetAccess([]*EntitlementType{entitlementE, entitlementF}, Conjunction) + eAndGAccess := NewEntitlementSetAccess([]*EntitlementType{entitlementE, entitlementG}, Conjunction) + eFAndGAccess := NewEntitlementSetAccess([]*EntitlementType{entitlementE, entitlementF, entitlementG}, Conjunction) + + mapM := NewEntitlementMapType(nil, testLocation, "M") + mapM.Relations = []EntitlementRelation{ + { + Input: entitlementE, + Output: entitlementF, + }, + { + Input: entitlementF, + Output: entitlementG, + }, + } + mapAccess := NewEntitlementMapAccess(mapM) + + compositeStructWithOnlyE := &CompositeType{ + Location: testLocation, + Identifier: "S", + Kind: common.CompositeKindStructure, + Members: &StringMemberOrderedMap{}, + } + compositeStructWithOnlyE.Members.Set( + "foo", + NewFieldMember(nil, compositeStructWithOnlyE, eAccess, ast.VariableKindConstant, "foo", IntType, ""), + ) + + compositeResourceWithOnlyF := &CompositeType{ + Location: testLocation, + Identifier: "R", + Kind: common.CompositeKindResource, + Members: &StringMemberOrderedMap{}, + } + compositeResourceWithOnlyF.Members.Set( + "bar", + NewFieldMember(nil, compositeResourceWithOnlyF, fAccess, ast.VariableKindConstant, "bar", IntType, ""), + ) + compositeResourceWithOnlyF.Members.Set( + "baz", + NewFieldMember(nil, compositeResourceWithOnlyF, fAccess, ast.VariableKindConstant, "baz", compositeStructWithOnlyE, ""), + ) + + compositeResourceWithEOrF := &CompositeType{ + Location: testLocation, + Identifier: "R", + Kind: common.CompositeKindResource, + Members: &StringMemberOrderedMap{}, + } + compositeResourceWithEOrF.Members.Set( + "qux", + NewFieldMember(nil, compositeResourceWithEOrF, eOrFAccess, ast.VariableKindConstant, "qux", IntType, ""), + ) + + compositeTwoFields := &CompositeType{ + Location: testLocation, + Identifier: "S", + Kind: common.CompositeKindStructure, + Members: &StringMemberOrderedMap{}, + } + compositeTwoFields.Members.Set( + "foo", + NewFieldMember(nil, compositeTwoFields, eAccess, ast.VariableKindConstant, "foo", IntType, ""), + ) + compositeTwoFields.Members.Set( + "bar", + NewFieldMember(nil, compositeTwoFields, fAccess, ast.VariableKindConstant, "bar", IntType, ""), + ) + + interfaceTypeWithEAndG := &InterfaceType{ + Location: testLocation, + Identifier: "I", + CompositeKind: common.CompositeKindResource, + Members: &StringMemberOrderedMap{}, + } + interfaceTypeWithEAndG.Members.Set( + "foo", + NewFunctionMember(nil, interfaceTypeWithEAndG, eAndGAccess, "foo", &FunctionType{}, ""), + ) + + interfaceTypeInheriting := &InterfaceType{ + Location: testLocation, + Identifier: "J", + CompositeKind: common.CompositeKindResource, + Members: &StringMemberOrderedMap{}, + ExplicitInterfaceConformances: []*InterfaceType{interfaceTypeWithEAndG}, + } + + compositeTypeInheriting := &CompositeType{ + Location: testLocation, + Identifier: "RI", + Kind: common.CompositeKindResource, + Members: &StringMemberOrderedMap{}, + ExplicitInterfaceConformances: []*InterfaceType{interfaceTypeInheriting}, + } + + compositeTypeWithMap := &CompositeType{ + Location: testLocation, + Identifier: "RI", + Kind: common.CompositeKindResource, + Members: &StringMemberOrderedMap{}, + } + compositeTypeWithMap.Members.Set( + "foo", + NewFunctionMember(nil, compositeTypeWithMap, mapAccess, "foo", &FunctionType{}, ""), + ) + + interfaceTypeWithMap := &InterfaceType{ + Location: testLocation, + Identifier: "RI", + CompositeKind: common.CompositeKindResource, + Members: &StringMemberOrderedMap{}, + } + interfaceTypeWithMap.Members.Set( + "foo", + NewFunctionMember(nil, interfaceTypeWithMap, mapAccess, "foo", &FunctionType{}, ""), + ) + + compositeTypeWithCapField := &CompositeType{ + Location: testLocation, + Identifier: "RI", + Kind: common.CompositeKindResource, + Members: &StringMemberOrderedMap{}, + } + compositeTypeWithCapField.Members.Set( + "foo", + NewFieldMember( + nil, compositeTypeWithCapField, UnauthorizedAccess, ast.VariableKindConstant, "foo", + NewCapabilityType(nil, + NewReferenceType(nil, interfaceTypeInheriting, UnauthorizedAccess), + ), + "", + ), + ) + + interfaceTypeWithCapField := &InterfaceType{ + Location: testLocation, + Identifier: "RI", + CompositeKind: common.CompositeKindResource, + Members: &StringMemberOrderedMap{}, + } + interfaceTypeWithCapField.Members.Set( + "foo", + NewFieldMember( + nil, interfaceTypeWithCapField, UnauthorizedAccess, ast.VariableKindConstant, "foo", + NewCapabilityType(nil, + NewReferenceType(nil, interfaceTypeInheriting, UnauthorizedAccess), + ), + "", + ), + ) + + interfaceTypeInheritingCapField := &InterfaceType{ + Location: testLocation, + Identifier: "J", + CompositeKind: common.CompositeKindResource, + Members: &StringMemberOrderedMap{}, + ExplicitInterfaceConformances: []*InterfaceType{interfaceTypeWithCapField}, + } + + compositeTypeInheritingCapField := &CompositeType{ + Location: testLocation, + Identifier: "RI", + Kind: common.CompositeKindResource, + Members: &StringMemberOrderedMap{}, + ExplicitInterfaceConformances: []*InterfaceType{interfaceTypeInheritingCapField}, + } + + tests := []struct { + Input Type + Output Type + Name string + }{ + { + Input: NewReferenceType(nil, IntType, UnauthorizedAccess), + Output: NewReferenceType(nil, IntType, UnauthorizedAccess), + Name: "int", + }, + { + Input: NewReferenceType(nil, &FunctionType{}, UnauthorizedAccess), + Output: NewReferenceType(nil, &FunctionType{}, UnauthorizedAccess), + Name: "function", + }, + { + Input: NewReferenceType(nil, compositeStructWithOnlyE, UnauthorizedAccess), + Output: NewReferenceType(nil, compositeStructWithOnlyE, eAccess), + Name: "composite E", + }, + { + Input: NewReferenceType(nil, compositeResourceWithOnlyF, UnauthorizedAccess), + Output: NewReferenceType(nil, compositeResourceWithOnlyF, fAccess), + Name: "composite F", + }, + { + Input: NewReferenceType(nil, compositeResourceWithEOrF, UnauthorizedAccess), + Output: NewReferenceType(nil, compositeResourceWithEOrF, eAndFAccess), + Name: "composite E or F", + }, + { + Input: NewReferenceType(nil, compositeTwoFields, UnauthorizedAccess), + Output: NewReferenceType(nil, compositeTwoFields, eAndFAccess), + Name: "composite E and F", + }, + { + Input: NewReferenceType(nil, interfaceTypeWithEAndG, UnauthorizedAccess), + Output: NewReferenceType(nil, interfaceTypeWithEAndG, eAndGAccess), + Name: "interface E and G", + }, + { + Input: NewReferenceType(nil, interfaceTypeInheriting, UnauthorizedAccess), + Output: NewReferenceType(nil, interfaceTypeInheriting, eAndGAccess), + Name: "interface inheritance", + }, + { + Input: NewReferenceType(nil, compositeTypeInheriting, UnauthorizedAccess), + Output: NewReferenceType(nil, compositeTypeInheriting, eAndGAccess), + Name: "composite inheritance", + }, + { + Input: NewReferenceType(nil, compositeTypeWithMap, UnauthorizedAccess), + Output: NewReferenceType(nil, compositeTypeWithMap, eAndFAccess), + Name: "composite map", + }, + { + Input: NewReferenceType(nil, interfaceTypeWithMap, UnauthorizedAccess), + Output: NewReferenceType(nil, interfaceTypeWithMap, eAndFAccess), + Name: "interface map", + }, + { + Input: NewReferenceType(nil, NewCapabilityType(nil, NewReferenceType(nil, compositeTypeWithMap, UnauthorizedAccess)), UnauthorizedAccess), + Output: NewReferenceType(nil, NewCapabilityType(nil, NewReferenceType(nil, compositeTypeWithMap, eAndFAccess)), UnauthorizedAccess), + Name: "reference to capability", + }, + { + Input: NewReferenceType(nil, NewIntersectionType(nil, []*InterfaceType{interfaceTypeInheriting, interfaceTypeWithMap}), UnauthorizedAccess), + Output: NewReferenceType(nil, NewIntersectionType(nil, []*InterfaceType{interfaceTypeInheriting, interfaceTypeWithMap}), eFAndGAccess), + Name: "intersection", + }, + // no change + { + Input: NewReferenceType(nil, compositeTypeWithCapField, UnauthorizedAccess), + Output: NewReferenceType(nil, compositeTypeWithCapField, UnauthorizedAccess), + Name: "composite with capability field", + }, + // no change + { + Input: NewReferenceType(nil, interfaceTypeWithCapField, UnauthorizedAccess), + Output: NewReferenceType(nil, interfaceTypeWithCapField, UnauthorizedAccess), + Name: "interface with capability field", + }, + // no change + { + Input: NewReferenceType(nil, compositeTypeInheritingCapField, UnauthorizedAccess), + Output: NewReferenceType(nil, compositeTypeInheritingCapField, UnauthorizedAccess), + Name: "composite inheriting capability field", + }, + // no change + { + Input: NewReferenceType(nil, interfaceTypeInheritingCapField, UnauthorizedAccess), + Output: NewReferenceType(nil, interfaceTypeInheritingCapField, UnauthorizedAccess), + Name: "interface inheriting capability field", + }, + // TODO: add tests for array and dictionary entitlements once the mutability changes are merged + } + + // create capability versions of all the existing tests + for _, test := range tests { + var capabilityTest struct { + Input Type + Output Type + Name string + } + capabilityTest.Input = NewCapabilityType(nil, test.Input) + capabilityTest.Output = NewCapabilityType(nil, test.Output) + capabilityTest.Name = "capability " + test.Name + + tests = append(tests, capabilityTest) + } + + // create optional versions of all the existing tests + for _, test := range tests { + var optionalTest struct { + Input Type + Output Type + Name string + } + optionalTest.Input = NewOptionalType(nil, test.Input) + optionalTest.Output = NewOptionalType(nil, test.Output) + optionalTest.Name = "optional " + test.Name + + tests = append(tests, optionalTest) + } + + var compareTypesRecursively func(t *testing.T, expected Type, actual Type) + compareTypesRecursively = func(t *testing.T, expected Type, actual Type) { + require.IsType(t, expected, actual) + + switch expected := expected.(type) { + case *ReferenceType: + actual := actual.(*ReferenceType) + require.IsType(t, expected.Authorization, actual.Authorization) + require.True(t, expected.Authorization.Equal(actual.Authorization)) + compareTypesRecursively(t, expected.Type, actual.Type) + case *OptionalType: + actual := actual.(*OptionalType) + compareTypesRecursively(t, expected.Type, actual.Type) + case *CapabilityType: + actual := actual.(*CapabilityType) + compareTypesRecursively(t, expected.BorrowType, actual.BorrowType) + } + } + + for _, test := range tests { + t.Run(test.Name, func(t *testing.T) { + compareTypesRecursively(t, ConvertToEntitledType(nil, test.Input), test.Output) + }) + } + +} From 4ecf451b3d634641cf2617e36e10e549d790e97d Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Thu, 16 Nov 2023 13:41:22 -0500 Subject: [PATCH 13/40] add implementation and tests for ConvertEntitledType --- migrations/entitlements/migration_test.go | 225 +++++++++++----------- 1 file changed, 113 insertions(+), 112 deletions(-) diff --git a/migrations/entitlements/migration_test.go b/migrations/entitlements/migration_test.go index 2df398025b..8900e13986 100644 --- a/migrations/entitlements/migration_test.go +++ b/migrations/entitlements/migration_test.go @@ -23,6 +23,7 @@ import ( "github.com/onflow/cadence/runtime/ast" "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/runtime/sema" "github.com/stretchr/testify/require" ) @@ -32,19 +33,19 @@ func TestConvertToEntitledType(t *testing.T) { testLocation := common.StringLocation("test") - entitlementE := NewEntitlementType(nil, testLocation, "E") - entitlementF := NewEntitlementType(nil, testLocation, "F") - entitlementG := NewEntitlementType(nil, testLocation, "G") + entitlementE := sema.NewEntitlementType(nil, testLocation, "E") + entitlementF := sema.NewEntitlementType(nil, testLocation, "F") + entitlementG := sema.NewEntitlementType(nil, testLocation, "G") - eAccess := NewEntitlementSetAccess([]*EntitlementType{entitlementE}, Conjunction) - fAccess := NewEntitlementSetAccess([]*EntitlementType{entitlementF}, Conjunction) - eOrFAccess := NewEntitlementSetAccess([]*EntitlementType{entitlementE, entitlementF}, Disjunction) - eAndFAccess := NewEntitlementSetAccess([]*EntitlementType{entitlementE, entitlementF}, Conjunction) - eAndGAccess := NewEntitlementSetAccess([]*EntitlementType{entitlementE, entitlementG}, Conjunction) - eFAndGAccess := NewEntitlementSetAccess([]*EntitlementType{entitlementE, entitlementF, entitlementG}, Conjunction) + eAccess := sema.NewEntitlementSetAccess([]*sema.EntitlementType{entitlementE}, sema.Conjunction) + fAccess := sema.NewEntitlementSetAccess([]*sema.EntitlementType{entitlementF}, sema.Conjunction) + eOrFAccess := sema.NewEntitlementSetAccess([]*sema.EntitlementType{entitlementE, entitlementF}, sema.Disjunction) + eAndFAccess := sema.NewEntitlementSetAccess([]*sema.EntitlementType{entitlementE, entitlementF}, sema.Conjunction) + eAndGAccess := sema.NewEntitlementSetAccess([]*sema.EntitlementType{entitlementE, entitlementG}, sema.Conjunction) + eFAndGAccess := sema.NewEntitlementSetAccess([]*sema.EntitlementType{entitlementE, entitlementF, entitlementG}, sema.Conjunction) - mapM := NewEntitlementMapType(nil, testLocation, "M") - mapM.Relations = []EntitlementRelation{ + mapM := sema.NewEntitlementMapType(nil, testLocation, "M") + mapM.Relations = []sema.EntitlementRelation{ { Input: entitlementE, Output: entitlementF, @@ -54,251 +55,251 @@ func TestConvertToEntitledType(t *testing.T) { Output: entitlementG, }, } - mapAccess := NewEntitlementMapAccess(mapM) + mapAccess := sema.NewEntitlementMapAccess(mapM) - compositeStructWithOnlyE := &CompositeType{ + compositeStructWithOnlyE := &sema.CompositeType{ Location: testLocation, Identifier: "S", Kind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, + Members: &sema.StringMemberOrderedMap{}, } compositeStructWithOnlyE.Members.Set( "foo", - NewFieldMember(nil, compositeStructWithOnlyE, eAccess, ast.VariableKindConstant, "foo", IntType, ""), + sema.NewFieldMember(nil, compositeStructWithOnlyE, eAccess, ast.VariableKindConstant, "foo", sema.IntType, ""), ) - compositeResourceWithOnlyF := &CompositeType{ + compositeResourceWithOnlyF := &sema.CompositeType{ Location: testLocation, Identifier: "R", Kind: common.CompositeKindResource, - Members: &StringMemberOrderedMap{}, + Members: &sema.StringMemberOrderedMap{}, } compositeResourceWithOnlyF.Members.Set( "bar", - NewFieldMember(nil, compositeResourceWithOnlyF, fAccess, ast.VariableKindConstant, "bar", IntType, ""), + sema.NewFieldMember(nil, compositeResourceWithOnlyF, fAccess, ast.VariableKindConstant, "bar", sema.IntType, ""), ) compositeResourceWithOnlyF.Members.Set( "baz", - NewFieldMember(nil, compositeResourceWithOnlyF, fAccess, ast.VariableKindConstant, "baz", compositeStructWithOnlyE, ""), + sema.NewFieldMember(nil, compositeResourceWithOnlyF, fAccess, ast.VariableKindConstant, "baz", compositeStructWithOnlyE, ""), ) - compositeResourceWithEOrF := &CompositeType{ + compositeResourceWithEOrF := &sema.CompositeType{ Location: testLocation, Identifier: "R", Kind: common.CompositeKindResource, - Members: &StringMemberOrderedMap{}, + Members: &sema.StringMemberOrderedMap{}, } compositeResourceWithEOrF.Members.Set( "qux", - NewFieldMember(nil, compositeResourceWithEOrF, eOrFAccess, ast.VariableKindConstant, "qux", IntType, ""), + sema.NewFieldMember(nil, compositeResourceWithEOrF, eOrFAccess, ast.VariableKindConstant, "qux", sema.IntType, ""), ) - compositeTwoFields := &CompositeType{ + compositeTwoFields := &sema.CompositeType{ Location: testLocation, Identifier: "S", Kind: common.CompositeKindStructure, - Members: &StringMemberOrderedMap{}, + Members: &sema.StringMemberOrderedMap{}, } compositeTwoFields.Members.Set( "foo", - NewFieldMember(nil, compositeTwoFields, eAccess, ast.VariableKindConstant, "foo", IntType, ""), + sema.NewFieldMember(nil, compositeTwoFields, eAccess, ast.VariableKindConstant, "foo", sema.IntType, ""), ) compositeTwoFields.Members.Set( "bar", - NewFieldMember(nil, compositeTwoFields, fAccess, ast.VariableKindConstant, "bar", IntType, ""), + sema.NewFieldMember(nil, compositeTwoFields, fAccess, ast.VariableKindConstant, "bar", sema.IntType, ""), ) - interfaceTypeWithEAndG := &InterfaceType{ + interfaceTypeWithEAndG := &sema.InterfaceType{ Location: testLocation, Identifier: "I", CompositeKind: common.CompositeKindResource, - Members: &StringMemberOrderedMap{}, + Members: &sema.StringMemberOrderedMap{}, } interfaceTypeWithEAndG.Members.Set( "foo", - NewFunctionMember(nil, interfaceTypeWithEAndG, eAndGAccess, "foo", &FunctionType{}, ""), + sema.NewFunctionMember(nil, interfaceTypeWithEAndG, eAndGAccess, "foo", &sema.FunctionType{}, ""), ) - interfaceTypeInheriting := &InterfaceType{ + interfaceTypeInheriting := &sema.InterfaceType{ Location: testLocation, Identifier: "J", CompositeKind: common.CompositeKindResource, - Members: &StringMemberOrderedMap{}, - ExplicitInterfaceConformances: []*InterfaceType{interfaceTypeWithEAndG}, + Members: &sema.StringMemberOrderedMap{}, + ExplicitInterfaceConformances: []*sema.InterfaceType{interfaceTypeWithEAndG}, } - compositeTypeInheriting := &CompositeType{ + compositeTypeInheriting := &sema.CompositeType{ Location: testLocation, Identifier: "RI", Kind: common.CompositeKindResource, - Members: &StringMemberOrderedMap{}, - ExplicitInterfaceConformances: []*InterfaceType{interfaceTypeInheriting}, + Members: &sema.StringMemberOrderedMap{}, + ExplicitInterfaceConformances: []*sema.InterfaceType{interfaceTypeInheriting}, } - compositeTypeWithMap := &CompositeType{ + compositeTypeWithMap := &sema.CompositeType{ Location: testLocation, Identifier: "RI", Kind: common.CompositeKindResource, - Members: &StringMemberOrderedMap{}, + Members: &sema.StringMemberOrderedMap{}, } compositeTypeWithMap.Members.Set( "foo", - NewFunctionMember(nil, compositeTypeWithMap, mapAccess, "foo", &FunctionType{}, ""), + sema.NewFunctionMember(nil, compositeTypeWithMap, mapAccess, "foo", &sema.FunctionType{}, ""), ) - interfaceTypeWithMap := &InterfaceType{ + interfaceTypeWithMap := &sema.InterfaceType{ Location: testLocation, Identifier: "RI", CompositeKind: common.CompositeKindResource, - Members: &StringMemberOrderedMap{}, + Members: &sema.StringMemberOrderedMap{}, } interfaceTypeWithMap.Members.Set( "foo", - NewFunctionMember(nil, interfaceTypeWithMap, mapAccess, "foo", &FunctionType{}, ""), + sema.NewFunctionMember(nil, interfaceTypeWithMap, mapAccess, "foo", &sema.FunctionType{}, ""), ) - compositeTypeWithCapField := &CompositeType{ + compositeTypeWithCapField := &sema.CompositeType{ Location: testLocation, Identifier: "RI", Kind: common.CompositeKindResource, - Members: &StringMemberOrderedMap{}, + Members: &sema.StringMemberOrderedMap{}, } compositeTypeWithCapField.Members.Set( "foo", - NewFieldMember( - nil, compositeTypeWithCapField, UnauthorizedAccess, ast.VariableKindConstant, "foo", - NewCapabilityType(nil, - NewReferenceType(nil, interfaceTypeInheriting, UnauthorizedAccess), + sema.NewFieldMember( + nil, compositeTypeWithCapField, sema.UnauthorizedAccess, ast.VariableKindConstant, "foo", + sema.NewCapabilityType(nil, + sema.NewReferenceType(nil, sema.UnauthorizedAccess, interfaceTypeInheriting), ), "", ), ) - interfaceTypeWithCapField := &InterfaceType{ + interfaceTypeWithCapField := &sema.InterfaceType{ Location: testLocation, Identifier: "RI", CompositeKind: common.CompositeKindResource, - Members: &StringMemberOrderedMap{}, + Members: &sema.StringMemberOrderedMap{}, } interfaceTypeWithCapField.Members.Set( "foo", - NewFieldMember( - nil, interfaceTypeWithCapField, UnauthorizedAccess, ast.VariableKindConstant, "foo", - NewCapabilityType(nil, - NewReferenceType(nil, interfaceTypeInheriting, UnauthorizedAccess), + sema.NewFieldMember( + nil, interfaceTypeWithCapField, sema.UnauthorizedAccess, ast.VariableKindConstant, "foo", + sema.NewCapabilityType(nil, + sema.NewReferenceType(nil, sema.UnauthorizedAccess, interfaceTypeInheriting), ), "", ), ) - interfaceTypeInheritingCapField := &InterfaceType{ + interfaceTypeInheritingCapField := &sema.InterfaceType{ Location: testLocation, Identifier: "J", CompositeKind: common.CompositeKindResource, - Members: &StringMemberOrderedMap{}, - ExplicitInterfaceConformances: []*InterfaceType{interfaceTypeWithCapField}, + Members: &sema.StringMemberOrderedMap{}, + ExplicitInterfaceConformances: []*sema.InterfaceType{interfaceTypeWithCapField}, } - compositeTypeInheritingCapField := &CompositeType{ + compositeTypeInheritingCapField := &sema.CompositeType{ Location: testLocation, Identifier: "RI", Kind: common.CompositeKindResource, - Members: &StringMemberOrderedMap{}, - ExplicitInterfaceConformances: []*InterfaceType{interfaceTypeInheritingCapField}, + Members: &sema.StringMemberOrderedMap{}, + ExplicitInterfaceConformances: []*sema.InterfaceType{interfaceTypeInheritingCapField}, } tests := []struct { - Input Type - Output Type + Input sema.Type + Output sema.Type Name string }{ { - Input: NewReferenceType(nil, IntType, UnauthorizedAccess), - Output: NewReferenceType(nil, IntType, UnauthorizedAccess), + Input: sema.NewReferenceType(nil, sema.UnauthorizedAccess, sema.IntType), + Output: sema.NewReferenceType(nil, sema.UnauthorizedAccess, sema.IntType), Name: "int", }, { - Input: NewReferenceType(nil, &FunctionType{}, UnauthorizedAccess), - Output: NewReferenceType(nil, &FunctionType{}, UnauthorizedAccess), + Input: sema.NewReferenceType(nil, sema.UnauthorizedAccess, &sema.FunctionType{}), + Output: sema.NewReferenceType(nil, sema.UnauthorizedAccess, &sema.FunctionType{}), Name: "function", }, { - Input: NewReferenceType(nil, compositeStructWithOnlyE, UnauthorizedAccess), - Output: NewReferenceType(nil, compositeStructWithOnlyE, eAccess), + Input: sema.NewReferenceType(nil, sema.UnauthorizedAccess, compositeStructWithOnlyE), + Output: sema.NewReferenceType(nil, eAccess, compositeStructWithOnlyE), Name: "composite E", }, { - Input: NewReferenceType(nil, compositeResourceWithOnlyF, UnauthorizedAccess), - Output: NewReferenceType(nil, compositeResourceWithOnlyF, fAccess), + Input: sema.NewReferenceType(nil, sema.UnauthorizedAccess, compositeResourceWithOnlyF), + Output: sema.NewReferenceType(nil, fAccess, compositeResourceWithOnlyF), Name: "composite F", }, { - Input: NewReferenceType(nil, compositeResourceWithEOrF, UnauthorizedAccess), - Output: NewReferenceType(nil, compositeResourceWithEOrF, eAndFAccess), + Input: sema.NewReferenceType(nil, sema.UnauthorizedAccess, compositeResourceWithEOrF), + Output: sema.NewReferenceType(nil, eAndFAccess, compositeResourceWithEOrF), Name: "composite E or F", }, { - Input: NewReferenceType(nil, compositeTwoFields, UnauthorizedAccess), - Output: NewReferenceType(nil, compositeTwoFields, eAndFAccess), + Input: sema.NewReferenceType(nil, sema.UnauthorizedAccess, compositeTwoFields), + Output: sema.NewReferenceType(nil, eAndFAccess, compositeTwoFields), Name: "composite E and F", }, { - Input: NewReferenceType(nil, interfaceTypeWithEAndG, UnauthorizedAccess), - Output: NewReferenceType(nil, interfaceTypeWithEAndG, eAndGAccess), + Input: sema.NewReferenceType(nil, sema.UnauthorizedAccess, interfaceTypeWithEAndG), + Output: sema.NewReferenceType(nil, eAndGAccess, interfaceTypeWithEAndG), Name: "interface E and G", }, { - Input: NewReferenceType(nil, interfaceTypeInheriting, UnauthorizedAccess), - Output: NewReferenceType(nil, interfaceTypeInheriting, eAndGAccess), + Input: sema.NewReferenceType(nil, sema.UnauthorizedAccess, interfaceTypeInheriting), + Output: sema.NewReferenceType(nil, eAndGAccess, interfaceTypeInheriting), Name: "interface inheritance", }, { - Input: NewReferenceType(nil, compositeTypeInheriting, UnauthorizedAccess), - Output: NewReferenceType(nil, compositeTypeInheriting, eAndGAccess), + Input: sema.NewReferenceType(nil, sema.UnauthorizedAccess, compositeTypeInheriting), + Output: sema.NewReferenceType(nil, eAndGAccess, compositeTypeInheriting), Name: "composite inheritance", }, { - Input: NewReferenceType(nil, compositeTypeWithMap, UnauthorizedAccess), - Output: NewReferenceType(nil, compositeTypeWithMap, eAndFAccess), + Input: sema.NewReferenceType(nil, sema.UnauthorizedAccess, compositeTypeWithMap), + Output: sema.NewReferenceType(nil, eAndFAccess, compositeTypeWithMap), Name: "composite map", }, { - Input: NewReferenceType(nil, interfaceTypeWithMap, UnauthorizedAccess), - Output: NewReferenceType(nil, interfaceTypeWithMap, eAndFAccess), + Input: sema.NewReferenceType(nil, sema.UnauthorizedAccess, interfaceTypeWithMap), + Output: sema.NewReferenceType(nil, eAndFAccess, interfaceTypeWithMap), Name: "interface map", }, { - Input: NewReferenceType(nil, NewCapabilityType(nil, NewReferenceType(nil, compositeTypeWithMap, UnauthorizedAccess)), UnauthorizedAccess), - Output: NewReferenceType(nil, NewCapabilityType(nil, NewReferenceType(nil, compositeTypeWithMap, eAndFAccess)), UnauthorizedAccess), + Input: sema.NewReferenceType(nil, sema.UnauthorizedAccess, sema.NewCapabilityType(nil, sema.NewReferenceType(nil, sema.UnauthorizedAccess, compositeTypeWithMap))), + Output: sema.NewReferenceType(nil, sema.UnauthorizedAccess, sema.NewCapabilityType(nil, sema.NewReferenceType(nil, eAndFAccess, compositeTypeWithMap))), Name: "reference to capability", }, { - Input: NewReferenceType(nil, NewIntersectionType(nil, []*InterfaceType{interfaceTypeInheriting, interfaceTypeWithMap}), UnauthorizedAccess), - Output: NewReferenceType(nil, NewIntersectionType(nil, []*InterfaceType{interfaceTypeInheriting, interfaceTypeWithMap}), eFAndGAccess), + Input: sema.NewReferenceType(nil, sema.UnauthorizedAccess, sema.NewIntersectionType(nil, []*sema.InterfaceType{interfaceTypeInheriting, interfaceTypeWithMap})), + Output: sema.NewReferenceType(nil, eFAndGAccess, sema.NewIntersectionType(nil, []*sema.InterfaceType{interfaceTypeInheriting, interfaceTypeWithMap})), Name: "intersection", }, // no change { - Input: NewReferenceType(nil, compositeTypeWithCapField, UnauthorizedAccess), - Output: NewReferenceType(nil, compositeTypeWithCapField, UnauthorizedAccess), + Input: sema.NewReferenceType(nil, sema.UnauthorizedAccess, compositeTypeWithCapField), + Output: sema.NewReferenceType(nil, sema.UnauthorizedAccess, compositeTypeWithCapField), Name: "composite with capability field", }, // no change { - Input: NewReferenceType(nil, interfaceTypeWithCapField, UnauthorizedAccess), - Output: NewReferenceType(nil, interfaceTypeWithCapField, UnauthorizedAccess), + Input: sema.NewReferenceType(nil, sema.UnauthorizedAccess, interfaceTypeWithCapField), + Output: sema.NewReferenceType(nil, sema.UnauthorizedAccess, interfaceTypeWithCapField), Name: "interface with capability field", }, // no change { - Input: NewReferenceType(nil, compositeTypeInheritingCapField, UnauthorizedAccess), - Output: NewReferenceType(nil, compositeTypeInheritingCapField, UnauthorizedAccess), + Input: sema.NewReferenceType(nil, sema.UnauthorizedAccess, compositeTypeInheritingCapField), + Output: sema.NewReferenceType(nil, sema.UnauthorizedAccess, compositeTypeInheritingCapField), Name: "composite inheriting capability field", }, // no change { - Input: NewReferenceType(nil, interfaceTypeInheritingCapField, UnauthorizedAccess), - Output: NewReferenceType(nil, interfaceTypeInheritingCapField, UnauthorizedAccess), + Input: sema.NewReferenceType(nil, sema.UnauthorizedAccess, interfaceTypeInheritingCapField), + Output: sema.NewReferenceType(nil, sema.UnauthorizedAccess, interfaceTypeInheritingCapField), Name: "interface inheriting capability field", }, // TODO: add tests for array and dictionary entitlements once the mutability changes are merged @@ -307,12 +308,12 @@ func TestConvertToEntitledType(t *testing.T) { // create capability versions of all the existing tests for _, test := range tests { var capabilityTest struct { - Input Type - Output Type + Input sema.Type + Output sema.Type Name string } - capabilityTest.Input = NewCapabilityType(nil, test.Input) - capabilityTest.Output = NewCapabilityType(nil, test.Output) + capabilityTest.Input = sema.NewCapabilityType(nil, test.Input) + capabilityTest.Output = sema.NewCapabilityType(nil, test.Output) capabilityTest.Name = "capability " + test.Name tests = append(tests, capabilityTest) @@ -321,39 +322,39 @@ func TestConvertToEntitledType(t *testing.T) { // create optional versions of all the existing tests for _, test := range tests { var optionalTest struct { - Input Type - Output Type + Input sema.Type + Output sema.Type Name string } - optionalTest.Input = NewOptionalType(nil, test.Input) - optionalTest.Output = NewOptionalType(nil, test.Output) + optionalTest.Input = sema.NewOptionalType(nil, test.Input) + optionalTest.Output = sema.NewOptionalType(nil, test.Output) optionalTest.Name = "optional " + test.Name tests = append(tests, optionalTest) } - var compareTypesRecursively func(t *testing.T, expected Type, actual Type) - compareTypesRecursively = func(t *testing.T, expected Type, actual Type) { + var compareTypesRecursively func(t *testing.T, expected sema.Type, actual sema.Type) + compareTypesRecursively = func(t *testing.T, expected sema.Type, actual sema.Type) { require.IsType(t, expected, actual) switch expected := expected.(type) { - case *ReferenceType: - actual := actual.(*ReferenceType) + case *sema.ReferenceType: + actual := actual.(*sema.ReferenceType) require.IsType(t, expected.Authorization, actual.Authorization) require.True(t, expected.Authorization.Equal(actual.Authorization)) compareTypesRecursively(t, expected.Type, actual.Type) - case *OptionalType: - actual := actual.(*OptionalType) + case *sema.OptionalType: + actual := actual.(*sema.OptionalType) compareTypesRecursively(t, expected.Type, actual.Type) - case *CapabilityType: - actual := actual.(*CapabilityType) + case *sema.CapabilityType: + actual := actual.(*sema.CapabilityType) compareTypesRecursively(t, expected.BorrowType, actual.BorrowType) } } for _, test := range tests { t.Run(test.Name, func(t *testing.T) { - compareTypesRecursively(t, ConvertToEntitledType(nil, test.Input), test.Output) + compareTypesRecursively(t, ConvertToEntitledType(test.Input), test.Output) }) } From 7533eef68975584681561ee506448b332edd2cad Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Fri, 17 Nov 2023 12:30:17 -0500 Subject: [PATCH 14/40] add tests for array and dict values --- migrations/entitlements/migration.go | 6 +- migrations/entitlements/migration_test.go | 459 ++++++++++++++-------- 2 files changed, 295 insertions(+), 170 deletions(-) diff --git a/migrations/entitlements/migration.go b/migrations/entitlements/migration.go index 0c01c24b8e..d8326fdc15 100644 --- a/migrations/entitlements/migration.go +++ b/migrations/entitlements/migration.go @@ -24,11 +24,13 @@ import ( "github.com/onflow/cadence/runtime/sema" ) -type EntitlementsMigration struct{} +type EntitlementsMigration struct { + Interpreter *interpreter.Interpreter +} var _ migrations.Migration = EntitlementsMigration{} -func NewAccountTypeMigration() EntitlementsMigration { +func NewEntitlementsMigration() EntitlementsMigration { return EntitlementsMigration{} } diff --git a/migrations/entitlements/migration_test.go b/migrations/entitlements/migration_test.go index 92a8753385..75e0a6b8d8 100644 --- a/migrations/entitlements/migration_test.go +++ b/migrations/entitlements/migration_test.go @@ -450,6 +450,54 @@ func TestConvertToEntitledValue(t *testing.T) { nestedValue, err := inter.Invoke("makeNested") require.NoError(t, err) + unentitledSRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, sValue, inter.MustSemaTypeOfValue(sValue)) + unentitledSRefStaticType := unentitledSRef.StaticType(inter) + + entitledSRef := interpreter.NewEphemeralReferenceValue( + inter, + interpreter.NewEntitlementSetAuthorization( + inter, + func() []common.TypeID { return []common.TypeID{"S.test.E", "S.test.F"} }, + 2, + sema.Conjunction, + ), + sValue, + inter.MustSemaTypeOfValue(sValue), + ) + entitledSRefStaticType := entitledSRef.StaticType(inter) + + unentitledRRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, rValue, inter.MustSemaTypeOfValue(rValue)) + unentitledRRefStaticType := unentitledRRef.StaticType(inter) + + entitledRRef := interpreter.NewEphemeralReferenceValue( + inter, + interpreter.NewEntitlementSetAuthorization( + inter, + func() []common.TypeID { return []common.TypeID{"S.test.E", "S.test.G"} }, + 2, + sema.Conjunction, + ), + rValue, + inter.MustSemaTypeOfValue(rValue), + ) + entitledRRefStaticType := entitledRRef.StaticType(inter) + + unentitledNestedRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, nestedValue, inter.MustSemaTypeOfValue(nestedValue)) + unentitledNestedRefStaticType := unentitledNestedRef.StaticType(inter) + + entitledNestedRef := interpreter.NewEphemeralReferenceValue( + inter, + interpreter.NewEntitlementSetAuthorization( + inter, + func() []common.TypeID { return []common.TypeID{"S.test.E", "S.test.F"} }, + 2, + sema.Conjunction, + ), + nestedValue, + inter.MustSemaTypeOfValue(nestedValue), + ) + //entitledNestedRefStaticType := entitledNestedRef.StaticType(inter) + tests := []struct { Input interpreter.Value Output interpreter.Value @@ -471,50 +519,25 @@ func TestConvertToEntitledValue(t *testing.T) { Name: "Nested", }, { - Input: interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, sValue, inter.MustSemaTypeOfValue(sValue)), - Output: interpreter.NewEphemeralReferenceValue( - inter, - interpreter.NewEntitlementSetAuthorization( - inter, - func() []common.TypeID { return []common.TypeID{"S.test.E", "S.test.F"} }, - 2, - sema.Conjunction, - ), - sValue, - inter.MustSemaTypeOfValue(sValue), - ), - Name: "&S", + Input: unentitledSRef, + Output: entitledSRef, + Name: "&S", }, { Input: interpreter.NewArrayValue( inter, interpreter.EmptyLocationRange, - interpreter.NewVariableSizedStaticType(inter, interpreter.NewReferenceStaticType(inter, interpreter.UnauthorizedAccess, sValue.StaticType(inter))), + interpreter.NewVariableSizedStaticType(inter, unentitledSRefStaticType), testAddress, - interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, sValue, inter.MustSemaTypeOfValue(sValue)), + unentitledSRef, ), Output: interpreter.NewArrayValue( inter, interpreter.EmptyLocationRange, - interpreter.NewVariableSizedStaticType( - inter, - interpreter.NewReferenceStaticType(inter, - interpreter.UnauthorizedAccess, - sValue.StaticType(inter), - ), - ), + // TODO: why is this still unentitled? + interpreter.NewVariableSizedStaticType(inter, unentitledSRefStaticType), testAddress, - interpreter.NewEphemeralReferenceValue( - inter, - interpreter.NewEntitlementSetAuthorization( - inter, - func() []common.TypeID { return []common.TypeID{"S.test.E", "S.test.F"} }, - 2, - sema.Conjunction, - ), - sValue, - inter.MustSemaTypeOfValue(sValue), - ), + entitledSRef, ), Name: "[&S]", }, @@ -524,35 +547,14 @@ func TestConvertToEntitledValue(t *testing.T) { interpreter.EmptyLocationRange, interpreter.NewVariableSizedStaticType(inter, interpreter.PrimitiveStaticTypeMetaType), testAddress, - interpreter.NewTypeValue( - inter, - interpreter.NewEphemeralReferenceValue( - inter, - interpreter.UnauthorizedAccess, - sValue, - inter.MustSemaTypeOfValue(sValue), - ).StaticType(inter), - ), + interpreter.NewTypeValue(inter, unentitledSRefStaticType), ), Output: interpreter.NewArrayValue( inter, interpreter.EmptyLocationRange, interpreter.NewVariableSizedStaticType(inter, interpreter.PrimitiveStaticTypeMetaType), testAddress, - interpreter.NewTypeValue( - inter, - interpreter.NewEphemeralReferenceValue( - inter, - interpreter.NewEntitlementSetAuthorization( - inter, - func() []common.TypeID { return []common.TypeID{"S.test.E", "S.test.F"} }, - 2, - sema.Conjunction, - ), - sValue, - inter.MustSemaTypeOfValue(sValue), - ).StaticType(inter), - ), + interpreter.NewTypeValue(inter, entitledSRefStaticType), ), Name: "[Type]", }, @@ -560,29 +562,17 @@ func TestConvertToEntitledValue(t *testing.T) { Input: interpreter.NewDictionaryValue( inter, interpreter.EmptyLocationRange, - interpreter.NewDictionaryStaticType(inter, interpreter.PrimitiveStaticTypeInt, interpreter.NewReferenceStaticType(inter, interpreter.UnauthorizedAccess, sValue.StaticType(inter))), + interpreter.NewDictionaryStaticType(inter, interpreter.PrimitiveStaticTypeInt, unentitledSRefStaticType), interpreter.NewIntValueFromInt64(inter, 0), - interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, sValue, inter.MustSemaTypeOfValue(sValue)), + unentitledSRef, ), Output: interpreter.NewDictionaryValue( inter, interpreter.EmptyLocationRange, - interpreter.NewDictionaryStaticType(inter, interpreter.PrimitiveStaticTypeInt, interpreter.NewReferenceStaticType(inter, - interpreter.UnauthorizedAccess, - sValue.StaticType(inter), - )), + // TODO: why is this still unentitled? + interpreter.NewDictionaryStaticType(inter, interpreter.PrimitiveStaticTypeInt, unentitledSRefStaticType), interpreter.NewIntValueFromInt64(inter, 0), - interpreter.NewEphemeralReferenceValue( - inter, - interpreter.NewEntitlementSetAuthorization( - inter, - func() []common.TypeID { return []common.TypeID{"S.test.E", "S.test.F"} }, - 2, - sema.Conjunction, - ), - sValue, - inter.MustSemaTypeOfValue(sValue), - ), + entitledSRef, ), Name: "{Int: &S}", }, @@ -592,116 +582,60 @@ func TestConvertToEntitledValue(t *testing.T) { interpreter.EmptyLocationRange, interpreter.NewDictionaryStaticType(inter, interpreter.PrimitiveStaticTypeInt, interpreter.PrimitiveStaticTypeMetaType), interpreter.NewIntValueFromInt64(inter, 0), - interpreter.NewTypeValue( - inter, - interpreter.NewEphemeralReferenceValue( - inter, - interpreter.UnauthorizedAccess, - sValue, - inter.MustSemaTypeOfValue(sValue), - ).StaticType(inter), - ), + interpreter.NewTypeValue(inter, unentitledSRefStaticType), ), Output: interpreter.NewDictionaryValue( inter, interpreter.EmptyLocationRange, interpreter.NewDictionaryStaticType(inter, interpreter.PrimitiveStaticTypeInt, interpreter.PrimitiveStaticTypeMetaType), interpreter.NewIntValueFromInt64(inter, 0), - interpreter.NewTypeValue(inter, - interpreter.NewEphemeralReferenceValue( - inter, - interpreter.NewEntitlementSetAuthorization( - inter, - func() []common.TypeID { return []common.TypeID{"S.test.E", "S.test.F"} }, - 2, - sema.Conjunction, - ), sValue, inter.MustSemaTypeOfValue(sValue), - ).StaticType(inter), - ), + interpreter.NewTypeValue(inter, entitledSRefStaticType), ), Name: "{Int: Type}", }, { - Input: interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, rValue, inter.MustSemaTypeOfValue(rValue)), - Output: interpreter.NewEphemeralReferenceValue( - inter, - interpreter.NewEntitlementSetAuthorization( - inter, - func() []common.TypeID { return []common.TypeID{"S.test.E", "S.test.G"} }, - 2, - sema.Conjunction, - ), - rValue, - inter.MustSemaTypeOfValue(rValue), - ), - Name: "&R", + Input: unentitledRRef, + Output: entitledRRef, + Name: "&R", }, { Input: interpreter.NewArrayValue( inter, interpreter.EmptyLocationRange, - interpreter.NewVariableSizedStaticType(inter, interpreter.NewReferenceStaticType(inter, interpreter.UnauthorizedAccess, rValue.StaticType(inter))), + interpreter.NewVariableSizedStaticType(inter, unentitledRRefStaticType), testAddress, - interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, rValue, inter.MustSemaTypeOfValue(rValue)), + unentitledRRef, ), Output: interpreter.NewArrayValue( inter, interpreter.EmptyLocationRange, - interpreter.NewVariableSizedStaticType(inter, interpreter.NewReferenceStaticType(inter, interpreter.UnauthorizedAccess, rValue.StaticType(inter))), + // TODO: why is this still unentitled? + interpreter.NewVariableSizedStaticType(inter, unentitledRRefStaticType), testAddress, - interpreter.NewEphemeralReferenceValue( - inter, - interpreter.NewEntitlementSetAuthorization( - inter, - func() []common.TypeID { return []common.TypeID{"S.test.E", "S.test.G"} }, - 2, - sema.Conjunction, - ), - rValue, - inter.MustSemaTypeOfValue(rValue), - ), + entitledRRef, ), Name: "[&R]", }, { - Input: interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, nestedValue, inter.MustSemaTypeOfValue(nestedValue)), - Output: interpreter.NewEphemeralReferenceValue( - inter, - interpreter.NewEntitlementSetAuthorization( - inter, - func() []common.TypeID { return []common.TypeID{"S.test.E", "S.test.F"} }, - 2, - sema.Conjunction, - ), - nestedValue, - inter.MustSemaTypeOfValue(nestedValue), - ), - Name: "&Nested", + Input: unentitledNestedRef, + Output: entitledNestedRef, + Name: "&Nested", }, { Input: interpreter.NewArrayValue( inter, interpreter.EmptyLocationRange, - interpreter.NewVariableSizedStaticType(inter, interpreter.NewReferenceStaticType(inter, interpreter.UnauthorizedAccess, nestedValue.StaticType(inter))), + interpreter.NewVariableSizedStaticType(inter, unentitledNestedRefStaticType), testAddress, - interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, nestedValue, inter.MustSemaTypeOfValue(nestedValue)), + unentitledNestedRef, ), Output: interpreter.NewArrayValue( inter, interpreter.EmptyLocationRange, - interpreter.NewVariableSizedStaticType(inter, interpreter.NewReferenceStaticType(inter, interpreter.UnauthorizedAccess, nestedValue.StaticType(inter))), + // TODO: why is this still unentitled? + interpreter.NewVariableSizedStaticType(inter, unentitledNestedRefStaticType), testAddress, - interpreter.NewEphemeralReferenceValue( - inter, - interpreter.NewEntitlementSetAuthorization( - inter, - func() []common.TypeID { return []common.TypeID{"S.test.E", "S.test.F"} }, - 2, - sema.Conjunction, - ), - nestedValue, - inter.MustSemaTypeOfValue(nestedValue), - ), + entitledNestedRef, ), Name: "[&Nested]", }, @@ -710,22 +644,13 @@ func TestConvertToEntitledValue(t *testing.T) { inter, 0, interpreter.NewAddressValue(inter, testAddress), - interpreter.NewReferenceStaticType(inter, interpreter.UnauthorizedAccess, sValue.StaticType(inter)), + unentitledSRefStaticType, ), Output: interpreter.NewCapabilityValue( inter, 0, interpreter.NewAddressValue(inter, testAddress), - interpreter.NewReferenceStaticType( - inter, - interpreter.NewEntitlementSetAuthorization( - inter, - func() []common.TypeID { return []common.TypeID{"S.test.E", "S.test.F"} }, - 2, - sema.Conjunction, - ), - sValue.StaticType(inter), - ), + entitledSRefStaticType, ), Name: "Capability<&S>", }, @@ -734,26 +659,181 @@ func TestConvertToEntitledValue(t *testing.T) { inter, 0, interpreter.NewAddressValue(inter, testAddress), - interpreter.NewReferenceStaticType(inter, interpreter.UnauthorizedAccess, rValue.StaticType(inter)), + unentitledRRefStaticType, ), Output: interpreter.NewCapabilityValue( inter, 0, interpreter.NewAddressValue(inter, testAddress), - interpreter.NewReferenceStaticType( + entitledRRefStaticType, + ), + Name: "Capability<&R>", + }, + { + Input: interpreter.NewEphemeralReferenceValue( + inter, + interpreter.UnauthorizedAccess, + interpreter.NewArrayValue( + inter, + interpreter.EmptyLocationRange, + interpreter.NewVariableSizedStaticType(inter, rValue.StaticType(inter)), + testAddress, + rValue.Clone(inter), + ), + sema.NewVariableSizedType(inter, inter.MustSemaTypeOfValue(rValue)), + ), + Output: interpreter.NewEphemeralReferenceValue( + inter, + interpreter.NewEntitlementSetAuthorization( + inter, + func() []common.TypeID { return []common.TypeID{"Mutate", "Insert", "Remove"} }, + 3, + sema.Conjunction, + ), + interpreter.NewArrayValue( + inter, + interpreter.EmptyLocationRange, + interpreter.NewVariableSizedStaticType(inter, rValue.StaticType(inter)), + testAddress, + rValue.Clone(inter), + ), + sema.NewVariableSizedType(inter, inter.MustSemaTypeOfValue(rValue)), + ), + Name: "&[R]", + }, + { + Input: interpreter.NewEphemeralReferenceValue( + inter, + interpreter.UnauthorizedAccess, + interpreter.NewArrayValue( inter, - interpreter.NewEntitlementSetAuthorization( + interpreter.EmptyLocationRange, + interpreter.NewVariableSizedStaticType(inter, unentitledRRefStaticType), + testAddress, + unentitledRRef, + ), + sema.NewVariableSizedType( + inter, + sema.NewReferenceType( inter, - func() []common.TypeID { return []common.TypeID{"S.test.E", "S.test.G"} }, - 2, - sema.Conjunction, + sema.UnauthorizedAccess, + inter.MustSemaTypeOfValue(rValue), ), - rValue.StaticType(inter), ), ), - Name: "Capability<&R>", + Output: interpreter.NewEphemeralReferenceValue( + inter, + interpreter.NewEntitlementSetAuthorization( + inter, + func() []common.TypeID { return []common.TypeID{"Mutate", "Insert", "Remove"} }, + 3, + sema.Conjunction, + ), + interpreter.NewArrayValue( + inter, + interpreter.EmptyLocationRange, + // TODO: why is this still unentitled + interpreter.NewVariableSizedStaticType(inter, unentitledRRefStaticType), + testAddress, + entitledRRef, + ), + sema.NewVariableSizedType( + inter, + sema.NewReferenceType( + inter, + /*sema.NewEntitlementSetAccess( + []*sema.EntitlementType{ + sema.NewEntitlementType(inter, inter.Location, "E"), + sema.NewEntitlementType(inter, inter.Location, "G"), + }, + sema.Conjunction, + ),*/ + // TODO: why is this still unentitled + sema.UnauthorizedAccess, + inter.MustSemaTypeOfValue(rValue), + ), + ), + ), + Name: "&[&R]", + }, + { + Input: interpreter.NewEphemeralReferenceValue( + inter, + interpreter.UnauthorizedAccess, + interpreter.NewDictionaryValue( + inter, + interpreter.EmptyLocationRange, + interpreter.NewDictionaryStaticType(inter, interpreter.PrimitiveStaticTypeInt, rValue.StaticType(inter)), + interpreter.NewIntValueFromInt64(inter, 0), + rValue.Clone(inter), + ), + sema.NewDictionaryType(inter, sema.IntType, inter.MustSemaTypeOfValue(rValue)), + ), + Output: interpreter.NewEphemeralReferenceValue( + inter, + interpreter.NewEntitlementSetAuthorization( + inter, + func() []common.TypeID { return []common.TypeID{"Mutate", "Insert", "Remove"} }, + 3, + sema.Conjunction, + ), + interpreter.NewDictionaryValue( + inter, + interpreter.EmptyLocationRange, + interpreter.NewDictionaryStaticType(inter, interpreter.PrimitiveStaticTypeInt, rValue.StaticType(inter)), + interpreter.NewIntValueFromInt64(inter, 0), + rValue.Clone(inter), + ), + sema.NewDictionaryType(inter, sema.IntType, inter.MustSemaTypeOfValue(rValue)), + ), + Name: "&{Int: R}", + }, + { + Input: interpreter.NewEphemeralReferenceValue( + inter, + interpreter.UnauthorizedAccess, + interpreter.NewDictionaryValue( + inter, + interpreter.EmptyLocationRange, + interpreter.NewDictionaryStaticType(inter, interpreter.PrimitiveStaticTypeInt, unentitledRRefStaticType), + interpreter.NewIntValueFromInt64(inter, 0), + unentitledRRef, + ), + sema.NewDictionaryType(inter, sema.IntType, + sema.NewReferenceType( + inter, + sema.UnauthorizedAccess, + inter.MustSemaTypeOfValue(rValue), + ), + ), + ), + Output: interpreter.NewEphemeralReferenceValue( + inter, + interpreter.NewEntitlementSetAuthorization( + inter, + func() []common.TypeID { return []common.TypeID{"Mutate", "Insert", "Remove"} }, + 3, + sema.Conjunction, + ), + interpreter.NewDictionaryValue( + inter, + interpreter.EmptyLocationRange, + // TODO: why is this still unentitled + interpreter.NewDictionaryStaticType(inter, interpreter.PrimitiveStaticTypeInt, unentitledRRefStaticType), + interpreter.NewIntValueFromInt64(inter, 0), + entitledRRef, + ), + sema.NewDictionaryType(inter, sema.IntType, + sema.NewReferenceType( + inter, + // TODO: why is this still unentitled + sema.UnauthorizedAccess, + inter.MustSemaTypeOfValue(rValue), + ), + ), + ), + Name: "&{Int: &R}", }, - // TODO: after mutability entitlements, add tests for references to arrays and dictionaries } for _, test := range tests { @@ -782,12 +862,55 @@ func TestConvertToEntitledValue(t *testing.T) { tests = append(tests, optionalValueTest) } + var referencePeekingEqual func(interpreter.EquatableValue, interpreter.Value) bool + + // equality that peeks inside referneces to use structural equality for their values + referencePeekingEqual = func(input interpreter.EquatableValue, output interpreter.Value) bool { + switch v := input.(type) { + case *interpreter.SomeValue: + otherSome, ok := output.(*interpreter.SomeValue) + if !ok { + return false + } + + switch innerValue := v.InnerValue(inter, interpreter.EmptyLocationRange).(type) { + case interpreter.EquatableValue: + return referencePeekingEqual( + innerValue, + otherSome.InnerValue(inter, interpreter.EmptyLocationRange), + ) + default: + return innerValue == otherSome.InnerValue(inter, interpreter.EmptyLocationRange) + } + case *interpreter.EphemeralReferenceValue: + otherReference, ok := output.(*interpreter.EphemeralReferenceValue) + if !ok || !v.Authorization.Equal(otherReference.Authorization) { + return false + } + + if v.BorrowedType == nil && otherReference.BorrowedType != nil { + return false + } else if !v.BorrowedType.Equal(otherReference.BorrowedType) { + return false + } + + switch innerValue := v.Value.(type) { + case interpreter.EquatableValue: + return innerValue.Equal(inter, interpreter.EmptyLocationRange, otherReference.Value) + default: + return innerValue == otherReference.Value + } + } + + return input.Equal(inter, interpreter.EmptyLocationRange, output) + } + for _, test := range tests { t.Run(test.Name, func(t *testing.T) { inter.ConvertValueToEntitlements(test.Input, ConvertToEntitledType) switch input := test.Input.(type) { case interpreter.EquatableValue: - require.True(t, input.Equal(inter, interpreter.EmptyLocationRange, test.Output)) + require.True(t, referencePeekingEqual(input, test.Output)) default: require.Equal(t, input, test.Output) } From 6caeeecec62a6f07cdde0dc18b71454ee9d5b536 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Fri, 17 Nov 2023 12:47:14 -0500 Subject: [PATCH 15/40] convert array and dict types --- migrations/entitlements/migration.go | 6 ++++ migrations/entitlements/migration_test.go | 39 +++++++++++------------ runtime/interpreter/interpreter.go | 4 ++- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/migrations/entitlements/migration.go b/migrations/entitlements/migration.go index d8326fdc15..1779301595 100644 --- a/migrations/entitlements/migration.go +++ b/migrations/entitlements/migration.go @@ -73,6 +73,12 @@ func ConvertToEntitledType(t sema.Type) sema.Type { return sema.NewOptionalType(nil, ConvertToEntitledType(t.Type)) case *sema.CapabilityType: return sema.NewCapabilityType(nil, ConvertToEntitledType(t.BorrowType)) + case *sema.VariableSizedType: + return sema.NewVariableSizedType(nil, ConvertToEntitledType(t.Type)) + case *sema.ConstantSizedType: + return sema.NewConstantSizedType(nil, ConvertToEntitledType(t.Type), t.Size) + case *sema.DictionaryType: + return sema.NewDictionaryType(nil, ConvertToEntitledType(t.KeyType), ConvertToEntitledType(t.ValueType)) default: return t } diff --git a/migrations/entitlements/migration_test.go b/migrations/entitlements/migration_test.go index 75e0a6b8d8..317e1394ed 100644 --- a/migrations/entitlements/migration_test.go +++ b/migrations/entitlements/migration_test.go @@ -496,7 +496,7 @@ func TestConvertToEntitledValue(t *testing.T) { nestedValue, inter.MustSemaTypeOfValue(nestedValue), ) - //entitledNestedRefStaticType := entitledNestedRef.StaticType(inter) + entitledNestedRefStaticType := entitledNestedRef.StaticType(inter) tests := []struct { Input interpreter.Value @@ -534,8 +534,7 @@ func TestConvertToEntitledValue(t *testing.T) { Output: interpreter.NewArrayValue( inter, interpreter.EmptyLocationRange, - // TODO: why is this still unentitled? - interpreter.NewVariableSizedStaticType(inter, unentitledSRefStaticType), + interpreter.NewVariableSizedStaticType(inter, entitledSRefStaticType), testAddress, entitledSRef, ), @@ -569,8 +568,7 @@ func TestConvertToEntitledValue(t *testing.T) { Output: interpreter.NewDictionaryValue( inter, interpreter.EmptyLocationRange, - // TODO: why is this still unentitled? - interpreter.NewDictionaryStaticType(inter, interpreter.PrimitiveStaticTypeInt, unentitledSRefStaticType), + interpreter.NewDictionaryStaticType(inter, interpreter.PrimitiveStaticTypeInt, entitledSRefStaticType), interpreter.NewIntValueFromInt64(inter, 0), entitledSRef, ), @@ -609,8 +607,7 @@ func TestConvertToEntitledValue(t *testing.T) { Output: interpreter.NewArrayValue( inter, interpreter.EmptyLocationRange, - // TODO: why is this still unentitled? - interpreter.NewVariableSizedStaticType(inter, unentitledRRefStaticType), + interpreter.NewVariableSizedStaticType(inter, entitledRRefStaticType), testAddress, entitledRRef, ), @@ -632,8 +629,7 @@ func TestConvertToEntitledValue(t *testing.T) { Output: interpreter.NewArrayValue( inter, interpreter.EmptyLocationRange, - // TODO: why is this still unentitled? - interpreter.NewVariableSizedStaticType(inter, unentitledNestedRefStaticType), + interpreter.NewVariableSizedStaticType(inter, entitledNestedRefStaticType), testAddress, entitledNestedRef, ), @@ -732,8 +728,7 @@ func TestConvertToEntitledValue(t *testing.T) { interpreter.NewArrayValue( inter, interpreter.EmptyLocationRange, - // TODO: why is this still unentitled - interpreter.NewVariableSizedStaticType(inter, unentitledRRefStaticType), + interpreter.NewVariableSizedStaticType(inter, entitledRRefStaticType), testAddress, entitledRRef, ), @@ -741,15 +736,13 @@ func TestConvertToEntitledValue(t *testing.T) { inter, sema.NewReferenceType( inter, - /*sema.NewEntitlementSetAccess( + sema.NewEntitlementSetAccess( []*sema.EntitlementType{ - sema.NewEntitlementType(inter, inter.Location, "E"), - sema.NewEntitlementType(inter, inter.Location, "G"), + checker.Elaboration.EntitlementType("S.test.E"), + checker.Elaboration.EntitlementType("S.test.G"), }, sema.Conjunction, - ),*/ - // TODO: why is this still unentitled - sema.UnauthorizedAccess, + ), inter.MustSemaTypeOfValue(rValue), ), ), @@ -818,16 +811,20 @@ func TestConvertToEntitledValue(t *testing.T) { interpreter.NewDictionaryValue( inter, interpreter.EmptyLocationRange, - // TODO: why is this still unentitled - interpreter.NewDictionaryStaticType(inter, interpreter.PrimitiveStaticTypeInt, unentitledRRefStaticType), + interpreter.NewDictionaryStaticType(inter, interpreter.PrimitiveStaticTypeInt, entitledRRefStaticType), interpreter.NewIntValueFromInt64(inter, 0), entitledRRef, ), sema.NewDictionaryType(inter, sema.IntType, sema.NewReferenceType( inter, - // TODO: why is this still unentitled - sema.UnauthorizedAccess, + sema.NewEntitlementSetAccess( + []*sema.EntitlementType{ + checker.Elaboration.EntitlementType("S.test.E"), + checker.Elaboration.EntitlementType("S.test.G"), + }, + sema.Conjunction, + ), inter.MustSemaTypeOfValue(rValue), ), ), diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 6bd5c075bf..ad63b5035e 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -5465,7 +5465,9 @@ func (interpreter *Interpreter) withResourceDestruction( // Converts the input value into a version compatible with the new entitlements feature, // with the same members/operations accessible on any references as would have been accessible in the past. -// Modifies the input `v` in place +// Modifies the input `v` in place. +// This is used for migrations, but must be located in this package because some of the fields we wish to modify are +// privately scoped to this package func (interpreter *Interpreter) ConvertValueToEntitlements(v Value, convertToEntitledType func(sema.Type) sema.Type) { semaType := interpreter.MustSemaTypeOfValue(v) entitledType := convertToEntitledType(semaType) From 4e72efcff97c64c195703d1b9b7a804213fd3734 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Mon, 20 Nov 2023 11:32:43 -0500 Subject: [PATCH 16/40] scaffolding for capabilities and storagereferences --- runtime/interpreter/interpreter.go | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index ad63b5035e..5c88a9a9ab 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -5468,7 +5468,12 @@ func (interpreter *Interpreter) withResourceDestruction( // Modifies the input `v` in place. // This is used for migrations, but must be located in this package because some of the fields we wish to modify are // privately scoped to this package -func (interpreter *Interpreter) ConvertValueToEntitlements(v Value, convertToEntitledType func(sema.Type) sema.Type) { +func (interpreter *Interpreter) ConvertValueToEntitlements( + v Value, + convertToEntitledType func(sema.Type) sema.Type, + loadValue func(PathValue, common.Address) Value, + saveValue func(PathValue, common.Address, Value), +) { semaType := interpreter.MustSemaTypeOfValue(v) entitledType := convertToEntitledType(semaType) @@ -5478,39 +5483,46 @@ func (interpreter *Interpreter) ConvertValueToEntitlements(v Value, convertToEnt staticAuthorization := ConvertSemaAccessToStaticAuthorization(interpreter, entitledReferenceType.Authorization) v.Authorization = staticAuthorization v.BorrowedType = entitledReferenceType.Type - interpreter.ConvertValueToEntitlements(v.Value, convertToEntitledType) + interpreter.ConvertValueToEntitlements(v.Value, convertToEntitledType, loadValue, saveValue) case *StorageReferenceValue: entitledReferenceType := entitledType.(*sema.ReferenceType) staticAuthorization := ConvertSemaAccessToStaticAuthorization(interpreter, entitledReferenceType.Authorization) v.Authorization = staticAuthorization v.BorrowedType = entitledReferenceType.Type - // stored value is not converted; instead we will convert it upon load - // change this + + storedValue := loadValue(v.TargetPath, v.TargetStorageAddress) + interpreter.ConvertValueToEntitlements(storedValue, convertToEntitledType, loadValue, saveValue) + saveValue(v.TargetPath, v.TargetStorageAddress, storedValue) + case *SomeValue: - interpreter.ConvertValueToEntitlements(v.value, convertToEntitledType) + interpreter.ConvertValueToEntitlements(v.value, convertToEntitledType, loadValue, saveValue) // reset the storable, to be recomputed on next access v.valueStorable = nil case *CompositeValue: // convert all the fields of this composite value to entitlements - v.Walk(interpreter, func(v Value) { interpreter.ConvertValueToEntitlements(v, convertToEntitledType) }) + v.Walk(interpreter, func(v Value) { interpreter.ConvertValueToEntitlements(v, convertToEntitledType, loadValue, saveValue) }) case *ArrayValue: entitledArrayType := entitledType.(sema.ArrayType) v.semaType = entitledArrayType v.Type = ConvertSemaArrayTypeToStaticArrayType(interpreter, entitledArrayType) // convert all the elements of this array value to entitlements - v.Walk(interpreter, func(v Value) { interpreter.ConvertValueToEntitlements(v, convertToEntitledType) }) + v.Walk(interpreter, func(v Value) { interpreter.ConvertValueToEntitlements(v, convertToEntitledType, loadValue, saveValue) }) case *DictionaryValue: entitledDictionaryType := entitledType.(*sema.DictionaryType) v.semaType = entitledDictionaryType v.Type = ConvertSemaDictionaryTypeToStaticDictionaryType(interpreter, entitledDictionaryType) // convert all the elements of this array value to entitlements - v.Walk(interpreter, func(v Value) { interpreter.ConvertValueToEntitlements(v, convertToEntitledType) }) + v.Walk(interpreter, func(v Value) { interpreter.ConvertValueToEntitlements(v, convertToEntitledType, loadValue, saveValue) }) // capabilities should just have their borrow type updated; // we will update their underlying value when the capability is borrowed // TODO: fix this case *CapabilityValue: entitledCapabilityValue := entitledType.(*sema.CapabilityType) v.BorrowType = ConvertSemaToStaticType(interpreter, entitledCapabilityValue.BorrowType) + + storedValue := loadValue(v.TargetPath, common.Address(v.Address)) + interpreter.ConvertValueToEntitlements(storedValue, convertToEntitledType, loadValue, saveValue) + saveValue(v.TargetPath, common.Address(v.Address), storedValue) case *TypeValue: if v.Type == nil { return From be445200fc4e3d2d18d47ad75edca17321eb3d52 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Tue, 21 Nov 2023 12:24:55 -0500 Subject: [PATCH 17/40] support for migrating legacy intersection types --- migrations/entitlements/migration_test.go | 131 ++++++++++++++++++++-- runtime/interpreter/interpreter.go | 70 ++++++++---- 2 files changed, 175 insertions(+), 26 deletions(-) diff --git a/migrations/entitlements/migration_test.go b/migrations/entitlements/migration_test.go index 317e1394ed..25920f8924 100644 --- a/migrations/entitlements/migration_test.go +++ b/migrations/entitlements/migration_test.go @@ -304,7 +304,6 @@ func TestConvertToEntitledType(t *testing.T) { Output: sema.NewReferenceType(nil, sema.UnauthorizedAccess, interfaceTypeInheritingCapField), Name: "interface inheriting capability field", }, - // TODO: add tests for array and dictionary entitlements once the mutability changes are merged } // create capability versions of all the existing tests @@ -390,10 +389,22 @@ func TestConvertToEntitledValue(t *testing.T) { } } - access(all) resource R { + access(all) resource interface I { + access(E) let eField: Int + } + + access(all) resource interface J { + access(G) let gField: Int + } + + access(all) resource R: I, J { + access(E) let eField: Int + access(G) let gField: Int access(E, G) let egField: Int init() { self.egField = 0 + self.eField = 1 + self.gField = 2 } } @@ -402,9 +413,6 @@ func TestConvertToEntitledValue(t *testing.T) { init() { self.efField <- create R() } - destroy() { - destroy self.efField - } } access(all) fun makeS(): S { @@ -450,6 +458,8 @@ func TestConvertToEntitledValue(t *testing.T) { nestedValue, err := inter.Invoke("makeNested") require.NoError(t, err) + // &S + unentitledSRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, sValue, inter.MustSemaTypeOfValue(sValue)) unentitledSRefStaticType := unentitledSRef.StaticType(inter) @@ -466,6 +476,8 @@ func TestConvertToEntitledValue(t *testing.T) { ) entitledSRefStaticType := entitledSRef.StaticType(inter) + // &R + unentitledRRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, rValue, inter.MustSemaTypeOfValue(rValue)) unentitledRRefStaticType := unentitledRRef.StaticType(inter) @@ -482,6 +494,83 @@ func TestConvertToEntitledValue(t *testing.T) { ) entitledRRefStaticType := entitledRRef.StaticType(inter) + // &{I} + + intersectionIType := sema.NewIntersectionType(inter, []*sema.InterfaceType{checker.Elaboration.InterfaceType("S.test.I")}) + unentitledIRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, rValue, intersectionIType) + + entitledIRef := interpreter.NewEphemeralReferenceValue( + inter, + interpreter.NewEntitlementSetAuthorization( + inter, + func() []common.TypeID { return []common.TypeID{"S.test.E"} }, + 1, + sema.Conjunction, + ), + rValue, + intersectionIType, + ) + + // legacy Capability<&R{I}> + + legacyIntersectionType := interpreter.ConvertSemaToStaticType(inter, intersectionIType).(*interpreter.IntersectionStaticType) + legacyIntersectionType.LegacyType = rValue.StaticType(inter) + unentitledLegacyReferenceStaticType := interpreter.NewReferenceStaticType( + inter, + interpreter.UnauthorizedAccess, + legacyIntersectionType, + ) + + unentitledLegacyCapability := interpreter.NewCapabilityValue( + inter, + 0, + interpreter.NewAddressValue(inter, testAddress), + unentitledLegacyReferenceStaticType, + ) + + entitledConvertedLegacyReferenceStaticType := interpreter.NewReferenceStaticType( + inter, + interpreter.NewEntitlementSetAuthorization( + inter, + func() []common.TypeID { return []common.TypeID{"S.test.E"} }, + 1, + sema.Conjunction, + ), + rValue.StaticType(inter), + ) + + entitledLegacyConvertedCapability := interpreter.NewCapabilityValue( + inter, + 0, + interpreter.NewAddressValue(inter, testAddress), + entitledConvertedLegacyReferenceStaticType, + ) + + // &{I, J} + + intersectionIJType := sema.NewIntersectionType( + inter, + []*sema.InterfaceType{ + checker.Elaboration.InterfaceType("S.test.I"), + checker.Elaboration.InterfaceType("S.test.J"), + }, + ) + unentitledIJRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, rValue, intersectionIJType) + + entitledIJRef := interpreter.NewEphemeralReferenceValue( + inter, + interpreter.NewEntitlementSetAuthorization( + inter, + func() []common.TypeID { return []common.TypeID{"S.test.E", "S.test.G"} }, + 2, + sema.Conjunction, + ), + rValue, + intersectionIJType, + ) + + // &Nested + unentitledNestedRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, nestedValue, inter.MustSemaTypeOfValue(nestedValue)) unentitledNestedRefStaticType := unentitledNestedRef.StaticType(inter) @@ -596,6 +685,16 @@ func TestConvertToEntitledValue(t *testing.T) { Output: entitledRRef, Name: "&R", }, + { + Input: unentitledIRef, + Output: entitledIRef, + Name: "&{I}", + }, + { + Input: unentitledIJRef, + Output: entitledIJRef, + Name: "&{I, J}", + }, { Input: interpreter.NewArrayValue( inter, @@ -665,6 +764,11 @@ func TestConvertToEntitledValue(t *testing.T) { ), Name: "Capability<&R>", }, + { + Input: unentitledLegacyCapability, + Output: entitledLegacyConvertedCapability, + Name: "Capability<&R{I}>", + }, { Input: interpreter.NewEphemeralReferenceValue( inter, @@ -833,14 +937,27 @@ func TestConvertToEntitledValue(t *testing.T) { }, } + getStaticType := func(v interpreter.Value) interpreter.StaticType { + // for reference types, we want to use the borrow type, rather than the type of the referenced value + if referenceValue, isReferenceValue := v.(*interpreter.EphemeralReferenceValue); isReferenceValue { + return interpreter.NewReferenceStaticType( + inter, + referenceValue.Authorization, + interpreter.ConvertSemaToStaticType(inter, referenceValue.BorrowedType), + ) + } else { + return v.StaticType(inter) + } + } + for _, test := range tests { var runtimeTypeTest struct { Input interpreter.Value Output interpreter.Value Name string } - runtimeTypeTest.Input = interpreter.NewTypeValue(inter, test.Input.Clone(inter).StaticType(inter)) - runtimeTypeTest.Output = interpreter.NewTypeValue(inter, test.Output.Clone(inter).StaticType(inter)) + runtimeTypeTest.Input = interpreter.NewTypeValue(inter, getStaticType(test.Input.Clone(inter))) + runtimeTypeTest.Output = interpreter.NewTypeValue(inter, getStaticType(test.Output.Clone(inter))) runtimeTypeTest.Name = "runtime type " + test.Name tests = append(tests, runtimeTypeTest) diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 95ae5d5c67..d5585a855d 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -5477,10 +5477,50 @@ func (interpreter *Interpreter) withResourceDestruction( func (interpreter *Interpreter) ConvertValueToEntitlements( v Value, convertToEntitledType func(sema.Type) sema.Type, - loadValue func(PathValue, common.Address) Value, - saveValue func(PathValue, common.Address, Value), ) { - semaType := interpreter.MustSemaTypeOfValue(v) + + var staticType StaticType + // for reference types, we want to use the borrow type, rather than the type of the referenced value + if referenceValue, isReferenceValue := v.(*EphemeralReferenceValue); isReferenceValue { + staticType = NewReferenceStaticType( + interpreter, + referenceValue.Authorization, + ConvertSemaToStaticType(interpreter, referenceValue.BorrowedType), + ) + } else { + staticType = v.StaticType(interpreter) + } + + // if the static type contains a legacy restricted type, convert it to a new type according to some rules: + // &T{I} -> auth(SupportedEntitlements(I)) &T + // Capability<&T{I}> -> Capability + var convertLegacyStaticType func(StaticType) + convertLegacyStaticType = func(staticType StaticType) { + switch t := staticType.(type) { + case *ReferenceStaticType: + switch referencedType := t.ReferencedType.(type) { + case *IntersectionStaticType: + if referencedType.LegacyType != nil { + t.ReferencedType = referencedType.LegacyType + intersectionSemaType := interpreter.MustConvertStaticToSemaType(referencedType).(*sema.IntersectionType) + auth := sema.UnauthorizedAccess + supportedEntitlements := intersectionSemaType.SupportedEntitlements() + if supportedEntitlements.Len() > 0 { + auth = sema.EntitlementSetAccess{ + SetKind: sema.Conjunction, + Entitlements: supportedEntitlements, + } + } + t.Authorization = ConvertSemaAccessToStaticAuthorization(interpreter, auth) + } + } + case *CapabilityStaticType: + convertLegacyStaticType(t.BorrowType) + } + } + + convertLegacyStaticType(staticType) + semaType := interpreter.MustConvertStaticToSemaType(staticType) entitledType := convertToEntitledType(semaType) switch v := v.(type) { @@ -5489,46 +5529,38 @@ func (interpreter *Interpreter) ConvertValueToEntitlements( staticAuthorization := ConvertSemaAccessToStaticAuthorization(interpreter, entitledReferenceType.Authorization) v.Authorization = staticAuthorization v.BorrowedType = entitledReferenceType.Type - interpreter.ConvertValueToEntitlements(v.Value, convertToEntitledType, loadValue, saveValue) + interpreter.ConvertValueToEntitlements(v.Value, convertToEntitledType) case *StorageReferenceValue: + // a stored value will in itself be migrated at another point, so no need to do anything here other than change the type entitledReferenceType := entitledType.(*sema.ReferenceType) staticAuthorization := ConvertSemaAccessToStaticAuthorization(interpreter, entitledReferenceType.Authorization) v.Authorization = staticAuthorization v.BorrowedType = entitledReferenceType.Type - storedValue := loadValue(v.TargetPath, v.TargetStorageAddress) - interpreter.ConvertValueToEntitlements(storedValue, convertToEntitledType, loadValue, saveValue) - saveValue(v.TargetPath, v.TargetStorageAddress, storedValue) - case *SomeValue: - interpreter.ConvertValueToEntitlements(v.value, convertToEntitledType, loadValue, saveValue) + interpreter.ConvertValueToEntitlements(v.value, convertToEntitledType) // reset the storable, to be recomputed on next access v.valueStorable = nil case *CompositeValue: // convert all the fields of this composite value to entitlements - v.Walk(interpreter, func(v Value) { interpreter.ConvertValueToEntitlements(v, convertToEntitledType, loadValue, saveValue) }) + v.Walk(interpreter, func(v Value) { interpreter.ConvertValueToEntitlements(v, convertToEntitledType) }) case *ArrayValue: entitledArrayType := entitledType.(sema.ArrayType) v.semaType = entitledArrayType v.Type = ConvertSemaArrayTypeToStaticArrayType(interpreter, entitledArrayType) // convert all the elements of this array value to entitlements - v.Walk(interpreter, func(v Value) { interpreter.ConvertValueToEntitlements(v, convertToEntitledType, loadValue, saveValue) }) + v.Walk(interpreter, func(v Value) { interpreter.ConvertValueToEntitlements(v, convertToEntitledType) }) case *DictionaryValue: entitledDictionaryType := entitledType.(*sema.DictionaryType) v.semaType = entitledDictionaryType v.Type = ConvertSemaDictionaryTypeToStaticDictionaryType(interpreter, entitledDictionaryType) // convert all the elements of this array value to entitlements - v.Walk(interpreter, func(v Value) { interpreter.ConvertValueToEntitlements(v, convertToEntitledType, loadValue, saveValue) }) - // capabilities should just have their borrow type updated; - // we will update their underlying value when the capability is borrowed - // TODO: fix this + v.Walk(interpreter, func(v Value) { interpreter.ConvertValueToEntitlements(v, convertToEntitledType) }) case *CapabilityValue: + // capabilities should just have their borrow type updated, as the pointed-to value will also be visited + // by the migration on its own entitledCapabilityValue := entitledType.(*sema.CapabilityType) v.BorrowType = ConvertSemaToStaticType(interpreter, entitledCapabilityValue.BorrowType) - - storedValue := loadValue(v.TargetPath, common.Address(v.Address)) - interpreter.ConvertValueToEntitlements(storedValue, convertToEntitledType, loadValue, saveValue) - saveValue(v.TargetPath, common.Address(v.Address), storedValue) case *TypeValue: if v.Type == nil { return From 26688cc1c1ba6020e713e4a7b0ec2f786ff52f8e Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Tue, 21 Nov 2023 15:35:39 -0500 Subject: [PATCH 18/40] test for simple migration --- migrations/entitlements/migration.go | 12 +- migrations/entitlements/migration_test.go | 135 +++++++++++++++++++++- runtime/interpreter/interpreter.go | 11 +- 3 files changed, 149 insertions(+), 9 deletions(-) diff --git a/migrations/entitlements/migration.go b/migrations/entitlements/migration.go index 1779301595..735de48e32 100644 --- a/migrations/entitlements/migration.go +++ b/migrations/entitlements/migration.go @@ -30,12 +30,12 @@ type EntitlementsMigration struct { var _ migrations.Migration = EntitlementsMigration{} -func NewEntitlementsMigration() EntitlementsMigration { - return EntitlementsMigration{} +func NewEntitlementsMigration(inter *interpreter.Interpreter) EntitlementsMigration { + return EntitlementsMigration{Interpreter: inter} } func (EntitlementsMigration) Name() string { - return "AccountTypeMigration" + return "EntitlementsMigration" } // Converts its input to an entitled type according to the following rules: @@ -84,7 +84,7 @@ func ConvertToEntitledType(t sema.Type) sema.Type { } } -func (EntitlementsMigration) Migrate(value interpreter.Value) (newValue interpreter.Value) { - - return nil +func (mig EntitlementsMigration) Migrate(value interpreter.Value) (newValue interpreter.Value) { + mig.Interpreter.ConvertValueToEntitlements(value, ConvertToEntitledType) + return value } diff --git a/migrations/entitlements/migration_test.go b/migrations/entitlements/migration_test.go index 25920f8924..742759d679 100644 --- a/migrations/entitlements/migration_test.go +++ b/migrations/entitlements/migration_test.go @@ -21,11 +21,15 @@ package entitlements import ( "testing" + "github.com/onflow/cadence/migrations" + "github.com/onflow/cadence/runtime" "github.com/onflow/cadence/runtime/ast" "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/interpreter" "github.com/onflow/cadence/runtime/sema" checkerUtils "github.com/onflow/cadence/runtime/tests/checker" + "github.com/onflow/cadence/runtime/tests/runtime_utils" + . "github.com/onflow/cadence/runtime/tests/utils" "github.com/stretchr/testify/require" ) @@ -767,7 +771,7 @@ func TestConvertToEntitledValue(t *testing.T) { { Input: unentitledLegacyCapability, Output: entitledLegacyConvertedCapability, - Name: "Capability<&R{I}>", + Name: "Capability<&R{I}> -> Capability", }, { Input: interpreter.NewEphemeralReferenceValue( @@ -1031,3 +1035,132 @@ func TestConvertToEntitledValue(t *testing.T) { }) } } + +func TestMigrateSimpleContract(t *testing.T) { + t.Parallel() + + var uuid uint64 + + account := common.Address{0x42} + ledger := runtime_utils.NewTestLedger(nil, nil) + + type testCase struct { + storedValue interpreter.Value + expectedValue interpreter.Value + } + + storage := runtime.NewStorage(ledger, nil) + + code := ` + access(all) entitlement E + access(all) resource R { + access(E) fun foo() {} + } + access(all) fun makeR(): @R { + return <- create R() + } + ` + checker, err := checkerUtils.ParseAndCheckWithOptions(t, + code, + checkerUtils.ParseAndCheckOptions{}, + ) + + require.NoError(t, err) + + inter, err := interpreter.NewInterpreter( + interpreter.ProgramFromChecker(checker), + checker.Location, + &interpreter.Config{ + Storage: storage, + UUIDHandler: func() (uint64, error) { + uuid++ + return uuid, nil + }, + }, + ) + require.NoError(t, err) + + storageIdentifier := common.PathDomainStorage.Identifier() + + err = inter.Interpret() + require.NoError(t, err) + + rValue, err := inter.Invoke("makeR") + require.NoError(t, err) + + unentitledRRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, rValue, inter.MustSemaTypeOfValue(rValue)) + unentitledRRefStaticType := unentitledRRef.StaticType(inter) + entitledRRef := interpreter.NewEphemeralReferenceValue( + inter, + interpreter.NewEntitlementSetAuthorization( + inter, + func() []common.TypeID { return []common.TypeID{"S.test.E"} }, + 2, + sema.Conjunction, + ), + rValue, + inter.MustSemaTypeOfValue(rValue), + ) + entitledRRefStaticType := entitledRRef.StaticType(inter) + + testCases := map[string]testCase{ + "foo": { + storedValue: interpreter.NewCapabilityValue( + inter, + 0, + interpreter.NewAddressValue(inter, account), + unentitledRRefStaticType, + ), + expectedValue: interpreter.NewCapabilityValue( + inter, + 0, + interpreter.NewAddressValue(inter, account), + entitledRRefStaticType, + ), + }, + } + + for name, testCase := range testCases { + inter.WriteStored( + account, + storageIdentifier, + interpreter.StringStorageMapKey(name), + testCase.storedValue, + ) + } + + err = storage.Commit(inter, true) + require.NoError(t, err) + + // Migrate + + migration := migrations.NewStorageMigration(inter, storage) + migration.Migrate( + &migrations.AddressSliceIterator{ + Addresses: []common.Address{ + account, + }, + }, + nil, + NewEntitlementsMigration(inter), + ) + + storageMap := storage.GetStorageMap(account, storageIdentifier, false) + require.NotNil(t, storageMap) + require.Greater(t, storageMap.Count(), uint64(0)) + + iterator := storageMap.Iterator(inter) + + for key, value := iterator.Next(); key != nil; key, value = iterator.Next() { + identifier := string(key.(interpreter.StringAtreeValue)) + + t.Run(identifier, func(t *testing.T) { + testCase, ok := testCases[identifier] + require.True(t, ok) + + expectedStoredValue := testCase.expectedValue + + AssertValuesEqual(t, inter, expectedStoredValue, value) + }) + } +} diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index d5585a855d..3c6f947c1f 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -5481,13 +5481,20 @@ func (interpreter *Interpreter) ConvertValueToEntitlements( var staticType StaticType // for reference types, we want to use the borrow type, rather than the type of the referenced value - if referenceValue, isReferenceValue := v.(*EphemeralReferenceValue); isReferenceValue { + switch referenceValue := v.(type) { + case *EphemeralReferenceValue: staticType = NewReferenceStaticType( interpreter, referenceValue.Authorization, ConvertSemaToStaticType(interpreter, referenceValue.BorrowedType), ) - } else { + case *StorageReferenceValue: + staticType = NewReferenceStaticType( + interpreter, + referenceValue.Authorization, + ConvertSemaToStaticType(interpreter, referenceValue.BorrowedType), + ) + default: staticType = v.StaticType(interpreter) } From c9a78f586e3f25899774c48b3d77b4d3557163cb Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Mon, 27 Nov 2023 13:57:08 -0500 Subject: [PATCH 19/40] add test for full update flow --- migrations/entitlements/migration_test.go | 294 +++++++++++++++++++++- 1 file changed, 284 insertions(+), 10 deletions(-) diff --git a/migrations/entitlements/migration_test.go b/migrations/entitlements/migration_test.go index 742759d679..176b407f16 100644 --- a/migrations/entitlements/migration_test.go +++ b/migrations/entitlements/migration_test.go @@ -21,6 +21,7 @@ package entitlements import ( "testing" + "github.com/onflow/cadence" "github.com/onflow/cadence/migrations" "github.com/onflow/cadence/runtime" "github.com/onflow/cadence/runtime/ast" @@ -28,7 +29,8 @@ import ( "github.com/onflow/cadence/runtime/interpreter" "github.com/onflow/cadence/runtime/sema" checkerUtils "github.com/onflow/cadence/runtime/tests/checker" - "github.com/onflow/cadence/runtime/tests/runtime_utils" + . "github.com/onflow/cadence/runtime/tests/runtime_utils" + "github.com/onflow/cadence/runtime/tests/utils" . "github.com/onflow/cadence/runtime/tests/utils" "github.com/stretchr/testify/require" ) @@ -1042,7 +1044,7 @@ func TestMigrateSimpleContract(t *testing.T) { var uuid uint64 account := common.Address{0x42} - ledger := runtime_utils.NewTestLedger(nil, nil) + ledger := NewTestLedger(nil, nil) type testCase struct { storedValue interpreter.Value @@ -1056,9 +1058,18 @@ func TestMigrateSimpleContract(t *testing.T) { access(all) resource R { access(E) fun foo() {} } + access(all) resource T { + access(all) let cap: Capability? + init() { + self.cap = nil + } + } access(all) fun makeR(): @R { return <- create R() } + access(all) fun makeT(): @T { + return <- create T() + } ` checker, err := checkerUtils.ParseAndCheckWithOptions(t, code, @@ -1088,29 +1099,46 @@ func TestMigrateSimpleContract(t *testing.T) { rValue, err := inter.Invoke("makeR") require.NoError(t, err) + tValue, err := inter.Invoke("makeT") + require.NoError(t, err) + unentitledRRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, rValue, inter.MustSemaTypeOfValue(rValue)) unentitledRRefStaticType := unentitledRRef.StaticType(inter) + + unentitledRCap := interpreter.NewCapabilityValue( + inter, + 0, + interpreter.NewAddressValue(inter, account), + unentitledRRefStaticType, + ) + entitledRRef := interpreter.NewEphemeralReferenceValue( inter, interpreter.NewEntitlementSetAuthorization( inter, func() []common.TypeID { return []common.TypeID{"S.test.E"} }, - 2, + 1, sema.Conjunction, ), rValue, inter.MustSemaTypeOfValue(rValue), ) entitledRRefStaticType := entitledRRef.StaticType(inter) + entitledRCap := interpreter.NewCapabilityValue( + inter, + 0, + interpreter.NewAddressValue(inter, account), + entitledRRefStaticType, + ) + + tValue.(*interpreter.CompositeValue).SetMember(inter, interpreter.EmptyLocationRange, "cap", unentitledRCap.Clone(inter)) + + expeectedTValue := tValue.Clone(inter) + expeectedTValue.(*interpreter.CompositeValue).SetMember(inter, interpreter.EmptyLocationRange, "cap", entitledRCap.Clone(inter)) testCases := map[string]testCase{ - "foo": { - storedValue: interpreter.NewCapabilityValue( - inter, - 0, - interpreter.NewAddressValue(inter, account), - unentitledRRefStaticType, - ), + "rCap": { + storedValue: unentitledRCap.Clone(inter), expectedValue: interpreter.NewCapabilityValue( inter, 0, @@ -1118,6 +1146,14 @@ func TestMigrateSimpleContract(t *testing.T) { entitledRRefStaticType, ), }, + "rValue": { + storedValue: rValue.Clone(inter), + expectedValue: rValue.Clone(inter), + }, + "tValue": { + storedValue: tValue.Clone(inter), + expectedValue: expeectedTValue.Clone(inter), + }, } for name, testCase := range testCases { @@ -1164,3 +1200,241 @@ func TestMigrateSimpleContract(t *testing.T) { }) } } + +func TestMigrateAcrossContracts(t *testing.T) { + t.Parallel() + + address1 := [8]byte{0, 0, 0, 0, 0, 0, 0, 1} + address2 := [8]byte{0, 0, 0, 0, 0, 0, 0, 2} + + storage := NewTestLedger(nil, nil) + rt := NewTestInterpreterRuntime() + + accountCodes := map[common.Location][]byte{} + interfaces := map[common.Location]*TestRuntimeInterface{} + + runtimeInterface1 := &TestRuntimeInterface{ + Storage: storage, + OnEmitEvent: func(event cadence.Event) error { + return nil + }, + OnGetSigningAccounts: func() ([]runtime.Address, error) { + return []runtime.Address{address1}, nil + }, + OnGetCode: func(location common.Location) (bytes []byte, err error) { + return accountCodes[location], nil + }, + OnResolveLocation: MultipleIdentifierLocationResolver, + OnGetAccountContractCode: func(location common.AddressLocation) (code []byte, err error) { + code = accountCodes[location] + return code, nil + }, + } + runtimeInterface1.OnUpdateAccountContractCode = func(location common.AddressLocation, code []byte) error { + accountCodes[location] = code + interfaces[location] = runtimeInterface1 + return nil + } + + runtimeInterface2 := &TestRuntimeInterface{ + Storage: storage, + OnEmitEvent: func(event cadence.Event) error { + return nil + }, + OnGetCode: func(location common.Location) (bytes []byte, err error) { + return accountCodes[location], nil + }, + OnGetSigningAccounts: func() ([]runtime.Address, error) { + return []runtime.Address{address2}, nil + }, + OnResolveLocation: MultipleIdentifierLocationResolver, + OnGetAccountContractCode: func(location common.AddressLocation) (code []byte, err error) { + code = accountCodes[location] + return code, nil + }, + } + runtimeInterface2.OnUpdateAccountContractCode = func(location common.AddressLocation, code []byte) error { + accountCodes[location] = code + interfaces[location] = runtimeInterface2 + return nil + } + + nextTransactionLocation := NewTransactionLocationGenerator() + + oldContract := []byte(` + access(all) contract C { + access(all) resource R { + access(all) fun foo() {} + } + access(all) resource T { + access(all) let cap: Capability<&R> + init(_ cap: Capability<&R>) { + self.cap = cap + } + } + access(all) fun makeR(): @R { + return <- create R() + } + access(all) fun makeT(_ cap: Capability<&R>): @T { + return <- create T(cap) + } + } + `) + + contract := []byte(` + access(all) contract C { + access(all) entitlement E + access(all) resource R { + access(E) fun foo() {} + } + access(all) resource T { + access(all) let cap: Capability + init(_ cap: Capability) { + self.cap = cap + } + } + access(all) fun makeR(): @R { + return <- create R() + } + access(all) fun makeT(_ cap: Capability): @T { + return <- create T(cap) + } + } + `) + + saveValues := []byte(` + import C from 0x1 + + transaction { + prepare(signer: auth(Storage, Capabilities) &Account) { + let r <- C.makeR() + signer.storage.save(<-r, to: /storage/foo) + let cap = signer.capabilities.storage.issue<&C.R>(/storage/foo) + let t <- C.makeT(cap) + signer.storage.save(<-t, to: /storage/bar) + } + } + `) + + // Deploy contract to 0x1 + err := rt.ExecuteTransaction( + runtime.Script{ + Source: DeploymentTransaction("C", oldContract), + }, + runtime.Context{ + Interface: runtimeInterface1, + Location: nextTransactionLocation(), + }, + ) + require.NoError(t, err) + + // Execute transaction on 0x2 + err = rt.ExecuteTransaction( + runtime.Script{ + Source: saveValues, + }, + runtime.Context{ + Interface: runtimeInterface2, + Location: nextTransactionLocation(), + }, + ) + require.NoError(t, err) + + // update contract on 0x1 + err = rt.ExecuteTransaction( + runtime.Script{ + Source: UpdateTransaction("C", contract), + }, + runtime.Context{ + Interface: runtimeInterface1, + Location: nextTransactionLocation(), + }, + ) + require.NoError(t, err) + + runtimeStorage := runtime.NewStorage(storage, nil) + + inter, err := interpreter.NewInterpreter( + nil, + utils.TestLocation, + &interpreter.Config{ + Storage: runtimeStorage, + AtreeValueValidationEnabled: false, + AtreeStorageValidationEnabled: false, + ImportLocationHandler: func(inter *interpreter.Interpreter, location common.Location) interpreter.Import { + program, err := rt.ParseAndCheckProgram( + accountCodes[location], + runtime.Context{ + Interface: interfaces[location], + Location: location, + }, + ) + require.NoError(t, err) + + subInterpreter, err := inter.NewSubInterpreter(program, location) + require.NoError(t, err) + + return interpreter.InterpreterImport{ + Interpreter: subInterpreter, + } + }, + }, + ) + require.NoError(t, err) + + storageIdentifier := common.PathDomainStorage.Identifier() + storageMap := runtimeStorage.GetStorageMap(address2, storageIdentifier, false) + require.NotNil(t, storageMap) + require.Greater(t, storageMap.Count(), uint64(0)) + + value := storageMap.ReadValue(nil, interpreter.StringStorageMapKey("bar")) + + require.IsType(t, &interpreter.CompositeValue{}, value) + tValue := value.(*interpreter.CompositeValue) + require.Equal(t, "C.T", tValue.QualifiedIdentifier) + + field := tValue.GetMember(inter, interpreter.EmptyLocationRange, "cap") + + require.IsType(t, &interpreter.CapabilityValue{}, field) + cap := field.(*interpreter.CapabilityValue) + require.IsType(t, &interpreter.ReferenceStaticType{}, cap.BorrowType) + ref := cap.BorrowType.(*interpreter.ReferenceStaticType) + ref.Authorization = interpreter.UnauthorizedAccess + + // Migrate + + migration := migrations.NewStorageMigration(inter, runtimeStorage) + migration.Migrate( + &migrations.AddressSliceIterator{ + Addresses: []common.Address{ + address1, + address2, + }, + }, + nil, + NewEntitlementsMigration(inter), + ) + + value = storageMap.ReadValue(nil, interpreter.StringStorageMapKey("bar")) + + require.IsType(t, &interpreter.CompositeValue{}, value) + tValue = value.(*interpreter.CompositeValue) + require.Equal(t, "C.T", tValue.QualifiedIdentifier) + + field = tValue.GetMember(inter, interpreter.EmptyLocationRange, "cap") + + require.IsType(t, &interpreter.CapabilityValue{}, field) + cap = field.(*interpreter.CapabilityValue) + require.IsType(t, &interpreter.ReferenceStaticType{}, cap.BorrowType) + ref = cap.BorrowType.(*interpreter.ReferenceStaticType) + require.Equal(t, + interpreter.NewEntitlementSetAuthorization( + inter, + func() []common.TypeID { return []common.TypeID{"A.0000000000000001.C.E"} }, + 1, + sema.Conjunction, + ), + ref.Authorization, + ) + +} From 6193e44f1239728403d6cfe0667f9d11947ab4ea Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Mon, 27 Nov 2023 14:12:52 -0500 Subject: [PATCH 20/40] improve test --- migrations/entitlements/migration_test.go | 24 +++++------------------ 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/migrations/entitlements/migration_test.go b/migrations/entitlements/migration_test.go index 176b407f16..5cdfd1ca61 100644 --- a/migrations/entitlements/migration_test.go +++ b/migrations/entitlements/migration_test.go @@ -1387,20 +1387,6 @@ func TestMigrateAcrossContracts(t *testing.T) { require.NotNil(t, storageMap) require.Greater(t, storageMap.Count(), uint64(0)) - value := storageMap.ReadValue(nil, interpreter.StringStorageMapKey("bar")) - - require.IsType(t, &interpreter.CompositeValue{}, value) - tValue := value.(*interpreter.CompositeValue) - require.Equal(t, "C.T", tValue.QualifiedIdentifier) - - field := tValue.GetMember(inter, interpreter.EmptyLocationRange, "cap") - - require.IsType(t, &interpreter.CapabilityValue{}, field) - cap := field.(*interpreter.CapabilityValue) - require.IsType(t, &interpreter.ReferenceStaticType{}, cap.BorrowType) - ref := cap.BorrowType.(*interpreter.ReferenceStaticType) - ref.Authorization = interpreter.UnauthorizedAccess - // Migrate migration := migrations.NewStorageMigration(inter, runtimeStorage) @@ -1415,18 +1401,18 @@ func TestMigrateAcrossContracts(t *testing.T) { NewEntitlementsMigration(inter), ) - value = storageMap.ReadValue(nil, interpreter.StringStorageMapKey("bar")) + value := storageMap.ReadValue(nil, interpreter.StringStorageMapKey("bar")) require.IsType(t, &interpreter.CompositeValue{}, value) - tValue = value.(*interpreter.CompositeValue) + tValue := value.(*interpreter.CompositeValue) require.Equal(t, "C.T", tValue.QualifiedIdentifier) - field = tValue.GetMember(inter, interpreter.EmptyLocationRange, "cap") + field := tValue.GetMember(inter, interpreter.EmptyLocationRange, "cap") require.IsType(t, &interpreter.CapabilityValue{}, field) - cap = field.(*interpreter.CapabilityValue) + cap := field.(*interpreter.CapabilityValue) require.IsType(t, &interpreter.ReferenceStaticType{}, cap.BorrowType) - ref = cap.BorrowType.(*interpreter.ReferenceStaticType) + ref := cap.BorrowType.(*interpreter.ReferenceStaticType) require.Equal(t, interpreter.NewEntitlementSetAuthorization( inter, From f201835d34bfcdd60b2332ddb6f0e96b218f9b65 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Fri, 1 Dec 2023 15:51:04 -0500 Subject: [PATCH 21/40] also walk container static types when removing restricted types --- migrations/entitlements/migration_test.go | 25 +++++++++++++++++++++++ runtime/interpreter/interpreter.go | 9 ++++++++ 2 files changed, 34 insertions(+) diff --git a/migrations/entitlements/migration_test.go b/migrations/entitlements/migration_test.go index 5cdfd1ca61..80403b8f37 100644 --- a/migrations/entitlements/migration_test.go +++ b/migrations/entitlements/migration_test.go @@ -534,6 +534,16 @@ func TestConvertToEntitledValue(t *testing.T) { unentitledLegacyReferenceStaticType, ) + unentitledLegacyCapabilityArray := interpreter.NewArrayValue( + inter, + interpreter.EmptyLocationRange, + interpreter.NewVariableSizedStaticType(inter, unentitledLegacyCapability.StaticType(inter)), + testAddress, + unentitledLegacyCapability, + ) + + unentitledLegacyCapabilityOptionalArray := interpreter.NewSomeValueNonCopying(inter, unentitledLegacyCapabilityArray) + entitledConvertedLegacyReferenceStaticType := interpreter.NewReferenceStaticType( inter, interpreter.NewEntitlementSetAuthorization( @@ -552,6 +562,16 @@ func TestConvertToEntitledValue(t *testing.T) { entitledConvertedLegacyReferenceStaticType, ) + entitledLegacyConvertedCapabilityArray := interpreter.NewArrayValue( + inter, + interpreter.EmptyLocationRange, + interpreter.NewVariableSizedStaticType(inter, entitledLegacyConvertedCapability.StaticType(inter)), + testAddress, + entitledLegacyConvertedCapability, + ) + + entitledLegacyConvertedCapabilityOptionalArray := interpreter.NewSomeValueNonCopying(inter, entitledLegacyConvertedCapabilityArray) + // &{I, J} intersectionIJType := sema.NewIntersectionType( @@ -775,6 +795,11 @@ func TestConvertToEntitledValue(t *testing.T) { Output: entitledLegacyConvertedCapability, Name: "Capability<&R{I}> -> Capability", }, + { + Input: unentitledLegacyCapabilityOptionalArray, + Output: entitledLegacyConvertedCapabilityOptionalArray, + Name: "[Capability<&R{I}>]? -> [Capability]?", + }, { Input: interpreter.NewEphemeralReferenceValue( inter, diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 3c6f947c1f..bce3e31844 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -5523,6 +5523,15 @@ func (interpreter *Interpreter) ConvertValueToEntitlements( } case *CapabilityStaticType: convertLegacyStaticType(t.BorrowType) + case *VariableSizedStaticType: + convertLegacyStaticType(t.Type) + case *ConstantSizedStaticType: + convertLegacyStaticType(t.Type) + case *DictionaryStaticType: + convertLegacyStaticType(t.KeyType) + convertLegacyStaticType(t.ValueType) + case *OptionalStaticType: + convertLegacyStaticType(t.Type) } } From c465ab0846d4716ea75bff01b04c7d99917a205e Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Mon, 4 Dec 2023 10:49:21 -0500 Subject: [PATCH 22/40] fix test --- migrations/entitlements/migration_test.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/migrations/entitlements/migration_test.go b/migrations/entitlements/migration_test.go index 80403b8f37..4b030fb60a 100644 --- a/migrations/entitlements/migration_test.go +++ b/migrations/entitlements/migration_test.go @@ -791,13 +791,8 @@ func TestConvertToEntitledValue(t *testing.T) { Name: "Capability<&R>", }, { - Input: unentitledLegacyCapability, - Output: entitledLegacyConvertedCapability, - Name: "Capability<&R{I}> -> Capability", - }, - { - Input: unentitledLegacyCapabilityOptionalArray, - Output: entitledLegacyConvertedCapabilityOptionalArray, + Input: unentitledLegacyCapabilityOptionalArray.Clone(inter), + Output: entitledLegacyConvertedCapabilityOptionalArray.Clone(inter), Name: "[Capability<&R{I}>]? -> [Capability]?", }, { From 6aaa0238993bf40b937563526756e9a922af81e7 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Mon, 11 Dec 2023 16:36:50 -0500 Subject: [PATCH 23/40] update convertValue to create a new value --- migrations/entitlements/migration.go | 3 +- migrations/entitlements/migration_test.go | 26 ++++++++--- runtime/interpreter/interpreter.go | 57 +++++++++++++---------- 3 files changed, 54 insertions(+), 32 deletions(-) diff --git a/migrations/entitlements/migration.go b/migrations/entitlements/migration.go index 735de48e32..06e7f9200d 100644 --- a/migrations/entitlements/migration.go +++ b/migrations/entitlements/migration.go @@ -85,6 +85,5 @@ func ConvertToEntitledType(t sema.Type) sema.Type { } func (mig EntitlementsMigration) Migrate(value interpreter.Value) (newValue interpreter.Value) { - mig.Interpreter.ConvertValueToEntitlements(value, ConvertToEntitledType) - return value + return mig.Interpreter.ConvertValueToEntitlements(value, ConvertToEntitledType) } diff --git a/migrations/entitlements/migration_test.go b/migrations/entitlements/migration_test.go index 4b030fb60a..f29459100a 100644 --- a/migrations/entitlements/migration_test.go +++ b/migrations/entitlements/migration_test.go @@ -466,7 +466,7 @@ func TestConvertToEntitledValue(t *testing.T) { // &S - unentitledSRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, sValue, inter.MustSemaTypeOfValue(sValue)) + unentitledSRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, sValue, inter.MustSemaTypeOfValue(sValue), interpreter.EmptyLocationRange) unentitledSRefStaticType := unentitledSRef.StaticType(inter) entitledSRef := interpreter.NewEphemeralReferenceValue( @@ -479,12 +479,13 @@ func TestConvertToEntitledValue(t *testing.T) { ), sValue, inter.MustSemaTypeOfValue(sValue), + interpreter.EmptyLocationRange, ) entitledSRefStaticType := entitledSRef.StaticType(inter) // &R - unentitledRRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, rValue, inter.MustSemaTypeOfValue(rValue)) + unentitledRRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, rValue, inter.MustSemaTypeOfValue(rValue), interpreter.EmptyLocationRange) unentitledRRefStaticType := unentitledRRef.StaticType(inter) entitledRRef := interpreter.NewEphemeralReferenceValue( @@ -497,13 +498,14 @@ func TestConvertToEntitledValue(t *testing.T) { ), rValue, inter.MustSemaTypeOfValue(rValue), + interpreter.EmptyLocationRange, ) entitledRRefStaticType := entitledRRef.StaticType(inter) // &{I} intersectionIType := sema.NewIntersectionType(inter, []*sema.InterfaceType{checker.Elaboration.InterfaceType("S.test.I")}) - unentitledIRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, rValue, intersectionIType) + unentitledIRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, rValue, intersectionIType, interpreter.EmptyLocationRange) entitledIRef := interpreter.NewEphemeralReferenceValue( inter, @@ -515,6 +517,7 @@ func TestConvertToEntitledValue(t *testing.T) { ), rValue, intersectionIType, + interpreter.EmptyLocationRange, ) // legacy Capability<&R{I}> @@ -581,7 +584,7 @@ func TestConvertToEntitledValue(t *testing.T) { checker.Elaboration.InterfaceType("S.test.J"), }, ) - unentitledIJRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, rValue, intersectionIJType) + unentitledIJRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, rValue, intersectionIJType, interpreter.EmptyLocationRange) entitledIJRef := interpreter.NewEphemeralReferenceValue( inter, @@ -593,11 +596,12 @@ func TestConvertToEntitledValue(t *testing.T) { ), rValue, intersectionIJType, + interpreter.EmptyLocationRange, ) // &Nested - unentitledNestedRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, nestedValue, inter.MustSemaTypeOfValue(nestedValue)) + unentitledNestedRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, nestedValue, inter.MustSemaTypeOfValue(nestedValue), interpreter.EmptyLocationRange) unentitledNestedRefStaticType := unentitledNestedRef.StaticType(inter) entitledNestedRef := interpreter.NewEphemeralReferenceValue( @@ -610,6 +614,7 @@ func TestConvertToEntitledValue(t *testing.T) { ), nestedValue, inter.MustSemaTypeOfValue(nestedValue), + interpreter.EmptyLocationRange, ) entitledNestedRefStaticType := entitledNestedRef.StaticType(inter) @@ -807,6 +812,7 @@ func TestConvertToEntitledValue(t *testing.T) { rValue.Clone(inter), ), sema.NewVariableSizedType(inter, inter.MustSemaTypeOfValue(rValue)), + interpreter.EmptyLocationRange, ), Output: interpreter.NewEphemeralReferenceValue( inter, @@ -824,6 +830,7 @@ func TestConvertToEntitledValue(t *testing.T) { rValue.Clone(inter), ), sema.NewVariableSizedType(inter, inter.MustSemaTypeOfValue(rValue)), + interpreter.EmptyLocationRange, ), Name: "&[R]", }, @@ -846,6 +853,7 @@ func TestConvertToEntitledValue(t *testing.T) { inter.MustSemaTypeOfValue(rValue), ), ), + interpreter.EmptyLocationRange, ), Output: interpreter.NewEphemeralReferenceValue( inter, @@ -876,6 +884,7 @@ func TestConvertToEntitledValue(t *testing.T) { inter.MustSemaTypeOfValue(rValue), ), ), + interpreter.EmptyLocationRange, ), Name: "&[&R]", }, @@ -891,6 +900,7 @@ func TestConvertToEntitledValue(t *testing.T) { rValue.Clone(inter), ), sema.NewDictionaryType(inter, sema.IntType, inter.MustSemaTypeOfValue(rValue)), + interpreter.EmptyLocationRange, ), Output: interpreter.NewEphemeralReferenceValue( inter, @@ -908,6 +918,7 @@ func TestConvertToEntitledValue(t *testing.T) { rValue.Clone(inter), ), sema.NewDictionaryType(inter, sema.IntType, inter.MustSemaTypeOfValue(rValue)), + interpreter.EmptyLocationRange, ), Name: "&{Int: R}", }, @@ -929,6 +940,7 @@ func TestConvertToEntitledValue(t *testing.T) { inter.MustSemaTypeOfValue(rValue), ), ), + interpreter.EmptyLocationRange, ), Output: interpreter.NewEphemeralReferenceValue( inter, @@ -958,6 +970,7 @@ func TestConvertToEntitledValue(t *testing.T) { inter.MustSemaTypeOfValue(rValue), ), ), + interpreter.EmptyLocationRange, ), Name: "&{Int: &R}", }, @@ -1122,7 +1135,7 @@ func TestMigrateSimpleContract(t *testing.T) { tValue, err := inter.Invoke("makeT") require.NoError(t, err) - unentitledRRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, rValue, inter.MustSemaTypeOfValue(rValue)) + unentitledRRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, rValue, inter.MustSemaTypeOfValue(rValue), interpreter.EmptyLocationRange) unentitledRRefStaticType := unentitledRRef.StaticType(inter) unentitledRCap := interpreter.NewCapabilityValue( @@ -1142,6 +1155,7 @@ func TestMigrateSimpleContract(t *testing.T) { ), rValue, inter.MustSemaTypeOfValue(rValue), + interpreter.EmptyLocationRange, ) entitledRRefStaticType := entitledRRef.StaticType(inter) entitledRCap := interpreter.NewCapabilityValue( diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 09168f02c8..ad2c7838f5 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -5471,7 +5471,7 @@ func (interpreter *Interpreter) withResourceDestruction( func (interpreter *Interpreter) ConvertValueToEntitlements( v Value, convertToEntitledType func(sema.Type) sema.Type, -) { +) Value { var staticType StaticType // for reference types, we want to use the borrow type, rather than the type of the referenced value @@ -5537,50 +5537,59 @@ func (interpreter *Interpreter) ConvertValueToEntitlements( case *EphemeralReferenceValue: entitledReferenceType := entitledType.(*sema.ReferenceType) staticAuthorization := ConvertSemaAccessToStaticAuthorization(interpreter, entitledReferenceType.Authorization) - v.Authorization = staticAuthorization - v.BorrowedType = entitledReferenceType.Type - interpreter.ConvertValueToEntitlements(v.Value, convertToEntitledType) + convertedValue := interpreter.ConvertValueToEntitlements(v.Value, convertToEntitledType) + return NewEphemeralReferenceValue( + interpreter, + staticAuthorization, + convertedValue, + entitledReferenceType, + EmptyLocationRange, + ) + case *StorageReferenceValue: // a stored value will in itself be migrated at another point, so no need to do anything here other than change the type entitledReferenceType := entitledType.(*sema.ReferenceType) staticAuthorization := ConvertSemaAccessToStaticAuthorization(interpreter, entitledReferenceType.Authorization) - v.Authorization = staticAuthorization - v.BorrowedType = entitledReferenceType.Type - + return NewStorageReferenceValue( + interpreter, + staticAuthorization, + v.TargetStorageAddress, + v.TargetPath, + entitledReferenceType, + ) case *SomeValue: - interpreter.ConvertValueToEntitlements(v.value, convertToEntitledType) - // reset the storable, to be recomputed on next access - v.valueStorable = nil - case *CompositeValue: - // convert all the fields of this composite value to entitlements - v.Walk(interpreter, func(v Value) { interpreter.ConvertValueToEntitlements(v, convertToEntitledType) }) + return NewSomeValueNonCopying(interpreter, interpreter.ConvertValueToEntitlements(v.value, convertToEntitledType)) + case *ArrayValue: entitledArrayType := entitledType.(sema.ArrayType) - v.semaType = entitledArrayType - v.Type = ConvertSemaArrayTypeToStaticArrayType(interpreter, entitledArrayType) - // convert all the elements of this array value to entitlements - v.Walk(interpreter, func(v Value) { interpreter.ConvertValueToEntitlements(v, convertToEntitledType) }) + arrayStaticType := ConvertSemaArrayTypeToStaticArrayType(interpreter, entitledArrayType) + return newArrayValueFromAtreeArray(interpreter, arrayStaticType, v.elementSize, v.array) + case *DictionaryValue: entitledDictionaryType := entitledType.(*sema.DictionaryType) - v.semaType = entitledDictionaryType - v.Type = ConvertSemaDictionaryTypeToStaticDictionaryType(interpreter, entitledDictionaryType) - // convert all the elements of this array value to entitlements - v.Walk(interpreter, func(v Value) { interpreter.ConvertValueToEntitlements(v, convertToEntitledType) }) + dictionaryStaticType := ConvertSemaDictionaryTypeToStaticDictionaryType(interpreter, entitledDictionaryType) + return newDictionaryValueFromAtreeMap(interpreter, dictionaryStaticType, v.elementSize, v.dictionary) + case *CapabilityValue: // capabilities should just have their borrow type updated, as the pointed-to value will also be visited // by the migration on its own entitledCapabilityValue := entitledType.(*sema.CapabilityType) - v.BorrowType = ConvertSemaToStaticType(interpreter, entitledCapabilityValue.BorrowType) + capabilityStaticType := ConvertSemaToStaticType(interpreter, entitledCapabilityValue.BorrowType) + return NewCapabilityValue(interpreter, v.ID, v.Address, capabilityStaticType) + case *TypeValue: if v.Type == nil { - return + return v } // convert the static type of the value - v.Type = ConvertSemaToStaticType( + entitledStaticType := ConvertSemaToStaticType( interpreter, convertToEntitledType( interpreter.MustConvertStaticToSemaType(v.Type), ), ) + return NewTypeValue(interpreter, entitledStaticType) } + + return v } From e867648053edf3840e7e4d437760402f81a74e5a Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Tue, 12 Dec 2023 15:48:36 -0500 Subject: [PATCH 24/40] adjust tests --- migrations/entitlements/migration_test.go | 12 ++++++++---- runtime/interpreter/interpreter.go | 4 ++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/migrations/entitlements/migration_test.go b/migrations/entitlements/migration_test.go index f29459100a..9a6aab476d 100644 --- a/migrations/entitlements/migration_test.go +++ b/migrations/entitlements/migration_test.go @@ -367,6 +367,10 @@ func TestConvertToEntitledType(t *testing.T) { } +func convertEntireTestValue(inter *interpreter.Interpreter, v interpreter.Value) interpreter.Value { + return inter.ConvertValueToEntitlements(v, ConvertToEntitledType) +} + func TestConvertToEntitledValue(t *testing.T) { t.Parallel() @@ -1060,12 +1064,12 @@ func TestConvertToEntitledValue(t *testing.T) { for _, test := range tests { t.Run(test.Name, func(t *testing.T) { - inter.ConvertValueToEntitlements(test.Input, ConvertToEntitledType) - switch input := test.Input.(type) { + convertedValue := convertEntireTestValue(inter, test.Input) + switch convertedValue := convertedValue.(type) { case interpreter.EquatableValue: - require.True(t, referencePeekingEqual(input, test.Output)) + require.True(t, referencePeekingEqual(convertedValue, test.Output)) default: - require.Equal(t, input, test.Output) + require.Equal(t, convertedValue, test.Output) } }) } diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index ad2c7838f5..eaac77e29a 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -5542,7 +5542,7 @@ func (interpreter *Interpreter) ConvertValueToEntitlements( interpreter, staticAuthorization, convertedValue, - entitledReferenceType, + entitledReferenceType.Type, EmptyLocationRange, ) @@ -5555,7 +5555,7 @@ func (interpreter *Interpreter) ConvertValueToEntitlements( staticAuthorization, v.TargetStorageAddress, v.TargetPath, - entitledReferenceType, + entitledReferenceType.Type, ) case *SomeValue: return NewSomeValueNonCopying(interpreter, interpreter.ConvertValueToEntitlements(v.value, convertToEntitledType)) From af22600580dd72e0ada19b1459db7f73bc1b1a24 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Wed, 13 Dec 2023 13:08:58 -0500 Subject: [PATCH 25/40] add test for array of values --- migrations/account_type/migration_test.go | 2 +- migrations/entitlements/migration_test.go | 262 +++++++++++++++++++++- 2 files changed, 257 insertions(+), 7 deletions(-) diff --git a/migrations/account_type/migration_test.go b/migrations/account_type/migration_test.go index 349ef70e5a..6e87a69373 100644 --- a/migrations/account_type/migration_test.go +++ b/migrations/account_type/migration_test.go @@ -407,7 +407,7 @@ func TestTypeValueMigration(t *testing.T) { // `IntersectionType.LegacyType` is not considered in the `IntersectionType.Equal` method. // Therefore, check for the legacy type equality manually. - typeValue := value.(interpreter.TypeValue) + typeValue := value.(*interpreter.TypeValue) if actualIntersectionType, ok := typeValue.Type.(*interpreter.IntersectionStaticType); ok { expectedIntersectionType := testCase.expectedType.(*interpreter.IntersectionStaticType) assert.True(t, actualIntersectionType.LegacyType.Equal(expectedIntersectionType.LegacyType)) diff --git a/migrations/entitlements/migration_test.go b/migrations/entitlements/migration_test.go index 4b030fb60a..c9c8815b4d 100644 --- a/migrations/entitlements/migration_test.go +++ b/migrations/entitlements/migration_test.go @@ -466,7 +466,7 @@ func TestConvertToEntitledValue(t *testing.T) { // &S - unentitledSRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, sValue, inter.MustSemaTypeOfValue(sValue)) + unentitledSRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, sValue, inter.MustSemaTypeOfValue(sValue), interpreter.EmptyLocationRange) unentitledSRefStaticType := unentitledSRef.StaticType(inter) entitledSRef := interpreter.NewEphemeralReferenceValue( @@ -479,12 +479,13 @@ func TestConvertToEntitledValue(t *testing.T) { ), sValue, inter.MustSemaTypeOfValue(sValue), + interpreter.EmptyLocationRange, ) entitledSRefStaticType := entitledSRef.StaticType(inter) // &R - unentitledRRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, rValue, inter.MustSemaTypeOfValue(rValue)) + unentitledRRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, rValue, inter.MustSemaTypeOfValue(rValue), interpreter.EmptyLocationRange) unentitledRRefStaticType := unentitledRRef.StaticType(inter) entitledRRef := interpreter.NewEphemeralReferenceValue( @@ -497,13 +498,14 @@ func TestConvertToEntitledValue(t *testing.T) { ), rValue, inter.MustSemaTypeOfValue(rValue), + interpreter.EmptyLocationRange, ) entitledRRefStaticType := entitledRRef.StaticType(inter) // &{I} intersectionIType := sema.NewIntersectionType(inter, []*sema.InterfaceType{checker.Elaboration.InterfaceType("S.test.I")}) - unentitledIRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, rValue, intersectionIType) + unentitledIRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, rValue, intersectionIType, interpreter.EmptyLocationRange) entitledIRef := interpreter.NewEphemeralReferenceValue( inter, @@ -515,6 +517,7 @@ func TestConvertToEntitledValue(t *testing.T) { ), rValue, intersectionIType, + interpreter.EmptyLocationRange, ) // legacy Capability<&R{I}> @@ -581,7 +584,7 @@ func TestConvertToEntitledValue(t *testing.T) { checker.Elaboration.InterfaceType("S.test.J"), }, ) - unentitledIJRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, rValue, intersectionIJType) + unentitledIJRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, rValue, intersectionIJType, interpreter.EmptyLocationRange) entitledIJRef := interpreter.NewEphemeralReferenceValue( inter, @@ -593,11 +596,12 @@ func TestConvertToEntitledValue(t *testing.T) { ), rValue, intersectionIJType, + interpreter.EmptyLocationRange, ) // &Nested - unentitledNestedRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, nestedValue, inter.MustSemaTypeOfValue(nestedValue)) + unentitledNestedRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, nestedValue, inter.MustSemaTypeOfValue(nestedValue), interpreter.EmptyLocationRange) unentitledNestedRefStaticType := unentitledNestedRef.StaticType(inter) entitledNestedRef := interpreter.NewEphemeralReferenceValue( @@ -610,6 +614,7 @@ func TestConvertToEntitledValue(t *testing.T) { ), nestedValue, inter.MustSemaTypeOfValue(nestedValue), + interpreter.EmptyLocationRange, ) entitledNestedRefStaticType := entitledNestedRef.StaticType(inter) @@ -807,6 +812,7 @@ func TestConvertToEntitledValue(t *testing.T) { rValue.Clone(inter), ), sema.NewVariableSizedType(inter, inter.MustSemaTypeOfValue(rValue)), + interpreter.EmptyLocationRange, ), Output: interpreter.NewEphemeralReferenceValue( inter, @@ -824,6 +830,7 @@ func TestConvertToEntitledValue(t *testing.T) { rValue.Clone(inter), ), sema.NewVariableSizedType(inter, inter.MustSemaTypeOfValue(rValue)), + interpreter.EmptyLocationRange, ), Name: "&[R]", }, @@ -846,6 +853,7 @@ func TestConvertToEntitledValue(t *testing.T) { inter.MustSemaTypeOfValue(rValue), ), ), + interpreter.EmptyLocationRange, ), Output: interpreter.NewEphemeralReferenceValue( inter, @@ -876,6 +884,7 @@ func TestConvertToEntitledValue(t *testing.T) { inter.MustSemaTypeOfValue(rValue), ), ), + interpreter.EmptyLocationRange, ), Name: "&[&R]", }, @@ -891,6 +900,7 @@ func TestConvertToEntitledValue(t *testing.T) { rValue.Clone(inter), ), sema.NewDictionaryType(inter, sema.IntType, inter.MustSemaTypeOfValue(rValue)), + interpreter.EmptyLocationRange, ), Output: interpreter.NewEphemeralReferenceValue( inter, @@ -908,6 +918,7 @@ func TestConvertToEntitledValue(t *testing.T) { rValue.Clone(inter), ), sema.NewDictionaryType(inter, sema.IntType, inter.MustSemaTypeOfValue(rValue)), + interpreter.EmptyLocationRange, ), Name: "&{Int: R}", }, @@ -929,6 +940,7 @@ func TestConvertToEntitledValue(t *testing.T) { inter.MustSemaTypeOfValue(rValue), ), ), + interpreter.EmptyLocationRange, ), Output: interpreter.NewEphemeralReferenceValue( inter, @@ -958,6 +970,7 @@ func TestConvertToEntitledValue(t *testing.T) { inter.MustSemaTypeOfValue(rValue), ), ), + interpreter.EmptyLocationRange, ), Name: "&{Int: &R}", }, @@ -1122,7 +1135,7 @@ func TestMigrateSimpleContract(t *testing.T) { tValue, err := inter.Invoke("makeT") require.NoError(t, err) - unentitledRRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, rValue, inter.MustSemaTypeOfValue(rValue)) + unentitledRRef := interpreter.NewEphemeralReferenceValue(inter, interpreter.UnauthorizedAccess, rValue, inter.MustSemaTypeOfValue(rValue), interpreter.EmptyLocationRange) unentitledRRefStaticType := unentitledRRef.StaticType(inter) unentitledRCap := interpreter.NewCapabilityValue( @@ -1142,6 +1155,7 @@ func TestMigrateSimpleContract(t *testing.T) { ), rValue, inter.MustSemaTypeOfValue(rValue), + interpreter.EmptyLocationRange, ) entitledRRefStaticType := entitledRRef.StaticType(inter) entitledRCap := interpreter.NewCapabilityValue( @@ -1444,3 +1458,239 @@ func TestMigrateAcrossContracts(t *testing.T) { ) } + +func TestMigrateArrayOfValues(t *testing.T) { + t.Parallel() + + address1 := [8]byte{0, 0, 0, 0, 0, 0, 0, 1} + address2 := [8]byte{0, 0, 0, 0, 0, 0, 0, 2} + + storage := NewTestLedger(nil, nil) + rt := NewTestInterpreterRuntime() + + accountCodes := map[common.Location][]byte{} + interfaces := map[common.Location]*TestRuntimeInterface{} + + runtimeInterface1 := &TestRuntimeInterface{ + Storage: storage, + OnEmitEvent: func(event cadence.Event) error { + return nil + }, + OnGetSigningAccounts: func() ([]runtime.Address, error) { + return []runtime.Address{address1}, nil + }, + OnGetCode: func(location common.Location) (bytes []byte, err error) { + return accountCodes[location], nil + }, + OnResolveLocation: MultipleIdentifierLocationResolver, + OnGetAccountContractCode: func(location common.AddressLocation) (code []byte, err error) { + code = accountCodes[location] + return code, nil + }, + } + runtimeInterface1.OnUpdateAccountContractCode = func(location common.AddressLocation, code []byte) error { + accountCodes[location] = code + interfaces[location] = runtimeInterface1 + return nil + } + + runtimeInterface2 := &TestRuntimeInterface{ + Storage: storage, + OnEmitEvent: func(event cadence.Event) error { + return nil + }, + OnGetCode: func(location common.Location) (bytes []byte, err error) { + return accountCodes[location], nil + }, + OnGetSigningAccounts: func() ([]runtime.Address, error) { + return []runtime.Address{address2}, nil + }, + OnResolveLocation: MultipleIdentifierLocationResolver, + OnGetAccountContractCode: func(location common.AddressLocation) (code []byte, err error) { + code = accountCodes[location] + return code, nil + }, + } + runtimeInterface2.OnUpdateAccountContractCode = func(location common.AddressLocation, code []byte) error { + accountCodes[location] = code + interfaces[location] = runtimeInterface2 + return nil + } + + nextTransactionLocation := NewTransactionLocationGenerator() + + oldContract := []byte(` + access(all) contract C { + access(all) resource R { + access(all) fun foo() {} + } + access(all) fun makeR(): @R { + return <- create R() + } + } + `) + + contract := []byte(` + access(all) contract C { + access(all) entitlement E + access(all) resource R { + access(E) fun foo() {} + } + access(all) fun makeR(): @R { + return <- create R() + } + } + `) + + saveValues := []byte(` + import C from 0x1 + + transaction { + prepare(signer: auth(Storage, Capabilities) &Account) { + let r1 <- C.makeR() + let r2 <- C.makeR() + signer.storage.save(<-r1, to: /storage/foo) + signer.storage.save(<-r2, to: /storage/bar) + let cap1 = signer.capabilities.storage.issue<&C.R>(/storage/foo) + let cap2 = signer.capabilities.storage.issue<&C.R>(/storage/bar) + let arr = [cap1, cap2] + signer.storage.save(arr, to: /storage/caps) + } + } + `) + + // Deploy contract to 0x1 + err := rt.ExecuteTransaction( + runtime.Script{ + Source: DeploymentTransaction("C", oldContract), + }, + runtime.Context{ + Interface: runtimeInterface1, + Location: nextTransactionLocation(), + }, + ) + require.NoError(t, err) + + // Execute transaction on 0x2 + err = rt.ExecuteTransaction( + runtime.Script{ + Source: saveValues, + }, + runtime.Context{ + Interface: runtimeInterface2, + Location: nextTransactionLocation(), + }, + ) + require.NoError(t, err) + + // update contract on 0x1 + err = rt.ExecuteTransaction( + runtime.Script{ + Source: UpdateTransaction("C", contract), + }, + runtime.Context{ + Interface: runtimeInterface1, + Location: nextTransactionLocation(), + }, + ) + require.NoError(t, err) + + runtimeStorage := runtime.NewStorage(storage, nil) + + inter, err := interpreter.NewInterpreter( + nil, + utils.TestLocation, + &interpreter.Config{ + Storage: runtimeStorage, + AtreeValueValidationEnabled: false, + AtreeStorageValidationEnabled: false, + ImportLocationHandler: func(inter *interpreter.Interpreter, location common.Location) interpreter.Import { + program, err := rt.ParseAndCheckProgram( + accountCodes[location], + runtime.Context{ + Interface: interfaces[location], + Location: location, + }, + ) + require.NoError(t, err) + + subInterpreter, err := inter.NewSubInterpreter(program, location) + require.NoError(t, err) + + return interpreter.InterpreterImport{ + Interpreter: subInterpreter, + } + }, + }, + ) + require.NoError(t, err) + + storageIdentifier := common.PathDomainStorage.Identifier() + storageMap := runtimeStorage.GetStorageMap(address2, storageIdentifier, false) + require.NotNil(t, storageMap) + require.Greater(t, storageMap.Count(), uint64(0)) + + // Migrate + + migration := migrations.NewStorageMigration(inter, runtimeStorage) + migration.Migrate( + &migrations.AddressSliceIterator{ + Addresses: []common.Address{ + address1, + address2, + }, + }, + nil, + NewEntitlementsMigration(inter), + ) + + arrayValue := storageMap.ReadValue(nil, interpreter.StringStorageMapKey("caps")) + require.IsType(t, &interpreter.ArrayValue{}, arrayValue) + arrValue := arrayValue.(*interpreter.ArrayValue) + require.Equal(t, 2, arrValue.Count()) + + elementType := arrValue.Type.ElementType() + require.IsType(t, &interpreter.CapabilityStaticType{}, elementType) + capElementType := elementType.(*interpreter.CapabilityStaticType) + require.IsType(t, &interpreter.ReferenceStaticType{}, capElementType.BorrowType) + ref := capElementType.BorrowType.(*interpreter.ReferenceStaticType) + require.Equal(t, + interpreter.NewEntitlementSetAuthorization( + inter, + func() []common.TypeID { return []common.TypeID{"A.0000000000000001.C.E"} }, + 1, + sema.Conjunction, + ), + ref.Authorization, + ) + + cap1 := arrValue.Get(inter, interpreter.EmptyLocationRange, 0) + require.IsType(t, &interpreter.CapabilityValue{}, cap1) + capValue := cap1.(*interpreter.CapabilityValue) + require.IsType(t, &interpreter.ReferenceStaticType{}, capValue.BorrowType) + ref = capValue.BorrowType.(*interpreter.ReferenceStaticType) + require.Equal(t, + interpreter.NewEntitlementSetAuthorization( + inter, + func() []common.TypeID { return []common.TypeID{"A.0000000000000001.C.E"} }, + 1, + sema.Conjunction, + ), + ref.Authorization, + ) + + cap2 := arrValue.Get(inter, interpreter.EmptyLocationRange, 1) + require.IsType(t, &interpreter.CapabilityValue{}, cap2) + capValue = cap1.(*interpreter.CapabilityValue) + require.IsType(t, &interpreter.ReferenceStaticType{}, capValue.BorrowType) + ref = capValue.BorrowType.(*interpreter.ReferenceStaticType) + require.Equal(t, + interpreter.NewEntitlementSetAuthorization( + inter, + func() []common.TypeID { return []common.TypeID{"A.0000000000000001.C.E"} }, + 1, + sema.Conjunction, + ), + ref.Authorization, + ) +} From 591471d497ed3833e429a4720ddfe66c388a0e88 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Wed, 13 Dec 2023 13:10:32 -0500 Subject: [PATCH 26/40] broken commit for testing --- migrations/migration.go | 45 +++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/migrations/migration.go b/migrations/migration.go index 25fb9527d5..3bb4b009c0 100644 --- a/migrations/migration.go +++ b/migrations/migration.go @@ -131,9 +131,6 @@ func (m *StorageMigration) migrateNestedValue( } } - // The array itself doesn't need to be replaced. - return - case *interpreter.CompositeValue: composite := value @@ -156,9 +153,6 @@ func (m *StorageMigration) migrateNestedValue( composite.SetMember(m.interpreter, emptyLocationRange, fieldName, migratedValue) } - // The composite itself does not have to be replaced - return - case *interpreter.DictionaryValue: dictionary := value @@ -207,29 +201,26 @@ func (m *StorageMigration) migrateNestedValue( dictionary.SetKey(m.interpreter, emptyLocationRange, keyToSet, valueToSet) } + } - // The dictionary itself does not have to be replaced - return - default: - // Assumption: all migrations only migrate non-container typed values. - for _, migration := range migrations { - converted := migration.Migrate(value) - - if converted != nil { - // Chain the migrations. - // Probably not needed, because of the assumption above. - // i.e: A single non-container value may not get converted from two migrations. - // But have it here to be safe. - value = converted - - newValue = converted - - if reporter != nil { - reporter.Report(address, domain, key, migration.Name()) - } + // Assumption: all migrations only migrate non-container typed values. + for _, migration := range migrations { + converted := migration.Migrate(value) + + if converted != nil { + // Chain the migrations. + // Probably not needed, because of the assumption above. + // i.e: A single non-container value may not get converted from two migrations. + // But have it here to be safe. + value = converted + + newValue = converted + + if reporter != nil { + reporter.Report(address, domain, key, migration.Name()) } } - - return } + return + } From cb3d93406e243fff3f1771582a2a8be7ffc05ff9 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Wed, 13 Dec 2023 13:36:54 -0500 Subject: [PATCH 27/40] add new test --- migrations/entitlements/migration_test.go | 236 ++++++++++++++++++++++ runtime/interpreter/interpreter.go | 2 +- 2 files changed, 237 insertions(+), 1 deletion(-) diff --git a/migrations/entitlements/migration_test.go b/migrations/entitlements/migration_test.go index 9a6aab476d..5e4a6f22f5 100644 --- a/migrations/entitlements/migration_test.go +++ b/migrations/entitlements/migration_test.go @@ -1462,3 +1462,239 @@ func TestMigrateAcrossContracts(t *testing.T) { ) } + +func TestMigrateArrayOfValues(t *testing.T) { + t.Parallel() + + address1 := [8]byte{0, 0, 0, 0, 0, 0, 0, 1} + address2 := [8]byte{0, 0, 0, 0, 0, 0, 0, 2} + + storage := NewTestLedger(nil, nil) + rt := NewTestInterpreterRuntime() + + accountCodes := map[common.Location][]byte{} + interfaces := map[common.Location]*TestRuntimeInterface{} + + runtimeInterface1 := &TestRuntimeInterface{ + Storage: storage, + OnEmitEvent: func(event cadence.Event) error { + return nil + }, + OnGetSigningAccounts: func() ([]runtime.Address, error) { + return []runtime.Address{address1}, nil + }, + OnGetCode: func(location common.Location) (bytes []byte, err error) { + return accountCodes[location], nil + }, + OnResolveLocation: MultipleIdentifierLocationResolver, + OnGetAccountContractCode: func(location common.AddressLocation) (code []byte, err error) { + code = accountCodes[location] + return code, nil + }, + } + runtimeInterface1.OnUpdateAccountContractCode = func(location common.AddressLocation, code []byte) error { + accountCodes[location] = code + interfaces[location] = runtimeInterface1 + return nil + } + + runtimeInterface2 := &TestRuntimeInterface{ + Storage: storage, + OnEmitEvent: func(event cadence.Event) error { + return nil + }, + OnGetCode: func(location common.Location) (bytes []byte, err error) { + return accountCodes[location], nil + }, + OnGetSigningAccounts: func() ([]runtime.Address, error) { + return []runtime.Address{address2}, nil + }, + OnResolveLocation: MultipleIdentifierLocationResolver, + OnGetAccountContractCode: func(location common.AddressLocation) (code []byte, err error) { + code = accountCodes[location] + return code, nil + }, + } + runtimeInterface2.OnUpdateAccountContractCode = func(location common.AddressLocation, code []byte) error { + accountCodes[location] = code + interfaces[location] = runtimeInterface2 + return nil + } + + nextTransactionLocation := NewTransactionLocationGenerator() + + oldContract := []byte(` + access(all) contract C { + access(all) resource R { + access(all) fun foo() {} + } + access(all) fun makeR(): @R { + return <- create R() + } + } + `) + + contract := []byte(` + access(all) contract C { + access(all) entitlement E + access(all) resource R { + access(E) fun foo() {} + } + access(all) fun makeR(): @R { + return <- create R() + } + } + `) + + saveValues := []byte(` + import C from 0x1 + + transaction { + prepare(signer: auth(Storage, Capabilities) &Account) { + let r1 <- C.makeR() + let r2 <- C.makeR() + signer.storage.save(<-r1, to: /storage/foo) + signer.storage.save(<-r2, to: /storage/bar) + let cap1 = signer.capabilities.storage.issue<&C.R>(/storage/foo) + let cap2 = signer.capabilities.storage.issue<&C.R>(/storage/bar) + let arr = [cap1, cap2] + signer.storage.save(arr, to: /storage/caps) + } + } + `) + + // Deploy contract to 0x1 + err := rt.ExecuteTransaction( + runtime.Script{ + Source: DeploymentTransaction("C", oldContract), + }, + runtime.Context{ + Interface: runtimeInterface1, + Location: nextTransactionLocation(), + }, + ) + require.NoError(t, err) + + // Execute transaction on 0x2 + err = rt.ExecuteTransaction( + runtime.Script{ + Source: saveValues, + }, + runtime.Context{ + Interface: runtimeInterface2, + Location: nextTransactionLocation(), + }, + ) + require.NoError(t, err) + + // update contract on 0x1 + err = rt.ExecuteTransaction( + runtime.Script{ + Source: UpdateTransaction("C", contract), + }, + runtime.Context{ + Interface: runtimeInterface1, + Location: nextTransactionLocation(), + }, + ) + require.NoError(t, err) + + runtimeStorage := runtime.NewStorage(storage, nil) + + inter, err := interpreter.NewInterpreter( + nil, + utils.TestLocation, + &interpreter.Config{ + Storage: runtimeStorage, + AtreeValueValidationEnabled: false, + AtreeStorageValidationEnabled: false, + ImportLocationHandler: func(inter *interpreter.Interpreter, location common.Location) interpreter.Import { + program, err := rt.ParseAndCheckProgram( + accountCodes[location], + runtime.Context{ + Interface: interfaces[location], + Location: location, + }, + ) + require.NoError(t, err) + + subInterpreter, err := inter.NewSubInterpreter(program, location) + require.NoError(t, err) + + return interpreter.InterpreterImport{ + Interpreter: subInterpreter, + } + }, + }, + ) + require.NoError(t, err) + + storageIdentifier := common.PathDomainStorage.Identifier() + storageMap := runtimeStorage.GetStorageMap(address2, storageIdentifier, false) + require.NotNil(t, storageMap) + require.Greater(t, storageMap.Count(), uint64(0)) + + // Migrate + + migration := migrations.NewStorageMigration(inter, runtimeStorage) + migration.Migrate( + &migrations.AddressSliceIterator{ + Addresses: []common.Address{ + address1, + address2, + }, + }, + nil, + NewEntitlementsMigration(inter), + ) + + arrayValue := storageMap.ReadValue(nil, interpreter.StringStorageMapKey("caps")) + require.IsType(t, &interpreter.ArrayValue{}, arrayValue) + arrValue := arrayValue.(*interpreter.ArrayValue) + require.Equal(t, 2, arrValue.Count()) + + elementType := arrValue.Type.ElementType() + require.IsType(t, &interpreter.CapabilityStaticType{}, elementType) + capElementType := elementType.(*interpreter.CapabilityStaticType) + require.IsType(t, &interpreter.ReferenceStaticType{}, capElementType.BorrowType) + ref := capElementType.BorrowType.(*interpreter.ReferenceStaticType) + require.Equal(t, + interpreter.NewEntitlementSetAuthorization( + inter, + func() []common.TypeID { return []common.TypeID{"A.0000000000000001.C.E"} }, + 1, + sema.Conjunction, + ), + ref.Authorization, + ) + + cap1 := arrValue.Get(inter, interpreter.EmptyLocationRange, 0) + require.IsType(t, &interpreter.CapabilityValue{}, cap1) + capValue := cap1.(*interpreter.CapabilityValue) + require.IsType(t, &interpreter.ReferenceStaticType{}, capValue.BorrowType) + ref = capValue.BorrowType.(*interpreter.ReferenceStaticType) + require.Equal(t, + interpreter.NewEntitlementSetAuthorization( + inter, + func() []common.TypeID { return []common.TypeID{"A.0000000000000001.C.E"} }, + 1, + sema.Conjunction, + ), + ref.Authorization, + ) + + cap2 := arrValue.Get(inter, interpreter.EmptyLocationRange, 1) + require.IsType(t, &interpreter.CapabilityValue{}, cap2) + capValue = cap1.(*interpreter.CapabilityValue) + require.IsType(t, &interpreter.ReferenceStaticType{}, capValue.BorrowType) + ref = capValue.BorrowType.(*interpreter.ReferenceStaticType) + require.Equal(t, + interpreter.NewEntitlementSetAuthorization( + inter, + func() []common.TypeID { return []common.TypeID{"A.0000000000000001.C.E"} }, + 1, + sema.Conjunction, + ), + ref.Authorization, + ) +} diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index eaac77e29a..d36c393d91 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -5591,5 +5591,5 @@ func (interpreter *Interpreter) ConvertValueToEntitlements( return NewTypeValue(interpreter, entitledStaticType) } - return v + return nil } From cf9f801fa546cccd313caf7c90f0e57370edc183 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Wed, 13 Dec 2023 14:29:40 -0500 Subject: [PATCH 28/40] return nil for unchanged values --- migrations/account_type/migration_test.go | 2 +- migrations/entitlements/migration.go | 156 ++++++++- migrations/entitlements/migration_test.go | 386 ++++++++++++++++------ migrations/migration.go | 15 +- runtime/interpreter/interpreter.go | 131 -------- 5 files changed, 439 insertions(+), 251 deletions(-) diff --git a/migrations/account_type/migration_test.go b/migrations/account_type/migration_test.go index 349ef70e5a..6e87a69373 100644 --- a/migrations/account_type/migration_test.go +++ b/migrations/account_type/migration_test.go @@ -407,7 +407,7 @@ func TestTypeValueMigration(t *testing.T) { // `IntersectionType.LegacyType` is not considered in the `IntersectionType.Equal` method. // Therefore, check for the legacy type equality manually. - typeValue := value.(interpreter.TypeValue) + typeValue := value.(*interpreter.TypeValue) if actualIntersectionType, ok := typeValue.Type.(*interpreter.IntersectionStaticType); ok { expectedIntersectionType := testCase.expectedType.(*interpreter.IntersectionStaticType) assert.True(t, actualIntersectionType.LegacyType.Equal(expectedIntersectionType.LegacyType)) diff --git a/migrations/entitlements/migration.go b/migrations/entitlements/migration.go index 06e7f9200d..16375c514f 100644 --- a/migrations/entitlements/migration.go +++ b/migrations/entitlements/migration.go @@ -84,6 +84,160 @@ func ConvertToEntitledType(t sema.Type) sema.Type { } } +// Converts the input value into a version compatible with the new entitlements feature, +// with the same members/operations accessible on any references as would have been accessible in the past. +// Modifies the input `v` in place. +// This is used for migrations, but must be located in this package because some of the fields we wish to modify are +// privately scoped to this package +func ConvertValueToEntitlements( + inter *interpreter.Interpreter, + v interpreter.Value, +) interpreter.Value { + + var staticType interpreter.StaticType + // for reference types, we want to use the borrow type, rather than the type of the referenced value + switch referenceValue := v.(type) { + case *interpreter.EphemeralReferenceValue: + staticType = interpreter.NewReferenceStaticType( + inter, + referenceValue.Authorization, + interpreter.ConvertSemaToStaticType(inter, referenceValue.BorrowedType), + ) + case *interpreter.StorageReferenceValue: + staticType = interpreter.NewReferenceStaticType( + inter, + referenceValue.Authorization, + interpreter.ConvertSemaToStaticType(inter, referenceValue.BorrowedType), + ) + default: + staticType = v.StaticType(inter) + } + + // if the static type contains a legacy restricted type, convert it to a new type according to some rules: + // &T{I} -> auth(SupportedEntitlements(I)) &T + // Capability<&T{I}> -> Capability + var convertLegacyStaticType func(interpreter.StaticType) + convertLegacyStaticType = func(staticType interpreter.StaticType) { + switch t := staticType.(type) { + case *interpreter.ReferenceStaticType: + switch referencedType := t.ReferencedType.(type) { + case *interpreter.IntersectionStaticType: + if referencedType.LegacyType != nil { + t.ReferencedType = referencedType.LegacyType + intersectionSemaType := inter.MustConvertStaticToSemaType(referencedType).(*sema.IntersectionType) + auth := sema.UnauthorizedAccess + supportedEntitlements := intersectionSemaType.SupportedEntitlements() + if supportedEntitlements.Len() > 0 { + auth = sema.EntitlementSetAccess{ + SetKind: sema.Conjunction, + Entitlements: supportedEntitlements, + } + } + t.Authorization = interpreter.ConvertSemaAccessToStaticAuthorization(inter, auth) + } + } + case *interpreter.CapabilityStaticType: + convertLegacyStaticType(t.BorrowType) + case *interpreter.VariableSizedStaticType: + convertLegacyStaticType(t.Type) + case *interpreter.ConstantSizedStaticType: + convertLegacyStaticType(t.Type) + case *interpreter.DictionaryStaticType: + convertLegacyStaticType(t.KeyType) + convertLegacyStaticType(t.ValueType) + case *interpreter.OptionalStaticType: + convertLegacyStaticType(t.Type) + } + } + + convertLegacyStaticType(staticType) + semaType := inter.MustConvertStaticToSemaType(staticType) + entitledType := ConvertToEntitledType(semaType) + + switch v := v.(type) { + case *interpreter.EphemeralReferenceValue: + entitledReferenceType := entitledType.(*sema.ReferenceType) + staticAuthorization := interpreter.ConvertSemaAccessToStaticAuthorization(inter, entitledReferenceType.Authorization) + convertedValue := ConvertValueToEntitlements(inter, v.Value) + // if the underlying value did not change, we still want to use the old value in the newly created reference + if convertedValue == nil { + convertedValue = v.Value + } + return interpreter.NewEphemeralReferenceValue( + inter, + staticAuthorization, + convertedValue, + entitledReferenceType.Type, + interpreter.EmptyLocationRange, + ) + + case *interpreter.StorageReferenceValue: + // a stored value will in itself be migrated at another point, so no need to do anything here other than change the type + entitledReferenceType := entitledType.(*sema.ReferenceType) + staticAuthorization := interpreter.ConvertSemaAccessToStaticAuthorization(inter, entitledReferenceType.Authorization) + return interpreter.NewStorageReferenceValue( + inter, + staticAuthorization, + v.TargetStorageAddress, + v.TargetPath, + entitledReferenceType.Type, + ) + + case *interpreter.ArrayValue: + entitledArrayType := entitledType.(sema.ArrayType) + arrayStaticType := interpreter.ConvertSemaArrayTypeToStaticArrayType(inter, entitledArrayType) + + iterator := v.Iterator(inter) + + newArray := interpreter.NewArrayValueWithIterator(inter, arrayStaticType, v.GetOwner(), uint64(v.Count()), func() interpreter.Value { + return iterator.Next(inter) + }) + return newArray + + case *interpreter.DictionaryValue: + entitledDictionaryType := entitledType.(*sema.DictionaryType) + dictionaryStaticType := interpreter.ConvertSemaDictionaryTypeToStaticDictionaryType(inter, entitledDictionaryType) + + var values []interpreter.Value + + v.Iterate(inter, func(key, value interpreter.Value) (resume bool) { + values = append(values, key) + values = append(values, value) + return true + }) + + newDict := interpreter.NewDictionaryValue( + inter, + interpreter.EmptyLocationRange, + dictionaryStaticType, + values..., + ) + return newDict + + case *interpreter.CapabilityValue: + // capabilities should just have their borrow type updated, as the pointed-to value will also be visited + // by the migration on its own + entitledCapabilityValue := entitledType.(*sema.CapabilityType) + capabilityStaticType := interpreter.ConvertSemaToStaticType(inter, entitledCapabilityValue.BorrowType) + return interpreter.NewCapabilityValue(inter, v.ID, v.Address, capabilityStaticType) + + case *interpreter.TypeValue: + if v.Type == nil { + return v + } + // convert the static type of the value + entitledStaticType := interpreter.ConvertSemaToStaticType( + inter, + ConvertToEntitledType( + inter.MustConvertStaticToSemaType(v.Type), + ), + ) + return interpreter.NewTypeValue(inter, entitledStaticType) + } + + return nil +} + func (mig EntitlementsMigration) Migrate(value interpreter.Value) (newValue interpreter.Value) { - return mig.Interpreter.ConvertValueToEntitlements(value, ConvertToEntitledType) + return ConvertValueToEntitlements(mig.Interpreter, value) } diff --git a/migrations/entitlements/migration_test.go b/migrations/entitlements/migration_test.go index 5e4a6f22f5..6a815e2dc2 100644 --- a/migrations/entitlements/migration_test.go +++ b/migrations/entitlements/migration_test.go @@ -29,6 +29,7 @@ import ( "github.com/onflow/cadence/runtime/interpreter" "github.com/onflow/cadence/runtime/sema" checkerUtils "github.com/onflow/cadence/runtime/tests/checker" + "github.com/onflow/cadence/runtime/tests/runtime_utils" . "github.com/onflow/cadence/runtime/tests/runtime_utils" "github.com/onflow/cadence/runtime/tests/utils" . "github.com/onflow/cadence/runtime/tests/utils" @@ -367,8 +368,41 @@ func TestConvertToEntitledType(t *testing.T) { } -func convertEntireTestValue(inter *interpreter.Interpreter, v interpreter.Value) interpreter.Value { - return inter.ConvertValueToEntitlements(v, ConvertToEntitledType) +type testEntitlementsMigration struct { + inter *interpreter.Interpreter +} + +func (testEntitlementsMigration) Name() string { + return "Test Entitlements Migration" +} + +func (m testEntitlementsMigration) Migrate(value interpreter.Value) (newValue interpreter.Value) { + return ConvertValueToEntitlements(m.inter, value) +} + +func convertEntireTestValue( + inter *interpreter.Interpreter, + storage *runtime.Storage, + address common.Address, + v interpreter.Value, +) interpreter.Value { + testMig := testEntitlementsMigration{inter: inter} + storageMig := migrations.NewStorageMigration(inter, storage) + + migratedValue := storageMig.MigrateNestedValue( + v, + address, + common.PathDomainStorage, + "", + []migrations.Migration{testMig}, + nil, + ) + + if migratedValue == nil { + return v + } else { + return migratedValue + } } func TestConvertToEntitledValue(t *testing.T) { @@ -376,7 +410,8 @@ func TestConvertToEntitledValue(t *testing.T) { var uuid uint64 - storage := interpreter.NewInMemoryStorage(nil) + ledger := runtime_utils.NewTestLedger(nil, nil) + storage := runtime.NewStorage(ledger, nil) testAddress := common.MustBytesToAddress([]byte{0x1}) @@ -838,60 +873,6 @@ func TestConvertToEntitledValue(t *testing.T) { ), Name: "&[R]", }, - { - Input: interpreter.NewEphemeralReferenceValue( - inter, - interpreter.UnauthorizedAccess, - interpreter.NewArrayValue( - inter, - interpreter.EmptyLocationRange, - interpreter.NewVariableSizedStaticType(inter, unentitledRRefStaticType), - testAddress, - unentitledRRef, - ), - sema.NewVariableSizedType( - inter, - sema.NewReferenceType( - inter, - sema.UnauthorizedAccess, - inter.MustSemaTypeOfValue(rValue), - ), - ), - interpreter.EmptyLocationRange, - ), - Output: interpreter.NewEphemeralReferenceValue( - inter, - interpreter.NewEntitlementSetAuthorization( - inter, - func() []common.TypeID { return []common.TypeID{"Mutate", "Insert", "Remove"} }, - 3, - sema.Conjunction, - ), - interpreter.NewArrayValue( - inter, - interpreter.EmptyLocationRange, - interpreter.NewVariableSizedStaticType(inter, entitledRRefStaticType), - testAddress, - entitledRRef, - ), - sema.NewVariableSizedType( - inter, - sema.NewReferenceType( - inter, - sema.NewEntitlementSetAccess( - []*sema.EntitlementType{ - checker.Elaboration.EntitlementType("S.test.E"), - checker.Elaboration.EntitlementType("S.test.G"), - }, - sema.Conjunction, - ), - inter.MustSemaTypeOfValue(rValue), - ), - ), - interpreter.EmptyLocationRange, - ), - Name: "&[&R]", - }, { Input: interpreter.NewEphemeralReferenceValue( inter, @@ -926,58 +907,6 @@ func TestConvertToEntitledValue(t *testing.T) { ), Name: "&{Int: R}", }, - { - Input: interpreter.NewEphemeralReferenceValue( - inter, - interpreter.UnauthorizedAccess, - interpreter.NewDictionaryValue( - inter, - interpreter.EmptyLocationRange, - interpreter.NewDictionaryStaticType(inter, interpreter.PrimitiveStaticTypeInt, unentitledRRefStaticType), - interpreter.NewIntValueFromInt64(inter, 0), - unentitledRRef, - ), - sema.NewDictionaryType(inter, sema.IntType, - sema.NewReferenceType( - inter, - sema.UnauthorizedAccess, - inter.MustSemaTypeOfValue(rValue), - ), - ), - interpreter.EmptyLocationRange, - ), - Output: interpreter.NewEphemeralReferenceValue( - inter, - interpreter.NewEntitlementSetAuthorization( - inter, - func() []common.TypeID { return []common.TypeID{"Mutate", "Insert", "Remove"} }, - 3, - sema.Conjunction, - ), - interpreter.NewDictionaryValue( - inter, - interpreter.EmptyLocationRange, - interpreter.NewDictionaryStaticType(inter, interpreter.PrimitiveStaticTypeInt, entitledRRefStaticType), - interpreter.NewIntValueFromInt64(inter, 0), - entitledRRef, - ), - sema.NewDictionaryType(inter, sema.IntType, - sema.NewReferenceType( - inter, - sema.NewEntitlementSetAccess( - []*sema.EntitlementType{ - checker.Elaboration.EntitlementType("S.test.E"), - checker.Elaboration.EntitlementType("S.test.G"), - }, - sema.Conjunction, - ), - inter.MustSemaTypeOfValue(rValue), - ), - ), - interpreter.EmptyLocationRange, - ), - Name: "&{Int: &R}", - }, } getStaticType := func(v interpreter.Value) interpreter.StaticType { @@ -1064,7 +993,7 @@ func TestConvertToEntitledValue(t *testing.T) { for _, test := range tests { t.Run(test.Name, func(t *testing.T) { - convertedValue := convertEntireTestValue(inter, test.Input) + convertedValue := convertEntireTestValue(inter, storage, testAddress, test.Input) switch convertedValue := convertedValue.(type) { case interpreter.EquatableValue: require.True(t, referencePeekingEqual(convertedValue, test.Output)) @@ -1698,3 +1627,240 @@ func TestMigrateArrayOfValues(t *testing.T) { ref.Authorization, ) } + +func TestMigrateDictOfValues(t *testing.T) { + t.Parallel() + + address1 := [8]byte{0, 0, 0, 0, 0, 0, 0, 1} + address2 := [8]byte{0, 0, 0, 0, 0, 0, 0, 2} + + storage := NewTestLedger(nil, nil) + rt := NewTestInterpreterRuntime() + + accountCodes := map[common.Location][]byte{} + interfaces := map[common.Location]*TestRuntimeInterface{} + + runtimeInterface1 := &TestRuntimeInterface{ + Storage: storage, + OnEmitEvent: func(event cadence.Event) error { + return nil + }, + OnGetSigningAccounts: func() ([]runtime.Address, error) { + return []runtime.Address{address1}, nil + }, + OnGetCode: func(location common.Location) (bytes []byte, err error) { + return accountCodes[location], nil + }, + OnResolveLocation: MultipleIdentifierLocationResolver, + OnGetAccountContractCode: func(location common.AddressLocation) (code []byte, err error) { + code = accountCodes[location] + return code, nil + }, + } + runtimeInterface1.OnUpdateAccountContractCode = func(location common.AddressLocation, code []byte) error { + accountCodes[location] = code + interfaces[location] = runtimeInterface1 + return nil + } + + runtimeInterface2 := &TestRuntimeInterface{ + Storage: storage, + OnEmitEvent: func(event cadence.Event) error { + return nil + }, + OnGetCode: func(location common.Location) (bytes []byte, err error) { + return accountCodes[location], nil + }, + OnGetSigningAccounts: func() ([]runtime.Address, error) { + return []runtime.Address{address2}, nil + }, + OnResolveLocation: MultipleIdentifierLocationResolver, + OnGetAccountContractCode: func(location common.AddressLocation) (code []byte, err error) { + code = accountCodes[location] + return code, nil + }, + } + runtimeInterface2.OnUpdateAccountContractCode = func(location common.AddressLocation, code []byte) error { + accountCodes[location] = code + interfaces[location] = runtimeInterface2 + return nil + } + + nextTransactionLocation := NewTransactionLocationGenerator() + + oldContract := []byte(` + access(all) contract C { + access(all) resource R { + access(all) fun foo() {} + } + access(all) fun makeR(): @R { + return <- create R() + } + } + `) + + contract := []byte(` + access(all) contract C { + access(all) entitlement E + access(all) resource R { + access(E) fun foo() {} + } + access(all) fun makeR(): @R { + return <- create R() + } + } + `) + + saveValues := []byte(` + import C from 0x1 + + transaction { + prepare(signer: auth(Storage, Capabilities) &Account) { + let r1 <- C.makeR() + let r2 <- C.makeR() + signer.storage.save(<-r1, to: /storage/foo) + signer.storage.save(<-r2, to: /storage/bar) + let cap1 = signer.capabilities.storage.issue<&C.R>(/storage/foo) + let cap2 = signer.capabilities.storage.issue<&C.R>(/storage/bar) + let arr = {"a": cap1, "b": cap2} + signer.storage.save(arr, to: /storage/caps) + } + } + `) + + // Deploy contract to 0x1 + err := rt.ExecuteTransaction( + runtime.Script{ + Source: DeploymentTransaction("C", oldContract), + }, + runtime.Context{ + Interface: runtimeInterface1, + Location: nextTransactionLocation(), + }, + ) + require.NoError(t, err) + + // Execute transaction on 0x2 + err = rt.ExecuteTransaction( + runtime.Script{ + Source: saveValues, + }, + runtime.Context{ + Interface: runtimeInterface2, + Location: nextTransactionLocation(), + }, + ) + require.NoError(t, err) + + // update contract on 0x1 + err = rt.ExecuteTransaction( + runtime.Script{ + Source: UpdateTransaction("C", contract), + }, + runtime.Context{ + Interface: runtimeInterface1, + Location: nextTransactionLocation(), + }, + ) + require.NoError(t, err) + + runtimeStorage := runtime.NewStorage(storage, nil) + + inter, err := interpreter.NewInterpreter( + nil, + utils.TestLocation, + &interpreter.Config{ + Storage: runtimeStorage, + AtreeValueValidationEnabled: false, + AtreeStorageValidationEnabled: false, + ImportLocationHandler: func(inter *interpreter.Interpreter, location common.Location) interpreter.Import { + program, err := rt.ParseAndCheckProgram( + accountCodes[location], + runtime.Context{ + Interface: interfaces[location], + Location: location, + }, + ) + require.NoError(t, err) + + subInterpreter, err := inter.NewSubInterpreter(program, location) + require.NoError(t, err) + + return interpreter.InterpreterImport{ + Interpreter: subInterpreter, + } + }, + }, + ) + require.NoError(t, err) + + storageIdentifier := common.PathDomainStorage.Identifier() + storageMap := runtimeStorage.GetStorageMap(address2, storageIdentifier, false) + require.NotNil(t, storageMap) + require.Greater(t, storageMap.Count(), uint64(0)) + + // Migrate + + migration := migrations.NewStorageMigration(inter, runtimeStorage) + migration.Migrate( + &migrations.AddressSliceIterator{ + Addresses: []common.Address{ + address1, + address2, + }, + }, + nil, + NewEntitlementsMigration(inter), + ) + + dictValue := storageMap.ReadValue(nil, interpreter.StringStorageMapKey("caps")) + require.IsType(t, &interpreter.DictionaryValue{}, dictValue) + dictionaryValue := dictValue.(*interpreter.DictionaryValue) + + valueType := dictionaryValue.Type.ValueType + require.IsType(t, &interpreter.CapabilityStaticType{}, valueType) + capElementType := valueType.(*interpreter.CapabilityStaticType) + require.IsType(t, &interpreter.ReferenceStaticType{}, capElementType.BorrowType) + ref := capElementType.BorrowType.(*interpreter.ReferenceStaticType) + require.Equal(t, + interpreter.NewEntitlementSetAuthorization( + inter, + func() []common.TypeID { return []common.TypeID{"A.0000000000000001.C.E"} }, + 1, + sema.Conjunction, + ), + ref.Authorization, + ) + + cap1, present := dictionaryValue.Get(inter, interpreter.EmptyLocationRange, interpreter.NewUnmeteredStringValue("a")) + require.True(t, present) + require.IsType(t, &interpreter.CapabilityValue{}, cap1) + capValue := cap1.(*interpreter.CapabilityValue) + require.IsType(t, &interpreter.ReferenceStaticType{}, capValue.BorrowType) + ref = capValue.BorrowType.(*interpreter.ReferenceStaticType) + require.Equal(t, + interpreter.NewEntitlementSetAuthorization( + inter, + func() []common.TypeID { return []common.TypeID{"A.0000000000000001.C.E"} }, + 1, + sema.Conjunction, + ), + ref.Authorization, + ) + + cap2, present := dictionaryValue.Get(inter, interpreter.EmptyLocationRange, interpreter.NewUnmeteredStringValue("b")) + require.True(t, present) + require.IsType(t, &interpreter.CapabilityValue{}, cap2) + capValue = cap1.(*interpreter.CapabilityValue) + require.IsType(t, &interpreter.ReferenceStaticType{}, capValue.BorrowType) + ref = capValue.BorrowType.(*interpreter.ReferenceStaticType) + require.Equal(t, + interpreter.NewEntitlementSetAuthorization( + inter, + func() []common.TypeID { return []common.TypeID{"A.0000000000000001.C.E"} }, + 1, + sema.Conjunction, + ), + ref.Authorization, + ) +} diff --git a/migrations/migration.go b/migrations/migration.go index 3bb4b009c0..700102a87c 100644 --- a/migrations/migration.go +++ b/migrations/migration.go @@ -83,7 +83,7 @@ func (m *StorageMigration) migrateValuesInAccount( domain common.PathDomain, key string, ) interpreter.Value { - return m.migrateNestedValue(value, address, domain, key, migrations, reporter) + return m.MigrateNestedValue(value, address, domain, key, migrations, reporter) } accountStorage.ForEachValue( @@ -95,7 +95,7 @@ func (m *StorageMigration) migrateValuesInAccount( var emptyLocationRange = interpreter.EmptyLocationRange -func (m *StorageMigration) migrateNestedValue( +func (m *StorageMigration) MigrateNestedValue( value interpreter.Value, address common.Address, domain common.PathDomain, @@ -106,7 +106,7 @@ func (m *StorageMigration) migrateNestedValue( switch value := value.(type) { case *interpreter.SomeValue: innerValue := value.InnerValue(m.interpreter, emptyLocationRange) - newInnerValue := m.migrateNestedValue(innerValue, address, domain, key, migrations, reporter) + newInnerValue := m.MigrateNestedValue(innerValue, address, domain, key, migrations, reporter) if newInnerValue != nil { return interpreter.NewSomeValueNonCopying(m.interpreter, newInnerValue) } @@ -120,7 +120,7 @@ func (m *StorageMigration) migrateNestedValue( count := array.Count() for index := 0; index < count; index++ { element := array.Get(m.interpreter, emptyLocationRange, index) - newElement := m.migrateNestedValue(element, address, domain, key, migrations, reporter) + newElement := m.MigrateNestedValue(element, address, domain, key, migrations, reporter) if newElement != nil { array.Set( m.interpreter, @@ -145,7 +145,7 @@ func (m *StorageMigration) migrateNestedValue( for _, fieldName := range fieldNames { existingValue := composite.GetField(m.interpreter, interpreter.EmptyLocationRange, fieldName) - migratedValue := m.migrateNestedValue(existingValue, address, domain, key, migrations, reporter) + migratedValue := m.MigrateNestedValue(existingValue, address, domain, key, migrations, reporter) if migratedValue == nil { continue } @@ -170,8 +170,8 @@ func (m *StorageMigration) migrateNestedValue( panic(errors.NewUnreachableError()) } - newKey := m.migrateNestedValue(existingKey, address, domain, key, migrations, reporter) - newValue := m.migrateNestedValue(existingValue, address, domain, key, migrations, reporter) + newKey := m.MigrateNestedValue(existingKey, address, domain, key, migrations, reporter) + newValue := m.MigrateNestedValue(existingValue, address, domain, key, migrations, reporter) if newKey == nil && newValue == nil { continue } @@ -203,7 +203,6 @@ func (m *StorageMigration) migrateNestedValue( } } - // Assumption: all migrations only migrate non-container typed values. for _, migration := range migrations { converted := migration.Migrate(value) diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index d36c393d91..7edcc055e4 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -5462,134 +5462,3 @@ func (interpreter *Interpreter) withResourceDestruction( f() } - -// Converts the input value into a version compatible with the new entitlements feature, -// with the same members/operations accessible on any references as would have been accessible in the past. -// Modifies the input `v` in place. -// This is used for migrations, but must be located in this package because some of the fields we wish to modify are -// privately scoped to this package -func (interpreter *Interpreter) ConvertValueToEntitlements( - v Value, - convertToEntitledType func(sema.Type) sema.Type, -) Value { - - var staticType StaticType - // for reference types, we want to use the borrow type, rather than the type of the referenced value - switch referenceValue := v.(type) { - case *EphemeralReferenceValue: - staticType = NewReferenceStaticType( - interpreter, - referenceValue.Authorization, - ConvertSemaToStaticType(interpreter, referenceValue.BorrowedType), - ) - case *StorageReferenceValue: - staticType = NewReferenceStaticType( - interpreter, - referenceValue.Authorization, - ConvertSemaToStaticType(interpreter, referenceValue.BorrowedType), - ) - default: - staticType = v.StaticType(interpreter) - } - - // if the static type contains a legacy restricted type, convert it to a new type according to some rules: - // &T{I} -> auth(SupportedEntitlements(I)) &T - // Capability<&T{I}> -> Capability - var convertLegacyStaticType func(StaticType) - convertLegacyStaticType = func(staticType StaticType) { - switch t := staticType.(type) { - case *ReferenceStaticType: - switch referencedType := t.ReferencedType.(type) { - case *IntersectionStaticType: - if referencedType.LegacyType != nil { - t.ReferencedType = referencedType.LegacyType - intersectionSemaType := interpreter.MustConvertStaticToSemaType(referencedType).(*sema.IntersectionType) - auth := sema.UnauthorizedAccess - supportedEntitlements := intersectionSemaType.SupportedEntitlements() - if supportedEntitlements.Len() > 0 { - auth = sema.EntitlementSetAccess{ - SetKind: sema.Conjunction, - Entitlements: supportedEntitlements, - } - } - t.Authorization = ConvertSemaAccessToStaticAuthorization(interpreter, auth) - } - } - case *CapabilityStaticType: - convertLegacyStaticType(t.BorrowType) - case *VariableSizedStaticType: - convertLegacyStaticType(t.Type) - case *ConstantSizedStaticType: - convertLegacyStaticType(t.Type) - case *DictionaryStaticType: - convertLegacyStaticType(t.KeyType) - convertLegacyStaticType(t.ValueType) - case *OptionalStaticType: - convertLegacyStaticType(t.Type) - } - } - - convertLegacyStaticType(staticType) - semaType := interpreter.MustConvertStaticToSemaType(staticType) - entitledType := convertToEntitledType(semaType) - - switch v := v.(type) { - case *EphemeralReferenceValue: - entitledReferenceType := entitledType.(*sema.ReferenceType) - staticAuthorization := ConvertSemaAccessToStaticAuthorization(interpreter, entitledReferenceType.Authorization) - convertedValue := interpreter.ConvertValueToEntitlements(v.Value, convertToEntitledType) - return NewEphemeralReferenceValue( - interpreter, - staticAuthorization, - convertedValue, - entitledReferenceType.Type, - EmptyLocationRange, - ) - - case *StorageReferenceValue: - // a stored value will in itself be migrated at another point, so no need to do anything here other than change the type - entitledReferenceType := entitledType.(*sema.ReferenceType) - staticAuthorization := ConvertSemaAccessToStaticAuthorization(interpreter, entitledReferenceType.Authorization) - return NewStorageReferenceValue( - interpreter, - staticAuthorization, - v.TargetStorageAddress, - v.TargetPath, - entitledReferenceType.Type, - ) - case *SomeValue: - return NewSomeValueNonCopying(interpreter, interpreter.ConvertValueToEntitlements(v.value, convertToEntitledType)) - - case *ArrayValue: - entitledArrayType := entitledType.(sema.ArrayType) - arrayStaticType := ConvertSemaArrayTypeToStaticArrayType(interpreter, entitledArrayType) - return newArrayValueFromAtreeArray(interpreter, arrayStaticType, v.elementSize, v.array) - - case *DictionaryValue: - entitledDictionaryType := entitledType.(*sema.DictionaryType) - dictionaryStaticType := ConvertSemaDictionaryTypeToStaticDictionaryType(interpreter, entitledDictionaryType) - return newDictionaryValueFromAtreeMap(interpreter, dictionaryStaticType, v.elementSize, v.dictionary) - - case *CapabilityValue: - // capabilities should just have their borrow type updated, as the pointed-to value will also be visited - // by the migration on its own - entitledCapabilityValue := entitledType.(*sema.CapabilityType) - capabilityStaticType := ConvertSemaToStaticType(interpreter, entitledCapabilityValue.BorrowType) - return NewCapabilityValue(interpreter, v.ID, v.Address, capabilityStaticType) - - case *TypeValue: - if v.Type == nil { - return v - } - // convert the static type of the value - entitledStaticType := ConvertSemaToStaticType( - interpreter, - convertToEntitledType( - interpreter.MustConvertStaticToSemaType(v.Type), - ), - ) - return NewTypeValue(interpreter, entitledStaticType) - } - - return nil -} From 600eafc39fd4b1679b03115e602947c5cf0213e7 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Wed, 13 Dec 2023 14:31:27 -0500 Subject: [PATCH 29/40] fix comment --- migrations/entitlements/migration.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/migrations/entitlements/migration.go b/migrations/entitlements/migration.go index 16375c514f..9c2a5c978e 100644 --- a/migrations/entitlements/migration.go +++ b/migrations/entitlements/migration.go @@ -86,9 +86,6 @@ func ConvertToEntitledType(t sema.Type) sema.Type { // Converts the input value into a version compatible with the new entitlements feature, // with the same members/operations accessible on any references as would have been accessible in the past. -// Modifies the input `v` in place. -// This is used for migrations, but must be located in this package because some of the fields we wish to modify are -// privately scoped to this package func ConvertValueToEntitlements( inter *interpreter.Interpreter, v interpreter.Value, From 59e9af86460eca4a7d349ca1e4a903a0697f78ed Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Wed, 13 Dec 2023 15:23:02 -0500 Subject: [PATCH 30/40] respond to review --- migrations/entitlements/migration.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/migrations/entitlements/migration.go b/migrations/entitlements/migration.go index 9c2a5c978e..41c2dbe06a 100644 --- a/migrations/entitlements/migration.go +++ b/migrations/entitlements/migration.go @@ -151,6 +151,11 @@ func ConvertValueToEntitlements( semaType := inter.MustConvertStaticToSemaType(staticType) entitledType := ConvertToEntitledType(semaType) + // if the types of the values are equal and the value is not a runtime type, there's nothing to migrate + if entitledType.Equal(semaType) && !entitledType.Equal(sema.MetaType) { + return nil + } + switch v := v.(type) { case *interpreter.EphemeralReferenceValue: entitledReferenceType := entitledType.(*sema.ReferenceType) @@ -220,7 +225,7 @@ func ConvertValueToEntitlements( case *interpreter.TypeValue: if v.Type == nil { - return v + return nil } // convert the static type of the value entitledStaticType := interpreter.ConvertSemaToStaticType( From 9ebd4168d048658abacef3f9be56b15a8625cbc4 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Fri, 15 Dec 2023 14:51:33 -0500 Subject: [PATCH 31/40] revert change of TypeValue to pointer type --- migrations/account_type/migration.go | 2 +- migrations/account_type/migration_test.go | 2 +- migrations/entitlements/migration.go | 2 +- runtime/convertValues.go | 6 +- runtime/convertValues_test.go | 6 +- runtime/interpreter/decode.go | 2 +- runtime/interpreter/encoding_test.go | 8 +-- runtime/interpreter/interpreter.go | 20 +++--- runtime/interpreter/value.go | 64 ++++++++--------- runtime/interpreter/value_test.go | 10 +-- runtime/interpreter/visitor.go | 6 +- runtime/stdlib/test_emulatorbackend.go | 2 +- runtime/tests/interpreter/account_test.go | 4 +- runtime/tests/interpreter/attachments_test.go | 4 +- runtime/tests/interpreter/interpreter_test.go | 14 ++-- runtime/tests/interpreter/metatype_test.go | 30 ++++---- runtime/tests/interpreter/runtimetype_test.go | 70 +++++++++---------- runtime/tests/interpreter/string_test.go | 8 +-- 18 files changed, 130 insertions(+), 130 deletions(-) diff --git a/migrations/account_type/migration.go b/migrations/account_type/migration.go index 2a52272039..b3b0502410 100644 --- a/migrations/account_type/migration.go +++ b/migrations/account_type/migration.go @@ -41,7 +41,7 @@ func (AccountTypeMigration) Name() string { // to the account reference type (&Account). func (AccountTypeMigration) Migrate(value interpreter.Value) (newValue interpreter.Value) { switch value := value.(type) { - case *interpreter.TypeValue: + case interpreter.TypeValue: convertedType := maybeConvertAccountType(value.Type) if convertedType == nil { return diff --git a/migrations/account_type/migration_test.go b/migrations/account_type/migration_test.go index 6e87a69373..349ef70e5a 100644 --- a/migrations/account_type/migration_test.go +++ b/migrations/account_type/migration_test.go @@ -407,7 +407,7 @@ func TestTypeValueMigration(t *testing.T) { // `IntersectionType.LegacyType` is not considered in the `IntersectionType.Equal` method. // Therefore, check for the legacy type equality manually. - typeValue := value.(*interpreter.TypeValue) + typeValue := value.(interpreter.TypeValue) if actualIntersectionType, ok := typeValue.Type.(*interpreter.IntersectionStaticType); ok { expectedIntersectionType := testCase.expectedType.(*interpreter.IntersectionStaticType) assert.True(t, actualIntersectionType.LegacyType.Equal(expectedIntersectionType.LegacyType)) diff --git a/migrations/entitlements/migration.go b/migrations/entitlements/migration.go index 41c2dbe06a..577c9e16ee 100644 --- a/migrations/entitlements/migration.go +++ b/migrations/entitlements/migration.go @@ -223,7 +223,7 @@ func ConvertValueToEntitlements( capabilityStaticType := interpreter.ConvertSemaToStaticType(inter, entitledCapabilityValue.BorrowType) return interpreter.NewCapabilityValue(inter, v.ID, v.Address, capabilityStaticType) - case *interpreter.TypeValue: + case interpreter.TypeValue: if v.Type == nil { return nil } diff --git a/runtime/convertValues.go b/runtime/convertValues.go index 1ff191b6da..f67836cab9 100644 --- a/runtime/convertValues.go +++ b/runtime/convertValues.go @@ -227,7 +227,7 @@ func exportValueWithInterpreter( return cadence.NewMeteredAddress(inter, v), nil case interpreter.PathValue: return exportPathValue(inter, v) - case *interpreter.TypeValue: + case interpreter.TypeValue: return exportTypeValue(v, inter), nil case *interpreter.CapabilityValue: return exportCapabilityValue(v, inter) @@ -604,7 +604,7 @@ func exportPathValue(gauge common.MemoryGauge, v interpreter.PathValue) (cadence ) } -func exportTypeValue(v *interpreter.TypeValue, inter *interpreter.Interpreter) cadence.TypeValue { +func exportTypeValue(v interpreter.TypeValue, inter *interpreter.Interpreter) cadence.TypeValue { var typ sema.Type if v.Type != nil { typ = inter.MustConvertStaticToSemaType(v.Type) @@ -1068,7 +1068,7 @@ func (i valueImporter) importPathValue(v cadence.Path) interpreter.PathValue { ) } -func (i valueImporter) importTypeValue(v cadence.Type) (*interpreter.TypeValue, error) { +func (i valueImporter) importTypeValue(v cadence.Type) (interpreter.TypeValue, error) { inter := i.inter typ := ImportType(inter, v) diff --git a/runtime/convertValues_test.go b/runtime/convertValues_test.go index 1bcf0f5229..e0635104f7 100644 --- a/runtime/convertValues_test.go +++ b/runtime/convertValues_test.go @@ -881,7 +881,7 @@ func TestRuntimeImportValue(t *testing.T) { { label: "Type()", value: cadence.NewTypeValue(cadence.IntType), - expected: &interpreter.TypeValue{Type: interpreter.PrimitiveStaticTypeInt}, + expected: interpreter.TypeValue{Type: interpreter.PrimitiveStaticTypeInt}, }, } { test(tt) @@ -2104,7 +2104,7 @@ func TestRuntimeExportTypeValue(t *testing.T) { t.Parallel() - value := &interpreter.TypeValue{ + value := interpreter.TypeValue{ Type: nil, } actual, err := ExportValue( @@ -2150,7 +2150,7 @@ func TestRuntimeExportTypeValue(t *testing.T) { inter := NewTestInterpreter(t) inter.Program = interpreter.ProgramFromChecker(checker) - ty := &interpreter.TypeValue{ + ty := interpreter.TypeValue{ Type: &interpreter.IntersectionStaticType{ Types: []*interpreter.InterfaceStaticType{ interpreter.NewInterfaceStaticTypeComputeTypeID(nil, TestLocation, "SI"), diff --git a/runtime/interpreter/decode.go b/runtime/interpreter/decode.go index 50014210eb..acb204ea96 100644 --- a/runtime/interpreter/decode.go +++ b/runtime/interpreter/decode.go @@ -1179,7 +1179,7 @@ func (d StorableDecoder) decodePublishedValue() (*PublishedValue, error) { return NewPublishedValue(d.memoryGauge, addressValue, capabilityValue), nil } -func (d StorableDecoder) decodeType() (*TypeValue, error) { +func (d StorableDecoder) decodeType() (TypeValue, error) { const expectedLength = encodedTypeValueTypeLength arraySize, err := d.decoder.DecodeArrayHead() diff --git a/runtime/interpreter/encoding_test.go b/runtime/interpreter/encoding_test.go index 4a1a33aee1..6ad603fddb 100644 --- a/runtime/interpreter/encoding_test.go +++ b/runtime/interpreter/encoding_test.go @@ -3372,7 +3372,7 @@ func TestEncodeDecodeTypeValue(t *testing.T) { t.Parallel() - value := &TypeValue{ + value := TypeValue{ Type: ConvertSemaToPrimitiveStaticType(nil, sema.BoolType), } @@ -3399,7 +3399,7 @@ func TestEncodeDecodeTypeValue(t *testing.T) { t.Parallel() - value := &TypeValue{ + value := TypeValue{ Type: ConvertSemaToPrimitiveStaticType(nil, sema.IntType), } @@ -3426,7 +3426,7 @@ func TestEncodeDecodeTypeValue(t *testing.T) { t.Parallel() - value := &TypeValue{ + value := TypeValue{ Type: nil, } @@ -3457,7 +3457,7 @@ func TestEncodeDecodeTypeValue(t *testing.T) { maxInlineElementSize := atree.MaxInlineArrayElementSize identifier := strings.Repeat("x", int(maxInlineElementSize+1)) - expected := &TypeValue{ + expected := TypeValue{ Type: NewCompositeStaticTypeComputeTypeID( nil, common.AddressLocation{}, diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 7edcc055e4..7ec3a47007 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -3420,12 +3420,12 @@ func init() { } func dictionaryTypeFunction(invocation Invocation) Value { - keyTypeValue, ok := invocation.Arguments[0].(*TypeValue) + keyTypeValue, ok := invocation.Arguments[0].(TypeValue) if !ok { panic(errors.NewUnreachableError()) } - valueTypeValue, ok := invocation.Arguments[1].(*TypeValue) + valueTypeValue, ok := invocation.Arguments[1].(TypeValue) if !ok { panic(errors.NewUnreachableError()) } @@ -3461,7 +3461,7 @@ func referenceTypeFunction(invocation Invocation) Value { panic(errors.NewUnreachableError()) } - typeValue, ok := invocation.Arguments[1].(*TypeValue) + typeValue, ok := invocation.Arguments[1].(TypeValue) if !ok { panic(errors.NewUnreachableError()) } @@ -3565,7 +3565,7 @@ func functionTypeFunction(invocation Invocation) Value { panic(errors.NewUnreachableError()) } - typeValue, ok := invocation.Arguments[1].(*TypeValue) + typeValue, ok := invocation.Arguments[1].(TypeValue) if !ok { panic(errors.NewUnreachableError()) } @@ -3577,7 +3577,7 @@ func functionTypeFunction(invocation Invocation) Value { if parameterCount > 0 { parameterTypes = make([]sema.Parameter, 0, parameterCount) parameters.Iterate(interpreter, func(param Value) bool { - semaType := interpreter.MustConvertStaticToSemaType(param.(*TypeValue).Type) + semaType := interpreter.MustConvertStaticToSemaType(param.(TypeValue).Type) parameterTypes = append( parameterTypes, sema.Parameter{ @@ -3756,7 +3756,7 @@ var runtimeTypeConstructors = []runtimeTypeConstructor{ converter: NewUnmeteredHostFunctionValue( sema.OptionalTypeFunctionType, func(invocation Invocation) Value { - typeValue, ok := invocation.Arguments[0].(*TypeValue) + typeValue, ok := invocation.Arguments[0].(TypeValue) if !ok { panic(errors.NewUnreachableError()) } @@ -3776,7 +3776,7 @@ var runtimeTypeConstructors = []runtimeTypeConstructor{ converter: NewUnmeteredHostFunctionValue( sema.VariableSizedArrayTypeFunctionType, func(invocation Invocation) Value { - typeValue, ok := invocation.Arguments[0].(*TypeValue) + typeValue, ok := invocation.Arguments[0].(TypeValue) if !ok { panic(errors.NewUnreachableError()) } @@ -3797,7 +3797,7 @@ var runtimeTypeConstructors = []runtimeTypeConstructor{ converter: NewUnmeteredHostFunctionValue( sema.ConstantSizedArrayTypeFunctionType, func(invocation Invocation) Value { - typeValue, ok := invocation.Arguments[0].(*TypeValue) + typeValue, ok := invocation.Arguments[0].(TypeValue) if !ok { panic(errors.NewUnreachableError()) } @@ -3823,7 +3823,7 @@ var runtimeTypeConstructors = []runtimeTypeConstructor{ converter: NewUnmeteredHostFunctionValue( sema.CapabilityTypeFunctionType, func(invocation Invocation) Value { - typeValue, ok := invocation.Arguments[0].(*TypeValue) + typeValue, ok := invocation.Arguments[0].(TypeValue) if !ok { panic(errors.NewUnreachableError()) } @@ -4909,7 +4909,7 @@ func (interpreter *Interpreter) isInstanceFunction(self Value) *HostFunctionValu interpreter := invocation.Interpreter firstArgument := invocation.Arguments[0] - typeValue, ok := firstArgument.(*TypeValue) + typeValue, ok := firstArgument.(TypeValue) if !ok { panic(errors.NewUnreachableError()) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 6108e089c5..deace59e76 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -310,44 +310,44 @@ type TypeValue struct { Type StaticType } -var EmptyTypeValue = &TypeValue{} +var EmptyTypeValue = TypeValue{} -var _ Value = &TypeValue{} -var _ atree.Storable = &TypeValue{} -var _ EquatableValue = &TypeValue{} -var _ MemberAccessibleValue = &TypeValue{} +var _ Value = TypeValue{} +var _ atree.Storable = TypeValue{} +var _ EquatableValue = TypeValue{} +var _ MemberAccessibleValue = TypeValue{} -func NewUnmeteredTypeValue(t StaticType) *TypeValue { - return &TypeValue{Type: t} +func NewUnmeteredTypeValue(t StaticType) TypeValue { + return TypeValue{Type: t} } func NewTypeValue( memoryGauge common.MemoryGauge, staticType StaticType, -) *TypeValue { +) TypeValue { common.UseMemory(memoryGauge, common.TypeValueMemoryUsage) return NewUnmeteredTypeValue(staticType) } -func (*TypeValue) isValue() {} +func (TypeValue) isValue() {} -func (v *TypeValue) Accept(interpreter *Interpreter, visitor Visitor) { +func (v TypeValue) Accept(interpreter *Interpreter, visitor Visitor) { visitor.VisitTypeValue(interpreter, v) } -func (*TypeValue) Walk(_ *Interpreter, _ func(Value)) { +func (TypeValue) Walk(_ *Interpreter, _ func(Value)) { // NO-OP } -func (*TypeValue) StaticType(interpreter *Interpreter) StaticType { +func (TypeValue) StaticType(interpreter *Interpreter) StaticType { return NewPrimitiveStaticType(interpreter, PrimitiveStaticTypeMetaType) } -func (*TypeValue) IsImportable(_ *Interpreter) bool { +func (TypeValue) IsImportable(_ *Interpreter) bool { return sema.MetaType.Importable } -func (v *TypeValue) String() string { +func (v TypeValue) String() string { var typeString string staticType := v.Type if staticType != nil { @@ -357,11 +357,11 @@ func (v *TypeValue) String() string { return format.TypeValue(typeString) } -func (v *TypeValue) RecursiveString(_ SeenReferences) string { +func (v TypeValue) RecursiveString(_ SeenReferences) string { return v.String() } -func (v *TypeValue) MeteredString(memoryGauge common.MemoryGauge, _ SeenReferences) string { +func (v TypeValue) MeteredString(memoryGauge common.MemoryGauge, _ SeenReferences) string { common.UseMemory(memoryGauge, common.TypeValueStringMemoryUsage) var typeString string @@ -372,8 +372,8 @@ func (v *TypeValue) MeteredString(memoryGauge common.MemoryGauge, _ SeenReferenc return format.TypeValue(typeString) } -func (v *TypeValue) Equal(_ *Interpreter, _ LocationRange, other Value) bool { - otherTypeValue, ok := other.(*TypeValue) +func (v TypeValue) Equal(_ *Interpreter, _ LocationRange, other Value) bool { + otherTypeValue, ok := other.(TypeValue) if !ok { return false } @@ -390,7 +390,7 @@ func (v *TypeValue) Equal(_ *Interpreter, _ LocationRange, other Value) bool { return staticType.Equal(otherStaticType) } -func (v *TypeValue) GetMember(interpreter *Interpreter, _ LocationRange, name string) Value { +func (v TypeValue) GetMember(interpreter *Interpreter, _ LocationRange, name string) Value { switch name { case sema.MetaTypeIdentifierFieldName: var typeID string @@ -411,7 +411,7 @@ func (v *TypeValue) GetMember(interpreter *Interpreter, _ LocationRange, name st interpreter := invocation.Interpreter staticType := v.Type - otherTypeValue, ok := invocation.Arguments[0].(*TypeValue) + otherTypeValue, ok := invocation.Arguments[0].(TypeValue) if !ok { panic(errors.NewUnreachableError()) } @@ -434,17 +434,17 @@ func (v *TypeValue) GetMember(interpreter *Interpreter, _ LocationRange, name st return nil } -func (*TypeValue) RemoveMember(_ *Interpreter, _ LocationRange, _ string) Value { +func (TypeValue) RemoveMember(_ *Interpreter, _ LocationRange, _ string) Value { // Types have no removable members (fields / functions) panic(errors.NewUnreachableError()) } -func (*TypeValue) SetMember(_ *Interpreter, _ LocationRange, _ string, _ Value) bool { +func (TypeValue) SetMember(_ *Interpreter, _ LocationRange, _ string, _ Value) bool { // Types have no settable members (fields / functions) panic(errors.NewUnreachableError()) } -func (v *TypeValue) ConformsToStaticType( +func (v TypeValue) ConformsToStaticType( _ *Interpreter, _ LocationRange, _ TypeConformanceResults, @@ -452,7 +452,7 @@ func (v *TypeValue) ConformsToStaticType( return true } -func (v *TypeValue) Storable( +func (v TypeValue) Storable( storage atree.SlabStorage, address atree.Address, maxInlineSize uint64, @@ -465,15 +465,15 @@ func (v *TypeValue) Storable( ) } -func (*TypeValue) NeedsStoreTo(_ atree.Address) bool { +func (TypeValue) NeedsStoreTo(_ atree.Address) bool { return false } -func (*TypeValue) IsResourceKinded(_ *Interpreter) bool { +func (TypeValue) IsResourceKinded(_ *Interpreter) bool { return false } -func (v *TypeValue) Transfer( +func (v TypeValue) Transfer( interpreter *Interpreter, _ LocationRange, _ atree.Address, @@ -487,23 +487,23 @@ func (v *TypeValue) Transfer( return v } -func (v *TypeValue) Clone(_ *Interpreter) Value { +func (v TypeValue) Clone(_ *Interpreter) Value { return v } -func (*TypeValue) DeepRemove(_ *Interpreter) { +func (TypeValue) DeepRemove(_ *Interpreter) { // NO-OP } -func (v *TypeValue) ByteSize() uint32 { +func (v TypeValue) ByteSize() uint32 { return mustStorableSize(v) } -func (v *TypeValue) StoredValue(_ atree.SlabStorage) (atree.Value, error) { +func (v TypeValue) StoredValue(_ atree.SlabStorage) (atree.Value, error) { return v, nil } -func (*TypeValue) ChildStorables() []atree.Storable { +func (TypeValue) ChildStorables() []atree.Storable { return nil } diff --git a/runtime/interpreter/value_test.go b/runtime/interpreter/value_test.go index 724fe4429d..9d5117f618 100644 --- a/runtime/interpreter/value_test.go +++ b/runtime/interpreter/value_test.go @@ -2112,12 +2112,12 @@ func TestTypeValue_Equal(t *testing.T) { inter := newTestInterpreter(t) require.True(t, - (&TypeValue{ + (TypeValue{ Type: PrimitiveStaticTypeString, }).Equal( inter, EmptyLocationRange, - &TypeValue{ + TypeValue{ Type: PrimitiveStaticTypeString, }, ), @@ -2131,12 +2131,12 @@ func TestTypeValue_Equal(t *testing.T) { inter := newTestInterpreter(t) require.False(t, - (&TypeValue{ + (TypeValue{ Type: PrimitiveStaticTypeString, }).Equal( inter, EmptyLocationRange, - &TypeValue{ + TypeValue{ Type: PrimitiveStaticTypeInt, }, ), @@ -2150,7 +2150,7 @@ func TestTypeValue_Equal(t *testing.T) { inter := newTestInterpreter(t) require.False(t, - (&TypeValue{ + (TypeValue{ Type: PrimitiveStaticTypeString, }).Equal( inter, diff --git a/runtime/interpreter/visitor.go b/runtime/interpreter/visitor.go index 6f88231c73..ffd125917f 100644 --- a/runtime/interpreter/visitor.go +++ b/runtime/interpreter/visitor.go @@ -20,7 +20,7 @@ package interpreter type Visitor interface { VisitSimpleCompositeValue(interpreter *Interpreter, value *SimpleCompositeValue) - VisitTypeValue(interpreter *Interpreter, value *TypeValue) + VisitTypeValue(interpreter *Interpreter, value TypeValue) VisitVoidValue(interpreter *Interpreter, value VoidValue) VisitBoolValue(interpreter *Interpreter, value BoolValue) VisitStringValue(interpreter *Interpreter, value *StringValue) @@ -67,7 +67,7 @@ type Visitor interface { type EmptyVisitor struct { SimpleCompositeValueVisitor func(interpreter *Interpreter, value *SimpleCompositeValue) - TypeValueVisitor func(interpreter *Interpreter, value *TypeValue) + TypeValueVisitor func(interpreter *Interpreter, value TypeValue) VoidValueVisitor func(interpreter *Interpreter, value VoidValue) BoolValueVisitor func(interpreter *Interpreter, value BoolValue) CharacterValueVisitor func(interpreter *Interpreter, value CharacterValue) @@ -121,7 +121,7 @@ func (v EmptyVisitor) VisitSimpleCompositeValue(interpreter *Interpreter, value v.SimpleCompositeValueVisitor(interpreter, value) } -func (v EmptyVisitor) VisitTypeValue(interpreter *Interpreter, value *TypeValue) { +func (v EmptyVisitor) VisitTypeValue(interpreter *Interpreter, value TypeValue) { if v.TypeValueVisitor == nil { return } diff --git a/runtime/stdlib/test_emulatorbackend.go b/runtime/stdlib/test_emulatorbackend.go index 2dd0052f32..1fa3d6eef6 100644 --- a/runtime/stdlib/test_emulatorbackend.go +++ b/runtime/stdlib/test_emulatorbackend.go @@ -668,7 +668,7 @@ func (t *testEmulatorBackendType) newEventsFunction( // Do nothing case *interpreter.SomeValue: innerValue := value.InnerValue(invocation.Interpreter, invocation.LocationRange) - typeValue, ok := innerValue.(*interpreter.TypeValue) + typeValue, ok := innerValue.(interpreter.TypeValue) if !ok { panic(errors.NewUnreachableError()) } diff --git a/runtime/tests/interpreter/account_test.go b/runtime/tests/interpreter/account_test.go index bd75b19875..1086b4ca6f 100644 --- a/runtime/tests/interpreter/account_test.go +++ b/runtime/tests/interpreter/account_test.go @@ -637,7 +637,7 @@ func TestInterpretAccountStorageType(t *testing.T) { require.NoError(t, err) require.Equal(t, interpreter.NewUnmeteredSomeValueNonCopying( - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: interpreter.NewCompositeStaticTypeComputeTypeID(nil, TestLocation, "R"), }, ), @@ -656,7 +656,7 @@ func TestInterpretAccountStorageType(t *testing.T) { require.NoError(t, err) require.Equal(t, interpreter.NewUnmeteredSomeValueNonCopying( - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: interpreter.NewCompositeStaticTypeComputeTypeID(nil, TestLocation, "S"), }, ), diff --git a/runtime/tests/interpreter/attachments_test.go b/runtime/tests/interpreter/attachments_test.go index dd36c618ee..9795b95c7c 100644 --- a/runtime/tests/interpreter/attachments_test.go +++ b/runtime/tests/interpreter/attachments_test.go @@ -1817,8 +1817,8 @@ func TestInterpretAttachmentsRuntimeType(t *testing.T) { a, err := inter.Invoke("test") require.NoError(t, err) - require.IsType(t, &interpreter.TypeValue{}, a) - require.Equal(t, "S.test.A", a.(*interpreter.TypeValue).Type.String()) + require.IsType(t, interpreter.TypeValue{}, a) + require.Equal(t, "S.test.A", a.(interpreter.TypeValue).Type.String()) }) } diff --git a/runtime/tests/interpreter/interpreter_test.go b/runtime/tests/interpreter/interpreter_test.go index 96bdaf529b..f189dcd5ad 100644 --- a/runtime/tests/interpreter/interpreter_test.go +++ b/runtime/tests/interpreter/interpreter_test.go @@ -9910,8 +9910,8 @@ func TestInterpretHostFunctionStaticType(t *testing.T) { value.StaticType(inter), ) - require.IsType(t, &interpreter.TypeValue{}, value) - typeValue := value.(*interpreter.TypeValue) + require.IsType(t, interpreter.TypeValue{}, value) + typeValue := value.(interpreter.TypeValue) assert.Equal(t, interpreter.PrimitiveStaticTypeInt8, typeValue.Type) }) @@ -9967,7 +9967,7 @@ func TestInterpretArrayTypeInference(t *testing.T) { AssertValuesEqual( t, inter, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: &interpreter.VariableSizedStaticType{ Type: interpreter.PrimitiveStaticTypeAnyStruct, }, @@ -9992,7 +9992,7 @@ func TestInterpretArrayTypeInference(t *testing.T) { AssertValuesEqual( t, inter, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: &interpreter.VariableSizedStaticType{ Type: interpreter.PrimitiveStaticTypeInt, }, @@ -11173,7 +11173,7 @@ func TestInterpretCastingBoxing(t *testing.T) { require.Equal( t, interpreter.NewUnmeteredSomeValueNonCopying( - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: interpreter.PrimitiveStaticTypeInt, }, ), @@ -11195,7 +11195,7 @@ func TestInterpretCastingBoxing(t *testing.T) { require.Equal( t, interpreter.NewUnmeteredSomeValueNonCopying( - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: interpreter.PrimitiveStaticTypeInt, }, ), @@ -11217,7 +11217,7 @@ func TestInterpretCastingBoxing(t *testing.T) { require.Equal( t, interpreter.NewUnmeteredSomeValueNonCopying( - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: interpreter.PrimitiveStaticTypeInt, }, ), diff --git a/runtime/tests/interpreter/metatype_test.go b/runtime/tests/interpreter/metatype_test.go index b67b97355f..cee970ae6e 100644 --- a/runtime/tests/interpreter/metatype_test.go +++ b/runtime/tests/interpreter/metatype_test.go @@ -124,7 +124,7 @@ func TestInterpretMetaTypeEquality(t *testing.T) { valueDeclaration := stdlib.StandardLibraryValue{ Name: "unknownType", Type: sema.MetaType, - Value: &interpreter.TypeValue{ + Value: interpreter.TypeValue{ Type: nil, }, Kind: common.DeclarationKindConstant, @@ -171,7 +171,7 @@ func TestInterpretMetaTypeEquality(t *testing.T) { { Name: "unknownType1", Type: sema.MetaType, - Value: &interpreter.TypeValue{ + Value: interpreter.TypeValue{ Type: nil, }, Kind: common.DeclarationKindConstant, @@ -179,7 +179,7 @@ func TestInterpretMetaTypeEquality(t *testing.T) { { Name: "unknownType2", Type: sema.MetaType, - Value: &interpreter.TypeValue{ + Value: interpreter.TypeValue{ Type: nil, }, Kind: common.DeclarationKindConstant, @@ -272,7 +272,7 @@ func TestInterpretMetaTypeIdentifier(t *testing.T) { { Name: "unknownType", Type: sema.MetaType, - Value: &interpreter.TypeValue{ + Value: interpreter.TypeValue{ Type: nil, }, Kind: common.DeclarationKindConstant, @@ -436,7 +436,7 @@ func TestInterpretIsInstance(t *testing.T) { valueDeclaration := stdlib.StandardLibraryValue{ Name: "unknownType", Type: sema.MetaType, - Value: &interpreter.TypeValue{ + Value: interpreter.TypeValue{ Type: nil, }, Kind: common.DeclarationKindConstant, @@ -580,7 +580,7 @@ func TestInterpretMetaTypeIsSubtype(t *testing.T) { valueDeclaration := stdlib.StandardLibraryValue{ Name: "unknownType", Type: sema.MetaType, - Value: &interpreter.TypeValue{ + Value: interpreter.TypeValue{ Type: nil, }, Kind: common.DeclarationKindConstant, @@ -632,7 +632,7 @@ func TestInterpretGetType(t *testing.T) { return "abc".getType() } `, - result: &interpreter.TypeValue{ + result: interpreter.TypeValue{ Type: interpreter.PrimitiveStaticTypeString, }, }, @@ -643,7 +643,7 @@ func TestInterpretGetType(t *testing.T) { return (1).getType() } `, - result: &interpreter.TypeValue{ + result: interpreter.TypeValue{ Type: interpreter.PrimitiveStaticTypeInt, }, }, @@ -659,7 +659,7 @@ func TestInterpretGetType(t *testing.T) { return res } `, - result: &interpreter.TypeValue{ + result: interpreter.TypeValue{ Type: interpreter.NewCompositeStaticTypeComputeTypeID(nil, TestLocation, "R"), }, }, @@ -678,7 +678,7 @@ func TestInterpretGetType(t *testing.T) { return optRef.getType() } `, - result: &interpreter.TypeValue{ + result: interpreter.TypeValue{ Type: &interpreter.OptionalStaticType{ Type: &interpreter.ReferenceStaticType{ Authorization: interpreter.NewEntitlementSetAuthorization( @@ -706,7 +706,7 @@ func TestInterpretGetType(t *testing.T) { return optRef.getType() } `, - result: &interpreter.TypeValue{ + result: interpreter.TypeValue{ Type: &interpreter.OptionalStaticType{ Type: &interpreter.ReferenceStaticType{ // Reference was converted @@ -731,7 +731,7 @@ func TestInterpretGetType(t *testing.T) { return optRef.getType() } `, - result: &interpreter.TypeValue{ + result: interpreter.TypeValue{ Type: &interpreter.OptionalStaticType{ Type: &interpreter.ReferenceStaticType{ Authorization: interpreter.NewEntitlementSetAuthorization( @@ -762,7 +762,7 @@ func TestInterpretGetType(t *testing.T) { return optRef.getType() } `, - result: &interpreter.TypeValue{ + result: interpreter.TypeValue{ Type: &interpreter.OptionalStaticType{ Type: &interpreter.ReferenceStaticType{ // Reference was converted @@ -791,7 +791,7 @@ func TestInterpretGetType(t *testing.T) { return optRef.getType() } `, - result: &interpreter.TypeValue{ + result: interpreter.TypeValue{ Type: &interpreter.OptionalStaticType{ Type: &interpreter.ReferenceStaticType{ Authorization: interpreter.NewEntitlementSetAuthorization( @@ -811,7 +811,7 @@ func TestInterpretGetType(t *testing.T) { return [1, 3].getType() } `, - result: &interpreter.TypeValue{ + result: interpreter.TypeValue{ Type: &interpreter.VariableSizedStaticType{ Type: interpreter.PrimitiveStaticTypeInt, }, diff --git a/runtime/tests/interpreter/runtimetype_test.go b/runtime/tests/interpreter/runtimetype_test.go index 036999e62b..d3411a8880 100644 --- a/runtime/tests/interpreter/runtimetype_test.go +++ b/runtime/tests/interpreter/runtimetype_test.go @@ -45,7 +45,7 @@ func TestInterpretOptionalType(t *testing.T) { `) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: &interpreter.OptionalStaticType{ Type: interpreter.PrimitiveStaticTypeString, }, @@ -54,7 +54,7 @@ func TestInterpretOptionalType(t *testing.T) { ) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: &interpreter.OptionalStaticType{ Type: interpreter.PrimitiveStaticTypeInt, }, @@ -63,7 +63,7 @@ func TestInterpretOptionalType(t *testing.T) { ) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: &interpreter.OptionalStaticType{ Type: interpreter.NewCompositeStaticTypeComputeTypeID(nil, utils.TestLocation, "R"), }, @@ -72,7 +72,7 @@ func TestInterpretOptionalType(t *testing.T) { ) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: &interpreter.OptionalStaticType{ Type: &interpreter.OptionalStaticType{ Type: interpreter.PrimitiveStaticTypeString, @@ -104,7 +104,7 @@ func TestInterpretVariableSizedArrayType(t *testing.T) { `) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: &interpreter.VariableSizedStaticType{ Type: interpreter.PrimitiveStaticTypeString, }, @@ -113,7 +113,7 @@ func TestInterpretVariableSizedArrayType(t *testing.T) { ) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: &interpreter.VariableSizedStaticType{ Type: interpreter.PrimitiveStaticTypeInt, }, @@ -122,7 +122,7 @@ func TestInterpretVariableSizedArrayType(t *testing.T) { ) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: &interpreter.VariableSizedStaticType{ Type: interpreter.NewCompositeStaticTypeComputeTypeID(nil, utils.TestLocation, "R"), }, @@ -131,7 +131,7 @@ func TestInterpretVariableSizedArrayType(t *testing.T) { ) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: &interpreter.VariableSizedStaticType{ Type: &interpreter.VariableSizedStaticType{ Type: interpreter.PrimitiveStaticTypeString, @@ -162,7 +162,7 @@ func TestInterpretConstantSizedArrayType(t *testing.T) { `) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: &interpreter.ConstantSizedStaticType{ Type: interpreter.PrimitiveStaticTypeString, Size: int64(10), @@ -172,7 +172,7 @@ func TestInterpretConstantSizedArrayType(t *testing.T) { ) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: &interpreter.ConstantSizedStaticType{ Type: interpreter.PrimitiveStaticTypeInt, Size: int64(5), @@ -182,7 +182,7 @@ func TestInterpretConstantSizedArrayType(t *testing.T) { ) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: &interpreter.ConstantSizedStaticType{ Type: interpreter.NewCompositeStaticTypeComputeTypeID(nil, utils.TestLocation, "R"), Size: int64(400), @@ -192,7 +192,7 @@ func TestInterpretConstantSizedArrayType(t *testing.T) { ) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: &interpreter.ConstantSizedStaticType{ Type: &interpreter.ConstantSizedStaticType{ Type: interpreter.PrimitiveStaticTypeString, @@ -228,7 +228,7 @@ func TestInterpretDictionaryType(t *testing.T) { `) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: &interpreter.DictionaryStaticType{ KeyType: interpreter.PrimitiveStaticTypeString, ValueType: interpreter.PrimitiveStaticTypeInt, @@ -238,7 +238,7 @@ func TestInterpretDictionaryType(t *testing.T) { ) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: &interpreter.DictionaryStaticType{ KeyType: interpreter.PrimitiveStaticTypeInt, ValueType: interpreter.PrimitiveStaticTypeString, @@ -248,7 +248,7 @@ func TestInterpretDictionaryType(t *testing.T) { ) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: &interpreter.DictionaryStaticType{ ValueType: interpreter.NewCompositeStaticTypeComputeTypeID(nil, utils.TestLocation, "R"), KeyType: interpreter.PrimitiveStaticTypeInt, @@ -258,7 +258,7 @@ func TestInterpretDictionaryType(t *testing.T) { ) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: &interpreter.DictionaryStaticType{ ValueType: &interpreter.DictionaryStaticType{ KeyType: interpreter.PrimitiveStaticTypeString, @@ -304,14 +304,14 @@ func TestInterpretCompositeType(t *testing.T) { `) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: interpreter.NewCompositeStaticTypeComputeTypeID(nil, utils.TestLocation, "R"), }, inter.Globals.Get("a").GetValue(), ) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: interpreter.NewCompositeStaticTypeComputeTypeID(nil, utils.TestLocation, "S"), }, inter.Globals.Get("b").GetValue(), @@ -333,21 +333,21 @@ func TestInterpretCompositeType(t *testing.T) { ) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: interpreter.NewCompositeStaticTypeComputeTypeID(nil, utils.TestLocation, "F"), }, inter.Globals.Get("f").GetValue(), ) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: interpreter.NewCompositeStaticTypeComputeTypeID(nil, nil, "PublicKey"), }, inter.Globals.Get("g").GetValue(), ) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: interpreter.NewCompositeStaticTypeComputeTypeID(nil, nil, "HashAlgorithm"), }, inter.Globals.Get("h").GetValue(), @@ -370,14 +370,14 @@ func TestInterpretInterfaceType(t *testing.T) { `) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: interpreter.NewInterfaceStaticTypeComputeTypeID(nil, utils.TestLocation, "R"), }, inter.Globals.Get("a").GetValue(), ) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: interpreter.NewInterfaceStaticTypeComputeTypeID(nil, utils.TestLocation, "S"), }, inter.Globals.Get("b").GetValue(), @@ -407,7 +407,7 @@ func TestInterpretFunctionType(t *testing.T) { `) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: interpreter.FunctionStaticType{ Type: &sema.FunctionType{ Parameters: []sema.Parameter{ @@ -423,7 +423,7 @@ func TestInterpretFunctionType(t *testing.T) { ) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: interpreter.FunctionStaticType{ Type: &sema.FunctionType{ Parameters: []sema.Parameter{ @@ -438,7 +438,7 @@ func TestInterpretFunctionType(t *testing.T) { ) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: interpreter.FunctionStaticType{ Type: &sema.FunctionType{ ReturnTypeAnnotation: sema.StringTypeAnnotation, @@ -471,7 +471,7 @@ func TestInterpretReferenceType(t *testing.T) { `) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: &interpreter.ReferenceStaticType{ ReferencedType: interpreter.NewCompositeStaticTypeComputeTypeID(nil, utils.TestLocation, "R"), Authorization: interpreter.NewEntitlementSetAuthorization( @@ -486,7 +486,7 @@ func TestInterpretReferenceType(t *testing.T) { ) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: &interpreter.ReferenceStaticType{ ReferencedType: interpreter.PrimitiveStaticTypeString, Authorization: interpreter.UnauthorizedAccess, @@ -496,7 +496,7 @@ func TestInterpretReferenceType(t *testing.T) { ) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: &interpreter.ReferenceStaticType{ ReferencedType: interpreter.NewCompositeStaticTypeComputeTypeID(nil, utils.TestLocation, "S"), Authorization: interpreter.NewEntitlementSetAuthorization( @@ -550,7 +550,7 @@ func TestInterpretIntersectionType(t *testing.T) { `) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: &interpreter.IntersectionStaticType{ Types: []*interpreter.InterfaceStaticType{ interpreter.NewInterfaceStaticTypeComputeTypeID(nil, utils.TestLocation, "R"), @@ -566,7 +566,7 @@ func TestInterpretIntersectionType(t *testing.T) { ) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: &interpreter.IntersectionStaticType{ Types: []*interpreter.InterfaceStaticType{ interpreter.NewInterfaceStaticTypeComputeTypeID(nil, utils.TestLocation, "S"), @@ -582,7 +582,7 @@ func TestInterpretIntersectionType(t *testing.T) { ) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: &interpreter.IntersectionStaticType{ Types: []*interpreter.InterfaceStaticType{ interpreter.NewInterfaceStaticTypeComputeTypeID(nil, utils.TestLocation, "S"), @@ -625,7 +625,7 @@ func TestInterpretCapabilityType(t *testing.T) { `) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: &interpreter.CapabilityStaticType{ BorrowType: &interpreter.ReferenceStaticType{ ReferencedType: interpreter.PrimitiveStaticTypeString, @@ -637,7 +637,7 @@ func TestInterpretCapabilityType(t *testing.T) { ) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: &interpreter.CapabilityStaticType{ BorrowType: &interpreter.ReferenceStaticType{ ReferencedType: interpreter.PrimitiveStaticTypeInt, @@ -649,7 +649,7 @@ func TestInterpretCapabilityType(t *testing.T) { ) assert.Equal(t, - &interpreter.TypeValue{ + interpreter.TypeValue{ Type: &interpreter.CapabilityStaticType{ BorrowType: &interpreter.ReferenceStaticType{ ReferencedType: interpreter.NewCompositeStaticTypeComputeTypeID(nil, utils.TestLocation, "R"), diff --git a/runtime/tests/interpreter/string_test.go b/runtime/tests/interpreter/string_test.go index aa235a1bac..176927311c 100644 --- a/runtime/tests/interpreter/string_test.go +++ b/runtime/tests/interpreter/string_test.go @@ -342,7 +342,7 @@ func TestInterpretStringAccess(t *testing.T) { require.NoError(t, err) require.Equal(t, - &interpreter.TypeValue{Type: interpreter.PrimitiveStaticTypeCharacter}, + interpreter.TypeValue{Type: interpreter.PrimitiveStaticTypeCharacter}, result, ) } @@ -362,7 +362,7 @@ func TestInterpretCharacterLiteralType(t *testing.T) { require.NoError(t, err) require.Equal(t, - &interpreter.TypeValue{Type: interpreter.PrimitiveStaticTypeCharacter}, + interpreter.TypeValue{Type: interpreter.PrimitiveStaticTypeCharacter}, result, ) } @@ -382,7 +382,7 @@ func TestInterpretOneCharacterStringLiteralType(t *testing.T) { require.NoError(t, err) require.Equal(t, - &interpreter.TypeValue{Type: interpreter.PrimitiveStaticTypeString}, + interpreter.TypeValue{Type: interpreter.PrimitiveStaticTypeString}, result, ) } @@ -402,7 +402,7 @@ func TestInterpretCharacterLiteralTypeNoAnnotation(t *testing.T) { require.NoError(t, err) require.Equal(t, - &interpreter.TypeValue{Type: interpreter.PrimitiveStaticTypeString}, + interpreter.TypeValue{Type: interpreter.PrimitiveStaticTypeString}, result, ) } From 0afbaea61ccf42960058c0b4784317197b966118 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Fri, 15 Dec 2023 14:54:22 -0500 Subject: [PATCH 32/40] remove unnecessary formatting changes --- runtime/interpreter/value_test.go | 14 +++++++------- runtime/sema/type_test.go | 1 - 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/runtime/interpreter/value_test.go b/runtime/interpreter/value_test.go index 9d5117f618..a3b00b8985 100644 --- a/runtime/interpreter/value_test.go +++ b/runtime/interpreter/value_test.go @@ -82,7 +82,7 @@ func TestOwnerNewArray(t *testing.T) { t.Parallel() - storage := NewInMemoryStorage(nil) + storage := newUnmeteredInMemoryStorage() elaboration := sema.NewElaboration(nil) elaboration.SetCompositeType( @@ -2112,9 +2112,9 @@ func TestTypeValue_Equal(t *testing.T) { inter := newTestInterpreter(t) require.True(t, - (TypeValue{ + TypeValue{ Type: PrimitiveStaticTypeString, - }).Equal( + }.Equal( inter, EmptyLocationRange, TypeValue{ @@ -2131,9 +2131,9 @@ func TestTypeValue_Equal(t *testing.T) { inter := newTestInterpreter(t) require.False(t, - (TypeValue{ + TypeValue{ Type: PrimitiveStaticTypeString, - }).Equal( + }.Equal( inter, EmptyLocationRange, TypeValue{ @@ -2150,9 +2150,9 @@ func TestTypeValue_Equal(t *testing.T) { inter := newTestInterpreter(t) require.False(t, - (TypeValue{ + TypeValue{ Type: PrimitiveStaticTypeString, - }).Equal( + }.Equal( inter, EmptyLocationRange, NewUnmeteredStringValue("String"), diff --git a/runtime/sema/type_test.go b/runtime/sema/type_test.go index 4a98eddfeb..3308e027ef 100644 --- a/runtime/sema/type_test.go +++ b/runtime/sema/type_test.go @@ -2148,7 +2148,6 @@ func TestReferenceType_ID(t *testing.T) { } func TestReferenceType_String(t *testing.T) { - t.Parallel() testLocation := common.StringLocation("test") From f1873297f4e54eeebbaafd8453a858512b21f180 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Fri, 15 Dec 2023 14:54:48 -0500 Subject: [PATCH 33/40] remove unnecessary formatting changes --- runtime/interpreter/value.go | 1 - 1 file changed, 1 deletion(-) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index deace59e76..9103d98fc0 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -510,7 +510,6 @@ func (TypeValue) ChildStorables() []atree.Storable { // HashInput returns a byte slice containing: // - HashInputTypeType (1 byte) // - type id (n bytes) - func (v TypeValue) HashInput(interpreter *Interpreter, _ LocationRange, scratch []byte) []byte { typeID := v.Type.ID() From fb88f4944dd62367bd776153f37658a64a2d04dd Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Wed, 20 Dec 2023 10:47:28 -0500 Subject: [PATCH 34/40] Update migrations/migration.go Co-authored-by: Supun Setunga --- migrations/migration.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/migrations/migration.go b/migrations/migration.go index 700102a87c..8b750d02d2 100644 --- a/migrations/migration.go +++ b/migrations/migration.go @@ -208,9 +208,6 @@ func (m *StorageMigration) MigrateNestedValue( if converted != nil { // Chain the migrations. - // Probably not needed, because of the assumption above. - // i.e: A single non-container value may not get converted from two migrations. - // But have it here to be safe. value = converted newValue = converted From c46d84ebee1a6b3d44bb639c8d733919f6139277 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Fri, 5 Jan 2024 11:13:11 -0500 Subject: [PATCH 35/40] don't create new wrapper types when inner type does not change --- migrations/entitlements/migration.go | 57 +++++++++++++++++------ migrations/entitlements/migration_test.go | 3 +- 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/migrations/entitlements/migration.go b/migrations/entitlements/migration.go index b0714ea610..914887de25 100644 --- a/migrations/entitlements/migration.go +++ b/migrations/entitlements/migration.go @@ -44,12 +44,12 @@ func (EntitlementsMigration) Name() string { // * `ConvertToEntitledType(T?) ---> ConvertToEntitledType(T)? // * `ConvertToEntitledType(T) ---> T` // where Entitlements(I) is defined as the result of T.SupportedEntitlements() -func ConvertToEntitledType(t sema.Type) sema.Type { +func ConvertToEntitledType(t sema.Type) (sema.Type, bool) { switch t := t.(type) { case *sema.ReferenceType: switch t.Authorization { case sema.UnauthorizedAccess: - innerType := ConvertToEntitledType(t.Type) + innerType, convertedInner := ConvertToEntitledType(t.Type) auth := sema.UnauthorizedAccess if entitlementSupportingType, ok := innerType.(sema.EntitlementSupportingType); ok { supportedEntitlements := entitlementSupportingType.SupportedEntitlements() @@ -60,27 +60,51 @@ func ConvertToEntitledType(t sema.Type) sema.Type { } } } + if auth.Equal(sema.UnauthorizedAccess) && !convertedInner { + return t, false + } return sema.NewReferenceType( nil, auth, innerType, - ) + ), true // type is already entitled default: - return t + return t, false } case *sema.OptionalType: - return sema.NewOptionalType(nil, ConvertToEntitledType(t.Type)) + ty, converted := ConvertToEntitledType(t.Type) + if !converted { + return t, false + } + return sema.NewOptionalType(nil, ty), true case *sema.CapabilityType: - return sema.NewCapabilityType(nil, ConvertToEntitledType(t.BorrowType)) + ty, converted := ConvertToEntitledType(t.BorrowType) + if !converted { + return t, false + } + return sema.NewCapabilityType(nil, ty), true case *sema.VariableSizedType: - return sema.NewVariableSizedType(nil, ConvertToEntitledType(t.Type)) + ty, converted := ConvertToEntitledType(t.Type) + if !converted { + return t, false + } + return sema.NewVariableSizedType(nil, ty), true case *sema.ConstantSizedType: - return sema.NewConstantSizedType(nil, ConvertToEntitledType(t.Type), t.Size) + ty, converted := ConvertToEntitledType(t.Type) + if !converted { + return t, false + } + return sema.NewConstantSizedType(nil, ty, t.Size), true case *sema.DictionaryType: - return sema.NewDictionaryType(nil, ConvertToEntitledType(t.KeyType), ConvertToEntitledType(t.ValueType)) + keyTy, convertedKey := ConvertToEntitledType(t.KeyType) + valueTy, convertedValue := ConvertToEntitledType(t.ValueType) + if !convertedKey && !convertedValue { + return t, false + } + return sema.NewDictionaryType(nil, keyTy, valueTy), true default: - return t + return t, false } } @@ -149,10 +173,10 @@ func ConvertValueToEntitlements( convertLegacyStaticType(staticType) semaType := inter.MustConvertStaticToSemaType(staticType) - entitledType := ConvertToEntitledType(semaType) + entitledType, converted := ConvertToEntitledType(semaType) // if the types of the values are equal and the value is not a runtime type, there's nothing to migrate - if entitledType.Equal(semaType) && !entitledType.Equal(sema.MetaType) { + if !converted && !entitledType.Equal(sema.MetaType) { return nil } @@ -227,12 +251,15 @@ func ConvertValueToEntitlements( if v.Type == nil { return nil } + + convertedType, _ := ConvertToEntitledType( + inter.MustConvertStaticToSemaType(v.Type), + ) + // convert the static type of the value entitledStaticType := interpreter.ConvertSemaToStaticType( inter, - ConvertToEntitledType( - inter.MustConvertStaticToSemaType(v.Type), - ), + convertedType, ) return interpreter.NewTypeValue(inter, entitledStaticType) } diff --git a/migrations/entitlements/migration_test.go b/migrations/entitlements/migration_test.go index 59effe1d8f..a0e75d86b4 100644 --- a/migrations/entitlements/migration_test.go +++ b/migrations/entitlements/migration_test.go @@ -362,7 +362,8 @@ func TestConvertToEntitledType(t *testing.T) { for _, test := range tests { t.Run(test.Name, func(t *testing.T) { - compareTypesRecursively(t, ConvertToEntitledType(test.Input), test.Output) + converedType, _ := ConvertToEntitledType(test.Input) + compareTypesRecursively(t, converedType, test.Output) }) } From f5f26f6b21df4b040f90bbf354bc7eb4ab74aa36 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Fri, 5 Jan 2024 12:32:25 -0500 Subject: [PATCH 36/40] add comment --- migrations/entitlements/migration.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/migrations/entitlements/migration.go b/migrations/entitlements/migration.go index 914887de25..7f7ab25754 100644 --- a/migrations/entitlements/migration.go +++ b/migrations/entitlements/migration.go @@ -116,6 +116,7 @@ func ConvertValueToEntitlements( ) interpreter.Value { var staticType interpreter.StaticType + // during a real migration these two reference cases will not be hit, but they are here for easier testing // for reference types, we want to use the borrow type, rather than the type of the referenced value switch referenceValue := v.(type) { case *interpreter.EphemeralReferenceValue: @@ -181,6 +182,7 @@ func ConvertValueToEntitlements( } switch v := v.(type) { + // during a real migration these two reference cases will not be hit, but they are here for easier testing case *interpreter.EphemeralReferenceValue: entitledReferenceType := entitledType.(*sema.ReferenceType) staticAuthorization := interpreter.ConvertSemaAccessToStaticAuthorization(inter, entitledReferenceType.Authorization) From de138faf9d8c54f6cbbfd9b464b0487caddd88ce Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Fri, 5 Jan 2024 13:15:08 -0500 Subject: [PATCH 37/40] add test for typeValue keys --- migrations/entitlements/migration_test.go | 241 ++++++++++++++++++++++ 1 file changed, 241 insertions(+) diff --git a/migrations/entitlements/migration_test.go b/migrations/entitlements/migration_test.go index a0e75d86b4..f86732dc9e 100644 --- a/migrations/entitlements/migration_test.go +++ b/migrations/entitlements/migration_test.go @@ -1866,3 +1866,244 @@ func TestMigrateDictOfValues(t *testing.T) { ref.Authorization, ) } + +func TestMigrateDictOfWithTypeValueKey(t *testing.T) { + t.Parallel() + + address1 := [8]byte{0, 0, 0, 0, 0, 0, 0, 1} + address2 := [8]byte{0, 0, 0, 0, 0, 0, 0, 2} + + storage := NewTestLedger(nil, nil) + rt := NewTestInterpreterRuntime() + + accountCodes := map[common.Location][]byte{} + interfaces := map[common.Location]*TestRuntimeInterface{} + + runtimeInterface1 := &TestRuntimeInterface{ + Storage: storage, + OnEmitEvent: func(event cadence.Event) error { + return nil + }, + OnGetSigningAccounts: func() ([]runtime.Address, error) { + return []runtime.Address{address1}, nil + }, + OnGetCode: func(location common.Location) (bytes []byte, err error) { + return accountCodes[location], nil + }, + OnResolveLocation: MultipleIdentifierLocationResolver, + OnGetAccountContractCode: func(location common.AddressLocation) (code []byte, err error) { + code = accountCodes[location] + return code, nil + }, + } + runtimeInterface1.OnUpdateAccountContractCode = func(location common.AddressLocation, code []byte) error { + accountCodes[location] = code + interfaces[location] = runtimeInterface1 + return nil + } + + runtimeInterface2 := &TestRuntimeInterface{ + Storage: storage, + OnEmitEvent: func(event cadence.Event) error { + return nil + }, + OnGetCode: func(location common.Location) (bytes []byte, err error) { + return accountCodes[location], nil + }, + OnGetSigningAccounts: func() ([]runtime.Address, error) { + return []runtime.Address{address2}, nil + }, + OnResolveLocation: MultipleIdentifierLocationResolver, + OnGetAccountContractCode: func(location common.AddressLocation) (code []byte, err error) { + code = accountCodes[location] + return code, nil + }, + } + runtimeInterface2.OnUpdateAccountContractCode = func(location common.AddressLocation, code []byte) error { + accountCodes[location] = code + interfaces[location] = runtimeInterface2 + return nil + } + + nextTransactionLocation := NewTransactionLocationGenerator() + + oldContract := []byte(` + access(all) contract C { + access(all) resource R { + access(all) fun foo() {} + } + access(all) fun makeR(): @R { + return <- create R() + } + } + `) + + contract := []byte(` + access(all) contract C { + access(all) entitlement E + access(all) resource R { + access(E) fun foo() {} + } + access(all) fun makeR(): @R { + return <- create R() + } + } + `) + + saveValues := []byte(` + import C from 0x1 + + transaction { + prepare(signer: auth(Storage, Capabilities) &Account) { + let r1 <- C.makeR() + let r2 <- C.makeR() + let rType = ReferenceType(entitlements: [], type: r1.getType())! + signer.storage.save(<-r1, to: /storage/foo) + signer.storage.save(<-r2, to: /storage/bar) + let cap1 = signer.capabilities.storage.issue<&C.R>(/storage/foo) + let cap2 = signer.capabilities.storage.issue<&C.R>(/storage/bar) + let arr = {rType: cap1, Type(): cap2} + signer.storage.save(arr, to: /storage/caps) + } + } + `) + + // Deploy contract to 0x1 + err := rt.ExecuteTransaction( + runtime.Script{ + Source: DeploymentTransaction("C", oldContract), + }, + runtime.Context{ + Interface: runtimeInterface1, + Location: nextTransactionLocation(), + }, + ) + require.NoError(t, err) + + // Execute transaction on 0x2 + err = rt.ExecuteTransaction( + runtime.Script{ + Source: saveValues, + }, + runtime.Context{ + Interface: runtimeInterface2, + Location: nextTransactionLocation(), + }, + ) + require.NoError(t, err) + + // update contract on 0x1 + err = rt.ExecuteTransaction( + runtime.Script{ + Source: UpdateTransaction("C", contract), + }, + runtime.Context{ + Interface: runtimeInterface1, + Location: nextTransactionLocation(), + }, + ) + require.NoError(t, err) + + runtimeStorage := runtime.NewStorage(storage, nil) + + inter, err := interpreter.NewInterpreter( + nil, + utils.TestLocation, + &interpreter.Config{ + Storage: runtimeStorage, + AtreeValueValidationEnabled: false, + AtreeStorageValidationEnabled: false, + ImportLocationHandler: func(inter *interpreter.Interpreter, location common.Location) interpreter.Import { + program, err := rt.ParseAndCheckProgram( + accountCodes[location], + runtime.Context{ + Interface: interfaces[location], + Location: location, + }, + ) + require.NoError(t, err) + + subInterpreter, err := inter.NewSubInterpreter(program, location) + require.NoError(t, err) + + return interpreter.InterpreterImport{ + Interpreter: subInterpreter, + } + }, + }, + ) + require.NoError(t, err) + + storageIdentifier := common.PathDomainStorage.Identifier() + storageMap := runtimeStorage.GetStorageMap(address2, storageIdentifier, false) + require.NotNil(t, storageMap) + require.Greater(t, storageMap.Count(), uint64(0)) + + // Migrate + + migration := migrations.NewStorageMigration(inter, runtimeStorage) + pathMigrator := migration.NewValueMigrationsPathMigrator(nil, NewEntitlementsMigration(inter)) + migration.Migrate( + &migrations.AddressSliceIterator{ + Addresses: []common.Address{ + address1, + address2, + }, + }, + pathMigrator, + ) + + dictValue := storageMap.ReadValue(nil, interpreter.StringStorageMapKey("caps")) + require.IsType(t, &interpreter.DictionaryValue{}, dictValue) + dictionaryValue := dictValue.(*interpreter.DictionaryValue) + + valueType := dictionaryValue.Type.ValueType + require.IsType(t, &interpreter.CapabilityStaticType{}, valueType) + capElementType := valueType.(*interpreter.CapabilityStaticType) + require.IsType(t, &interpreter.ReferenceStaticType{}, capElementType.BorrowType) + ref := capElementType.BorrowType.(*interpreter.ReferenceStaticType) + require.Equal(t, + interpreter.NewEntitlementSetAuthorization( + inter, + func() []common.TypeID { return []common.TypeID{"A.0000000000000001.C.E"} }, + 1, + sema.Conjunction, + ), + ref.Authorization, + ) + + rTypeKey := interpreter.NewTypeValue(nil, ref) + intTypeKey := interpreter.NewTypeValue(nil, interpreter.PrimitiveStaticTypeInt) + + cap1, present := dictionaryValue.Get(inter, interpreter.EmptyLocationRange, rTypeKey) + require.True(t, present) + require.IsType(t, &interpreter.CapabilityValue{}, cap1) + capValue := cap1.(*interpreter.CapabilityValue) + require.IsType(t, &interpreter.ReferenceStaticType{}, capValue.BorrowType) + ref = capValue.BorrowType.(*interpreter.ReferenceStaticType) + require.Equal(t, + interpreter.NewEntitlementSetAuthorization( + inter, + func() []common.TypeID { return []common.TypeID{"A.0000000000000001.C.E"} }, + 1, + sema.Conjunction, + ), + ref.Authorization, + ) + + cap2, present := dictionaryValue.Get(inter, interpreter.EmptyLocationRange, intTypeKey) + require.True(t, present) + require.IsType(t, &interpreter.CapabilityValue{}, cap2) + capValue = cap1.(*interpreter.CapabilityValue) + require.IsType(t, &interpreter.ReferenceStaticType{}, capValue.BorrowType) + ref = capValue.BorrowType.(*interpreter.ReferenceStaticType) + require.Equal(t, + interpreter.NewEntitlementSetAuthorization( + inter, + func() []common.TypeID { return []common.TypeID{"A.0000000000000001.C.E"} }, + 1, + sema.Conjunction, + ), + ref.Authorization, + ) +} From c2bb44db606f8ddaf76e5bb33e1a09cfa06c9475 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Fri, 12 Jan 2024 13:27:36 -0500 Subject: [PATCH 38/40] fix compile --- migrations/entitlements/migration.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/migrations/entitlements/migration.go b/migrations/entitlements/migration.go index 638f98b1a5..5f893da9e5 100644 --- a/migrations/entitlements/migration.go +++ b/migrations/entitlements/migration.go @@ -215,10 +215,10 @@ func ConvertValueToEntitlements( entitledArrayType := entitledType.(sema.ArrayType) arrayStaticType := interpreter.ConvertSemaArrayTypeToStaticArrayType(inter, entitledArrayType) - iterator := v.Iterator(inter) + iterator := v.Iterator(inter, interpreter.EmptyLocationRange) newArray := interpreter.NewArrayValueWithIterator(inter, arrayStaticType, v.GetOwner(), uint64(v.Count()), func() interpreter.Value { - return iterator.Next(inter) + return iterator.Next(inter, interpreter.EmptyLocationRange) }) return newArray From 96c87d56241d938a44cbd9fdf8e23fd2c4a258b9 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Tue, 16 Jan 2024 10:10:54 -0500 Subject: [PATCH 39/40] Update migrations/migration.go Co-authored-by: Supun Setunga --- migrations/migration.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrations/migration.go b/migrations/migration.go index dee9060be2..58fe140f32 100644 --- a/migrations/migration.go +++ b/migrations/migration.go @@ -271,7 +271,7 @@ func (m *StorageMigration) MigrateNestedValue( } for _, migration := range valueMigrations { - converted, err := migration.Migrate(addressPath, value, m.interpreter) + converted, err := m.migrate(migration, addressPath, value) if err != nil { if reporter != nil { From 4a4ba6b67d43f7194bf8131a09ec33d60b805a94 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Tue, 16 Jan 2024 11:48:47 -0500 Subject: [PATCH 40/40] fix lint --- migrations/entitlements/migration_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/migrations/entitlements/migration_test.go b/migrations/entitlements/migration_test.go index 0181dc9c37..b1217e4ed9 100644 --- a/migrations/entitlements/migration_test.go +++ b/migrations/entitlements/migration_test.go @@ -21,6 +21,8 @@ package entitlements import ( "testing" + "github.com/stretchr/testify/require" + "github.com/onflow/cadence" "github.com/onflow/cadence/migrations" "github.com/onflow/cadence/runtime" @@ -33,7 +35,6 @@ import ( . "github.com/onflow/cadence/runtime/tests/runtime_utils" "github.com/onflow/cadence/runtime/tests/utils" . "github.com/onflow/cadence/runtime/tests/utils" - "github.com/stretchr/testify/require" ) func TestConvertToEntitledType(t *testing.T) { @@ -952,7 +953,7 @@ func TestConvertToEntitledValue(t *testing.T) { var referencePeekingEqual func(interpreter.EquatableValue, interpreter.Value) bool - // equality that peeks inside referneces to use structural equality for their values + // equality that peeks inside references to use structural equality for their values referencePeekingEqual = func(input interpreter.EquatableValue, output interpreter.Value) bool { switch v := input.(type) { case *interpreter.SomeValue: