| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // Copyright 2015 The go-ethereum Authors | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // This file is part of the go-ethereum library. | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // | 
					
						
							| 
									
										
										
										
											2015-07-23 18:35:11 +02:00
										 |  |  | // The go-ethereum library is free software: you can redistribute it and/or modify | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // 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. | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // The go-ethereum library is distributed in the hope that it will be useful, | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // GNU Lesser General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Lesser General Public License | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-07 05:08:16 +02:00
										 |  |  | // Package jsre provides execution environment for JavaScript. | 
					
						
							| 
									
										
										
										
											2015-03-15 13:13:39 +07:00
										 |  |  | package jsre | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2016-02-19 12:06:58 +01:00
										 |  |  | 	crand "crypto/rand" | 
					
						
							|  |  |  | 	"encoding/binary" | 
					
						
							| 
									
										
										
										
											2015-03-15 13:13:39 +07:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2016-05-06 12:40:23 +03:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2015-03-15 13:13:39 +07:00
										 |  |  | 	"io/ioutil" | 
					
						
							| 
									
										
										
										
											2016-02-19 12:06:58 +01:00
										 |  |  | 	"math/rand" | 
					
						
							| 
									
										
										
										
											2015-04-22 02:31:59 +02:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2015-03-24 17:49:28 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | 	"github.com/dop251/goja" | 
					
						
							| 
									
										
										
										
											2015-03-16 22:48:54 +07:00
										 |  |  | 	"github.com/ethereum/go-ethereum/common" | 
					
						
							| 
									
										
										
										
											2015-03-15 13:13:39 +07:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | // JSRE is a JS runtime environment embedding the goja interpreter. | 
					
						
							|  |  |  | // It provides helper functions to load code from files, run code snippets | 
					
						
							|  |  |  | // and bind native go objects to JS. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The runtime runs all code on a dedicated event loop and does not expose the underlying | 
					
						
							|  |  |  | // goja runtime directly. To use the runtime, call JSRE.Do. When binding a Go function, | 
					
						
							|  |  |  | // use the Call type to gain access to the runtime. | 
					
						
							| 
									
										
										
										
											2015-03-15 13:13:39 +07:00
										 |  |  | type JSRE struct { | 
					
						
							| 
									
										
										
										
											2015-05-25 02:27:37 +02:00
										 |  |  | 	assetPath     string | 
					
						
							| 
									
										
										
										
											2016-05-06 12:40:23 +03:00
										 |  |  | 	output        io.Writer | 
					
						
							| 
									
										
										
										
											2015-04-22 02:31:59 +02:00
										 |  |  | 	evalQueue     chan *evalReq | 
					
						
							|  |  |  | 	stopEventLoop chan bool | 
					
						
							| 
									
										
										
										
											2016-06-02 21:20:32 +02:00
										 |  |  | 	closed        chan struct{} | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | 	vm            *goja.Runtime | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Call is the argument type of Go functions which are callable from JS. | 
					
						
							|  |  |  | type Call struct { | 
					
						
							|  |  |  | 	goja.FunctionCall | 
					
						
							|  |  |  | 	VM *goja.Runtime | 
					
						
							| 
									
										
										
										
											2015-04-22 02:31:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // jsTimer is a single timer instance with a callback function | 
					
						
							|  |  |  | type jsTimer struct { | 
					
						
							|  |  |  | 	timer    *time.Timer | 
					
						
							|  |  |  | 	duration time.Duration | 
					
						
							|  |  |  | 	interval bool | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | 	call     goja.FunctionCall | 
					
						
							| 
									
										
										
										
											2015-03-15 13:13:39 +07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-25 02:27:37 +02:00
										 |  |  | // evalReq is a serialized vm execution request processed by runEventLoop. | 
					
						
							| 
									
										
										
										
											2015-04-22 02:31:59 +02:00
										 |  |  | type evalReq struct { | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | 	fn   func(vm *goja.Runtime) | 
					
						
							| 
									
										
										
										
											2015-04-22 02:31:59 +02:00
										 |  |  | 	done chan bool | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // runtime must be stopped with Stop() after use and cannot be used after stopping | 
					
						
							| 
									
										
										
										
											2016-05-06 12:40:23 +03:00
										 |  |  | func New(assetPath string, output io.Writer) *JSRE { | 
					
						
							| 
									
										
										
										
											2015-03-15 13:13:39 +07:00
										 |  |  | 	re := &JSRE{ | 
					
						
							| 
									
										
										
										
											2015-05-25 02:27:37 +02:00
										 |  |  | 		assetPath:     assetPath, | 
					
						
							| 
									
										
										
										
											2016-05-06 12:40:23 +03:00
										 |  |  | 		output:        output, | 
					
						
							| 
									
										
										
										
											2016-06-02 21:20:32 +02:00
										 |  |  | 		closed:        make(chan struct{}), | 
					
						
							| 
									
										
										
										
											2015-05-25 02:27:37 +02:00
										 |  |  | 		evalQueue:     make(chan *evalReq), | 
					
						
							|  |  |  | 		stopEventLoop: make(chan bool), | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | 		vm:            goja.New(), | 
					
						
							| 
									
										
										
										
											2015-03-15 13:13:39 +07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-04-22 02:31:59 +02:00
										 |  |  | 	go re.runEventLoop() | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | 	re.Set("loadScript", MakeCallback(re.vm, re.loadScript)) | 
					
						
							| 
									
										
										
										
											2016-12-11 01:53:34 +01:00
										 |  |  | 	re.Set("inspect", re.prettyPrintJS) | 
					
						
							| 
									
										
										
										
											2015-03-15 13:13:39 +07:00
										 |  |  | 	return re | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-19 12:06:58 +01:00
										 |  |  | // 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) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-25 02:27:37 +02:00
										 |  |  | // 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. | 
					
						
							| 
									
										
										
										
											2015-04-22 02:31:59 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-25 02:27:37 +02:00
										 |  |  | // Exported functions always access the vm through the event queue. You can | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | // call the functions of the goja vm directly to circumvent the queue. These | 
					
						
							| 
									
										
										
										
											2015-05-25 02:27:37 +02:00
										 |  |  | // functions should be used if and only if running a routine that was already | 
					
						
							|  |  |  | // called from JS through an RPC call. | 
					
						
							| 
									
										
										
										
											2018-05-02 01:26:21 -07:00
										 |  |  | func (re *JSRE) runEventLoop() { | 
					
						
							|  |  |  | 	defer close(re.closed) | 
					
						
							| 
									
										
										
										
											2016-06-02 21:20:32 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-19 12:06:58 +01:00
										 |  |  | 	r := randomSource() | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | 	re.vm.SetRandSource(r.Float64) | 
					
						
							| 
									
										
										
										
											2016-02-19 12:06:58 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-22 02:31:59 +02:00
										 |  |  | 	registry := map[*jsTimer]*jsTimer{} | 
					
						
							|  |  |  | 	ready := make(chan *jsTimer) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | 	newTimer := func(call goja.FunctionCall, interval bool) (*jsTimer, goja.Value) { | 
					
						
							|  |  |  | 		delay := call.Argument(1).ToInteger() | 
					
						
							| 
									
										
										
										
											2015-04-22 02:31:59 +02:00
										 |  |  | 		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 | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | 		return timer, re.vm.ToValue(timer) | 
					
						
							| 
									
										
										
										
											2015-04-22 02:31:59 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | 	setTimeout := func(call goja.FunctionCall) goja.Value { | 
					
						
							| 
									
										
										
										
											2015-04-22 02:31:59 +02:00
										 |  |  | 		_, value := newTimer(call, false) | 
					
						
							|  |  |  | 		return value | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | 	setInterval := func(call goja.FunctionCall) goja.Value { | 
					
						
							| 
									
										
										
										
											2015-04-22 02:31:59 +02:00
										 |  |  | 		_, value := newTimer(call, true) | 
					
						
							|  |  |  | 		return value | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | 	clearTimeout := func(call goja.FunctionCall) goja.Value { | 
					
						
							|  |  |  | 		timer := call.Argument(0).Export() | 
					
						
							| 
									
										
										
										
											2015-04-22 02:31:59 +02:00
										 |  |  | 		if timer, ok := timer.(*jsTimer); ok { | 
					
						
							|  |  |  | 			timer.timer.Stop() | 
					
						
							|  |  |  | 			delete(registry, timer) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | 		return goja.Undefined() | 
					
						
							| 
									
										
										
										
											2015-04-22 02:31:59 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | 	re.vm.Set("_setTimeout", setTimeout) | 
					
						
							|  |  |  | 	re.vm.Set("_setInterval", setInterval) | 
					
						
							|  |  |  | 	re.vm.RunString(`var setTimeout = function(args) { | 
					
						
							| 
									
										
										
										
											2015-11-23 13:03:31 +02:00
										 |  |  | 		if (arguments.length < 1) { | 
					
						
							|  |  |  | 			throw TypeError("Failed to execute 'setTimeout': 1 argument required, but only 0 present."); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return _setTimeout.apply(this, arguments); | 
					
						
							|  |  |  | 	}`) | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | 	re.vm.RunString(`var setInterval = function(args) { | 
					
						
							| 
									
										
										
										
											2015-11-23 13:03:31 +02:00
										 |  |  | 		if (arguments.length < 1) { | 
					
						
							|  |  |  | 			throw TypeError("Failed to execute 'setInterval': 1 argument required, but only 0 present."); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return _setInterval.apply(this, arguments); | 
					
						
							|  |  |  | 	}`) | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | 	re.vm.Set("clearTimeout", clearTimeout) | 
					
						
							|  |  |  | 	re.vm.Set("clearInterval", clearTimeout) | 
					
						
							| 
									
										
										
										
											2015-04-22 02:31:59 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	var waitForCallbacks bool | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | loop: | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		select { | 
					
						
							|  |  |  | 		case timer := <-ready: | 
					
						
							|  |  |  | 			// execute callback, remove/reschedule the timer | 
					
						
							|  |  |  | 			var arguments []interface{} | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | 			if len(timer.call.Arguments) > 2 { | 
					
						
							|  |  |  | 				tmp := timer.call.Arguments[2:] | 
					
						
							| 
									
										
										
										
											2015-04-22 02:31:59 +02:00
										 |  |  | 				arguments = make([]interface{}, 2+len(tmp)) | 
					
						
							|  |  |  | 				for i, value := range tmp { | 
					
						
							|  |  |  | 					arguments[i+2] = value | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				arguments = make([]interface{}, 1) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | 			arguments[0] = timer.call.Arguments[0] | 
					
						
							|  |  |  | 			call, isFunc := goja.AssertFunction(timer.call.Arguments[0]) | 
					
						
							|  |  |  | 			if !isFunc { | 
					
						
							|  |  |  | 				panic(re.vm.ToValue("js error: timer/timeout callback is not a function")) | 
					
						
							| 
									
										
										
										
											2015-04-22 02:31:59 +02:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | 			call(goja.Null(), timer.call.Arguments...) | 
					
						
							| 
									
										
										
										
											2015-11-23 13:03:31 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-16 11:57:33 +02:00
										 |  |  | 			_, inreg := registry[timer] // when clearInterval is called from within the callback don't reset it | 
					
						
							|  |  |  | 			if timer.interval && inreg { | 
					
						
							| 
									
										
										
										
											2015-04-22 02:31:59 +02:00
										 |  |  | 				timer.timer.Reset(timer.duration) | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				delete(registry, timer) | 
					
						
							|  |  |  | 				if waitForCallbacks && (len(registry) == 0) { | 
					
						
							|  |  |  | 					break loop | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-05-02 01:26:21 -07:00
										 |  |  | 		case req := <-re.evalQueue: | 
					
						
							| 
									
										
										
										
											2015-04-22 02:31:59 +02:00
										 |  |  | 			// run the code, send the result back | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | 			req.fn(re.vm) | 
					
						
							| 
									
										
										
										
											2015-05-25 02:27:37 +02:00
										 |  |  | 			close(req.done) | 
					
						
							| 
									
										
										
										
											2015-04-22 02:31:59 +02:00
										 |  |  | 			if waitForCallbacks && (len(registry) == 0) { | 
					
						
							|  |  |  | 				break loop | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-05-02 01:26:21 -07:00
										 |  |  | 		case waitForCallbacks = <-re.stopEventLoop: | 
					
						
							| 
									
										
										
										
											2015-04-22 02:31:59 +02:00
										 |  |  | 			if !waitForCallbacks || (len(registry) == 0) { | 
					
						
							|  |  |  | 				break loop | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, timer := range registry { | 
					
						
							|  |  |  | 		timer.timer.Stop() | 
					
						
							|  |  |  | 		delete(registry, timer) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-20 14:28:34 +01:00
										 |  |  | // Do executes the given function on the JS event loop. | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | func (re *JSRE) Do(fn func(*goja.Runtime)) { | 
					
						
							| 
									
										
										
										
											2015-05-25 02:27:37 +02:00
										 |  |  | 	done := make(chan bool) | 
					
						
							|  |  |  | 	req := &evalReq{fn, done} | 
					
						
							| 
									
										
										
										
											2018-05-02 01:26:21 -07:00
										 |  |  | 	re.evalQueue <- req | 
					
						
							| 
									
										
										
										
											2015-05-25 02:27:37 +02:00
										 |  |  | 	<-done | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-22 02:31:59 +02:00
										 |  |  | // stops the event loop before exit, optionally waits for all timers to expire | 
					
						
							| 
									
										
										
										
											2018-05-02 01:26:21 -07:00
										 |  |  | func (re *JSRE) Stop(waitForCallbacks bool) { | 
					
						
							| 
									
										
										
										
											2016-06-02 21:20:32 +02:00
										 |  |  | 	select { | 
					
						
							| 
									
										
										
										
											2018-05-02 01:26:21 -07:00
										 |  |  | 	case <-re.closed: | 
					
						
							|  |  |  | 	case re.stopEventLoop <- waitForCallbacks: | 
					
						
							|  |  |  | 		<-re.closed | 
					
						
							| 
									
										
										
										
											2016-06-02 21:20:32 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-04-22 02:31:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-15 13:13:39 +07:00
										 |  |  | // Exec(file) loads and runs the contents of a file | 
					
						
							|  |  |  | // if a relative path is given, the jsre's assetPath is used | 
					
						
							| 
									
										
										
										
											2018-05-02 01:26:21 -07:00
										 |  |  | func (re *JSRE) Exec(file string) error { | 
					
						
							|  |  |  | 	code, err := ioutil.ReadFile(common.AbsolutePath(re.assetPath, file)) | 
					
						
							| 
									
										
										
										
											2015-03-15 13:13:39 +07:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | 	return re.Compile(file, string(code)) | 
					
						
							| 
									
										
										
										
											2015-03-15 13:13:39 +07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-25 02:27:37 +02:00
										 |  |  | // Run runs a piece of JS code. | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | func (re *JSRE) Run(code string) (v goja.Value, err error) { | 
					
						
							|  |  |  | 	re.Do(func(vm *goja.Runtime) { v, err = vm.RunString(code) }) | 
					
						
							| 
									
										
										
										
											2015-05-25 02:27:37 +02:00
										 |  |  | 	return v, err | 
					
						
							| 
									
										
										
										
											2015-03-15 13:13:39 +07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-25 02:27:37 +02:00
										 |  |  | // Set assigns value v to a variable in the JS environment. | 
					
						
							| 
									
										
										
										
											2018-05-02 01:26:21 -07:00
										 |  |  | func (re *JSRE) Set(ns string, v interface{}) (err error) { | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | 	re.Do(func(vm *goja.Runtime) { vm.Set(ns, v) }) | 
					
						
							| 
									
										
										
										
											2015-05-25 02:27:37 +02:00
										 |  |  | 	return err | 
					
						
							| 
									
										
										
										
											2015-03-15 13:13:39 +07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | // MakeCallback turns the given function into a function that's callable by JS. | 
					
						
							|  |  |  | func MakeCallback(vm *goja.Runtime, fn func(Call) (goja.Value, error)) goja.Value { | 
					
						
							|  |  |  | 	return vm.ToValue(func(call goja.FunctionCall) goja.Value { | 
					
						
							|  |  |  | 		result, err := fn(Call{call, vm}) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			panic(vm.NewGoError(err)) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return result | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2015-03-15 13:13:39 +07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | // Evaluate executes code and pretty prints the result to the specified output stream. | 
					
						
							|  |  |  | func (re *JSRE) Evaluate(code string, w io.Writer) { | 
					
						
							|  |  |  | 	re.Do(func(vm *goja.Runtime) { | 
					
						
							|  |  |  | 		val, err := vm.RunString(code) | 
					
						
							| 
									
										
										
										
											2015-05-25 02:27:37 +02:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2016-05-11 17:28:29 +03:00
										 |  |  | 			prettyError(vm, err, w) | 
					
						
							| 
									
										
										
										
											2016-05-06 12:40:23 +03:00
										 |  |  | 		} else { | 
					
						
							|  |  |  | 			prettyPrint(vm, val, w) | 
					
						
							| 
									
										
										
										
											2015-05-25 02:27:37 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-05-11 17:28:29 +03:00
										 |  |  | 		fmt.Fprintln(w) | 
					
						
							| 
									
										
										
										
											2015-05-25 02:27:37 +02:00
										 |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2015-03-15 13:13:39 +07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2015-03-24 17:49:28 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-25 02:27:37 +02:00
										 |  |  | // Compile compiles and then runs a piece of JS code. | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | func (re *JSRE) Compile(filename string, src string) (err error) { | 
					
						
							|  |  |  | 	re.Do(func(vm *goja.Runtime) { _, err = compileAndRun(vm, filename, src) }) | 
					
						
							| 
									
										
										
										
											2015-05-25 02:27:37 +02:00
										 |  |  | 	return err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | // 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) { | 
					
						
							|  |  |  | 	script, err := goja.Compile(filename, src, false) | 
					
						
							| 
									
										
										
										
											2015-03-24 17:49:28 +01:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | 		return goja.Null(), err | 
					
						
							| 
									
										
										
										
											2015-03-24 17:49:28 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-27 11:50:48 +01:00
										 |  |  | 	return vm.RunProgram(script) | 
					
						
							| 
									
										
										
										
											2015-03-24 17:49:28 +01:00
										 |  |  | } |