github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/transform/staticlark/environment.go (about) 1 package staticlark 2 3 import ( 4 "fmt" 5 "path" 6 "sort" 7 "strings" 8 9 "go.starlark.net/syntax" 10 ) 11 12 const sensitiveVarName = "$" 13 14 // environment is a collection of variable bindings, and the 15 // list of other bindings that it derives value from 16 // TODO(dustmop): Handle lexical scoping 17 type environment struct { 18 vars map[string]*deriv 19 taints map[string]reason 20 } 21 22 func newEnvironment() *environment { 23 return &environment{ 24 vars: make(map[string]*deriv), 25 taints: make(map[string]reason), 26 } 27 } 28 29 func (e *environment) String() string { 30 result := "Env:\n" 31 keys := getKeys(e.vars) 32 for _, name := range keys { 33 der := e.vars[name] 34 status := "" 35 if _, ok := e.taints[name]; ok { 36 status = " tainted" 37 } 38 result += fmt.Sprintf(" %s -> %s%s\n", name, der, status) 39 } 40 return result 41 } 42 43 func (e *environment) clone() *environment { 44 vars := make(map[string]*deriv, len(e.vars)) 45 taints := make(map[string]reason, len(e.taints)) 46 for name := range e.vars { 47 vars[name] = e.vars[name].clone() 48 } 49 for name := range e.taints { 50 taints[name] = e.taints[name].clone() 51 } 52 return &environment{vars: vars, taints: taints} 53 } 54 55 func (e *environment) union(other *environment) *environment { 56 result := e.clone() 57 for name := range other.vars { 58 otherDerivs := other.vars[name] 59 result.vars[name].merge(otherDerivs) 60 // TODO(dustmop): union taints also 61 } 62 return result 63 } 64 65 func (e *environment) copyFrom(other *environment) { 66 for name := range other.vars { 67 e.vars[name] = other.vars[name] 68 } 69 } 70 71 func getKeys(m map[string]*deriv) []string { 72 keys := make([]string, 0, len(m)) 73 for k := range m { 74 keys = append(keys, k) 75 } 76 sort.Strings(keys) 77 return keys 78 } 79 80 func (e *environment) markParams(params []string) { 81 for _, p := range params { 82 e.vars[p] = &deriv{inputs: []string{p}, param: true} 83 } 84 } 85 86 func (e *environment) assign(dest string, sources []string) { 87 result := []string{} 88 for _, src := range sources { 89 if src == sensitiveVarName { 90 // meta variable, don't try to resolve 91 result = append(result, src) 92 continue 93 } 94 if lookup, ok := e.vars[src]; ok { 95 result = append(result, lookup.inputs...) 96 } else { 97 result = append(result, src) 98 } 99 } 100 if e.vars[dest] == nil { 101 e.vars[dest] = &deriv{} 102 } 103 e.vars[dest].inputs = result 104 } 105 106 func (e *environment) isSecret(name string) bool { 107 if e.vars[name] == nil { 108 return false 109 } 110 for _, inp := range e.vars[name].inputs { 111 if inp == sensitiveVarName { 112 return true 113 } 114 } 115 return false 116 } 117 118 func (e *environment) taint(name string, reason reason) { 119 if lookup, ok := e.vars[name]; ok { 120 // variable used 121 for _, inp := range lookup.inputs { 122 e.taints[inp] = reason 123 } 124 } else { 125 // parameter used direcly 126 e.taints[name] = reason 127 } 128 } 129 130 func (e *environment) getHighSensitive(params []string) ([]bool, []reason) { 131 dangerousParams := make([]bool, len(params)) 132 reasonParams := make([]reason, len(params)) 133 for i, p := range params { 134 if reason, ok := e.taints[p]; ok { 135 dangerousParams[i] = true 136 reasonParams[i] = reason 137 } 138 } 139 return dangerousParams, reasonParams 140 } 141 142 type deriv struct { 143 inputs []string 144 param bool 145 } 146 147 func (d *deriv) String() string { 148 return strings.Join(d.inputs, ",") 149 } 150 151 func (d *deriv) clone() *deriv { 152 inputs := make([]string, len(d.inputs)) 153 for i, inp := range d.inputs { 154 inputs[i] = inp 155 } 156 return &deriv{inputs: inputs, param: d.param} 157 } 158 159 func (d *deriv) merge(other *deriv) { 160 for _, val := range other.inputs { 161 if !arrayContains(d.inputs, val) { 162 d.inputs = append(d.inputs, val) 163 } 164 } 165 if other.param { 166 d.param = true 167 } 168 } 169 170 func arrayContains(arr []string, val string) bool { 171 for _, elem := range arr { 172 if elem == val { 173 return true 174 } 175 } 176 return false 177 } 178 179 type reason struct { 180 lines []string 181 } 182 183 func (r reason) clone() reason { 184 lines := make([]string, len(r.lines)) 185 for i, ln := range r.lines { 186 lines[i] = ln 187 } 188 return reason{lines: lines} 189 } 190 191 func makeReason(pos syntax.Position, currFunc, varName, invokeName, paramName string, prev reason) reason { 192 baseName := path.Base(pos.Filename()) 193 text := fmt.Sprintf("%s:%d: %s passes %s to %s argument %s", baseName, pos.Line, currFunc, varName, invokeName, paramName) 194 return reason{ 195 lines: append([]string{text}, prev.lines...), 196 } 197 } 198 199 func (r reason) String() string { 200 return strings.Join(r.lines, "\n") 201 }