Skip to content

Commit

Permalink
Merge pull request onflow#2956 from onflow/supun/sync-with-0.42
Browse files Browse the repository at this point in the history
Port bug fixes from v0.42.5-patch.1
  • Loading branch information
SupunS authored Nov 30, 2023
2 parents 25e5405 + 7beb8f2 commit b48fd6b
Show file tree
Hide file tree
Showing 16 changed files with 1,578 additions and 156 deletions.
11 changes: 11 additions & 0 deletions runtime/ast/conditionkind.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,14 @@ func (k ConditionKind) Name() string {
func (k ConditionKind) MarshalJSON() ([]byte, error) {
return json.Marshal(k.String())
}

func (k ConditionKind) Keyword() string {
switch k {
case ConditionKindPre:
return "pre"
case ConditionKindPost:
return "post"
}

panic(errors.NewUnreachableError())
}
2 changes: 0 additions & 2 deletions runtime/deployment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,6 @@ func TestRuntimeTransactionWithContractDeployment(t *testing.T) {
var runtimeErr Error
require.ErrorAs(t, err, &runtimeErr)

println(runtimeErr.Error())

assert.EqualError(t, runtimeErr, expectedErrorMessage)

assert.Len(t, runtimeErr.Codes, 2)
Expand Down
25 changes: 13 additions & 12 deletions runtime/interpreter/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -666,10 +666,7 @@ func (interpreter *Interpreter) VisitProgram(program *ast.Program) {
var variable *Variable

variable = NewVariableWithGetter(interpreter, func() Value {
var result Value
interpreter.visitVariableDeclaration(declaration, func(_ string, value Value) {
result = value
})
result := interpreter.visitVariableDeclaration(declaration, false)

// Global variables are lazily loaded. Therefore, start resource tracking also
// lazily when the resource is used for the first time.
Expand Down Expand Up @@ -902,13 +899,10 @@ func (interpreter *Interpreter) declareVariable(identifier string, value Value)

func (interpreter *Interpreter) visitAssignment(
transferOperation ast.TransferOperation,
targetExpression ast.Expression, targetType sema.Type,
targetGetterSetter getterSetter, targetType sema.Type,
valueExpression ast.Expression, valueType sema.Type,
position ast.HasPosition,
) {
// First evaluate the target, which results in a getter/setter function pair
getterSetter := interpreter.assignmentGetterSetter(targetExpression)

locationRange := LocationRange{
Location: interpreter.Location,
HasPosition: position,
Expand All @@ -925,7 +919,7 @@ func (interpreter *Interpreter) visitAssignment(

const allowMissing = true

target := getterSetter.get(allowMissing)
target := targetGetterSetter.get(allowMissing)

if _, ok := target.(NilValue); !ok && target != nil {
panic(ForceAssignmentToNonNilResourceError{
Expand All @@ -940,7 +934,7 @@ func (interpreter *Interpreter) visitAssignment(

transferredValue := interpreter.transferAndConvert(value, valueType, targetType, locationRange)

getterSetter.set(transferredValue)
targetGetterSetter.set(transferredValue)
}

// NOTE: only called for top-level composite declarations
Expand Down Expand Up @@ -5567,17 +5561,24 @@ func (interpreter *Interpreter) withMutationPrevention(storageID atree.StorageID
}
}

func (interpreter *Interpreter) withResourceDestruction(
func (interpreter *Interpreter) enforceNotResourceDestruction(
storageID atree.StorageID,
locationRange LocationRange,
f func(),
) {
_, exists := interpreter.SharedState.destroyedResources[storageID]
if exists {
panic(DestroyedResourceError{
LocationRange: locationRange,
})
}
}

func (interpreter *Interpreter) withResourceDestruction(
storageID atree.StorageID,
locationRange LocationRange,
f func(),
) {
interpreter.enforceNotResourceDestruction(storageID, locationRange)

interpreter.SharedState.destroyedResources[storageID] = struct{}{}

Expand Down
3 changes: 3 additions & 0 deletions runtime/interpreter/interpreter_expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -1120,6 +1120,9 @@ func (interpreter *Interpreter) VisitCastingExpression(expression *ast.CastingEx
// The failable cast may upcast to an optional type, e.g. `1 as? Int?`, so box
value = interpreter.BoxOptional(locationRange, value, expectedType)

// Failable casting is a resource invalidation
interpreter.invalidateResource(value)

return NewSomeValueNonCopying(interpreter, value)

case ast.OperationForceCast:
Expand Down
117 changes: 39 additions & 78 deletions runtime/interpreter/interpreter_statement.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"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 @@ -141,66 +142,22 @@ func (interpreter *Interpreter) visitIfStatementWithVariableDeclaration(
thenBlock, elseBlock *ast.Block,
) StatementResult {

// NOTE: It is *REQUIRED* that the getter for the value is used
// instead of just evaluating value expression,
// as the value may be an access expression (member access, index access),
// which implicitly removes a resource.
//
// Performing the removal from the container is essential
// (and just evaluating the expression does not perform the removal),
// because if there is a second value,
// the assignment to the value will cause an overwrite of the value.
// If the resource was not moved ou of the container,
// its contents get deleted.

getterSetter := interpreter.assignmentGetterSetter(declaration.Value)

const allowMissing = false
value := getterSetter.get(allowMissing)
if value == nil {
panic(errors.NewUnreachableError())
}

variableDeclarationTypes := interpreter.Program.Elaboration.VariableDeclarationTypes(declaration)
valueType := variableDeclarationTypes.ValueType

if declaration.SecondValue != nil {
secondValueType := variableDeclarationTypes.SecondValueType

interpreter.visitAssignment(
declaration.Transfer.Operation,
declaration.Value,
valueType,
declaration.SecondValue,
secondValueType,
declaration,
)
}
value := interpreter.visitVariableDeclaration(declaration, true)

if someValue, ok := value.(*SomeValue); ok {

targetType := variableDeclarationTypes.TargetType
locationRange := LocationRange{
Location: interpreter.Location,
HasPosition: declaration.Value,
}

innerValue := someValue.InnerValue(interpreter, locationRange)
transferredUnwrappedValue := interpreter.transferAndConvert(
innerValue,
valueType,
targetType,
locationRange,
)

interpreter.activations.PushNewWithCurrent()
defer interpreter.activations.Pop()

// Assignment can also be a resource move.
interpreter.invalidateResource(innerValue)

interpreter.declareVariable(
declaration.Identifier.Identifier,
transferredUnwrappedValue,
innerValue,
)

return interpreter.visitBlock(thenBlock)
Expand Down Expand Up @@ -461,27 +418,23 @@ func (interpreter *Interpreter) VisitPragmaDeclaration(_ *ast.PragmaDeclaration)
// then declares the variable with the name bound to the value
func (interpreter *Interpreter) VisitVariableDeclaration(declaration *ast.VariableDeclaration) StatementResult {

interpreter.visitVariableDeclaration(
declaration,
func(identifier string, value Value) {
value := interpreter.visitVariableDeclaration(declaration, false)

// NOTE: lexical scope, always declare a new variable.
// Do not find an existing variable and assign the value!
// NOTE: lexical scope, always declare a new variable.
// Do not find an existing variable and assign the value!

_ = interpreter.declareVariable(
identifier,
value,
)
},
_ = interpreter.declareVariable(
declaration.Identifier.Identifier,
value,
)

return nil
}

func (interpreter *Interpreter) visitVariableDeclaration(
declaration *ast.VariableDeclaration,
valueCallback func(identifier string, value Value),
) {
isOptionalBinding bool,
) Value {

variableDeclarationTypes := interpreter.Program.Elaboration.VariableDeclarationTypes(declaration)
targetType := variableDeclarationTypes.TargetType
Expand All @@ -497,7 +450,7 @@ func (interpreter *Interpreter) visitVariableDeclaration(
// (and just evaluating the expression does not perform the removal),
// because if there is a second value,
// the assignment to the value will cause an overwrite of the value.
// If the resource was not moved ou of the container,
// If the resource was not moved out of the container,
// its contents get deleted.

getterSetter := interpreter.assignmentGetterSetter(declaration.Value)
Expand All @@ -508,33 +461,39 @@ func (interpreter *Interpreter) visitVariableDeclaration(
panic(errors.NewUnreachableError())
}

// Assignment is a potential resource move.
interpreter.invalidateResource(result)

locationRange := LocationRange{
Location: interpreter.Location,
HasPosition: declaration.Value,
}

transferredValue := interpreter.transferAndConvert(result, valueType, targetType, locationRange)
if isOptionalBinding {
targetType = &sema.OptionalType{
Type: targetType,
}
}

valueCallback(
declaration.Identifier.Identifier,
transferredValue,
transferredValue := interpreter.transferAndConvert(
result,
valueType,
targetType,
locationRange,
)

if declaration.SecondValue == nil {
return
// Assignment is a potential resource move.
interpreter.invalidateResource(result)

if declaration.SecondValue != nil {
interpreter.visitAssignment(
declaration.Transfer.Operation,
getterSetter,
valueType,
declaration.SecondValue,
secondValueType,
declaration,
)
}

interpreter.visitAssignment(
declaration.Transfer.Operation,
declaration.Value,
valueType,
declaration.SecondValue,
secondValueType,
declaration,
)
return transferredValue
}

func (interpreter *Interpreter) VisitAssignmentStatement(assignment *ast.AssignmentStatement) StatementResult {
Expand All @@ -545,9 +504,11 @@ func (interpreter *Interpreter) VisitAssignmentStatement(assignment *ast.Assignm
target := assignment.Target
value := assignment.Value

getterSetter := interpreter.assignmentGetterSetter(target)

interpreter.visitAssignment(
assignment.Transfer.Operation,
target, targetType,
getterSetter, targetType,
value, valueType,
assignment,
)
Expand Down
7 changes: 7 additions & 0 deletions runtime/interpreter/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -16811,6 +16811,8 @@ func (v *CompositeValue) SetMember(
v.checkInvalidatedResourceUse(locationRange)
}

interpreter.enforceNotResourceDestruction(v.StorageID(), locationRange)

if config.TracingEnabled {
startTime := time.Now()

Expand Down Expand Up @@ -19429,6 +19431,11 @@ func (v *SomeValue) NeedsStoreTo(address atree.Address) bool {
}

func (v *SomeValue) IsResourceKinded(interpreter *Interpreter) bool {
// If the inner value is `nil`, then this is an invalidated resource.
if v.value == nil {
return true
}

return v.value.IsResourceKinded(interpreter)
}

Expand Down
Loading

0 comments on commit b48fd6b

Please sign in to comment.