Skip to content

Commit

Permalink
Rename Context to ExecutableProgram
Browse files Browse the repository at this point in the history
  • Loading branch information
SupunS committed Oct 25, 2024
1 parent 966036c commit b7004d6
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 57 deletions.
10 changes: 5 additions & 5 deletions bbq/vm/callframe.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
29 changes: 24 additions & 5 deletions bbq/vm/context.go → bbq/vm/executable_program.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
16 changes: 8 additions & 8 deletions bbq/vm/linker.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
}
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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,
}
}
4 changes: 2 additions & 2 deletions bbq/vm/value_function.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ import (
)

type FunctionValue struct {
Function *bbq.Function
Context *Context
Function *bbq.Function
Executable *ExecutableProgram
}

var _ Value = FunctionValue{}
Expand Down
70 changes: 33 additions & 37 deletions bbq/vm/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ type VM struct {
stack []Value
config *Config
linkedGlobalsCache map[common.Location]LinkedGlobals
contextCache map[common.Location]*Context
}

func NewVM(
Expand Down Expand Up @@ -66,7 +65,6 @@ func NewVM(
vm := &VM{
linkedGlobalsCache: linkedGlobalsCache,
config: conf,
contextCache: make(map[common.Location]*Context),
}

// Link global variables and functions.
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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()

Expand Down Expand Up @@ -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)
}
Expand All @@ -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) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -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:
Expand All @@ -710,31 +708,37 @@ 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)
}

return 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
}

Expand All @@ -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)
Expand Down

0 comments on commit b7004d6

Please sign in to comment.