Skip to content

Commit

Permalink
Merge pull request #2793 from onflow/bastian/port-internal-143
Browse files Browse the repository at this point in the history
  • Loading branch information
turbolent authored Sep 19, 2023
2 parents ed71b40 + 9d552fc commit b3ff121
Show file tree
Hide file tree
Showing 5 changed files with 357 additions and 12 deletions.
35 changes: 30 additions & 5 deletions runtime/interpreter/simplecompositevalue.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,18 +78,25 @@ func (v *SimpleCompositeValue) Accept(interpreter *Interpreter, visitor Visitor)

// ForEachField iterates over all field-name field-value pairs of the composite value.
// It does NOT iterate over computed fields and functions!
func (v *SimpleCompositeValue) ForEachField(_ *Interpreter, f func(fieldName string, fieldValue Value)) {
func (v *SimpleCompositeValue) ForEachField(
f func(fieldName string, fieldValue Value) (resume bool),
) {
for _, fieldName := range v.FieldNames {
fieldValue := v.Fields[fieldName]
f(fieldName, fieldValue)
if !f(fieldName, fieldValue) {
break
}
}
}

// Walk iterates over all field values of the composite value.
// It does NOT walk the computed fields and functions!
func (v *SimpleCompositeValue) Walk(interpreter *Interpreter, walkChild func(Value)) {
v.ForEachField(interpreter, func(_ string, fieldValue Value) {
func (v *SimpleCompositeValue) Walk(_ *Interpreter, walkChild func(Value)) {
v.ForEachField(func(_ string, fieldValue Value) (resume bool) {
walkChild(fieldValue)

// continue iteration
return true
})
}

Expand All @@ -98,9 +105,27 @@ func (v *SimpleCompositeValue) StaticType(_ *Interpreter) StaticType {
}

func (v *SimpleCompositeValue) IsImportable(inter *Interpreter) bool {
// Check type is importable
staticType := v.StaticType(inter)
semaType := inter.MustConvertStaticToSemaType(staticType)
return semaType.IsImportable(map[*sema.Member]bool{})
if !semaType.IsImportable(map[*sema.Member]bool{}) {
return false
}

// Check all field values are importable
importable := true
v.ForEachField(func(_ string, value Value) (resume bool) {
if !value.IsImportable(inter) {
importable = false
// stop iteration
return false
}

// continue iteration
return true
})

return importable
}

func (v *SimpleCompositeValue) GetMember(
Expand Down
39 changes: 33 additions & 6 deletions runtime/interpreter/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -16395,16 +16395,22 @@ func (v *CompositeValue) Accept(interpreter *Interpreter, visitor Visitor) {
return
}

v.ForEachField(interpreter, func(_ string, value Value) {
v.ForEachField(interpreter, func(_ string, value Value) (resume bool) {
value.Accept(interpreter, visitor)

// continue iteration
return true
})
}

// Walk iterates over all field values of the composite value.
// It does NOT walk the computed field or functions!
func (v *CompositeValue) Walk(interpreter *Interpreter, walkChild func(Value)) {
v.ForEachField(interpreter, func(_ string, value Value) {
v.ForEachField(interpreter, func(_ string, value Value) (resume bool) {
walkChild(value)

// continue iteration
return true
})
}

Expand All @@ -16423,9 +16429,27 @@ func (v *CompositeValue) StaticType(interpreter *Interpreter) StaticType {
}

func (v *CompositeValue) IsImportable(inter *Interpreter) bool {
// Check type is importable
staticType := v.StaticType(inter)
semaType := inter.MustConvertStaticToSemaType(staticType)
return semaType.IsImportable(map[*sema.Member]bool{})
if !semaType.IsImportable(map[*sema.Member]bool{}) {
return false
}

// Check all field values are importable
importable := true
v.ForEachField(inter, func(_ string, value Value) (resume bool) {
if !value.IsImportable(inter) {
importable = false
// stop iteration
return false
}

// continue iteration
return true
})

return importable
}

func (v *CompositeValue) IsDestroyed() bool {
Expand Down Expand Up @@ -17452,14 +17476,17 @@ func (v *CompositeValue) GetOwner() common.Address {

// ForEachField iterates over all field-name field-value pairs of the composite value.
// It does NOT iterate over computed fields and functions!
func (v *CompositeValue) ForEachField(gauge common.MemoryGauge, f func(fieldName string, fieldValue Value)) {
func (v *CompositeValue) ForEachField(
gauge common.MemoryGauge,
f func(fieldName string, fieldValue Value) (resume bool),
) {

err := v.dictionary.Iterate(func(key atree.Value, value atree.Value) (resume bool, err error) {
f(
resume = f(
string(key.(StringAtreeValue)),
MustConvertStoredValue(gauge, value),
)
return true, nil
return
})
if err != nil {
panic(errors.NewExternalError(err))
Expand Down
143 changes: 143 additions & 0 deletions runtime/program_params_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1141,4 +1141,147 @@ func TestRuntimeTransactionParameterTypeValidation(t *testing.T) {
var entryPointErr *InvalidEntryPointArgumentError
require.ErrorAs(t, err, &entryPointErr)
})

t.Run("Invalid private cap in struct", func(t *testing.T) {
t.Parallel()

contracts := map[common.AddressLocation][]byte{
{
Address: common.MustBytesToAddress([]byte{0x1}),
Name: "C",
}: []byte(`
pub contract C {
pub struct S {
pub let cap: Capability
init(cap: Capability) {
self.cap = cap
}
}
}
`),
}

script := `
import C from 0x1
transaction(arg: C.S) {}
`

address := common.MustBytesToAddress([]byte{0x1})

path, err := cadence.NewPath(common.PathDomainPrivate, "foo")
require.NoError(t, err)

capability := cadence.NewPathCapability(
cadence.Address(address),
path,
cadence.NewReferenceType(false, cadence.AuthAccountType{}),
)

arg := cadence.Struct{
StructType: &cadence.StructType{
Location: common.AddressLocation{
Address: address,
Name: "C",
},
QualifiedIdentifier: "C.S",
Fields: []cadence.Field{
{
Identifier: "cap",
Type: &cadence.CapabilityType{},
},
},
},
Fields: []cadence.Value{
capability,
},
}

err = executeTransaction(t, script, contracts, arg)
expectRuntimeError(t, err, &ArgumentNotImportableError{})
})

t.Run("Invalid private cap in array", func(t *testing.T) {
t.Parallel()

script := `
transaction(arg: [AnyStruct]) {}
`

address := common.MustBytesToAddress([]byte{0x1})

path, err := cadence.NewPath(common.PathDomainPrivate, "foo")
require.NoError(t, err)

capability := cadence.NewPathCapability(
cadence.Address(address),
path,
cadence.NewReferenceType(false, cadence.AuthAccountType{}),
)

arg := cadence.Array{
ArrayType: cadence.NewVariableSizedArrayType(cadence.AnyStructType{}),
Values: []cadence.Value{
capability,
},
}

err = executeTransaction(t, script, nil, arg)
expectRuntimeError(t, err, &ArgumentNotImportableError{})
})

t.Run("Invalid private cap in optional", func(t *testing.T) {
t.Parallel()

script := `
transaction(arg: AnyStruct?) {}
`

address := common.MustBytesToAddress([]byte{0x1})

path, err := cadence.NewPath(common.PathDomainPrivate, "foo")
require.NoError(t, err)

capability := cadence.NewPathCapability(
cadence.Address(address),
path,
cadence.NewReferenceType(false, cadence.AuthAccountType{}),
)

arg := cadence.NewOptional(capability)

err = executeTransaction(t, script, nil, arg)
expectRuntimeError(t, err, &ArgumentNotImportableError{})
})

t.Run("Invalid private cap in dictionary value", func(t *testing.T) {
t.Parallel()

script := `
transaction(arg: {String: AnyStruct}) {}
`

address := common.MustBytesToAddress([]byte{0x1})

path, err := cadence.NewPath(common.PathDomainPrivate, "foo")
require.NoError(t, err)

capability := cadence.NewPathCapability(
cadence.Address(address),
path,
cadence.NewReferenceType(false, cadence.AuthAccountType{}),
)

arg := cadence.NewDictionary([]cadence.KeyValuePair{
{
Key: cadence.String("cap"),
Value: capability,
},
})

err = executeTransaction(t, script, nil, arg)
expectRuntimeError(t, err, &ArgumentNotImportableError{})
})

}
Loading

0 comments on commit b3ff121

Please sign in to comment.