From 85c06355941e33fb7240faeee450e03374329b64 Mon Sep 17 00:00:00 2001 From: Raymond Zhang Date: Fri, 10 Oct 2025 16:44:44 -0400 Subject: [PATCH 01/10] Reuse sema types from compiler, remove sema type in member access check. --- bbq/compiler/compiler.go | 21 +++++++---- bbq/program.go | 16 ++++---- bbq/vm/vm.go | 8 ++-- interpreter/interpreter_expression.go | 53 +++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 19 deletions(-) diff --git a/bbq/compiler/compiler.go b/bbq/compiler/compiler.go index 10d11e96e3..ead38787d2 100644 --- a/bbq/compiler/compiler.go +++ b/bbq/compiler/compiler.go @@ -105,6 +105,9 @@ type Compiler[E, T any] struct { // this table maps a global from its address qualified name to its original un-aliased typename // used mainly for exporting imports for linking globalRemoveAddressTable map[string]string + + // Passed to the vm to avoid repeated conversions from static-types to sema-types. + semaTypeCache map[sema.TypeID]sema.Type } var _ ast.DeclarationVisitor[struct{}] = &Compiler[any, any]{} @@ -194,6 +197,7 @@ func newCompiler[E, T any]( importedGlobals: importedGlobals, typesInPool: make(map[sema.TypeID]uint16), constantsInPool: make(map[constantUniqueKey]*DecodedConstant), + semaTypeCache: make(map[sema.TypeID]sema.Type), compositeTypeStack: &Stack[sema.CompositeKindedType]{ elements: make([]sema.CompositeKindedType, 0), }, @@ -722,13 +726,14 @@ func (c *Compiler[E, T]) Compile() *bbq.Program[E, T] { } return &bbq.Program[E, T]{ - Functions: functions, - Constants: constants, - Types: types, - Imports: imports, - Contracts: contracts, - Variables: variables, - Globals: globals, + Functions: functions, + Constants: constants, + Types: types, + Imports: imports, + Contracts: contracts, + Variables: variables, + Globals: globals, + SemaTypeCache: c.semaTypeCache, } } @@ -4099,6 +4104,8 @@ func (c *Compiler[_, _]) getOrAddType(ty sema.Type) uint16 { data := c.typeGen.CompileType(staticType) index = c.addCompiledType(ty, data) c.typesInPool[typeID] = index + + c.semaTypeCache[typeID] = ty } return index diff --git a/bbq/program.go b/bbq/program.go index 5020de965f..443ea60871 100644 --- a/bbq/program.go +++ b/bbq/program.go @@ -21,16 +21,18 @@ package bbq import ( "github.com/onflow/cadence/bbq/constant" "github.com/onflow/cadence/bbq/opcode" + "github.com/onflow/cadence/sema" ) type Program[E, T any] struct { - Contracts []*Contract - Imports []Import - Functions []Function[E] - Constants []constant.DecodedConstant - Variables []Variable[E] - Types []T - Globals []Global + Contracts []*Contract + Imports []Import + Functions []Function[E] + Constants []constant.DecodedConstant + Variables []Variable[E] + Types []T + Globals []Global + SemaTypeCache map[sema.TypeID]sema.Type } type InstructionProgram = Program[opcode.Instruction, StaticType] diff --git a/bbq/vm/vm.go b/bbq/vm/vm.go index 890542fb1d..119c0e0154 100644 --- a/bbq/vm/vm.go +++ b/bbq/vm/vm.go @@ -67,6 +67,7 @@ func NewVM( vm.configureContext() context.recoverErrors = vm.RecoverErrors + context.semaTypeCache = program.SemaTypeCache // Link global variables and functions. linkedGlobals := context.linkGlobals( @@ -1069,13 +1070,10 @@ func checkMemberAccessTargetType( context := vm.context - // TODO: Avoid sema type conversion. - accessedSemaType := context.SemaTypeFromStaticType(accessedType) - - interpreter.CheckMemberAccessTargetType( + interpreter.CheckMemberAccessTargetTypeStatic( context, accessedValue, - accessedSemaType, + accessedType, ) } diff --git a/interpreter/interpreter_expression.go b/interpreter/interpreter_expression.go index 9c42bd4c7e..d059fcfb0d 100644 --- a/interpreter/interpreter_expression.go +++ b/interpreter/interpreter_expression.go @@ -380,6 +380,59 @@ func CheckMemberAccessTargetType( } } +func CheckMemberAccessTargetTypeStatic( + context ValueStaticTypeContext, + target Value, + expectedType StaticType, +) { + switch expectedType := expectedType.(type) { + case *CompositeStaticType: + if expectedType.Location == nil { + return + } + + if expectedType.TypeID == "" { + return + } + } + + // NOTE: accesses of (optional) storage reference values + // are already checked in StorageReferenceValue.dereference + _, isStorageReference := target.(*StorageReferenceValue) + if !isStorageReference { + if optional, ok := target.(*SomeValue); ok { + _, isStorageReference = optional.value.(*StorageReferenceValue) + } + } + if isStorageReference { + return + } + + targetStaticType := target.StaticType(context) + + if _, ok := expectedType.(*OptionalStaticType); ok { + if _, ok := targetStaticType.(*OptionalStaticType); !ok { + targetSemaType := MustConvertStaticToSemaType(targetStaticType, context) + expectedSemaType := MustConvertStaticToSemaType(expectedType, context) + + panic(&MemberAccessTypeError{ + ExpectedType: expectedSemaType, + ActualType: targetSemaType, + }) + } + } + + if !IsSubType(context, targetStaticType, expectedType) { + targetSemaType := MustConvertStaticToSemaType(targetStaticType, context) + expectedSemaType := MustConvertStaticToSemaType(expectedType, context) + + panic(&MemberAccessTypeError{ + ExpectedType: expectedSemaType, + ActualType: targetSemaType, + }) + } +} + func (interpreter *Interpreter) VisitIdentifierExpression(expression *ast.IdentifierExpression) Value { name := expression.Identifier.Identifier variable := interpreter.FindVariable(name) From 5d2ff5ce1faeeb8c3bcbafadd64d629bc7ccc777 Mon Sep 17 00:00:00 2001 From: Raymond Zhang Date: Tue, 14 Oct 2025 14:03:37 -0400 Subject: [PATCH 02/10] Add comments. --- bbq/vm/linker.go | 2 ++ interpreter/interpreter_expression.go | 1 + 2 files changed, 3 insertions(+) diff --git a/bbq/vm/linker.go b/bbq/vm/linker.go index d7a4f8cb5d..96c4629d09 100644 --- a/bbq/vm/linker.go +++ b/bbq/vm/linker.go @@ -170,6 +170,8 @@ func linkImportedGlobal( context, linkedGlobalsCache, ) + + // TODO: Potentially add the imported program's sema type cache to the context's sema type cache } indexedGlobals = linkedGlobals.indexedGlobals diff --git a/interpreter/interpreter_expression.go b/interpreter/interpreter_expression.go index d059fcfb0d..f1d688980b 100644 --- a/interpreter/interpreter_expression.go +++ b/interpreter/interpreter_expression.go @@ -391,6 +391,7 @@ func CheckMemberAccessTargetTypeStatic( return } + // transactions have empty type ID if expectedType.TypeID == "" { return } From a11e16bdf7ac5cacef39c1c294d2279c91d530f1 Mon Sep 17 00:00:00 2001 From: Raymond Zhang Date: Tue, 14 Oct 2025 14:46:18 -0400 Subject: [PATCH 03/10] Update type conversion memory metering test. --- interpreter/memory_metering_test.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/interpreter/memory_metering_test.go b/interpreter/memory_metering_test.go index 5cb3b853a2..03238548f1 100644 --- a/interpreter/memory_metering_test.go +++ b/interpreter/memory_metering_test.go @@ -9234,14 +9234,13 @@ func TestInterpretStaticTypeConversionMetering(t *testing.T) { _, err = inter.Invoke("main") require.NoError(t, err) - assert.Equal(t, uint64(2), meter.getMemory(common.MemoryKindDictionarySemaType)) - assert.Equal(t, uint64(4), meter.getMemory(common.MemoryKindVariableSizedSemaType)) - assert.Equal(t, uint64(2), meter.getMemory(common.MemoryKindConstantSizedSemaType)) - assert.Equal(t, uint64(3), meter.getMemory(common.MemoryKindIntersectionSemaType)) - assert.Equal(t, uint64(4), meter.getMemory(common.MemoryKindReferenceSemaType)) - assert.Equal(t, uint64(2), meter.getMemory(common.MemoryKindCapabilitySemaType)) - // TODO: investigate why this is different for the compiler/VM - assert.Equal(t, ifCompile[uint64](3, 2), meter.getMemory(common.MemoryKindOptionalSemaType)) + assert.Equal(t, ifCompile[uint64](1, 2), meter.getMemory(common.MemoryKindDictionarySemaType)) + assert.Equal(t, ifCompile[uint64](2, 4), meter.getMemory(common.MemoryKindVariableSizedSemaType)) + assert.Equal(t, ifCompile[uint64](1, 2), meter.getMemory(common.MemoryKindConstantSizedSemaType)) + assert.Equal(t, ifCompile[uint64](2, 3), meter.getMemory(common.MemoryKindIntersectionSemaType)) + assert.Equal(t, ifCompile[uint64](2, 4), meter.getMemory(common.MemoryKindReferenceSemaType)) + assert.Equal(t, ifCompile[uint64](1, 2), meter.getMemory(common.MemoryKindCapabilitySemaType)) + assert.Equal(t, ifCompile[uint64](1, 2), meter.getMemory(common.MemoryKindOptionalSemaType)) }) } From 2962daaf76528c0eea37a9e5068c8471d0fb80b5 Mon Sep 17 00:00:00 2001 From: Raymond Zhang Date: Tue, 14 Oct 2025 14:56:29 -0400 Subject: [PATCH 04/10] Update runtime test. --- runtime/runtime_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/runtime/runtime_test.go b/runtime/runtime_test.go index 2d64575909..d5cea4fcf2 100644 --- a/runtime/runtime_test.go +++ b/runtime/runtime_test.go @@ -7045,10 +7045,6 @@ func TestRuntimeOnGetOrLoadProgramHits(t *testing.T) { Address: Address{0x1}, Name: "HelloWorld", }, - common.AddressLocation{ - Address: Address{0x1}, - Name: "HelloWorld", - }, } } else { expectedHits = []common.Location{ From 751e32e74a6c00efd3b12a205c9abd94df9aba14 Mon Sep 17 00:00:00 2001 From: Raymond Zhang Date: Tue, 14 Oct 2025 14:58:06 -0400 Subject: [PATCH 05/10] Add type cache of linked programs. --- bbq/vm/linker.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/bbq/vm/linker.go b/bbq/vm/linker.go index 96c4629d09..934e6ac10e 100644 --- a/bbq/vm/linker.go +++ b/bbq/vm/linker.go @@ -49,6 +49,8 @@ func LinkGlobals( globals := make([]Variable, len(program.Globals)) indexedGlobals := activations.NewActivation[Variable](memoryGauge, nil) + var visitedLocations map[common.Location]struct{} + // NOTE: ensure both the context and the mapping are updated for _, global := range program.Globals { @@ -108,6 +110,7 @@ func LinkGlobals( typedGlobal, context, linkedGlobalsCache, + visitedLocations, ) globals[index] = importedGlobal @@ -145,6 +148,7 @@ func linkImportedGlobal( importedGlobal *bbq.ImportedGlobal, context *Context, linkedGlobalsCache map[common.Location]LinkedGlobals, + visitedLocations map[common.Location]struct{}, ) Variable { importLocation := importedGlobal.Location @@ -171,7 +175,17 @@ func linkImportedGlobal( linkedGlobalsCache, ) - // TODO: Potentially add the imported program's sema type cache to the context's sema type cache + if _, ok := visitedLocations[importLocation]; !ok { + // add the imported program's sema type cache to the context's sema type cache + // TODO: Maybe this is excessive, balance size and performance + for typeID, semaType := range importedProgram.SemaTypeCache { + if _, ok := context.semaTypeCache[typeID]; ok { + continue + } + context.semaTypeCache[typeID] = semaType + } + visitedLocations[importLocation] = struct{}{} + } } indexedGlobals = linkedGlobals.indexedGlobals From d6167e789f05f736f8248cb02e304ec63c1bfa97 Mon Sep 17 00:00:00 2001 From: Raymond Zhang Date: Tue, 14 Oct 2025 15:23:31 -0400 Subject: [PATCH 06/10] Fix linker changes. --- bbq/vm/linker.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bbq/vm/linker.go b/bbq/vm/linker.go index 934e6ac10e..3ee9370ee7 100644 --- a/bbq/vm/linker.go +++ b/bbq/vm/linker.go @@ -49,7 +49,7 @@ func LinkGlobals( globals := make([]Variable, len(program.Globals)) indexedGlobals := activations.NewActivation[Variable](memoryGauge, nil) - var visitedLocations map[common.Location]struct{} + visitedLocations := map[common.Location]struct{}{} // NOTE: ensure both the context and the mapping are updated @@ -178,7 +178,7 @@ func linkImportedGlobal( if _, ok := visitedLocations[importLocation]; !ok { // add the imported program's sema type cache to the context's sema type cache // TODO: Maybe this is excessive, balance size and performance - for typeID, semaType := range importedProgram.SemaTypeCache { + for typeID, semaType := range importedProgram.SemaTypeCache { //nolint:maprange if _, ok := context.semaTypeCache[typeID]; ok { continue } From 273842ae1e525692b9c6154424512bf1fd30ba53 Mon Sep 17 00:00:00 2001 From: Raymond Zhang Date: Tue, 14 Oct 2025 17:04:10 -0400 Subject: [PATCH 07/10] Revert check member access changes. --- bbq/vm/vm.go | 7 +++- interpreter/interpreter_expression.go | 54 --------------------------- 2 files changed, 5 insertions(+), 56 deletions(-) diff --git a/bbq/vm/vm.go b/bbq/vm/vm.go index 119c0e0154..f639dd9bc1 100644 --- a/bbq/vm/vm.go +++ b/bbq/vm/vm.go @@ -1070,10 +1070,13 @@ func checkMemberAccessTargetType( context := vm.context - interpreter.CheckMemberAccessTargetTypeStatic( + // TODO: Avoid sema type conversion + accessedSemaType := context.SemaTypeFromStaticType(accessedType) + + interpreter.CheckMemberAccessTargetType( context, accessedValue, - accessedType, + accessedSemaType, ) } diff --git a/interpreter/interpreter_expression.go b/interpreter/interpreter_expression.go index f1d688980b..9c42bd4c7e 100644 --- a/interpreter/interpreter_expression.go +++ b/interpreter/interpreter_expression.go @@ -380,60 +380,6 @@ func CheckMemberAccessTargetType( } } -func CheckMemberAccessTargetTypeStatic( - context ValueStaticTypeContext, - target Value, - expectedType StaticType, -) { - switch expectedType := expectedType.(type) { - case *CompositeStaticType: - if expectedType.Location == nil { - return - } - - // transactions have empty type ID - if expectedType.TypeID == "" { - return - } - } - - // NOTE: accesses of (optional) storage reference values - // are already checked in StorageReferenceValue.dereference - _, isStorageReference := target.(*StorageReferenceValue) - if !isStorageReference { - if optional, ok := target.(*SomeValue); ok { - _, isStorageReference = optional.value.(*StorageReferenceValue) - } - } - if isStorageReference { - return - } - - targetStaticType := target.StaticType(context) - - if _, ok := expectedType.(*OptionalStaticType); ok { - if _, ok := targetStaticType.(*OptionalStaticType); !ok { - targetSemaType := MustConvertStaticToSemaType(targetStaticType, context) - expectedSemaType := MustConvertStaticToSemaType(expectedType, context) - - panic(&MemberAccessTypeError{ - ExpectedType: expectedSemaType, - ActualType: targetSemaType, - }) - } - } - - if !IsSubType(context, targetStaticType, expectedType) { - targetSemaType := MustConvertStaticToSemaType(targetStaticType, context) - expectedSemaType := MustConvertStaticToSemaType(expectedType, context) - - panic(&MemberAccessTypeError{ - ExpectedType: expectedSemaType, - ActualType: targetSemaType, - }) - } -} - func (interpreter *Interpreter) VisitIdentifierExpression(expression *ast.IdentifierExpression) Value { name := expression.Identifier.Identifier variable := interpreter.FindVariable(name) From 7bc4611274b955e98b8479718c5844f7a9d59b06 Mon Sep 17 00:00:00 2001 From: Raymond Zhang Date: Wed, 15 Oct 2025 15:30:10 -0400 Subject: [PATCH 08/10] Remove linker changes to reuse compiler types. --- bbq/vm/linker.go | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/bbq/vm/linker.go b/bbq/vm/linker.go index 3ee9370ee7..d7a4f8cb5d 100644 --- a/bbq/vm/linker.go +++ b/bbq/vm/linker.go @@ -49,8 +49,6 @@ func LinkGlobals( globals := make([]Variable, len(program.Globals)) indexedGlobals := activations.NewActivation[Variable](memoryGauge, nil) - visitedLocations := map[common.Location]struct{}{} - // NOTE: ensure both the context and the mapping are updated for _, global := range program.Globals { @@ -110,7 +108,6 @@ func LinkGlobals( typedGlobal, context, linkedGlobalsCache, - visitedLocations, ) globals[index] = importedGlobal @@ -148,7 +145,6 @@ func linkImportedGlobal( importedGlobal *bbq.ImportedGlobal, context *Context, linkedGlobalsCache map[common.Location]LinkedGlobals, - visitedLocations map[common.Location]struct{}, ) Variable { importLocation := importedGlobal.Location @@ -174,18 +170,6 @@ func linkImportedGlobal( context, linkedGlobalsCache, ) - - if _, ok := visitedLocations[importLocation]; !ok { - // add the imported program's sema type cache to the context's sema type cache - // TODO: Maybe this is excessive, balance size and performance - for typeID, semaType := range importedProgram.SemaTypeCache { //nolint:maprange - if _, ok := context.semaTypeCache[typeID]; ok { - continue - } - context.semaTypeCache[typeID] = semaType - } - visitedLocations[importLocation] = struct{}{} - } } indexedGlobals = linkedGlobals.indexedGlobals From b7fad6bf6f7001af09a90f565ce8733f67ec099c Mon Sep 17 00:00:00 2001 From: Raymond Zhang Date: Wed, 15 Oct 2025 15:37:49 -0400 Subject: [PATCH 09/10] Fix runtime tests. --- runtime/runtime_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/runtime/runtime_test.go b/runtime/runtime_test.go index d5cea4fcf2..2d64575909 100644 --- a/runtime/runtime_test.go +++ b/runtime/runtime_test.go @@ -7045,6 +7045,10 @@ func TestRuntimeOnGetOrLoadProgramHits(t *testing.T) { Address: Address{0x1}, Name: "HelloWorld", }, + common.AddressLocation{ + Address: Address{0x1}, + Name: "HelloWorld", + }, } } else { expectedHits = []common.Location{ From cd93e3dc303654dd66a5a3cbc846ab2a0cb97530 Mon Sep 17 00:00:00 2001 From: Raymond Zhang Date: Mon, 1 Dec 2025 14:12:55 -0500 Subject: [PATCH 10/10] Move type cache externally. --- bbq/compiler/compiler.go | 25 ++++++++++++------------- bbq/compiler/config.go | 5 +++++ bbq/program.go | 16 +++++++--------- bbq/vm/config.go | 7 +++++++ bbq/vm/context.go | 21 ++++++++++++++++++++- bbq/vm/test/ft_test.go | 13 ++++++++++++- bbq/vm/vm.go | 1 - 7 files changed, 63 insertions(+), 25 deletions(-) diff --git a/bbq/compiler/compiler.go b/bbq/compiler/compiler.go index f99ea21951..257c202088 100644 --- a/bbq/compiler/compiler.go +++ b/bbq/compiler/compiler.go @@ -106,9 +106,7 @@ type Compiler[E, T any] struct { // used mainly for exporting imports for linking globalRemoveAddressTable map[string]string - // Passed to the vm to avoid repeated conversions from static-types to sema-types. - semaTypeCache map[sema.TypeID]sema.Type - addedImports map[common.Location]struct{} + addedImports map[common.Location]struct{} } var _ ast.DeclarationVisitor[struct{}] = &Compiler[any, any]{} @@ -198,7 +196,6 @@ func newCompiler[E, T any]( importedGlobals: importedGlobals, typesInPool: make(map[sema.TypeID]uint16), constantsInPool: make(map[constantUniqueKey]*DecodedConstant), - semaTypeCache: make(map[sema.TypeID]sema.Type), compositeTypeStack: &Stack[sema.CompositeKindedType]{ elements: make([]sema.CompositeKindedType, 0), }, @@ -739,14 +736,13 @@ func (c *Compiler[E, T]) Compile() *bbq.Program[E, T] { common.UseMemory(c.Config.MemoryGauge, common.CompilerBBQProgramMemoryUsage) return &bbq.Program[E, T]{ - Functions: functions, - Constants: constants, - Types: types, - Imports: imports, - Contracts: contracts, - Variables: variables, - Globals: globals, - SemaTypeCache: c.semaTypeCache, + Functions: functions, + Constants: constants, + Types: types, + Imports: imports, + Contracts: contracts, + Variables: variables, + Globals: globals, } } @@ -4279,7 +4275,10 @@ func (c *Compiler[_, _]) getOrAddType(ty sema.Type) uint16 { index = c.addCompiledType(ty, data) c.typesInPool[typeID] = index - c.semaTypeCache[typeID] = ty + semaTypeCache := c.Config.GetSemaTypeCache() + if semaTypeCache != nil { + semaTypeCache[typeID] = ty + } } return index diff --git a/bbq/compiler/config.go b/bbq/compiler/config.go index ae1f865a43..1a10487134 100644 --- a/bbq/compiler/config.go +++ b/bbq/compiler/config.go @@ -22,12 +22,15 @@ import ( "github.com/onflow/cadence/activations" "github.com/onflow/cadence/bbq/commons" "github.com/onflow/cadence/common" + "github.com/onflow/cadence/sema" ) type BuiltinGlobalsProvider func(location common.Location) *activations.Activation[GlobalImport] type ElaborationResolver func(location common.Location) (*DesugaredElaboration, error) +type GetSemaTypeCache func() map[sema.TypeID]sema.Type + type Config struct { MemoryGauge common.MemoryGauge ImportHandler commons.ImportHandler @@ -38,4 +41,6 @@ type Config struct { BuiltinGlobalsProvider BuiltinGlobalsProvider PeepholeOptimizationsEnabled bool + + GetSemaTypeCache GetSemaTypeCache } diff --git a/bbq/program.go b/bbq/program.go index 443ea60871..5020de965f 100644 --- a/bbq/program.go +++ b/bbq/program.go @@ -21,18 +21,16 @@ package bbq import ( "github.com/onflow/cadence/bbq/constant" "github.com/onflow/cadence/bbq/opcode" - "github.com/onflow/cadence/sema" ) type Program[E, T any] struct { - Contracts []*Contract - Imports []Import - Functions []Function[E] - Constants []constant.DecodedConstant - Variables []Variable[E] - Types []T - Globals []Global - SemaTypeCache map[sema.TypeID]sema.Type + Contracts []*Contract + Imports []Import + Functions []Function[E] + Constants []constant.DecodedConstant + Variables []Variable[E] + Types []T + Globals []Global } type InstructionProgram = Program[opcode.Instruction, StaticType] diff --git a/bbq/vm/config.go b/bbq/vm/config.go index b9991826a2..4997ff2f20 100644 --- a/bbq/vm/config.go +++ b/bbq/vm/config.go @@ -74,6 +74,8 @@ type Config struct { StackDepthLimit uint64 debugEnabled bool + + GetSemaTypeCache GetSemaTypeCache } func NewConfig(storage interpreter.Storage) *Config { @@ -85,6 +87,9 @@ func NewConfig(storage interpreter.Storage) *Config { storage: storage, StackDepthLimit: math.MaxInt, Tracer: tracer, + GetSemaTypeCache: func() map[sema.TypeID]sema.Type { + return nil + }, } } @@ -301,6 +306,8 @@ type ContractValueHandler func( type ElaborationResolver func(location common.Location) (*sema.Elaboration, error) +type GetSemaTypeCache func() map[sema.TypeID]sema.Type + type EntitlementTypeHandlerFunc func(location common.Location, typeID interpreter.TypeID) *sema.EntitlementType type EntitlementMapTypeHandlerFunc func(location common.Location, typeID interpreter.TypeID) *sema.EntitlementMapType diff --git a/bbq/vm/context.go b/bbq/vm/context.go index 9053802e58..bb12277a7b 100644 --- a/bbq/vm/context.go +++ b/bbq/vm/context.go @@ -19,6 +19,8 @@ package vm import ( + "fmt" + "github.com/onflow/atree" "github.com/onflow/cadence/bbq" @@ -29,6 +31,9 @@ import ( "github.com/onflow/cadence/sema" ) +var cacheHitCount int = 0 +var cacheMissCount int = 0 + // Context holds the information about the current execution at any given point of time. // It consists of: // - Re-usable configurations (Config). @@ -76,7 +81,8 @@ var _ interpreter.InvocationContext = &Context{} func NewContext(config *Config) *Context { return &Context{ - Config: config, + Config: config, + semaTypeCache: config.GetSemaTypeCache(), } } @@ -427,6 +433,7 @@ func (c *Context) SemaTypeFromStaticType(staticType interpreter.StaticType) sema typeID := staticType.ID() semaType, ok := c.semaTypeCache[typeID] if ok { + cacheHitCount++ return semaType } @@ -438,6 +445,8 @@ func (c *Context) SemaTypeFromStaticType(staticType interpreter.StaticType) sema } c.semaTypeCache[typeID] = semaType + cacheMissCount++ + return semaType } @@ -551,3 +560,13 @@ func (c *Context) SemaAccessFromStaticAuthorization(auth interpreter.Authorizati return semaAccess, nil } + +func (c *Context) GetCacheStatistics() { + fmt.Printf("Cache hit count: %d\n", cacheHitCount) + fmt.Printf("Cache miss count: %d\n", cacheMissCount) +} + +func (c *Context) ResetCacheStatistics() { + cacheHitCount = 0 + cacheMissCount = 0 +} diff --git a/bbq/vm/test/ft_test.go b/bbq/vm/test/ft_test.go index 4527259cb0..5ab2b94935 100644 --- a/bbq/vm/test/ft_test.go +++ b/bbq/vm/test/ft_test.go @@ -61,6 +61,8 @@ func compiledFTTransfer(tb testing.TB) { nonFungibleTokenLocation := common.NewAddressLocation(nil, contractsAddress, "NonFungibleToken") flowTokenLocation := common.NewAddressLocation(nil, contractsAddress, "FlowToken") + semaTypeCache := make(map[sema.TypeID]sema.Type) + codes := map[common.Location][]byte{ burnerLocation: []byte(contracts.RealBurnerContract), viewResolverLocation: []byte(contracts.RealViewResolverContract), @@ -91,7 +93,10 @@ func compiledFTTransfer(tb testing.TB) { compilerConfig := &compiler.Config{ LocationHandler: locationHandler, - ImportHandler: importHandler, + GetSemaTypeCache: func() map[sema.TypeID]sema.Type { + return semaTypeCache + }, + ImportHandler: importHandler, ElaborationResolver: func(location common.Location) (*compiler.DesugaredElaboration, error) { imported, ok := compiledPrograms[location] if !ok { @@ -164,6 +169,10 @@ func compiledFTTransfer(tb testing.TB) { vmConfig := vm.NewConfig(storage) + // vmConfig.GetSemaTypeCache = func() map[sema.TypeID]sema.Type { + // return semaTypeCache + // } + vmConfig.CapabilityBorrowHandler = func( context interpreter.BorrowCapabilityControllerContext, address interpreter.AddressValue, @@ -483,6 +492,8 @@ func compiledFTTransfer(tb testing.TB) { ) } } + + tokenTransferTxVM.Context().GetCacheStatistics() } func TestFTTransfer(t *testing.T) { diff --git a/bbq/vm/vm.go b/bbq/vm/vm.go index d6af8e942f..a07c96212b 100644 --- a/bbq/vm/vm.go +++ b/bbq/vm/vm.go @@ -67,7 +67,6 @@ func NewVM( vm.configureContext() context.recoverErrors = vm.RecoverErrors - context.semaTypeCache = program.SemaTypeCache // Link global variables and functions. linkedGlobals := context.linkGlobals(