xref: /aosp_15_r20/external/starlark-go/starlark/unpack.go (revision 4947cdc739c985f6d86941e22894f5cefe7c9e9a)
1*4947cdc7SCole Faustpackage starlark
2*4947cdc7SCole Faust
3*4947cdc7SCole Faust// This file defines the Unpack helper functions used by
4*4947cdc7SCole Faust// built-in functions to interpret their call arguments.
5*4947cdc7SCole Faust
6*4947cdc7SCole Faustimport (
7*4947cdc7SCole Faust	"fmt"
8*4947cdc7SCole Faust	"log"
9*4947cdc7SCole Faust	"reflect"
10*4947cdc7SCole Faust	"strings"
11*4947cdc7SCole Faust)
12*4947cdc7SCole Faust
13*4947cdc7SCole Faust// An Unpacker defines custom argument unpacking behavior.
14*4947cdc7SCole Faust// See UnpackArgs.
15*4947cdc7SCole Fausttype Unpacker interface {
16*4947cdc7SCole Faust	Unpack(v Value) error
17*4947cdc7SCole Faust}
18*4947cdc7SCole Faust
19*4947cdc7SCole Faust// UnpackArgs unpacks the positional and keyword arguments into the
20*4947cdc7SCole Faust// supplied parameter variables.  pairs is an alternating list of names
21*4947cdc7SCole Faust// and pointers to variables.
22*4947cdc7SCole Faust//
23*4947cdc7SCole Faust// If the variable is a bool, integer, string, *List, *Dict, Callable,
24*4947cdc7SCole Faust// Iterable, or user-defined implementation of Value,
25*4947cdc7SCole Faust// UnpackArgs performs the appropriate type check.
26*4947cdc7SCole Faust// Predeclared Go integer types uses the AsInt check.
27*4947cdc7SCole Faust// If the parameter name ends with "?",
28*4947cdc7SCole Faust// it and all following parameters are optional.
29*4947cdc7SCole Faust//
30*4947cdc7SCole Faust// If the variable implements Unpacker, its Unpack argument
31*4947cdc7SCole Faust// is called with the argument value, allowing an application
32*4947cdc7SCole Faust// to define its own argument validation and conversion.
33*4947cdc7SCole Faust//
34*4947cdc7SCole Faust// If the variable implements Value, UnpackArgs may call
35*4947cdc7SCole Faust// its Type() method while constructing the error message.
36*4947cdc7SCole Faust//
37*4947cdc7SCole Faust// Examples:
38*4947cdc7SCole Faust//
39*4947cdc7SCole Faust//      var (
40*4947cdc7SCole Faust//          a Value
41*4947cdc7SCole Faust//          b = MakeInt(42)
42*4947cdc7SCole Faust//          c Value = starlark.None
43*4947cdc7SCole Faust//      )
44*4947cdc7SCole Faust//
45*4947cdc7SCole Faust//      // 1. mixed parameters, like def f(a, b=42, c=None).
46*4947cdc7SCole Faust//      err := UnpackArgs("f", args, kwargs, "a", &a, "b?", &b, "c?", &c)
47*4947cdc7SCole Faust//
48*4947cdc7SCole Faust//      // 2. keyword parameters only, like def f(*, a, b, c=None).
49*4947cdc7SCole Faust//      if len(args) > 0 {
50*4947cdc7SCole Faust//              return fmt.Errorf("f: unexpected positional arguments")
51*4947cdc7SCole Faust//      }
52*4947cdc7SCole Faust//      err := UnpackArgs("f", args, kwargs, "a", &a, "b?", &b, "c?", &c)
53*4947cdc7SCole Faust//
54*4947cdc7SCole Faust//      // 3. positional parameters only, like def f(a, b=42, c=None, /) in Python 3.8.
55*4947cdc7SCole Faust//      err := UnpackPositionalArgs("f", args, kwargs, 1, &a, &b, &c)
56*4947cdc7SCole Faust//
57*4947cdc7SCole Faust// More complex forms such as def f(a, b=42, *args, c, d=123, **kwargs)
58*4947cdc7SCole Faust// require additional logic, but their need in built-ins is exceedingly rare.
59*4947cdc7SCole Faust//
60*4947cdc7SCole Faust// In the examples above, the declaration of b with type Int causes UnpackArgs
61*4947cdc7SCole Faust// to require that b's argument value, if provided, is also an int.
62*4947cdc7SCole Faust// To allow arguments of any type, while retaining the default value of 42,
63*4947cdc7SCole Faust// declare b as a Value:
64*4947cdc7SCole Faust//
65*4947cdc7SCole Faust//	var b Value = MakeInt(42)
66*4947cdc7SCole Faust//
67*4947cdc7SCole Faust// The zero value of a variable of type Value, such as 'a' in the
68*4947cdc7SCole Faust// examples above, is not a valid Starlark value, so if the parameter is
69*4947cdc7SCole Faust// optional, the caller must explicitly handle the default case by
70*4947cdc7SCole Faust// interpreting nil as None or some computed default. The same is true
71*4947cdc7SCole Faust// for the zero values of variables of type *List, *Dict, Callable, or
72*4947cdc7SCole Faust// Iterable. For example:
73*4947cdc7SCole Faust//
74*4947cdc7SCole Faust//      // def myfunc(d=None, e=[], f={})
75*4947cdc7SCole Faust//      var (
76*4947cdc7SCole Faust//          d Value
77*4947cdc7SCole Faust//          e *List
78*4947cdc7SCole Faust//          f *Dict
79*4947cdc7SCole Faust//      )
80*4947cdc7SCole Faust//      err := UnpackArgs("myfunc", args, kwargs, "d?", &d, "e?", &e, "f?", &f)
81*4947cdc7SCole Faust//      if d == nil { d = None; }
82*4947cdc7SCole Faust//      if e == nil { e = new(List); }
83*4947cdc7SCole Faust//      if f == nil { f = new(Dict); }
84*4947cdc7SCole Faust//
85*4947cdc7SCole Faustfunc UnpackArgs(fnname string, args Tuple, kwargs []Tuple, pairs ...interface{}) error {
86*4947cdc7SCole Faust	nparams := len(pairs) / 2
87*4947cdc7SCole Faust	var defined intset
88*4947cdc7SCole Faust	defined.init(nparams)
89*4947cdc7SCole Faust
90*4947cdc7SCole Faust	paramName := func(x interface{}) string { // (no free variables)
91*4947cdc7SCole Faust		name := x.(string)
92*4947cdc7SCole Faust		if name[len(name)-1] == '?' {
93*4947cdc7SCole Faust			name = name[:len(name)-1]
94*4947cdc7SCole Faust		}
95*4947cdc7SCole Faust		return name
96*4947cdc7SCole Faust	}
97*4947cdc7SCole Faust
98*4947cdc7SCole Faust	// positional arguments
99*4947cdc7SCole Faust	if len(args) > nparams {
100*4947cdc7SCole Faust		return fmt.Errorf("%s: got %d arguments, want at most %d",
101*4947cdc7SCole Faust			fnname, len(args), nparams)
102*4947cdc7SCole Faust	}
103*4947cdc7SCole Faust	for i, arg := range args {
104*4947cdc7SCole Faust		defined.set(i)
105*4947cdc7SCole Faust		if err := unpackOneArg(arg, pairs[2*i+1]); err != nil {
106*4947cdc7SCole Faust			name := paramName(pairs[2*i])
107*4947cdc7SCole Faust			return fmt.Errorf("%s: for parameter %s: %s", fnname, name, err)
108*4947cdc7SCole Faust		}
109*4947cdc7SCole Faust	}
110*4947cdc7SCole Faust
111*4947cdc7SCole Faust	// keyword arguments
112*4947cdc7SCole Faustkwloop:
113*4947cdc7SCole Faust	for _, item := range kwargs {
114*4947cdc7SCole Faust		name, arg := item[0].(String), item[1]
115*4947cdc7SCole Faust		for i := 0; i < nparams; i++ {
116*4947cdc7SCole Faust			if paramName(pairs[2*i]) == string(name) {
117*4947cdc7SCole Faust				// found it
118*4947cdc7SCole Faust				if defined.set(i) {
119*4947cdc7SCole Faust					return fmt.Errorf("%s: got multiple values for keyword argument %s",
120*4947cdc7SCole Faust						fnname, name)
121*4947cdc7SCole Faust				}
122*4947cdc7SCole Faust				ptr := pairs[2*i+1]
123*4947cdc7SCole Faust				if err := unpackOneArg(arg, ptr); err != nil {
124*4947cdc7SCole Faust					return fmt.Errorf("%s: for parameter %s: %s", fnname, name, err)
125*4947cdc7SCole Faust				}
126*4947cdc7SCole Faust				continue kwloop
127*4947cdc7SCole Faust			}
128*4947cdc7SCole Faust		}
129*4947cdc7SCole Faust		return fmt.Errorf("%s: unexpected keyword argument %s", fnname, name)
130*4947cdc7SCole Faust	}
131*4947cdc7SCole Faust
132*4947cdc7SCole Faust	// Check that all non-optional parameters are defined.
133*4947cdc7SCole Faust	// (We needn't check the first len(args).)
134*4947cdc7SCole Faust	for i := len(args); i < nparams; i++ {
135*4947cdc7SCole Faust		name := pairs[2*i].(string)
136*4947cdc7SCole Faust		if strings.HasSuffix(name, "?") {
137*4947cdc7SCole Faust			break // optional
138*4947cdc7SCole Faust		}
139*4947cdc7SCole Faust		if !defined.get(i) {
140*4947cdc7SCole Faust			return fmt.Errorf("%s: missing argument for %s", fnname, name)
141*4947cdc7SCole Faust		}
142*4947cdc7SCole Faust	}
143*4947cdc7SCole Faust
144*4947cdc7SCole Faust	return nil
145*4947cdc7SCole Faust}
146*4947cdc7SCole Faust
147*4947cdc7SCole Faust// UnpackPositionalArgs unpacks the positional arguments into
148*4947cdc7SCole Faust// corresponding variables.  Each element of vars is a pointer; see
149*4947cdc7SCole Faust// UnpackArgs for allowed types and conversions.
150*4947cdc7SCole Faust//
151*4947cdc7SCole Faust// UnpackPositionalArgs reports an error if the number of arguments is
152*4947cdc7SCole Faust// less than min or greater than len(vars), if kwargs is nonempty, or if
153*4947cdc7SCole Faust// any conversion fails.
154*4947cdc7SCole Faust//
155*4947cdc7SCole Faust// See UnpackArgs for general comments.
156*4947cdc7SCole Faustfunc UnpackPositionalArgs(fnname string, args Tuple, kwargs []Tuple, min int, vars ...interface{}) error {
157*4947cdc7SCole Faust	if len(kwargs) > 0 {
158*4947cdc7SCole Faust		return fmt.Errorf("%s: unexpected keyword arguments", fnname)
159*4947cdc7SCole Faust	}
160*4947cdc7SCole Faust	max := len(vars)
161*4947cdc7SCole Faust	if len(args) < min {
162*4947cdc7SCole Faust		var atleast string
163*4947cdc7SCole Faust		if min < max {
164*4947cdc7SCole Faust			atleast = "at least "
165*4947cdc7SCole Faust		}
166*4947cdc7SCole Faust		return fmt.Errorf("%s: got %d arguments, want %s%d", fnname, len(args), atleast, min)
167*4947cdc7SCole Faust	}
168*4947cdc7SCole Faust	if len(args) > max {
169*4947cdc7SCole Faust		var atmost string
170*4947cdc7SCole Faust		if max > min {
171*4947cdc7SCole Faust			atmost = "at most "
172*4947cdc7SCole Faust		}
173*4947cdc7SCole Faust		return fmt.Errorf("%s: got %d arguments, want %s%d", fnname, len(args), atmost, max)
174*4947cdc7SCole Faust	}
175*4947cdc7SCole Faust	for i, arg := range args {
176*4947cdc7SCole Faust		if err := unpackOneArg(arg, vars[i]); err != nil {
177*4947cdc7SCole Faust			return fmt.Errorf("%s: for parameter %d: %s", fnname, i+1, err)
178*4947cdc7SCole Faust		}
179*4947cdc7SCole Faust	}
180*4947cdc7SCole Faust	return nil
181*4947cdc7SCole Faust}
182*4947cdc7SCole Faust
183*4947cdc7SCole Faustfunc unpackOneArg(v Value, ptr interface{}) error {
184*4947cdc7SCole Faust	// On failure, don't clobber *ptr.
185*4947cdc7SCole Faust	switch ptr := ptr.(type) {
186*4947cdc7SCole Faust	case Unpacker:
187*4947cdc7SCole Faust		return ptr.Unpack(v)
188*4947cdc7SCole Faust	case *Value:
189*4947cdc7SCole Faust		*ptr = v
190*4947cdc7SCole Faust	case *string:
191*4947cdc7SCole Faust		s, ok := AsString(v)
192*4947cdc7SCole Faust		if !ok {
193*4947cdc7SCole Faust			return fmt.Errorf("got %s, want string", v.Type())
194*4947cdc7SCole Faust		}
195*4947cdc7SCole Faust		*ptr = s
196*4947cdc7SCole Faust	case *bool:
197*4947cdc7SCole Faust		b, ok := v.(Bool)
198*4947cdc7SCole Faust		if !ok {
199*4947cdc7SCole Faust			return fmt.Errorf("got %s, want bool", v.Type())
200*4947cdc7SCole Faust		}
201*4947cdc7SCole Faust		*ptr = bool(b)
202*4947cdc7SCole Faust	case *int, *int8, *int16, *int32, *int64,
203*4947cdc7SCole Faust		*uint, *uint8, *uint16, *uint32, *uint64, *uintptr:
204*4947cdc7SCole Faust		return AsInt(v, ptr)
205*4947cdc7SCole Faust	case *float64:
206*4947cdc7SCole Faust		f, ok := v.(Float)
207*4947cdc7SCole Faust		if !ok {
208*4947cdc7SCole Faust			return fmt.Errorf("got %s, want float", v.Type())
209*4947cdc7SCole Faust		}
210*4947cdc7SCole Faust		*ptr = float64(f)
211*4947cdc7SCole Faust	case **List:
212*4947cdc7SCole Faust		list, ok := v.(*List)
213*4947cdc7SCole Faust		if !ok {
214*4947cdc7SCole Faust			return fmt.Errorf("got %s, want list", v.Type())
215*4947cdc7SCole Faust		}
216*4947cdc7SCole Faust		*ptr = list
217*4947cdc7SCole Faust	case **Dict:
218*4947cdc7SCole Faust		dict, ok := v.(*Dict)
219*4947cdc7SCole Faust		if !ok {
220*4947cdc7SCole Faust			return fmt.Errorf("got %s, want dict", v.Type())
221*4947cdc7SCole Faust		}
222*4947cdc7SCole Faust		*ptr = dict
223*4947cdc7SCole Faust	case *Callable:
224*4947cdc7SCole Faust		f, ok := v.(Callable)
225*4947cdc7SCole Faust		if !ok {
226*4947cdc7SCole Faust			return fmt.Errorf("got %s, want callable", v.Type())
227*4947cdc7SCole Faust		}
228*4947cdc7SCole Faust		*ptr = f
229*4947cdc7SCole Faust	case *Iterable:
230*4947cdc7SCole Faust		it, ok := v.(Iterable)
231*4947cdc7SCole Faust		if !ok {
232*4947cdc7SCole Faust			return fmt.Errorf("got %s, want iterable", v.Type())
233*4947cdc7SCole Faust		}
234*4947cdc7SCole Faust		*ptr = it
235*4947cdc7SCole Faust	default:
236*4947cdc7SCole Faust		// v must have type *V, where V is some subtype of starlark.Value.
237*4947cdc7SCole Faust		ptrv := reflect.ValueOf(ptr)
238*4947cdc7SCole Faust		if ptrv.Kind() != reflect.Ptr {
239*4947cdc7SCole Faust			log.Panicf("internal error: not a pointer: %T", ptr)
240*4947cdc7SCole Faust		}
241*4947cdc7SCole Faust		paramVar := ptrv.Elem()
242*4947cdc7SCole Faust		if !reflect.TypeOf(v).AssignableTo(paramVar.Type()) {
243*4947cdc7SCole Faust			// The value is not assignable to the variable.
244*4947cdc7SCole Faust
245*4947cdc7SCole Faust			// Detect a possible bug in the Go program that called Unpack:
246*4947cdc7SCole Faust			// If the variable *ptr is not a subtype of Value,
247*4947cdc7SCole Faust			// no value of v can possibly work.
248*4947cdc7SCole Faust			if !paramVar.Type().AssignableTo(reflect.TypeOf(new(Value)).Elem()) {
249*4947cdc7SCole Faust				log.Panicf("pointer element type does not implement Value: %T", ptr)
250*4947cdc7SCole Faust			}
251*4947cdc7SCole Faust
252*4947cdc7SCole Faust			// Report Starlark dynamic type error.
253*4947cdc7SCole Faust			//
254*4947cdc7SCole Faust			// We prefer the Starlark Value.Type name over
255*4947cdc7SCole Faust			// its Go reflect.Type name, but calling the
256*4947cdc7SCole Faust			// Value.Type method on the variable is not safe
257*4947cdc7SCole Faust			// in general. If the variable is an interface,
258*4947cdc7SCole Faust			// the call will fail. Even if the variable has
259*4947cdc7SCole Faust			// a concrete type, it might not be safe to call
260*4947cdc7SCole Faust			// Type() on a zero instance. Thus we must use
261*4947cdc7SCole Faust			// recover.
262*4947cdc7SCole Faust
263*4947cdc7SCole Faust			// Default to Go reflect.Type name
264*4947cdc7SCole Faust			paramType := paramVar.Type().String()
265*4947cdc7SCole Faust
266*4947cdc7SCole Faust			// Attempt to call Value.Type method.
267*4947cdc7SCole Faust			func() {
268*4947cdc7SCole Faust				defer func() { recover() }()
269*4947cdc7SCole Faust				paramType = paramVar.MethodByName("Type").Call(nil)[0].String()
270*4947cdc7SCole Faust			}()
271*4947cdc7SCole Faust			return fmt.Errorf("got %s, want %s", v.Type(), paramType)
272*4947cdc7SCole Faust		}
273*4947cdc7SCole Faust		paramVar.Set(reflect.ValueOf(v))
274*4947cdc7SCole Faust	}
275*4947cdc7SCole Faust	return nil
276*4947cdc7SCole Faust}
277*4947cdc7SCole Faust
278*4947cdc7SCole Fausttype intset struct {
279*4947cdc7SCole Faust	small uint64       // bitset, used if n < 64
280*4947cdc7SCole Faust	large map[int]bool //    set, used if n >= 64
281*4947cdc7SCole Faust}
282*4947cdc7SCole Faust
283*4947cdc7SCole Faustfunc (is *intset) init(n int) {
284*4947cdc7SCole Faust	if n >= 64 {
285*4947cdc7SCole Faust		is.large = make(map[int]bool)
286*4947cdc7SCole Faust	}
287*4947cdc7SCole Faust}
288*4947cdc7SCole Faust
289*4947cdc7SCole Faustfunc (is *intset) set(i int) (prev bool) {
290*4947cdc7SCole Faust	if is.large == nil {
291*4947cdc7SCole Faust		prev = is.small&(1<<uint(i)) != 0
292*4947cdc7SCole Faust		is.small |= 1 << uint(i)
293*4947cdc7SCole Faust	} else {
294*4947cdc7SCole Faust		prev = is.large[i]
295*4947cdc7SCole Faust		is.large[i] = true
296*4947cdc7SCole Faust	}
297*4947cdc7SCole Faust	return
298*4947cdc7SCole Faust}
299*4947cdc7SCole Faust
300*4947cdc7SCole Faustfunc (is *intset) get(i int) bool {
301*4947cdc7SCole Faust	if is.large == nil {
302*4947cdc7SCole Faust		return is.small&(1<<uint(i)) != 0
303*4947cdc7SCole Faust	}
304*4947cdc7SCole Faust	return is.large[i]
305*4947cdc7SCole Faust}
306*4947cdc7SCole Faust
307*4947cdc7SCole Faustfunc (is *intset) len() int {
308*4947cdc7SCole Faust	if is.large == nil {
309*4947cdc7SCole Faust		// Suboptimal, but used only for error reporting.
310*4947cdc7SCole Faust		len := 0
311*4947cdc7SCole Faust		for i := 0; i < 64; i++ {
312*4947cdc7SCole Faust			if is.small&(1<<uint(i)) != 0 {
313*4947cdc7SCole Faust				len++
314*4947cdc7SCole Faust			}
315*4947cdc7SCole Faust		}
316*4947cdc7SCole Faust		return len
317*4947cdc7SCole Faust	}
318*4947cdc7SCole Faust	return len(is.large)
319*4947cdc7SCole Faust}
320