diff --git a/bbq/vm/context.go b/bbq/vm/context.go index a218c2376..9053802e5 100644 --- a/bbq/vm/context.go +++ b/bbq/vm/context.go @@ -55,7 +55,8 @@ type Context struct { // This cache-alike is maintained per execution. // TODO: Re-use the conversions from the compiler. // TODO: Maybe extend/share this between executions. - semaTypeCache map[sema.TypeID]sema.Type + semaTypeCache map[sema.TypeID]sema.Type + semaAccessCache map[interpreter.Authorization]sema.Access // linkedGlobalsCache is a local cache-alike that is being used to hold already linked imports. linkedGlobalsCache map[common.Location]LinkedGlobals @@ -531,3 +532,22 @@ func (c *Context) GetEntitlementMapType( func (c *Context) LocationRange() interpreter.LocationRange { return c.getLocationRange() } + +func (c *Context) SemaAccessFromStaticAuthorization(auth interpreter.Authorization) (sema.Access, error) { + semaAccess, ok := c.semaAccessCache[auth] + if ok { + return semaAccess, nil + } + + semaAccess, err := interpreter.ConvertStaticAuthorizationToSemaAccess(auth, c) + if err != nil { + return nil, err + } + + if c.semaAccessCache == nil { + c.semaAccessCache = make(map[interpreter.Authorization]sema.Access) + } + c.semaAccessCache[auth] = semaAccess + + return semaAccess, nil +} diff --git a/interpreter/account_test.go b/interpreter/account_test.go index 4d6dc59e1..5c2513f51 100644 --- a/interpreter/account_test.go +++ b/interpreter/account_test.go @@ -417,64 +417,68 @@ type NoOpReferenceCreationContext struct{} var _ interpreter.ReferenceCreationContext = NoOpReferenceCreationContext{} -func (n NoOpReferenceCreationContext) ClearReferencedResourceKindedValues(_ atree.ValueID) { +func (NoOpReferenceCreationContext) ClearReferencedResourceKindedValues(_ atree.ValueID) { // NO-OP } -func (n NoOpReferenceCreationContext) ReferencedResourceKindedValues(_ atree.ValueID) map[*interpreter.EphemeralReferenceValue]struct{} { +func (NoOpReferenceCreationContext) ReferencedResourceKindedValues(_ atree.ValueID) map[*interpreter.EphemeralReferenceValue]struct{} { // NO-OP return nil } -func (n NoOpReferenceCreationContext) MaybeTrackReferencedResourceKindedValue(_ *interpreter.EphemeralReferenceValue) { +func (NoOpReferenceCreationContext) MaybeTrackReferencedResourceKindedValue(_ *interpreter.EphemeralReferenceValue) { // NO-OP } -func (n NoOpReferenceCreationContext) MeterMemory(_ common.MemoryUsage) error { +func (NoOpReferenceCreationContext) MeterMemory(_ common.MemoryUsage) error { // NO-OP return nil } -func (n NoOpReferenceCreationContext) MeterComputation(_ common.ComputationUsage) error { +func (NoOpReferenceCreationContext) MeterComputation(_ common.ComputationUsage) error { // NO-OP return nil } -func (n NoOpReferenceCreationContext) ReadStored(_ common.Address, _ common.StorageDomain, _ interpreter.StorageMapKey) interpreter.Value { +func (NoOpReferenceCreationContext) ReadStored(_ common.Address, _ common.StorageDomain, _ interpreter.StorageMapKey) interpreter.Value { // NO-OP return nil } -func (n NoOpReferenceCreationContext) GetEntitlementType(_ interpreter.TypeID) (*sema.EntitlementType, error) { +func (NoOpReferenceCreationContext) GetEntitlementType(_ interpreter.TypeID) (*sema.EntitlementType, error) { // NO-OP return nil, nil } -func (n NoOpReferenceCreationContext) GetEntitlementMapType(_ interpreter.TypeID) (*sema.EntitlementMapType, error) { +func (NoOpReferenceCreationContext) GetEntitlementMapType(_ interpreter.TypeID) (*sema.EntitlementMapType, error) { // NO-OP return nil, nil } -func (n NoOpReferenceCreationContext) GetInterfaceType(_ common.Location, _ string, _ interpreter.TypeID) (*sema.InterfaceType, error) { +func (NoOpReferenceCreationContext) GetInterfaceType(_ common.Location, _ string, _ interpreter.TypeID) (*sema.InterfaceType, error) { // NO-OP return nil, nil } -func (n NoOpReferenceCreationContext) GetCompositeType(_ common.Location, _ string, _ interpreter.TypeID) (*sema.CompositeType, error) { +func (NoOpReferenceCreationContext) GetCompositeType(_ common.Location, _ string, _ interpreter.TypeID) (*sema.CompositeType, error) { // NO-OP return nil, nil } -func (n NoOpReferenceCreationContext) IsTypeInfoRecovered(_ common.Location) bool { +func (NoOpReferenceCreationContext) IsTypeInfoRecovered(_ common.Location) bool { // NO-OP return false } -func (n NoOpReferenceCreationContext) SemaTypeFromStaticType(_ interpreter.StaticType) sema.Type { +func (NoOpReferenceCreationContext) SemaTypeFromStaticType(_ interpreter.StaticType) sema.Type { // NO-OP return nil } +func (NoOpReferenceCreationContext) SemaAccessFromStaticAuthorization(interpreter.Authorization) (sema.Access, error) { + panic(errors.NewUnreachableError()) +} + type NoOpFunctionCreationContext struct { //Just to make the compiler happy interpreter.ResourceDestructionContext @@ -482,28 +486,28 @@ type NoOpFunctionCreationContext struct { var _ interpreter.FunctionCreationContext = NoOpFunctionCreationContext{} -func (n NoOpFunctionCreationContext) ClearReferencedResourceKindedValues(_ atree.ValueID) { +func (NoOpFunctionCreationContext) ClearReferencedResourceKindedValues(_ atree.ValueID) { // NO-OP } -func (n NoOpFunctionCreationContext) ReferencedResourceKindedValues( +func (NoOpFunctionCreationContext) ReferencedResourceKindedValues( _ atree.ValueID, ) map[*interpreter.EphemeralReferenceValue]struct{} { // NO-OP return nil } -func (n NoOpFunctionCreationContext) CheckInvalidatedResourceOrResourceReference( +func (NoOpFunctionCreationContext) CheckInvalidatedResourceOrResourceReference( _ interpreter.Value, ) { // NO-OP } -func (n NoOpFunctionCreationContext) MaybeTrackReferencedResourceKindedValue(_ *interpreter.EphemeralReferenceValue) { +func (NoOpFunctionCreationContext) MaybeTrackReferencedResourceKindedValue(_ *interpreter.EphemeralReferenceValue) { // NO-OP } -func (n NoOpFunctionCreationContext) MeterMemory(_ common.MemoryUsage) error { +func (NoOpFunctionCreationContext) MeterMemory(_ common.MemoryUsage) error { // NO-OP return nil } diff --git a/interpreter/interface.go b/interpreter/interface.go index ac1c537fc..e81386f68 100644 --- a/interpreter/interface.go +++ b/interpreter/interface.go @@ -30,6 +30,7 @@ type TypeConverter interface { common.MemoryGauge StaticTypeConversionHandler SemaTypeFromStaticType(staticType StaticType) sema.Type + SemaAccessFromStaticAuthorization(auth Authorization) (sema.Access, error) } var _ TypeConverter = &Interpreter{} @@ -538,98 +539,102 @@ type NoOpStringContext struct { var _ ValueStringContext = NoOpStringContext{} -func (ctx NoOpStringContext) MeterMemory(_ common.MemoryUsage) error { +func (NoOpStringContext) MeterMemory(_ common.MemoryUsage) error { return nil } -func (ctx NoOpStringContext) MeterComputation(_ common.ComputationUsage) error { +func (NoOpStringContext) MeterComputation(_ common.ComputationUsage) error { return nil } -func (ctx NoOpStringContext) WithContainerMutationPrevention(_ atree.ValueID, f func()) { +func (NoOpStringContext) WithContainerMutationPrevention(_ atree.ValueID, f func()) { f() } -func (ctx NoOpStringContext) ValidateContainerMutation(_ atree.ValueID) { +func (NoOpStringContext) ValidateContainerMutation(_ atree.ValueID) { panic(errors.NewUnreachableError()) } -func (ctx NoOpStringContext) EnforceNotResourceDestruction(_ atree.ValueID) { +func (NoOpStringContext) EnforceNotResourceDestruction(_ atree.ValueID) { panic(errors.NewUnreachableError()) } -func (ctx NoOpStringContext) ReadStored(_ common.Address, _ common.StorageDomain, _ StorageMapKey) Value { +func (NoOpStringContext) ReadStored(_ common.Address, _ common.StorageDomain, _ StorageMapKey) Value { panic(errors.NewUnreachableError()) } -func (ctx NoOpStringContext) WriteStored(_ common.Address, _ common.StorageDomain, _ StorageMapKey, _ Value) (existed bool) { +func (NoOpStringContext) WriteStored(_ common.Address, _ common.StorageDomain, _ StorageMapKey, _ Value) (existed bool) { panic(errors.NewUnreachableError()) } -func (ctx NoOpStringContext) Storage() Storage { +func (NoOpStringContext) Storage() Storage { panic(errors.NewUnreachableError()) } -func (ctx NoOpStringContext) MaybeValidateAtreeValue(_ atree.Value) { +func (NoOpStringContext) MaybeValidateAtreeValue(_ atree.Value) { panic(errors.NewUnreachableError()) } -func (ctx NoOpStringContext) MaybeValidateAtreeStorage() { +func (NoOpStringContext) MaybeValidateAtreeStorage() { panic(errors.NewUnreachableError()) } -func (ctx NoOpStringContext) MaybeTrackReferencedResourceKindedValue(_ *EphemeralReferenceValue) { +func (NoOpStringContext) MaybeTrackReferencedResourceKindedValue(_ *EphemeralReferenceValue) { panic(errors.NewUnreachableError()) } -func (ctx NoOpStringContext) ClearReferencedResourceKindedValues(_ atree.ValueID) { +func (NoOpStringContext) ClearReferencedResourceKindedValues(_ atree.ValueID) { panic(errors.NewUnreachableError()) } -func (ctx NoOpStringContext) ReferencedResourceKindedValues(_ atree.ValueID) map[*EphemeralReferenceValue]struct{} { +func (NoOpStringContext) ReferencedResourceKindedValues(_ atree.ValueID) map[*EphemeralReferenceValue]struct{} { panic(errors.NewUnreachableError()) } -func (ctx NoOpStringContext) OnResourceOwnerChange(_ *CompositeValue, _ common.Address, _ common.Address) { +func (NoOpStringContext) OnResourceOwnerChange(_ *CompositeValue, _ common.Address, _ common.Address) { panic(errors.NewUnreachableError()) } -func (ctx NoOpStringContext) RecordStorageMutation() { +func (NoOpStringContext) RecordStorageMutation() { panic(errors.NewUnreachableError()) } -func (ctx NoOpStringContext) StorageMutatedDuringIteration() bool { +func (NoOpStringContext) StorageMutatedDuringIteration() bool { panic(errors.NewUnreachableError()) } -func (ctx NoOpStringContext) InStorageIteration() bool { +func (NoOpStringContext) InStorageIteration() bool { panic(errors.NewUnreachableError()) } -func (ctx NoOpStringContext) SetInStorageIteration(_ bool) { +func (NoOpStringContext) SetInStorageIteration(_ bool) { panic(errors.NewUnreachableError()) } -func (ctx NoOpStringContext) GetEntitlementType(_ TypeID) (*sema.EntitlementType, error) { +func (NoOpStringContext) GetEntitlementType(_ TypeID) (*sema.EntitlementType, error) { panic(errors.NewUnreachableError()) } -func (ctx NoOpStringContext) GetEntitlementMapType(_ TypeID) (*sema.EntitlementMapType, error) { +func (NoOpStringContext) GetEntitlementMapType(_ TypeID) (*sema.EntitlementMapType, error) { panic(errors.NewUnreachableError()) } -func (ctx NoOpStringContext) GetInterfaceType(_ common.Location, _ string, _ TypeID) (*sema.InterfaceType, error) { +func (NoOpStringContext) GetInterfaceType(_ common.Location, _ string, _ TypeID) (*sema.InterfaceType, error) { panic(errors.NewUnreachableError()) } -func (ctx NoOpStringContext) GetCompositeType(_ common.Location, _ string, _ TypeID) (*sema.CompositeType, error) { +func (NoOpStringContext) GetCompositeType(_ common.Location, _ string, _ TypeID) (*sema.CompositeType, error) { panic(errors.NewUnreachableError()) } -func (ctx NoOpStringContext) IsTypeInfoRecovered(_ common.Location) bool { +func (NoOpStringContext) IsTypeInfoRecovered(_ common.Location) bool { panic(errors.NewUnreachableError()) } -func (ctx NoOpStringContext) SemaTypeFromStaticType(_ StaticType) sema.Type { +func (NoOpStringContext) SemaTypeFromStaticType(_ StaticType) sema.Type { + panic(errors.NewUnreachableError()) +} + +func (NoOpStringContext) SemaAccessFromStaticAuthorization(Authorization) (sema.Access, error) { panic(errors.NewUnreachableError()) } diff --git a/interpreter/interpreter.go b/interpreter/interpreter.go index 38535aca0..ac54b915e 100644 --- a/interpreter/interpreter.go +++ b/interpreter/interpreter.go @@ -5187,17 +5187,6 @@ func (interpreter *Interpreter) GetEntitlementMapType(typeID common.TypeID) (*se return ty, nil } -func MustConvertStaticAuthorizationToSemaAccess( - handler StaticAuthorizationConversionHandler, - auth Authorization, -) sema.Access { - access, err := ConvertStaticAuthorizationToSemaAccess(auth, handler) - if err != nil { - panic(err) - } - return access -} - func (interpreter *Interpreter) getElaboration(location common.Location) *sema.Elaboration { // Ensure the program for this location is loaded, @@ -6400,6 +6389,10 @@ func (interpreter *Interpreter) MaybeUpdateStorageReferenceMemberReceiver( return member } +func (interpreter *Interpreter) SemaAccessFromStaticAuthorization(auth Authorization) (sema.Access, error) { + return ConvertStaticAuthorizationToSemaAccess(auth, interpreter) +} + func StorageReference( context ValueStaticTypeContext, storageReference *StorageReferenceValue, diff --git a/interpreter/statictype.go b/interpreter/statictype.go index 290a8563f..2b7d99bc0 100644 --- a/interpreter/statictype.go +++ b/interpreter/statictype.go @@ -1184,6 +1184,12 @@ func ConvertSemaTransactionToStaticTransactionType( ) } +// ConvertStaticAuthorizationToSemaAccess converts authorization of static-types +// to the sema-type representation of the same. +// +// **IMPORTANT**: Do not use this function directly. Instead, use the +// `SemaAccessFromStaticAuthorization` method of the `TypeConverter` interface, +// since it will cache and re-use the conversion results. func ConvertStaticAuthorizationToSemaAccess( auth Authorization, handler StaticAuthorizationConversionHandler, @@ -1370,7 +1376,7 @@ func ConvertStaticToSemaType( return nil, err } - access, err := ConvertStaticAuthorizationToSemaAccess(t.Authorization, context) + access, err := context.SemaAccessFromStaticAuthorization(t.Authorization) if err != nil { return nil, err diff --git a/interpreter/statictype_test.go b/interpreter/statictype_test.go index a4f23f746..3d376a800 100644 --- a/interpreter/statictype_test.go +++ b/interpreter/statictype_test.go @@ -1776,6 +1776,10 @@ func (s staticTypeConversionHandler) SemaTypeFromStaticType(staticType StaticTyp return MustConvertStaticToSemaType(staticType, s) } +func (s staticTypeConversionHandler) SemaAccessFromStaticAuthorization(auth Authorization) (sema.Access, error) { + return ConvertStaticAuthorizationToSemaAccess(auth, s) +} + func TestIntersectionStaticType_ID(t *testing.T) { t.Parallel() diff --git a/interpreter/value_ephemeral_reference.go b/interpreter/value_ephemeral_reference.go index b55cbdda5..50c30b5ba 100644 --- a/interpreter/value_ephemeral_reference.go +++ b/interpreter/value_ephemeral_reference.go @@ -186,10 +186,16 @@ func (v *EphemeralReferenceValue) GetTypeKey(context MemberAccessibleContext, ke self := v.Value if selfComposite, isComposite := self.(*CompositeValue); isComposite { + + semaAccess, err := context.SemaAccessFromStaticAuthorization(v.Authorization) + if err != nil { + panic(err) + } + return selfComposite.getTypeKey( context, key, - MustConvertStaticAuthorizationToSemaAccess(context, v.Authorization), + semaAccess, ) } diff --git a/interpreter/value_storage_reference.go b/interpreter/value_storage_reference.go index c15135187..a2d33c943 100644 --- a/interpreter/value_storage_reference.go +++ b/interpreter/value_storage_reference.go @@ -283,7 +283,10 @@ func (v *StorageReferenceValue) GetTypeKey( self := v.mustReferencedValue(context) if selfComposite, isComposite := self.(*CompositeValue); isComposite { - access := MustConvertStaticAuthorizationToSemaAccess(context, v.Authorization) + access, err := context.SemaAccessFromStaticAuthorization(v.Authorization) + if err != nil { + panic(err) + } return selfComposite.getTypeKey( context, key,