Code Monkey home page Code Monkey logo

monkey-1's Introduction

What is

This package is SpiderMonkey wrapper for Go.

You can use this package to embed JavaScript into your Go program.

It has a rich API and can be freely used in any multi-goroutine Go program.

Install

You need install SpiderMonkey first.

Mac OS X:

brew install spidermonkey

Ubuntu:

sudo apt-get install libmozjs185-dev

Or compile by yourself (reference).

And then install Monkey by "go get" command.

go get github.com/chrisfarms/monkey

Performance

There are some benchmark test in "monkey_test.go".

You can run those test like this:

go test -bench="."

The benchmark result on my Mac:

Benchmark_ADD_IN_JS           200000     13410 ns/op
Benchmark_ADD_BY_JS           200000     15265 ns/op
Benchmark_ADD_BY_GO           100000     24779 ns/op
Benchmark_OOXX_IN_JS           10000    271113 ns/op
Benchmark_OOXX_IN_GO           50000     57133 ns/op
Benchmark_OOXX_BY_GO           20000     81031 ns/op
Benchmark_ADD_IN_JS_IN_USE   1000000      1446 ns/op
Benchmark_ADD_BY_JS_IN_USE   1000000      1427 ns/op
Benchmark_ADD_BY_GO_IN_USE    500000      7139 ns/op
Benchmark_OOXX_IN_JS_IN_USE    10000    262562 ns/op
Benchmark_OOXX_BY_GO_IN_USE    50000     63353 ns/op

Examples

All the example codes can be found in "examples" folder.

You can run all of the example codes like this:

go run examples/hello_world.go

Hello World

The "hello_world.go" shows what Monkey can do.

package main

import "fmt"
import js "github.com/chrisfarms/monkey"

func main() {
	// Create script runtime
	runtime := js.NewRuntime(8 * 1024 * 1024)

	// Create script context
	context := runtime.NewContext()

	// Evaluate script
	value := context.Eval("'Hello ' + 'World!'")
	println(value.ToString())

	// Define a function and call it
	context.DefineFunction("println", func(f *js.Func) {
		for i := 0; i < f.Argc(); i++ {
			fmt.Print(f.Argv(i))
		}
		fmt.Println()
		f.Return(f.Context().Void())
	})
	context.Eval("println('Hello Function!')")

	// Compile once, run many times
	script := context.Compile(
		"println('Hello Compiler!')",
		"<no name>", 0,
	)
	script.Execute()
	script.Execute()
	script.Execute()

	// Error handler
	context.SetErrorReporter(func(report *js.ErrorReport) {
		println(fmt.Sprintf(
			"%s:%d: %s",
			report.FileName, report.LineNum, report.Message,
		))
		if report.LineBuf != "" {
			println("\t", report.LineBuf)
		}
	})
	context.Eval("not_exists()")
}

This code will output:

Hello World!
Hello Function!
Hello Compiler!
Hello Compiler!
Hello Compiler!
Eval():0: ReferenceError: not_exists is not defined

Thread Safe

The "many_many.go" shows Monkey is thread safe.

package main

import "fmt"
import "sync"
import "runtime"
import js "github.com/chrisfarms/monkey"

func assert(c bool) bool {
	if !c {
		panic("assert failed")
	}
	return c
}

func main() {
	runtime.GOMAXPROCS(20)

	// Create script runtime
	runtime := js.NewRuntime(8 * 1024 * 1024)

	// Create script context
	context := runtime.NewContext()

	context.DefineFunction("println", func(f *js.Func) {
		for i := 0; i < f.Argc(); i++ {
			fmt.Print(f.Argv(i))
		}
		fmt.Println()
		f.Return(f.Context().Void())
	})

	wg := new(sync.WaitGroup)

	// One runtime instance used by many goroutines
	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func() {
			for j := 0; j < 100; j++ {
				v := context.Eval("println('Hello World!')")
				assert(v != nil)
			}
			wg.Done()
		}()
	}

	wg.Wait()
}

Value

The "op_value.go" shows how to convert JS value to Go value.

package main

import js "github.com/chrisfarms/monkey"

func assert(c bool) bool {
	if !c {
		panic("assert failed")
	}
	return c
}

func main() {
	// Create script Runtime
	runtime := js.NewRuntime(8 * 1024 * 1024)

	// Create script context
	context := runtime.NewContext()

	// String
	if value := context.Eval("'abc'"); assert(value != nil) {
		assert(value.IsString())
		assert(value.ToString() == "abc")
	}

	// Int
	if value := context.Eval("123456789"); assert(value != nil) {
		assert(value.IsInt())

		if value1, ok1 := value.ToInt(); assert(ok1) {
			assert(value1 == 123456789)
		}
	}

	// Number
	if value := context.Eval("12345.6789"); assert(value != nil) {
		assert(value.IsNumber())

		if value1, ok1 := value.ToNumber(); assert(ok1) {
			assert(value1 == 12345.6789)
		}
	}
}

Function

The "op_func.go" shows how to play with JS function value.

package main

import js "github.com/chrisfarms/monkey"

func assert(c bool) bool {
	if !c {
		panic("assert failed")
	}
	return c
}

func main() {
	// Create script runtime
	runtime := js.NewRuntime(8 * 1024 * 1024)

	// Create script context
	context := runtime.NewContext()

	// Return a function object from JavaScript
	if value := context.Eval("function(a,b){ return a+b; }"); assert(value != nil) {
		// Type check
		assert(value.IsFunction())

		// Call
		value1 := value.Call([]*js.Value{
			context.Int(10),
			context.Int(20),
		})

		// Result check
		assert(value1 != nil)
		assert(value1.IsNumber())

		if value2, ok2 := value1.ToNumber(); assert(ok2) {
			assert(value2 == 30)
		}
	}

	// Define a function that return an object with function from Go
	ok := context.DefineFunction("get_data", func(f *js.Func) {
		obj := f.Context().NewObject(nil)

		ok := obj.DefineFunction("abc",
			func(object *js.Object, name string, args []*js.Value) *js.Value {
				return f.Context().Int(100)
			},
		)

		assert(ok)

		f.Return(obj.ToValue())
	})

	assert(ok)

	if value := context.Eval(`
		a = get_data(); 
		a.abc();
	`); assert(value != nil) {
		assert(value.IsInt())

		if value2, ok2 := value.ToInt(); assert(ok2) {
			assert(value2 == 100)
		}
	}
}

Array

The "op_array.go" shows how to play with JS array.

package main

import js "github.com/chrisfarms/monkey"

func assert(c bool) bool {
	if !c {
		panic("assert failed")
	}
	return c
}

func main() {
	// Create script runtime
	runtime := js.NewRuntime(8 * 1024 * 1024)

	// Create script context
	context := runtime.NewContext()

	// Return an array from JavaScript
	if value := context.Eval("[123, 456];"); assert(value != nil) {
		// Check type
		assert(value.IsArray())
		array := value.ToArray()
		assert(array != nil)

		// Check length
		assert(array.GetLength() == 2)

		// Check first item
		value1, ok1 := array.GetInt(0)
		assert(ok1)
		assert(value1 == 123)

		// Check second item
		value2, ok2 := array.GetInt(1)
		assert(ok2)
		assert(value2 == 456)

		// Set first item
		assert(array.SetInt(0, 789))
		value3, ok3 := array.GetInt(0)
		assert(ok3)
		assert(value3 == 789)

		// Grows
		assert(array.SetLength(3))
		assert(array.GetLength() == 3)
	}

	// Return an array from Go
	if ok := context.DefineFunction("get_data", func(f *js.Func) {
		array := f.Context().NewArray()
		array.SetInt(0, 100)
		array.SetInt(1, 200)
		f.Return(array.ToValue())
	}); assert(ok) {
		if value := context.Eval("get_data()"); assert(value != nil) {
			// Check type
			assert(value.IsArray())
			array := value.ToArray()
			assert(array != nil)

			// Check length
			assert(array.GetLength() == 2)

			// Check first item
			value1, ok1 := array.GetInt(0)
			assert(ok1)
			assert(value1 == 100)

			// Check second item
			value2, ok2 := array.GetInt(1)
			assert(ok2)
			assert(value2 == 200)
		}
	}
}

Object

The "op_object.go" shows how to play with JS object.

package main

import js "github.com/chrisfarms/monkey"

func assert(c bool) bool {
	if !c {
		panic("assert failed")
	}
	return c
}

func main() {
	// Create script runtime
	runtime := js.NewRuntime(8 * 1024 * 1024)

	// Create script context
	context := runtime.NewContext()

	// Return an object from JavaScript
	if value := context.Eval("x={a:123}"); assert(value != nil) {
		// Type check
		assert(value.IsObject())
		obj := value.ToObject()

		// Get property 'a'
		value1, ok1 := obj.GetInt("a")
		assert(ok1)
		assert(value1 == 123)

		// Set property 'b'
		assert(obj.SetInt("b", 456))
		value2, ok2 := obj.GetInt("b")
		assert(ok2)
		assert(value2 == 456)
	}

	// Return and object From Go
	ok := context.DefineFunction("get_data", func(f *js.Func) {
		obj := f.Context().NewObject(nil)
		obj.SetInt("abc", 100)
		obj.SetInt("def", 200)
		f.Return(obj.ToValue())
	})

	assert(ok)

	if value := context.Eval("get_data()"); assert(value != nil) {
		// Type check
		assert(value.IsObject())
		obj := value.ToObject()

		// Get property 'abc'
		value1, ok1 := obj.GetInt("abc")
		assert(ok1)
		assert(value1 == 100)

		// Get property 'def'
		value2, ok2 := obj.GetInt("def")
		assert(ok2)
		assert(value2 == 200)
	}
}

Property

The "op_prop.go" shows how to handle JavaScript object's property in Go.

package main

import js "github.com/chrisfarms/monkey"

func assert(c bool) bool {
	if !c {
		panic("assert failed")
	}
	return c
}

type T struct {
	abc int32
}

func main() {
	// Create Script Runtime
	runtime := js.NewRuntime(8 * 1024 * 1024)

	// Create script context
	context := runtime.NewContext()

	// Return Object With Property Getter And Setter From Go
	ok := context.DefineFunction("get_data", func(f *js.Func) {
		cx := f.Context()
		obj := cx.NewObject(&T{123})

		// Define the property 'abc' with getter and setter
		ok := obj.DefineProperty("abc",
			// Init value
			cx.Void(),
			// T getter callback called each time
			// JavaScript code accesses the property's value
			func(g *js.Getter) {
				t := g.Object().GetPrivate().(*T)
				if g.Name() == "abc" {
					g.Return(cx.Int(t.abc))
				} else {
					panic("undefined property " + g.Name())
				}
			},
			// The setter callback is called each time
			// JavaScript code assigns to the property
			func(s *js.Setter) {
				t := s.Object().GetPrivate().(*T)
				if s.Name() == "abc" {
					d, ok := s.Value().ToInt()
					assert(ok)
					t.abc = d
				} else {
					panic("undefined property " + s.Name())
				}
			},
			0,
		)

		assert(ok)

		f.Return(obj.ToValue())
	})

	assert(ok)

	if value := context.Eval(`
		a = get_data();
		v1 = a.abc;
		a.abc = 456;
		v2 = a.abc;
		[v1, v2, a];
	`); assert(value != nil) {
		// Type check
		assert(value.IsArray())
		array := value.ToArray()
		assert(array != nil)

		// Length check
		assert(array.GetLength() == 3)

		// Check v1
		value1, ok1 := array.GetInt(0)
		assert(ok1)
		assert(value1 == 123)

		// Check v2
		value2, ok2 := array.GetInt(1)
		assert(ok2)
		assert(value2 == 456)

		// Check v3
		obj := array.GetObject(2)
		assert(obj != nil)
		t, ok3 := obj.GetPrivate().(*T)
		assert(ok3)
		assert(t.abc == 456)
	}

	if value := context.Eval(`
		var a = {};
		a.field1 = 1;
		a.field2 = "hello";
		a.field3 = 1.2;
		a.field4 = true;
		a.field5 = {};
		a.func1 = function(){};
		a;
	`); assert(value != nil) {
		obj := value.ToObject()
		assert(obj != nil)

		keys := obj.Keys()
		assert(len(keys) == 6)
		assert(keys[0] == "field1")
		assert(keys[1] == "field2")
		assert(keys[2] == "field3")
		assert(keys[3] == "field4")
		assert(keys[4] == "field5")
		assert(keys[5] == "func1")

		keys = obj.GetProperty("field5").ToObject().Keys()
		assert(len(keys) == 0)
	}
}

monkey-1's People

Contributors

chrisfarms avatar lazytiger avatar realint avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.