forked from cerc-io/plugeth
cmd/geth, jsre, rpc: run all JS code on the event loop
Some JSRE methods (PrettyPrint, ToVal) bypassed the event loop. All calls to the JS VM are now wrapped. In order to make this somewhat more foolproof, the otto VM is now a local variable inside the event loop.
This commit is contained in:
parent
394826f520
commit
e221a449e0
@ -144,7 +144,8 @@ func (js *jsre) pendingTransactions(call otto.FunctionCall) otto.Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return js.re.ToVal(ltxs)
|
v, _ := call.Otto.ToValue(ltxs)
|
||||||
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (js *jsre) resend(call otto.FunctionCall) otto.Value {
|
func (js *jsre) resend(call otto.FunctionCall) otto.Value {
|
||||||
@ -175,7 +176,8 @@ func (js *jsre) resend(call otto.FunctionCall) otto.Value {
|
|||||||
}
|
}
|
||||||
js.ethereum.TxPool().RemoveTransactions(types.Transactions{tx.tx})
|
js.ethereum.TxPool().RemoveTransactions(types.Transactions{tx.tx})
|
||||||
|
|
||||||
return js.re.ToVal(ret)
|
v, _ := call.Otto.ToValue(ret)
|
||||||
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("first argument must be a transaction")
|
fmt.Println("first argument must be a transaction")
|
||||||
@ -198,12 +200,13 @@ func (js *jsre) sign(call otto.FunctionCall) otto.Value {
|
|||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return otto.UndefinedValue()
|
return otto.UndefinedValue()
|
||||||
}
|
}
|
||||||
v, err := js.xeth.Sign(signer, data, false)
|
signed, err := js.xeth.Sign(signer, data, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return otto.UndefinedValue()
|
return otto.UndefinedValue()
|
||||||
}
|
}
|
||||||
return js.re.ToVal(v)
|
v, _ := call.Otto.ToValue(signed)
|
||||||
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (js *jsre) debugBlock(call otto.FunctionCall) otto.Value {
|
func (js *jsre) debugBlock(call otto.FunctionCall) otto.Value {
|
||||||
@ -237,8 +240,8 @@ func (js *jsre) setHead(call otto.FunctionCall) otto.Value {
|
|||||||
|
|
||||||
func (js *jsre) downloadProgress(call otto.FunctionCall) otto.Value {
|
func (js *jsre) downloadProgress(call otto.FunctionCall) otto.Value {
|
||||||
current, max := js.ethereum.Downloader().Stats()
|
current, max := js.ethereum.Downloader().Stats()
|
||||||
|
v, _ := call.Otto.ToValue(fmt.Sprintf("%d/%d", current, max))
|
||||||
return js.re.ToVal(fmt.Sprintf("%d/%d", current, max))
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (js *jsre) getBlockRlp(call otto.FunctionCall) otto.Value {
|
func (js *jsre) getBlockRlp(call otto.FunctionCall) otto.Value {
|
||||||
@ -248,7 +251,8 @@ func (js *jsre) getBlockRlp(call otto.FunctionCall) otto.Value {
|
|||||||
return otto.UndefinedValue()
|
return otto.UndefinedValue()
|
||||||
}
|
}
|
||||||
encoded, _ := rlp.EncodeToBytes(block)
|
encoded, _ := rlp.EncodeToBytes(block)
|
||||||
return js.re.ToVal(fmt.Sprintf("%x", encoded))
|
v, _ := call.Otto.ToValue(fmt.Sprintf("%x", encoded))
|
||||||
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (js *jsre) setExtra(call otto.FunctionCall) otto.Value {
|
func (js *jsre) setExtra(call otto.FunctionCall) otto.Value {
|
||||||
@ -278,8 +282,9 @@ func (js *jsre) setGasPrice(call otto.FunctionCall) otto.Value {
|
|||||||
return otto.UndefinedValue()
|
return otto.UndefinedValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (js *jsre) hashrate(otto.FunctionCall) otto.Value {
|
func (js *jsre) hashrate(call otto.FunctionCall) otto.Value {
|
||||||
return js.re.ToVal(js.ethereum.Miner().HashRate())
|
v, _ := call.Otto.ToValue(js.ethereum.Miner().HashRate())
|
||||||
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (js *jsre) makeDAG(call otto.FunctionCall) otto.Value {
|
func (js *jsre) makeDAG(call otto.FunctionCall) otto.Value {
|
||||||
@ -495,15 +500,18 @@ func (js *jsre) newAccount(call otto.FunctionCall) otto.Value {
|
|||||||
fmt.Printf("Could not create the account: %v", err)
|
fmt.Printf("Could not create the account: %v", err)
|
||||||
return otto.UndefinedValue()
|
return otto.UndefinedValue()
|
||||||
}
|
}
|
||||||
return js.re.ToVal(acct.Address.Hex())
|
v, _ := call.Otto.ToValue(acct.Address.Hex())
|
||||||
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (js *jsre) nodeInfo(call otto.FunctionCall) otto.Value {
|
func (js *jsre) nodeInfo(call otto.FunctionCall) otto.Value {
|
||||||
return js.re.ToVal(js.ethereum.NodeInfo())
|
v, _ := call.Otto.ToValue(js.ethereum.NodeInfo())
|
||||||
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (js *jsre) peers(call otto.FunctionCall) otto.Value {
|
func (js *jsre) peers(call otto.FunctionCall) otto.Value {
|
||||||
return js.re.ToVal(js.ethereum.PeersInfo())
|
v, _ := call.Otto.ToValue(js.ethereum.PeersInfo())
|
||||||
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (js *jsre) importChain(call otto.FunctionCall) otto.Value {
|
func (js *jsre) importChain(call otto.FunctionCall) otto.Value {
|
||||||
@ -562,7 +570,8 @@ func (js *jsre) dumpBlock(call otto.FunctionCall) otto.Value {
|
|||||||
|
|
||||||
statedb := state.New(block.Root(), js.ethereum.StateDb())
|
statedb := state.New(block.Root(), js.ethereum.StateDb())
|
||||||
dump := statedb.RawDump()
|
dump := statedb.RawDump()
|
||||||
return js.re.ToVal(dump)
|
v, _ := call.Otto.ToValue(dump)
|
||||||
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (js *jsre) waitForBlocks(call otto.FunctionCall) otto.Value {
|
func (js *jsre) waitForBlocks(call otto.FunctionCall) otto.Value {
|
||||||
@ -611,7 +620,8 @@ func (js *jsre) waitForBlocks(call otto.FunctionCall) otto.Value {
|
|||||||
return otto.UndefinedValue()
|
return otto.UndefinedValue()
|
||||||
case height = <-wait:
|
case height = <-wait:
|
||||||
}
|
}
|
||||||
return js.re.ToVal(height.Uint64())
|
v, _ := call.Otto.ToValue(height.Uint64())
|
||||||
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (js *jsre) sleep(call otto.FunctionCall) otto.Value {
|
func (js *jsre) sleep(call otto.FunctionCall) otto.Value {
|
||||||
@ -704,8 +714,8 @@ func (js *jsre) register(call otto.FunctionCall) otto.Value {
|
|||||||
return otto.UndefinedValue()
|
return otto.UndefinedValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
return js.re.ToVal(contenthash.Hex())
|
v, _ := call.Otto.ToValue(contenthash.Hex())
|
||||||
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (js *jsre) registerUrl(call otto.FunctionCall) otto.Value {
|
func (js *jsre) registerUrl(call otto.FunctionCall) otto.Value {
|
||||||
@ -764,7 +774,8 @@ func (js *jsre) getContractInfo(call otto.FunctionCall) otto.Value {
|
|||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return otto.UndefinedValue()
|
return otto.UndefinedValue()
|
||||||
}
|
}
|
||||||
return js.re.ToVal(info)
|
v, _ := call.Otto.ToValue(info)
|
||||||
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (js *jsre) startNatSpec(call otto.FunctionCall) otto.Value {
|
func (js *jsre) startNatSpec(call otto.FunctionCall) otto.Value {
|
||||||
|
@ -104,7 +104,7 @@ func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain string, interactive boo
|
|||||||
func (js *jsre) apiBindings(f xeth.Frontend) {
|
func (js *jsre) apiBindings(f xeth.Frontend) {
|
||||||
xe := xeth.New(js.ethereum, f)
|
xe := xeth.New(js.ethereum, f)
|
||||||
ethApi := rpc.NewEthereumApi(xe)
|
ethApi := rpc.NewEthereumApi(xe)
|
||||||
jeth := rpc.NewJeth(ethApi, js.re.ToVal, js.re)
|
jeth := rpc.NewJeth(ethApi, js.re)
|
||||||
|
|
||||||
js.re.Set("jeth", struct{}{})
|
js.re.Set("jeth", struct{}{})
|
||||||
t, _ := js.re.Get("jeth")
|
t, _ := js.re.Get("jeth")
|
||||||
|
239
jsre/jsre.go
239
jsre/jsre.go
@ -19,9 +19,7 @@ It provides some helper functions to
|
|||||||
- bind native go objects
|
- bind native go objects
|
||||||
*/
|
*/
|
||||||
type JSRE struct {
|
type JSRE struct {
|
||||||
assetPath string
|
assetPath string
|
||||||
vm *otto.Otto
|
|
||||||
|
|
||||||
evalQueue chan *evalReq
|
evalQueue chan *evalReq
|
||||||
stopEventLoop chan bool
|
stopEventLoop chan bool
|
||||||
loopWg sync.WaitGroup
|
loopWg sync.WaitGroup
|
||||||
@ -35,68 +33,37 @@ type jsTimer struct {
|
|||||||
call otto.FunctionCall
|
call otto.FunctionCall
|
||||||
}
|
}
|
||||||
|
|
||||||
// evalResult is a structure to store the result of any serialized vm execution
|
// evalReq is a serialized vm execution request processed by runEventLoop.
|
||||||
type evalResult struct {
|
|
||||||
result otto.Value
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
// evalReq is a serialized vm execution request put in evalQueue and processed by runEventLoop
|
|
||||||
type evalReq struct {
|
type evalReq struct {
|
||||||
fn func(res *evalResult)
|
fn func(vm *otto.Otto)
|
||||||
done chan bool
|
done chan bool
|
||||||
res evalResult
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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) *JSRE {
|
func New(assetPath string) *JSRE {
|
||||||
re := &JSRE{
|
re := &JSRE{
|
||||||
assetPath: assetPath,
|
assetPath: assetPath,
|
||||||
vm: otto.New(),
|
evalQueue: make(chan *evalReq),
|
||||||
|
stopEventLoop: make(chan bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
// load prettyprint func definition
|
|
||||||
re.vm.Run(pp_js)
|
|
||||||
re.vm.Set("loadScript", re.loadScript)
|
|
||||||
|
|
||||||
re.evalQueue = make(chan *evalReq)
|
|
||||||
re.stopEventLoop = make(chan bool)
|
|
||||||
re.loopWg.Add(1)
|
re.loopWg.Add(1)
|
||||||
go re.runEventLoop()
|
go re.runEventLoop()
|
||||||
|
re.Compile("pp.js", pp_js) // load prettyprint func definition
|
||||||
|
re.Set("loadScript", re.loadScript)
|
||||||
return re
|
return re
|
||||||
}
|
}
|
||||||
|
|
||||||
// this function runs a piece of JS code either in a serialized way (when useEQ is true) or instantly, circumventing the evalQueue
|
// This function runs the main event loop from a goroutine that is started
|
||||||
func (self *JSRE) run(src interface{}, useEQ bool) (value otto.Value, err error) {
|
// when JSRE is created. Use Stop() before exiting to properly stop it.
|
||||||
if useEQ {
|
// The event loop processes vm access requests from the evalQueue in a
|
||||||
done := make(chan bool)
|
// serialized way and calls timer callback functions at the appropriate time.
|
||||||
req := &evalReq{
|
|
||||||
fn: func(res *evalResult) {
|
|
||||||
res.result, res.err = self.vm.Run(src)
|
|
||||||
},
|
|
||||||
done: done,
|
|
||||||
}
|
|
||||||
self.evalQueue <- req
|
|
||||||
<-done
|
|
||||||
return req.res.result, req.res.err
|
|
||||||
} else {
|
|
||||||
return self.vm.Run(src)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
// Exported functions always access the vm through the event queue. You can
|
||||||
This function runs the main event loop from a goroutine that is started
|
// call the functions of the otto vm directly to circumvent the queue. These
|
||||||
when JSRE is created. Use Stop() before exiting to properly stop it.
|
// functions should be used if and only if running a routine that was already
|
||||||
The event loop processes vm access requests from the evalQueue in a
|
// called from JS through an RPC call.
|
||||||
serialized way and calls timer callback functions at the appropriate time.
|
|
||||||
|
|
||||||
Exported functions always access the vm through the event queue. You can
|
|
||||||
call the functions of the otto vm directly to circumvent the queue. These
|
|
||||||
functions should be used if and only if running a routine that was already
|
|
||||||
called from JS through an RPC call.
|
|
||||||
*/
|
|
||||||
func (self *JSRE) runEventLoop() {
|
func (self *JSRE) runEventLoop() {
|
||||||
|
vm := otto.New()
|
||||||
registry := map[*jsTimer]*jsTimer{}
|
registry := map[*jsTimer]*jsTimer{}
|
||||||
ready := make(chan *jsTimer)
|
ready := make(chan *jsTimer)
|
||||||
|
|
||||||
@ -143,10 +110,10 @@ func (self *JSRE) runEventLoop() {
|
|||||||
}
|
}
|
||||||
return otto.UndefinedValue()
|
return otto.UndefinedValue()
|
||||||
}
|
}
|
||||||
self.vm.Set("setTimeout", setTimeout)
|
vm.Set("setTimeout", setTimeout)
|
||||||
self.vm.Set("setInterval", setInterval)
|
vm.Set("setInterval", setInterval)
|
||||||
self.vm.Set("clearTimeout", clearTimeout)
|
vm.Set("clearTimeout", clearTimeout)
|
||||||
self.vm.Set("clearInterval", clearTimeout)
|
vm.Set("clearInterval", clearTimeout)
|
||||||
|
|
||||||
var waitForCallbacks bool
|
var waitForCallbacks bool
|
||||||
|
|
||||||
@ -166,8 +133,7 @@ loop:
|
|||||||
arguments = make([]interface{}, 1)
|
arguments = make([]interface{}, 1)
|
||||||
}
|
}
|
||||||
arguments[0] = timer.call.ArgumentList[0]
|
arguments[0] = timer.call.ArgumentList[0]
|
||||||
_, err := self.vm.Call(`Function.call.call`, nil, arguments...)
|
_, err := vm.Call(`Function.call.call`, nil, arguments...)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("js error:", err, arguments)
|
fmt.Println("js error:", err, arguments)
|
||||||
}
|
}
|
||||||
@ -179,10 +145,10 @@ loop:
|
|||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case evalReq := <-self.evalQueue:
|
case req := <-self.evalQueue:
|
||||||
// run the code, send the result back
|
// run the code, send the result back
|
||||||
evalReq.fn(&evalReq.res)
|
req.fn(vm)
|
||||||
close(evalReq.done)
|
close(req.done)
|
||||||
if waitForCallbacks && (len(registry) == 0) {
|
if waitForCallbacks && (len(registry) == 0) {
|
||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
@ -201,6 +167,14 @@ loop:
|
|||||||
self.loopWg.Done()
|
self.loopWg.Done()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// do schedules the given function on the event loop.
|
||||||
|
func (self *JSRE) do(fn func(*otto.Otto)) {
|
||||||
|
done := make(chan bool)
|
||||||
|
req := &evalReq{fn, done}
|
||||||
|
self.evalQueue <- req
|
||||||
|
<-done
|
||||||
|
}
|
||||||
|
|
||||||
// stops the event loop before exit, optionally waits for all timers to expire
|
// stops the event loop before exit, optionally waits for all timers to expire
|
||||||
func (self *JSRE) Stop(waitForCallbacks bool) {
|
func (self *JSRE) Stop(waitForCallbacks bool) {
|
||||||
self.stopEventLoop <- waitForCallbacks
|
self.stopEventLoop <- waitForCallbacks
|
||||||
@ -210,119 +184,78 @@ func (self *JSRE) Stop(waitForCallbacks bool) {
|
|||||||
// Exec(file) loads and runs the contents of a file
|
// Exec(file) loads and runs the contents of a file
|
||||||
// if a relative path is given, the jsre's assetPath is used
|
// if a relative path is given, the jsre's assetPath is used
|
||||||
func (self *JSRE) Exec(file string) error {
|
func (self *JSRE) Exec(file string) error {
|
||||||
return self.exec(common.AbsolutePath(self.assetPath, file), true)
|
code, err := ioutil.ReadFile(common.AbsolutePath(self.assetPath, file))
|
||||||
}
|
|
||||||
|
|
||||||
// circumvents the eval queue, see runEventLoop
|
|
||||||
func (self *JSRE) execWithoutEQ(file string) error {
|
|
||||||
return self.exec(common.AbsolutePath(self.assetPath, file), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *JSRE) exec(path string, useEQ bool) error {
|
|
||||||
code, err := ioutil.ReadFile(path)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = self.run(code, useEQ)
|
self.do(func(vm *otto.Otto) { _, err = vm.Run(code) })
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// assigns value v to a variable in the JS environment
|
// Bind assigns value v to a variable in the JS environment
|
||||||
func (self *JSRE) Bind(name string, v interface{}) (err error) {
|
// This method is deprecated, use Set.
|
||||||
self.Set(name, v)
|
func (self *JSRE) Bind(name string, v interface{}) error {
|
||||||
return
|
return self.Set(name, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// runs a piece of JS code
|
// Run runs a piece of JS code.
|
||||||
func (self *JSRE) Run(code string) (otto.Value, error) {
|
func (self *JSRE) Run(code string) (v otto.Value, err error) {
|
||||||
return self.run(code, true)
|
self.do(func(vm *otto.Otto) { v, err = vm.Run(code) })
|
||||||
|
return v, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns the value of a variable in the JS environment
|
// Get returns the value of a variable in the JS environment.
|
||||||
func (self *JSRE) Get(ns string) (otto.Value, error) {
|
func (self *JSRE) Get(ns string) (v otto.Value, err error) {
|
||||||
done := make(chan bool)
|
self.do(func(vm *otto.Otto) { v, err = vm.Get(ns) })
|
||||||
req := &evalReq{
|
return v, err
|
||||||
fn: func(res *evalResult) {
|
|
||||||
res.result, res.err = self.vm.Get(ns)
|
|
||||||
},
|
|
||||||
done: done,
|
|
||||||
}
|
|
||||||
self.evalQueue <- req
|
|
||||||
<-done
|
|
||||||
return req.res.result, req.res.err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// assigns value v to a variable in the JS environment
|
// Set assigns value v to a variable in the JS environment.
|
||||||
func (self *JSRE) Set(ns string, v interface{}) error {
|
func (self *JSRE) Set(ns string, v interface{}) (err error) {
|
||||||
done := make(chan bool)
|
self.do(func(vm *otto.Otto) { err = vm.Set(ns, v) })
|
||||||
req := &evalReq{
|
return err
|
||||||
fn: func(res *evalResult) {
|
|
||||||
res.err = self.vm.Set(ns, v)
|
|
||||||
},
|
|
||||||
done: done,
|
|
||||||
}
|
|
||||||
self.evalQueue <- req
|
|
||||||
<-done
|
|
||||||
return req.res.err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// loadScript executes a JS script from inside the currently executing JS code.
|
||||||
Executes a JS script from inside the currently executing JS code.
|
|
||||||
Should only be called from inside an RPC routine.
|
|
||||||
*/
|
|
||||||
func (self *JSRE) loadScript(call otto.FunctionCall) otto.Value {
|
func (self *JSRE) loadScript(call otto.FunctionCall) otto.Value {
|
||||||
file, err := call.Argument(0).ToString()
|
file, err := call.Argument(0).ToString()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// TODO: throw exception
|
||||||
return otto.FalseValue()
|
return otto.FalseValue()
|
||||||
}
|
}
|
||||||
if err := self.execWithoutEQ(file); err != nil { // loadScript is only called from inside js
|
file = common.AbsolutePath(self.assetPath, file)
|
||||||
|
source, err := ioutil.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
// TODO: throw exception
|
||||||
|
return otto.FalseValue()
|
||||||
|
}
|
||||||
|
if _, err := compileAndRun(call.Otto, file, source); err != nil {
|
||||||
|
// TODO: throw exception
|
||||||
fmt.Println("err:", err)
|
fmt.Println("err:", err)
|
||||||
return otto.FalseValue()
|
return otto.FalseValue()
|
||||||
}
|
}
|
||||||
|
// TODO: return evaluation result
|
||||||
return otto.TrueValue()
|
return otto.TrueValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
// uses the "prettyPrint" JS function to format a value
|
// PrettyPrint writes v to standard output.
|
||||||
func (self *JSRE) PrettyPrint(v interface{}) (val otto.Value, err error) {
|
func (self *JSRE) PrettyPrint(v interface{}) (val otto.Value, err error) {
|
||||||
var method otto.Value
|
var method otto.Value
|
||||||
v, err = self.ToValue(v)
|
self.do(func(vm *otto.Otto) {
|
||||||
if err != nil {
|
val, err = vm.ToValue(v)
|
||||||
return
|
if err != nil {
|
||||||
}
|
return
|
||||||
method, err = self.vm.Get("prettyPrint")
|
}
|
||||||
if err != nil {
|
method, err = vm.Get("prettyPrint")
|
||||||
return
|
if err != nil {
|
||||||
}
|
return
|
||||||
return method.Call(method, v)
|
}
|
||||||
|
val, err = method.Call(method, val)
|
||||||
|
})
|
||||||
|
return val, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// creates an otto value from a go type (serialized version)
|
// Eval evaluates JS function and returns result in a pretty printed string format.
|
||||||
func (self *JSRE) ToValue(v interface{}) (otto.Value, error) {
|
|
||||||
done := make(chan bool)
|
|
||||||
req := &evalReq{
|
|
||||||
fn: func(res *evalResult) {
|
|
||||||
res.result, res.err = self.vm.ToValue(v)
|
|
||||||
},
|
|
||||||
done: done,
|
|
||||||
}
|
|
||||||
self.evalQueue <- req
|
|
||||||
<-done
|
|
||||||
return req.res.result, req.res.err
|
|
||||||
}
|
|
||||||
|
|
||||||
// creates an otto value from a go type (non-serialized version)
|
|
||||||
func (self *JSRE) ToVal(v interface{}) otto.Value {
|
|
||||||
|
|
||||||
result, err := self.vm.ToValue(v)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Value unknown:", err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluates JS function and returns result in a pretty printed string format
|
|
||||||
func (self *JSRE) Eval(code string) (s string, err error) {
|
func (self *JSRE) Eval(code string) (s string, err error) {
|
||||||
var val otto.Value
|
var val otto.Value
|
||||||
val, err = self.Run(code)
|
val, err = self.Run(code)
|
||||||
@ -336,12 +269,16 @@ func (self *JSRE) Eval(code string) (s string, err error) {
|
|||||||
return fmt.Sprintf("%v", val), nil
|
return fmt.Sprintf("%v", val), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// compiles and then runs a piece of JS code
|
// Compile compiles and then runs a piece of JS code.
|
||||||
func (self *JSRE) Compile(fn string, src interface{}) error {
|
func (self *JSRE) Compile(filename string, src interface{}) (err error) {
|
||||||
script, err := self.vm.Compile(fn, src)
|
self.do(func(vm *otto.Otto) { _, err = compileAndRun(vm, filename, src) })
|
||||||
if err != nil {
|
return err
|
||||||
return err
|
}
|
||||||
}
|
|
||||||
self.run(script, true)
|
func compileAndRun(vm *otto.Otto, filename string, src interface{}) (otto.Value, error) {
|
||||||
return nil
|
script, err := vm.Compile(filename, src)
|
||||||
|
if err != nil {
|
||||||
|
return otto.Value{}, err
|
||||||
|
}
|
||||||
|
return vm.Run(script)
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,15 @@
|
|||||||
package jsre
|
package jsre
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/robertkrimen/otto"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/robertkrimen/otto"
|
||||||
)
|
)
|
||||||
|
|
||||||
type testNativeObjectBinding struct {
|
type testNativeObjectBinding struct{}
|
||||||
toVal func(interface{}) otto.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
type msg struct {
|
type msg struct {
|
||||||
Msg string
|
Msg string
|
||||||
@ -21,7 +20,8 @@ func (no *testNativeObjectBinding) TestMethod(call otto.FunctionCall) otto.Value
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return otto.UndefinedValue()
|
return otto.UndefinedValue()
|
||||||
}
|
}
|
||||||
return no.toVal(&msg{m})
|
v, _ := call.Otto.ToValue(&msg{m})
|
||||||
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExec(t *testing.T) {
|
func TestExec(t *testing.T) {
|
||||||
@ -74,7 +74,7 @@ func TestNatto(t *testing.T) {
|
|||||||
func TestBind(t *testing.T) {
|
func TestBind(t *testing.T) {
|
||||||
jsre := New("/tmp")
|
jsre := New("/tmp")
|
||||||
|
|
||||||
jsre.Bind("no", &testNativeObjectBinding{jsre.ToVal})
|
jsre.Bind("no", &testNativeObjectBinding{})
|
||||||
|
|
||||||
val, err := jsre.Run(`no.TestMethod("testMsg")`)
|
val, err := jsre.Run(`no.TestMethod("testMsg")`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -3,18 +3,18 @@ package rpc
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/jsre"
|
"github.com/ethereum/go-ethereum/jsre"
|
||||||
"github.com/robertkrimen/otto"
|
"github.com/robertkrimen/otto"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Jeth struct {
|
type Jeth struct {
|
||||||
ethApi *EthereumApi
|
ethApi *EthereumApi
|
||||||
toVal func(interface{}) otto.Value
|
|
||||||
re *jsre.JSRE
|
re *jsre.JSRE
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewJeth(ethApi *EthereumApi, toVal func(interface{}) otto.Value, re *jsre.JSRE) *Jeth {
|
func NewJeth(ethApi *EthereumApi, re *jsre.JSRE) *Jeth {
|
||||||
return &Jeth{ethApi, toVal, re}
|
return &Jeth{ethApi, re}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Jeth) err(call otto.FunctionCall, code int, msg string, id interface{}) (response otto.Value) {
|
func (self *Jeth) err(call otto.FunctionCall, code int, msg string, id interface{}) (response otto.Value) {
|
||||||
|
Loading…
Reference in New Issue
Block a user