github.com/mavryk-network/mvgo@v1.19.9/micheline/entrypoint.go (about) 1 // Copyright (c) 2020-2021 Blockwatch Data Inc. 2 // Author: alex@blockwatch.cc 3 4 package micheline 5 6 import ( 7 "fmt" 8 "strings" 9 ) 10 11 type Entrypoint struct { 12 Id int `json:"id"` 13 Name string `json:"name"` 14 Branch string `json:"branch"` 15 Typedef []Typedef `json:"type"` 16 Prim *Prim `json:"prim,omitempty"` 17 } 18 19 func (e Entrypoint) Type() Type { 20 if e.Prim == nil { 21 e.Prim = &Prim{} // invalid 22 } 23 typ := NewType(*e.Prim) 24 if !typ.HasLabel() { 25 typ.Prim.Anno = []string{"@" + e.Name} 26 } 27 return typ 28 } 29 30 func (e Entrypoint) IsCallback() bool { 31 if e.Prim == nil { 32 return false 33 } 34 return e.Prim.IsPair() && e.Prim.Args.Last().OpCode == T_CONTRACT 35 } 36 37 type Entrypoints map[string]Entrypoint 38 39 func (e Entrypoints) FindBranch(branch string) (Entrypoint, bool) { 40 if branch == "" { 41 return Entrypoint{}, false 42 } 43 for _, v := range e { 44 if v.Branch == branch { 45 return v, true 46 } 47 } 48 return Entrypoint{}, false 49 } 50 51 func (e Entrypoints) FindId(id int) (Entrypoint, bool) { 52 for _, v := range e { 53 if v.Id == id { 54 return v, true 55 } 56 } 57 return Entrypoint{}, false 58 } 59 60 func (t Type) Entrypoints(withPrim bool) (Entrypoints, error) { 61 e := make(Entrypoints) 62 if !t.IsValid() { 63 return e, nil 64 } 65 if err := listEntrypoints(e, "", t.Prim); err != nil { 66 return nil, err 67 } 68 if !withPrim { 69 for n, v := range e { 70 v.Prim = nil 71 e[n] = v 72 } 73 } 74 return e, nil 75 } 76 77 // returns path to named entrypoint 78 func (t Type) ResolveEntrypointPath(name string) string { 79 if !t.IsValid() { 80 return "" 81 } 82 return resolveEntrypointPath(name, "", t.Prim) 83 } 84 85 func resolveEntrypointPath(name, branch string, node Prim) string { 86 if node.GetVarAnnoAny() == name { 87 return branch 88 } 89 if node.OpCode == T_OR { 90 if b := resolveEntrypointPath(name, branch+"/L", node.Args[0]); b != "" { 91 return b 92 } 93 if b := resolveEntrypointPath(name, branch+"/R", node.Args[1]); b != "" { 94 return b 95 } 96 } 97 return "" 98 } 99 100 // Explicit list of prefixes for detecting entrypoints. 101 // 102 // This is necessary to resolve ambiguities in contract designs that 103 // use T_OR as call parameter. 104 105 // - to handle conflicts between T_OR used for call params vs used for marking entrypoint 106 // skip annotated T_OR branches (exclude the root T_OR and any branch called 'default') 107 var knownEntrypointPrefixes = []string{"_Liq_entry_"} 108 109 func isKnownEntrypointPrefix(s string) bool { 110 if len(s) == 0 { 111 return false 112 } 113 for _, v := range knownEntrypointPrefixes { 114 if strings.HasPrefix(s, v) { 115 return true 116 } 117 } 118 return false 119 } 120 121 // walks T_OR expressions and stores each non-T_OR branch as entrypoint 122 func listEntrypoints(e Entrypoints, branch string, node Prim) error { 123 // prefer % annotations 124 name := node.GetVarAnnoAny() 125 if node.OpCode == T_OR && !isKnownEntrypointPrefix(name) { 126 if l := len(node.Args); l != 2 { 127 return fmt.Errorf("micheline: expected 2 arguments for T_OR, got %d", l) 128 } 129 130 if err := listEntrypoints(e, branch+"/L", node.Args[0]); err != nil { 131 return err 132 } 133 134 if err := listEntrypoints(e, branch+"/R", node.Args[1]); err != nil { 135 return err 136 } 137 138 return nil 139 } 140 141 // need unique entrypoint name 142 if name == "" { 143 if len(e) == 0 { 144 name = DEFAULT 145 } else { 146 name = fmt.Sprintf("%s_%d", CONST_ENTRYPOINT, len(e)) 147 } 148 } 149 150 // process non-T_OR branches 151 cp := node.Clone() 152 ep := Entrypoint{ 153 Id: len(e), 154 Branch: branch, 155 Name: name, 156 Prim: &cp, 157 } 158 if node.IsScalarType() || node.IsContainerType() { 159 ep.Typedef = []Typedef{buildTypedef("", node, []int{})} 160 ep.Typedef[0].Name = "" 161 } else { 162 td := buildTypedef("", node, []int{}) 163 switch len(td.Args) { 164 case 0: 165 ep.Typedef = []Typedef{td} 166 case 1: 167 td.Name = "" 168 ep.Typedef = []Typedef{td} 169 default: 170 ep.Typedef = td.Args 171 } 172 } 173 174 e[name] = ep 175 return nil 176 }