Skip to content

Commit

Permalink
Merge pull request #3396 from onflow/bastian/cache-static-types-in-mi…
Browse files Browse the repository at this point in the history
…gration
  • Loading branch information
turbolent authored Jun 5, 2024
2 parents bf9965d + 30494d2 commit 81170a2
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 3 deletions.
79 changes: 79 additions & 0 deletions migrations/cache.go
Original file line number Diff line number Diff line change
@@ -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,
},
)
}
28 changes: 26 additions & 2 deletions migrations/entitlements/migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,23 @@ import (
)

type EntitlementsMigration struct {
Interpreter *interpreter.Interpreter
Interpreter *interpreter.Interpreter
migratedTypeCache migrations.StaticTypeCache
}

var _ migrations.ValueMigration = EntitlementsMigration{}

func NewEntitlementsMigration(inter *interpreter.Interpreter) EntitlementsMigration {
return NewEntitlementsMigrationWithCache(inter, nil)
}

func NewEntitlementsMigrationWithCache(
inter *interpreter.Interpreter,
migratedTypeCache migrations.StaticTypeCache,
) EntitlementsMigration {
return EntitlementsMigration{
Interpreter: inter,
Interpreter: inter,
migratedTypeCache: migratedTypeCache,
}
}

Expand Down Expand Up @@ -74,6 +83,21 @@ func (m EntitlementsMigration) ConvertToEntitledType(
}

inter := m.Interpreter
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 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:
Expand Down
32 changes: 31 additions & 1 deletion migrations/statictypes/statictype_migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
type StaticTypeMigration struct {
compositeTypeConverter CompositeTypeConverterFunc
interfaceTypeConverter InterfaceTypeConverterFunc
migratedTypeCache migrations.StaticTypeCache
}

type CompositeTypeConverterFunc func(*interpreter.CompositeStaticType) interpreter.StaticType
Expand All @@ -39,7 +40,13 @@ type InterfaceTypeConverterFunc func(*interpreter.InterfaceStaticType) interpret
var _ migrations.ValueMigration = &StaticTypeMigration{}

func NewStaticTypeMigration() *StaticTypeMigration {
return &StaticTypeMigration{}
return NewStaticTypeMigrationWithCache(nil)
}

func NewStaticTypeMigrationWithCache(migratedTypeCache migrations.StaticTypeCache) *StaticTypeMigration {
return &StaticTypeMigration{
migratedTypeCache: migratedTypeCache,
}
}

func (m *StaticTypeMigration) WithCompositeTypeConverter(converterFunc CompositeTypeConverterFunc) *StaticTypeMigration {
Expand Down Expand Up @@ -164,6 +171,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
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
}

defer func() {
migratedTypeCache.Set(key, resultType, nil)
}()
}
}
}

switch staticType := staticType.(type) {
case *interpreter.ConstantSizedStaticType:
convertedType := m.maybeConvertStaticType(staticType.Type, staticType)
Expand Down

0 comments on commit 81170a2

Please sign in to comment.