Files
five/hbrt/vm.go
Charles KWON OhJun d7513eeb24 fix: Code review round 2 — race conditions, dead code, hardcoded paths
CRITICAL fixes:
- #1 vm.go: Mutex on libModules/dynamicFuncs global slices
  RegisterLibModule/RegisterDynamicFunc now thread-safe
  RegisterLibModules copies under lock, clears, releases
- #4 shutdown.go: Signal handler goroutine leak fixed
  Uses done channel + select for clean exit on normal shutdown

HIGH fixes:
- #7-8 gobridge.go: Remove dead if/else branches (both identical)
- #13-14 main.go: Remove hardcoded /mnt/d/harbour-core paths
  Use HB_INC env var + standard /usr/local/include/harbour only
- #15 main.go: Remove unused frbModSeq variable

MEDIUM fixes:
- #22 expr.go: Remove unused parts variable in parseInterpolatedString
- #51 macro.go: Remove var _ = fmt.Sprintf import guard
- macroeval.go: Remove unused lexer import and guard

Total fixed this session: 12/53 issues resolved
Remaining: 41 (CRITICAL: 1, HIGH: 9, MEDIUM: 16, LOW: 16)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 10:32:09 +09:00

178 lines
4.2 KiB
Go

// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com)
// All rights reserved.
package hbrt
import "sync"
// VM is the shared state across all threads.
type VM struct {
mu sync.RWMutex
modules []*Module
symbols map[string]*Symbol
statics map[string][]Value
threads []*Thread // all threads created (for shutdown cleanup)
waFactory func() interface{} // creates WorkAreaManager for new threads
onExit func() // called when Run() finishes (restore terminal etc.)
Debugger *Debugger // nil = no debugging; set by five debug command
}
// SetWAFactory sets the factory for creating WorkAreaManagers.
func (vm *VM) SetWAFactory(f func() interface{}) {
vm.waFactory = f
}
// SetOnExit sets a callback for when Run() finishes.
func (vm *VM) SetOnExit(f func()) {
vm.onExit = f
}
// Library modules registered via init() — protected by mutex for FRB concurrent loading.
var (
libModules []*Module
dynamicFuncs []Symbol // from HB_FUNC() in #pragma BEGINDUMP
libRegistryMu sync.Mutex
)
// RegisterLibModule registers a module from a library PRG file.
// Called by init() in generated library code.
func RegisterLibModule(m *Module) {
libRegistryMu.Lock()
libModules = append(libModules, m)
libRegistryMu.Unlock()
}
// RegisterDynamicFunc registers a Go function callable from PRG.
// Called from init() in #pragma BEGINDUMP code via HB_FUNC().
func RegisterDynamicFunc(name string, fn func(*Thread)) {
libRegistryMu.Lock()
dynamicFuncs = append(dynamicFuncs, Symbol{
Name: name,
Scope: FsPublic | FsLocal,
Func: fn,
})
libRegistryMu.Unlock()
}
// RegisterLibModules registers any pending lib modules and dynamic functions.
func (vm *VM) RegisterLibModules() {
libRegistryMu.Lock()
mods := libModules
libModules = nil
dyns := dynamicFuncs
dynamicFuncs = nil
libRegistryMu.Unlock()
for _, m := range mods {
vm.RegisterModule(m)
}
for i := range dyns {
sym := &dyns[i]
vm.RegisterSymbol(sym)
}
dynamicFuncs = nil
}
// NewVM creates a new VM instance.
func NewVM() *VM {
return &VM{
modules: make([]*Module, 0),
symbols: make(map[string]*Symbol),
statics: make(map[string][]Value),
}
}
// RegisterModule registers a module's symbols with the VM.
func (vm *VM) RegisterModule(m *Module) {
vm.mu.Lock()
defer vm.mu.Unlock()
vm.modules = append(vm.modules, m)
for i := range m.Symbols {
sym := &m.Symbols[i]
vm.symbols[sym.Name] = sym
}
}
// RegisterSymbol registers a single symbol.
func (vm *VM) RegisterSymbol(sym *Symbol) {
vm.mu.Lock()
defer vm.mu.Unlock()
vm.symbols[sym.Name] = sym
}
// UnregisterSymbol removes a symbol by name. Returns the old symbol if any.
func (vm *VM) UnregisterSymbol(name string) *Symbol {
vm.mu.Lock()
defer vm.mu.Unlock()
old := vm.symbols[name]
delete(vm.symbols, name)
return old
}
// SymbolNames returns all registered symbol names.
func (vm *VM) SymbolNames() []string {
vm.mu.RLock()
defer vm.mu.RUnlock()
names := make([]string, 0, len(vm.symbols))
for n := range vm.symbols {
names = append(names, n)
}
return names
}
// FindSymbol looks up a symbol by name.
func (vm *VM) FindSymbol(name string) *Symbol {
vm.mu.RLock()
defer vm.mu.RUnlock()
return vm.symbols[name]
}
// NewThread creates a new Thread attached to this VM.
func (vm *VM) NewThread() *Thread {
t := NewThread(vm)
vm.mu.Lock()
vm.threads = append(vm.threads, t)
vm.mu.Unlock()
return t
}
// Run starts execution from the named function.
func (vm *VM) Run(funcName string) Value {
// Register any library modules from init()
for _, m := range libModules {
vm.RegisterModule(m)
}
libModules = nil
sym := vm.FindSymbol(funcName)
if sym == nil {
panic("function not found: " + funcName)
}
if sym.Func == nil {
panic("function has no implementation: " + funcName)
}
t := vm.NewThread()
// Auto-initialize WorkAreaManager if not set
if t.WA == nil && vm.waFactory != nil {
t.WA = vm.waFactory()
}
// Copy statics to thread
vm.mu.RLock()
for k, v := range vm.statics {
t.statics[k] = v
}
vm.mu.RUnlock()
// Install signal handlers for clean shutdown
vm.InstallSignalHandlers()
// Call the function, ensure full shutdown on exit
defer vm.Shutdown()
sym.Func(t)
return t.retVal
}