Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow different base activations per location in checker and interpreter #2887

Merged
merged 5 commits into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions runtime/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,10 @@ func DefaultCheckerConfig(
baseValueActivation.DeclareValue(stdlib.NewLogFunction(StandardOutputLogger{}))

return &sema.Config{
BaseValueActivation: baseValueActivation,
AccessCheckMode: sema.AccessCheckModeStrict,
BaseValueActivationHandler: func(_ common.Location) *sema.VariableActivation {
return baseValueActivation
},
AccessCheckMode: sema.AccessCheckModeStrict,
ImportHandler: func(
checker *sema.Checker,
importedLocation common.Location,
Expand Down Expand Up @@ -192,8 +194,10 @@ func PrepareInterpreter(filename string, debugger *interpreter.Debugger) (*inter
interpreter.Declare(baseActivation, stdlib.NewLogFunction(StandardOutputLogger{}))

config := &interpreter.Config{
BaseActivation: baseActivation,
Storage: storage,
BaseActivationHandler: func(_ common.Location) *interpreter.VariableActivation {
return baseActivation
},
Storage: storage,
UUIDHandler: func() (uint64, error) {
defer func() { uuid++ }()
return uuid, nil
Expand Down
200 changes: 176 additions & 24 deletions runtime/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,14 @@
type Environment interface {
ArgumentDecoder

DeclareValue(valueDeclaration stdlib.StandardLibraryValue)
DeclareType(typeDeclaration stdlib.StandardLibraryType)
DeclareValue(
valueDeclaration stdlib.StandardLibraryValue,
location common.Location,
)
DeclareType(
typeDeclaration stdlib.StandardLibraryType,
location common.Location,
)
Configure(
runtimeInterface Interface,
codesAndPrograms CodesAndPrograms,
Expand Down Expand Up @@ -79,9 +85,37 @@

type interpreterEnvironment struct {
interpreterEnvironmentReconfigured
baseTypeActivation *sema.VariableActivation
baseValueActivation *sema.VariableActivation
baseActivation *interpreter.VariableActivation

// defaultBaseTypeActivation is the base type activation that applies to all locations by default.
defaultBaseTypeActivation *sema.VariableActivation
// The base type activations for individual locations.
// location == nil is the base type activation that applies to all locations,
// unless there is a base type activation for the given location.
//
// Base type activations are lazily / implicitly created
// by DeclareType / semaBaseActivationFor
baseTypeActivationsByLocation map[common.Location]*sema.VariableActivation

// defaultBaseValueActivation is the base value activation that applies to all locations by default.
defaultBaseValueActivation *sema.VariableActivation
// The base value activations for individual locations.
// location == nil is the base value activation that applies to all locations,
// unless there is a base value activation for the given location.
//
// Base value activations are lazily / implicitly created
// by DeclareValue / semaBaseActivationFor
baseValueActivationsByLocation map[common.Location]*sema.VariableActivation

// defaultBaseActivation is the base activation that applies to all locations by default
defaultBaseActivation *interpreter.VariableActivation
// The base activations for individual locations.
// location == nil is the base activation that applies to all locations,
// unless there is a base activation for the given location.
//
// Base activations are lazily / implicitly created
// by DeclareValue / interpreterBaseActivationFor
baseActivationsByLocation map[common.Location]*interpreter.VariableActivation

InterpreterConfig *interpreter.Config
CheckerConfig *sema.Config
deployedContractConstructorInvocation *stdlib.DeployedContractConstructorInvocation
Expand Down Expand Up @@ -109,16 +143,16 @@
var _ common.MemoryGauge = &interpreterEnvironment{}

func newInterpreterEnvironment(config Config) *interpreterEnvironment {
baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation)
baseTypeActivation := sema.NewVariableActivation(sema.BaseTypeActivation)
baseActivation := activations.NewActivation[*interpreter.Variable](nil, interpreter.BaseActivation)
defaultBaseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation)
defaultBaseTypeActivation := sema.NewVariableActivation(sema.BaseTypeActivation)
defaultBaseActivation := activations.NewActivation(nil, interpreter.BaseActivation)

env := &interpreterEnvironment{
config: config,
baseValueActivation: baseValueActivation,
baseTypeActivation: baseTypeActivation,
baseActivation: baseActivation,
stackDepthLimiter: newStackDepthLimiter(config.StackDepthLimit),
config: config,
defaultBaseValueActivation: defaultBaseValueActivation,
defaultBaseTypeActivation: defaultBaseTypeActivation,
defaultBaseActivation: defaultBaseActivation,
stackDepthLimiter: newStackDepthLimiter(config.StackDepthLimit),
}
env.InterpreterConfig = env.newInterpreterConfig()
env.CheckerConfig = env.newCheckerConfig()
Expand All @@ -129,7 +163,7 @@
return &interpreter.Config{
InvalidatedResourceValidationEnabled: true,
MemoryGauge: e,
BaseActivation: e.baseActivation,
BaseActivationHandler: e.getBaseActivation,
OnEventEmitted: e.newOnEventEmittedHandler(),
OnAccountLinked: e.newOnAccountLinkedHandler(),
InjectedCompositeFieldsHandler: e.newInjectedCompositeFieldsHandler(),
Expand Down Expand Up @@ -161,8 +195,8 @@
func (e *interpreterEnvironment) newCheckerConfig() *sema.Config {
return &sema.Config{
AccessCheckMode: sema.AccessCheckModeStrict,
BaseValueActivation: e.baseValueActivation,
BaseTypeActivation: e.baseTypeActivation,
BaseValueActivationHandler: e.getBaseValueActivation,
BaseTypeActivationHandler: e.getBaseTypeActivation,
ValidTopLevelDeclarationsHandler: validTopLevelDeclarations,
LocationHandler: e.newLocationHandler(),
ImportHandler: e.resolveImport,
Expand All @@ -176,15 +210,15 @@
func NewBaseInterpreterEnvironment(config Config) *interpreterEnvironment {
env := newInterpreterEnvironment(config)
for _, valueDeclaration := range stdlib.DefaultStandardLibraryValues(env) {
env.DeclareValue(valueDeclaration)
env.DeclareValue(valueDeclaration, nil)
}
return env
}

func NewScriptInterpreterEnvironment(config Config) Environment {
env := newInterpreterEnvironment(config)
for _, valueDeclaration := range stdlib.DefaultScriptStandardLibraryValues(env) {
env.DeclareValue(valueDeclaration)
env.DeclareValue(valueDeclaration, nil)
}
return env
}
Expand All @@ -203,13 +237,63 @@
e.stackDepthLimiter.depth = 0
}

func (e *interpreterEnvironment) DeclareValue(valueDeclaration stdlib.StandardLibraryValue) {
e.baseValueActivation.DeclareValue(valueDeclaration)
interpreter.Declare(e.baseActivation, valueDeclaration)
func (e *interpreterEnvironment) DeclareValue(valueDeclaration stdlib.StandardLibraryValue, location common.Location) {
e.semaBaseActivationFor(
location,
&e.baseValueActivationsByLocation,
e.defaultBaseValueActivation,
).DeclareValue(valueDeclaration)

activation := e.interpreterBaseActivationFor(location)
interpreter.Declare(activation, valueDeclaration)
}

func (e *interpreterEnvironment) DeclareType(typeDeclaration stdlib.StandardLibraryType, location common.Location) {
e.semaBaseActivationFor(
location,
&e.baseTypeActivationsByLocation,
e.defaultBaseTypeActivation,
).DeclareType(typeDeclaration)
}

func (e *interpreterEnvironment) DeclareType(typeDeclaration stdlib.StandardLibraryType) {
e.baseTypeActivation.DeclareType(typeDeclaration)
func (e *interpreterEnvironment) semaBaseActivationFor(
location common.Location,
baseActivationsByLocation *map[Location]*sema.VariableActivation,
defaultBaseActivation *sema.VariableActivation,
) (baseActivation *sema.VariableActivation) {
if location == nil {
return defaultBaseActivation
}

if *baseActivationsByLocation == nil {
*baseActivationsByLocation = map[Location]*sema.VariableActivation{}
} else {
baseActivation = (*baseActivationsByLocation)[location]
}

Check warning on line 272 in runtime/environment.go

View check run for this annotation

Codecov / codecov/patch

runtime/environment.go#L271-L272

Added lines #L271 - L272 were not covered by tests
if baseActivation == nil {
baseActivation = sema.NewVariableActivation(defaultBaseActivation)
(*baseActivationsByLocation)[location] = baseActivation
}
return baseActivation
}

func (e *interpreterEnvironment) interpreterBaseActivationFor(
location common.Location,
) *interpreter.VariableActivation {
defaultBaseActivation := e.defaultBaseActivation
if location == nil {
return defaultBaseActivation
}

baseActivation := e.baseActivationsByLocation[location]
if baseActivation == nil {
baseActivation = activations.NewActivation[*interpreter.Variable](nil, defaultBaseActivation)
if e.baseActivationsByLocation == nil {
e.baseActivationsByLocation = map[common.Location]*interpreter.VariableActivation{}
}
e.baseActivationsByLocation[location] = baseActivation
}
return baseActivation
}

func (e *interpreterEnvironment) NewAuthAccountValue(address interpreter.AddressValue) interpreter.Value {
Expand Down Expand Up @@ -908,7 +992,8 @@

case nil:
qualifiedIdentifier := string(typeID)
ty := sema.TypeActivationNestedType(e.baseTypeActivation, qualifiedIdentifier)
baseTypeActivation := e.getBaseTypeActivation(location)
ty := sema.TypeActivationNestedType(baseTypeActivation, qualifiedIdentifier)
if ty == nil {
return nil
}
Expand Down Expand Up @@ -1096,3 +1181,70 @@

return nil
}

// getBaseValueActivation returns the base activation for the given location.
// If a value was declared for the location (using DeclareValue),
// then the specific base value activation for this location is returned.
// Otherwise, the default base activation that applies for all locations is returned.
func (e *interpreterEnvironment) getBaseValueActivation(
location common.Location,
) (
baseValueActivation *sema.VariableActivation,
) {
baseValueActivationsByLocation := e.baseValueActivationsByLocation
// Use the base value activation for the location, if any
// (previously implicitly created using DeclareValue)
baseValueActivation = baseValueActivationsByLocation[location]
if baseValueActivation == nil {
// If no base value activation for the location exists
// (no value was previously, specifically declared for the location using DeclareValue),
// return the base value activation that applies to all locations by default
baseValueActivation = e.defaultBaseValueActivation
}
return

}

// getBaseTypeActivation returns the base activation for the given location.
// If a type was declared for the location (using DeclareType),
// then the specific base type activation for this location is returned.
// Otherwise, the default base activation that applies for all locations is returned.
func (e *interpreterEnvironment) getBaseTypeActivation(
location common.Location,
) (
baseTypeActivation *sema.VariableActivation,
) {
// Use the base type activation for the location, if any
// (previously implicitly created using DeclareType)
baseTypeActivationsByLocation := e.baseTypeActivationsByLocation
baseTypeActivation = baseTypeActivationsByLocation[location]
if baseTypeActivation == nil {
// If no base type activation for the location exists
// (no type was previously, specifically declared for the location using DeclareType),
// return the base type activation that applies to all locations by default
baseTypeActivation = e.defaultBaseTypeActivation
}
return
}

// getBaseActivation returns the base activation for the given location.
// If a value was declared for the location (using DeclareValue),
// then the specific base activation for this location is returned.
// Otherwise, the default base activation that applies for all locations is returned.
func (e *interpreterEnvironment) getBaseActivation(
location common.Location,
) (
baseActivation *interpreter.VariableActivation,
) {
// Use the base activation for the location, if any
// (previously implicitly created using DeclareValue)
baseActivationsByLocation := e.baseActivationsByLocation
baseActivation = baseActivationsByLocation[location]
if baseActivation == nil {
// If no base activation for the location exists
// (no value was previously, specifically declared for the location using DeclareValue),
// return the base activation that applies to all locations by default
baseActivation = e.defaultBaseActivation
}
return
}
6 changes: 3 additions & 3 deletions runtime/interpreter/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ type Config struct {
// UUIDHandler is used to handle the generation of UUIDs
UUIDHandler UUIDHandlerFunc
// CompositeTypeHandler is used to load composite types
CompositeTypeHandler CompositeTypeHandlerFunc
BaseActivation *VariableActivation
Debugger *Debugger
CompositeTypeHandler CompositeTypeHandlerFunc
BaseActivationHandler func(location common.Location) *VariableActivation
Debugger *Debugger
// OnStatement is triggered when a statement is about to be executed
OnStatement OnStatementFunc
// OnLoopIteration is triggered when a loop iteration is about to be executed
Expand Down
8 changes: 7 additions & 1 deletion runtime/interpreter/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,9 +296,15 @@ func NewInterpreterWithSharedState(
sharedState.allInterpreters[location] = interpreter
}

// Initialize activations

interpreter.activations = activations.NewActivations[*Variable](interpreter)

baseActivation := sharedState.Config.BaseActivation
var baseActivation *VariableActivation
baseActivationHandler := sharedState.Config.BaseActivationHandler
if baseActivationHandler != nil {
baseActivation = baseActivationHandler(location)
}
if baseActivation == nil {
baseActivation = BaseActivation
}
Expand Down
Loading
Loading