Skip to content

Commit d82aa37

Browse files
authored
perf: avoid use of reflection when generating bzl expressions (#2204)
**What type of PR is this?** Other **What package or component does this PR mostly affect?** all **What does this PR do? Why is it needed?** Perf. Reduce use of `reflect.*` in common cases.
1 parent ac1a5c3 commit d82aa37

File tree

1 file changed

+73
-19
lines changed

1 file changed

+73
-19
lines changed

rule/value.go

Lines changed: 73 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ limitations under the License.
1616
package rule
1717

1818
import (
19-
"fmt"
2019
"log"
2120
"reflect"
2221
"sort"
22+
"strconv"
2323

2424
bzl "github.com/bazelbuild/buildtools/build"
2525
)
@@ -228,27 +228,53 @@ func ExprFromValue(val interface{}) bzl.Expr {
228228
return be.BzlExpr()
229229
}
230230

231-
rv := reflect.ValueOf(val)
232-
switch rv.Kind() {
233-
case reflect.Bool:
234-
tok := "False"
235-
if rv.Bool() {
236-
tok = "True"
231+
// Fast paths for common types to avoid reflection overhead.
232+
switch v := val.(type) {
233+
// primitives
234+
case string:
235+
return &bzl.StringExpr{Value: v}
236+
case bool:
237+
if v {
238+
return &bzl.LiteralExpr{Token: "True"}
237239
}
238-
return &bzl.LiteralExpr{Token: tok}
239-
240-
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
241-
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
242-
return &bzl.LiteralExpr{Token: fmt.Sprintf("%d", val)}
243-
244-
case reflect.Float32, reflect.Float64:
245-
return &bzl.LiteralExpr{Token: fmt.Sprintf("%f", val)}
246-
247-
case reflect.String:
248-
return &bzl.StringExpr{Value: val.(string)}
240+
return &bzl.LiteralExpr{Token: "False"}
241+
case int:
242+
return intLiteralExpr(int64(v))
243+
case int8:
244+
return intLiteralExpr(int64(v))
245+
case int16:
246+
return intLiteralExpr(int64(v))
247+
case int32:
248+
return intLiteralExpr(int64(v))
249+
case int64:
250+
return intLiteralExpr(v)
251+
case uint:
252+
return uintLiteralExpr(uint64(v))
253+
case uint8:
254+
return uintLiteralExpr(uint64(v))
255+
case uint16:
256+
return uintLiteralExpr(uint64(v))
257+
case uint32:
258+
return uintLiteralExpr(uint64(v))
259+
case uint64:
260+
return uintLiteralExpr(v)
261+
case float32:
262+
return floatLiteralExpr(float64(v))
263+
case float64:
264+
return floatLiteralExpr(v)
265+
266+
// common types of slices
267+
case []string:
268+
return stringSliceToExpr(v)
269+
case []interface{}:
270+
return interfaceSliceToExpr(v)
271+
}
249272

273+
// Fallback to reflection for less common types
274+
rv := reflect.ValueOf(val)
275+
switch rv.Kind() {
250276
case reflect.Slice, reflect.Array:
251-
var list []bzl.Expr
277+
list := make([]bzl.Expr, 0, rv.Len())
252278
for i := 0; i < rv.Len(); i++ {
253279
elem := ExprFromValue(rv.Index(i).Interface())
254280
list = append(list, elem)
@@ -274,6 +300,34 @@ func ExprFromValue(val interface{}) bzl.Expr {
274300
return nil
275301
}
276302

303+
func intLiteralExpr(v int64) bzl.Expr {
304+
return &bzl.LiteralExpr{Token: strconv.FormatInt(v, 10)}
305+
}
306+
307+
func uintLiteralExpr(v uint64) bzl.Expr {
308+
return &bzl.LiteralExpr{Token: strconv.FormatUint(v, 10)}
309+
}
310+
311+
func floatLiteralExpr(v float64) bzl.Expr {
312+
return &bzl.LiteralExpr{Token: strconv.FormatFloat(v, 'g', -1, 64)}
313+
}
314+
315+
func stringSliceToExpr(strs []string) *bzl.ListExpr {
316+
list := make([]bzl.Expr, len(strs))
317+
for i, s := range strs {
318+
list[i] = &bzl.StringExpr{Value: s}
319+
}
320+
return &bzl.ListExpr{List: list}
321+
}
322+
323+
func interfaceSliceToExpr(vals []interface{}) *bzl.ListExpr {
324+
list := make([]bzl.Expr, len(vals))
325+
for i, v := range vals {
326+
list[i] = ExprFromValue(v)
327+
}
328+
return &bzl.ListExpr{List: list}
329+
}
330+
277331
func mapKeyString(k reflect.Value) string {
278332
switch s := k.Interface().(type) {
279333
case string:

0 commit comments

Comments
 (0)