- Compiler: PP → Lexer → Parser → Analyzer → Gengo pipeline - Parser: 232/236 (98%) Harbour compatibility, registry-based dispatch - RTL: 351 Harbour-compatible functions - RDD: DBF/NTX/CDX engines with Rushmore bitmap optimization - Go Interop: IMPORT + pkg.Func() + obj:Method() with FastPath (15M calls/sec) - HB_FUNC API: Full Harbour C API compatible Go bridge - Concurrency: SPAWN/LAUNCH/GOROUTINE, <-, WATCH, PARALLEL FOR, ASYNC/AWAIT - Extensions: Multi-return, DEFER, Slice, f-string, Nil-safe ?:, CONST - Macro Compiler: Runtime AST parsing and evaluation - Debugger: TUI debugger with source display, breakpoints, stepping - FRB: Native + Pcode dual mode runtime binary - Tests: 13 packages ALL PASS Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
207 lines
5.8 KiB
Go
207 lines
5.8 KiB
Go
// 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
|
|
}
|