Permalink
Browse files

call go's function from javascript that's generated from go.

  • Loading branch information...
1 parent ccf974d commit 5c4cfd378fbcd5e24cda2448bccb9946578e2249 @mattn committed Nov 17, 2011
Showing with 202 additions and 61 deletions.
  1. +2 −2 Makefile
  2. +21 −20 example/example.go
  3. +62 −13 v8.go
  4. +115 −26 v8wrap.cc
  5. +2 −0 v8wrap.def
View
@@ -15,9 +15,9 @@ include $(GOROOT)/src/Make.pkg
ifeq ($(GOOS),windows)
v8wrap.dll : v8wrap.cc
- g++ -shared -o v8wrap.dll -Ic:/mingw/include/v8 v8wrap.cc -lv8 -lstdc++ -lws2_32 -lwinmm
+ g++ -shared -o v8wrap.dll -I. -Ic:/mingw/include/v8 v8wrap.cc -lv8 -lstdc++ -lws2_32 -lwinmm
dlltool -d v8wrap.def -l libv8wrap.a
else
libv8wrap.so : v8wrap.cc
- g++ -shared -o libv8wrap.so v8wrap.cc -lv8
+ g++ -shared -o libv8wrap.so -I. v8wrap.cc -lv8
endif
View
@@ -2,30 +2,31 @@ package main
import (
"fmt"
- "reflect"
"github.com/mattn/go-v8/v8"
)
func main() {
v8ctx := v8.NewContext()
- ret, err := v8ctx.Eval(`
-var a = 1;
-a += 2;
-a;
-`)
- if err != nil {
- fmt.Println(err)
- } else {
- println(ret.(float64))
- }
- ret, err = v8ctx.Eval(`
-a+'b'
-`)
- println(reflect.ValueOf(ret).Type().Name())
- if err != nil {
- fmt.Println(err)
- } else {
- println(ret.(string))
- }
+ // setup console.log()
+ v8ctx.Eval(`
+ this.console = { "log": function(args) { _console_log(args) }}`)
+ v8ctx.AddFunc("_console_log", func(args... interface{}) interface{} {
+ for _, arg := range args {
+ fmt.Printf("%v ", arg)
+ }
+ fmt.Println()
+ return ""
+ })
+
+ ret, _ := v8ctx.Eval(`
+ var a = 1;
+ var b = 'B'
+ a += 2;
+ a;
+ `)
+ println(int(ret.(float64))) // 3
+
+ v8ctx.Eval(`console.log(a + '年' + b + '組 金八先生!')`) // 3b
+ v8ctx.Eval(`console.log("Hello World, こんにちわ世界")`) // john
}
View
75 v8.go
@@ -2,26 +2,57 @@ package v8
/*
#include <stdlib.h>
-extern void* v8_create();
-extern void v8_release(void* ctx);
-extern char* v8_execute(void* ctx, char* str);
+#include "v8wrap.h"
*/
import "C"
import (
+ "bytes"
+ "encoding/json"
"errors"
+ "runtime"
+ "text/template"
"unsafe"
)
-import "runtime"
-import "encoding/json"
-import "bytes"
+var contexts = make(map[uint32]*V8Context)
+
+var tmpl = template.Must(template.New("go-v8").Parse(`
+function {{.name}}() {
+ return _go_call({{.id}}, "{{.name}}", JSON.stringify([].slice.call(arguments)));
+}`))
+
+func init() {
+ var f = func(id uint32, n, a *C.char) *C.char {
+ c := contexts[id]
+ f := c.funcs[C.GoString(n)]
+ if f != nil {
+ var argv []interface{}
+ json.Unmarshal([]byte(C.GoString(a)), &argv)
+ ret := f(argv...)
+ if ret != nil {
+ b, _ := json.Marshal(ret)
+ return C.CString(string(b))
+ }
+ return nil
+ }
+ return C.CString("undefined")
+ }
+ C.v8_init(*(*unsafe.Pointer)(unsafe.Pointer(&f)))
+}
type V8Context struct {
+ id uint32
v8context unsafe.Pointer
+ funcs map[string]func(... interface{}) interface{}
}
func NewContext() *V8Context {
- v := &V8Context{C.v8_create()}
+ v := &V8Context{
+ uint32(len(contexts)),
+ C.v8_create(),
+ make(map[string]func(... interface{}) interface{}),
+ }
+ contexts[v.id] = v
runtime.SetFinalizer(v, func(p *V8Context) {
C.v8_release(p.v8context)
})
@@ -34,11 +65,29 @@ func (v *V8Context) Eval(in string) (res interface{}, err error) {
ret := C.v8_execute(v.v8context, ptr)
if ret != nil {
out := C.GoString(ret)
- var buf bytes.Buffer
- buf.Write([]byte(out))
- dec := json.NewDecoder(&buf)
- err = dec.Decode(&res)
- return
+ if out != "" {
+ C.free(unsafe.Pointer(ret))
+ var buf bytes.Buffer
+ buf.Write([]byte(out))
+ dec := json.NewDecoder(&buf)
+ err = dec.Decode(&res)
+ return
+ }
+ return nil, nil
}
- return nil, errors.New("failed to eval")
+ ret = C.v8_error(v.v8context)
+ out := C.GoString(ret)
+ C.free(unsafe.Pointer(ret))
+ return nil, errors.New(out)
+}
+
+func (v *V8Context) AddFunc(name string, f func(...interface{}) interface{}) error {
+ v.funcs[name] = f
+ b := bytes.NewBufferString("")
+ tmpl.Execute(b, map[string]interface{} {
+ "id": v.id,
+ "name": name,
+ })
+ _, err := v.Eval(b.String())
+ return err
}
View
141 v8wrap.cc
@@ -1,74 +1,163 @@
#include <v8.h>
-#include <string.h>
+#include <iostream>
+#include <sstream>
+#include <cstring>
+#include <cstdlib>
+#include "v8wrap.h"
extern "C" {
+v8wrap_callback _go_callback = NULL;
+v8::Handle<v8::ObjectTemplate> global;
+
+static std::string
+to_json(v8::Handle<v8::Value> value) {
+ v8::HandleScope scope;
+ v8::TryCatch try_catch;
+ v8::Handle<v8::Object> json = v8::Handle<v8::Object>::Cast(
+ v8::Context::GetCurrent()->Global()->Get(v8::String::New("JSON")));
+ v8::Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(
+ json->GetRealNamedProperty(v8::String::New("stringify")));
+ v8::Handle<v8::Value> args[1];
+ args[0] = value;
+ v8::String::Utf8Value ret(
+ func->Call(v8::Context::GetCurrent()->Global(), 1, args)->ToString());
+ return (char*) *ret;
+}
+
+v8::Handle<v8::Value>
+from_json(std::string str) {
+ v8::HandleScope scope;
+ v8::TryCatch try_catch;
+ v8::Handle<v8::Object> json = v8::Handle<v8::Object>::Cast(
+ v8::Context::GetCurrent()->Global()->Get(v8::String::New("JSON")));
+ v8::Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(
+ json->GetRealNamedProperty(v8::String::New("parse")));
+ v8::Handle<v8::Value> args[1];
+ args[0] = v8::String::New(str.c_str());
+ return func->Call(v8::Context::GetCurrent()->Global(), 1, args);
+}
+
+v8::Handle<v8::Value>
+_go_call(const v8::Arguments& args) {
+ uint32_t id = args[0]->ToUint32()->Value();
+ v8::String::Utf8Value name(args[1]);
+ v8::String::Utf8Value argv(args[2]);
+ v8::HandleScope scope;
+ char* retv = _go_callback(id, *name, *argv);
+ v8::Handle<v8::Value> ret = v8::Undefined();
+ if (retv != NULL) ret = from_json(retv);
+ free(retv);
+ return ret;
+}
+
class V8Context {
public:
V8Context() {
v8::HandleScope scope;
-
- v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
+ global = v8::ObjectTemplate::New();
+ global->Set(v8::String::New("_go_call"),
+ v8::FunctionTemplate::New(_go_call));
v8::Handle<v8::Context> context = v8::Context::New(NULL, global);
-
context_ = v8::Persistent<v8::Context>::New(context);
};
- virtual ~V8Context() {
- context_.Dispose();
- };
-
+ virtual ~V8Context() { context_.Dispose(); };
v8::Handle<v8::Context> context() { return context_; };
+ std::string err() const { return err_; };
+ void err(const std::string err) { this->err_ = err; }
private:
v8::Persistent<v8::Context> context_;
+ std::string err_;
};
+void
+v8_init(void *p) {
+ _go_callback = (v8wrap_callback) p;
+}
+
void*
v8_create() {
- return (void*) new V8Context;
+ return (void*) new V8Context();
}
void
v8_release(void* ctx) {
- delete static_cast<V8Context *>(ctx);
+ delete static_cast<V8Context *>(ctx);
}
char*
-v8_execute(V8Context *ctx, char* source) {
+v8_error(void* ctx) {
+ V8Context *context = static_cast<V8Context *>(ctx);
+ return strdup(context->err().c_str());
+}
+
+static std::string
+report_exception(v8::TryCatch& try_catch) {
+ v8::Handle<v8::Message> message = try_catch.Message();
+ v8::String::Utf8Value exception(try_catch.Exception());
+ std::stringstream ss;
+ if (message.IsEmpty()) {
+ ss << *exception << std::endl;
+ } else {
+ v8::String::Utf8Value filename(message->GetScriptResourceName());
+ const char* filename_string = *filename;
+ int linenum = message->GetLineNumber();
+ ss
+ << filename_string
+ << ":" << linenum
+ << ": " << *exception << std::endl;
+ v8::String::Utf8Value sourceline(message->GetSourceLine());
+ ss << *sourceline << std::endl;
+ int start = message->GetStartColumn();
+ for (int n = 0; n < start; n++) {
+ ss << " ";
+ }
+ int end = message->GetEndColumn();
+ for (int n = start; n < end; n++) {
+ ss << "^";
+ }
+ ss << std::endl;
+ v8::String::Utf8Value stack_trace(try_catch.StackTrace());
+ if (stack_trace.length() > 0) {
+ const char* stack_trace_string = *stack_trace;
+ ss << stack_trace_string << std::endl;
+ }
+ }
+ return ss.str();
+}
+
+char*
+v8_execute(void *ctx, char* source) {
V8Context *context = static_cast<V8Context *>(ctx);
v8::HandleScope scope;
v8::TryCatch try_catch;
v8::Context::Scope context_scope(context->context());
+ context->err("");
v8::Handle<v8::Script> script
= v8::Script::Compile(v8::String::New(source), v8::Undefined());
if (script.IsEmpty()) {
v8::ThrowException(try_catch.Exception());
+ context->err(report_exception(try_catch));
return NULL;
- }
- else {
+ } else {
v8::Handle<v8::Value> result = script->Run();
if (result.IsEmpty()) {
v8::ThrowException(try_catch.Exception());
+ context->err(report_exception(try_catch));
return NULL;
}
- else {
- v8::Handle<v8::Object> json = v8::Handle<v8::Object>::Cast(
- context->context()->Global()->Get(
- v8::String::New("JSON")));
- v8::Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(
- json->GetRealNamedProperty(
- v8::String::New("stringify")));
- v8::Handle<v8::Value> args[1];
- args[0] = result;
- v8::String::Utf8Value ret(
- func->Call(context->context()->Global(), 1, args)
- ->ToString());
- return strdup(*ret);
+ else if (result->IsFunction() || result->IsUndefined()) {
+ return strdup("");
+ } else {
+ return strdup(to_json(result).c_str());
}
}
}
}
+
+// vim:set et sw=2 ts=2 ai:
View
@@ -1,5 +1,7 @@
LIBRARY v8wrap.dll
EXPORTS
+v8_init
v8_create
v8_execute
v8_release
+v8_error

0 comments on commit 5c4cfd3

Please sign in to comment.