mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
internal/jsre: move runtime creation back into JSRE and clean up API
This commit is contained in:
parent
e230602627
commit
fd422db34f
3 changed files with 47 additions and 59 deletions
|
|
@ -20,12 +20,10 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/dop251/goja"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCompleteKeywords(t *testing.T) {
|
func TestCompleteKeywords(t *testing.T) {
|
||||||
re := New("", os.Stdout, goja.New())
|
re := New("", os.Stdout)
|
||||||
re.Run(`
|
re.Run(`
|
||||||
function theClass() {
|
function theClass() {
|
||||||
this.foo = 3;
|
this.foo = 3;
|
||||||
|
|
|
||||||
|
|
@ -36,14 +36,13 @@ var (
|
||||||
Web3Js = deps.MustAsset("web3.js")
|
Web3Js = deps.MustAsset("web3.js")
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
// JSRE is a JS runtime environment embedding the goja interpreter.
|
||||||
JSRE is a generic JS runtime environment embedding the goja JS interpreter.
|
// It provides helper functions to load code from files, run code snippets
|
||||||
It provides some helper functions to
|
// and bind native go objects to JS.
|
||||||
- load code from files
|
//
|
||||||
- run code snippets
|
// The runtime runs all code on a dedicated event loop and does not expose the underlying
|
||||||
- require libraries
|
// goja runtime directly. To use the runtime, call JSRE.Do. When binding a Go function,
|
||||||
- bind native go objects
|
// use the Call type to gain access to the runtime.
|
||||||
*/
|
|
||||||
type JSRE struct {
|
type JSRE struct {
|
||||||
assetPath string
|
assetPath string
|
||||||
output io.Writer
|
output io.Writer
|
||||||
|
|
@ -53,6 +52,12 @@ type JSRE struct {
|
||||||
vm *goja.Runtime
|
vm *goja.Runtime
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Call is the argument type of Go functions which are callable from JS.
|
||||||
|
type Call struct {
|
||||||
|
goja.FunctionCall
|
||||||
|
VM *goja.Runtime
|
||||||
|
}
|
||||||
|
|
||||||
// jsTimer is a single timer instance with a callback function
|
// jsTimer is a single timer instance with a callback function
|
||||||
type jsTimer struct {
|
type jsTimer struct {
|
||||||
timer *time.Timer
|
timer *time.Timer
|
||||||
|
|
@ -68,17 +73,17 @@ type evalReq struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// runtime must be stopped with Stop() after use and cannot be used after stopping
|
// runtime must be stopped with Stop() after use and cannot be used after stopping
|
||||||
func New(assetPath string, output io.Writer, vm *goja.Runtime) *JSRE {
|
func New(assetPath string, output io.Writer) *JSRE {
|
||||||
re := &JSRE{
|
re := &JSRE{
|
||||||
assetPath: assetPath,
|
assetPath: assetPath,
|
||||||
output: output,
|
output: output,
|
||||||
closed: make(chan struct{}),
|
closed: make(chan struct{}),
|
||||||
evalQueue: make(chan *evalReq),
|
evalQueue: make(chan *evalReq),
|
||||||
stopEventLoop: make(chan bool),
|
stopEventLoop: make(chan bool),
|
||||||
vm: vm,
|
vm: goja.New(),
|
||||||
}
|
}
|
||||||
go re.runEventLoop()
|
go re.runEventLoop()
|
||||||
re.Set("loadScript", re.loadScript)
|
re.Set("loadScript", MakeCallback(re.vm, re.loadScript))
|
||||||
re.Set("inspect", re.prettyPrintJS)
|
re.Set("inspect", re.prettyPrintJS)
|
||||||
return re
|
return re
|
||||||
}
|
}
|
||||||
|
|
@ -244,21 +249,7 @@ func (re *JSRE) Exec(file string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var script *goja.Program
|
return re.Compile(file, string(code))
|
||||||
re.Do(func(vm *goja.Runtime) {
|
|
||||||
script, err = goja.Compile(file, string(code), false)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, err = vm.RunProgram(script)
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bind assigns value v to a variable in the JS environment
|
|
||||||
// This method is deprecated, use Set.
|
|
||||||
func (re *JSRE) Bind(name string, v interface{}) error {
|
|
||||||
return re.Set(name, v)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run runs a piece of JS code.
|
// Run runs a piece of JS code.
|
||||||
|
|
@ -267,40 +258,25 @@ func (re *JSRE) Run(code string) (v goja.Value, err error) {
|
||||||
return v, err
|
return v, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns the value of a variable in the JS environment.
|
|
||||||
func (re *JSRE) Get(ns string) (v goja.Value) {
|
|
||||||
re.Do(func(vm *goja.Runtime) { v = vm.Get(ns) })
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set assigns value v to a variable in the JS environment.
|
// Set assigns value v to a variable in the JS environment.
|
||||||
func (re *JSRE) Set(ns string, v interface{}) (err error) {
|
func (re *JSRE) Set(ns string, v interface{}) (err error) {
|
||||||
re.Do(func(vm *goja.Runtime) { vm.Set(ns, v) })
|
re.Do(func(vm *goja.Runtime) { vm.Set(ns, v) })
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadScript executes a JS script from inside the currently executing JS code.
|
// MakeCallback turns the given function into a function that's callable by JS.
|
||||||
func (re *JSRE) loadScript(call goja.FunctionCall) goja.Value {
|
func MakeCallback(vm *goja.Runtime, fn func(Call) (goja.Value, error)) goja.Value {
|
||||||
file := call.Argument(0).ToString().String()
|
return vm.ToValue(func(call goja.FunctionCall) goja.Value {
|
||||||
file = common.AbsolutePath(re.assetPath, file)
|
result, err := fn(Call{call, vm})
|
||||||
source, err := ioutil.ReadFile(file)
|
if err != nil {
|
||||||
if err != nil {
|
panic(vm.NewGoError(err))
|
||||||
// Panicking with a goja.Value arg will cause a JS exception
|
}
|
||||||
// in the caller.
|
return result
|
||||||
panic(re.vm.ToValue(fmt.Sprintf("Could not read file %s: %v", file, err)))
|
})
|
||||||
}
|
|
||||||
value, err := compileAndRun(re.vm, file, string(source))
|
|
||||||
if err != nil {
|
|
||||||
panic(re.vm.ToValue(fmt.Sprintf("Error while compiling or running script: %v", err)))
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate executes code and pretty prints the result to the specified output
|
// Evaluate executes code and pretty prints the result to the specified output stream.
|
||||||
// stream.
|
func (re *JSRE) Evaluate(code string, w io.Writer) {
|
||||||
func (re *JSRE) Evaluate(code string, w io.Writer) error {
|
|
||||||
var fail error
|
|
||||||
|
|
||||||
re.Do(func(vm *goja.Runtime) {
|
re.Do(func(vm *goja.Runtime) {
|
||||||
val, err := vm.RunString(code)
|
val, err := vm.RunString(code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -310,7 +286,6 @@ func (re *JSRE) Evaluate(code string, w io.Writer) error {
|
||||||
}
|
}
|
||||||
fmt.Fprintln(w)
|
fmt.Fprintln(w)
|
||||||
})
|
})
|
||||||
return fail
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile compiles and then runs a piece of JS code.
|
// Compile compiles and then runs a piece of JS code.
|
||||||
|
|
@ -319,6 +294,21 @@ func (re *JSRE) Compile(filename string, src string) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// loadScript loads and executes a JS file.
|
||||||
|
func (re *JSRE) loadScript(call Call) (goja.Value, error) {
|
||||||
|
file := call.Argument(0).ToString().String()
|
||||||
|
file = common.AbsolutePath(re.assetPath, file)
|
||||||
|
source, err := ioutil.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Could not read file %s: %v", file, err)
|
||||||
|
}
|
||||||
|
value, err := compileAndRun(re.vm, file, string(source))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error while compiling or running script: %v", err)
|
||||||
|
}
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
|
||||||
func compileAndRun(vm *goja.Runtime, filename string, src string) (goja.Value, error) {
|
func compileAndRun(vm *goja.Runtime, filename string, src string) (goja.Value, error) {
|
||||||
script, err := goja.Compile(filename, src, false)
|
script, err := goja.Compile(filename, src, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ func newWithTestJS(t *testing.T, testjs string) (*JSRE, string) {
|
||||||
t.Fatal("cannot create test.js:", err)
|
t.Fatal("cannot create test.js:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jsre := New(dir, os.Stdout, goja.New())
|
jsre := New(dir, os.Stdout)
|
||||||
return jsre, dir
|
return jsre, dir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -102,10 +102,10 @@ func TestNatto(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBind(t *testing.T) {
|
func TestBind(t *testing.T) {
|
||||||
jsre := New("", os.Stdout, goja.New())
|
jsre := New("", os.Stdout)
|
||||||
defer jsre.Stop(false)
|
defer jsre.Stop(false)
|
||||||
|
|
||||||
jsre.Bind("no", &testNativeObjectBinding{vm: jsre.vm})
|
jsre.Set("no", &testNativeObjectBinding{vm: jsre.vm})
|
||||||
|
|
||||||
_, err := jsre.Run(`no.TestMethod("testMsg")`)
|
_, err := jsre.Run(`no.TestMethod("testMsg")`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue