Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow defining packages for environments #359

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions env/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ type (
parent *Env
values map[string]reflect.Value
types map[string]reflect.Type
packages map[string]map[string]reflect.Value
packageTypes map[string]map[string]reflect.Type
externalLookup ExternalLookup
}
)
Expand Down
76 changes: 76 additions & 0 deletions env/envPackage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package env

import "reflect"

// Package returns the methods for the package defined by name.
// If no package is found by this name in the environment,
// the globally defined package with this name is returned.
func (e *Env) Package(name string) map[string]reflect.Value {
e.rwMutex.RLock()
pkg, ok := e.packages[name]
e.rwMutex.RUnlock()

if ok {
return pkg
}

if e.parent == nil {
pkg, ok := Packages[name]
if ok {
return pkg
}
}

return nil
}

// DefinePackage defines methods for the package name.
func (e *Env) DefinePackage(name string, values map[string]reflect.Value) {
e.rwMutex.RLock()
defer e.rwMutex.RUnlock()

if e.packages == nil {
e.packages = map[string]map[string]reflect.Value{}
}
e.packages[name] = make(map[string]reflect.Value, len(values))
for k, v := range values {
e.packages[name][k] = v
}
}

// PackageTypes returns the types for the package defined by name.
// If no package is found by this name in the environment,
// the globally defined package with this name is returned
func (e *Env) PackageTypes(name string) map[string]reflect.Type {
e.rwMutex.RLock()
pkg, ok := e.packageTypes[name]
e.rwMutex.RUnlock()

if ok {
return pkg
}

if e.parent == nil {
pkg, ok := PackageTypes[name]
if ok {
return pkg
}
}

return nil
}

// DefinePackageTypes defines types for the package name.
func (e *Env) DefinePackageTypes(name string, types map[string]reflect.Type) {
e.rwMutex.RLock()
defer e.rwMutex.RUnlock()

if e.packageTypes == nil {
e.packageTypes = map[string]map[string]reflect.Type{}
}

e.packageTypes[name] = make(map[string]reflect.Type, len(types))
for k, v := range types {
e.packageTypes[name][k] = v
}
}
92 changes: 92 additions & 0 deletions env/envPackage_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package env

import (
"reflect"
"testing"
)

func TestDefinePackage(t *testing.T) {
e := NewEnv()

if custom := e.Package("custom"); custom != nil {
t.Fatal("unexpected \"custom\" package", custom)
}

// Define the method "hello" for the package "custom" using the global variable.
Packages["custom"] = map[string]reflect.Value{
"hello": reflect.ValueOf(func() string { return "hello" }),
}

if custom := e.Package("custom"); custom == nil {
t.Fatal("expected \"custom\" package")
} else if hello, ok := custom["hello"]; !ok {
t.Fatal("expected method definition \"hello\" in package \"custom\"")
} else if fn, ok := hello.Interface().(func() string); !ok {
t.Fatal("expected method func() string {}")
} else if res := fn(); res != "hello" {
t.Fatalf("expected return value \"hello\" but got %v", res)
}

// Re-define the method for the package "custom" at the environment level.
e.DefinePackage("custom", map[string]reflect.Value{
"hello": reflect.ValueOf(func() string { return "hallo" }),
})

if custom := e.Package("custom"); custom == nil {
t.Fatal("expected \"custom\" package")
} else if hello, ok := custom["hello"]; !ok {
t.Fatal("expected method definition \"hello\" in package \"custom\"")
} else if fn, ok := hello.Interface().(func() string); !ok {
t.Fatal("expected method func() string {}")
} else if res := fn(); res != "hallo" {
t.Fatalf("expected return value \"hallo\" but got %v", res)
}
}

func TestDefinePackageTypes(t *testing.T) {
e := NewEnv()

if custom := e.PackageTypes("custom"); custom != nil {
t.Fatal("unexpected \"custom\" package", custom)
}

type A struct {
a string
}

// Define the type "A" for the package "custom" using the global variable.
PackageTypes["custom"] = map[string]reflect.Type{
"A": reflect.TypeOf(A{}),
}

if custom := e.PackageTypes("custom"); custom == nil {
t.Fatal("expected \"custom\" package")
} else if typeA, ok := custom["A"]; !ok {
t.Fatal("expected type definition \"A\" in package \"custom\"")
} else {
a := reflect.New(typeA).Interface()
if is, want := a, (&A{}); !reflect.DeepEqual(is, want) {
t.Fatalf("%T != %T", is, want)
}
}

type B struct {
b int
}

// Re-define the type for the package "custom" at the environment level.
e.DefinePackageTypes("custom", map[string]reflect.Type{
"A": reflect.TypeOf(B{}),
})

if custom := e.PackageTypes("custom"); custom == nil {
t.Fatal("expected \"custom\" package")
} else if typeA, ok := custom["A"]; !ok {
t.Fatal("expected type definition \"A\" in package \"custom\"")
} else {
a := reflect.New(typeA).Interface()
if is, want := a, (&B{}); !reflect.DeepEqual(is, want) {
t.Fatalf("%T != %T", is, want)
}
}
}
8 changes: 4 additions & 4 deletions vm/vmExpr.go
Original file line number Diff line number Diff line change
Expand Up @@ -522,8 +522,8 @@ func (runInfo *runInfoStruct) invokeExpr() {
name := runInfo.rv.String()
runInfo.rv = nilValue

methods, ok := env.Packages[name]
if !ok {
methods := runInfo.env.Package(name)
if methods == nil {
runInfo.err = newStringError(expr, "package not found: "+name)
return
}
Expand All @@ -537,8 +537,8 @@ func (runInfo *runInfoStruct) invokeExpr() {
}
}

types, ok := env.PackageTypes[name]
if ok {
types := runInfo.env.PackageTypes(name)
if types != nil {
for typeName, typeValue := range types {
err = pack.DefineReflectType(typeName, typeValue)
if err != nil {
Expand Down