From 628c40910a93abc3c3a4200acc55f641626755e4 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 16 May 2026 19:27:57 +0000 Subject: [PATCH] perf(bridge/core): Optimize reflection for slices and maps - Intercept `[]byte` in `bindSlice` and output JS `Uint8Array` directly, bypassing expensive interface slice allocations. - Replace `v.MapKeys()` with `v.MapRange()` in `bindMap` to avoid key slice allocations. - Eliminate `fmt.Sprint` overhead by explicitly formatting numeric map keys via `strconv`. - Add `ToUint8Array` with graceful fallback to `ArrayBuffer` in `arraybuffer.go`. - Ensured strict safety guidelines with `// @safety` annotations protecting shared memory mutation. Co-authored-by: repyh <63894915+repyh@users.noreply.github.com> --- bridge/core/arraybuffer.go | 16 ++++++++++++++++ bridge/core/reflection.go | 36 +++++++++++++++++++++++++++++++----- 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/bridge/core/arraybuffer.go b/bridge/core/arraybuffer.go index a99b224..ba017e1 100644 --- a/bridge/core/arraybuffer.go +++ b/bridge/core/arraybuffer.go @@ -10,6 +10,22 @@ import ( // // For shared memory scenarios where modifications should be visible to both // Go and JavaScript, use MapSharedBuffer instead. +// ToUint8Array safely creates a JS Uint8Array from a byte slice. +// If Uint8Array is missing from the global object, it falls back to ArrayBuffer. +func ToUint8Array(vm *sobek.Runtime, data []byte) sobek.Value { + buf := vm.NewArrayBuffer(data) + ctor := vm.Get("Uint8Array") + if ctor == nil || sobek.IsNull(ctor) || sobek.IsUndefined(ctor) { + return vm.ToValue(buf) + } + + typedArray, err := vm.New(ctor.ToObject(vm), vm.ToValue(buf)) + if err != nil || typedArray == nil { + return vm.ToValue(buf) + } + return typedArray +} + func ToArrayBuffer(vm *sobek.Runtime, data []byte) sobek.Value { return vm.ToValue(vm.NewArrayBuffer(data)) } diff --git a/bridge/core/reflection.go b/bridge/core/reflection.go index b84aa2c..8a2f787 100644 --- a/bridge/core/reflection.go +++ b/bridge/core/reflection.go @@ -3,6 +3,7 @@ package core import ( "fmt" "reflect" + "strconv" "sync" "github.com/grafana/sobek" @@ -337,6 +338,24 @@ func wrapJSCallback(vm *sobek.Runtime, callable sobek.Callable, goType reflect.T } func bindSlice(vm *sobek.Runtime, v reflect.Value, visited map[uintptr]sobek.Value) (sobek.Value, error) { + // @optimized: bindSlice optimizes []byte by using ToUint8Array to create a JavaScript Uint8Array directly. + if v.Type().Elem() == reflect.TypeOf(byte(0)) { + var data []byte + if v.CanAddr() || v.Kind() == reflect.Slice { + data = v.Bytes() + } else { + // Unaddressable array, fallback to copying via allocation + l := v.Len() + data = make([]byte, l) + reflect.Copy(reflect.ValueOf(data), v) + } + + // @safety: Ensure copy of the byte slice to prevent shared memory mutation + cpy := make([]byte, len(data)) + copy(cpy, data) + return ToUint8Array(vm, cpy), nil + } + // @optimized: Pre-allocate slice and use NewArray(vals...) to avoid repeated Set calls. l := v.Len() vals := make([]interface{}, l) @@ -352,16 +371,23 @@ func bindSlice(vm *sobek.Runtime, v reflect.Value, visited map[uintptr]sobek.Val func bindMap(vm *sobek.Runtime, v reflect.Value, visited map[uintptr]sobek.Value) (sobek.Value, error) { obj := vm.NewObject() - for _, key := range v.MapKeys() { + iter := v.MapRange() + for iter.Next() { + key := iter.Key() var keyStr string - // @optimized: Avoid Sprintf if key is already a string. - if key.Kind() == reflect.String { + // @optimized: Use MapRange to avoid allocating a slice of keys. Avoid Sprintf if key is already a string or numeric type. + switch key.Kind() { + case reflect.String: keyStr = key.String() - } else { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + keyStr = strconv.FormatInt(key.Int(), 10) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + keyStr = strconv.FormatUint(key.Uint(), 10) + default: keyStr = fmt.Sprint(key.Interface()) } - val, err := bindValue(vm, v.MapIndex(key), visited) + val, err := bindValue(vm, iter.Value(), visited) if err != nil { return nil, err }