aboutsummaryrefslogtreecommitdiffstats
path: root/jsre/jsre.go
diff options
context:
space:
mode:
authorPéter Szilágyi <peterke@gmail.com>2016-05-06 17:40:23 +0800
committerPéter Szilágyi <peterke@gmail.com>2016-05-30 22:25:23 +0800
commitffaf58f0a98bd987bbe76e8669bb22c405dcd62a (patch)
tree4cd484504d4a7089cfd85d22a2a9fa62c79abb7d /jsre/jsre.go
parentab664c7e17009729d1083d8d4f7c37eb387012d3 (diff)
downloaddexon-ffaf58f0a98bd987bbe76e8669bb22c405dcd62a.tar
dexon-ffaf58f0a98bd987bbe76e8669bb22c405dcd62a.tar.gz
dexon-ffaf58f0a98bd987bbe76e8669bb22c405dcd62a.tar.bz2
dexon-ffaf58f0a98bd987bbe76e8669bb22c405dcd62a.tar.lz
dexon-ffaf58f0a98bd987bbe76e8669bb22c405dcd62a.tar.xz
dexon-ffaf58f0a98bd987bbe76e8669bb22c405dcd62a.tar.zst
dexon-ffaf58f0a98bd987bbe76e8669bb22c405dcd62a.zip
cmd, console: split off the console into a reusable package
Diffstat (limited to 'jsre/jsre.go')
-rw-r--r--jsre/jsre.go322
1 files changed, 0 insertions, 322 deletions
diff --git a/jsre/jsre.go b/jsre/jsre.go
deleted file mode 100644
index 59730bc0d..000000000
--- a/jsre/jsre.go
+++ /dev/null
@@ -1,322 +0,0 @@
-// Copyright 2015 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
-
-// Package jsre provides execution environment for JavaScript.
-package jsre
-
-import (
- crand "crypto/rand"
- "encoding/binary"
- "fmt"
- "io/ioutil"
- "math/rand"
- "sync"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/robertkrimen/otto"
-)
-
-/*
-JSRE is a generic JS runtime environment embedding the otto JS interpreter.
-It provides some helper functions to
-- load code from files
-- run code snippets
-- require libraries
-- bind native go objects
-*/
-type JSRE struct {
- assetPath string
- evalQueue chan *evalReq
- stopEventLoop chan bool
- loopWg sync.WaitGroup
-}
-
-// jsTimer is a single timer instance with a callback function
-type jsTimer struct {
- timer *time.Timer
- duration time.Duration
- interval bool
- call otto.FunctionCall
-}
-
-// evalReq is a serialized vm execution request processed by runEventLoop.
-type evalReq struct {
- fn func(vm *otto.Otto)
- done chan bool
-}
-
-// runtime must be stopped with Stop() after use and cannot be used after stopping
-func New(assetPath string) *JSRE {
- re := &JSRE{
- assetPath: assetPath,
- evalQueue: make(chan *evalReq),
- stopEventLoop: make(chan bool),
- }
- re.loopWg.Add(1)
- go re.runEventLoop()
- re.Set("loadScript", re.loadScript)
- re.Set("inspect", prettyPrintJS)
- return re
-}
-
-// randomSource returns a pseudo random value generator.
-func randomSource() *rand.Rand {
- bytes := make([]byte, 8)
- seed := time.Now().UnixNano()
- if _, err := crand.Read(bytes); err == nil {
- seed = int64(binary.LittleEndian.Uint64(bytes))
- }
-
- src := rand.NewSource(seed)
- return rand.New(src)
-}
-
-// This function runs the main event loop from a goroutine that is started
-// when JSRE is created. Use Stop() before exiting to properly stop it.
-// The event loop processes vm access requests from the evalQueue in a
-// 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() {
- vm := otto.New()
- r := randomSource()
- vm.SetRandomSource(r.Float64)
-
- registry := map[*jsTimer]*jsTimer{}
- ready := make(chan *jsTimer)
-
- newTimer := func(call otto.FunctionCall, interval bool) (*jsTimer, otto.Value) {
- delay, _ := call.Argument(1).ToInteger()
- if 0 >= delay {
- delay = 1
- }
- timer := &jsTimer{
- duration: time.Duration(delay) * time.Millisecond,
- call: call,
- interval: interval,
- }
- registry[timer] = timer
-
- timer.timer = time.AfterFunc(timer.duration, func() {
- ready <- timer
- })
-
- value, err := call.Otto.ToValue(timer)
- if err != nil {
- panic(err)
- }
- return timer, value
- }
-
- setTimeout := func(call otto.FunctionCall) otto.Value {
- _, value := newTimer(call, false)
- return value
- }
-
- setInterval := func(call otto.FunctionCall) otto.Value {
- _, value := newTimer(call, true)
- return value
- }
-
- clearTimeout := func(call otto.FunctionCall) otto.Value {
- timer, _ := call.Argument(0).Export()
- if timer, ok := timer.(*jsTimer); ok {
- timer.timer.Stop()
- delete(registry, timer)
- }
- return otto.UndefinedValue()
- }
- vm.Set("_setTimeout", setTimeout)
- vm.Set("_setInterval", setInterval)
- vm.Run(`var setTimeout = function(args) {
- if (arguments.length < 1) {
- throw TypeError("Failed to execute 'setTimeout': 1 argument required, but only 0 present.");
- }
- return _setTimeout.apply(this, arguments);
- }`)
- vm.Run(`var setInterval = function(args) {
- if (arguments.length < 1) {
- throw TypeError("Failed to execute 'setInterval': 1 argument required, but only 0 present.");
- }
- return _setInterval.apply(this, arguments);
- }`)
- vm.Set("clearTimeout", clearTimeout)
- vm.Set("clearInterval", clearTimeout)
-
- var waitForCallbacks bool
-
-loop:
- for {
- select {
- case timer := <-ready:
- // execute callback, remove/reschedule the timer
- var arguments []interface{}
- if len(timer.call.ArgumentList) > 2 {
- tmp := timer.call.ArgumentList[2:]
- arguments = make([]interface{}, 2+len(tmp))
- for i, value := range tmp {
- arguments[i+2] = value
- }
- } else {
- arguments = make([]interface{}, 1)
- }
- arguments[0] = timer.call.ArgumentList[0]
- _, err := vm.Call(`Function.call.call`, nil, arguments...)
- if err != nil {
- fmt.Println("js error:", err, arguments)
- }
-
- _, inreg := registry[timer] // when clearInterval is called from within the callback don't reset it
- if timer.interval && inreg {
- timer.timer.Reset(timer.duration)
- } else {
- delete(registry, timer)
- if waitForCallbacks && (len(registry) == 0) {
- break loop
- }
- }
- case req := <-self.evalQueue:
- // run the code, send the result back
- req.fn(vm)
- close(req.done)
- if waitForCallbacks && (len(registry) == 0) {
- break loop
- }
- case waitForCallbacks = <-self.stopEventLoop:
- if !waitForCallbacks || (len(registry) == 0) {
- break loop
- }
- }
- }
-
- for _, timer := range registry {
- timer.timer.Stop()
- delete(registry, timer)
- }
-
- self.loopWg.Done()
-}
-
-// Do executes the given function on the JS 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
-func (self *JSRE) Stop(waitForCallbacks bool) {
- self.stopEventLoop <- waitForCallbacks
- self.loopWg.Wait()
-}
-
-// Exec(file) loads and runs the contents of a file
-// if a relative path is given, the jsre's assetPath is used
-func (self *JSRE) Exec(file string) error {
- code, err := ioutil.ReadFile(common.AbsolutePath(self.assetPath, file))
- if err != nil {
- return err
- }
- var script *otto.Script
- self.Do(func(vm *otto.Otto) {
- script, err = vm.Compile(file, code)
- if err != nil {
- return
- }
- _, err = vm.Run(script)
- })
- return err
-}
-
-// Bind assigns value v to a variable in the JS environment
-// This method is deprecated, use Set.
-func (self *JSRE) Bind(name string, v interface{}) error {
- return self.Set(name, v)
-}
-
-// Run runs a piece of JS code.
-func (self *JSRE) Run(code string) (v otto.Value, err error) {
- self.Do(func(vm *otto.Otto) { v, err = vm.Run(code) })
- return v, err
-}
-
-// Get returns the value of a variable in the JS environment.
-func (self *JSRE) Get(ns string) (v otto.Value, err error) {
- self.Do(func(vm *otto.Otto) { v, err = vm.Get(ns) })
- return v, err
-}
-
-// Set assigns value v to a variable in the JS environment.
-func (self *JSRE) Set(ns string, v interface{}) (err error) {
- self.Do(func(vm *otto.Otto) { err = vm.Set(ns, v) })
- return err
-}
-
-// loadScript executes a JS script from inside the currently executing JS code.
-func (self *JSRE) loadScript(call otto.FunctionCall) otto.Value {
- file, err := call.Argument(0).ToString()
- if err != nil {
- // TODO: throw exception
- return otto.FalseValue()
- }
- 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)
- return otto.FalseValue()
- }
- // TODO: return evaluation result
- return otto.TrueValue()
-}
-
-// EvalAndPrettyPrint evaluates code and pretty prints the result to
-// standard output.
-func (self *JSRE) EvalAndPrettyPrint(code string) (err error) {
- self.Do(func(vm *otto.Otto) {
- var val otto.Value
- val, err = vm.Run(code)
- if err != nil {
- return
- }
- prettyPrint(vm, val)
- fmt.Println()
- })
- return err
-}
-
-// Compile compiles and then runs a piece of JS code.
-func (self *JSRE) Compile(filename string, src interface{}) (err error) {
- self.Do(func(vm *otto.Otto) { _, err = compileAndRun(vm, filename, src) })
- return err
-}
-
-func compileAndRun(vm *otto.Otto, filename string, src interface{}) (otto.Value, error) {
- script, err := vm.Compile(filename, src)
- if err != nil {
- return otto.Value{}, err
- }
- return vm.Run(script)
-}