1package main
2
3import (
4	crand "crypto/rand"
5	"encoding/binary"
6	"flag"
7	"fmt"
8	"io/ioutil"
9	"log"
10	"math/rand"
11	"os"
12	"runtime/debug"
13	"runtime/pprof"
14	"time"
15
16	"github.com/dop251/goja"
17	"github.com/dop251/goja_nodejs/console"
18	"github.com/dop251/goja_nodejs/require"
19)
20
21var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
22var timelimit = flag.Int("timelimit", 0, "max time to run (in seconds)")
23
24func readSource(filename string) ([]byte, error) {
25	if filename == "" || filename == "-" {
26		return ioutil.ReadAll(os.Stdin)
27	}
28	return ioutil.ReadFile(filename)
29}
30
31func load(vm *goja.Runtime, call goja.FunctionCall) goja.Value {
32	p := call.Argument(0).String()
33	b, err := readSource(p)
34	if err != nil {
35		panic(vm.ToValue(fmt.Sprintf("Could not read %s: %v", p, err)))
36	}
37	v, err := vm.RunScript(p, string(b))
38	if err != nil {
39		panic(err)
40	}
41	return v
42}
43
44func newRandSource() goja.RandSource {
45	var seed int64
46	if err := binary.Read(crand.Reader, binary.LittleEndian, &seed); err != nil {
47		panic(fmt.Errorf("Could not read random bytes: %v", err))
48	}
49	return rand.New(rand.NewSource(seed)).Float64
50}
51
52func run() error {
53	filename := flag.Arg(0)
54	src, err := readSource(filename)
55	if err != nil {
56		return err
57	}
58
59	if filename == "" || filename == "-" {
60		filename = "<stdin>"
61	}
62
63	vm := goja.New()
64	vm.SetRandSource(newRandSource())
65
66	new(require.Registry).Enable(vm)
67	console.Enable(vm)
68
69	vm.Set("load", func(call goja.FunctionCall) goja.Value {
70		return load(vm, call)
71	})
72
73	vm.Set("readFile", func(name string) (string, error) {
74		b, err := ioutil.ReadFile(name)
75		if err != nil {
76			return "", err
77		}
78		return string(b), nil
79	})
80
81	if *timelimit > 0 {
82		time.AfterFunc(time.Duration(*timelimit)*time.Second, func() {
83			vm.Interrupt("timeout")
84		})
85	}
86
87	//log.Println("Compiling...")
88	prg, err := goja.Compile(filename, string(src), false)
89	if err != nil {
90		return err
91	}
92	//log.Println("Running...")
93	_, err = vm.RunProgram(prg)
94	//log.Println("Finished.")
95	return err
96}
97
98func main() {
99	defer func() {
100		if x := recover(); x != nil {
101			debug.Stack()
102			panic(x)
103		}
104	}()
105	flag.Parse()
106	if *cpuprofile != "" {
107		f, err := os.Create(*cpuprofile)
108		if err != nil {
109			log.Fatal(err)
110		}
111		pprof.StartCPUProfile(f)
112		defer pprof.StopCPUProfile()
113	}
114
115	if err := run(); err != nil {
116		//fmt.Printf("err type: %T\n", err)
117		switch err := err.(type) {
118		case *goja.Exception:
119			fmt.Println(err.String())
120		case *goja.InterruptedError:
121			fmt.Println(err.String())
122		default:
123			fmt.Println(err)
124		}
125		os.Exit(64)
126	}
127}
128