Skip to content

Commit

Permalink
Merge pull request #3634 from onflow/bastian/resolve-value-import-loc…
Browse files Browse the repository at this point in the history
…ation
  • Loading branch information
turbolent authored Oct 21, 2024
2 parents 8f1bb54 + 6585620 commit 1662018
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 24 deletions.
1 change: 1 addition & 0 deletions runtime/contract_function_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ func (executor *interpreterContractFunctionExecutor) convertArgument(
inter,
locationRange,
environment,
environment.ResolveLocation,
argument,
argumentType,
)
Expand Down
32 changes: 32 additions & 0 deletions runtime/convertValues.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ package runtime

import (
"math/big"
"strings"
_ "unsafe"

"github.com/onflow/cadence"
"github.com/onflow/cadence/ast"
"github.com/onflow/cadence/common"
"github.com/onflow/cadence/errors"
"github.com/onflow/cadence/interpreter"
Expand Down Expand Up @@ -792,20 +794,23 @@ type valueImporter struct {
inter *interpreter.Interpreter
locationRange interpreter.LocationRange
standardLibraryHandler stdlib.StandardLibraryHandler
resolveLocation sema.LocationHandlerFunc
}

// ImportValue converts a Cadence value to a runtime value.
func ImportValue(
inter *interpreter.Interpreter,
locationRange interpreter.LocationRange,
standardLibraryHandler stdlib.StandardLibraryHandler,
resolveLocation sema.LocationHandlerFunc,
value cadence.Value,
expectedType sema.Type,
) (interpreter.Value, error) {
return valueImporter{
inter: inter,
locationRange: locationRange,
standardLibraryHandler: standardLibraryHandler,
resolveLocation: resolveLocation,
}.importValue(value, expectedType)
}

Expand Down Expand Up @@ -1509,6 +1514,33 @@ func (i valueImporter) importCompositeValue(
inter := i.inter
locationRange := i.locationRange

// Resolve the location if it is not nil (not a built-in type)

if location != nil {
resolveLocation := i.resolveLocation
if resolveLocation != nil {

rootIdentifier := strings.SplitN(qualifiedIdentifier, ".", 2)[0]

resolvedLocations, err := resolveLocation(
[]ast.Identifier{{Identifier: rootIdentifier}},
location,
)
if err != nil {
return nil, err
}

if len(resolvedLocations) != 1 {
return nil, errors.NewDefaultUserError(
"cannot import value of type %s: location resolution failed",
qualifiedIdentifier,
)
}

location = resolvedLocations[0].Location
}
}

typeID := common.NewTypeIDFromQualifiedName(inter, location, qualifiedIdentifier)
compositeType, typeErr := inter.GetCompositeType(location, qualifiedIdentifier, typeID)
if typeErr != nil {
Expand Down
26 changes: 16 additions & 10 deletions runtime/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ type Environment interface {
)
CommitStorage(inter *interpreter.Interpreter) error
NewAccountValue(inter *interpreter.Interpreter, address interpreter.AddressValue) interpreter.Value

ResolveLocation(identifiers []ast.Identifier, location common.Location) ([]ResolvedLocation, error)
}

// interpreterEnvironmentReconfigured is the portion of interpreterEnvironment
Expand Down Expand Up @@ -206,7 +208,7 @@ func (e *interpreterEnvironment) newCheckerConfig() *sema.Config {
BaseValueActivationHandler: e.getBaseValueActivation,
BaseTypeActivationHandler: e.getBaseTypeActivation,
ValidTopLevelDeclarationsHandler: validTopLevelDeclarations,
LocationHandler: e.newLocationHandler(),
LocationHandler: e.ResolveLocation,
ImportHandler: e.resolveImport,
CheckHandler: e.newCheckHandler(),
AttachmentsEnabled: e.config.AttachmentsEnabled,
Expand Down Expand Up @@ -644,16 +646,20 @@ func (e *interpreterEnvironment) check(
return elaboration, nil
}

func (e *interpreterEnvironment) newLocationHandler() sema.LocationHandlerFunc {
return func(identifiers []Identifier, location Location) (res []ResolvedLocation, err error) {
errors.WrapPanic(func() {
res, err = e.runtimeInterface.ResolveLocation(identifiers, location)
})
if err != nil {
err = interpreter.WrappedExternalError(err)
}
return
func (e *interpreterEnvironment) ResolveLocation(
identifiers []Identifier,
location Location,
) (
res []ResolvedLocation,
err error,
) {
errors.WrapPanic(func() {
res, err = e.runtimeInterface.ResolveLocation(identifiers, location)
})
if err != nil {
err = interpreter.WrappedExternalError(err)
}
return
}

func (e *interpreterEnvironment) newCheckHandler() sema.CheckHandlerFunc {
Expand Down
2 changes: 2 additions & 0 deletions runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ func UserPanicToError(f func()) (returnedError error) {

type ArgumentDecoder interface {
stdlib.StandardLibraryHandler
ResolveLocation(identifiers []ast.Identifier, location common.Location) ([]ResolvedLocation, error)

// DecodeArgument decodes a transaction/script argument against the given type.
DecodeArgument(argument []byte, argumentType cadence.Type) (cadence.Value, error)
Expand Down Expand Up @@ -414,6 +415,7 @@ func validateArgumentParams(
inter,
locationRange,
decoder,
decoder.ResolveLocation,
value,
parameterType,
)
Expand Down
107 changes: 107 additions & 0 deletions tests/convertValues_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/stretchr/testify/require"

"github.com/onflow/cadence"
"github.com/onflow/cadence/ast"
"github.com/onflow/cadence/common"
"github.com/onflow/cadence/encoding/json"
"github.com/onflow/cadence/errors"
Expand Down Expand Up @@ -628,6 +629,7 @@ func TestRuntimeImportValue(t *testing.T) {
inter,
interpreter.EmptyLocationRange,
nil,
nil,
tt.value,
tt.expectedType,
)
Expand Down Expand Up @@ -1367,6 +1369,7 @@ func TestImportInclusiveRangeValue(t *testing.T) {
inter,
interpreter.EmptyLocationRange,
nil,
nil,
value,
sema.NewInclusiveRangeType(inter, sema.IntType),
)
Expand Down Expand Up @@ -1401,6 +1404,7 @@ func TestImportInclusiveRangeValue(t *testing.T) {
inter,
interpreter.EmptyLocationRange,
nil,
nil,
value,
sema.AnyStructType,
)
Expand Down Expand Up @@ -1435,6 +1439,7 @@ func TestImportInclusiveRangeValue(t *testing.T) {
inter,
interpreter.EmptyLocationRange,
nil,
nil,
value,
sema.AnyStructType,
)
Expand Down Expand Up @@ -1465,6 +1470,7 @@ func TestImportInclusiveRangeValue(t *testing.T) {
inter,
interpreter.EmptyLocationRange,
nil,
nil,
value,
sema.StringType,
)
Expand Down Expand Up @@ -3489,6 +3495,7 @@ func TestRuntimeImportExportArrayValue(t *testing.T) {
inter,
interpreter.EmptyLocationRange,
nil,
nil,
value,
sema.ByteArrayType,
)
Expand Down Expand Up @@ -3559,6 +3566,7 @@ func TestRuntimeImportExportArrayValue(t *testing.T) {
inter,
interpreter.EmptyLocationRange,
nil,
nil,
value,
&sema.VariableSizedType{
Type: sema.AnyStructType,
Expand Down Expand Up @@ -3604,6 +3612,7 @@ func TestRuntimeImportExportArrayValue(t *testing.T) {
inter,
interpreter.EmptyLocationRange,
nil,
nil,
value,
sema.AnyStructType,
)
Expand Down Expand Up @@ -3693,6 +3702,7 @@ func TestRuntimeImportExportDictionaryValue(t *testing.T) {
inter,
interpreter.EmptyLocationRange,
nil,
nil,
value,
&sema.DictionaryType{
KeyType: sema.StringType,
Expand Down Expand Up @@ -3779,6 +3789,7 @@ func TestRuntimeImportExportDictionaryValue(t *testing.T) {
inter,
interpreter.EmptyLocationRange,
nil,
nil,
value,
&sema.DictionaryType{
KeyType: sema.StringType,
Expand Down Expand Up @@ -3843,6 +3854,7 @@ func TestRuntimeImportExportDictionaryValue(t *testing.T) {
inter,
interpreter.EmptyLocationRange,
nil,
nil,
value,
sema.AnyStructType,
)
Expand Down Expand Up @@ -3910,6 +3922,7 @@ func TestRuntimeImportExportDictionaryValue(t *testing.T) {
inter,
interpreter.EmptyLocationRange,
nil,
nil,
dictionaryWithHeterogenousKeys,
sema.AnyStructType,
)
Expand Down Expand Up @@ -5023,6 +5036,7 @@ func TestRuntimeImportExportComplex(t *testing.T) {
inter,
interpreter.EmptyLocationRange,
nil,
nil,
externalCompositeValue,
semaCompositeType,
)
Expand Down Expand Up @@ -5552,3 +5566,96 @@ func TestRuntimeExportInterfaceType(t *testing.T) {
require.ErrorAs(t, err, &invalidReturnType)
})
}
func TestRuntimeImportResolvedLocation(t *testing.T) {

t.Parallel()

addressLocation := common.AddressLocation{
Address: common.MustBytesToAddress([]byte{42}),
Name: "Test",
}

identifierLocation := common.IdentifierLocation("Test")

storage := NewUnmeteredInMemoryStorage()

program := &interpreter.Program{
Elaboration: sema.NewElaboration(nil),
}

inter, err := interpreter.NewInterpreter(
program,
addressLocation,
&interpreter.Config{
Storage: storage,
AtreeValueValidationEnabled: true,
AtreeStorageValidationEnabled: true,
},
)
require.NoError(t, err)

semaCompositeType := &sema.CompositeType{
Location: addressLocation,
Identifier: "Foo",
Kind: common.CompositeKindStructure,
Members: &sema.StringMemberOrderedMap{},
}

program.Elaboration.SetCompositeType(
semaCompositeType.ID(),
semaCompositeType,
)

externalCompositeType := cadence.NewStructType(
identifierLocation,
"Foo",
[]cadence.Field{},
nil,
)

externalCompositeValue := cadence.NewStruct(nil).
WithType(externalCompositeType)

resolveLocation := func(
identifiers []ast.Identifier,
location common.Location,
) ([]sema.ResolvedLocation, error) {
require.Equal(t, identifierLocation, location)

location = addressLocation

return []sema.ResolvedLocation{
{
Location: location,
Identifiers: identifiers,
},
}, nil
}

actual, err := ImportValue(
inter,
interpreter.EmptyLocationRange,
nil,
resolveLocation,
externalCompositeValue,
semaCompositeType,
)
require.NoError(t, err)

internalCompositeValue := interpreter.NewCompositeValue(
inter,
interpreter.EmptyLocationRange,
addressLocation,
"Foo",
common.CompositeKindStructure,
nil,
common.ZeroAddress,
)

AssertValuesEqual(
t,
inter,
internalCompositeValue,
actual,
)
}
1 change: 1 addition & 0 deletions tests/deployment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ func TestRuntimeTransactionWithContractDeployment(t *testing.T) {
inter,
interpreter.EmptyLocationRange,
nil,
nil,
codeHashValue,
stdlib.HashType,
)
Expand Down
8 changes: 1 addition & 7 deletions tests/runtime_utils/location.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,7 @@ func NewScriptLocationGenerator() func() common.ScriptLocation {
return NewLocationGenerator[common.ScriptLocation]()
}

func NewSingleIdentifierLocationResolver(t testing.TB) func(
identifiers []runtime.Identifier,
location runtime.Location,
) (
[]runtime.ResolvedLocation,
error,
) {
func NewSingleIdentifierLocationResolver(t testing.TB) sema.LocationHandlerFunc {
return func(
identifiers []runtime.Identifier,
location runtime.Location,
Expand Down
8 changes: 1 addition & 7 deletions tests/runtime_utils/testinterface.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,7 @@ import (
type TestRuntimeInterface struct {
Storage TestLedger

OnResolveLocation func(
identifiers []runtime.Identifier,
location runtime.Location,
) (
[]runtime.ResolvedLocation,
error,
)
OnResolveLocation sema.LocationHandlerFunc
OnGetCode func(_ runtime.Location) ([]byte, error)
OnGetAndSetProgram func(
location runtime.Location,
Expand Down

0 comments on commit 1662018

Please sign in to comment.