Skip to content

Commit

Permalink
Update reference tracking to match stable-cadence
Browse files Browse the repository at this point in the history
  • Loading branch information
SupunS committed Dec 6, 2023
1 parent 6f1f736 commit a5d2f8e
Show file tree
Hide file tree
Showing 14 changed files with 78 additions and 139 deletions.
2 changes: 1 addition & 1 deletion runtime/attachments_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ func TestRuntimeAccountAttachmentExportFailure(t *testing.T) {
},
)
require.Error(t, err)
require.ErrorAs(t, err, &interpreter.DestroyedResourceError{})
require.ErrorAs(t, err, &interpreter.InvalidatedResourceReferenceError{})
}

func TestRuntimeAccountAttachmentExport(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion runtime/convertValues_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5286,7 +5286,7 @@ func TestRuntimeDestroyedResourceReferenceExport(t *testing.T) {
},
)
require.Error(t, err)
require.ErrorAs(t, err, &interpreter.DestroyedResourceError{})
require.ErrorAs(t, err, &interpreter.InvalidatedResourceReferenceError{})
}

func TestRuntimeDeploymentResultValueImportExport(t *testing.T) {
Expand Down
50 changes: 2 additions & 48 deletions runtime/interpreter/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -4979,41 +4979,6 @@ func (interpreter *Interpreter) checkContainerMutation(
}
}

func (interpreter *Interpreter) checkReferencedResourceNotDestroyed(value Value, locationRange LocationRange) {
resourceKindedValue, ok := value.(ResourceKindedValue)
if !ok || !resourceKindedValue.IsDestroyed() {
return
}

panic(DestroyedResourceError{
LocationRange: locationRange,
})
}

func (interpreter *Interpreter) checkReferencedResourceNotMovedOrDestroyed(
referencedValue Value,
locationRange LocationRange,
) {

// First check if the referencedValue is a resource.
// This is to handle optionals, since optionals does not
// belong to `ReferenceTrackedResourceKindedValue`

resourceKindedValue, ok := referencedValue.(ResourceKindedValue)
if ok && resourceKindedValue.IsDestroyed() {
panic(DestroyedResourceError{
LocationRange: locationRange,
})
}

referenceTrackedResourceKindedValue, ok := referencedValue.(ReferenceTrackedResourceKindedValue)
if ok && referenceTrackedResourceKindedValue.IsStaleResource(interpreter) {
panic(InvalidatedResourceReferenceError{
LocationRange: locationRange,
})
}
}

func (interpreter *Interpreter) RemoveReferencedSlab(storable atree.Storable) {
storageIDStorable, ok := storable.(atree.StorageIDStorable)
if !ok {
Expand Down Expand Up @@ -5192,6 +5157,7 @@ func (interpreter *Interpreter) trackReferencedResourceKindedValue(
values[value] = struct{}{}
}

// TODO: Remove the `destroyed` flag
func (interpreter *Interpreter) invalidateReferencedResources(value Value, destroyed bool) {
// skip non-resource typed values
if !value.IsResourceKinded(interpreter) {
Expand Down Expand Up @@ -5234,19 +5200,7 @@ func (interpreter *Interpreter) invalidateReferencedResources(value Value, destr
}

for value := range values { //nolint:maprange
switch value := value.Value.(type) {
case *CompositeValue:
value.dictionary = nil
value.isDestroyed = destroyed
case *DictionaryValue:
value.dictionary = nil
value.isDestroyed = destroyed
case *ArrayValue:
value.array = nil
value.isDestroyed = destroyed
default:
panic(errors.NewUnreachableError())
}
value.Value = nil
}

// The old resource instances are already cleared/invalidated above.
Expand Down
44 changes: 35 additions & 9 deletions runtime/interpreter/interpreter_expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -358,18 +358,44 @@ func (interpreter *Interpreter) VisitIdentifierExpression(expression *ast.Identi

func (interpreter *Interpreter) evalExpression(expression ast.Expression) Value {
result := ast.AcceptExpression[Value](expression, interpreter)
interpreter.checkInvalidatedResourceOrResourceReference(result, expression)
return result
}

resourceKindedValue, ok := result.(ResourceKindedValue)
if ok && resourceKindedValue.isInvalidatedResource(interpreter) {
panic(InvalidatedResourceError{
LocationRange: LocationRange{
Location: interpreter.Location,
HasPosition: expression,
},
})
func (interpreter *Interpreter) checkInvalidatedResourceOrResourceReference(value Value, hasPosition ast.HasPosition) {
// Unwrap SomeValue, to access references wrapped inside optionals.
someValue, isSomeValue := value.(*SomeValue)
for isSomeValue && someValue.value != nil {
value = someValue.value
someValue, isSomeValue = value.(*SomeValue)
}

return result
switch value := value.(type) {
case ResourceKindedValue:
if value.isInvalidatedResource(interpreter) {
panic(InvalidatedResourceError{
LocationRange: LocationRange{
Location: interpreter.Location,
HasPosition: hasPosition,
},
})
}
case *EphemeralReferenceValue:
if value.Value == nil {
panic(InvalidatedResourceReferenceError{
LocationRange: LocationRange{
Location: interpreter.Location,
HasPosition: hasPosition,
},
})
} else {
// If the value is there, check is the referenced value is an invalidated one.
// This step is not really needed, since reference tracking is supposed to clear the
// `value.Value` if the referenced-value was moved/deleted.
// However, have this as a second layer of defensive.
interpreter.checkInvalidatedResourceOrResourceReference(value.Value, hasPosition)
}
}
}

func (interpreter *Interpreter) VisitBinaryExpression(expression *ast.BinaryExpression) Value {
Expand Down
13 changes: 2 additions & 11 deletions runtime/interpreter/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -19845,11 +19845,7 @@ func (v *StorageReferenceValue) mustReferencedValue(
})
}

self := *referencedValue

interpreter.checkReferencedResourceNotDestroyed(self, locationRange)

return self
return *referencedValue
}

func (v *StorageReferenceValue) GetMember(
Expand Down Expand Up @@ -20245,10 +20241,7 @@ func (v *EphemeralReferenceValue) MustReferencedValue(
})
}

self := *referencedValue

interpreter.checkReferencedResourceNotMovedOrDestroyed(self, locationRange)
return self
return *referencedValue
}

func (v *EphemeralReferenceValue) GetMember(
Expand Down Expand Up @@ -20401,8 +20394,6 @@ func (v *EphemeralReferenceValue) ConformsToStaticType(
return false
}

interpreter.checkReferencedResourceNotMovedOrDestroyed(*referencedValue, locationRange)

self := *referencedValue

staticType := self.StaticType(interpreter)
Expand Down
2 changes: 1 addition & 1 deletion runtime/interpreter/value_function.go
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ func (f BoundFunctionValue) invoke(invocation Invocation) Value {
invocation.BoundAuthorization = f.BoundAuthorization

// Check if the 'self' is not invalidated.
_ = f.selfRef.MustReferencedValue(invocation.Interpreter, invocation.LocationRange)
invocation.Interpreter.checkInvalidatedResourceOrResourceReference(f.selfRef, invocation.LocationRange)

return f.Function.invoke(invocation)
}
Expand Down
1 change: 1 addition & 0 deletions runtime/interpreter/value_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1118,6 +1118,7 @@ func TestStringer(t *testing.T) {
common.ZeroAddress,
)
arrayRef := NewUnmeteredEphemeralReferenceValue(
inter,
UnauthorizedAccess,
array,
&sema.VariableSizedType{
Expand Down
2 changes: 1 addition & 1 deletion runtime/runtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8270,7 +8270,7 @@ func TestRuntimeReturnDestroyedOptional(t *testing.T) {
)

RequireError(t, err)
require.ErrorAs(t, err, &interpreter.DestroyedResourceError{})
require.ErrorAs(t, err, &interpreter.InvalidatedResourceReferenceError{})
}

func TestRuntimeComputationMeteringError(t *testing.T) {
Expand Down
15 changes: 5 additions & 10 deletions runtime/storage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5611,19 +5611,14 @@ func TestRuntimeStorageReferenceBoundFunction(t *testing.T) {
import Test from 0x42
transaction {
prepare(signer: AuthAccount) {
signer.save(<-Test.createR(), to: /storage/r)
signer.link<&Test.R>(
/public/r,
target: /storage/r
)
prepare(signer: auth(Storage) &Account) {
signer.storage.save(<-Test.createR(), to: /storage/r)
let ref = signer.getCapability<&Test.R>(/public/r).borrow()!
let ref = signer.storage.borrow<&Test.R>(from: /storage/r)!
var func = ref.foo
let r <- signer.load<@Test.R>(from: /storage/r)!
let r <- signer.storage.load<@Test.R>(from: /storage/r)!
// Should be OK
func()
Expand All @@ -5647,5 +5642,5 @@ func TestRuntimeStorageReferenceBoundFunction(t *testing.T) {
)

RequireError(t, err)
require.ErrorAs(t, err, &interpreter.DestroyedResourceError{})
require.ErrorAs(t, err, &interpreter.InvalidatedResourceReferenceError{})
}
4 changes: 2 additions & 2 deletions runtime/tests/interpreter/attachments_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1618,7 +1618,7 @@ func TestInterpretAttachmentResourceReferenceInvalidation(t *testing.T) {
})

_, err := inter.Invoke("test")
require.ErrorAs(t, err, &interpreter.DestroyedResourceError{})
require.ErrorAs(t, err, &interpreter.InvalidatedResourceReferenceError{})
})

t.Run("nested", func(t *testing.T) {
Expand Down Expand Up @@ -1747,7 +1747,7 @@ func TestInterpretAttachmentResourceReferenceInvalidation(t *testing.T) {
})

_, err := inter.Invoke("test")
require.ErrorAs(t, err, &interpreter.DestroyedResourceError{})
require.ErrorAs(t, err, &interpreter.InvalidatedResourceReferenceError{})
})

t.Run("self reference", func(t *testing.T) {
Expand Down
4 changes: 2 additions & 2 deletions runtime/tests/interpreter/container_mutation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1004,7 +1004,7 @@ func TestInterpretContainerMutationWhileIterating(t *testing.T) {
interpreter.NewArrayValue(
inter,
interpreter.EmptyLocationRange,
interpreter.VariableSizedStaticType{
&interpreter.VariableSizedStaticType{
Type: interpreter.PrimitiveStaticTypeString,
},
common.ZeroAddress,
Expand Down Expand Up @@ -1144,7 +1144,7 @@ func TestInterpretContainerMutationWhileIterating(t *testing.T) {
fun test(): @{String: Foo} {
let dictionary: @{String: Foo} <- {"a": <- create Foo(), "b": <- create Foo(), "c": <- create Foo()}
var dictionaryRef = &dictionary as &{String: Foo}
var dictionaryRef = &dictionary as auth(Mutate) &{String: Foo}
var i = 0
dictionary.forEachKey(fun (key: String): Bool {
Expand Down
1 change: 1 addition & 0 deletions runtime/tests/interpreter/interpreter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7305,6 +7305,7 @@ func TestInterpretReferenceEventParameter(t *testing.T) {
)

ref := interpreter.NewUnmeteredEphemeralReferenceValue(
inter,
interpreter.UnauthorizedAccess,
arrayValue,
inter.MustConvertStaticToSemaType(arrayStaticType),
Expand Down
13 changes: 10 additions & 3 deletions runtime/tests/interpreter/reference_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,7 @@ func TestInterpretResourceReferenceInvalidationOnMove(t *testing.T) {
)

arrayRef := interpreter.NewUnmeteredEphemeralReferenceValue(
inter,
interpreter.NewEntitlementSetAuthorization(
nil,
func() []common.TypeID { return []common.TypeID{"Mutate"} },
Expand Down Expand Up @@ -754,6 +755,7 @@ func TestInterpretResourceReferenceInvalidationOnMove(t *testing.T) {
)

arrayRef1 := interpreter.NewUnmeteredEphemeralReferenceValue(
inter,
interpreter.NewEntitlementSetAuthorization(
nil,
func() []common.TypeID { return []common.TypeID{"Mutate"} },
Expand All @@ -779,6 +781,7 @@ func TestInterpretResourceReferenceInvalidationOnMove(t *testing.T) {
)

arrayRef2 := interpreter.NewUnmeteredEphemeralReferenceValue(
inter,
interpreter.NewEntitlementSetAuthorization(
nil,
func() []common.TypeID { return []common.TypeID{"Mutate"} },
Expand Down Expand Up @@ -851,6 +854,7 @@ func TestInterpretResourceReferenceInvalidationOnMove(t *testing.T) {
)

arrayRef := interpreter.NewUnmeteredEphemeralReferenceValue(
inter,
interpreter.NewEntitlementSetAuthorization(
nil,
func() []common.TypeID { return []common.TypeID{"Mutate"} },
Expand Down Expand Up @@ -976,6 +980,7 @@ func TestInterpretResourceReferenceInvalidationOnMove(t *testing.T) {
)

arrayRef := interpreter.NewUnmeteredEphemeralReferenceValue(
inter,
interpreter.NewEntitlementSetAuthorization(
nil,
func() []common.TypeID { return []common.TypeID{"Mutate"} },
Expand Down Expand Up @@ -1555,7 +1560,7 @@ func TestInterpretResourceReferenceInvalidationOnDestroy(t *testing.T) {

_, err := inter.Invoke("test")
RequireError(t, err)
require.ErrorAs(t, err, &interpreter.DestroyedResourceError{})
require.ErrorAs(t, err, &interpreter.InvalidatedResourceReferenceError{})
})

t.Run("ref source is field", func(t *testing.T) {
Expand Down Expand Up @@ -1598,7 +1603,7 @@ func TestInterpretResourceReferenceInvalidationOnDestroy(t *testing.T) {

_, err := inter.Invoke("test")
RequireError(t, err)
require.ErrorAs(t, err, &interpreter.DestroyedResourceError{})
require.ErrorAs(t, err, &interpreter.InvalidatedResourceReferenceError{})

})
}
Expand Down Expand Up @@ -1722,7 +1727,9 @@ func TestInterpretInvalidatedReferenceToOptional(t *testing.T) {
`)

_, err := inter.Invoke("main")
require.NoError(t, err)
RequireError(t, err)

require.ErrorAs(t, err, &interpreter.InvalidatedResourceReferenceError{})
}

func TestInterpretReferenceToReference(t *testing.T) {
Expand Down
Loading

0 comments on commit a5d2f8e

Please sign in to comment.