Break it: forget to commit often

This commit is contained in:
kayos@tcp.direct 2022-09-19 19:54:26 -07:00
parent 12006ca82c
commit 7f8fbec295
Signed by: kayos
GPG Key ID: 4B841471B4BEE979
16 changed files with 330 additions and 189 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.idea/

View File

@ -1,31 +1,3 @@
# gode
# imply
A test project to implement a Node like javascript environment based on goja
# Example
```golang
package main
import (
"fmt"
"github.com/81120/gode/core"
)
func main() {
gode := core.New()
gode.RegisterBuildInModule()
r := gode.GetRts()
v, err := r.RunString(`
var t = require('./test.js');
t.test();
`)
if err != nil {
fmt.Print(err)
} else {
fmt.Println(v)
}
}
```
mostly a failed attempt to continue the work of [this repo](github.com/81120/gode/core), which as it turns out I don't even think is necessary - however this may be a good place to start for putting together embedded scripting for some project.

24
console.go Normal file
View File

@ -0,0 +1,24 @@
package imply
import (
"fmt"
js "github.com/dop251/goja"
)
func basicLog(call js.FunctionCall) js.Value {
str := call.Argument(0)
fmt.Println(str.String())
return str
}
// RegisterConsole register a console.basicLog to runtime
func (c *Core) RegisterConsole(log func(call js.FunctionCall) js.Value) error {
o := c.NewObject()
err := o.Set("log", log)
if err != nil {
return err
}
err = c.Set("console", o)
return err
}

View File

@ -1,21 +0,0 @@
package core
import (
"fmt"
js "github.com/dop251/goja"
)
func log(call js.FunctionCall) js.Value {
str := call.Argument(0)
fmt.Print(str.String())
return str
}
// RegisterConsole register a console.log to runtime
func RegisterConsole(c *Core) {
r := c.GetRts()
o := r.NewObject()
o.Set("log", log)
r.Set("console", o)
}

View File

@ -1 +0,0 @@
package core

View File

@ -1,59 +0,0 @@
package core
import (
"io/ioutil"
"path/filepath"
js "github.com/dop251/goja"
)
func moduleTemplate(c string) string {
return "(function(module, exports) {" + c + "\n})"
}
func createModule(c *Core) *js.Object {
r := c.GetRts()
m := r.NewObject()
e := r.NewObject()
m.Set("exports", e)
return m
}
func compileModule(p string) *js.Program {
code, _ := ioutil.ReadFile(p)
text := moduleTemplate(string(code))
prg, _ := js.Compile(p, text, false)
return prg
}
func loadModule(c *Core, p string) js.Value {
p = filepath.Clean(p)
pkg := c.Pkg[p]
if pkg != nil {
return pkg
}
prg := compileModule(p)
r := c.GetRts()
f, _ := r.RunProgram(prg)
g, _ := js.AssertFunction(f)
m := createModule(c)
jsExports := m.Get("exports")
g(jsExports, m, jsExports)
return m.Get("exports")
}
// RegisterLoader register a simple commonjs style loader to runtime
func RegisterLoader(c *Core) {
r := c.GetRts()
r.Set("require", func(call js.FunctionCall) js.Value {
p := call.Argument(0).String()
return loadModule(c, p)
})
}

View File

@ -1,33 +0,0 @@
package core
import (
js "github.com/dop251/goja"
)
// Core is the basic struct of gode
type Core struct {
Rts *js.Runtime
Pkg map[string]js.Value
}
// New create a *Core
func New() *Core {
vm := js.New()
pkg := make(map[string]js.Value)
return &Core{
Rts: vm,
Pkg: pkg,
}
}
// GetRts get the object of javascript runtime
func (c *Core) GetRts() *js.Runtime {
return c.Rts
}
// RegisterBuildInModule register some build in modules to the runtime
func (c *Core) RegisterBuildInModule() {
RegisterConsole(c)
RegisterLoader(c)
}

13
go.mod
View File

@ -1,10 +1,11 @@
module github.com/81120/gode
module git.tcp.direct/kayos/imply
go 1.13
go 1.19
require github.com/dop251/goja v0.0.0-20220915101355-d79e1b125a30
require (
github.com/dlclark/regexp2 v1.2.0 // indirect
github.com/dop251/goja v0.0.0-20190912223329-aa89e6a4c733
github.com/go-sourcemap/sourcemap v2.1.2+incompatible // indirect
golang.org/x/text v0.3.2 // indirect
github.com/dlclark/regexp2 v1.7.0 // indirect
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
golang.org/x/text v0.3.7 // indirect
)

35
go.sum
View File

@ -1,9 +1,28 @@
github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk=
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dop251/goja v0.0.0-20190912223329-aa89e6a4c733 h1:cyNc40Dx5YNEO94idePU8rhVd3dn+sd04Arh0kDBAaw=
github.com/dop251/goja v0.0.0-20190912223329-aa89e6a4c733/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA=
github.com/go-sourcemap/sourcemap v2.1.2+incompatible h1:0b/xya7BKGhXuqFESKM4oIiRo9WOt2ebz7KxfreD6ug=
github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo=
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
github.com/dop251/goja v0.0.0-20220915101355-d79e1b125a30 h1:ygMJa3f5Uw4JHQo9n52aSFHYxdRvZWPOoihpDK8hCPs=
github.com/dop251/goja v0.0.0-20220915101355-d79e1b125a30/go.mod h1:yRkwfj0CBpOGre+TwBsqPV0IH0Pk73e4PXJOeNDboGs=
github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=

46
imply.go Normal file
View File

@ -0,0 +1,46 @@
package imply
import (
"sync"
js "github.com/dop251/goja"
)
// Core is the basic struct of gode
type Core struct {
*js.Runtime
*sync.RWMutex
Pkg map[string]js.Value
}
// NewCore creates a pointer to a new Core.
// A Core is a type that is used to organize a collection of runtime javascript modules.
func NewCore() *Core {
return &Core{
Runtime: js.New(),
RWMutex: &sync.RWMutex{},
Pkg: make(map[string]js.Value),
}
}
// RegisterBuiltins register some build in modules to the runtime
func (c *Core) RegisterBuiltins() error {
if err := c.RegisterConsole(basicLog); err != nil {
return err
}
return c.RegisterLoader()
}
// RegisterLoader registers a simple commonjs style loader to runtime.
func (c *Core) RegisterLoader() error {
err := c.Set("require", func(call js.FunctionCall) js.Value {
var val js.Value
var err error
val, err = c.LoadJSPackageFile(call.Argument(0).String())
if err != nil {
return c.NewGoError(err)
}
return val
})
return err
}

100
loader.go Normal file
View File

@ -0,0 +1,100 @@
package imply
import (
"fmt"
"os"
js "github.com/dop251/goja"
)
func moduleTemplate(c string) string {
return "(function(module, exports) {\n" + c + "\n})"
}
func (c *Core) createPack() (*js.Object, error) {
m := c.NewObject()
e := c.NewObject()
err := m.Set("exports", e)
return m, err
}
func load(name string, code []byte) (*js.Program, error) {
text := moduleTemplate(string(code))
return js.Compile(name, text, false)
}
func (c *Core) validate(prg *js.Program) (jsExports js.Value, err error) {
var f js.Value
f, err = c.RunProgram(prg)
if err != nil {
return
}
g, ok := js.AssertFunction(f)
if !ok {
err = fmt.Errorf("[%T] %v is not a function", f, f)
return
}
pkg, err := c.createPack()
if err != nil {
return
}
jsExports = pkg.Get("exports")
_, err = g(jsExports, pkg, jsExports)
if err != nil {
return
}
// fmt.Printf("VALIDATED: [%T] %v\n", jsExports, spew.Sprint(jsExports))
return
}
func (c *Core) checkExisting(p string) (js.Value, bool) {
c.RLock()
defer c.RUnlock()
val, ok := c.Pkg[p]
return val, ok
}
func (c *Core) registerModule(p string, jsExports js.Value) {
c.Lock()
c.Pkg[p] = jsExports
c.Unlock()
}
func (c *Core) validateAndRegister(name string, prg *js.Program) (jsExports js.Value, err error) {
if jsExports, err = c.validate(prg); err != nil {
return nil, err
}
if jsExports == nil {
return nil, fmt.Errorf("module %s does not export anything", name)
}
c.registerModule(name, jsExports)
return jsExports, nil
}
// LoadJSPackageFile loads a javascript package from a file, validates it, and registers it.
// The given path will be used as the name of the module.
//
// Warning: If name is already registered, the existing module will be returned.
func (c *Core) LoadJSPackageFile(path string) (js.Value, error) {
code, err := os.ReadFile(path)
if err != nil {
// println("!!ERR!! " + err.Error())
return nil, err
}
// println("new: " + path)
return c.LoadJSPackage(path, code)
}
// LoadJSPackage loads a javascript package from code, validates it, and registers it by name.
//
// Warning: If name is already registered, the existing module will be returned.
func (c *Core) LoadJSPackage(name string, code []byte) (js.Value, error) {
if mod, ok := c.checkExisting(name); ok {
return mod, nil
}
prg, err := load(name, code)
if err != nil {
return nil, err
}
return c.validateAndRegister(name, prg)
}

99
loader_test.go Normal file
View File

@ -0,0 +1,99 @@
package imply
import (
"strings"
"testing"
js "github.com/dop251/goja"
)
type testConsole struct {
t *testing.T
}
func (tc *testConsole) log(call js.FunctionCall) js.Value {
tc.t.Helper()
ret := call.Argument(0)
tc.t.Logf("[JSLOG]%v: [%T] %v", call.This, ret, ret)
return ret
}
func newTestConsole(t *testing.T) *testConsole {
t.Helper()
return &testConsole{t}
}
func testCore(t *testing.T) *Core {
t.Helper()
core := NewCore()
tc := newTestConsole(t)
if err := core.RegisterConsole(tc.log); err != nil {
t.Fatalf("RegisterConsole failed: %s", err)
}
if err := core.RegisterLoader(); err != nil {
t.Fatalf("RegisterLoader failed: %s", err)
}
_, err := core.RunString("console.log('init');")
if err != nil {
t.Fatalf("failed sanity check: %v", err)
}
return core
}
func ExampleNewCore() {
core := NewCore()
if err := core.RegisterBuiltins(); err != nil {
panic(err)
}
}
func TestImply(t *testing.T) {
const (
one = "let t = require('tests/"
two = "test.js');\n"
three = "test-mustfail.js');\n"
)
var base *strings.Builder
var want = "Hello Golang~"
res := func(base *strings.Builder, js string, shouldFail bool) {
core := testCore(t)
jscode := base.String() + js
t.Logf("Running:\n%s", jscode)
v, err := core.RunString(jscode)
switch {
case err != nil && !shouldFail:
t.Errorf("RunString failed: %s", err)
case v == nil && !shouldFail:
t.Errorf("RunString failed: got nil")
case v != nil && v.String() != want && !shouldFail:
t.Errorf("RunString failed.\nwanted: %s\ngot: %s", want, v.String())
case err == nil && shouldFail:
t.Errorf("RunString should have failed, got nil error")
case v != nil && shouldFail:
t.Errorf("RunString result should have been nil!\nGot: [%T] %v", v, v)
case err != nil && shouldFail:
// t.Logf("success, wanted err: [%T] %v", err, err)
}
}
t.Run("Simple", func(t *testing.T) {
base = &strings.Builder{}
base.WriteString(one)
base.WriteString(two)
res(base, "t.test();", false)
res(base, "t.foo();", false)
res(base, "t.bar();", true)
base.Reset()
})
t.Run("InvalidSyntax", func(t *testing.T) {
base = &strings.Builder{}
base.WriteString(one)
base.WriteString(three)
res(base, "t.test();", true)
base.Reset()
})
}

23
main.go
View File

@ -1,23 +0,0 @@
package main
import (
"fmt"
"github.com/81120/gode/core"
)
func main() {
gode := core.New()
gode.RegisterBuildInModule()
r := gode.GetRts()
v, err := r.RunString(`
var t = require('./test.js');
t.test();
`)
if err != nil {
fmt.Print(err)
} else {
fmt.Println(v)
}
}

View File

@ -1,8 +0,0 @@
function test() {
console.log('hello world\n');
return 'Hello Golang~';
}
module.exports = {
test: test,
};

11
tests/test-mustfail.js Normal file
View File

@ -0,0 +1,11 @@
function test() {
console.log('hello world\n');
return 'Hello Golang~';;;;;;
fasd;lkfgaderogkdrfoa
;l!#$!@1
}
module.exports = {
bad: test,
nope: test,
};

13
tests/test.js Normal file
View File

@ -0,0 +1,13 @@
function test() {
console.log('hello world\n');
return 'Hello Golang~';
}
function bar() {
return self.foo();
}
module.exports = {
test: test,
foo: test,
};