本作品采用知识共享署名 4.0 国际许可协议进行许可。转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource4 
本博客同步在https://cnodejs.org/topic/56ed249356d74f3d3624b3ff 
本博客同步在http://www.cnblogs.com/papertree/p/5285705.html


  上面讲到node调用Script::Compile()和Script::Run()解析执行app.js,并把io操作和callback保存到default_loop_struct,那么app.js里面js代码如何调用C++的函数呢?

  在4.2节进行解释,先在4.1节来点知识预热。

4.1 V8运行js代码的基础知识 —— V8的上下文

  来看看google V8开发者文档的一点介绍:(地址:https://developers.google.com/v8/get_started

  • context is an execution environment that allows separate, unrelated, JavaScript code to run in a single instance of V8. You must explicitly specify the context in which you want any JavaScript code to be run.

  大概意思就是context(上下文)是用来执行javascript代码的运行环境,而且运行javascript代码的时候必须指定一个context。

  从文档里面摘了一段hello world代码:

int main(int argc, char* argv[]) {
// Initialize V8.
V8::InitializeICU();
V8::InitializeExternalStartupData(argv[0]);
Platform* platform = platform::CreateDefaultPlatform();
V8::InitializePlatform(platform);
V8::Initialize(); // Create a new Isolate and make it the current one.
ArrayBufferAllocator allocator;
Isolate::CreateParams create_params;
create_params.array_buffer_allocator = &allocator;
Isolate* isolate = Isolate::New(create_params);
{
Isolate::Scope isolate_scope(isolate); // Create a stack-allocated handle scope.
HandleScope handle_scope(isolate); // Create a new context.
Local<Context> context = Context::New(isolate); // Enter the context for compiling and running the hello world script.
Context::Scope context_scope(context); // Create a string containing the JavaScript source code.
Local<String> source =
String::NewFromUtf8(isolate, "'Hello' + ', World!'",
NewStringType::kNormal).ToLocalChecked(); // Compile the source code.
Local<Script> script = Script::Compile(context, source).ToLocalChecked(); // Run the script to get the result.
Local<Value> result = script->Run(context).ToLocalChecked(); // Convert the result to an UTF8 string and print it.
String::Utf8Value utf8(result);
printf("%s\n", *utf8);
} // Dispose the isolate and tear down V8.
isolate->Dispose();
V8::Dispose();
V8::ShutdownPlatform();
delete platform;
return 0;
}

  你可能会发现,上面说了script->Run(context) 一定要指定一个context。那么看回3.1.2 中的图3-1-3,node.cc里面的script->Run()并没有context参数。

  跳到v8的源码,deps/v8/src/api.cc,就会发现这实际上是两个重载函数,无参Script::Run()会先从Script对象取得当前的context,再调用Script::Run(Local<Context> context)。

  

图4-1-1


4.2 理解js代码如何调用C++函数 —— 运行时的上下文

  看个例子:

  左边为node 原生lib模块网络socket操作部分的文件 —— net.js,我们平时使用server.listen()时,最终调用到net.js里面,先通过new TCP()创建一个handle对象,再调用handle.listen()。而这个TCP和listen,均来自左边tcp_wrap.cc文件。

  也就是说,通过net.js里面的handle.listen()调用了tcp_wrap.cc里面的TCPWrap::Listen()函数,并且传给handle.listen()的 js参数—— backlog,被包装到了C++的 FunctionCallbackInfo<Value>类对象args。

图4-2-1

  如果你第一感觉是js代码调用C++代码无法理解,那么一定是受到“语法”的干扰。

  确实,从静态的角度来看,js和C++是两种语言,语法不互通,直接在js代码调用C++函数那是不可能的。

  那么,从动态的角度(运行时)来看呢?别忘了,任何编程语言最终运行起来都不过是进程空间里的二进制代码和数据。

  

图 4-2-2

4.2.1 从js代码到context

  4.1 中已经讲了,Script::Compile()和Script::Run() 的时候必须为 js代码指定一个运行环境(context)。那么 js代码和context的关联是很自然的。

4.2.2 设置C++函数到context

  那么,上图蓝色标号1-5这几个步骤,即在C++代码层面,把C++函数设置到context的细节和相应的V8 接口是什么呢?


4.3 node的js模块调用C++模块的细节

  在node里面,在C++代码里面提供给运行时javascript代码使用的无非就是这几种:

  1. 一个对象(比如process),对象上设置属性(比如process.versions)、或者方法(比如process._kill)

  2. 函数对象(比如TCP),设置原型方法(比如TCP.prototype.listen)

4.3.1  process对象 —— V8的Object类

  在3.2中讲到,main函数启动后会加载执行src/node.js文件,并且把process对象传给node.js文件,在里面设置process.nextTick()等方法。

  那么来看看 C++如何创建一个给js使用的对象。

4.3.1.1 类型

  回去3.1.2节看一下“图3-1-3”。在LoadEnvironment() 里面执行 f->Call()调用node.js里的匿名函数时,传过去的process对象是通过env->process_object()获取的。

  env->process_object()的实现如下: 

 图 4-3-1

  这里是个宏,展开就是

inline v8::Local<v8::Object> Environment::process_object() const {
return StrongPersistentToLocal(process_object_);
}

  那么上面标红的process_object_ 成员,定义如下:

图 4-3-2

  这里也是一个宏,展开就是

class Environment {
v8::Persistent<v8::Object> process_object_;
}

  那么这里可以看到,C++里面提供给js代码的对象,就是一个v8::Object类型的对象。

4.3.1.2 设置属性或方法

  那么,v8::Object类型的对象如何在C++里面设置属性呢?

图4-3-3

  这里可以看到,v8::Object类提供了Set()方法,来让你设置供js访问的属性或方法。

4.3.2 TCP类 —— v8的FunctionTemplate类

  那么第二种类型,就是设置prototype方法。在js里面,没有真正的类的概念,而是通过给函数对象TCP的prototype属性设置方法,使用的时候通过new TCP()去创建实例。

  那么,v8如何设置原型方法?

4.3.2.1 设置原型方法

图4-3-4

  这里可以看到,通过创建一个v8::FunctionTemplate类型的对象 t,通过 t->PrototypeTemplate() 去获取函数对象的prototype,并进一步调用Set()去设置prototype上的方法。

  最后再通过 t->GetFunction() 去获取一个该函数模版的方法。

  

注:关于 js文件process.binding('tcp_wrap')引入TCP函数对象的机制,在下一篇博客讲。

node源码详解(四)的更多相关文章

  1. node源码详解(五) —— 在main函数之前 —— js和C++的边界,process.binding

    本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource5 本博客同步在https://cnodejs.o ...

  2. node源码详解(二 )—— 运行机制 、整体流程

    本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource2 本博客同步在https://cnodejs.o ...

  3. node源码详解(五)

    本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource5 本博客同步在https://cnodejs.o ...

  4. node源码详解(四) —— js代码如何调用C++的函数

    本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource4 本博客同步在https://cnodejs.o ...

  5. node源码详解(三)—— js代码在node中的位置,process、require、module、exports的由来

    本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource3 本博客同步在https://cnodejs.o ...

  6. node源码详解(三)

    本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource3 本博客同步在https://cnodejs.o ...

  7. node源码详解 (一)

    本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource1 本博客同步在https://cnodejs.o ...

  8. node源码详解(七) —— 文件异步io、线程池【互斥锁、条件变量、管道、事件对象】

    本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource7 本博客同步在https://cnodejs.o ...

  9. node源码详解(六) —— 从server.listen 到事件循环

    本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource6 本博客同步在https://cnodejs.o ...

随机推荐

  1. NSRange and NSString对象

    http://www.cnblogs.com/pengyingh/articles/2451292.html 当谈到NSString方法时,你会发现很多都涉及到NSRange.不必操心.它事实上仅仅只 ...

  2. MySQL具体解释(15)-----------海量数据解说

    第1章  引言 随着互联网应用的广泛普及,海量数据的存储和訪问成为了系统设计的瓶颈问题. 对于一个大型的互联网应用.每天几十亿的PV无疑对数据库造成了相当高的负载.对于系统的稳定性和扩展性造成了极大的 ...

  3. Java:深入自定义注解(Annotation)

    在网上找了很多资料也有写的比较好的,但是总有有一点半点的细节没有写出来,在这里自己总结下使用. 使用Java的自定义注解,首先个人需要了解下Java为我们提供的元注解和相关定义注解的语法.(这个我在网 ...

  4. PCB MS SQL表值函数与CLR 表值函数 (例:字符串分割转表)

    将字符串分割为表表经常用到,这里 SQL表值函数与CLR  表值函数,两种实现方法例出来如下: SELECT * FROM FP_EMSDB_PUB.dbo.SqlSplit('/','1oz/1.5 ...

  5. 洛谷 P2129 L国的战斗续之多路出击(模拟)

    P2129 L国的战斗续之多路出击 题目背景 广而告之:背景见其他L国的战斗!!大家一起刷 题目描述 这一次,L国决定军队分成n组,分布在各地,若以L国为原点,可以看作在一个直角坐标系内.但是他们都受 ...

  6. Gym - 101981J The 2018 ICPC Asia Nanjing Regional Contest J.Prime Game 计数

    题面 题意:1e6的数组(1<a[i]<1e6),     mul (l,r) =l × (l+1) ×...× r,  fac(l,r) 代表 mul(l,r) 中不同素因子的个数,求s ...

  7. HBase编程 API入门系列之get(客户端而言)(2)

    心得,写在前面的话,也许,中间会要多次执行,连接超时,多试试就好了. 前面是基础,如下 HBase编程 API入门系列之put(客户端而言)(1) package zhouls.bigdata.Hba ...

  8. 前端-git思维导图笔记

    命令汇总 git config配置本地仓库 常用git config --global user.name.git config --global user.email git config --li ...

  9. Ionic3 环境搭建以及基础配置实现(更新中)

    GitHub:https://github.com/Teloi 环境配置输入以下命令安装 Ionic (如果刚才设置了淘宝镜像源,可以使用 cnpm 代替 npm):npm install -g io ...

  10. dell inspiron 15 3000 装XP win7 等GHOST系统方法

    dell inspiron 装XP win7 等GHOST系统方法 . 开机按F2,进入BIOS .在 BIOS 的Boot菜单下,将Secure Boot 改为 Disabled . 将Boot L ...