// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com) // All rights reserved. // gobridge_fast.go — Performance optimizations for Go interop. // // Strategy 1: Method cache — cache reflect.Method by (type, name) // Strategy 2: Fast path — bypass reflect for common signatures // Strategy 3: Function registry — pre-register Go funcs with typed wrappers package hbrt import ( "reflect" "sync" ) // --------------------------------------------------------------------------- // 1. Method Cache — cache reflect.Method by (type, methodName) // --------------------------------------------------------------------------- type methodKey struct { typ reflect.Type name string } var ( methodCache = make(map[methodKey]reflect.Method) methodCacheMu sync.RWMutex ) // cachedMethod looks up a method with caching. func cachedMethod(rv reflect.Value, name string) (reflect.Value, bool) { key := methodKey{rv.Type(), name} methodCacheMu.RLock() m, ok := methodCache[key] methodCacheMu.RUnlock() if ok { return rv.Method(m.Index), true } // Slow path: lookup and cache mt, found := rv.Type().MethodByName(name) if !found { return reflect.Value{}, false } methodCacheMu.Lock() methodCache[key] = mt methodCacheMu.Unlock() return rv.Method(mt.Index), true } // --------------------------------------------------------------------------- // 2. Fast Path — common function signatures without reflect // --------------------------------------------------------------------------- // FastFunc is a type-specialized Go function wrapper. // Avoids reflect.Call for common signatures. type FastFunc struct { name string fn interface{} // original function for fallback // Typed fast paths (only one is non-nil) fnSS func(string) string // string → string fnSSB func(string, string) bool // string, string → bool fnSSS func(string, string) string // string, string → string fnSSSS func(string, string, string) string // string, string, string → string fnSI func(string) int // string → int fnSSI func(string, string) int // string, string → int fnFI func(float64) float64 // float64 → float64 fnFFI func(float64, float64) float64 // float64, float64 → float64 fnII func(int) int // int → int } // GoCallFast calls a pre-registered fast function. func GoCallFast(ff *FastFunc, args ...Value) []Value { n := len(args) // Try fast paths first if ff.fnSS != nil && n == 1 { return []Value{MakeString(ff.fnSS(args[0].AsString()))} } if ff.fnSSB != nil && n == 2 { return []Value{MakeBool(ff.fnSSB(args[0].AsString(), args[1].AsString()))} } if ff.fnSSS != nil && n == 2 { return []Value{MakeString(ff.fnSSS(args[0].AsString(), args[1].AsString()))} } if ff.fnSSSS != nil && n == 3 { return []Value{MakeString(ff.fnSSSS(args[0].AsString(), args[1].AsString(), args[2].AsString()))} } if ff.fnSI != nil && n == 1 { return []Value{MakeInt(ff.fnSI(args[0].AsString()))} } if ff.fnSSI != nil && n == 2 { return []Value{MakeInt(ff.fnSSI(args[0].AsString(), args[1].AsString()))} } if ff.fnFI != nil && n == 1 { return []Value{MakeDouble(ff.fnFI(args[0].AsNumDouble()), 0, 0)} } if ff.fnFFI != nil && n == 2 { return []Value{MakeDouble(ff.fnFFI(args[0].AsNumDouble(), args[1].AsNumDouble()), 0, 0)} } if ff.fnII != nil && n == 1 { return []Value{MakeInt(ff.fnII(args[0].AsInt()))} } // Fallback to reflect return GoCallFunc(ff.fn, args...) } // --------------------------------------------------------------------------- // 3. GoCallCached — GoCall with method cache // --------------------------------------------------------------------------- // GoCallCached is a faster version of GoCall that caches method lookups. func GoCallCached(receiver Value, method string, args ...Value) []Value { obj := UnwrapGo(receiver) if obj == nil { return []Value{MakeNil(), MakeString("nil receiver")} } rv := reflect.ValueOf(obj) m, ok := cachedMethod(rv, method) if !ok { return []Value{MakeNil(), MakeString("method not found: " + method)} } mt := m.Type() isVariadic := mt.IsVariadic() fixedCount := mt.NumIn() if isVariadic { fixedCount-- } goArgs := make([]reflect.Value, len(args)) for i, arg := range args { if i < fixedCount { goArgs[i] = valueToReflect(arg, mt.In(i)) } else if isVariadic { elemType := mt.In(mt.NumIn() - 1).Elem() goArgs[i] = valueToReflect(arg, elemType) } else { goArgs[i] = reflect.ValueOf(valueToInterface(arg)) } } results := m.Call(goArgs) hbResults := make([]Value, len(results)) for i, r := range results { hbResults[i] = reflectToValue(r) } return hbResults } // --------------------------------------------------------------------------- // 4. Function Registry — pre-register known functions // --------------------------------------------------------------------------- var ( fastFuncRegistry = make(map[string]*FastFunc) fastFuncRegistryMu sync.RWMutex ) // RegisterFastFunc registers a Go function with typed fast paths. func RegisterFastFunc(name string, fn interface{}) *FastFunc { ff := &FastFunc{name: name, fn: fn} // Auto-detect signature and set fast path switch f := fn.(type) { case func(string) string: ff.fnSS = f case func(string, string) bool: ff.fnSSB = f case func(string, string) string: ff.fnSSS = f case func(string, string, string) string: ff.fnSSSS = f case func(string) int: ff.fnSI = f case func(string, string) int: ff.fnSSI = f case func(float64) float64: ff.fnFI = f case func(float64, float64) float64: ff.fnFFI = f case func(int) int: ff.fnII = f } fastFuncRegistryMu.Lock() fastFuncRegistry[name] = ff fastFuncRegistryMu.Unlock() return ff } // GetFastFunc looks up a registered fast function. func GetFastFunc(name string) *FastFunc { fastFuncRegistryMu.RLock() ff := fastFuncRegistry[name] fastFuncRegistryMu.RUnlock() return ff }