Skip to content

Commit

Permalink
Merge pull request #3591 from onflow/supun/port-251
Browse files Browse the repository at this point in the history
  • Loading branch information
turbolent authored Sep 26, 2024
2 parents addc4d5 + 5a4a928 commit 878f55e
Show file tree
Hide file tree
Showing 2 changed files with 287 additions and 70 deletions.
160 changes: 90 additions & 70 deletions runtime/interpreter/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -1267,16 +1267,7 @@ func (declarationInterpreter *Interpreter) declareNonEnumCompositeValue(
functions.Set(resourceDefaultDestroyEventName(compositeType), destroyEventConstructor)
}

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

// Wrap initializer

initializerFunctionWrapper :=
code.InitializerFunctionWrapper

if initializerFunctionWrapper != nil {
initializerFunction = initializerFunctionWrapper(initializerFunction)
}
applyDefaultFunctions := func(ty *sema.InterfaceType, code WrapperCode) {

// Apply default functions, if conforming type does not provide the function

Expand All @@ -1295,6 +1286,18 @@ func (declarationInterpreter *Interpreter) declareNonEnumCompositeValue(
functions.Set(name, function)
})
}
}

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

// Wrap initializer

initializerFunctionWrapper :=
code.InitializerFunctionWrapper

if initializerFunctionWrapper != nil {
initializerFunction = initializerFunctionWrapper(initializerFunction)
}

// Wrap functions

Expand All @@ -1303,7 +1306,11 @@ func (declarationInterpreter *Interpreter) declareNonEnumCompositeValue(
// the order does not matter.

for name, functionWrapper := range code.FunctionWrappers { //nolint:maprange
fn, _ := functions.Get(name)
fn, ok := functions.Get(name)
// If there is a wrapper, there MUST be a body.
if !ok {
panic(errors.NewUnreachableError())
}
functions.Set(name, functionWrapper(fn))
}

Expand All @@ -1313,9 +1320,21 @@ func (declarationInterpreter *Interpreter) declareNonEnumCompositeValue(
}

conformances := compositeType.EffectiveInterfaceConformances()
interfaceCodes := declarationInterpreter.SharedState.typeCodes.InterfaceCodes

// First apply the default functions, and then wrap with conditions.
// These needs to be done in separate phases.
// Otherwise, if the condition and the default implementation are coming from two different inherited interfaces,
// then the condition would wrap an empty implementation, because the default impl is not resolved by the time.

for i := len(conformances) - 1; i >= 0; i-- {
conformance := conformances[i].InterfaceType
applyDefaultFunctions(conformance, interfaceCodes[conformance.ID()])
}

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

declarationInterpreter.SharedState.typeCodes.CompositeCodes[compositeType.ID()] = CompositeTypeCode{
Expand Down Expand Up @@ -2451,6 +2470,13 @@ func (interpreter *Interpreter) functionConditionsWrapper(
}

return func(inner FunctionValue) FunctionValue {

// NOTE: The `inner` function cannot be nil.
// An executing function always have a body.
if inner == nil {
panic(errors.NewUnreachableError())
}

// Condition wrapper is a static function.
return NewStaticHostFunctionValue(
interpreter,
Expand All @@ -2476,72 +2502,66 @@ func (interpreter *Interpreter) functionConditionsWrapper(
interpreter.declareVariable(sema.BaseIdentifier, invocation.Base)
}

// NOTE: The `inner` function might be nil.
// This is the case if the conforming type did not declare a function.

var body func() StatementResult
if inner != nil {
// NOTE: It is important to wrap the invocation in a function,
// so the inner function isn't invoked here

body = func() StatementResult {

// Pre- and post-condition wrappers "re-declare" the same
// parameters as are used in the actual body of the function,
// see the use of bindParameterArguments at the start of this function wrapper.
//
// When these parameters are given resource-kinded arguments,
// this can trick the resource analysis into believing that these
// resources exist in multiple variables at once
// (one for each condition wrapper + the function itself).
//
// This is not the case, however, as execution of the pre- and post-conditions
// occurs strictly before and after execution of the body respectively.
//
// To prevent the analysis from reporting a false positive here,
// when we enter the body of the wrapped function,
// we invalidate any resources that were assigned to parameters by the precondition block,
// and then restore them after execution of the wrapped function,
// for use by the post-condition block.

type argumentVariable struct {
variable Variable
value ResourceKindedValue
// NOTE: It is important to wrap the invocation in a function,
// so the inner function isn't invoked here

body := func() StatementResult {

// Pre- and post-condition wrappers "re-declare" the same
// parameters as are used in the actual body of the function,
// see the use of bindParameterArguments at the start of this function wrapper.
//
// When these parameters are given resource-kinded arguments,
// this can trick the resource analysis into believing that these
// resources exist in multiple variables at once
// (one for each condition wrapper + the function itself).
//
// This is not the case, however, as execution of the pre- and post-conditions
// occurs strictly before and after execution of the body respectively.
//
// To prevent the analysis from reporting a false positive here,
// when we enter the body of the wrapped function,
// we invalidate any resources that were assigned to parameters by the precondition block,
// and then restore them after execution of the wrapped function,
// for use by the post-condition block.

type argumentVariable struct {
variable Variable
value ResourceKindedValue
}

var argumentVariables []argumentVariable
for _, argument := range invocation.Arguments {
resourceKindedValue := interpreter.resourceForValidation(argument)
if resourceKindedValue == nil {
continue
}

var argumentVariables []argumentVariable
for _, argument := range invocation.Arguments {
resourceKindedValue := interpreter.resourceForValidation(argument)
if resourceKindedValue == nil {
continue
}

argumentVariables = append(
argumentVariables,
argumentVariable{
variable: interpreter.SharedState.resourceVariables[resourceKindedValue],
value: resourceKindedValue,
},
)
argumentVariables = append(
argumentVariables,
argumentVariable{
variable: interpreter.SharedState.resourceVariables[resourceKindedValue],
value: resourceKindedValue,
},
)

interpreter.invalidateResource(resourceKindedValue)
}
interpreter.invalidateResource(resourceKindedValue)
}

// NOTE: It is important to actually return the value returned
// from the inner function, otherwise it is lost
// NOTE: It is important to actually return the value returned
// from the inner function, otherwise it is lost

returnValue := inner.invoke(invocation)
returnValue := inner.invoke(invocation)

// Restore the resources which were temporarily invalidated
// before execution of the inner function
// Restore the resources which were temporarily invalidated
// before execution of the inner function

for _, argumentVariable := range argumentVariables {
value := argumentVariable.value
interpreter.invalidateResource(value)
interpreter.SharedState.resourceVariables[value] = argumentVariable.variable
}
return ReturnResult{Value: returnValue}
for _, argumentVariable := range argumentVariables {
value := argumentVariable.value
interpreter.invalidateResource(value)
interpreter.SharedState.resourceVariables[value] = argumentVariable.variable
}
return ReturnResult{Value: returnValue}
}

declarationLocationRange := LocationRange{
Expand Down
Loading

0 comments on commit 878f55e

Please sign in to comment.