candyjs

package module
v0.0.0-...-9d8574a Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 17, 2019 License: MIT Imports: 11 Imported by: 0

README

CandyJS is an intent of create a fully transparent bridge between Go and the JavaScript engine duktape. Basicly is a syntax-sugar library built it on top of go-duktape using reflection techniques.

This is a fork of https://github.com/mcuadros/go-candyjs.

It encodes UTF-8 strings passed to duktape into CESU-8.

It decodes strings returned by duktape from CESU-8 into UTF-8.

It uses https://github.com/crazytyper/go-cesu8 to encode/decode CESU-8.

ok but what for ...

build extensible applications that allow to the user execute arbitrary code (let's say plugins) without the requirement of compile it.

Demo

asciicast

Features

Embeddable Ecmascript E5/E5.1 compliant engine (duktape).

ctx := candyjs.NewContext()
ctx.EvalString(`
  function factorial(n) {
    if (n === 0) return 1;
    return n * factorial(n - 1);
  }

  print(factorial(10));
`)  //3628800

Call Go functions from JavaScript and vice versa.

ctx := candyjs.NewContext()
ctx.PushGlobalGoFunction("golangMultiply", func(a, b int) int {
    return a * b
})

ctx.EvalString(`print(golangMultiply(5, 10));`) //50

Transparent interface between Go structs and JavaScript.

type MyStruct struct {
    Number int
}

func (m *MyStruct) Multiply(x int) int {
    return m.Number * x
}
...
ctx := candyjs.NewContext()
ctx.PushGlobalStruct("golangStruct", &MyStruct{10})

ctx.EvalString(`print(golangStruct.number);`) //10
ctx.EvalString(`print(golangStruct.multiply(5));`) //50

Import of Go packages into the JavaScript context.

//go:generate candyjs import fmt
...
ctx := candyjs.NewContext()
ctx.EvalString(`
    var fmt = CandyJS.require('fmt');
    fmt.printf('candyjs is %s', 'awesome')
`) // 'candyjs is awesome'

Installation

The recommended way to install go-candyjs is:

go get -u github.com/crazytyper/go-candyjs/...

CandyJS includes a binary tool used by go generate, please be sure that $GOPATH/bin is on your $PATH

Examples

JavaScript running a HTTP server

In this example a gin server is executed and a small JSON is server. In CandyJS you can import Go packages directly if they are defined previously on the Go code.

Interpreter code (main.go)

...
//go:generate candyjs import time
//go:generate candyjs import github.com/gin-gonic/gin
func main() {
    ctx := candyjs.NewContext()
    ctx.PevalFile("example.js")
}

Program code (example.js)

var time = CandyJS.require('time');
var gin = CandyJS.require('github.com/gin-gonic/gin');

var engine = gin.default();
engine.get("/back", CandyJS.proxy(function(ctx) {
  var future = time.date(2015, 10, 21, 4, 29 ,0, 0, time.UTC);
  var now = time.now();

  ctx.json(200, {
    future: future.string(),
    now: now.string(),
    nsecs: future.sub(now)
  });
}));

engine.run(':8080');

Caveats

Due to an incompatibility with Duktape's error handling system and Go, you can't throw errors from Go. All errors generated from Go functions are generic ones error error (rc -100)

License

MIT, see LICENSE

Documentation

Index

Constants

View Source
const (
	// ErrorCodeUndefinedProperty is returned when accessing an invalid property.
	ErrorCodeUndefinedProperty = "candyjs:undefinedproperty"
	// ErrorCodePackageNotFound is returned when a package cannot be found, usually this
	// happend when a PackagePusher function was not registered using
	// RegisterPackagePusher.
	ErrorCodePackageNotFound = "candyjs:packagenotfound"
)

Variables

This section is empty.

Functions

func ErrorCode

func ErrorCode(err error) string

ErrorCode returns the candy error code for this error. Returns "" if the error is not a candyjs error.

func RegisterPackagePusher

func RegisterPackagePusher(pckgName string, f PackagePusher)

RegisterPackagePusher registers a PackagePusher into the global storage, this storage is a private map defined on the candyjs package. The pushers are launch by the function PushGlobalPackage.

Types

type Context

type Context struct {
	*duktape.Context
	// contains filtered or unexported fields
}

Context represents a Duktape thread and its call and value stacks.

func NewContext

func NewContext() *Context

NewContext returns a new Context

func (*Context) ConsumeLastGoError

func (ctx *Context) ConsumeLastGoError() error

ConsumeLastGoError returns the last error returned by a GO function and clears it.

func (*Context) GetValue

func (ctx *Context) GetValue(index int, value interface{}) error

GetValue gets and marshals the value at the specified stack index. Takes special care for proxies.

func (*Context) LastGoError

func (ctx *Context) LastGoError() error

LastGoError returns the last error returned by a GO function.

func (*Context) PushGlobalGoFunction

func (ctx *Context) PushGlobalGoFunction(name string, f interface{}) (int, error)

PushGlobalGoFunction like PushGoFunction but pushed to the global object

func (*Context) PushGlobalInterface

func (ctx *Context) PushGlobalInterface(name string, v interface{}) error

PushGlobalInterface like PushInterface but pushed to the global object

func (*Context) PushGlobalPackage

func (ctx *Context) PushGlobalPackage(pckgName, alias string) error

PushGlobalPackage all the functions and types from the given package using the pre-registered PackagePusher function.

func (*Context) PushGlobalProxy

func (ctx *Context) PushGlobalProxy(name string, v interface{}) int

PushGlobalProxy like PushProxy but pushed to the global object

func (*Context) PushGlobalStruct

func (ctx *Context) PushGlobalStruct(name string, s interface{}) (int, error)

PushGlobalStruct like PushStruct but pushed to the global object

func (*Context) PushGlobalType

func (ctx *Context) PushGlobalType(name string, s interface{}) int

PushGlobalType like PushType but pushed to the global object

func (*Context) PushGoFunction

func (ctx *Context) PushGoFunction(f interface{}) int

PushGoFunction push a native Go function of any signature to the stack. A pointer to the function is stored in the internals of the context and collected by the duktape GC removing any reference in Go also.

The most common types are supported as input arguments, also the variadic functions can be used.

You can use JS functions as arguments but you should wrapper it with the helper `CandyJS.proxy`. Example:

ctx.PushGlobalGoFunction("test", func(fn func(int, int) int) {
	...
})

ctx.PevalString(`test(CandyJS.proxy(function(a, b) { return a * b; }));`)

The structs can be delivered to the functions in three ways:

  • In-line representation as plain JS objects: `{'int':42}`
  • Using a previous pushed type using `PushGlobalType`: `new MyModel`
  • Using a previous pushed instance using `PushGlobalProxy`

All other types are loaded into Go using `json.Unmarshal` internally

The following types are not supported chans, complex64 or complex128, and the types rune, byte and arrays are not tested.

The returns are handled in the following ways:

  • The result of functions with a single return value like `func() int` is pushed directly to the stack.
  • Functions with a n return values like `func() (int, int)` are pushed as an array. The errors are removed from this array.
  • Returns of functions with a trailling error like `func() (string, err)`: if err is not nil an error is throw in the context, and the other values are discarded. IF err is nil, the values are pushed to the stack, following the previuos rules.

All the non erros returning values are pushed following the same rules of `PushInterface` method

func (*Context) PushInterface

func (ctx *Context) PushInterface(v interface{}) error

PushInterface push any type of value to the stack, the following types are supported:

  • Bool
  • Int, Int8, Int16, Int32, Uint, Uint8, Uint16, Uint32 and Uint64
  • Float32 and Float64
  • Strings and []byte
  • Structs
  • Functions with any signature

Please read carefully the following notes:

  • The pointers are resolved and the value is pushed
  • Structs are pushed ussing PushProxy, if you want to make a copy use PushStruct
  • Int64 and UInt64 are supported but before push it to the stack are casted to float64
  • Any unsuported value is pushed as a null

func (*Context) PushProxy

func (ctx *Context) PushProxy(v interface{}) int

PushProxy push a proxified pointer of the given value to the stack, this refence will be stored on an internal storage. The pushed objects has the exact same methods and properties from the original value. http://duktape.org/guide.html#virtualization-proxy-object

func (*Context) PushStruct

func (ctx *Context) PushStruct(s interface{}) (int, error)

PushStruct push a object to the stack with the same methods and properties the pushed object is a copy, any change made on JS is not reflected on the Go instance.

func (*Context) PushType

func (ctx *Context) PushType(s interface{}) int

PushType push a constructor for the type of the given value, this constructor returns an empty instance of the type. The value passed is discarded, only is used for retrieve the type, instead of require pass a `reflect.Type`.

func (*Context) SetErrorFactory

func (ctx *Context) SetErrorFactory(f ErrorFactoryFunc)

SetErrorFactory sets the function used to create go errors from Javascript execptions. By default `errors.New(execption.toString())` will be used.

func (*Context) SetRequireFunction

func (ctx *Context) SetRequireFunction(f interface{}) int

SetRequireFunction sets the modSearch function into the Duktape JS object http://duktape.org/guide.html#builtin-duktape-modsearch-modloade

type Error

type Error interface {
	error
	Code() string
}

Error represents an error returned by candy JS

type ErrorFactoryFunc

type ErrorFactoryFunc func(ctx *Context, index int) error

ErrorFactoryFunc ...

type PackagePusher

type PackagePusher func(ctx *Context)

PackagePusher should be a function capable of register all functions and types contained on a golang packages. This functions are generated by the go generate tool `candyjs` a example of this header is:

//go:generate candyjs import time

type Proxy

type Proxy interface {
	Has(t interface{}, k string) bool
	Get(t interface{}, k string, recv interface{}) (interface{}, error)
	Set(t interface{}, k string, v, recv interface{}) (bool, error)
	Enumerate(t interface{}) (interface{}, error)
}

Proxy defines the GO interface for ECMASCRIPTs proxy objects.

Directories

Path Synopsis
cmd module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL