Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolve location of imported values #3634

Merged
merged 2 commits into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading