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  }