diff --git a/bridge/core/reflection.go b/bridge/core/reflection.go index b84aa2c..176a599 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,28 @@ 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: Optimize []byte to Uint8Array for zero-copy extraction and zero interface boxing overhead. + if v.Type().Elem() == reflect.TypeOf(byte(0)) { + if v.CanAddr() || v.Kind() == reflect.Slice { + data := v.Bytes() + + // Explicit copy required since VM does not copy the backing array + copied := make([]byte, len(data)) + copy(copied, data) + + ctor := vm.Get("Uint8Array") + if ctor != nil && !sobek.IsNull(ctor) && !sobek.IsUndefined(ctor) { + arrBuf := vm.ToValue(vm.NewArrayBuffer(copied)) + typedArray, err := vm.New(ctor.ToObject(vm), arrBuf) + if err == nil && typedArray != nil { + return typedArray, nil + } + } + // Fallback: array buffer with no wrapper + return vm.ToValue(vm.NewArrayBuffer(copied)), nil + } + } + // @optimized: Pre-allocate slice and use NewArray(vals...) to avoid repeated Set calls. l := v.Len() vals := make([]interface{}, l) @@ -352,16 +375,24 @@ 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() { + // @optimized: Use MapRange instead of MapKeys to avoid slice allocation. + 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 strconv for numeric keys to avoid fmt.Sprint allocation. + 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: + 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 }