Skip to content

Commit

Permalink
Merge pull request #2883 from onflow/sainati/entitlement-mapping-syntax
Browse files Browse the repository at this point in the history
Add required `mapping` keyword to entitlement mapping usages
  • Loading branch information
dsainati1 authored Oct 24, 2023
2 parents 685f6bf + 9f2ed22 commit 74af553
Show file tree
Hide file tree
Showing 17 changed files with 771 additions and 590 deletions.
82 changes: 61 additions & 21 deletions runtime/ast/access.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ func (s Separator) String() string {
}

type EntitlementSet interface {
Authorization
Entitlements() []*NominalType
Separator() Separator
}
Expand All @@ -64,6 +65,8 @@ type ConjunctiveEntitlementSet struct {

var _ EntitlementSet = &ConjunctiveEntitlementSet{}

func (ConjunctiveEntitlementSet) isAuthorization() {}

func (s *ConjunctiveEntitlementSet) Entitlements() []*NominalType {
return s.Elements
}
Expand All @@ -82,6 +85,8 @@ type DisjunctiveEntitlementSet struct {

var _ EntitlementSet = &DisjunctiveEntitlementSet{}

func (DisjunctiveEntitlementSet) isAuthorization() {}

func (s *DisjunctiveEntitlementSet) Entitlements() []*NominalType {
return s.Elements
}
Expand All @@ -94,6 +99,10 @@ func NewDisjunctiveEntitlementSet(entitlements []*NominalType) *DisjunctiveEntit
return &DisjunctiveEntitlementSet{Elements: entitlements}
}

type Authorization interface {
isAuthorization()
}

type EntitlementAccess struct {
EntitlementSet EntitlementSet
}
Expand Down Expand Up @@ -121,7 +130,7 @@ func (e EntitlementAccess) entitlementsString(prefix *strings.Builder) {

func (e EntitlementAccess) String() string {
str := &strings.Builder{}
str.WriteString("ConjunctiveEntitlementAccess ")
str.WriteString("EntitlementAccess ")
e.entitlementsString(str)
return str.String()
}
Expand All @@ -138,33 +147,64 @@ func (e EntitlementAccess) MarshalJSON() ([]byte, error) {
return json.Marshal(e.String())
}

func (e EntitlementAccess) subset(other EntitlementAccess) bool {
otherEntitlements := other.EntitlementSet.Entitlements()
otherSet := make(map[*NominalType]struct{}, len(otherEntitlements))
for _, entitlement := range otherEntitlements {
otherSet[entitlement] = struct{}{}
}
type MappedAccess struct {
EntitlementMap *NominalType
StartPos Position
}

for _, entitlement := range e.EntitlementSet.Entitlements() {
if _, found := otherSet[entitlement]; !found {
return false
}
}
var _ Access = &MappedAccess{}

func (*MappedAccess) isAccess() {}
func (*MappedAccess) isAuthorization() {}

return true
func (*MappedAccess) Description() string {
return "entitlement-mapped access"
}

func (e EntitlementAccess) IsLessPermissiveThan(other Access) bool {
switch other := other.(type) {
case PrimitiveAccess:
return other == AccessAll
case EntitlementAccess:
return e.subset(other)
default:
return false
func NewMappedAccess(
typ *NominalType,
startPos Position,
) *MappedAccess {
return &MappedAccess{
EntitlementMap: typ,
StartPos: startPos,
}
}

func (t *MappedAccess) StartPosition() Position {
return t.StartPos
}

func (t *MappedAccess) EndPosition(memoryGauge common.MemoryGauge) Position {
return t.EntitlementMap.EndPosition(memoryGauge)
}

func (e *MappedAccess) String() string {
var str strings.Builder
str.WriteString("mapping ")
str.WriteString(e.EntitlementMap.String())
return str.String()
}

func (e *MappedAccess) Keyword() string {
var str strings.Builder
str.WriteString("access(")
str.WriteString(e.String())
str.WriteString(")")
return str.String()
}

func (e *MappedAccess) MarshalJSON() ([]byte, error) {
type Alias MappedAccess
return json.Marshal(&struct {
*Alias
Range
}{
Range: NewUnmeteredRangeFromPositioned(e),
Alias: (*Alias)(e),
})
}

type PrimitiveAccess uint8

// NOTE: order indicates permissiveness: from least to most permissive!
Expand Down
25 changes: 25 additions & 0 deletions runtime/ast/access_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,31 @@ func TestPrimitiveAccess_MarshalJSON(t *testing.T) {
}
}

func TestMappedAccess_MarshalJSON(t *testing.T) {

t.Parallel()

e := NewNominalType(nil, NewIdentifier(nil, "E", Position{Offset: 1, Line: 2, Column: 3}), []Identifier{})

access := NewMappedAccess(e, Position{Offset: 0, Line: 0, Column: 0})
actual, err := json.Marshal(access)
require.NoError(t, err)

assert.JSONEq(t, `{
"EntitlementMap": {
"Type": "NominalType",
"Identifier": {
"Identifier": "E",
"StartPos": {"Offset": 1, "Line": 2, "Column": 3},
"EndPos": {"Offset": 1, "Line": 2, "Column": 3}
},
"StartPos": {"Offset": 1, "Line": 2, "Column": 3},
"EndPos": {"Offset": 1, "Line": 2, "Column": 3}
},
"EndPos": {"Offset": 1, "Line": 2, "Column": 3}
}`, string(actual))
}

func TestEntitlementAccess_MarshalJSON(t *testing.T) {

t.Parallel()
Expand Down
48 changes: 28 additions & 20 deletions runtime/ast/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/turbolent/prettier"

"github.com/onflow/cadence/runtime/common"
"github.com/onflow/cadence/runtime/errors"
)

const typeSeparatorSpaceDoc = prettier.Text(": ")
Expand Down Expand Up @@ -535,22 +536,17 @@ func (t *FunctionType) CheckEqual(other Type, checker TypeEqualityChecker) error
}

// ReferenceType

type Authorization struct {
EntitlementSet EntitlementSet `json:"EntitlementSet"`
}

type ReferenceType struct {
Type Type `json:"ReferencedType"`
StartPos Position `json:"-"`
Authorization *Authorization `json:"Authorization"`
Type Type `json:"ReferencedType"`
StartPos Position `json:"-"`
Authorization Authorization `json:"Authorization"`
}

var _ Type = &ReferenceType{}

func NewReferenceType(
memoryGauge common.MemoryGauge,
authorization *Authorization,
authorization Authorization,
typ Type,
startPos Position,
) *ReferenceType {
Expand All @@ -577,26 +573,38 @@ func (t *ReferenceType) EndPosition(memoryGauge common.MemoryGauge) Position {
}

const referenceTypeAuthKeywordDoc = prettier.Text("auth")
const referenceTypeMappingKeywordDoc = prettier.Text("mapping ")
const referenceTypeSymbolDoc = prettier.Text("&")

func (t *ReferenceType) Doc() prettier.Doc {
var doc prettier.Concat
if t.Authorization != nil {
doc = append(doc, referenceTypeAuthKeywordDoc)
entitlementSet := t.Authorization.EntitlementSet
if entitlementSet != nil && len(entitlementSet.Entitlements()) > 0 {
entitlements := entitlementSet.Entitlements()
// TODO: add indentation, improve separators. follow e.g. ParameterList.Doc()
doc = append(doc, prettier.Text("("))
for i, entitlement := range entitlements {
doc = append(doc, entitlement.Doc())
if i < len(entitlements)-1 {
doc = append(doc, prettier.Text(entitlementSet.Separator().String()), prettier.Space)
doc = append(doc, prettier.Text("("))
switch authorization := t.Authorization.(type) {
case EntitlementSet:
if len(authorization.Entitlements()) > 0 {
entitlements := authorization.Entitlements()
// TODO: add indentation, improve separators. follow e.g. ParameterList.Doc()
for i, entitlement := range entitlements {
doc = append(doc, entitlement.Doc())
if i < len(entitlements)-1 {
doc = append(doc, prettier.Text(authorization.Separator().String()), prettier.Space)
}
}
}
doc = append(doc, prettier.Text(")"))
case *MappedAccess:
doc = append(doc,
referenceTypeMappingKeywordDoc,
authorization.EntitlementMap.Doc(),
)
default:
panic(errors.NewUnreachableError())
}
doc = append(doc, prettier.Space)
doc = append(doc,
prettier.Text(")"),
prettier.Space,
)
}

return append(
Expand Down
Loading

0 comments on commit 74af553

Please sign in to comment.