From cdb811065b9915ee22a31ef8756132aaa52b2250 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 14 Sep 2023 15:57:03 -0700 Subject: [PATCH 01/12] fix capability controller deletion --- runtime/capabilitycontrollers_test.go | 182 +++++++++++++++ .../value_accountcapabilitycontroller.go | 89 ++++---- .../value_storagecapabilitycontroller.go | 153 ++++++++----- runtime/stdlib/account.go | 208 +++++++----------- 4 files changed, 410 insertions(+), 222 deletions(-) diff --git a/runtime/capabilitycontrollers_test.go b/runtime/capabilitycontrollers_test.go index fcdbabe440..8f80ebbc95 100644 --- a/runtime/capabilitycontrollers_test.go +++ b/runtime/capabilitycontrollers_test.go @@ -3185,3 +3185,185 @@ func TestRuntimeCapabilityControllers(t *testing.T) { }) } + +func TestRuntimeCapabilityControllerOperationAfterDeletion(t *testing.T) { + + t.Parallel() + + type operation struct { + name string + code string + } + + type testCase struct { + name string + setup string + operations []operation + } + + test := func(testCase testCase, operation operation) { + + testName := fmt.Sprintf("%s: %s", testCase.name, operation.name) + + t.Run(testName, func(t *testing.T) { + t.Parallel() + + rt := newTestInterpreterRuntime() + rt.defaultConfig.CapabilityControllersEnabled = true + + tx := []byte(fmt.Sprintf( + ` + transaction { + prepare(signer: AuthAccount) { + %s + %s + } + } + `, + testCase.setup, + operation.code, + )) + + address := common.MustBytesToAddress([]byte{0x1}) + accountIDs := map[common.Address]uint64{} + + runtimeInterface := &testRuntimeInterface{ + storage: newTestLedger(nil, nil), + getSigningAccounts: func() ([]Address, error) { + return []Address{address}, nil + }, + emitEvent: func(event cadence.Event) error { + return nil + }, + generateAccountID: func(address common.Address) (uint64, error) { + accountID := accountIDs[address] + 1 + accountIDs[address] = accountID + return accountID, nil + }, + } + + nextTransactionLocation := newTransactionLocationGenerator() + + // Test + + err := rt.ExecuteTransaction( + Script{ + Source: tx, + }, + Context{ + Interface: runtimeInterface, + Location: nextTransactionLocation(), + }, + ) + + require.ErrorContains(t, err, "controller is deleted") + }) + } + + testCases := []testCase{ + { + name: "Storage capability controller", + setup: ` + // Issue capability and get controller + let storageCapabilities = signer.capabilities.storage + let capability = storageCapabilities.issue<&AnyStruct>(/storage/test1) + let controller = storageCapabilities.getController(byCapabilityID: capability.id)! + + // Prepare bound functions + let delete = controller.delete + let tag = controller.tag + let target = controller.target + let retarget = controller.retarget + + // Delete + controller.delete() + `, + operations: []operation{ + // Read + { + name: "get capability", + code: `controller.capability`, + }, + { + name: "get tag", + code: `controller.tag`, + }, + { + name: "get borrow type", + code: `controller.borrowType`, + }, + { + name: "get ID", + code: `controller.capabilityID`, + }, + // Mutate + { + name: "delete", + code: `delete()`, + }, + { + name: "set tag", + code: `controller.tag = "test"`, + }, + { + name: "target", + code: `target()`, + }, + { + name: "retarget", + code: `retarget(/storage/test2)`, + }, + }, + }, + { + name: "Account capability controller", + setup: ` + // Issue capability and get controller + let accountCapabilities = signer.capabilities.account + let capability = accountCapabilities.issue<&AuthAccount>() + let controller = accountCapabilities.getController(byCapabilityID: capability.id)! + + // Prepare bound functions + let delete = controller.delete + let tag = controller.tag + + // Delete + controller.delete() + `, + operations: []operation{ + // Read + { + name: "get capability", + code: `controller.capability`, + }, + { + name: "get tag", + code: `controller.tag`, + }, + { + name: "get borrow type", + code: `controller.borrowType`, + }, + { + name: "get ID", + code: `controller.capabilityID`, + }, + // Mutate + { + name: "delete", + code: `delete()`, + }, + { + name: "set tag", + code: `controller.tag = "test"`, + }, + }, + }, + } + + for _, testCase := range testCases { + for _, operation := range testCase.operations { + test(testCase, operation) + } + } +} diff --git a/runtime/interpreter/value_accountcapabilitycontroller.go b/runtime/interpreter/value_accountcapabilitycontroller.go index 5f400f5765..da7c96ea4b 100644 --- a/runtime/interpreter/value_accountcapabilitycontroller.go +++ b/runtime/interpreter/value_accountcapabilitycontroller.go @@ -33,18 +33,21 @@ type AccountCapabilityControllerValue struct { BorrowType ReferenceStaticType CapabilityID UInt64Value - // tag is locally cached result of GetTag, and not stored. - // It is populated when the field `tag` is read. - tag *StringValue + // deleted indicates if the controller got deleted. Not stored + deleted bool - // Injected functions + // Lazily initialized function values. + // Host functions based on injected functions (see below). + deleteFunction FunctionValue + + // Injected functions. // Tags are not stored directly inside the controller // to avoid unnecessary storage reads // when the controller is loaded for borrowing/checking - GetCapability func() *IDCapabilityValue - GetTag func() *StringValue - SetTag func(*StringValue) - DeleteFunction FunctionValue + GetCapability func(inter *Interpreter) *IDCapabilityValue + GetTag func(inter *Interpreter) *StringValue + SetTag func(inter *Interpreter, tag *StringValue) + Delete func(inter *Interpreter, locationRange LocationRange) } func NewUnmeteredAccountCapabilityControllerValue( @@ -208,16 +211,12 @@ func (v *AccountCapabilityControllerValue) ChildStorables() []atree.Storable { } func (v *AccountCapabilityControllerValue) GetMember(inter *Interpreter, _ LocationRange, name string) Value { + // NOTE: check if controller is already deleted + v.checkDeleted() switch name { case sema.AccountCapabilityControllerTypeTagFieldName: - if v.tag == nil { - v.tag = v.GetTag() - if v.tag == nil { - v.tag = EmptyString - } - } - return v.tag + return v.GetTag(inter) case sema.AccountCapabilityControllerTypeCapabilityIDFieldName: return v.CapabilityID @@ -225,11 +224,16 @@ func (v *AccountCapabilityControllerValue) GetMember(inter *Interpreter, _ Locat case sema.AccountCapabilityControllerTypeBorrowTypeFieldName: return NewTypeValue(inter, v.BorrowType) + case sema.AccountCapabilityControllerTypeCapabilityFieldName: + return v.GetCapability(inter) + case sema.AccountCapabilityControllerTypeDeleteFunctionName: - return v.DeleteFunction + if v.deleteFunction == nil { + v.deleteFunction = v.newDeleteFunction(inter) + } + return v.deleteFunction - case sema.AccountCapabilityControllerTypeCapabilityFieldName: - return v.GetCapability() + // NOTE: when adding new functions, ensure checkDeleted is called! } return nil @@ -241,19 +245,21 @@ func (*AccountCapabilityControllerValue) RemoveMember(_ *Interpreter, _ Location } func (v *AccountCapabilityControllerValue) SetMember( - _ *Interpreter, + inter *Interpreter, _ LocationRange, identifier string, value Value, ) bool { + // NOTE: check if controller is already deleted + v.checkDeleted() + switch identifier { case sema.AccountCapabilityControllerTypeTagFieldName: stringValue, ok := value.(*StringValue) if !ok { panic(errors.NewUnreachableError()) } - v.tag = stringValue - v.SetTag(stringValue) + v.SetTag(inter, stringValue) return true } @@ -278,29 +284,32 @@ func (v *AccountCapabilityControllerValue) ReferenceValue( ) } -// SetDeleted sets the controller as deleted, i.e. functions panic from now on -func (v *AccountCapabilityControllerValue) SetDeleted(gauge common.MemoryGauge) { - - raiseError := func() { +// checkDeleted checks if the controller is deleted, +// and panics if it is. +func (v *AccountCapabilityControllerValue) checkDeleted() { + if v.deleted { panic(errors.NewDefaultUserError("controller is deleted")) } +} - v.SetTag = func(s *StringValue) { - raiseError() - } - v.GetTag = func() *StringValue { - raiseError() - return nil - } +func (v *AccountCapabilityControllerValue) newDeleteFunction( + inter *Interpreter, +) *HostFunctionValue { + return NewHostFunctionValue( + inter, + sema.AccountCapabilityControllerTypeDeleteFunctionType, + func(invocation Invocation) Value { + // NOTE: check if controller is already deleted + v.checkDeleted() - panicHostFunction := func(Invocation) Value { - raiseError() - return nil - } + inter := invocation.Interpreter + locationRange := invocation.LocationRange - v.DeleteFunction = NewHostFunctionValue( - gauge, - sema.AccountCapabilityControllerTypeDeleteFunctionType, - panicHostFunction, + v.Delete(inter, locationRange) + + v.deleted = true + + return Void + }, ) } diff --git a/runtime/interpreter/value_storagecapabilitycontroller.go b/runtime/interpreter/value_storagecapabilitycontroller.go index 470809612c..7f231d4160 100644 --- a/runtime/interpreter/value_storagecapabilitycontroller.go +++ b/runtime/interpreter/value_storagecapabilitycontroller.go @@ -46,20 +46,24 @@ type StorageCapabilityControllerValue struct { CapabilityID UInt64Value TargetPath PathValue - // tag is locally cached result of GetTag, and not stored. - // It is populated when the field `tag` is read. - tag *StringValue + // deleted indicates if the controller got deleted. Not stored + deleted bool + + // Lazily initialized function values. + // Host functions based on injected functions (see below). + deleteFunction FunctionValue + targetFunction FunctionValue + retargetFunction FunctionValue // Injected functions. // Tags are not stored directly inside the controller // to avoid unnecessary storage reads // when the controller is loaded for borrowing/checking - GetCapability func() *IDCapabilityValue - GetTag func() *StringValue - SetTag func(*StringValue) - TargetFunction FunctionValue - RetargetFunction FunctionValue - DeleteFunction FunctionValue + GetCapability func(inter *Interpreter) *IDCapabilityValue + GetTag func(inter *Interpreter) *StringValue + SetTag func(inter *Interpreter, tag *StringValue) + Delete func(inter *Interpreter, locationRange LocationRange) + SetTarget func(inter *Interpreter, locationRange LocationRange, target PathValue) } func NewUnmeteredStorageCapabilityControllerValue( @@ -233,16 +237,12 @@ func (v *StorageCapabilityControllerValue) ChildStorables() []atree.Storable { } func (v *StorageCapabilityControllerValue) GetMember(inter *Interpreter, _ LocationRange, name string) Value { + // NOTE: check if controller is already deleted + v.checkDeleted() switch name { case sema.StorageCapabilityControllerTypeTagFieldName: - if v.tag == nil { - v.tag = v.GetTag() - if v.tag == nil { - v.tag = EmptyString - } - } - return v.tag + return v.GetTag(inter) case sema.StorageCapabilityControllerTypeCapabilityIDFieldName: return v.CapabilityID @@ -250,17 +250,28 @@ func (v *StorageCapabilityControllerValue) GetMember(inter *Interpreter, _ Locat case sema.StorageCapabilityControllerTypeBorrowTypeFieldName: return NewTypeValue(inter, v.BorrowType) + case sema.StorageCapabilityControllerTypeCapabilityFieldName: + return v.GetCapability(inter) + + case sema.StorageCapabilityControllerTypeDeleteFunctionName: + if v.deleteFunction == nil { + v.deleteFunction = v.newDeleteFunction(inter) + } + return v.deleteFunction + case sema.StorageCapabilityControllerTypeTargetFunctionName: - return v.TargetFunction + if v.targetFunction == nil { + v.targetFunction = v.newTargetFunction(inter) + } + return v.targetFunction case sema.StorageCapabilityControllerTypeRetargetFunctionName: - return v.RetargetFunction - - case sema.StorageCapabilityControllerTypeDeleteFunctionName: - return v.DeleteFunction + if v.retargetFunction == nil { + v.retargetFunction = v.newRetargetFunction(inter) + } + return v.retargetFunction - case sema.StorageCapabilityControllerTypeCapabilityFieldName: - return v.GetCapability() + // NOTE: when adding new functions, ensure checkDeleted is called! } return nil @@ -272,19 +283,21 @@ func (*StorageCapabilityControllerValue) RemoveMember(_ *Interpreter, _ Location } func (v *StorageCapabilityControllerValue) SetMember( - _ *Interpreter, + inter *Interpreter, _ LocationRange, identifier string, value Value, ) bool { + // NOTE: check if controller is already deleted + v.checkDeleted() + switch identifier { case sema.StorageCapabilityControllerTypeTagFieldName: stringValue, ok := value.(*StringValue) if !ok { panic(errors.NewUnreachableError()) } - v.tag = stringValue - v.SetTag(stringValue) + v.SetTag(inter, stringValue) return true } @@ -309,39 +322,75 @@ func (v *StorageCapabilityControllerValue) ReferenceValue( ) } -// SetDeleted sets the controller as deleted, i.e. functions panic from now on -func (v *StorageCapabilityControllerValue) SetDeleted(gauge common.MemoryGauge) { - - raiseError := func() { +// checkDeleted checks if the controller is deleted, +// and panics if it is. +func (v *StorageCapabilityControllerValue) checkDeleted() { + if v.deleted { panic(errors.NewDefaultUserError("controller is deleted")) } +} - v.SetTag = func(s *StringValue) { - raiseError() - } - v.GetTag = func() *StringValue { - raiseError() - return nil - } +func (v *StorageCapabilityControllerValue) newDeleteFunction( + inter *Interpreter, +) *HostFunctionValue { + return NewHostFunctionValue( + inter, + sema.StorageCapabilityControllerTypeDeleteFunctionType, + func(invocation Invocation) Value { + // NOTE: check if controller is already deleted + v.checkDeleted() - panicHostFunction := func(Invocation) Value { - raiseError() - return nil - } + inter := invocation.Interpreter + locationRange := invocation.LocationRange + + v.Delete(inter, locationRange) - v.TargetFunction = NewHostFunctionValue( - gauge, + v.deleted = true + + return Void + }, + ) +} + +func (v *StorageCapabilityControllerValue) newTargetFunction( + inter *Interpreter, +) *HostFunctionValue { + return NewHostFunctionValue( + inter, sema.StorageCapabilityControllerTypeTargetFunctionType, - panicHostFunction, + func(invocation Invocation) Value { + // NOTE: check if controller is already deleted + v.checkDeleted() + + return v.TargetPath + }, ) - v.RetargetFunction = NewHostFunctionValue( - gauge, +} + +func (v *StorageCapabilityControllerValue) newRetargetFunction( + inter *Interpreter, +) *HostFunctionValue { + return NewHostFunctionValue( + inter, sema.StorageCapabilityControllerTypeRetargetFunctionType, - panicHostFunction, - ) - v.DeleteFunction = NewHostFunctionValue( - gauge, - sema.StorageCapabilityControllerTypeDeleteFunctionType, - panicHostFunction, + func(invocation Invocation) Value { + // NOTE: check if controller is already deleted + v.checkDeleted() + + inter := invocation.Interpreter + locationRange := invocation.LocationRange + + // Get path argument + + newTargetPathValue, ok := invocation.Arguments[0].(PathValue) + if !ok || newTargetPathValue.Domain != common.PathDomainStorage { + panic(errors.NewUnreachableError()) + } + + v.SetTarget(inter, locationRange, newTargetPathValue) + v.TargetPath = newTargetPathValue + + return Void + }, ) } diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index b97bcdd2a2..1577c34c9e 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -2720,33 +2720,32 @@ func getCapabilityController( capabilityID := controller.CapabilityID controller.GetCapability = - newCapabilityControllerGetCapabilityFunction(inter, address, controller) + newCapabilityControllerGetCapabilityFunction(address, controller) controller.GetTag = - newCapabilityControllerGetTagFunction(inter, address, capabilityID) + newCapabilityControllerGetTagFunction(address, capabilityID) controller.SetTag = - newCapabilityControllerSetTagFunction(inter, address, capabilityID) + newCapabilityControllerSetTagFunction(address, capabilityID) - controller.TargetFunction = - newStorageCapabilityControllerTargetFunction(inter, controller) - controller.RetargetFunction = - newStorageCapabilityControllerRetargetFunction(inter, address, controller) - controller.DeleteFunction = - newStorageCapabilityControllerDeleteFunction(inter, address, controller) + controller.Delete = + newStorageCapabilityControllerDeleteFunction(address, controller) + + controller.SetTarget = + newStorageCapabilityControllerSetTargetFunction(address, controller) case *interpreter.AccountCapabilityControllerValue: capabilityID := controller.CapabilityID controller.GetCapability = - newCapabilityControllerGetCapabilityFunction(inter, address, controller) + newCapabilityControllerGetCapabilityFunction(address, controller) controller.GetTag = - newCapabilityControllerGetTagFunction(inter, address, capabilityID) + newCapabilityControllerGetTagFunction(address, capabilityID) controller.SetTag = - newCapabilityControllerSetTagFunction(inter, address, capabilityID) + newCapabilityControllerSetTagFunction(address, capabilityID) - controller.DeleteFunction = - newAccountCapabilityControllerDeleteFunction(inter, address, controller) + controller.Delete = + newAccountCapabilityControllerDeleteFunction(address, controller) } return controller @@ -2776,94 +2775,59 @@ func getStorageCapabilityControllerReference( ) } -func newStorageCapabilityControllerTargetFunction( - inter *interpreter.Interpreter, - controller *interpreter.StorageCapabilityControllerValue, -) interpreter.FunctionValue { - return interpreter.NewHostFunctionValue( - inter, - sema.StorageCapabilityControllerTypeTargetFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - return controller.TargetPath - }, - ) -} - -func newStorageCapabilityControllerRetargetFunction( - inter *interpreter.Interpreter, +func newStorageCapabilityControllerSetTargetFunction( address common.Address, controller *interpreter.StorageCapabilityControllerValue, -) interpreter.FunctionValue { - return interpreter.NewHostFunctionValue( - inter, - sema.StorageCapabilityControllerTypeTargetFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - locationRange := invocation.LocationRange - - // Get path argument - - newTargetPathValue, ok := invocation.Arguments[0].(interpreter.PathValue) - if !ok || newTargetPathValue.Domain != common.PathDomainStorage { - panic(errors.NewUnreachableError()) - } - - oldTargetPathValue := controller.TargetPath - - capabilityID := controller.CapabilityID - unrecordStorageCapabilityController( - inter, - locationRange, - address, - oldTargetPathValue, - capabilityID, - ) - recordStorageCapabilityController( - inter, - locationRange, - address, - newTargetPathValue, - capabilityID, - ) - - controller.TargetPath = newTargetPathValue +) func(*interpreter.Interpreter, interpreter.LocationRange, interpreter.PathValue) { + return func( + inter *interpreter.Interpreter, + locationRange interpreter.LocationRange, + newTargetPathValue interpreter.PathValue, + ) { + oldTargetPathValue := controller.TargetPath + capabilityID := controller.CapabilityID - return interpreter.Void - }, - ) + unrecordStorageCapabilityController( + inter, + locationRange, + address, + oldTargetPathValue, + capabilityID, + ) + recordStorageCapabilityController( + inter, + locationRange, + address, + newTargetPathValue, + capabilityID, + ) + } } func newStorageCapabilityControllerDeleteFunction( - inter *interpreter.Interpreter, address common.Address, controller *interpreter.StorageCapabilityControllerValue, -) interpreter.FunctionValue { - return interpreter.NewHostFunctionValue( - inter, - sema.StorageCapabilityControllerTypeTargetFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - inter := invocation.Interpreter - locationRange := invocation.LocationRange - - capabilityID := controller.CapabilityID - - unrecordStorageCapabilityController( - inter, - locationRange, - address, - controller.TargetPath, - capabilityID, - ) - removeCapabilityController( - inter, - address, - capabilityID, - ) - - controller.SetDeleted(inter) +) func(*interpreter.Interpreter, interpreter.LocationRange) { + return func( + inter *interpreter.Interpreter, + locationRange interpreter.LocationRange, + ) { + targetPathValue := controller.TargetPath + capabilityID := controller.CapabilityID - return interpreter.Void - }, - ) + unrecordStorageCapabilityController( + inter, + locationRange, + address, + targetPathValue, + capabilityID, + ) + removeCapabilityController( + inter, + address, + capabilityID, + ) + } } var capabilityIDSetStaticType = interpreter.DictionaryStaticType{ @@ -3955,37 +3919,24 @@ func newAuthAccountAccountCapabilitiesForEachControllerFunction( } func newAccountCapabilityControllerDeleteFunction( - inter *interpreter.Interpreter, address common.Address, controller *interpreter.AccountCapabilityControllerValue, -) interpreter.FunctionValue { - return interpreter.NewHostFunctionValue( - inter, - sema.StorageCapabilityControllerTypeTargetFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - - inter := invocation.Interpreter - locationRange := invocation.LocationRange - - capabilityID := controller.CapabilityID - - unrecordAccountCapabilityController( - inter, - locationRange, - address, - capabilityID, - ) - removeCapabilityController( - inter, - address, - capabilityID, - ) - - controller.SetDeleted(inter) +) func(*interpreter.Interpreter, interpreter.LocationRange) { + return func(inter *interpreter.Interpreter, locationRange interpreter.LocationRange) { + capabilityID := controller.CapabilityID - return interpreter.Void - }, - ) + unrecordAccountCapabilityController( + inter, + locationRange, + address, + capabilityID, + ) + removeCapabilityController( + inter, + address, + capabilityID, + ) + } } // CapabilityControllerTagStorageDomain is the storage domain which stores @@ -4016,16 +3967,15 @@ func getCapabilityControllerTag( } func newCapabilityControllerGetCapabilityFunction( - inter *interpreter.Interpreter, address common.Address, controller interpreter.CapabilityControllerValue, -) func() *interpreter.IDCapabilityValue { +) func(inter *interpreter.Interpreter) *interpreter.IDCapabilityValue { addressValue := interpreter.AddressValue(address) capabilityID := controller.ControllerCapabilityID() borrowType := controller.CapabilityControllerBorrowType() - return func() *interpreter.IDCapabilityValue { + return func(inter *interpreter.Interpreter) *interpreter.IDCapabilityValue { return interpreter.NewIDCapabilityValue( inter, capabilityID, @@ -4036,12 +3986,11 @@ func newCapabilityControllerGetCapabilityFunction( } func newCapabilityControllerGetTagFunction( - inter *interpreter.Interpreter, address common.Address, capabilityIDValue interpreter.UInt64Value, -) func() *interpreter.StringValue { +) func(*interpreter.Interpreter) *interpreter.StringValue { - return func() *interpreter.StringValue { + return func(inter *interpreter.Interpreter) *interpreter.StringValue { return getCapabilityControllerTag( inter, address, @@ -4071,11 +4020,10 @@ func setCapabilityControllerTag( } func newCapabilityControllerSetTagFunction( - inter *interpreter.Interpreter, address common.Address, capabilityIDValue interpreter.UInt64Value, -) func(tagValue *interpreter.StringValue) { - return func(tagValue *interpreter.StringValue) { +) func(*interpreter.Interpreter, *interpreter.StringValue) { + return func(inter *interpreter.Interpreter, tagValue *interpreter.StringValue) { setCapabilityControllerTag( inter, address, From bc918dd90157af8d4104d94d76c797842d6805bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 19 Sep 2023 14:35:07 -0700 Subject: [PATCH 02/12] assign to whole group --- .github/ISSUE_TEMPLATE/feature-request.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature-request.yaml b/.github/ISSUE_TEMPLATE/feature-request.yaml index 83af1cfb1b..440c363d39 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.yaml +++ b/.github/ISSUE_TEMPLATE/feature-request.yaml @@ -1,7 +1,7 @@ name: Requesting a Feature or Improvement description: For feature requests. Please search for existing issues first. Also see CONTRIBUTING. labels: [Feature, Feedback] -assignees: turbolent +assignees: @onflow/cadence body: - type: markdown attributes: From 6486dd57f33b2e893df1f2d764b27316d86602d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 19 Sep 2023 14:35:51 -0700 Subject: [PATCH 03/12] assign whole group --- .github/ISSUE_TEMPLATE/bug-report.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yaml b/.github/ISSUE_TEMPLATE/bug-report.yaml index c251557905..da0f89acfe 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yaml +++ b/.github/ISSUE_TEMPLATE/bug-report.yaml @@ -1,7 +1,7 @@ name: Reporting a Problem/Bug description: Reporting a Problem/Bug labels: [Bug, Feedback] -assignees: turbolent, SupunS, dsainati1, dreamsmasher +assignees: onflow/cadence body: - type: markdown attributes: @@ -45,4 +45,4 @@ body: - Network: render: markdown validations: - required: true \ No newline at end of file + required: true From 7cdde068c26d3dbb3ae6484db5199f8625874ff7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 19 Sep 2023 14:36:55 -0700 Subject: [PATCH 04/12] Update .github/ISSUE_TEMPLATE/feature-request.yaml --- .github/ISSUE_TEMPLATE/feature-request.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature-request.yaml b/.github/ISSUE_TEMPLATE/feature-request.yaml index 440c363d39..1c0bc5c654 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.yaml +++ b/.github/ISSUE_TEMPLATE/feature-request.yaml @@ -1,7 +1,7 @@ name: Requesting a Feature or Improvement description: For feature requests. Please search for existing issues first. Also see CONTRIBUTING. labels: [Feature, Feedback] -assignees: @onflow/cadence +assignees: onflow/cadence body: - type: markdown attributes: From 0639275fbecacca418840b114a22784a7942eab4 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Wed, 20 Sep 2023 09:39:04 -0400 Subject: [PATCH 05/12] properly check removed expression for resource loss --- runtime/sema/check_remove_statement.go | 1 + runtime/tests/checker/attachments_test.go | 41 +++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/runtime/sema/check_remove_statement.go b/runtime/sema/check_remove_statement.go index 8e06758933..cc022e7071 100644 --- a/runtime/sema/check_remove_statement.go +++ b/runtime/sema/check_remove_statement.go @@ -33,6 +33,7 @@ func (checker *Checker) VisitRemoveStatement(statement *ast.RemoveStatement) (_ nominalType := checker.convertNominalType(statement.Attachment) base := checker.VisitExpression(statement.Value, nil) + checker.checkUnusedExpressionResourceLoss(base, statement.Value) if nominalType == InvalidType { return diff --git a/runtime/tests/checker/attachments_test.go b/runtime/tests/checker/attachments_test.go index 2f076c965f..bb181efa21 100644 --- a/runtime/tests/checker/attachments_test.go +++ b/runtime/tests/checker/attachments_test.go @@ -4372,3 +4372,44 @@ func TestCheckAttachmentsNotEnabled(t *testing.T) { require.NoError(t, err) }) } + +func TestCheckAttachmentRemoveLossTracking(t *testing.T) { + + t.Run("remove immediately added attachment", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + resource R {} + attachment A for R {} + fun loseResource(r: @R) { + remove A from <- attach A() to <- r + } + fun test() { + loseResource(r: <- create R()) + } + `) + + errs := RequireCheckerErrors(t, err, 1) + assert.IsType(t, &sema.ResourceLossError{}, errs[0]) + }) + + t.Run("remove from function call result", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + resource R {} + attachment A for R {} + fun createRwithA(): @R { + return <- attach A() to <- create R() + } + fun loseResource() { + remove A from <- createRwithA() + } + `) + + errs := RequireCheckerErrors(t, err, 1) + assert.IsType(t, &sema.ResourceLossError{}, errs[0]) + }) +} From 4957cb11689d80c2459085ea58f28d897a536057 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 21 Sep 2023 12:44:02 -0700 Subject: [PATCH 06/12] check functions from GetMember of capability controller values checked for deletion --- .../value_accountcapabilitycontroller.go | 44 ++++++++++++--- .../value_storagecapabilitycontroller.go | 54 +++++++++++++------ 2 files changed, 74 insertions(+), 24 deletions(-) diff --git a/runtime/interpreter/value_accountcapabilitycontroller.go b/runtime/interpreter/value_accountcapabilitycontroller.go index da7c96ea4b..3c90c91e3f 100644 --- a/runtime/interpreter/value_accountcapabilitycontroller.go +++ b/runtime/interpreter/value_accountcapabilitycontroller.go @@ -210,7 +210,20 @@ func (v *AccountCapabilityControllerValue) ChildStorables() []atree.Storable { } } -func (v *AccountCapabilityControllerValue) GetMember(inter *Interpreter, _ LocationRange, name string) Value { +type deletionCheckedFunctionValue struct { + FunctionValue +} + +func (v *AccountCapabilityControllerValue) GetMember(inter *Interpreter, _ LocationRange, name string) (result Value) { + defer func() { + switch typedResult := result.(type) { + case deletionCheckedFunctionValue: + result = typedResult.FunctionValue + case FunctionValue: + panic(errors.NewUnexpectedError("functions need to check deletion. Use newHostFunctionValue")) + } + }() + // NOTE: check if controller is already deleted v.checkDeleted() @@ -233,7 +246,8 @@ func (v *AccountCapabilityControllerValue) GetMember(inter *Interpreter, _ Locat } return v.deleteFunction - // NOTE: when adding new functions, ensure checkDeleted is called! + // NOTE: when adding new functions, ensure checkDeleted is called, + // by e.g. using AccountCapabilityControllerValue.newHostFunction } return nil @@ -292,16 +306,32 @@ func (v *AccountCapabilityControllerValue) checkDeleted() { } } +func (v *AccountCapabilityControllerValue) newHostFunctionValue( + gauge common.MemoryGauge, + funcType *sema.FunctionType, + f func(invocation Invocation) Value, +) FunctionValue { + return deletionCheckedFunctionValue{ + FunctionValue: NewHostFunctionValue( + gauge, + funcType, + func(invocation Invocation) Value { + // NOTE: check if controller is already deleted + v.checkDeleted() + + return f(invocation) + }, + ), + } +} + func (v *AccountCapabilityControllerValue) newDeleteFunction( inter *Interpreter, -) *HostFunctionValue { - return NewHostFunctionValue( +) FunctionValue { + return v.newHostFunctionValue( inter, sema.AccountCapabilityControllerTypeDeleteFunctionType, func(invocation Invocation) Value { - // NOTE: check if controller is already deleted - v.checkDeleted() - inter := invocation.Interpreter locationRange := invocation.LocationRange diff --git a/runtime/interpreter/value_storagecapabilitycontroller.go b/runtime/interpreter/value_storagecapabilitycontroller.go index 7f231d4160..60e4ca4e13 100644 --- a/runtime/interpreter/value_storagecapabilitycontroller.go +++ b/runtime/interpreter/value_storagecapabilitycontroller.go @@ -236,7 +236,16 @@ func (v *StorageCapabilityControllerValue) ChildStorables() []atree.Storable { } } -func (v *StorageCapabilityControllerValue) GetMember(inter *Interpreter, _ LocationRange, name string) Value { +func (v *StorageCapabilityControllerValue) GetMember(inter *Interpreter, _ LocationRange, name string) (result Value) { + defer func() { + switch typedResult := result.(type) { + case deletionCheckedFunctionValue: + result = typedResult.FunctionValue + case FunctionValue: + panic(errors.NewUnexpectedError("functions need to check deletion. Use newHostFunctionValue")) + } + }() + // NOTE: check if controller is already deleted v.checkDeleted() @@ -271,7 +280,8 @@ func (v *StorageCapabilityControllerValue) GetMember(inter *Interpreter, _ Locat } return v.retargetFunction - // NOTE: when adding new functions, ensure checkDeleted is called! + // NOTE: when adding new functions, ensure checkDeleted is called, + // by e.g. using StorageCapabilityControllerValue.newHostFunction } return nil @@ -330,16 +340,32 @@ func (v *StorageCapabilityControllerValue) checkDeleted() { } } +func (v *StorageCapabilityControllerValue) newHostFunctionValue( + gauge common.MemoryGauge, + funcType *sema.FunctionType, + f func(invocation Invocation) Value, +) FunctionValue { + return deletionCheckedFunctionValue{ + FunctionValue: NewHostFunctionValue( + gauge, + funcType, + func(invocation Invocation) Value { + // NOTE: check if controller is already deleted + v.checkDeleted() + + return f(invocation) + }, + ), + } +} + func (v *StorageCapabilityControllerValue) newDeleteFunction( inter *Interpreter, -) *HostFunctionValue { - return NewHostFunctionValue( +) FunctionValue { + return v.newHostFunctionValue( inter, sema.StorageCapabilityControllerTypeDeleteFunctionType, func(invocation Invocation) Value { - // NOTE: check if controller is already deleted - v.checkDeleted() - inter := invocation.Interpreter locationRange := invocation.LocationRange @@ -354,14 +380,11 @@ func (v *StorageCapabilityControllerValue) newDeleteFunction( func (v *StorageCapabilityControllerValue) newTargetFunction( inter *Interpreter, -) *HostFunctionValue { - return NewHostFunctionValue( +) FunctionValue { + return v.newHostFunctionValue( inter, sema.StorageCapabilityControllerTypeTargetFunctionType, func(invocation Invocation) Value { - // NOTE: check if controller is already deleted - v.checkDeleted() - return v.TargetPath }, ) @@ -369,14 +392,11 @@ func (v *StorageCapabilityControllerValue) newTargetFunction( func (v *StorageCapabilityControllerValue) newRetargetFunction( inter *Interpreter, -) *HostFunctionValue { - return NewHostFunctionValue( +) FunctionValue { + return v.newHostFunctionValue( inter, sema.StorageCapabilityControllerTypeRetargetFunctionType, func(invocation Invocation) Value { - // NOTE: check if controller is already deleted - v.checkDeleted() - inter := invocation.Interpreter locationRange := invocation.LocationRange From 58bf115c0ac83dc6a8ed596395405ce68e0e0026 Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Fri, 22 Sep 2023 12:27:04 +0300 Subject: [PATCH 07/12] Replace panic with nil return in newInjectedCompositeFieldsHandler --- runtime/environment.go | 2 +- runtime/tests/interpreter/interpreter_test.go | 79 +++++++++++-------- 2 files changed, 49 insertions(+), 32 deletions(-) diff --git a/runtime/environment.go b/runtime/environment.go index 32a5f8b024..70dd083ed6 100644 --- a/runtime/environment.go +++ b/runtime/environment.go @@ -827,7 +827,7 @@ func (e *interpreterEnvironment) newInjectedCompositeFieldsHandler() interpreter case common.AddressLocation: address = location.Address default: - panic(errors.NewUnreachableError()) + return nil } addressValue := interpreter.NewAddressValue( diff --git a/runtime/tests/interpreter/interpreter_test.go b/runtime/tests/interpreter/interpreter_test.go index ebc35eab09..92dbf988a8 100644 --- a/runtime/tests/interpreter/interpreter_test.go +++ b/runtime/tests/interpreter/interpreter_test.go @@ -25,6 +25,7 @@ import ( "strings" "testing" + "github.com/onflow/cadence/runtime" "github.com/onflow/cadence/runtime/activations" "github.com/onflow/atree" @@ -8603,42 +8604,58 @@ func TestInterpretContractAccountFieldUse(t *testing.T) { pub let address2 = Test.test() ` - addressValue := interpreter.AddressValue{ - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, - } + t.Run("with custom handler", func(t *testing.T) { + addressValue := interpreter.AddressValue{ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, + } - inter, err := parseCheckAndInterpretWithOptions(t, code, - ParseCheckAndInterpretOptions{ - Config: &interpreter.Config{ - ContractValueHandler: makeContractValueHandler(nil, nil, nil), - InjectedCompositeFieldsHandler: func( - inter *interpreter.Interpreter, - _ common.Location, - _ string, - _ common.CompositeKind, - ) map[string]interpreter.Value { - return map[string]interpreter.Value{ - "account": newTestAuthAccountValue(inter, addressValue), - } + inter, err := parseCheckAndInterpretWithOptions(t, code, + ParseCheckAndInterpretOptions{ + Config: &interpreter.Config{ + ContractValueHandler: makeContractValueHandler(nil, nil, nil), + InjectedCompositeFieldsHandler: func( + inter *interpreter.Interpreter, + _ common.Location, + _ string, + _ common.CompositeKind, + ) map[string]interpreter.Value { + return map[string]interpreter.Value{ + "account": newTestAuthAccountValue(inter, addressValue), + } + }, }, }, - }, - ) - require.NoError(t, err) + ) + require.NoError(t, err) - AssertValuesEqual( - t, - inter, - addressValue, - inter.Globals.Get("address1").GetValue(), - ) + AssertValuesEqual( + t, + inter, + addressValue, + inter.Globals.Get("address1").GetValue(), + ) - AssertValuesEqual( - t, - inter, - addressValue, - inter.Globals.Get("address2").GetValue(), - ) + AssertValuesEqual( + t, + inter, + addressValue, + inter.Globals.Get("address2").GetValue(), + ) + }) + + t.Run("with default handler", func(t *testing.T) { + env := runtime.NewBaseInterpreterEnvironment(runtime.Config{}) + _, err := parseCheckAndInterpretWithOptions(t, code, + ParseCheckAndInterpretOptions{ + Config: &interpreter.Config{ + ContractValueHandler: makeContractValueHandler(nil, nil, nil), + InjectedCompositeFieldsHandler: env.InterpreterConfig.InjectedCompositeFieldsHandler, + }, + }, + ) + require.Error(t, err) + assert.ErrorContains(t, err, "error: member `account` is used before it has been initialized") + }) } func TestInterpretConformToImportedInterface(t *testing.T) { From 7cb1074a3d80c553c24b7367583e109bfbca248b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 22 Sep 2023 10:24:02 -0700 Subject: [PATCH 08/12] add support for injecting types into the environment --- runtime/environment.go | 21 +++++++--- runtime/predeclaredvalues_test.go | 65 +++++++++++++++++++++++++++++-- 2 files changed, 77 insertions(+), 9 deletions(-) diff --git a/runtime/environment.go b/runtime/environment.go index 32a5f8b024..91011df111 100644 --- a/runtime/environment.go +++ b/runtime/environment.go @@ -38,7 +38,8 @@ import ( type Environment interface { ArgumentDecoder - Declare(valueDeclaration stdlib.StandardLibraryValue) + DeclareValue(valueDeclaration stdlib.StandardLibraryValue) + DeclareType(typeDeclaration stdlib.StandardLibraryType) Configure( runtimeInterface Interface, codesAndPrograms CodesAndPrograms, @@ -78,8 +79,9 @@ type interpreterEnvironmentReconfigured struct { type interpreterEnvironment struct { interpreterEnvironmentReconfigured - baseActivation *interpreter.VariableActivation + baseTypeActivation *sema.VariableActivation baseValueActivation *sema.VariableActivation + baseActivation *interpreter.VariableActivation InterpreterConfig *interpreter.Config CheckerConfig *sema.Config deployedContractConstructorInvocation *stdlib.DeployedContractConstructorInvocation @@ -108,12 +110,14 @@ 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) env := &interpreterEnvironment{ config: config, - baseActivation: baseActivation, baseValueActivation: baseValueActivation, + baseTypeActivation: baseTypeActivation, + baseActivation: baseActivation, stackDepthLimiter: newStackDepthLimiter(config.StackDepthLimit), } env.InterpreterConfig = env.newInterpreterConfig() @@ -158,6 +162,7 @@ func (e *interpreterEnvironment) newCheckerConfig() *sema.Config { return &sema.Config{ AccessCheckMode: sema.AccessCheckModeStrict, BaseValueActivation: e.baseValueActivation, + BaseTypeActivation: e.baseTypeActivation, ValidTopLevelDeclarationsHandler: validTopLevelDeclarations, LocationHandler: e.newLocationHandler(), ImportHandler: e.resolveImport, @@ -171,7 +176,7 @@ func (e *interpreterEnvironment) newCheckerConfig() *sema.Config { func NewBaseInterpreterEnvironment(config Config) *interpreterEnvironment { env := newInterpreterEnvironment(config) for _, valueDeclaration := range stdlib.DefaultStandardLibraryValues(env) { - env.Declare(valueDeclaration) + env.DeclareValue(valueDeclaration) } return env } @@ -179,7 +184,7 @@ func NewBaseInterpreterEnvironment(config Config) *interpreterEnvironment { func NewScriptInterpreterEnvironment(config Config) Environment { env := newInterpreterEnvironment(config) for _, valueDeclaration := range stdlib.DefaultScriptStandardLibraryValues(env) { - env.Declare(valueDeclaration) + env.DeclareValue(valueDeclaration) } return env } @@ -198,11 +203,15 @@ func (e *interpreterEnvironment) Configure( e.stackDepthLimiter.depth = 0 } -func (e *interpreterEnvironment) Declare(valueDeclaration stdlib.StandardLibraryValue) { +func (e *interpreterEnvironment) DeclareValue(valueDeclaration stdlib.StandardLibraryValue) { e.baseValueActivation.DeclareValue(valueDeclaration) interpreter.Declare(e.baseActivation, valueDeclaration) } +func (e *interpreterEnvironment) DeclareType(typeDeclaration stdlib.StandardLibraryType) { + e.baseTypeActivation.DeclareType(typeDeclaration) +} + func (e *interpreterEnvironment) NewAuthAccountValue(address interpreter.AddressValue) interpreter.Value { return stdlib.NewAuthAccountValue(e, e, address) } diff --git a/runtime/predeclaredvalues_test.go b/runtime/predeclaredvalues_test.go index 069838164b..fee5147550 100644 --- a/runtime/predeclaredvalues_test.go +++ b/runtime/predeclaredvalues_test.go @@ -39,7 +39,7 @@ func TestRuntimePredeclaredValues(t *testing.T) { valueDeclaration := stdlib.StandardLibraryValue{ Name: "foo", Type: sema.IntType, - Kind: common.DeclarationKindFunction, + Kind: common.DeclarationKindConstant, Value: interpreter.NewUnmeteredIntValueFromInt64(2), } @@ -91,7 +91,7 @@ func TestRuntimePredeclaredValues(t *testing.T) { // Run transaction transactionEnvironment := NewBaseInterpreterEnvironment(Config{}) - transactionEnvironment.Declare(valueDeclaration) + transactionEnvironment.DeclareValue(valueDeclaration) err := runtime.ExecuteTransaction( Script{ @@ -108,7 +108,7 @@ func TestRuntimePredeclaredValues(t *testing.T) { // Run script scriptEnvironment := NewScriptInterpreterEnvironment(Config{}) - scriptEnvironment.Declare(valueDeclaration) + scriptEnvironment.DeclareValue(valueDeclaration) result, err := runtime.ExecuteScript( Script{ @@ -127,3 +127,62 @@ func TestRuntimePredeclaredValues(t *testing.T) { result, ) } + +func TestRuntimePredeclaredTypes(t *testing.T) { + + t.Parallel() + + xType := sema.IntType + + valueDeclaration := stdlib.StandardLibraryValue{ + Name: "x", + Type: xType, + Kind: common.DeclarationKindConstant, + Value: interpreter.NewUnmeteredIntValueFromInt64(2), + } + + typeDeclaration := stdlib.StandardLibraryType{ + Name: "X", + Type: xType, + Kind: common.DeclarationKindType, + } + + script := []byte(` + pub fun main(): X { + return x + } + `) + + runtime := newTestInterpreterRuntime() + + runtimeInterface := &testRuntimeInterface{ + storage: newTestLedger(nil, nil), + getSigningAccounts: func() ([]Address, error) { + return []Address{common.MustBytesToAddress([]byte{0x1})}, nil + }, + resolveLocation: singleIdentifierLocationResolver(t), + } + + // Run script + + scriptEnvironment := NewScriptInterpreterEnvironment(Config{}) + scriptEnvironment.DeclareValue(valueDeclaration) + scriptEnvironment.DeclareType(typeDeclaration) + + result, err := runtime.ExecuteScript( + Script{ + Source: script, + }, + Context{ + Interface: runtimeInterface, + Location: common.ScriptLocation{}, + Environment: scriptEnvironment, + }, + ) + require.NoError(t, err) + + require.Equal(t, + cadence.Int{Value: big.NewInt(2)}, + result, + ) +} From 957de13ed7bfe480ad5c328cdba6be3e190f7004 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 22 Sep 2023 13:35:25 -0700 Subject: [PATCH 09/12] add support for injecting composite types, potentially nested: perform lookup in base type activation --- runtime/environment.go | 15 +- runtime/interpreter/interpreter.go | 18 +- runtime/predeclaredvalues_test.go | 401 +++++++++++++++++++++++++---- runtime/sema/type.go | 34 ++- 4 files changed, 404 insertions(+), 64 deletions(-) diff --git a/runtime/environment.go b/runtime/environment.go index 91011df111..1edd4bcaef 100644 --- a/runtime/environment.go +++ b/runtime/environment.go @@ -897,8 +897,21 @@ func (e *interpreterEnvironment) newImportLocationHandler() interpreter.ImportLo func (e *interpreterEnvironment) newCompositeTypeHandler() interpreter.CompositeTypeHandlerFunc { return func(location common.Location, typeID common.TypeID) *sema.CompositeType { - if _, ok := location.(stdlib.FlowLocation); ok { + + switch location.(type) { + case stdlib.FlowLocation: return stdlib.FlowEventTypes[typeID] + + case nil: + qualifiedIdentifier := string(typeID) + ty := sema.TypeActivationNestedType(e.baseTypeActivation, qualifiedIdentifier) + if ty == nil { + return nil + } + + if compositeType, ok := ty.(*sema.CompositeType); ok { + return compositeType + } } return nil diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index acb6ee8bbf..de338e7b8d 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -4796,16 +4796,18 @@ func (interpreter *Interpreter) GetCompositeType( if compositeType != nil { return compositeType, nil } - } else { - config := interpreter.SharedState.Config - compositeTypeHandler := config.CompositeTypeHandler - if compositeTypeHandler != nil { - compositeType = compositeTypeHandler(location, typeID) - if compositeType != nil { - return compositeType, nil - } + } + + config := interpreter.SharedState.Config + compositeTypeHandler := config.CompositeTypeHandler + if compositeTypeHandler != nil { + compositeType = compositeTypeHandler(location, typeID) + if compositeType != nil { + return compositeType, nil } + } + if location != nil { compositeType = interpreter.getUserCompositeType(location, typeID) if compositeType != nil { return compositeType, nil diff --git a/runtime/predeclaredvalues_test.go b/runtime/predeclaredvalues_test.go index fee5147550..57a86788e4 100644 --- a/runtime/predeclaredvalues_test.go +++ b/runtime/predeclaredvalues_test.go @@ -29,7 +29,7 @@ import ( "github.com/onflow/cadence/runtime/interpreter" "github.com/onflow/cadence/runtime/sema" "github.com/onflow/cadence/runtime/stdlib" - "github.com/onflow/cadence/runtime/tests/utils" + . "github.com/onflow/cadence/runtime/tests/utils" ) func TestRuntimePredeclaredValues(t *testing.T) { @@ -61,7 +61,7 @@ func TestRuntimePredeclaredValues(t *testing.T) { runtime := newTestInterpreterRuntime() - deploy := utils.DeploymentTransaction("C", contract) + deploy := DeploymentTransaction("C", contract) var accountCode []byte var events []cadence.Event @@ -132,57 +132,350 @@ func TestRuntimePredeclaredTypes(t *testing.T) { t.Parallel() - xType := sema.IntType + t.Run("type alias", func(t *testing.T) { + t.Parallel() + + xType := sema.IntType + + valueDeclaration := stdlib.StandardLibraryValue{ + Name: "x", + Type: xType, + Kind: common.DeclarationKindConstant, + Value: interpreter.NewUnmeteredIntValueFromInt64(2), + } + + typeDeclaration := stdlib.StandardLibraryType{ + Name: "X", + Type: xType, + Kind: common.DeclarationKindType, + } + + script := []byte(` + pub fun main(): X { + return x + } + `) + + runtime := newTestInterpreterRuntime() + + runtimeInterface := &testRuntimeInterface{ + storage: newTestLedger(nil, nil), + getSigningAccounts: func() ([]Address, error) { + return []Address{common.MustBytesToAddress([]byte{0x1})}, nil + }, + resolveLocation: singleIdentifierLocationResolver(t), + } + + // Run script + + scriptEnvironment := NewScriptInterpreterEnvironment(Config{}) + scriptEnvironment.DeclareValue(valueDeclaration) + scriptEnvironment.DeclareType(typeDeclaration) + + result, err := runtime.ExecuteScript( + Script{ + Source: script, + }, + Context{ + Interface: runtimeInterface, + Location: common.ScriptLocation{}, + Environment: scriptEnvironment, + }, + ) + require.NoError(t, err) + + require.Equal(t, + cadence.Int{Value: big.NewInt(2)}, + result, + ) + }) + + t.Run("composite type, top-level, existing", func(t *testing.T) { + t.Parallel() + + xType := &sema.CompositeType{ + Identifier: "X", + Kind: common.CompositeKindStructure, + Members: &sema.StringMemberOrderedMap{}, + } + + valueDeclaration := stdlib.StandardLibraryValue{ + Name: "x", + Type: xType, + Kind: common.DeclarationKindConstant, + Value: interpreter.NewSimpleCompositeValue(nil, + xType.ID(), + interpreter.ConvertSemaCompositeTypeToStaticCompositeType(nil, xType), + nil, + nil, + nil, + nil, + nil, + ), + } + + typeDeclaration := stdlib.StandardLibraryType{ + Name: "X", + Type: xType, + Kind: common.DeclarationKindType, + } + + script := []byte(` + pub fun main(): X { + return x + } + `) + + runtime := newTestInterpreterRuntime() + + runtimeInterface := &testRuntimeInterface{ + storage: newTestLedger(nil, nil), + getSigningAccounts: func() ([]Address, error) { + return []Address{common.MustBytesToAddress([]byte{0x1})}, nil + }, + resolveLocation: singleIdentifierLocationResolver(t), + } + + // Run script + + scriptEnvironment := NewScriptInterpreterEnvironment(Config{}) + scriptEnvironment.DeclareValue(valueDeclaration) + scriptEnvironment.DeclareType(typeDeclaration) + + result, err := runtime.ExecuteScript( + Script{ + Source: script, + }, + Context{ + Interface: runtimeInterface, + Location: common.ScriptLocation{}, + Environment: scriptEnvironment, + }, + ) + require.NoError(t, err) + + require.Equal(t, + cadence.ValueWithCachedTypeID( + cadence.Struct{ + StructType: cadence.NewStructType(nil, xType.QualifiedIdentifier(), []cadence.Field{}, nil), + Fields: []cadence.Value{}, + }, + ), + result, + ) + }) + + t.Run("composite type, top-level, non-existing", func(t *testing.T) { + t.Parallel() + + xType := &sema.CompositeType{ + Identifier: "X", + Kind: common.CompositeKindStructure, + Members: &sema.StringMemberOrderedMap{}, + } + + valueDeclaration := stdlib.StandardLibraryValue{ + Name: "x", + Type: xType, + Kind: common.DeclarationKindConstant, + Value: interpreter.NewSimpleCompositeValue(nil, + xType.ID(), + interpreter.ConvertSemaCompositeTypeToStaticCompositeType(nil, xType), + nil, + nil, + nil, + nil, + nil, + ), + } + + script := []byte(` + pub fun main(): AnyStruct { + return x + } + `) + + runtime := newTestInterpreterRuntime() + + runtimeInterface := &testRuntimeInterface{ + storage: newTestLedger(nil, nil), + getSigningAccounts: func() ([]Address, error) { + return []Address{common.MustBytesToAddress([]byte{0x1})}, nil + }, + resolveLocation: singleIdentifierLocationResolver(t), + } + + // Run script + + scriptEnvironment := NewScriptInterpreterEnvironment(Config{}) + scriptEnvironment.DeclareValue(valueDeclaration) + + _, err := runtime.ExecuteScript( + Script{ + Source: script, + }, + Context{ + Interface: runtimeInterface, + Location: common.ScriptLocation{}, + Environment: scriptEnvironment, + }, + ) + RequireError(t, err) + + var typeLoadingErr interpreter.TypeLoadingError + require.ErrorAs(t, err, &typeLoadingErr) + }) + + t.Run("composite type, nested, existing", func(t *testing.T) { + t.Parallel() + + yType := &sema.CompositeType{ + Identifier: "Y", + Kind: common.CompositeKindStructure, + Members: &sema.StringMemberOrderedMap{}, + } + + xType := &sema.CompositeType{ + Identifier: "X", + Kind: common.CompositeKindContract, + Members: &sema.StringMemberOrderedMap{}, + } + + xType.SetNestedType(yType.Identifier, yType) + + valueDeclaration := stdlib.StandardLibraryValue{ + Name: "y", + Type: yType, + Kind: common.DeclarationKindConstant, + Value: interpreter.NewSimpleCompositeValue(nil, + yType.ID(), + interpreter.ConvertSemaCompositeTypeToStaticCompositeType(nil, yType), + nil, + nil, + nil, + nil, + nil, + ), + } + + typeDeclaration := stdlib.StandardLibraryType{ + Name: "X", + Type: xType, + Kind: common.DeclarationKindType, + } + + script := []byte(` + pub fun main(): X.Y { + return y + } + `) + + runtime := newTestInterpreterRuntime() + + runtimeInterface := &testRuntimeInterface{ + storage: newTestLedger(nil, nil), + getSigningAccounts: func() ([]Address, error) { + return []Address{common.MustBytesToAddress([]byte{0x1})}, nil + }, + resolveLocation: singleIdentifierLocationResolver(t), + } + + // Run script + + scriptEnvironment := NewScriptInterpreterEnvironment(Config{}) + scriptEnvironment.DeclareValue(valueDeclaration) + scriptEnvironment.DeclareType(typeDeclaration) + + result, err := runtime.ExecuteScript( + Script{ + Source: script, + }, + Context{ + Interface: runtimeInterface, + Location: common.ScriptLocation{}, + Environment: scriptEnvironment, + }, + ) + require.NoError(t, err) + + require.Equal(t, + cadence.ValueWithCachedTypeID( + cadence.Struct{ + StructType: cadence.NewStructType(nil, yType.QualifiedIdentifier(), []cadence.Field{}, nil), + Fields: []cadence.Value{}, + }, + ), + result, + ) + }) + + t.Run("composite type, nested, non-existing", func(t *testing.T) { + t.Parallel() + + yType := &sema.CompositeType{ + Identifier: "Y", + Kind: common.CompositeKindStructure, + Members: &sema.StringMemberOrderedMap{}, + } + + xType := &sema.CompositeType{ + Identifier: "X", + Kind: common.CompositeKindContract, + Members: &sema.StringMemberOrderedMap{}, + } + + xType.SetNestedType(yType.Identifier, yType) + + valueDeclaration := stdlib.StandardLibraryValue{ + Name: "y", + Type: yType, + Kind: common.DeclarationKindConstant, + Value: interpreter.NewSimpleCompositeValue(nil, + yType.ID(), + interpreter.ConvertSemaCompositeTypeToStaticCompositeType(nil, yType), + nil, + nil, + nil, + nil, + nil, + ), + } + + script := []byte(` + pub fun main(): AnyStruct { + return y + } + `) + + runtime := newTestInterpreterRuntime() + + runtimeInterface := &testRuntimeInterface{ + storage: newTestLedger(nil, nil), + getSigningAccounts: func() ([]Address, error) { + return []Address{common.MustBytesToAddress([]byte{0x1})}, nil + }, + resolveLocation: singleIdentifierLocationResolver(t), + } + + // Run script + + scriptEnvironment := NewScriptInterpreterEnvironment(Config{}) + scriptEnvironment.DeclareValue(valueDeclaration) + + _, err := runtime.ExecuteScript( + Script{ + Source: script, + }, + Context{ + Interface: runtimeInterface, + Location: common.ScriptLocation{}, + Environment: scriptEnvironment, + }, + ) + RequireError(t, err) + + var typeLoadingErr interpreter.TypeLoadingError + require.ErrorAs(t, err, &typeLoadingErr) + }) - valueDeclaration := stdlib.StandardLibraryValue{ - Name: "x", - Type: xType, - Kind: common.DeclarationKindConstant, - Value: interpreter.NewUnmeteredIntValueFromInt64(2), - } - - typeDeclaration := stdlib.StandardLibraryType{ - Name: "X", - Type: xType, - Kind: common.DeclarationKindType, - } - - script := []byte(` - pub fun main(): X { - return x - } - `) - - runtime := newTestInterpreterRuntime() - - runtimeInterface := &testRuntimeInterface{ - storage: newTestLedger(nil, nil), - getSigningAccounts: func() ([]Address, error) { - return []Address{common.MustBytesToAddress([]byte{0x1})}, nil - }, - resolveLocation: singleIdentifierLocationResolver(t), - } - - // Run script - - scriptEnvironment := NewScriptInterpreterEnvironment(Config{}) - scriptEnvironment.DeclareValue(valueDeclaration) - scriptEnvironment.DeclareType(typeDeclaration) - - result, err := runtime.ExecuteScript( - Script{ - Source: script, - }, - Context{ - Interface: runtimeInterface, - Location: common.ScriptLocation{}, - Environment: scriptEnvironment, - }, - ) - require.NoError(t, err) - - require.Equal(t, - cadence.Int{Value: big.NewInt(2)}, - result, - ) } diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 9e2ab8b422..4eb524bc5e 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -31,6 +31,8 @@ import ( "github.com/onflow/cadence/runtime/errors" ) +const TypeIDSeparator = '.' + func qualifiedIdentifier(identifier string, containerType Type) string { if containerType == nil { return identifier @@ -56,7 +58,7 @@ func qualifiedIdentifier(identifier string, containerType Type) string { for i := len(identifiers) - 1; i >= 0; i-- { sb.WriteString(identifiers[i]) if i != 0 { - sb.WriteByte('.') + sb.WriteByte(TypeIDSeparator) } } @@ -229,6 +231,36 @@ func VisitThisAndNested(t Type, visit func(ty Type)) { }) } +func TypeActivationNestedType(typeActivation *VariableActivation, qualifiedIdentifier string) Type { + + typeIDComponents := strings.Split(qualifiedIdentifier, string(TypeIDSeparator)) + + rootTypeName := typeIDComponents[0] + variable := typeActivation.Find(rootTypeName) + if variable == nil { + return nil + } + ty := variable.Type + + // Traverse nested types until the leaf type + + for i := 1; i < len(typeIDComponents); i++ { + containerType, ok := ty.(ContainerType) + if !ok || !containerType.IsContainerType() { + return nil + } + + typeIDComponent := typeIDComponents[i] + + ty, ok = containerType.GetNestedTypes().Get(typeIDComponent) + if !ok { + return nil + } + } + + return ty +} + // CompositeKindedType is a type which has a composite kind type CompositeKindedType interface { Type From 9e8068e275f97ba97e1e5e361fa6a48bbb36fd3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 26 Sep 2023 15:10:18 -0700 Subject: [PATCH 10/12] use kr/pretty instead of go-test/deep to prevent empty diffs --- go.mod | 17 +++++------ go.sum | 10 +++++-- runtime/tests/checker/utils.go | 13 ++++---- runtime/tests/utils/utils.go | 55 +++++++++++++++++----------------- tools/compare-parsing/main.go | 10 ++----- 5 files changed, 50 insertions(+), 55 deletions(-) diff --git a/go.mod b/go.mod index 6dfc1fb807..3a9ac1bf22 100644 --- a/go.mod +++ b/go.mod @@ -4,14 +4,19 @@ go 1.20 require ( github.com/bits-and-blooms/bitset v1.5.0 + github.com/bytecodealliance/wasmtime-go/v7 v7.0.0 github.com/c-bata/go-prompt v0.2.6 + github.com/dave/dst v0.27.2 github.com/fxamacker/cbor/v2 v2.4.1-0.20230228173756-c0c9f774e40c - github.com/go-test/deep v1.1.0 + github.com/k0kubun/pp/v3 v3.2.0 + github.com/kr/pretty v0.3.1 github.com/leanovate/gopter v0.2.9 + github.com/logrusorgru/aurora/v4 v4.0.0 github.com/onflow/atree v0.6.0 github.com/rivo/uniseg v0.4.4 github.com/schollz/progressbar/v3 v3.13.1 github.com/stretchr/testify v1.8.2 + github.com/texttheater/golang-levenshtein/levenshtein v0.0.0-20200805054039-cae8b0eaed6c github.com/tidwall/pretty v1.2.1 github.com/turbolent/prettier v0.0.0-20220320183459-661cc755135d go.opentelemetry.io/otel v1.14.0 @@ -23,14 +28,6 @@ require ( golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 ) -require ( - github.com/bytecodealliance/wasmtime-go/v7 v7.0.0 - github.com/dave/dst v0.27.2 - github.com/k0kubun/pp/v3 v3.2.0 - github.com/logrusorgru/aurora/v4 v4.0.0 - github.com/texttheater/golang-levenshtein/levenshtein v0.0.0-20200805054039-cae8b0eaed6c -) - require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/fxamacker/circlehash v0.3.0 // indirect @@ -43,7 +40,9 @@ require ( github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect github.com/pkg/term v1.2.0-beta.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/x448/float16 v0.8.4 // indirect + github.com/zeebo/assert v1.3.0 // indirect github.com/zeebo/blake3 v0.2.3 // indirect golang.org/x/sys v0.6.0 // indirect golang.org/x/term v0.6.0 // indirect diff --git a/go.sum b/go.sum index 95f5ee6672..9dbb7f2d65 100644 --- a/go.sum +++ b/go.sum @@ -15,8 +15,6 @@ github.com/fxamacker/cbor/v2 v2.4.1-0.20230228173756-c0c9f774e40c h1:5tm/Wbs9d9r github.com/fxamacker/cbor/v2 v2.4.1-0.20230228173756-c0c9f774e40c/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/fxamacker/circlehash v0.3.0 h1:XKdvTtIJV9t7DDUtsf0RIpC1OcxZtPbmgIH7ekx28WA= github.com/fxamacker/circlehash v0.3.0/go.mod h1:3aq3OfVvsWtkWMb6A1owjOQFA+TLsD5FgJflnaQwtMM= -github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= -github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= github.com/k0kubun/pp/v3 v3.2.0 h1:h33hNTZ9nVFNP3u2Fsgz8JXiF5JINoZfFq4SvKJwNcs= @@ -24,6 +22,8 @@ github.com/k0kubun/pp/v3 v3.2.0/go.mod h1:ODtJQbQcIRfAD3N+theGCV1m/CBxweERz2dapd github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= @@ -54,6 +54,7 @@ github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/onflow/atree v0.6.0 h1:j7nQ2r8npznx4NX39zPpBYHmdy45f4xwoi+dm37Jk7c= github.com/onflow/atree v0.6.0/go.mod h1:gBHU0M05qCbv9NN0kijLWMgC47gHVNBIp4KmsVFi0tc= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/term v1.2.0-beta.2 h1:L3y/h2jkuBVFdWiJvNfYfKmzcCnILw7mJWm2JQuMppw= github.com/pkg/term v1.2.0-beta.2/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -61,6 +62,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/schollz/progressbar/v3 v3.13.1 h1:o8rySDYiQ59Mwzy2FELeHY5ZARXZTVJC7iHD6PEFUiE= github.com/schollz/progressbar/v3 v3.13.1/go.mod h1:xvrbki8kfT1fzWzBT/UZd9L6GA+jdL7HAgq2RFnO6fQ= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= @@ -81,8 +84,9 @@ github.com/turbolent/prettier v0.0.0-20220320183459-661cc755135d h1:5JInRQbk5UBX github.com/turbolent/prettier v0.0.0-20220320183459-661cc755135d/go.mod h1:Nlx5Y115XQvNcIdIy7dZXaNSUpzwBSge4/Ivk93/Yog= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= +github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg= github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ= github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= diff --git a/runtime/tests/checker/utils.go b/runtime/tests/checker/utils.go index d63c4a5f6d..dd20f5fe94 100644 --- a/runtime/tests/checker/utils.go +++ b/runtime/tests/checker/utils.go @@ -24,7 +24,8 @@ import ( "sync" "testing" - "github.com/go-test/deep" + gopretty "github.com/kr/pretty" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -35,10 +36,6 @@ import ( "github.com/onflow/cadence/runtime/tests/utils" ) -func init() { - deep.MaxDepth = 20 -} - func ParseAndCheck(t testing.TB, code string) (*sema.Checker, error) { return ParseAndCheckWithOptions(t, code, ParseAndCheckOptions{ // allow attachments is on by default for testing purposes @@ -159,9 +156,9 @@ func ParseAndCheckWithOptionsAndMemoryMetering( err = firstResult.err for otherResult := range results { - diff := deep.Equal(err, otherResult.err) - if diff != nil { - t.Error(diff) + diff := gopretty.Diff(err, otherResult.err) + if len(diff) > 0 { + t.Error(strings.Join(diff, "\n")) } } diff --git a/runtime/tests/utils/utils.go b/runtime/tests/utils/utils.go index 8835a83c6b..04a4476e44 100644 --- a/runtime/tests/utils/utils.go +++ b/runtime/tests/utils/utils.go @@ -24,7 +24,8 @@ import ( "strings" "testing" - "github.com/go-test/deep" + "github.com/k0kubun/pp/v3" + "github.com/kr/pretty" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -46,38 +47,36 @@ const ImportedLocation = common.StringLocation("imported") // // If the objects are not equal, this function prints a human-readable diff. func AssertEqualWithDiff(t *testing.T, expected, actual any) { - if !assert.Equal(t, expected, actual) { - // the maximum levels of a struct to recurse into - // this prevents infinite recursion from circular references - deep.MaxDepth = 100 - diff := deep.Equal(expected, actual) + // the maximum levels of a struct to recurse into + // this prevents infinite recursion from circular references + diff := pretty.Diff(expected, actual) - if len(diff) != 0 { - s := strings.Builder{} + if len(diff) != 0 { + s := strings.Builder{} - for i, d := range diff { - if i == 0 { - s.WriteString("diff : ") - } else { - s.WriteString(" ") - } - - s.WriteString(d) - s.WriteString("\n") + for i, d := range diff { + if i == 0 { + s.WriteString("diff : ") + } else { + s.WriteString(" ") } - t.Errorf( - "Not equal: \n"+ - "expected: %s\n"+ - "actual : %s\n\n"+ - "%s", - expected, - actual, - s.String(), - ) + s.WriteString(d) + s.WriteString("\n") } + + t.Errorf( + "Not equal: \n"+ + "expected: %s\n"+ + "actual : %s\n\n"+ + "%s", + pp.Sprint(expected), + pp.Sprint(actual), + s.String(), + ) } + } func AsInterfaceType(name string, kind common.CompositeKind) string { @@ -147,11 +146,11 @@ func ValuesAreEqual(inter *interpreter.Interpreter, expected, actual interpreter func AssertValuesEqual(t testing.TB, interpreter *interpreter.Interpreter, expected, actual interpreter.Value) bool { if !ValuesAreEqual(interpreter, expected, actual) { - diff := deep.Equal(expected, actual) + diff := pretty.Diff(expected, actual) var message string - if len(diff) != 0 { + if len(diff) > 0 { s := strings.Builder{} _, _ = fmt.Fprintf(&s, "Not equal: \n"+ diff --git a/tools/compare-parsing/main.go b/tools/compare-parsing/main.go index 9de50d73c7..36ea0cfab5 100644 --- a/tools/compare-parsing/main.go +++ b/tools/compare-parsing/main.go @@ -35,7 +35,7 @@ import ( "path" "strings" - "github.com/go-test/deep" + "github.com/kr/pretty" ) func main() { @@ -115,13 +115,9 @@ func compareParsing(directory string, location string, code string, parseOld str return } - // the maximum levels of a struct to recurse into - // this prevents infinite recursion from circular references - deep.MaxDepth = 100 + diff := pretty.Diff(res1, res2) - diff := deep.Equal(res1, res2) - - if len(diff) != 0 { + if len(diff) > 0 { var s strings.Builder for _, d := range diff { From 74e0b2d486eba26c7326247e41d72866910726c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 27 Sep 2023 10:01:05 -0700 Subject: [PATCH 11/12] remove gocap --- .github/workflows/ci.yml | 3 --- Makefile | 6 ------ go.cap | 13 ------------- 3 files changed, 22 deletions(-) delete mode 100644 go.cap diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5a5e483d72..238f823442 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,9 +73,6 @@ jobs: - name: Check license headers run: make check-headers - - name: Check capabilities of dependencies - run: make check-capabilities - lint-json: name: Lint JSON runs-on: ubuntu-latest diff --git a/Makefile b/Makefile index 327d437533..f822d9b20c 100644 --- a/Makefile +++ b/Makefile @@ -121,9 +121,3 @@ release: @(VERSIONED_FILES="version.go \ npm-packages/cadence-parser/package.json" \ bash ./bump-version.sh $(bump)) - -.PHONY: check-capabilities -check-capabilities: - go install github.com/cugu/gocap@v0.1.0 - go mod download - gocap check . diff --git a/go.cap b/go.cap deleted file mode 100644 index bb341c9fa6..0000000000 --- a/go.cap +++ /dev/null @@ -1,13 +0,0 @@ -github.com/onflow/cadence () - -github.com/davecgh/go-spew/spew (file) -github.com/klauspost/cpuid/v2 (file, runtime) -github.com/onflow/cadence/runtime/errors (runtime) -github.com/onflow/cadence/runtime/parser (file) -github.com/onflow/cadence/runtime/pretty (runtime) -github.com/stretchr/testify/assert (runtime, file, network) -github.com/stretchr/testify/require (network) -github.com/texttheater/golang-levenshtein/levenshtein (file) -github.com/zeebo/blake3/internal/consts (file) -golang.org/x/xerrors (runtime) -github.com/onflow/cadence/runtime/interpreter (runtime) From a03a45200ede2ee9ebeae0a8c56c049c649fe00e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 27 Sep 2023 11:49:46 -0700 Subject: [PATCH 12/12] fix lint --- runtime/stdlib/account.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index 17ae64b93f..f252312860 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -3765,7 +3765,7 @@ func newCapabilityControllerGetCapabilityFunction( capabilityID := controller.ControllerCapabilityID() borrowType := controller.CapabilityControllerBorrowType() - return func(inter *interpreter.Interpreter) *interpreter.CapabilityValue { + return func(inter *interpreter.Interpreter) *interpreter.CapabilityValue { return interpreter.NewCapabilityValue( inter, capabilityID,