Giter Club home page Giter Club logo

v8go's Introduction

Execute JavaScript from Go

Github release Go Report Card Go Reference CI V8 Build codecov FOSSA Status #v8go Slack Channel

V8 Gopher based on original artwork from the amazing Renee French

Usage

import v8 "rogchap.com/v8go"

Running a script

ctx := v8.NewContext() // creates a new V8 context with a new Isolate aka VM
ctx.RunScript("const add = (a, b) => a + b", "math.js") // executes a script on the global context
ctx.RunScript("const result = add(3, 4)", "main.js") // any functions previously added to the context can be called
val, _ := ctx.RunScript("result", "value.js") // return a value in JavaScript back to Go
fmt.Printf("addition result: %s", val)

One VM, many contexts

iso := v8.NewIsolate() // creates a new JavaScript VM
ctx1 := v8.NewContext(iso) // new context within the VM
ctx1.RunScript("const multiply = (a, b) => a * b", "math.js")

ctx2 := v8.NewContext(iso) // another context on the same VM
if _, err := ctx2.RunScript("multiply(3, 4)", "main.js"); err != nil {
  // this will error as multiply is not defined in this context
}

JavaScript function with Go callback

iso := v8.NewIsolate() // create a new VM
// a template that represents a JS function
printfn := v8.NewFunctionTemplate(iso, func(info *v8.FunctionCallbackInfo) *v8.Value {
    fmt.Printf("%v", info.Args()) // when the JS function is called this Go callback will execute
    return nil // you can return a value back to the JS caller if required
})
global := v8.NewObjectTemplate(iso) // a template that represents a JS Object
global.Set("print", printfn) // sets the "print" property of the Object to our function
ctx := v8.NewContext(iso, global) // new Context with the global Object set to our object template
ctx.RunScript("print('foo')", "print.js") // will execute the Go callback with a single argunent 'foo'

Update a JavaScript object from Go

ctx := v8.NewContext() // new context with a default VM
obj := ctx.Global() // get the global object from the context
obj.Set("version", "v1.0.0") // set the property "version" on the object
val, _ := ctx.RunScript("version", "version.js") // global object will have the property set within the JS VM
fmt.Printf("version: %s", val)

if obj.Has("version") { // check if a property exists on the object
    obj.Delete("version") // remove the property from the object
}

JavaScript errors

val, err := ctx.RunScript(src, filename)
if err != nil {
  e := err.(*v8.JSError) // JavaScript errors will be returned as the JSError struct
  fmt.Println(e.Message) // the message of the exception thrown
  fmt.Println(e.Location) // the filename, line number and the column where the error occured
  fmt.Println(e.StackTrace) // the full stack trace of the error, if available

  fmt.Printf("javascript error: %v", e) // will format the standard error message
  fmt.Printf("javascript stack trace: %+v", e) // will format the full error stack trace
}

Pre-compile context-independent scripts to speed-up execution times

For scripts that are large or are repeatedly run in different contexts, it is beneficial to compile the script once and used the cached data from that compilation to avoid recompiling every time you want to run it.

source := "const multiply = (a, b) => a * b"
iso1 := v8.NewIsolate() // creates a new JavaScript VM
ctx1 := v8.NewContext(iso1) // new context within the VM
script1, _ := iso1.CompileUnboundScript(source, "math.js", v8.CompileOptions{}) // compile script to get cached data
val, _ := script1.Run(ctx1)

cachedData := script1.CreateCodeCache()

iso2 := v8.NewIsolate() // create a new JavaScript VM
ctx2 := v8.NewContext(iso2) // new context within the VM

script2, _ := iso2.CompileUnboundScript(source, "math.js", v8.CompileOptions{CachedData: cachedData}) // compile script in new isolate with cached data
val, _ = script2.Run(ctx2)

Terminate long running scripts

vals := make(chan *v8.Value, 1)
errs := make(chan error, 1)

go func() {
    val, err := ctx.RunScript(script, "forever.js") // exec a long running script
    if err != nil {
        errs <- err
        return
    }
    vals <- val
}()

select {
case val := <- vals:
    // success
case err := <- errs:
    // javascript error
case <- time.After(200 * time.Milliseconds):
    vm := ctx.Isolate() // get the Isolate from the context
    vm.TerminateExecution() // terminate the execution
    err := <- errs // will get a termination error back from the running script
}

CPU Profiler

func createProfile() {
	iso := v8.NewIsolate()
	ctx := v8.NewContext(iso)
	cpuProfiler := v8.NewCPUProfiler(iso)

	cpuProfiler.StartProfiling("my-profile")

	ctx.RunScript(profileScript, "script.js") # this script is defined in cpuprofiler_test.go
	val, _ := ctx.Global().Get("start")
	fn, _ := val.AsFunction()
	fn.Call(ctx.Global())

	cpuProfile := cpuProfiler.StopProfiling("my-profile")

	printTree("", cpuProfile.GetTopDownRoot()) # helper function to print the profile
}

func printTree(nest string, node *v8.CPUProfileNode) {
	fmt.Printf("%s%s %s:%d:%d\n", nest, node.GetFunctionName(), node.GetScriptResourceName(), node.GetLineNumber(), node.GetColumnNumber())
	count := node.GetChildrenCount()
	if count == 0 {
		return
	}
	nest = fmt.Sprintf("%s  ", nest)
	for i := 0; i < count; i++ {
		printTree(nest, node.GetChild(i))
	}
}

// Output
// (root) :0:0
//   (program) :0:0
//   start script.js:23:15
//     foo script.js:15:13
//       delay script.js:12:15
//         loop script.js:1:14
//       bar script.js:13:13
//         delay script.js:12:15
//           loop script.js:1:14
//       baz script.js:14:13
//         delay script.js:12:15
//           loop script.js:1:14
//   (garbage collector) :0:0

Documentation

Go Reference & more examples: https://pkg.go.dev/rogchap.com/v8go

Support

If you would like to ask questions about this library or want to keep up-to-date with the latest changes and releases, please join the #v8go channel on Gophers Slack. Click here to join the Gophers Slack community!

Windows

There used to be Windows binary support. For further information see, PR #234.

The v8go library would welcome contributions from anyone able to get an external windows build of the V8 library linking with v8go, using the version of V8 checked out in the deps/v8 git submodule, and documentation of the process involved. This process will likely involve passing a linker flag when building v8go (e.g. using the CGO_LDFLAGS environment variable.

V8 dependency

V8 version: 9.0.257.18 (April 2021)

In order to make v8go usable as a standard Go package, prebuilt static libraries of V8 are included for Linux and macOS. you should not require to build V8 yourself.

Due to security concerns of binary blobs hiding malicious code, the V8 binary is built via CI ONLY.

Project Goals

To provide a high quality, idiomatic, Go binding to the V8 C++ API.

The API should match the original API as closely as possible, but with an API that Gophers (Go enthusiasts) expect. For example: using multiple return values to return both result and error from a function, rather than throwing an exception.

This project also aims to keep up-to-date with the latest (stable) release of V8.

License

FOSSA Status

Development

Recompile V8 with debug info and debug checks

Aside from data races, Go should be memory-safe and v8go should preserve this property by adding the necessary checks to return an error or panic on these unsupported code paths. Release builds of v8go don't include debugging information for the V8 library since it significantly adds to the binary size, slows down compilation and shouldn't be needed by users of v8go. However, if a v8go bug causes a crash (e.g. during new feature development) then it can be helpful to build V8 with debugging information to get a C++ backtrace with line numbers. The following steps will not only do that, but also enable V8 debug checking, which can help with catching misuse of the V8 API.

  1. Make sure to clone the projects submodules (ie. the V8's depot_tools project): git submodule update --init --recursive
  2. Build the V8 binary for your OS: deps/build.py --debug. V8 is a large project, and building the binary can take up to 30 minutes.
  3. Build the executable to debug, using go build for commands or go test -c for tests. You may need to add the -ldflags=-compressdwarf=false option to disable debug information compression so this information can be read by the debugger (e.g. lldb that comes with Xcode v12.5.1, the latest Xcode released at the time of writing)
  4. Run the executable with a debugger (e.g. lldb -- ./v8go.test -test.run TestThatIsCrashing, run to start execution then use bt to print a bracktrace after it breaks on a crash), since backtraces printed by Go or V8 don't currently include line number information.

Upgrading the V8 binaries

We have the upgradev8 workflow. The workflow is triggered every day or manually.

If the current v8_version is different from the latest stable version, the workflow takes care of fetching the latest stable v8 files and copying them into deps/include. The last step of the workflow opens a new PR with the branch name v8_upgrade/<v8-version> with all the changes.

The next steps are:

  1. The build is not yet triggered automatically. To trigger it manually, go to the V8 Build Github Action, Select "Run workflow", and select your pushed branch eg. v8_upgrade/<v8-version>.
  2. Once built, this should open 3 PRs against your branch to add the libv8.a for Linux (for x86_64) and macOS for x86_64 and arm64; merge these PRs into your branch. You are now ready to raise the PR against master with the latest version of V8.

Flushing after C/C++ standard library printing for debugging

When using the C/C++ standard library functions for printing (e.g. printf), then the output will be buffered by default. This can cause some confusion, especially because the test binary (created through go test) does not flush the buffer at exit (at the time of writing). When standard output is the terminal, then it will use line buffering and flush when a new line is printed, otherwise (e.g. if the output is redirected to a pipe or file) it will be fully buffered and not even flush at the end of a line. When the test binary is executed through go test . (e.g. instead of separately compiled with go test -c and run with ./v8go.test) Go may redirect standard output internally, resulting in standard output being fully buffered.

A simple way to avoid this problem is to flush the standard output stream after printing with the fflush(stdout); statement. Not relying on the flushing at exit can also help ensure the output is printed before a crash.

Local leak checking

Leak checking is automatically done in CI, but it can be useful to do locally to debug leaks.

Leak checking is done using the Leak Sanitizer which is a part of LLVM. As such, compiling with clang as the C/C++ compiler seems to produce more complete backtraces (unfortunately still only of the system stack at the time of writing).

For instance, on a Debian-based Linux system, you can use sudo apt-get install clang-12 to install a recent version of clang. Then CC and CXX environment variables are needed to use that compiler. With that compiler, the tests can be run as follows

CC=clang-12 CXX=clang++-12 go test -c --tags leakcheck && ./v8go.test

The separate compile and link commands are currently needed to get line numbers in the backtrace.

On macOS, leak checking isn't available with the version of clang that comes with Xcode, so a separate compiler installation is needed. For example, with homebrew, brew install llvm will install a version of clang with support for this. The ASAN_OPTIONS environment variable will also be needed to run the code with leak checking enabled, since it isn't enabled by default on macOS. E.g. with the homebrew installation of llvm, the tests can be run with

CXX=/usr/local/opt/llvm/bin/clang++ CC=/usr/local/opt/llvm/bin/clang go test -c --tags leakcheck -ldflags=-compressdwarf=false
ASAN_OPTIONS=detect_leaks=1 ./v8go.test

The -ldflags=-compressdwarf=false is currently (with clang 13) needed to get line numbers in the backtrace.

Formatting

Go has go fmt, C has clang-format. Any changes to the v8go.h|cc should be formated with clang-format with the "Chromium" Coding style. This can be done easily by running the go generate command.

brew install clang-format to install on macOS.


V8 Gopher image based on original artwork from the amazing Renee French.

v8go's People

Contributors

cleiner avatar colinking avatar dylanahsmith avatar epk avatar fizx avatar genevieve avatar github-actions[bot] avatar gustavocaso avatar iwind avatar jacques-n avatar jargv avatar katalonne avatar kuoruan avatar lukasmalkmus avatar mehrdadrad avatar neptoess avatar pigloverabbit520 avatar rgood avatar robfig avatar rogchap avatar ryanmurakami avatar tommie avatar wisepythagoras avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

v8go's Issues

How to custom APIs?

I'm interesting in adding custom APIs, such as adding a console API to print.

My question is how to add callbacks from JS to Go using v8go. I've used J2V8 before which allows to add APIs to the global scope, such as an object for console with the method log which calls back to Java in that case. Is that possible using this project?

The ideal API would be something like (pseudo code):

console = CreateObject()

console.AddMethod("log", (params Value[]) {
	print(params[0])
})

isolate.Add("console", console)

In addition, it would be much more useful to actually be able to use Value with more than strings.

Add Isolate.ThrowException(exception) support

Thanks for amazing project! Very exciting to be able to use v8 so easily.

I'm providing my JS script an API implemented with a Go FunctionCallback. If an error occurs, how do I signal that to the caller? Throwing a JS error seems like the correct route, but I don't see any way to do that.

For example

	
	global, _ := v8go.NewObjectTemplate(iso)
	fn, _ := v8go.NewFunctionTemplate(iso, func(info *v8go.FunctionCallbackInfo) *v8go.Value {
		// HOW TO RAISE A JS ERROR?
		return nil
	})
	global.Set("myfn", fn)

Thank you

V8 values returned by v8go are not garbage collected by V8

Using the print function from the examples (even with the actual fmt.Printf() commented out) seems to result in a memory leak.

Slightly modified example from the Readme to call the print function in an infinite loop:

iso, _ := v8go.NewIsolate() // create a new VM
// a template that represents a JS function
printfn, _ := v8go.NewFunctionTemplate(iso, func(info *v8go.FunctionCallbackInfo) *v8go.Value {
    //fmt.Printf("%v", info.Args()) // when the JS function is called this Go callback will execute
    return nil // you can return a value back to the JS caller if required
})
global, _ := v8go.NewObjectTemplate(iso) // a template that represents a JS Object
global.Set("print", printfn) // sets the "print" property of the Object to our function
ctx, _ := v8go.NewContext(iso, global) // new Context with the global Object set to our object template
ctx.RunScript("while (true) { print('foo') }", "print.js") // will execute the Go callback with a single argunent 'foo'

Warning: Running this will quickly consume all memory of the machine.

Removing the 'foo' parameter of the print() call seems to stop the leak. So I guess the parameters of the callback are somehow leaking.

why?

$ go get -u all
go: finding rogchap.com/v8go latest
go: downloading rogchap.com/v8go v0.0.0-20190917100500-0b5c09a84e8d
go: extracting rogchap.com/v8go v0.0.0-20190917100500-0b5c09a84e8d

rogchap.com/v8go

In file included from v8go.cc:3:
C:/Users/Administrator/go/pkg/mod/rogchap.com/[email protected]/deps/include/v8.h:
7063:5: error: typedef 'v8::UnhandledExceptionCallback' is initialized (use decltype instead)
_EXCEPTION_POINTERS* exception_pointers);
^~~~~~~~~~~~~~~~~~~
C:/Users/Administrator/go/pkg/mod/rogchap.com/[email protected]/deps/include/v8.h:
7063:5: error: '_EXCEPTION_POINTERS' was not declared in this scope
C:/Users/Administrator/go/pkg/mod/rogchap.com/[email protected]/deps/include/v8.h:
7063:26: error: 'exception_pointers' was not declared in this scope
_EXCEPTION_POINTERS* exception_pointers);
^~~~~~~~~~~~~~~~~~
C:/Users/Administrator/go/pkg/mod/rogchap.com/[email protected]/deps/include/v8.h:
8741:7: error: 'UnhandledExceptionCallback' has not been declared
UnhandledExceptionCallback unhandled_exception_callback);
^~~~~~~~~~~~~~~~~~~~~~~~~~

Administrator@admin-PC MINGW64 /e/Work/Golang/test/v8go
$ go version
go version go1.13 windows/amd64

Possible memory leak

Hi,

I've been playing with v8go, and so far liking it a lot!

I'm running into a fatal error when benchmarking isolate initialization (and running a simple workload). Here's my code:

package main

import (
	"encoding/json"
	"fmt"
	"math/rand"

	"rogchap.com/v8go"
)

const n = 10000 // tweak this

const script = `
	const process = (record) => {
		const res = [];
		for (let [k, v] of Object.entries(record)) {
			res.push({
				name: k,
				value: v,
			});
		}
		return JSON.stringify(res);
	};
`

func main() {
	for i := 0; i < n; i++ {
		vm, _ := v8go.NewIsolate()
		ctx, _ := v8go.NewContext(vm)
		ctx.RunScript(script, "main.js")
		str, _ := json.Marshal(makeObject())
		cmd := fmt.Sprintf("process(%s)", str)
		ctx.RunScript(cmd, "cmd.js")
	}
}

func makeObject() interface{} {
	return map[string]interface{}{
		"a": rand.Intn(1000000),
		"b": "AAAABBBBAAAABBBBAAAABBBBAAAABBBBAAAABBBB",
	}
}

When running it with n = 1000, it completes just fine. But running with n = 10000 gives me this error:

fatal error: unexpected signal during runtime execution
[signal SIGSEGV: segmentation violation code=0x1 addr=0xb01dfacedebac1e pc=0x4204ba0]

runtime stack:
runtime.throw(0x4ac92d0, 0x2a)
	/usr/local/go/src/runtime/panic.go:617 +0x72
runtime.sigpanic()
	/usr/local/go/src/runtime/signal_unix.go:374 +0x4a9

goroutine 18 [syscall]:
runtime.cgocall(0x40b5370, 0xc0000466b8, 0x0)
	/usr/local/go/src/runtime/cgocall.go:128 +0x5b fp=0xc000046688 sp=0xc000046650 pc=0x4004dfb
rogchap.com/v8go._Cfunc_ValueDispose(0x9ceed8d0)
	_cgo_gotypes.go:179 +0x41 fp=0xc0000466b8 sp=0xc000046688 pc=0x40b3961
rogchap.com/v8go.(*Value).finalizer.func1(0xc000422d58)
	/Users/benjamin/go/pkg/mod/rogchap.com/[email protected]/value.go:26 +0x5e fp=0xc0000466f8 sp=0xc0000466b8 pc=0x40b4c3e
rogchap.com/v8go.(*Value).finalizer(0xc000422d58)
	/Users/benjamin/go/pkg/mod/rogchap.com/[email protected]/value.go:26 +0x2b fp=0xc000046728 sp=0xc0000466f8 pc=0x40b449b
runtime.call32(0x0, 0x4aca8f0, 0xc0003c2000, 0x1000000010)
	/usr/local/go/src/runtime/asm_amd64.s:519 +0x3b fp=0xc000046758 sp=0xc000046728 pc=0x40522cb
runtime.runfinq()
	/usr/local/go/src/runtime/mfinal.go:222 +0x1e2 fp=0xc0000467e0 sp=0xc000046758 pc=0x40179d2
runtime.goexit()
	/usr/local/go/src/runtime/asm_amd64.s:1337 +0x1 fp=0xc0000467e8 sp=0xc0000467e0 pc=0x4053fc1
created by runtime.createfing
	/usr/local/go/src/runtime/mfinal.go:156 +0x61

goroutine 1 [syscall]:
rogchap.com/v8go._Cfunc_NewContext(0x9d8df000, 0x0)
	_cgo_gotypes.go:139 +0x4a
rogchap.com/v8go.NewContext.func1(0xc00046a008, 0xc000054ee0)
	/Users/benjamin/go/pkg/mod/rogchap.com/[email protected]/context.go:35 +0x5e
rogchap.com/v8go.NewContext(0xc00046a008, 0x0, 0x0, 0x4ac311b)
	/Users/benjamin/go/pkg/mod/rogchap.com/[email protected]/context.go:35 +0x3d
main.main()
	/Users/benjamin/Documents/streamtest/v8/main.go:29 +0x114
exit status 2

Monitoring the process, I noticed it's allocating several gigabytes of memory until eventually crashing.

Moving the initialization of cmd out of the loop prevents the crash, but the process still racks up lots of memory. It seems to me that this whole process should be lightweight, as it never runs more than a single isolate at once. Would appreciate some thoughts :)

Segmentation fault when calling Go func

The below code causes a panic due to a null pointer dereference in goContext() (ctx returned by getContext() seems nil).

package main

import (
	"fmt"

	"rogchap.com/v8go"
)


func main() {
	iso, _ := v8go.NewIsolate()
	global, _ := v8go.NewObjectTemplate(iso)

	printfn, _ := v8go.NewFunctionTemplate(iso, func(info *v8go.FunctionCallbackInfo) *v8go.Value {
		fmt.Printf("%+v\n", info.Args())
		return nil
	})

	global.Set("print", printfn, v8go.ReadOnly)


	ctx, _ := v8go.NewContext(iso, global)
	val, _ := ctx.RunScript(`() => { print("foo"); }`, "value.js")
	fn, _ := val.AsFunction()
	val, _ = fn.Call()

	fmt.Printf("result: %+v\n", val)
}
$ go run test.go 
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x850ed3]

goroutine 1 [running]:
rogchap.com/v8go.goContext(...)
	/home/user/go/pkg/mod/rogchap.com/[email protected]/context.go:164
rogchap.com/v8go._Cfunc_FunctionCall(0x2c2f3e0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
	_cgo_gotypes.go:191 +0x4d
rogchap.com/v8go.(*Function).Call.func1(0xc000067f40, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc000067e78, 0xc000067ea0, 0x850d15)
	/home/user/go/pkg/mod/rogchap.com/[email protected]/function.go:28 +0x93
rogchap.com/v8go.(*Function).Call(0xc000067f40, 0x0, 0x0, 0x0, 0x8, 0xc000012320, 0x0)
	/home/user/go/pkg/mod/rogchap.com/[email protected]/function.go:28 +0x11e
main.main()
	/home/user/dev/foo/test.go:25 +0x1c5
exit status 2

In file included from v8go.cc:3:

# rogchap.com/v8go
In file included from v8go.cc:3:
D:/wwwroot/go/src/rogchap.com/v8go/deps/include/v8.h:7161:5: error: typedef 'v8::UnhandledExceptionCallback' is initialized (use decltype instead)
     _EXCEPTION_POINTERS* exception_pointers);
     ^~~~~~~~~~~~~~~~~~~
D:/wwwroot/go/src/rogchap.com/v8go/deps/include/v8.h:7161:5: error: '_EXCEPTION_POINTERS' was not declared in this scope
D:/wwwroot/go/src/rogchap.com/v8go/deps/include/v8.h:7161:26: error: 'exception_pointers' was not declared in this scope
     _EXCEPTION_POINTERS* exception_pointers);
                          ^~~~~~~~~~~~~~~~~~
D:/wwwroot/go/src/rogchap.com/v8go/deps/include/v8.h:7161:26: note: suggested alternative: '_pxcptinfoptrs'
     _EXCEPTION_POINTERS* exception_pointers);
                          ^~~~~~~~~~~~~~~~~~
                          _pxcptinfoptrs
D:/wwwroot/go/src/rogchap.com/v8go/deps/include/v8.h:8844:7: error: 'UnhandledExceptionCallback' has not been declared
       UnhandledExceptionCallback unhandled_exception_callback);
       ^~~~~~~~~~~~~~~~~~~~~~~~~~

Support for debugging with Chrome DevTools

This's more of a feature request.
I'd like to specify a port in which v8go will expose a debugging websocket protocol.

Something like:

port := 9222
ctx, _ := v8go.NewContext(nil, port)

and connect to the v8 instance with chrome://inspect for debugging.

Thanks
Joe

Add linux/arm/v7 & linux/arm64 to architectures

ARM is a growing trend in server architecture and both can be built using standard github actions.

I opened a PR for arm64, but I'm having issues with armhf: see logs (PR: abemedia#1)

I'm a bit confused as clang is set to false but the commands that fail are running clang...

How to access `this` within a function template?

You can use a FunctionTemplate to create a constructor, having it return an instance from an ObjectTemplate.

You can set functions on an ObjectTemplate so the object has methods.

But, when someone calls a method (impl as a function template) on an object (from an object template), there's no way that I can see to get access to the object itself from within the method implementation. Are there any examples of something like this actually working?

I started a thread on slack but haven't heard anything.

Code example:

package main

import (
	"fmt"

	"rogchap.com/v8go"
)

func main() {
	iso, _ := v8go.NewIsolate()
	g, _ := v8go.NewObjectTemplate(iso)

	ot, err := v8go.NewObjectTemplate(iso)
	if err != nil {
		panic(err)
	}

	fooFn, err := v8go.NewFunctionTemplate(iso, func(info *v8go.FunctionCallbackInfo) *v8go.Value {
		// How do I get the instance created in the constructor below?
		fmt.Printf("MyObject.foo(): %q\n", info.Args())
		v, _ := v8go.NewValue(iso, "what is 'this'")
		return v
	})
	if err != nil {
		panic(err)
	}

	if err := ot.Set("foo", fooFn); err != nil {
		panic(err)
	}

	constructor, _ := v8go.NewFunctionTemplate(iso, func(info *v8go.FunctionCallbackInfo) *v8go.Value {
		inst, _ := ot.NewInstance(info.Context())
		inst.Set("prop", info.Args()[0])
		return inst.Value
	})

	if err := g.Set("MyObject", constructor); err != nil {
		panic(err)
	}

	ctx, _ := v8go.NewContext(iso, g)

	rv, err := ctx.RunScript(`let x = new MyObject('bar'); x.foo()`, "script.js")
	fmt.Printf("rv: %q, err: %q\n", rv, err)
}

[bug] segmentation violation panic after garbage collection

Hello!

I investigate random segmentation violation panics. This happens when GC execute value finalizer on disposed isolate.

Maybe related to #39.

Code to reproduce.

package main

import (
	"fmt"
	"runtime"
	"time"

	"rogchap.com/v8go"
)

func main() {
	iso, _ := v8go.NewIsolate()
	v8ctx, _ := v8go.NewContext(iso)
	val, _ := v8go.NewValue(iso, "some string")

	fmt.Println(val.String())

	v8ctx.Close()
	iso.Dispose()

	runtime.GC() // force garbage collection

	time.Sleep(time.Second)
}

Stack trace

fatal error: unexpected signal during runtime execution
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x4232ee8]

runtime stack:
runtime.throw(0x4c066c4, 0x2a)
        /usr/local/Cellar/go/1.16/libexec/src/runtime/panic.go:1117 +0x72
runtime.sigpanic()
        /usr/local/Cellar/go/1.16/libexec/src/runtime/signal_unix.go:718 +0x2ef

goroutine 5 [syscall]:
runtime.cgocall(0x40adde0, 0xc00004ee90, 0xf)
        /usr/local/Cellar/go/1.16/libexec/src/runtime/cgocall.go:154 +0x5b fp=0xc00004ee60 sp=0xc00004ee28 pc=0x400657b
rogchap.com/v8go._Cfunc_ValueFree(0xcb185e0)
        _cgo_gotypes.go:618 +0x3c fp=0xc00004ee90 sp=0xc00004ee60 pc=0x40aa91c
rogchap.com/v8go.(*Value).finalizer.func1(0xc0000122e0)
        /Users/user/projects/project/vendor/rogchap.com/v8go/value.go:530 +0x55 fp=0xc00004eec8 sp=0xc00004ee90 pc=0x40ad155
rogchap.com/v8go.(*Value).finalizer(0xc0000122e0)
        /Users/user/projects/project/vendor/rogchap.com/v8go/value.go:530 +0xa5 fp=0xc00004ef38 sp=0xc00004eec8 pc=0x40ac4a5
runtime.call16(0x0, 0x4c07d70, 0xc00001a070, 0x1000000010)
        /usr/local/Cellar/go/1.16/libexec/src/runtime/asm_amd64.s:550 +0x3e fp=0xc00004ef58 sp=0xc00004ef38 pc=0x4066afe
runtime.runfinq()
        /usr/local/Cellar/go/1.16/libexec/src/runtime/mfinal.go:222 +0x1f4 fp=0xc00004efe0 sp=0xc00004ef58 pc=0x401aab4
runtime.goexit()
        /usr/local/Cellar/go/1.16/libexec/src/runtime/asm_amd64.s:1371 +0x1 fp=0xc00004efe8 sp=0xc00004efe0 pc=0x4068541
created by runtime.createfing
        /usr/local/Cellar/go/1.16/libexec/src/runtime/mfinal.go:156 +0x65

goroutine 1 [sleep]:
time.Sleep(0x3b9aca00)
        /usr/local/Cellar/go/1.16/libexec/src/runtime/time.go:193 +0xd2
main.main()
        /Users/user/projects/project/tst/main.go:23 +0x146

pre-compiling code

Is there a way to pre-compile code to be run over and over again for performance vs evaluating always at the golang bindings level?
as in:

/v8go.cc: 394

MaybeLocal<Script> script = Script::Compile(local_ctx, src, &script_origin);
if (script.IsEmpty()) {
rtn.error = ExceptionError(try_catch, iso, local_ctx);
return rtn;
}

Consider using panic

Every method in the API returns an error, which makes it unnecessarily cumbersome to use.

For example

// NewFunctionTemplate creates a FunctionTemplate for a given callback.
func NewFunctionTemplate(iso *Isolate, callback FunctionCallback) (*FunctionTemplate, error) {
	if iso == nil {
		return nil, errors.New("v8go: failed to create new FunctionTemplate: Isolate cannot be <nil>")
	}
	if callback == nil {
		return nil, errors.New("v8go: failed to create new FunctionTemplate: FunctionCallback cannot be <nil>")
	}

	cbref := iso.registerCallback(callback)

	tmpl := &template{
		ptr: C.NewFunctionTemplate(iso.ptr, C.int(cbref)),
		iso: iso,
	}
	runtime.SetFinalizer(tmpl, (*template).finalizer)
	return &FunctionTemplate{tmpl}, nil
}

In this case, I would argue that it should panic if either of the parameter are nil, because this is incorrect use of the API and is trivially able to be checked by a caller who might have an Isolate or FunctionCallback of unknown provenance.

To draw a comparison, strings.Repeat panics if an invalid argument is provided, and I have never heard of that being considered problematic. Instead, returning many errors that can never happen either results in callers needing to do one of 3 things:

  1. Add a panic in their code, in which case, what has been saved? This is what I do, unhappily.
  2. Ignore the error. This is bad, because they would need to view the implementation to know which errors are impossible.
  3. Handle the error. This is time wasted and additional code to maintain

Obviously it's a breaking change, but I think it would be a welcome one to remove error returns of this sort from the API.

Thanks for considering.

Support for the Object value type

As documented here: https://v8.github.io/api/head/classv8_1_1Object.html

The Object class represents the non-primitive data types. ie (Objects, Arrays, Promises, Dates, Regex etc)
Primitive data types where implemented in #59

The main "entry" to the Object should be from the Value struct. ie. val.Object().

This may require a multi-PR as the Object class is quite large, and breaking it down will make implementation and reviews easier.

Compile against older libstdc++

It appears that the bundled libv8.a was compiled on a system with the latest versions of shared libraries. Our production environment runs CentOS7, and such new versions are not available without a major upgrade. My impression is that using CentOS in production is a common practice and CentOS7 is not terribly out of date, so I would suspect that this impacts others as well. This is not my area of expertise, though.

Is it a desirable and straightforward to compile libv8.a against older versions? That would allow it to run on a larger variety of systems, and I do not believe that there would be significant drawbacks for users on newer systems. Or, can you think of any workarounds that don't require rebuilding libv8.a or upgrading our production servers?

On deployment, I see these errors:

/lib64/libstdc++.so.6: version `GLIBCXX_3.4.21' not found
/lib64/libstdc++.so.6: version `CXXABI_1.3.9' not found
/lib64/libstdc++.so.6: version `CXXABI_1.3.8' not found
/lib64/libstdc++.so.6: version `GLIBCXX_3.4.20' not found

The system has glibcxx 3.4.19, and it requires 3.4.21:

$ cat /etc/centos-release
CentOS Linux release 7.9.2009 (Core)

$ ls -l /lib64/libstdc++.so.6
lrwxrwxrwx. 1 root root 19 Dec 21 09:32 /lib64/libstdc++.so.6 -> libstdc++.so.6.0.19

Panic when calling a JS function from a Go function

I'm attempting to pass a JS function as a parameter to a Go callback, then later invoke the JS function using its Call method, resulting in the error shown below. I may be doing this incorrectly, or I may be wrong to expect it to work at all.

Using the latest v8go (v0.5.2-0.20210416054929-571ffb097524) as of April 20th.

package main

import (
	"fmt"

	"rogchap.com/v8go"
)

func main() {
	iso, _ := v8go.NewIsolate()
	gbl, _ := v8go.NewObjectTemplate(iso)
	doCallback, _ := v8go.NewFunctionTemplate(iso, func(info *v8go.FunctionCallbackInfo) *v8go.Value {
		tf, _ := info.Args()[0].AsFunction()
		tf.Call()
		return nil
	})
	gbl.Set("doCallback", doCallback)
	ctx, _ := v8go.NewContext(iso, gbl)
	val, err := ctx.RunScript("doCallback(() => true)", "test.js")
	fmt.Printf("%v %v", val, err)
}
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x86d2d6]

goroutine 1 [running]:
rogchap.com/v8go.(*Context).register(0x0)
	/mnt/storage/home/echicken/Documents/Development/go/src/github.com/echicken/callbacks/vendor/rogchap.com/v8go/context.go:129 +0x36
rogchap.com/v8go.(*Function).Call(0xc000010040, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
	/mnt/storage/home/echicken/Documents/Development/go/src/github.com/echicken/callbacks/vendor/rogchap.com/v8go/function.go:28 +0x21a
main.main.func1(0xc000064020, 0x0)
	/mnt/storage/home/echicken/Documents/Development/go/src/github.com/echicken/callbacks/main.go:17 +0x148
rogchap.com/v8go.goFunctionCallback(0x1, 0x1, 0x7ffe1323cbd0, 0x1, 0x0)
	/mnt/storage/home/echicken/Documents/Development/go/src/github.com/echicken/callbacks/vendor/rogchap.com/v8go/function_template.go:77 +0x2fd
rogchap.com/v8go._Cfunc_RunScript(0x366b430, 0x366b410, 0x366b3f0, 0x0, 0x0, 0x0, 0x0)
	_cgo_gotypes.go:668 +0x50
rogchap.com/v8go.(*Context).RunScript.func3(0xc00000e030, 0x366b410, 0x366b3f0, 0x0, 0x0, 0x0, 0x0)
	/mnt/storage/home/echicken/Documents/Development/go/src/github.com/echicken/callbacks/vendor/rogchap.com/v8go/context.go:101 +0xad
rogchap.com/v8go.(*Context).RunScript(0xc00000e030, 0x140cbe5, 0x16, 0x140a283, 0x7, 0x0, 0x0, 0x0)
	/mnt/storage/home/echicken/Documents/Development/go/src/github.com/echicken/callbacks/vendor/rogchap.com/v8go/context.go:101 +0x173
main.main()
	/mnt/storage/home/echicken/Documents/Development/go/src/github.com/echicken/callbacks/main.go:22 +0x248

using defer on unsafe package

I have application which have over 200 goroutine, every goroutine holds their own vm (Isolate) and context, this goroutine listens more than one channels and one them recieves json data, goroutine gives this json to js function in already defined in context and receives result as strigify json.

When i run agreesion test on application between two computer with 200 concurrenct connection my application sometimes crash. i look this crash reports and i see the problem using defer in String function of *Value.

Here is my openion:

After i receive value from *Value i break the select statement and same time garbage collection kicks in and remove value's variable from memory then defer kicks in, it try to free allocation of unsafe pointer but this pointer value is collected and result of this: a crash

i remove defer statement in my local directory and String function become like this

func (v *Value) String() string {
	s := C.ValueToString(v.ptr)
	valueStr := C.GoString(s)
        C.free(unsafe.Pointer(s)) // free memory
        return valueStr 
}

Now my application runs smootly.

Using the same isolate in multiple concurrent go-routines

I have some code that creates a VM / isolate once, and then pass the isolate into a go routine. Inside this go routine, I create a context from the isolate, and create a new object template.

This works fine for a little while, but then panics (as more than a handful go routines run concurrently). If I pass in a brand new isolate to each instance of the go routine, the panic won't happen.

Am I wrong thinking this ought to work?

error building from windows

I was following all the readme steps to compile from windows but I get this error:

go run .\main.go

# rogchap.com/v8go
v8go.cc:16:37: fatal error: libplatform/libplatform.h: No such file or directory
compilation terminated.

main.go

package main

import (
	"rogchap.com/v8go"
	"fmt"
)

func main() {
	ctx, _ := v8go.NewContext() 
	ctx.RunScript("const add = (a, b) => a + b", "math.js")
	ctx.RunScript("const result = add(3, 4)", "main.js")
	val, _ := ctx.RunScript("result", "value.js")
	fmt.Printf("addition result: %s", val)
}

i'm using go 1.16 and windows 10 64bits

v8go is not in GOROOT

Hi there,

my go.mod is using go 1.16
I run go mod tidy

And I get this message

render imports
        rogchap/v8go: package rogchap/v8go is not in GOROOT (/usr/local/Cellar/go/1.16/libexec/src/rogchap/v8go)

Any idea what the issue is?

More background info?

A few questions I have about this repo:

  • It says pre-built binaries are included. Is this simply to reduce rebuild complexity, or is there another reason?

  • Pre-built binaries are only provided for linux and osx, what about windows, bsd, etc? Aside from reducing your own build complexity, is there any reason those platforms are omitted?

  • I saw mention of C, why is C used instead of rewriting v8 in pure go?

  • Another interpreter, otto, is written in Go, but has some limitations, such as Go doesn't support backtracking in regex which means those regex patterns aren't supported in the executed js. I assume this library isn't impacted since it doesn't use pure go?

  • What's the author's use case for this repo? Curious what problems you solved for yourself by tying v8 to go.

Polyfills

Hi all, I was wondering if you intent to maintain polyfills for some of the more common browser APIs: console, Fetch, atob, setTimeout... ?

Proposal: create GitHub organization to facilitate more distributed maintenance

This project is great! Many v8 bindings exist and this one is among the best if not the best. To lower burden on @rogchap alone I suggest that the v8go project moves to a GitHub organization and that some maintainer contribution guidelines be established.

I can't commit for the long run but I'm happy to help get some of this in place over the next couple of months.

Thoughts?

'v8.h' file not found

Hello 👋 First of all, thank you for building this library!

I was trying to use this package with my project and received the following error after running dep ensure:

# <>/<>/<>/forms-api/vendor/rogchap.com/v8go
v8go.cc:3:10: fatal error: 'v8.h' file not found

Am I missing any steps ? This is how my vendor directory looks after running ensure:
Screen Shot 2020-02-19 at 2 01 35 PM

dep version output:

dep:
 version     : v0.5.0
 build date  : 2018-08-16
 git hash    : 224a564
 go version  : go1.10.3
 go compiler : gc
 platform    : darwin/amd64
 features    : ImportDuringSolve=false

windows release dependencies

Hi, has any one tried to release a windows app build with v8go or to send it for testing on non-developer PC? I tried to do that but it will require all the v8 related libraries. I am wondering if anyone has a list of dependencies to be included with the .exe file built.

I have tried include all the .dll libraries in the C:\msys64\mingw64\bin folder, the app build still does not run (crash immediately after started).

Any help is appreciated. Thank you.

"undefined: v8go.Context" in GitHub Actions

First, thanks for sharing this project, it's a lot faster than other things I've tried 🚀

It builds fine on my local (Ubuntu 18.04) laptop, but I'm having some trouble using it with GitHub Actions however (I'm using Goreleaser to automatically build binaries). Anywhere it's referenced I see errors like undefined: v8go.Context and undefined: v8go.NewContext (see: https://github.com/plentico/plenti/runs/930324024#step:8:151).

GitHub Actions is using Ubuntu 18.04.4 too: https://github.com/plentico/plenti/runs/930279376#step:1:3

Is there something special I need to do to run this in CI environments? Thank you!

the version of v8

I see that the version of v8 is required to be 8.9.255.20, can I compile the latest version of v8?

libv8.a bundled with v0.3.0 doesn't work with golang Docker image

Testing with a very basic main.go

package main

import "rogchap.com/v8go"

func main() {
	if _, err := v8go.NewIsolate(); err != nil {
		panic(err)
	}
}

And the following Dockerfile

FROM golang:1.15

WORKDIR /go/src/v8
COPY . .
RUN go run main.go

I get this from docker build .

Sending build context to Docker daemon  75.76MB
Step 1/4 : FROM golang:1.15
 ---> 05c8f6d2538a
Step 2/4 : WORKDIR /go/src/v8
 ---> Using cache
 ---> 70761780e417
Step 3/4 : COPY . .
 ---> 96c01a616e96
Step 4/4 : RUN go run main.go
 ---> Running in f434a5b958aa
# rogchap.com/v8go
/usr/bin/ld: vendor/rogchap.com/v8go/deps/linux-x86_64/libv8.a(isolate.o): in function `std::_Sp_counted_ptr_inplace<v8::internal::Counters, std::allocator<v8::internal::Counters>, (__gnu_cxx::_Lock_policy)2>::_M_get_deleter(std::type_info const&)':
isolate.cc:(.text._ZNSt23_Sp_counted_ptr_inplaceIN2v88internal8CountersESaIS2_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info[_ZNSt23_Sp_counted_ptr_inplaceIN2v88internal8CountersESaIS2_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info]+0x16): undefined reference to `std::_Sp_make_shared_tag::_S_eq(std::type_info const&)'
/usr/bin/ld: vendor/rogchap.com/v8go/deps/linux-x86_64/libv8.a(isolate.o): in function `std::_Sp_counted_ptr_inplace<v8::internal::metrics::Recorder, std::allocator<v8::internal::metrics::Recorder>, (__gnu_cxx::_Lock_policy)2>::_M_get_deleter(std::type_info const&)':
isolate.cc:(.text._ZNSt23_Sp_counted_ptr_inplaceIN2v88internal7metrics8RecorderESaIS3_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info[_ZNSt23_Sp_counted_ptr_inplaceIN2v88internal7metrics8RecorderESaIS3_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info]+0x16): undefined reference to `std::_Sp_make_shared_tag::_S_eq(std::type_info const&)'
/usr/bin/ld: vendor/rogchap.com/v8go/deps/linux-x86_64/libv8.a(objects.o): in function `std::_Sp_counted_ptr_inplace<v8::internal::wasm::GlobalWasmCodeRef, std::allocator<v8::internal::wasm::GlobalWasmCodeRef>, (__gnu_cxx::_Lock_policy)2>::_M_get_deleter(std::type_info const&)':
objects.cc:(.text._ZNSt23_Sp_counted_ptr_inplaceIN2v88internal4wasm17GlobalWasmCodeRefESaIS3_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info[_ZNSt23_Sp_counted_ptr_inplaceIN2v88internal4wasm17GlobalWasmCodeRefESaIS3_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info]+0x16): undefined reference to `std::_Sp_make_shared_tag::_S_eq(std::type_info const&)'
/usr/bin/ld: vendor/rogchap.com/v8go/deps/linux-x86_64/libv8.a(wasm-code-manager.o): in function `std::_Sp_counted_ptr_inplace<v8::internal::OwnedVector<unsigned char const>, std::allocator<v8::internal::OwnedVector<unsigned char const> >, (__gnu_cxx::_Lock_policy)2>::_M_get_deleter(std::type_info const&)':
wasm-code-manager.cc:(.text._ZNSt23_Sp_counted_ptr_inplaceIN2v88internal11OwnedVectorIKhEESaIS4_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info[_ZNSt23_Sp_counted_ptr_inplaceIN2v88internal11OwnedVectorIKhEESaIS4_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info]+0x16): undefined reference to `std::_Sp_make_shared_tag::_S_eq(std::type_info const&)'
/usr/bin/ld: vendor/rogchap.com/v8go/deps/linux-x86_64/libv8.a(wasm-code-manager.o): in function `std::_Sp_counted_ptr_inplace<v8::internal::wasm::(anonymous namespace)::NativeModuleWireBytesStorage, std::allocator<v8::internal::wasm::(anonymous namespace)::NativeModuleWireBytesStorage>, (__gnu_cxx::_Lock_policy)2>::_M_get_deleter(std::type_info const&)':
wasm-code-manager.cc:(.text._ZNSt23_Sp_counted_ptr_inplaceIN2v88internal4wasm12_GLOBAL__N_128NativeModuleWireBytesStorageESaIS4_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info+0x16): undefined reference to `std::_Sp_make_shared_tag::_S_eq(std::type_info const&)'
/usr/bin/ld: vendor/rogchap.com/v8go/deps/linux-x86_64/libv8.a(wasm-engine.o):wasm-engine.cc:(.text._ZNSt23_Sp_counted_ptr_inplaceIN2v88internal4wasm10WasmEngineESaIS3_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info[_ZNSt23_Sp_counted_ptr_inplaceIN2v88internal4wasm10WasmEngineESaIS3_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info]+0x16): more undefined references to `std::_Sp_make_shared_tag::_S_eq(std::type_info const&)' follow
/usr/bin/ld: vendor/rogchap.com/v8go/deps/linux-x86_64/libv8.a(typer.o): in function `v8::internal::compiler::Typer::Visitor::UpdateType(v8::internal::compiler::Node*, v8::internal::compiler::Type)':
typer.cc:(.text._ZN2v88internal8compiler5Typer7Visitor10UpdateTypeEPNS1_4NodeENS1_4TypeE[_ZN2v88internal8compiler5Typer7Visitor10UpdateTypeEPNS1_4NodeENS1_4TypeE]+0xd9): undefined reference to `std::__cxx11::basic_ostringstream<char, std::char_traits<char>, std::allocator<char> >::basic_ostringstream()'
/usr/bin/ld: vendor/rogchap.com/v8go/deps/linux-x86_64/libv8.a(verifier.o): in function `v8::internal::compiler::Verifier::Visitor::CheckNotTyped(v8::internal::compiler::Node*)':
verifier.cc:(.text._ZN2v88internal8compiler8Verifier7Visitor13CheckNotTypedEPNS1_4NodeE[_ZN2v88internal8compiler8Verifier7Visitor13CheckNotTypedEPNS1_4NodeE]+0x4f): undefined reference to `std::__cxx11::basic_ostringstream<char, std::char_traits<char>, std::allocator<char> >::basic_ostringstream()'
/usr/bin/ld: vendor/rogchap.com/v8go/deps/linux-x86_64/libv8.a(verifier.o): in function `v8::internal::compiler::Verifier::Visitor::CheckOutput(v8::internal::compiler::Node*, v8::internal::compiler::Node*, int, char const*)':
verifier.cc:(.text._ZN2v88internal8compiler8Verifier7Visitor11CheckOutputEPNS1_4NodeES5_iPKc[_ZN2v88internal8compiler8Verifier7Visitor11CheckOutputEPNS1_4NodeES5_iPKc]+0x5e): undefined reference to `std::__cxx11::basic_ostringstream<char, std::char_traits<char>, std::allocator<char> >::basic_ostringstream()'
/usr/bin/ld: vendor/rogchap.com/v8go/deps/linux-x86_64/libv8.a(verifier.o): in function `v8::internal::compiler::Verifier::Visitor::CheckTypeIs(v8::internal::compiler::Node*, v8::internal::compiler::Type)':
verifier.cc:(.text._ZN2v88internal8compiler8Verifier7Visitor11CheckTypeIsEPNS1_4NodeENS1_4TypeE[_ZN2v88internal8compiler8Verifier7Visitor11CheckTypeIsEPNS1_4NodeENS1_4TypeE]+0x79): undefined reference to `std::__cxx11::basic_ostringstream<char, std::char_traits<char>, std::allocator<char> >::basic_ostringstream()'
/usr/bin/ld: vendor/rogchap.com/v8go/deps/linux-x86_64/libv8.a(verifier.o): in function `v8::internal::compiler::Verifier::Visitor::CheckValueInputIs(v8::internal::compiler::Node*, int, v8::internal::compiler::Type)':
verifier.cc:(.text._ZN2v88internal8compiler8Verifier7Visitor17CheckValueInputIsEPNS1_4NodeEiNS1_4TypeE[_ZN2v88internal8compiler8Verifier7Visitor17CheckValueInputIsEPNS1_4NodeEiNS1_4TypeE]+0x11e): undefined reference to `std::__cxx11::basic_ostringstream<char, std::char_traits<char>, std::allocator<char> >::basic_ostringstream()'
/usr/bin/ld: vendor/rogchap.com/v8go/deps/linux-x86_64/libv8.a(module-compiler.o): in function `std::_Sp_counted_ptr_inplace<v8::internal::wasm::JSToWasmWrapperCompilationUnit, std::allocator<v8::internal::wasm::JSToWasmWrapperCompilationUnit>, (__gnu_cxx::_Lock_policy)2>::_M_get_deleter(std::type_info const&)':
module-compiler.cc:(.text._ZNSt23_Sp_counted_ptr_inplaceIN2v88internal4wasm30JSToWasmWrapperCompilationUnitESaIS3_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info[_ZNSt23_Sp_counted_ptr_inplaceIN2v88internal4wasm30JSToWasmWrapperCompilationUnitESaIS3_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info]+0x16): undefined reference to `std::_Sp_make_shared_tag::_S_eq(std::type_info const&)'
/usr/bin/ld: vendor/rogchap.com/v8go/deps/linux-x86_64/libv8.a(module-compiler.o): in function `std::_Sp_counted_ptr_inplace<v8::internal::wasm::(anonymous namespace)::BackgroundCompileToken, std::allocator<v8::internal::wasm::(anonymous namespace)::BackgroundCompileToken>, (__gnu_cxx::_Lock_policy)2>::_M_get_deleter(std::type_info const&)':
module-compiler.cc:(.text._ZNSt23_Sp_counted_ptr_inplaceIN2v88internal4wasm12_GLOBAL__N_122BackgroundCompileTokenESaIS4_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info+0x16): undefined reference to `std::_Sp_make_shared_tag::_S_eq(std::type_info const&)'
/usr/bin/ld: vendor/rogchap.com/v8go/deps/linux-x86_64/libv8.a(module-compiler.o): in function `std::_Sp_counted_ptr_inplace<std::atomic<int>, std::allocator<std::atomic<int> >, (__gnu_cxx::_Lock_policy)2>::_M_get_deleter(std::type_info const&)':
module-compiler.cc:(.text._ZNSt23_Sp_counted_ptr_inplaceISt6atomicIiESaIS1_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info[_ZNSt23_Sp_counted_ptr_inplaceISt6atomicIiESaIS1_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info]+0x16): undefined reference to `std::_Sp_make_shared_tag::_S_eq(std::type_info const&)'
/usr/bin/ld: vendor/rogchap.com/v8go/deps/linux-x86_64/libv8.a(module-compiler.o): in function `std::_Sp_counted_ptr_inplace<v8::base::Semaphore, std::allocator<v8::base::Semaphore>, (__gnu_cxx::_Lock_policy)2>::_M_get_deleter(std::type_info const&)':
module-compiler.cc:(.text._ZNSt23_Sp_counted_ptr_inplaceIN2v84base9SemaphoreESaIS2_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info[_ZNSt23_Sp_counted_ptr_inplaceIN2v84base9SemaphoreESaIS2_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info]+0x16): undefined reference to `std::_Sp_make_shared_tag::_S_eq(std::type_info const&)'
/usr/bin/ld: vendor/rogchap.com/v8go/deps/linux-x86_64/libv8.a(streaming-decoder.o): in function `std::_Sp_counted_ptr_inplace<v8::internal::wasm::AsyncStreamingDecoder::SectionBuffer, std::allocator<v8::internal::wasm::AsyncStreamingDecoder::SectionBuffer>, (__gnu_cxx::_Lock_policy)2>::_M_get_deleter(std::type_info const&)':
streaming-decoder.cc:(.text._ZNSt23_Sp_counted_ptr_inplaceIN2v88internal4wasm21AsyncStreamingDecoder13SectionBufferESaIS4_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info[_ZNSt23_Sp_counted_ptr_inplaceIN2v88internal4wasm21AsyncStreamingDecoder13SectionBufferESaIS4_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info]+0x16): undefined reference to `std::_Sp_make_shared_tag::_S_eq(std::type_info const&)'
/usr/bin/ld: vendor/rogchap.com/v8go/deps/linux-x86_64/libv8.a(machine-graph-verifier.o): in function `v8::internal::compiler::MachineGraphVerifier::Run(v8::internal::compiler::Graph*, v8::internal::compiler::Schedule const*, v8::internal::compiler::Linkage*, bool, char const*, v8::internal::Zone*)':
machine-graph-verifier.cc:(.text._ZN2v88internal8compiler20MachineGraphVerifier3RunEPNS1_5GraphEPKNS1_8ScheduleEPNS1_7LinkageEbPKcPNS0_4ZoneE+0x12f7): undefined reference to `std::__cxx11::basic_stringstream<char, std::char_traits<char>, std::allocator<char> >::basic_stringstream()'
/usr/bin/ld: vendor/rogchap.com/v8go/deps/linux-x86_64/libv8.a(machine-graph-verifier.o): in function `v8::internal::compiler::(anonymous namespace)::MachineRepresentationChecker::CheckValueInputForInt64Op(v8::internal::compiler::Node const*, int)':
machine-graph-verifier.cc:(.text._ZN2v88internal8compiler12_GLOBAL__N_128MachineRepresentationChecker25CheckValueInputForInt64OpEPKNS1_4NodeEi+0xd3): undefined reference to `std::__cxx11::basic_ostringstream<char, std::char_traits<char>, std::allocator<char> >::basic_ostringstream()'
/usr/bin/ld: machine-graph-verifier.cc:(.text._ZN2v88internal8compiler12_GLOBAL__N_128MachineRepresentationChecker25CheckValueInputForInt64OpEPKNS1_4NodeEi+0x178): undefined reference to `std::__cxx11::basic_ostringstream<char, std::char_traits<char>, std::allocator<char> >::basic_ostringstream()'
/usr/bin/ld: vendor/rogchap.com/v8go/deps/linux-x86_64/libv8.a(machine-graph-verifier.o): in function `v8::internal::compiler::(anonymous namespace)::MachineRepresentationChecker::CheckValueInputRepresentationIs(v8::internal::compiler::Node const*, int, v8::internal::MachineRepresentation)':
machine-graph-verifier.cc:(.text._ZN2v88internal8compiler12_GLOBAL__N_128MachineRepresentationChecker31CheckValueInputRepresentationIsEPKNS1_4NodeEiNS0_21MachineRepresentationE+0xf5): undefined reference to `std::__cxx11::basic_stringstream<char, std::char_traits<char>, std::allocator<char> >::basic_stringstream()'
/usr/bin/ld: vendor/rogchap.com/v8go/deps/linux-x86_64/libv8.a(machine-graph-verifier.o): in function `v8::internal::compiler::(anonymous namespace)::MachineRepresentationChecker::CheckValueInputIsCompressedOrTagged(v8::internal::compiler::Node const*, int)':
machine-graph-verifier.cc:(.text._ZN2v88internal8compiler12_GLOBAL__N_128MachineRepresentationChecker35CheckValueInputIsCompressedOrTaggedEPKNS1_4NodeEi+0xf5): undefined reference to `std::__cxx11::basic_ostringstream<char, std::char_traits<char>, std::allocator<char> >::basic_ostringstream()'
/usr/bin/ld: vendor/rogchap.com/v8go/deps/linux-x86_64/libv8.a(machine-graph-verifier.o): in function `v8::internal::compiler::(anonymous namespace)::MachineRepresentationChecker::CheckValueInputIsTagged(v8::internal::compiler::Node const*, int)':
machine-graph-verifier.cc:(.text._ZN2v88internal8compiler12_GLOBAL__N_128MachineRepresentationChecker23CheckValueInputIsTaggedEPKNS1_4NodeEi+0xf5): undefined reference to `std::__cxx11::basic_ostringstream<char, std::char_traits<char>, std::allocator<char> >::basic_ostringstream()'
/usr/bin/ld: vendor/rogchap.com/v8go/deps/linux-x86_64/libv8.a(machine-graph-verifier.o): in function `v8::internal::compiler::(anonymous namespace)::MachineRepresentationChecker::CheckValueInputForFloat64Op(v8::internal::compiler::Node const*, int)':
machine-graph-verifier.cc:(.text._ZN2v88internal8compiler12_GLOBAL__N_128MachineRepresentationChecker27CheckValueInputForFloat64OpEPKNS1_4NodeEi+0xf2): undefined reference to `std::__cxx11::basic_ostringstream<char, std::char_traits<char>, std::allocator<char> >::basic_ostringstream()'
/usr/bin/ld: vendor/rogchap.com/v8go/deps/linux-x86_64/libv8.a(machine-graph-verifier.o): in function `v8::internal::compiler::(anonymous namespace)::MachineRepresentationChecker::CheckValueInputIsTaggedOrPointer(v8::internal::compiler::Node const*, int)':
machine-graph-verifier.cc:(.text._ZN2v88internal8compiler12_GLOBAL__N_128MachineRepresentationChecker32CheckValueInputIsTaggedOrPointerEPKNS1_4NodeEi+0xf5): undefined reference to `std::__cxx11::basic_ostringstream<char, std::char_traits<char>, std::allocator<char> >::basic_ostringstream()'
/usr/bin/ld: vendor/rogchap.com/v8go/deps/linux-x86_64/libv8.a(machine-graph-verifier.o): in function `v8::internal::compiler::(anonymous namespace)::MachineRepresentationChecker::CheckValueInputForInt32Op(v8::internal::compiler::Node const*, int)':
machine-graph-verifier.cc:(.text._ZN2v88internal8compiler12_GLOBAL__N_128MachineRepresentationChecker25CheckValueInputForInt32OpEPKNS1_4NodeEi+0xcf): undefined reference to `std::__cxx11::basic_ostringstream<char, std::char_traits<char>, std::allocator<char> >::basic_ostringstream()'
/usr/bin/ld: vendor/rogchap.com/v8go/deps/linux-x86_64/libv8.a(machine-graph-verifier.o):machine-graph-verifier.cc:(.text._ZN2v88internal8compiler12_GLOBAL__N_128MachineRepresentationChecker25CheckValueInputForInt32OpEPKNS1_4NodeEi+0x137): more undefined references to `std::__cxx11::basic_ostringstream<char, std::char_traits<char>, std::allocator<char> >::basic_ostringstream()' follow
/usr/bin/ld: vendor/rogchap.com/v8go/deps/linux-x86_64/libv8.a(default-platform.o): in function `std::_Sp_counted_ptr_inplace<v8::platform::DefaultJobState, std::allocator<v8::platform::DefaultJobState>, (__gnu_cxx::_Lock_policy)2>::_M_get_deleter(std::type_info const&)':
default-platform.cc:(.text._ZNSt23_Sp_counted_ptr_inplaceIN2v88platform15DefaultJobStateESaIS2_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info[_ZNSt23_Sp_counted_ptr_inplaceIN2v88platform15DefaultJobStateESaIS2_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info]+0x16): undefined reference to `std::_Sp_make_shared_tag::_S_eq(std::type_info const&)'
/usr/bin/ld: vendor/rogchap.com/v8go/deps/linux-x86_64/libv8.a(default-platform.o): in function `std::_Sp_counted_ptr_inplace<v8::platform::DefaultWorkerThreadsTaskRunner, std::allocator<v8::platform::DefaultWorkerThreadsTaskRunner>, (__gnu_cxx::_Lock_policy)2>::_M_get_deleter(std::type_info const&)':
default-platform.cc:(.text._ZNSt23_Sp_counted_ptr_inplaceIN2v88platform30DefaultWorkerThreadsTaskRunnerESaIS2_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info[_ZNSt23_Sp_counted_ptr_inplaceIN2v88platform30DefaultWorkerThreadsTaskRunnerESaIS2_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info]+0x16): undefined reference to `std::_Sp_make_shared_tag::_S_eq(std::type_info const&)'
/usr/bin/ld: vendor/rogchap.com/v8go/deps/linux-x86_64/libv8.a(default-platform.o): in function `std::_Sp_counted_ptr_inplace<v8::platform::DefaultForegroundTaskRunner, std::allocator<v8::platform::DefaultForegroundTaskRunner>, (__gnu_cxx::_Lock_policy)2>::_M_get_deleter(std::type_info const&)':
default-platform.cc:(.text._ZNSt23_Sp_counted_ptr_inplaceIN2v88platform27DefaultForegroundTaskRunnerESaIS2_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info[_ZNSt23_Sp_counted_ptr_inplaceIN2v88platform27DefaultForegroundTaskRunnerESaIS2_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info]+0x16): undefined reference to `std::_Sp_make_shared_tag::_S_eq(std::type_info const&)'
collect2: error: ld returned 1 exit status
The command '/bin/sh -c go run main.go' returned a non-zero code: 2

Replacing the deps folder with the one from v0.2.0 works.

Also, after a go mod vendor, I need to copy the deps folder manually. This can be solved by including a .go file with a dummy variable in deps and each of its subdirectories, like this https://github.com/confluentinc/confluent-kafka-go/blob/master/kafka/librdkafka/librdkafka.go

Cross-compile for Linux?

Hey, thanks a lot for creating this package!

I've found it the example script works beautifully on mac but when I try to cross-compile for Linux, I get the following:

$ GOOS=linux go run main.go
./main.go:10:14: undefined: v8go.NewContext

This looks a bit like I'm just missing the package or something, but it does work when I remove the GOOS=linux.

$ go run main.go
addition result: 7

Here's my details:

OSX 10.5 (Catalina)
go version go1.13.4 darwin/amd64
(using go.mod)

Support for ES Modules

It looks like v8go does not support ES Modules. For example, I translated the Context example to use modules:

package main

import (
	"fmt"

	"rogchap.com/v8go"
)

func main() {
	ctx, _ := v8go.NewContext()
	val, err := ctx.RunScript("export function add(a, b) { return a + b };", "math.mjs")
	fmt.Println(val, err)
	val, err = ctx.RunScript("import { add } from 'math.mjs';\nconst result = add(3, 4)", "main.js")
	fmt.Println(val, err)
	val, err = ctx.RunScript("result", "value.js")
	fmt.Println(val, err)
	fmt.Println(val)
}

and it resulted in these errors:

<nil> SyntaxError: Unexpected token 'export'
<nil> SyntaxError: Cannot use import statement outside a module
...

I do not typically develop JS or use Node.js, so I might be doing something dumb too.

Do you have any particular ideas around how this might work? I'd be happy to submit a pull request if so.

Thanks!
Rob

memory management

Hi,
I've been playing v8go recently, it is definitely the best JS engine I've used .
but when I run a benchmark, I found the memory consumption increase as time goes on. Here's my code :

const script = `
	const process = (record) => {
		const res = [];
		for (let [k, v] of Object.entries(record)) {
			res.push({
				name: k,
				value: v,
			});
		}
		return JSON.stringify(res);
	};
`

func makeObject() interface{} {
	return map[string]interface{}{
		"a": rand.Intn(1000000),
		"b": "AAAABBBBAAAABBBBAAAABBBBAAAABBBBAAAABBBB",
	}
}

func BenchmarkV8go(b *testing.B) {
	defer func() {
		if e := recover(); e != nil {
			b.Logf("err:%v", e)
		}
	}()
	b.ReportAllocs()
	ctx, _ := v8go.NewContext()
	lock := sync.Mutex{}
	for i := 0; i < b.N; i++ {
		lock.Lock()
		ctx.RunScript(script, "")
		str, _ := json.Marshal(makeObject())
		cmd := fmt.Sprintf("process(%s)", str)
		ctx.RunScript(cmd, "")
		lock.Unlock()
	}
}

the code mostly from #25, but what confuse me most is the JS engine only run one JS function repeatedly, why its memory consumption grows so fast and finally out of memory. is there a possible memory leak or just because I didn't use it correctly

build error on windowsx64

rogchap.com/v8go

In file included from v8go.cc:3:
G:/����֪ʶѧϰ/golang/pkg/mod/rogchap.com/[email protected]/deps/include/v8.h:7691:5: error: typedef 'v8::UnhandledExceptionCallback' is initialized (use decltype instead)
_EXCEPTION_POINTERS* exception_pointers);
^~~~~~~~~~~~~~~~~~~
G:/����֪ʶѧϰ/golang/pkg/mod/rogchap.com/[email protected]/deps/include/v8.h:7691:5: error: '_EXCEPTION_POINTERS' was not declared in this scope
G:/����֪ʶѧϰ/golang/pkg/mod/rogchap.com/[email protected]/deps/include/v8.h:7691:26: error: 'exception_pointers' was not declared in this scope
_EXCEPTION_POINTERS* exception_pointers);
^~~~~~~~~~~~~~~~~~
G:/����֪ʶѧϰ/golang/pkg/mod/rogchap.com/[email protected]/deps/include/v8.h:9462:7: error: 'UnhandledExceptionCallback' has not been declared
UnhandledExceptionCallback unhandled_exception_callback);

[Question] untrusted code and protection against side-channel attacks?

(Premise: these topics are way out of my comfort zone so please forgive me in advance if what I'm saying is complete crap)

As I've been doing some research on V8 Isolates, it seems that one potential risk with them is about side-channel attacks, such as Spectre.

Cloudlfare, which uses V8 Isolates for their Workers, seems to acknowledge this by listing the mitigation they put in place, including limiting the capabilities of the Date method, disallowing multi-threading, etc. Source: https://developers.cloudflare.com/workers/learning/security-model

Given this, how much do you think these concerns apply to v8go?

Or, to put it in other terms: would you trust running untrusted code in this sandbox?

[feature] How to get the Promise result value?

ctx, _ := v8go.NewContext(nil)

val, err := ctx.RunScript(`new Promise((resolve) => setTimeout(() => resolve(1), 1000)).then(console.log)`, "value.js")
if err != nil {
  panic(err)
}

fmt.Printf("result: %s", val.String())
result: [object Promise]

I want to get the Promise result, for the example code is 1

Data I/O

Can you help me understand if the following interpretations are correct?

  1. The only way to get input data to the script is by printing the data into the script itself.

  2. The only way to get output data is from the stringified value of the result of the script.

Are there plans to add additional ways to share values back and forth between Go and V8?

Copy the header files for v8 as part of CI

With new releases are made the header files need to be updated too.
We should split out the include directory for each platform to not conflict on builds and make the CI pipeline easier.

Sandboxed execution of V8 - restrict FS and Network Access?

We are wondering if it was possible to use v8go for server side rendering Web-Apps in Environments, where Node is not allowed for security reasons around the npm ecosystem.

Would it be possible to:

  1. Restrict Filesystem-Access to only specific directories
  2. Restrict Networkaccess initiated by v8go to whitelisted hosts (e.G. npm registry, github)

The easiest way I could think of would be to:

  1. Use default linux user permission system, create a user for v8go, grant execution / write rights only for our sandbox folder, start v8go as that user
  2. Use default linux firewall rules for the v8go process

Would that be a feasible / secure approach?

I know my question is rather an idea than a specific question, but any hints or resources that point us in the right directions would be much appreciated.

Thanks for your great work with this library!

Support for `context.Context` within all APIs

Currently, the Context type in v8go hold the v8::Context ptr of v8, but Golang already has a type context.Context

Because of this, we must write more code to impl timeout.

https://github.com/rogchap/v8go#terminate-long-running-scripts

So, we can just hide the v8:Context.

To create a new v8go object, use

v8, _ := v8go.New()

or

iso, _ := v8go.NewIsolate()
v8, _ := v8go.New(iso)

To run a script with timeout

ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()

v8.RunScriptContext(ctx, "const add = (a, b) => a + b", "math.js")

Like other Golang library do.

Few potential bugs in C++ code

Note that these are just a code review thing. I haven't verified it, but it looks like bugs.

  1. As far as I know when you convert a C string to Go string via cgo's C.GoString, the latter doesn't free C string memory. As a result RunScript usage here leaks memory, it might allocate some strings for error structure which are then not freed in getError when being converted to Go strings:

    v8go/context.go

    Lines 58 to 59 in 00d2f88

    rtn := C.RunScript(c.ptr, cSource, cOrigin)
    return getValue(rtn), getError(rtn)

  2. Another memory leak here:

    v8go/value.go

    Line 16 in 00d2f88

    return C.GoString(C.ValueToString(v.ptr))

    ValueToString just does a malloc at the end.

  3. What you do in CopyString and similar places is potentially unsafe. The sprintf usage is unsafe:

    v8go/v8go.cc

    Lines 34 to 38 in 00d2f88

    const char* CopyString(std::string str) {
    char* data = static_cast<char*>(malloc(str.length()));
    sprintf(data, "%s", str.c_str());
    return data;
    }

    IIRC std::string's length returns the length of the string without terminating null byte. And IIRC sprintf writes terminating null byte at the end to the buffer. Since you allocate memory that is 1 byte short of what is written, it's a potential out of bounds write.

Invoke a v8go.Value ?

Hi,
If I have a v8go.Value where IsFunction() returns true, is there any way to invoke that value?
Thank you!
Rob

How to pass in a DOM?

I would like to use this to run tests on a webapp. Is this possible?
To do so I'd need to pass in a DOM (i.e. a HTML page) where all the <script> tags are executed, to then check for elements in the DOM.

TypeError: Cannot mix BigInt and other types, use explicit conversions

I got this when I do something like this:

  1. Bind the function from native side:
        start := time.Now()
	hrTimeFunc, err := v8go.NewFunctionTemplate(iso, func(info *v8go.FunctionCallbackInfo) *v8go.Value {
		elapsed := time.Since(start).Nanoseconds()
		ret, err := v8go.NewValue(iso, elapsed)
		if err != nil {
			log.Errorf("failed to create v8go NewValue with %d: %v", elapsed, err)
			return nil
		}
		return ret
	})

	if err != nil {
		return err
	}

	err = globalObject.Set("hrtime", hrTimeFunc, v8go.ReadOnly)

2 Then run script
_, err := ctx.RunScript("var result = hrtime() + 1e6;")

  1. Then I got error: "TypeError: Cannot mix BigInt and other types, use explicit conversions"

I checked the code, the reason is because the value returned in hrtime() is actually a BigInt wrapping an int64. V8 does not allow direct calculation between BigInt and other number types. The direct cause of this error is at here: https://github.com/rogchap/v8go/blob/master/value.go#L67

This can be easily fixed, I just wonder how should I fix it. Why it is not using some code like this at first place? Any concern here?

ValuePtr NewValueLong(IsolatePtr iso_ptr, int64_t v) {
  ISOLATE_SCOPE_INTERNAL_CONTEXT(iso_ptr);
  m_value* val = new m_value;
  val->iso = iso;
  val->ctx = ctx;
  val->ptr = Persistent<Value, CopyablePersistentTraits<Value>>(
      iso, Integer::New(iso, v));
  return tracked_value(ctx, val);
}

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.