From b7004d65d3a548fa8ab0054134e718f923e42460 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Fri, 25 Oct 2024 11:28:54 -0700 Subject: [PATCH] Rename Context to ExecutableProgram --- bbq/vm/callframe.go | 10 +-- bbq/vm/{context.go => executable_program.go} | 29 ++++++-- bbq/vm/linker.go | 16 ++--- bbq/vm/value_function.go | 4 +- bbq/vm/vm.go | 70 +++++++++----------- 5 files changed, 72 insertions(+), 57 deletions(-) rename bbq/vm/{context.go => executable_program.go} (60%) diff --git a/bbq/vm/callframe.go b/bbq/vm/callframe.go index a0ca9dbb4..8aaa7e70c 100644 --- a/bbq/vm/callframe.go +++ b/bbq/vm/callframe.go @@ -23,11 +23,11 @@ import ( ) type callFrame struct { - parent *callFrame - context *Context - locals []Value - function *bbq.Function - ip uint16 + parent *callFrame + executable *ExecutableProgram + locals []Value + function *bbq.Function + ip uint16 } func (f *callFrame) getUint16() uint16 { diff --git a/bbq/vm/context.go b/bbq/vm/executable_program.go similarity index 60% rename from bbq/vm/context.go rename to bbq/vm/executable_program.go index fe8f2a9c3..d7ad6b08e 100644 --- a/bbq/vm/context.go +++ b/bbq/vm/executable_program.go @@ -20,25 +20,44 @@ package vm import ( "github.com/onflow/cadence/bbq" + "github.com/onflow/cadence/common" ) -// Context is the context of a program. +// ExecutableProgram is the 'executable' version of a `bbq.Program`. // It holds information that are accessible to a given program, // such as constants, static-types, and global variables. // These info are accessed by the opcodes of the program. -// i.e: indexes used in opcodes refer to the indexes of its context. -type Context struct { +// i.e: indexes used in opcodes refer to the indexes of its ExecutableProgram. +type ExecutableProgram struct { + Location common.Location Program *bbq.Program Globals []Value Constants []Value StaticTypes []StaticType } -func NewContext(program *bbq.Program, globals []Value) *Context { - return &Context{ +func NewExecutableProgram( + location common.Location, + program *bbq.Program, + globals []Value, +) *ExecutableProgram { + return &ExecutableProgram{ + Location: location, Program: program, Globals: globals, Constants: make([]Value, len(program.Constants)), StaticTypes: make([]StaticType, len(program.Types)), } } + +func NewLoadedExecutableProgram(location common.Location, program *bbq.Program) *ExecutableProgram { + executable := NewExecutableProgram(location, program, nil) + + // Optimization: Pre load/decode types + for index, bytes := range program.Types { + staticType := decodeType(bytes) + executable.StaticTypes[index] = staticType + } + + return executable +} diff --git a/bbq/vm/linker.go b/bbq/vm/linker.go index 1b781509e..ffac3ee43 100644 --- a/bbq/vm/linker.go +++ b/bbq/vm/linker.go @@ -28,7 +28,7 @@ import ( type LinkedGlobals struct { // context shared by the globals in the program. - context *Context + executable *ExecutableProgram // globals defined in the program, indexed by name. indexedGlobals map[string]Value @@ -74,7 +74,7 @@ func (vm *VM) LinkGlobals( contractValue := conf.ContractValueHandler(conf, location) // Update the globals - both the context and the mapping. // Contract value is always at the zero-th index. - linkedGlobals.context.Globals[0] = contractValue + linkedGlobals.executable.Globals[0] = contractValue linkedGlobals.indexedGlobals[contract.Name] = contractValue } } @@ -91,7 +91,7 @@ func (vm *VM) LinkGlobals( importedGlobals = append(importedGlobals, importedGlobal) } - ctx := vm.NewProgramContext(location, program) + executable := NewLoadedExecutableProgram(location, program) globals := make([]Value, 0) indexedGlobals := make(map[string]Value, 0) @@ -113,8 +113,8 @@ func (vm *VM) LinkGlobals( // TODO: include non-function globals for _, function := range program.Functions { value := FunctionValue{ - Function: function, - Context: ctx, + Function: function, + Executable: executable, } globals = append(globals, value) @@ -124,13 +124,13 @@ func (vm *VM) LinkGlobals( // Globals of the current program are added first. // This is the same order as they are added in the compiler. // e.g: [global1, global2, ... [importedGlobal1, importedGlobal2, ...]] - ctx.Globals = globals - ctx.Globals = append(ctx.Globals, importedGlobals...) + executable.Globals = globals + executable.Globals = append(executable.Globals, importedGlobals...) // Return only the globals defined in the current program. // Because the importer/caller doesn't need to know globals of nested imports. return LinkedGlobals{ - context: ctx, + executable: executable, indexedGlobals: indexedGlobals, } } diff --git a/bbq/vm/value_function.go b/bbq/vm/value_function.go index 90e17966c..0b9fdc06e 100644 --- a/bbq/vm/value_function.go +++ b/bbq/vm/value_function.go @@ -27,8 +27,8 @@ import ( ) type FunctionValue struct { - Function *bbq.Function - Context *Context + Function *bbq.Function + Executable *ExecutableProgram } var _ Value = FunctionValue{} diff --git a/bbq/vm/vm.go b/bbq/vm/vm.go index eecfe3f5d..1d8367ff9 100644 --- a/bbq/vm/vm.go +++ b/bbq/vm/vm.go @@ -38,7 +38,6 @@ type VM struct { stack []Value config *Config linkedGlobalsCache map[common.Location]LinkedGlobals - contextCache map[common.Location]*Context } func NewVM( @@ -66,7 +65,6 @@ func NewVM( vm := &VM{ linkedGlobalsCache: linkedGlobalsCache, config: conf, - contextCache: make(map[common.Location]*Context), } // Link global variables and functions. @@ -140,17 +138,17 @@ func (vm *VM) replaceTop(value Value) { vm.stack[lastIndex] = value } -func (vm *VM) pushCallFrame(ctx *Context, function *bbq.Function, arguments []Value) { - locals := make([]Value, function.LocalCount) +func (vm *VM) pushCallFrame(functionValue FunctionValue, arguments []Value) { + locals := make([]Value, functionValue.Function.LocalCount) for i, argument := range arguments { locals[i] = argument } callFrame := &callFrame{ - parent: vm.callFrame, - locals: locals, - function: function, - context: ctx, + parent: vm.callFrame, + locals: locals, + function: functionValue.Function, + executable: functionValue.Executable, } vm.callFrame = callFrame } @@ -193,7 +191,7 @@ func (vm *VM) invoke(function Value, arguments []Value) (Value, error) { ) } - vm.pushCallFrame(functionValue.Context, functionValue.Function, arguments) + vm.pushCallFrame(functionValue, arguments) vm.run() @@ -323,7 +321,7 @@ func opFalse(vm *VM) { func opGetConstant(vm *VM) { callFrame := vm.callFrame index := callFrame.getUint16() - constant := callFrame.context.Constants[index] + constant := callFrame.executable.Constants[index] if constant == nil { constant = vm.initializeConstant(index) } @@ -346,13 +344,13 @@ func opSetLocal(vm *VM) { func opGetGlobal(vm *VM) { callFrame := vm.callFrame index := callFrame.getUint16() - vm.push(callFrame.context.Globals[index]) + vm.push(callFrame.executable.Globals[index]) } func opSetGlobal(vm *VM) { callFrame := vm.callFrame index := callFrame.getUint16() - callFrame.context.Globals[index] = vm.pop() + callFrame.executable.Globals[index] = vm.pop() } func opSetIndex(vm *VM) { @@ -381,7 +379,7 @@ func opInvoke(vm *VM) { case FunctionValue: parameterCount := int(value.Function.ParameterCount) arguments := vm.stack[stackHeight-parameterCount:] - vm.pushCallFrame(value.Context, value.Function, arguments) + vm.pushCallFrame(value, arguments) vm.dropN(parameterCount) case NativeFunctionValue: parameterCount := value.ParameterCount @@ -437,7 +435,7 @@ func opInvokeDynamic(vm *VM) { parameterCount := int(functionValue.Function.ParameterCount) arguments := vm.stack[stackHeight-parameterCount:] - vm.pushCallFrame(functionValue.Context, functionValue.Function, arguments) + vm.pushCallFrame(functionValue, arguments) vm.dropN(parameterCount) } @@ -695,9 +693,9 @@ func (vm *VM) run() { } func (vm *VM) initializeConstant(index uint16) (value Value) { - ctx := vm.callFrame.context + executable := vm.callFrame.executable - constant := ctx.Program.Constants[index] + constant := executable.Program.Constants[index] switch constant.Kind { case constantkind.Int: // TODO: @@ -710,15 +708,17 @@ func (vm *VM) initializeConstant(index uint16) (value Value) { panic(errors.NewUnexpectedError("unsupported constant kind '%s'", constant.Kind.String())) } - ctx.Constants[index] = value + executable.Constants[index] = value return value } func (vm *VM) loadType() StaticType { callframe := vm.callFrame index := callframe.getUint16() - staticType := callframe.context.StaticTypes[index] + staticType := callframe.executable.StaticTypes[index] if staticType == nil { + // TODO: Remove. Should never reach because of the + // pre loading-decoding of types. staticType = vm.initializeType(index) } @@ -726,15 +726,19 @@ func (vm *VM) loadType() StaticType { } func (vm *VM) initializeType(index uint16) interpreter.StaticType { - ctx := vm.callFrame.context - typeBytes := ctx.Program.Types[index] + executable := vm.callFrame.executable + typeBytes := executable.Program.Types[index] + staticType := decodeType(typeBytes) + executable.StaticTypes[index] = staticType + return staticType +} + +func decodeType(typeBytes []byte) interpreter.StaticType { dec := interpreter.CBORDecMode.NewByteStreamDecoder(typeBytes) staticType, err := interpreter.NewTypeDecoder(dec, nil).DecodeStaticType() if err != nil { panic(err) } - - ctx.StaticTypes[index] = staticType return staticType } @@ -759,35 +763,27 @@ func (vm *VM) lookupFunction(location common.Location, name string) FunctionValu // TODO: This currently link all functions in program, unnecessarily. // Link only the requested function. program := vm.config.ImportHandler(location) - ctx := vm.NewProgramContext(location, program) + + // TODO: Instead of creating the executable here, maybe cache it, + // and the `ImportHandler` could return the executable itself. + executable := NewLoadedExecutableProgram(location, program) indexedGlobals := make(map[string]Value, len(program.Functions)) for _, function := range program.Functions { indexedGlobals[function.Name] = FunctionValue{ - Function: function, - Context: ctx, + Function: function, + Executable: executable, } } vm.linkedGlobalsCache[location] = LinkedGlobals{ - context: ctx, + executable: executable, indexedGlobals: indexedGlobals, } return indexedGlobals[name].(FunctionValue) } -func (vm *VM) NewProgramContext(location common.Location, program *bbq.Program) *Context { - // Only one context needs to be created per Program. - ctx, ok := vm.contextCache[location] - if !ok { - ctx = NewContext(program, nil) - vm.contextCache[location] = ctx - } - - return ctx -} - func decodeLocation(locationBytes []byte) common.Location { // TODO: is it possible to re-use decoders? dec := interpreter.CBORDecMode.NewByteStreamDecoder(locationBytes)