blob: 6c870f951ebea830a0581eff7637277597bc16aa [file] [log] [blame]
alandonovan32f34512019-07-02 18:37:51 -04001package starlark
2
3// This file defines the Unpack helper functions used by
4// built-in functions to interpret their call arguments.
5
6import (
7 "fmt"
8 "log"
9 "reflect"
10 "strings"
11)
12
13// UnpackArgs unpacks the positional and keyword arguments into the
14// supplied parameter variables. pairs is an alternating list of names
15// and pointers to variables.
16//
17// If the variable is a bool, int, string, *List, *Dict, Callable,
18// Iterable, or user-defined implementation of Value,
19// UnpackArgs performs the appropriate type check.
20// An int uses the AsInt32 check.
21// If the parameter name ends with "?",
22// it and all following parameters are optional.
23//
24// If the variable implements Value, UnpackArgs may call
25// its Type() method while constructing the error message.
26//
27// Beware: an optional *List, *Dict, Callable, Iterable, or Value variable that is
28// not assigned is not a valid Starlark Value, so the caller must
29// explicitly handle such cases by interpreting nil as None or some
30// computed default.
31func UnpackArgs(fnname string, args Tuple, kwargs []Tuple, pairs ...interface{}) error {
32 nparams := len(pairs) / 2
33 var defined intset
34 defined.init(nparams)
35
36 paramName := func(x interface{}) string { // (no free variables)
37 name := x.(string)
38 if name[len(name)-1] == '?' {
39 name = name[:len(name)-1]
40 }
41 return name
42 }
43
44 // positional arguments
45 if len(args) > nparams {
46 return fmt.Errorf("%s: got %d arguments, want at most %d",
47 fnname, len(args), nparams)
48 }
49 for i, arg := range args {
50 defined.set(i)
51 if err := unpackOneArg(arg, pairs[2*i+1]); err != nil {
52 name := paramName(pairs[2*i])
53 return fmt.Errorf("%s: for parameter %s: %s", fnname, name, err)
54 }
55 }
56
57 // keyword arguments
58kwloop:
59 for _, item := range kwargs {
60 name, arg := item[0].(String), item[1]
61 for i := 0; i < nparams; i++ {
62 if paramName(pairs[2*i]) == string(name) {
63 // found it
64 if defined.set(i) {
65 return fmt.Errorf("%s: got multiple values for keyword argument %s",
66 fnname, name)
67 }
68 ptr := pairs[2*i+1]
69 if err := unpackOneArg(arg, ptr); err != nil {
70 return fmt.Errorf("%s: for parameter %s: %s", fnname, name, err)
71 }
72 continue kwloop
73 }
74 }
75 return fmt.Errorf("%s: unexpected keyword argument %s", fnname, name)
76 }
77
78 // Check that all non-optional parameters are defined.
79 // (We needn't check the first len(args).)
80 for i := len(args); i < nparams; i++ {
81 name := pairs[2*i].(string)
82 if strings.HasSuffix(name, "?") {
83 break // optional
84 }
85 if !defined.get(i) {
86 return fmt.Errorf("%s: missing argument for %s", fnname, name)
87 }
88 }
89
90 return nil
91}
92
93// UnpackPositionalArgs unpacks the positional arguments into
94// corresponding variables. Each element of vars is a pointer; see
95// UnpackArgs for allowed types and conversions.
96//
97// UnpackPositionalArgs reports an error if the number of arguments is
98// less than min or greater than len(vars), if kwargs is nonempty, or if
99// any conversion fails.
100func UnpackPositionalArgs(fnname string, args Tuple, kwargs []Tuple, min int, vars ...interface{}) error {
101 if len(kwargs) > 0 {
102 return fmt.Errorf("%s: unexpected keyword arguments", fnname)
103 }
104 max := len(vars)
105 if len(args) < min {
106 var atleast string
107 if min < max {
108 atleast = "at least "
109 }
110 return fmt.Errorf("%s: got %d arguments, want %s%d", fnname, len(args), atleast, min)
111 }
112 if len(args) > max {
113 var atmost string
114 if max > min {
115 atmost = "at most "
116 }
117 return fmt.Errorf("%s: got %d arguments, want %s%d", fnname, len(args), atmost, max)
118 }
119 for i, arg := range args {
120 if err := unpackOneArg(arg, vars[i]); err != nil {
121 return fmt.Errorf("%s: for parameter %d: %s", fnname, i+1, err)
122 }
123 }
124 return nil
125}
126
127func unpackOneArg(v Value, ptr interface{}) error {
128 // On failure, don't clobber *ptr.
129 switch ptr := ptr.(type) {
130 case *Value:
131 *ptr = v
132 case *string:
133 s, ok := AsString(v)
134 if !ok {
135 return fmt.Errorf("got %s, want string", v.Type())
136 }
137 *ptr = s
138 case *bool:
139 b, ok := v.(Bool)
140 if !ok {
141 return fmt.Errorf("got %s, want bool", v.Type())
142 }
143 *ptr = bool(b)
144 case *int:
145 i, err := AsInt32(v)
146 if err != nil {
147 return err
148 }
149 *ptr = i
150 case **List:
151 list, ok := v.(*List)
152 if !ok {
153 return fmt.Errorf("got %s, want list", v.Type())
154 }
155 *ptr = list
156 case **Dict:
157 dict, ok := v.(*Dict)
158 if !ok {
159 return fmt.Errorf("got %s, want dict", v.Type())
160 }
161 *ptr = dict
162 case *Callable:
163 f, ok := v.(Callable)
164 if !ok {
165 return fmt.Errorf("got %s, want callable", v.Type())
166 }
167 *ptr = f
168 case *Iterable:
169 it, ok := v.(Iterable)
170 if !ok {
171 return fmt.Errorf("got %s, want iterable", v.Type())
172 }
173 *ptr = it
174 default:
175 // v must have type *V, where V is some subtype of starlark.Value.
176 ptrv := reflect.ValueOf(ptr)
177 if ptrv.Kind() != reflect.Ptr {
178 log.Panicf("internal error: not a pointer: %T", ptr)
179 }
180 paramVar := ptrv.Elem()
181 if !reflect.TypeOf(v).AssignableTo(paramVar.Type()) {
182 // The value is not assignable to the variable.
183
184 // Detect a possible bug in the Go program that called Unpack:
185 // If the variable *ptr is not a subtype of Value,
186 // no value of v can possibly work.
187 if !paramVar.Type().AssignableTo(reflect.TypeOf(new(Value)).Elem()) {
188 log.Panicf("pointer element type does not implement Value: %T", ptr)
189 }
190
191 // Report Starlark dynamic type error.
192 //
193 // We prefer the Starlark Value.Type name over
194 // its Go reflect.Type name, but calling the
195 // Value.Type method on the variable is not safe
196 // in general. If the variable is an interface,
197 // the call will fail. Even if the variable has
198 // a concrete type, it might not be safe to call
199 // Type() on a zero instance. Thus we must use
200 // recover.
201
202 // Default to Go reflect.Type name
203 paramType := paramVar.Type().String()
204
205 // Attempt to call Value.Type method.
206 func() {
207 defer func() { recover() }()
208 paramType = paramVar.MethodByName("Type").Call(nil)[0].String()
209 }()
210 return fmt.Errorf("got %s, want %s", v.Type(), paramType)
211 }
212 paramVar.Set(reflect.ValueOf(v))
213 }
214 return nil
215}
216
217type intset struct {
218 small uint64 // bitset, used if n < 64
219 large map[int]bool // set, used if n >= 64
220}
221
222func (is *intset) init(n int) {
223 if n >= 64 {
224 is.large = make(map[int]bool)
225 }
226}
227
228func (is *intset) set(i int) (prev bool) {
229 if is.large == nil {
230 prev = is.small&(1<<uint(i)) != 0
231 is.small |= 1 << uint(i)
232 } else {
233 prev = is.large[i]
234 is.large[i] = true
235 }
236 return
237}
238
239func (is *intset) get(i int) bool {
240 if is.large == nil {
241 return is.small&(1<<uint(i)) != 0
242 }
243 return is.large[i]
244}
245
246func (is *intset) len() int {
247 if is.large == nil {
248 // Suboptimal, but used only for error reporting.
249 len := 0
250 for i := 0; i < 64; i++ {
251 if is.small&(1<<uint(i)) != 0 {
252 len++
253 }
254 }
255 return len
256 }
257 return len(is.large)
258}