Skip to content

Commit

Permalink
Merge pull request #845 from goby-lang/document-and-refactor-vm
Browse files Browse the repository at this point in the history
Document and refactor vm package
  • Loading branch information
st0012 authored Apr 21, 2020
2 parents bc5c001 + f5b0c8b commit d8cfa98
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 47 deletions.
2 changes: 1 addition & 1 deletion native/db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type (
)

func init() {
vm.RegisterExternalClass("db", vm.ExternalClass("DB", "db.gb",
vm.RegisterExternalClass("db", vm.NewExternalClassLoader("DB", "db.gb",
// class methods
map[string]vm.Method{
"get_connection": getConnection,
Expand Down
2 changes: 1 addition & 1 deletion native/plugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ var (
)

func init() {
vm.RegisterExternalClass("plugin", vm.ExternalClass("Plugin", "plugin.gb",
vm.RegisterExternalClass("plugin", vm.NewExternalClassLoader("Plugin", "plugin.gb",
// class methods
map[string]Method{
"new": newPlugin,
Expand Down
2 changes: 1 addition & 1 deletion native/result/bindings.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (

func init() {
vm.RegisterExternalClass(
"result", vm.ExternalClass(
"result", vm.NewExternalClassLoader(
"Result",
"result.gb",
map[string]vm.Method{
Expand Down
2 changes: 1 addition & 1 deletion native/ripper/ripper.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ func tokenize(receiver Object, sourceLine int, t *Thread, args []Object) Object

// Internal functions ===================================================
func init() {
vm.RegisterExternalClass("ripper", vm.ExternalClass("Ripper", "ripper.gb",
vm.RegisterExternalClass("ripper", vm.NewExternalClassLoader("Ripper", "ripper.gb",
// class methods
map[string]vm.Method{
"instruction": instruction,
Expand Down
14 changes: 7 additions & 7 deletions vm/class.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ var externalClasses = map[string][]ClassLoader{}
var externalClassLock sync.Mutex

// RegisterExternalClass will add the given class to the global registry of available classes
func RegisterExternalClass(path string, c ...ClassLoader) {
func RegisterExternalClass(name string, c ...ClassLoader) {
externalClassLock.Lock()
externalClasses[path] = c
externalClasses[name] = c
externalClassLock.Unlock()
}

Expand All @@ -57,19 +57,19 @@ func buildMethods(m map[string]Method) []*BuiltinMethodObject {
return out
}

// ExternalClass helps define external go classes
func ExternalClass(name, path string, classMethods, instanceMethods map[string]Method) ClassLoader {
// NewExternalClassLoader helps define external go classes by generating a class loader function
func NewExternalClassLoader(className, libPath string, classMethods, instanceMethods map[string]Method) ClassLoader {
return func(v *VM) error {
pg := v.initializeClass(name)
pg := v.initializeClass(className)
pg.setBuiltinMethods(buildMethods(classMethods), true)
pg.setBuiltinMethods(buildMethods(instanceMethods), false)
v.objectClass.setClassConstant(pg)

if path == "" {
if libPath == "" {
return nil
}

return v.mainThread.execGobyLib(path)
return v.mainThread.execGobyLib(libPath)
}
}

Expand Down
79 changes: 44 additions & 35 deletions vm/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,12 @@ type VM struct {
fileDir string
// args are command line arguments
args []string
// projectRoot is goby root's absolute path, which is $GOROOT/src/github.com/goby-lang/goby
projectRoot string

// libPath indicates the Goby (.gb) libraries path. Defaults to `<projectRoot>/lib`, unless
// DefaultLibPath is specified.
// Goby vm uses libPath to find standard libraries written in Goby
// the fallback order is:
// 1. $GOBY_ROOT/lib
// 2. /usr/local/Cellar/goby/VERSION/lib - installed via homebrew
// 3. $GOPATH/src/github.com/goby-lang/goby/lib - development environment
libPath string

channelObjectMap *objectMap
Expand Down Expand Up @@ -99,33 +100,10 @@ func New(fileDir string, args []string) (vm *VM, e error) {
}
vm.fileDir = fileDir

gobyRoot := os.Getenv("GOBY_ROOT")

if len(gobyRoot) == 0 {
vm.projectRoot = fmt.Sprintf("/usr/local/Cellar/goby/%s", Version)

_, err := os.Stat(vm.projectRoot)

if err != nil {
gp := os.Getenv("GOPATH")
path, _ := filepath.Abs(gp + "/src/github.com/goby-lang/goby")
_, err = os.Stat(path)

if err != nil {
e = fmt.Errorf("You haven't set $GOBY_ROOT properly")
return nil, e
}

vm.projectRoot = path
}
} else {
vm.projectRoot = gobyRoot
}
err := vm.assignLibPath()

if DefaultLibPath != "" {
vm.libPath = DefaultLibPath
} else {
vm.libPath = filepath.Join(vm.projectRoot, "lib")
if err != nil {
return nil, err
}

vm.initConstants()
Expand All @@ -150,6 +128,37 @@ func (vm *VM) newThread() (t Thread) {
return
}

// vm.assignLibPath looks up and assigns vm.libPath
func (vm *VM) assignLibPath() (err error) {
if DefaultLibPath != "" {
vm.libPath = DefaultLibPath
return
}

gobyRoot := os.Getenv("GOBY_ROOT")

if len(gobyRoot) == 0 {
// if GOBY_ROOT is not set, fallback to homebrew's path
gobyRoot = fmt.Sprintf("/usr/local/Cellar/goby/%s", Version)


// if it's not installed via homebrew, assume it's in development env and Goby's source is under GOPATH
if _, err := os.Stat(gobyRoot); err != nil {
path, _ := filepath.Abs(os.Getenv("GOPATH") + "/src/github.com/goby-lang/goby")

if _, err = os.Stat(path); err != nil {
return fmt.Errorf("You haven't set $GOBY_ROOT properly")
}

gobyRoot = path
}
}

vm.libPath = filepath.Join(gobyRoot, "lib")

return
}

// ExecInstructions accepts a sequence of bytecodes and use vm to evaluate them.
func (vm *VM) ExecInstructions(sets []*bytecode.InstructionSet, fn string) {
translator := newInstructionTranslator(fn)
Expand All @@ -172,8 +181,8 @@ func (vm *VM) ExecInstructions(sets []*bytecode.InstructionSet, fn string) {
cf.self = vm.mainObj
vm.mainThread.callFrameStack.push(cf)

// here is the final destination of Goby errors at the VM level, and we don't deal with them at this point.
// we only decide how the user program should react to them.
// here is the final destination of Goby errors at the VM level, and we don't deal with them at this point.
// we only decide how the user program should react to them.
// at vm level, we don't deal with the error itself
// we only decide how the program should react to it
defer func() {
Expand All @@ -183,8 +192,8 @@ func (vm *VM) ExecInstructions(sets []*bytecode.InstructionSet, fn string) {
case error:
panic(err)

// if the error is one of the Goby's errors, such as argument error, we need to handle it depending on the mode of execution.
// we need to handle it depends on the type of program execution
// if the error is one of the Goby's errors, such as argument error, we need to handle it depending on the mode of execution.
// we need to handle it depends on the type of program execution
case *Error:

// REPLMode: We handle the error inside the igb package, so don't need to do anything here
Expand Down Expand Up @@ -415,4 +424,4 @@ func (vm *VM) checkArgTypes(args []Object, sourceLine int, types ...string) *Err
}

return nil
}
}
2 changes: 1 addition & 1 deletion vm/vm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ func TestAutoIncrementLocalVariable(t *testing.T) {
func TestLoadingGobyLibraryFail(t *testing.T) {
vm := initTestVM()

libFileFullPath := filepath.Join(vm.projectRoot, "lib/_library_not_existing.gb")
libFileFullPath := filepath.Join(vm.libPath, "_library_not_existing.gb")
expectedErrorMessage := fmt.Sprintf("open %s: no such file or directory", libFileFullPath)

err := vm.mainThread.execGobyLib("_library_not_existing.gb")
Expand Down

0 comments on commit d8cfa98

Please sign in to comment.