Deno Principle in detail, let's start with source code analysis

Keywords: Front-end Javascript github C

Father of Node: In his speech "Design Errors in Node", he said:

  • Binding any local function to V8 is not allowed.
  • All system calls will be done through message passing (protobuf serialization).
  • Two primitive functions: send and recv.
  • This not only simplifies the design process, but also makes the system easier to audit.

These points largely reflect the essential difference between node and deno in design, and at the same time they reflect the security of deno (the fact that JavaScript itself is a security sandbox)

V8worker2 is the bridge between Go and V8

  • Allow JavaScript to be executed from GO programs
  • Only messaging between GO and V8 is allowed (traditionally: exposing C++ functions as functions in JavaScript). )
  • Maintain a secure JS sandbox
  • In JS, only three functions are allowed to be bound: send(), recv(), print().

As you can see clearly from the figure, V8worker2 is the core component to implement calls between v8 and Go.

It can be seen that V8worker2 binds V8 through binding C++ module, and bingding exposes the basic operation methods: v8_init(), worker_load(), worker_send_bytes(), worker_dispose()... to GO for invocation.

//binding.h
const char* worker_version();
void worker_set_flags(int* argc, char** argv);
void v8_init();
worker* worker_new(int table_index);
int worker_load(worker* w, char* name_s, char* source_s);
const char* worker_last_exception(worker* w);
int worker_send_bytes(worker* w, void* data, size_t len);
void worker_dispose(worker* w);
void worker_terminate_execution(worker* w);

The communication between GO and V8 can be realized by calling the exposed method of C language through the CGO module provided by Golang's GC.

  1. Create an instance: v8worker2. New (Receive Message Callback)
  2. Load to load and execute JS: worker.Load(scriptName,codeString)
// worker.go
package v8worker2

import "C"
...

func recvCb(buf unsafe.Pointer, buflen C.int, index workerTableIndex) C.buf {
    ...
}

func New(cb ReceiveMessageCallback) *Worker {
    ...
	initV8Once.Do(func() {
		C.v8_init()
	})
}

func (w *Worker) Load(scriptName string, code string) error {
    ...
	r := C.worker_load(w.worker.cWorker, scriptName_s, code_s)
...
}

func (w *Worker) SendBytes(msg []byte) error {
    ...
	r := C.worker_send_bytes(w.worker.cWorker, msg_p, C.size_t(len(msg)))
}

Case demonstration

  • Implementing console.log() method in Js
  • Js sends data to Go
  • Go sends data to Js
// hello.go
package main

import (
	"fmt"

	"github.com/ry/v8worker2"
)

func main() {
	worker := v8worker2.New(recv)

	// The console.log method to implement JS
	err := worker.Load("hello.js", `
		this["console"] = {
			log(...args) {
				V8Worker2.print(args)
			}
		};
		console.log("Hello World");
	`)

	if err != nil {
		fmt.Println(err)
	}

	// Send data to GO
	err = worker.Load("sendData.js", `
		V8Worker2.send(new ArrayBuffer(5))
	`)
	if err != nil {
		fmt.Println(err)
	}

	// Send data to JS
	err = worker.Load("recvData.js", `
		V8Worker2.recv(function(msg) {
			const len =msg.byteLength;
			console.log("recv data from go,length: "+len);
		});
	`)
	if err != nil {
		fmt.Println(err)
	}
	err = worker.SendBytes([]byte("abcd"))

}

func recv(buf []byte) []byte {
	fmt.Println("recv data from js,length:", len(buf))
	return nil
}

Run on the console: go run hello.go

You need to run the test code to access my github directly: deno case source code

Reference material

Posted by salman_ahad@yahoo.com on Wed, 25 Sep 2019 08:51:02 -0700