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

Interpreting for default events #2817

Merged
Merged
Show file tree
Hide file tree
Changes from 12 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
2 changes: 1 addition & 1 deletion runtime/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -981,7 +981,7 @@ func (e *interpreterEnvironment) newCompositeValueFunctionsHandler() interpreter
inter *interpreter.Interpreter,
locationRange interpreter.LocationRange,
compositeValue *interpreter.CompositeValue,
) map[string]interpreter.FunctionValue {
) *interpreter.FunctionOrderedMap {

handler := e.compositeValueFunctionsHandlers[compositeValue.TypeID()]
if handler == nil {
Expand Down
155 changes: 125 additions & 30 deletions runtime/interpreter/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"github.com/onflow/cadence/runtime/activations"
"github.com/onflow/cadence/runtime/ast"
"github.com/onflow/cadence/runtime/common"
"github.com/onflow/cadence/runtime/common/orderedmap"
"github.com/onflow/cadence/runtime/errors"
"github.com/onflow/cadence/runtime/sema"
)
Expand Down Expand Up @@ -165,7 +166,7 @@
inter *Interpreter,
locationRange LocationRange,
compositeValue *CompositeValue,
) map[string]FunctionValue
) *FunctionOrderedMap

// CompositeTypeCode contains the "prepared" / "callable" "code"
// for the functions and the destructor of a composite
Expand All @@ -174,7 +175,7 @@
// As there is no support for inheritance of concrete types,
// these are the "leaf" nodes in the call chain, and are functions.
type CompositeTypeCode struct {
CompositeFunctions map[string]FunctionValue
CompositeFunctions *FunctionOrderedMap
}

type FunctionWrapper = func(inner FunctionValue) FunctionValue
Expand All @@ -185,9 +186,10 @@
// These are "branch" nodes in the call chain, and are function wrappers,
// i.e. they wrap the functions / function wrappers that inherit them.
type WrapperCode struct {
InitializerFunctionWrapper FunctionWrapper
FunctionWrappers map[string]FunctionWrapper
Functions map[string]FunctionValue
InitializerFunctionWrapper FunctionWrapper
FunctionWrappers map[string]FunctionWrapper
Functions *FunctionOrderedMap
DefaultDestroyEventConstructor FunctionValue
}

// TypeCodes is the value which stores the "prepared" / "callable" "code"
Expand Down Expand Up @@ -978,6 +980,47 @@
return interpreter.declareCompositeValue(declaration, lexicalScope)
}

// evaluates all the implicit default arguments to the default destroy event
dsainati1 marked this conversation as resolved.
Show resolved Hide resolved
//
// the handling of default arguments makes a number of assumptions to simplify the implementation;
// namely that a) all default arguments are lazily evaluated at the site of the invocation,
// b) that either all the parameters or none of the parameters of a function have default arguments,
// and c) functions cannot currently be explicitly invoked if they have default arguments
//
// if we plan to generalize this further, we will need to relax those assumptions
func (interpreter *Interpreter) evaluateDefaultDestroyEvent(
containerComposite *CompositeValue,
compositeDecl *ast.CompositeDeclaration,
dsainati1 marked this conversation as resolved.
Show resolved Hide resolved
compositeType *sema.CompositeType,
dsainati1 marked this conversation as resolved.
Show resolved Hide resolved
locationRange LocationRange,
) (arguments []Value) {
parameters := compositeDecl.DeclarationMembers().Initializers()[0].FunctionDeclaration.ParameterList.Parameters

interpreter.activations.PushNewWithParent(interpreter.activations.CurrentOrNew())
defer interpreter.activations.Pop()

var self MemberAccessibleValue = containerComposite
if containerComposite.Kind == common.CompositeKindAttachment {
var base *EphemeralReferenceValue
base, self = attachmentBaseAndSelfValues(interpreter, containerComposite)
interpreter.declareVariable(sema.BaseIdentifier, base)
}
interpreter.declareVariable(sema.SelfIdentifier, self)

for _, parameter := range parameters {
// lazily evaluate the default argument expressions
dsainati1 marked this conversation as resolved.
Show resolved Hide resolved
// note that we must evaluate them in the interpreter context that existed when the event
// was defined (i.e. the contract defining the resource) rather than the interpreter context
// that exists when the resource is destroyed. We accomplish this by using the original interpreter of the
// composite declaration, rather than the interpreter of the destroy expression
dsainati1 marked this conversation as resolved.
Show resolved Hide resolved

defaultArg := interpreter.evalExpression(parameter.DefaultArgument)
arguments = append(arguments, defaultArg)
}

return
}

// declareCompositeValue creates and declares the value for
// the composite declaration.
//
Expand Down Expand Up @@ -1027,6 +1070,8 @@

nestedVariables := map[string]*Variable{}

var destroyEventConstructor FunctionValue

(func() {
interpreter.activations.PushNewWithCurrent()
defer interpreter.activations.Pop()
Expand Down Expand Up @@ -1073,6 +1118,11 @@

memberIdentifier := nestedCompositeDeclaration.Identifier.Identifier
nestedVariables[memberIdentifier] = nestedVariable

// statically we know there is at most one of these
if nestedCompositeDeclaration.IsResourceDestructionDefaultEvent() {
destroyEventConstructor = nestedVariable.GetValue().(FunctionValue)
}
}

for _, nestedAttachmentDeclaration := range members.Attachments() {
Expand Down Expand Up @@ -1107,6 +1157,23 @@
locationRange := invocation.LocationRange
self := *invocation.Self

if len(compositeType.ConstructorParameters) < 1 {
return nil
}

// event interfaces do not exist
compositeDecl := declaration.(*ast.CompositeDeclaration)
if compositeDecl.IsResourceDestructionDefaultEvent() {
// we implicitly pass the containing composite value as an argument to this invocation
containerComposite := invocation.Arguments[0].(*CompositeValue)
invocation.Arguments = interpreter.evaluateDefaultDestroyEvent(
containerComposite,
compositeDecl,
compositeType,
locationRange,
)
}

for i, argument := range invocation.Arguments {
parameter := compositeType.ConstructorParameters[i]
self.SetMember(
Expand All @@ -1128,7 +1195,11 @@

functions := interpreter.compositeFunctions(declaration, lexicalScope)

wrapFunctions := func(code WrapperCode) {
if destroyEventConstructor != nil {
functions.Set(resourceDefaultDestroyEventName(compositeType), destroyEventConstructor)
}

wrapFunctions := func(ty *sema.InterfaceType, code WrapperCode) {

// Wrap initializer

Expand All @@ -1145,14 +1216,16 @@
// we only apply the function wrapper to each function,
// the order does not matter.

for name, function := range code.Functions { //nolint:maprange
if functions[name] != nil {
continue
}
if functions == nil {
functions = map[string]FunctionValue{}
}
functions[name] = function
if code.Functions != nil {
code.Functions.Foreach(func(name string, function FunctionValue) {
if functions == nil {
functions = orderedmap.New[FunctionOrderedMap](code.Functions.Len())
}

Check warning on line 1223 in runtime/interpreter/interpreter.go

View check run for this annotation

Codecov / codecov/patch

runtime/interpreter/interpreter.go#L1222-L1223

Added lines #L1222 - L1223 were not covered by tests
if functions.Contains(name) {
return
}
functions.Set(name, function)
})
}

// Wrap functions
Expand All @@ -1162,14 +1235,19 @@
// the order does not matter.

for name, functionWrapper := range code.FunctionWrappers { //nolint:maprange
functions[name] = functionWrapper(functions[name])
fn, _ := functions.Get(name)
functions.Set(name, functionWrapper(fn))
}

if code.DefaultDestroyEventConstructor != nil {
functions.Set(resourceDefaultDestroyEventName(ty), code.DefaultDestroyEventConstructor)
}
}

conformances := compositeType.EffectiveInterfaceConformances()
for i := len(conformances) - 1; i >= 0; i-- {
conformance := conformances[i].InterfaceType
wrapFunctions(interpreter.SharedState.typeCodes.InterfaceCodes[conformance.ID()])
wrapFunctions(conformance, interpreter.SharedState.typeCodes.InterfaceCodes[conformance.ID()])
}

interpreter.SharedState.typeCodes.CompositeCodes[compositeType.ID()] = CompositeTypeCode{
Expand Down Expand Up @@ -1520,7 +1598,7 @@
func (interpreter *Interpreter) defaultFunctions(
members *ast.Members,
lexicalScope *VariableActivation,
) map[string]FunctionValue {
) *FunctionOrderedMap {

functionDeclarations := members.Functions()
functionCount := len(functionDeclarations)
Expand All @@ -1529,17 +1607,20 @@
return nil
}

functions := make(map[string]FunctionValue, functionCount)
functions := orderedmap.New[FunctionOrderedMap](functionCount)

for _, functionDeclaration := range functionDeclarations {
name := functionDeclaration.Identifier.Identifier
if !functionDeclaration.FunctionBlock.HasStatements() {
continue
}

functions[name] = interpreter.compositeFunction(
functionDeclaration,
lexicalScope,
functions.Set(
name,
interpreter.compositeFunction(
functionDeclaration,
lexicalScope,
),
)
}

Expand All @@ -1549,17 +1630,19 @@
func (interpreter *Interpreter) compositeFunctions(
compositeDeclaration ast.CompositeLikeDeclaration,
lexicalScope *VariableActivation,
) map[string]FunctionValue {
) *FunctionOrderedMap {

functions := map[string]FunctionValue{}
functions := orderedmap.New[FunctionOrderedMap](len(compositeDeclaration.DeclarationMembers().Functions()))

for _, functionDeclaration := range compositeDeclaration.DeclarationMembers().Functions() {
name := functionDeclaration.Identifier.Identifier
functions[name] =
functions.Set(
name,
interpreter.compositeFunction(
functionDeclaration,
lexicalScope,
)
),
)
}

return functions
Expand Down Expand Up @@ -2180,13 +2263,25 @@
interfaceType.InitializerParameters,
lexicalScope,
)

var defaultDestroyEventConstructor FunctionValue
if defautlDestroyEvent := interpreter.Program.Elaboration.DefaultDestroyDeclaration(declaration); defautlDestroyEvent != nil {
var nestedVariable *Variable
lexicalScope, nestedVariable = interpreter.declareCompositeValue(
defautlDestroyEvent,
lexicalScope,
)
defaultDestroyEventConstructor = nestedVariable.GetValue().(FunctionValue)
}

functionWrappers := interpreter.functionWrappers(declaration.Members, lexicalScope)
defaultFunctions := interpreter.defaultFunctions(declaration.Members, lexicalScope)

interpreter.SharedState.typeCodes.InterfaceCodes[typeID] = WrapperCode{
InitializerFunctionWrapper: initializerFunctionWrapper,
FunctionWrappers: functionWrappers,
Functions: defaultFunctions,
InitializerFunctionWrapper: initializerFunctionWrapper,
FunctionWrappers: functionWrappers,
Functions: defaultFunctions,
DefaultDestroyEventConstructor: defaultDestroyEventConstructor,
}
}

Expand Down Expand Up @@ -4529,9 +4624,9 @@
func (interpreter *Interpreter) GetCompositeValueFunctions(
v *CompositeValue,
locationRange LocationRange,
) map[string]FunctionValue {
) *FunctionOrderedMap {

var functions map[string]FunctionValue
var functions *FunctionOrderedMap

typeID := v.TypeID()

Expand Down
31 changes: 19 additions & 12 deletions runtime/interpreter/interpreter_statement.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"github.com/onflow/cadence/runtime/ast"
"github.com/onflow/cadence/runtime/common"
"github.com/onflow/cadence/runtime/errors"
"github.com/onflow/cadence/runtime/sema"
)

func (interpreter *Interpreter) evalStatement(statement ast.Statement) StatementResult {
Expand Down Expand Up @@ -411,18 +412,7 @@
return nil, false
}

func (interpreter *Interpreter) VisitEmitStatement(statement *ast.EmitStatement) StatementResult {
event, ok := interpreter.evalExpression(statement.InvocationExpression).(*CompositeValue)
if !ok {
panic(errors.NewUnreachableError())
}

eventType := interpreter.Program.Elaboration.EmitStatementEventType(statement)

locationRange := LocationRange{
Location: interpreter.Location,
HasPosition: statement,
}
func (interpreter *Interpreter) emitEvent(event *CompositeValue, eventType *sema.CompositeType, locationRange LocationRange) {

config := interpreter.SharedState.Config

Expand All @@ -437,6 +427,23 @@
if err != nil {
panic(err)
}
}

func (interpreter *Interpreter) VisitEmitStatement(statement *ast.EmitStatement) StatementResult {

event, ok := interpreter.evalExpression(statement.InvocationExpression).(*CompositeValue)
if !ok {
panic(errors.NewUnreachableError())

Check warning on line 436 in runtime/interpreter/interpreter_statement.go

View check run for this annotation

Codecov / codecov/patch

runtime/interpreter/interpreter_statement.go#L436

Added line #L436 was not covered by tests
}

eventType := interpreter.Program.Elaboration.EmitStatementEventType(statement)

locationRange := LocationRange{
Location: interpreter.Location,
HasPosition: statement,
}

interpreter.emitEvent(event, eventType, locationRange)

return nil
}
Expand Down
Loading
Loading