From 3cf9e9fbbd8fc056006686336bd6956da9e214f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 4 Jun 2024 15:10:59 -0700 Subject: [PATCH 1/3] cache static types based on encoding --- migrations/cache.go | 79 +++++++++++++++++++ migrations/entitlements/migration.go | 29 ++++++- .../statictypes/statictype_migration.go | 33 +++++++- 3 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 migrations/cache.go diff --git a/migrations/cache.go b/migrations/cache.go new file mode 100644 index 0000000000..e36d610edc --- /dev/null +++ b/migrations/cache.go @@ -0,0 +1,79 @@ +/* + * 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 migrations + +import ( + "sync" + + "github.com/onflow/cadence/runtime/interpreter" +) + +type CachedStaticType struct { + StaticType interpreter.StaticType + Error error +} + +type StaticTypeKey string + +func NewStaticTypeKey(staticType interpreter.StaticType) (StaticTypeKey, error) { + raw, err := interpreter.StaticTypeToBytes(staticType) + if err != nil { + return "", err + } + return StaticTypeKey(raw), nil +} + +type StaticTypeCache interface { + Get(key StaticTypeKey) (CachedStaticType, bool) + Set( + key StaticTypeKey, + staticType interpreter.StaticType, + err error, + ) +} + +type DefaultStaticTypeCache struct { + entries sync.Map +} + +func NewDefaultStaticTypeCache() *DefaultStaticTypeCache { + return &DefaultStaticTypeCache{} +} + +func (c *DefaultStaticTypeCache) Get(key StaticTypeKey) (CachedStaticType, bool) { + v, ok := c.entries.Load(key) + if !ok { + return CachedStaticType{}, false + } + return v.(CachedStaticType), true +} + +func (c *DefaultStaticTypeCache) Set( + key StaticTypeKey, + staticType interpreter.StaticType, + err error, +) { + c.entries.Store( + key, + CachedStaticType{ + StaticType: staticType, + Error: err, + }, + ) +} diff --git a/migrations/entitlements/migration.go b/migrations/entitlements/migration.go index 2e9460e47a..3375bac138 100644 --- a/migrations/entitlements/migration.go +++ b/migrations/entitlements/migration.go @@ -23,19 +23,30 @@ import ( "github.com/onflow/cadence/migrations" "github.com/onflow/cadence/migrations/statictypes" + "github.com/onflow/cadence/runtime/errors" "github.com/onflow/cadence/runtime/interpreter" "github.com/onflow/cadence/runtime/sema" ) type EntitlementsMigration struct { - Interpreter *interpreter.Interpreter + Interpreter *interpreter.Interpreter + migratedTypeCache migrations.StaticTypeCache } var _ migrations.ValueMigration = EntitlementsMigration{} func NewEntitlementsMigration(inter *interpreter.Interpreter) EntitlementsMigration { + staticTypeCache := migrations.NewDefaultStaticTypeCache() + return NewEntitlementsMigrationWithCache(inter, staticTypeCache) +} + +func NewEntitlementsMigrationWithCache( + inter *interpreter.Interpreter, + migratedTypeCache migrations.StaticTypeCache, +) EntitlementsMigration { return EntitlementsMigration{ - Interpreter: inter, + Interpreter: inter, + migratedTypeCache: migratedTypeCache, } } @@ -74,6 +85,20 @@ func (m EntitlementsMigration) ConvertToEntitledType( } inter := m.Interpreter + migratedTypeCache := m.migratedTypeCache + + key, err := migrations.NewStaticTypeKey(staticType) + if err != nil { + panic(errors.NewUnexpectedErrorFromCause(err)) + } + + if migratedType, exists := migratedTypeCache.Get(key); exists { + return migratedType.StaticType, migratedType.Error + } + + defer func() { + migratedTypeCache.Set(key, resultType, err) + }() switch t := staticType.(type) { case *interpreter.ReferenceStaticType: diff --git a/migrations/statictypes/statictype_migration.go b/migrations/statictypes/statictype_migration.go index aa8cfc22dc..c013fe714a 100644 --- a/migrations/statictypes/statictype_migration.go +++ b/migrations/statictypes/statictype_migration.go @@ -31,6 +31,7 @@ import ( type StaticTypeMigration struct { compositeTypeConverter CompositeTypeConverterFunc interfaceTypeConverter InterfaceTypeConverterFunc + migratedTypeCache migrations.StaticTypeCache } type CompositeTypeConverterFunc func(*interpreter.CompositeStaticType) interpreter.StaticType @@ -39,7 +40,14 @@ type InterfaceTypeConverterFunc func(*interpreter.InterfaceStaticType) interpret var _ migrations.ValueMigration = &StaticTypeMigration{} func NewStaticTypeMigration() *StaticTypeMigration { - return &StaticTypeMigration{} + staticTypeCache := migrations.NewDefaultStaticTypeCache() + return NewStaticTypeMigrationWithCache(staticTypeCache) +} + +func NewStaticTypeMigrationWithCache(migratedTypeCache migrations.StaticTypeCache) *StaticTypeMigration { + return &StaticTypeMigration{ + migratedTypeCache: migratedTypeCache, + } } func (m *StaticTypeMigration) WithCompositeTypeConverter(converterFunc CompositeTypeConverterFunc) *StaticTypeMigration { @@ -164,6 +172,29 @@ func (m *StaticTypeMigration) maybeConvertStaticType( ) ( resultType interpreter.StaticType, ) { + // Consult the cache and cache the result at the root of the migration, + // i.e. when the parent type is nil. + // + // Parse of the migration, e.g. the intersection type migration depends on the parent type. + // For example, `{Ts}` in `&{Ts}` is migrated differently from `{Ts}`. + + if parentType == nil { + migratedTypeCache := m.migratedTypeCache + + key, err := migrations.NewStaticTypeKey(staticType) + if err != nil { + panic(errors.NewUnexpectedErrorFromCause(err)) + } + + if cachedType, exists := migratedTypeCache.Get(key); exists { + return cachedType.StaticType + } + + defer func() { + migratedTypeCache.Set(key, resultType, nil) + }() + } + switch staticType := staticType.(type) { case *interpreter.ConstantSizedStaticType: convertedType := m.maybeConvertStaticType(staticType.Type, staticType) From 8a9a4c37a1a577e2cd08d870c76cb92348b23474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 5 Jun 2024 08:10:57 -0700 Subject: [PATCH 2/3] only cache if cache key generation succeeds --- migrations/entitlements/migration.go | 22 +++++++++---------- .../statictypes/statictype_migration.go | 22 +++++++++---------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/migrations/entitlements/migration.go b/migrations/entitlements/migration.go index 3375bac138..71a570486a 100644 --- a/migrations/entitlements/migration.go +++ b/migrations/entitlements/migration.go @@ -23,7 +23,6 @@ import ( "github.com/onflow/cadence/migrations" "github.com/onflow/cadence/migrations/statictypes" - "github.com/onflow/cadence/runtime/errors" "github.com/onflow/cadence/runtime/interpreter" "github.com/onflow/cadence/runtime/sema" ) @@ -87,19 +86,20 @@ func (m EntitlementsMigration) ConvertToEntitledType( inter := m.Interpreter migratedTypeCache := m.migratedTypeCache - key, err := migrations.NewStaticTypeKey(staticType) - if err != nil { - panic(errors.NewUnexpectedErrorFromCause(err)) - } + if migratedTypeCache != nil { + // Only cache if cache key generation succeeds. + // Some static types, like function types, are not encodable. + if key, keyErr := migrations.NewStaticTypeKey(staticType); keyErr == nil { + if migratedType, exists := migratedTypeCache.Get(key); exists { + return migratedType.StaticType, migratedType.Error + } - if migratedType, exists := migratedTypeCache.Get(key); exists { - return migratedType.StaticType, migratedType.Error + defer func() { + migratedTypeCache.Set(key, resultType, err) + }() + } } - defer func() { - migratedTypeCache.Set(key, resultType, err) - }() - switch t := staticType.(type) { case *interpreter.ReferenceStaticType: diff --git a/migrations/statictypes/statictype_migration.go b/migrations/statictypes/statictype_migration.go index c013fe714a..5c33b96462 100644 --- a/migrations/statictypes/statictype_migration.go +++ b/migrations/statictypes/statictype_migration.go @@ -180,19 +180,19 @@ func (m *StaticTypeMigration) maybeConvertStaticType( if parentType == nil { migratedTypeCache := m.migratedTypeCache + if migratedTypeCache != nil { + // Only cache if cache key generation succeeds. + // Some static types, like function types, are not encodable. + if key, keyErr := migrations.NewStaticTypeKey(staticType); keyErr == nil { + if cachedType, exists := migratedTypeCache.Get(key); exists { + return cachedType.StaticType + } - key, err := migrations.NewStaticTypeKey(staticType) - if err != nil { - panic(errors.NewUnexpectedErrorFromCause(err)) - } - - if cachedType, exists := migratedTypeCache.Get(key); exists { - return cachedType.StaticType + defer func() { + migratedTypeCache.Set(key, resultType, nil) + }() + } } - - defer func() { - migratedTypeCache.Set(key, resultType, nil) - }() } switch staticType := staticType.(type) { From 30494d2f9c2c79dba6e84734def84b08a1f89218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 5 Jun 2024 08:11:35 -0700 Subject: [PATCH 3/3] disable caching by default --- migrations/entitlements/migration.go | 3 +-- migrations/statictypes/statictype_migration.go | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/migrations/entitlements/migration.go b/migrations/entitlements/migration.go index 71a570486a..94f6b19eeb 100644 --- a/migrations/entitlements/migration.go +++ b/migrations/entitlements/migration.go @@ -35,8 +35,7 @@ type EntitlementsMigration struct { var _ migrations.ValueMigration = EntitlementsMigration{} func NewEntitlementsMigration(inter *interpreter.Interpreter) EntitlementsMigration { - staticTypeCache := migrations.NewDefaultStaticTypeCache() - return NewEntitlementsMigrationWithCache(inter, staticTypeCache) + return NewEntitlementsMigrationWithCache(inter, nil) } func NewEntitlementsMigrationWithCache( diff --git a/migrations/statictypes/statictype_migration.go b/migrations/statictypes/statictype_migration.go index 5c33b96462..ccaee42f64 100644 --- a/migrations/statictypes/statictype_migration.go +++ b/migrations/statictypes/statictype_migration.go @@ -40,8 +40,7 @@ type InterfaceTypeConverterFunc func(*interpreter.InterfaceStaticType) interpret var _ migrations.ValueMigration = &StaticTypeMigration{} func NewStaticTypeMigration() *StaticTypeMigration { - staticTypeCache := migrations.NewDefaultStaticTypeCache() - return NewStaticTypeMigrationWithCache(staticTypeCache) + return NewStaticTypeMigrationWithCache(nil) } func NewStaticTypeMigrationWithCache(migratedTypeCache migrations.StaticTypeCache) *StaticTypeMigration {