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

Revert "Remove support for importing/exporting and JSON encoding/deco… #2381

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
25 changes: 25 additions & 0 deletions encoding/json/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,8 @@ func (d *Decoder) decodeJSON(v any) cadence.Value {
return d.decodeEvent(valueJSON)
case contractTypeStr:
return d.decodeContract(valueJSON)
case linkTypeStr:
return d.decodeLink(valueJSON)
case pathTypeStr:
return d.decodePath(valueJSON)
case typeTypeStr:
Expand Down Expand Up @@ -821,6 +823,29 @@ func (d *Decoder) decodeEnum(valueJSON any) cadence.Enum {
))
}

func (d *Decoder) decodeLink(valueJSON any) cadence.PathLink {
obj := toObject(valueJSON)

targetPath, ok := d.decodeJSON(obj.Get(targetPathKey)).(cadence.Path)
if !ok {
panic(errors.NewDefaultUserError("invalid link: missing or invalid target path"))
}

borrowType := obj.GetString(borrowTypeKey)

common.UseMemory(d.gauge, common.MemoryUsage{
Kind: common.MemoryKindRawString,
// no need to add 1 to account for empty string: string is metered in Link struct
Amount: uint64(len(borrowType)),
})

return cadence.NewMeteredLink(
d.gauge,
targetPath,
borrowType,
)
}

func (d *Decoder) decodePath(valueJSON any) cadence.Path {
obj := toObject(valueJSON)

Expand Down
18 changes: 18 additions & 0 deletions encoding/json/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@ type jsonCompositeField struct {
Name string `json:"name"`
}

type jsonPathLinkValue struct {
TargetPath jsonValue `json:"targetPath"`
BorrowType string `json:"borrowType"`
}

type jsonPathValue struct {
Domain string `json:"domain"`
Identifier string `json:"identifier"`
Expand Down Expand Up @@ -238,6 +243,7 @@ const (
resourceTypeStr = "Resource"
eventTypeStr = "Event"
contractTypeStr = "Contract"
linkTypeStr = "Link"
pathTypeStr = "Path"
typeTypeStr = "Type"
capabilityTypeStr = "Capability"
Expand Down Expand Up @@ -313,6 +319,8 @@ func Prepare(v cadence.Value) jsonValue {
return prepareEvent(x)
case cadence.Contract:
return prepareContract(x)
case cadence.PathLink:
return prepareLink(x)
case cadence.Path:
return preparePath(x)
case cadence.TypeValue:
Expand Down Expand Up @@ -592,6 +600,16 @@ func prepareComposite(kind, id string, fieldTypes []cadence.Field, fields []cade
}
}

func prepareLink(x cadence.PathLink) jsonValue {
return jsonValueObject{
Type: linkTypeStr,
Value: jsonPathLinkValue{
TargetPath: preparePath(x.TargetPath),
BorrowType: x.BorrowType,
},
}
}

func preparePath(x cadence.Path) jsonValue {
return jsonValueObject{
Type: pathTypeStr,
Expand Down
29 changes: 29 additions & 0 deletions encoding/json/encoding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1645,6 +1645,35 @@ func TestEncodeContract(t *testing.T) {
testAllEncodeAndDecode(t, simpleContract, resourceContract)
}

func TestEncodeLink(t *testing.T) {

t.Parallel()

testEncodeAndDecode(
t,
cadence.NewPathLink(
cadence.NewPath("storage", "foo"),
"Bar",
),
// language=json
`
{
"type": "Link",
"value": {
"targetPath": {
"type": "Path",
"value": {
"domain": "storage",
"identifier": "foo"
}
},
"borrowType": "Bar"
}
}
`,
)
}

func TestEncodeSimpleTypes(t *testing.T) {

t.Parallel()
Expand Down
10 changes: 10 additions & 0 deletions runtime/convertValues.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,8 @@ func exportValueWithInterpreter(
)
case interpreter.AddressValue:
return cadence.NewMeteredAddress(inter, v), nil
case interpreter.PathLinkValue:
return exportPathLinkValue(v, inter), nil
case interpreter.PathValue:
return exportPathValue(inter, v), nil
case interpreter.TypeValue:
Expand Down Expand Up @@ -583,6 +585,12 @@ func exportDictionaryValue(
return dictionary.WithType(exportType), err
}

func exportPathLinkValue(v interpreter.PathLinkValue, inter *interpreter.Interpreter) cadence.PathLink {
path := exportPathValue(inter, v.TargetPath)
ty := string(inter.MustConvertStaticToSemaType(v.Type).ID())
return cadence.NewMeteredLink(inter, path, ty)
}

func exportPathValue(gauge common.MemoryGauge, v interpreter.PathValue) cadence.Path {
domain := v.Domain.Identifier()
common.UseMemory(gauge, common.MemoryUsage{
Expand Down Expand Up @@ -802,6 +810,8 @@ func (i valueImporter) importValue(value cadence.Value, expectedType sema.Type)
return nil, errors.NewDefaultUserError("cannot import contract")
case cadence.Function:
return nil, errors.NewDefaultUserError("cannot import function")
case cadence.PathLink:
return nil, errors.NewDefaultUserError("cannot import link")
default:
// This means the implementation has unhandled types.
// Hence, return an internal error
Expand Down
96 changes: 96 additions & 0 deletions runtime/convertValues_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,17 @@ func TestImportValue(t *testing.T) {
Identifier: "foo",
},
},
{
label: "Link (invalid)",
value: cadence.PathLink{
TargetPath: cadence.Path{
Domain: "storage",
Identifier: "test",
},
BorrowType: "Int",
},
expected: nil,
},
{
label: "Capability (invalid)",
value: cadence.StorageCapability{
Expand Down Expand Up @@ -2087,6 +2098,91 @@ func TestExportStorageCapabilityValue(t *testing.T) {
})
}

func TestExportPathLinkValue(t *testing.T) {

t.Parallel()

t.Run("Int", func(t *testing.T) {

link := interpreter.PathLinkValue{
TargetPath: interpreter.PathValue{
Domain: common.PathDomainStorage,
Identifier: "foo",
},
Type: interpreter.PrimitiveStaticTypeInt,
}

actual, err := exportValueWithInterpreter(
link,
newTestInterpreter(t),
interpreter.EmptyLocationRange,
seenReferences{},
)
require.NoError(t, err)

expected := cadence.PathLink{
TargetPath: cadence.Path{
Domain: "storage",
Identifier: "foo",
},
BorrowType: "Int",
}

assert.Equal(t, expected, actual)
})

t.Run("Struct", func(t *testing.T) {

const code = `
struct S {}
`
program, err := parser.ParseProgram(nil, []byte(code), parser.Config{})
require.NoError(t, err)

checker, err := sema.NewChecker(
program,
TestLocation,
nil,
&sema.Config{
AccessCheckMode: sema.AccessCheckModeNotSpecifiedUnrestricted,
},
)
require.NoError(t, err)

err = checker.Check()
require.NoError(t, err)

inter := newTestInterpreter(t)
inter.Program = interpreter.ProgramFromChecker(checker)

capability := interpreter.PathLinkValue{
TargetPath: interpreter.PathValue{
Domain: common.PathDomainStorage,
Identifier: "foo",
},
Type: interpreter.NewCompositeStaticTypeComputeTypeID(inter, TestLocation, "S"),
}

actual, err := exportValueWithInterpreter(
capability,
inter,
interpreter.EmptyLocationRange,
seenReferences{},
)
require.NoError(t, err)

expected := cadence.PathLink{
TargetPath: cadence.Path{
Domain: "storage",
Identifier: "foo",
},
BorrowType: "S.test.S",
}

assert.Equal(t, expected, actual)
})
}

func TestExportCompositeValueWithFunctionValueField(t *testing.T) {

t.Parallel()
Expand Down
43 changes: 43 additions & 0 deletions values.go
Original file line number Diff line number Diff line change
Expand Up @@ -1815,6 +1815,49 @@ func (v Contract) String() string {
return formatComposite(v.ContractType.ID(), v.ContractType.Fields, v.Fields)
}

// PathLink

type PathLink struct {
TargetPath Path
// TODO: a future version might want to export the whole type
BorrowType string
}

var _ Value = PathLink{}

func NewPathLink(targetPath Path, borrowType string) PathLink {
return PathLink{
TargetPath: targetPath,
BorrowType: borrowType,
}
}

func NewMeteredLink(gauge common.MemoryGauge, targetPath Path, borrowType string) PathLink {
common.UseMemory(gauge, common.CadencePathLinkValueMemoryUsage)
return NewPathLink(targetPath, borrowType)
}

func (PathLink) isValue() {}

func (v PathLink) Type() Type {
return nil
}

func (v PathLink) MeteredType(_ common.MemoryGauge) Type {
return v.Type()
}

func (v PathLink) ToGoValue() any {
return nil
}

func (v PathLink) String() string {
return format.PathLink(
v.BorrowType,
v.TargetPath.String(),
)
}

// Path

type Path struct {
Expand Down
18 changes: 18 additions & 0 deletions values_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,16 @@ func TestStringer(t *testing.T) {
}),
expected: "S.test.FooContract(y: \"bar\")",
},
"Link": {
value: NewPathLink(
Path{
Domain: "storage",
Identifier: "foo",
},
"Int",
),
expected: "PathLink<Int>(/storage/foo)",
},
"Path": {
value: Path{
Domain: "storage",
Expand Down Expand Up @@ -627,6 +637,10 @@ func TestGetType(t *testing.T) {
reflect.TypeOf(Struct{}): {},
}

typelessTypes := map[reflect.Type]struct{}{
reflect.TypeOf(PathLink{}): {},
}

var valueInterface Value
valueInterfaceType := reflect.TypeOf(&valueInterface).Elem()

Expand All @@ -648,6 +662,10 @@ func TestGetType(t *testing.T) {
continue
}

if _, ok := typelessTypes[valueType]; ok {
continue
}

valueInstance := reflect.New(valueType)
value := valueInstance.Elem().Interface().(Value)

Expand Down