V8源码边缘试探-黑魔法指针偏移分分快三计划

作者:分分快三计划

  根据情况有两种检测,一种快速的,一种完全体的。默认都是走完全检测分支,里面会同时调用快速检测。

分分快三计划 1

  通过计算得到差值为48(16进制的30),算一算,刚好是6*8。

数据流向 C -> JavaScript

下面 demo 定义了一些常用的 JavaScript 类型,包括基本类型的以及 Object, Array, Fuction。

#include <node.h> #include <v8.h> using namespace v8; void MyFunction(const v8::FunctionCallbackInfo<Value>& args) { Isolate* isolate = args.GetIsolate(); args.GetReturnValue().Set(String::NewFromUtf8(isolate, "Hello World!")); } void Test(const v8::FunctionCallbackInfo<v8::Value>& args) { Isolate* isolate = args.GetIsolate(); // Number 类型的声明 Local<Number> retval = v8::Number::New(isolate, 1000); // String 类型的声明 Local<String> str = v8::String::NewFromUtf8(isolate, "Hello World!"); // Object 类型的声明 Local<Object> obj = v8::Object::New(isolate); // 对象的赋值 obj->Set(v8::String::NewFromUtf8(isolate, "arg1"), str); obj->Set(v8::String::NewFromUtf8(isolate, "arg2"), retval); // Function 类型的声明并赋值 Local<FunctionTemplate> tpl = v8::FunctionTemplate::New(isolate, MyFunction); Local<Function> fn = tpl->GetFunction(); // 函数名字 fn->SetName(String::NewFromUtf8(isolate, "theFunction")); obj->Set(v8::String::NewFromUtf8(isolate, "arg3"), fn); // Boolean 类型的声明 Local<Boolean> flag = Boolean::New(isolate, true); obj->Set(String::NewFromUtf8(isolate, "arg4"), flag); // Array 类型的声明 Local<Array> arr = Array::New(isolate); // Array 赋值 arr->Set(0, Number::New(isolate, 1)); arr->Set(1, Number::New(isolate, 10)); arr->Set(2, Number::New(isolate, 100)); arr->Set(3, Number::New(isolate, 1000)); obj->Set(String::NewFromUtf8(isolate, "arg5"), arr); // Undefined 类型的声明 Local<Value> und = Undefined(isolate); obj->Set(String::NewFromUtf8(isolate, "arg6"), und); // null 类型的声明 Local<Value> null = Null(isolate); obj->Set(String::NewFromUtf8(isolate, "arg7"), null); // 返回给 JavaScript 调用时的返回值 args.GetReturnValue().Set(obj); } void init(Local <Object> exports, Local<Object> module) { NODE_SET_METHOD(exports, "getTestValue", Test); } NODE_MODULE(returnValue, init)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <node.h>
#include <v8.h>
 
using namespace v8;
 
void MyFunction(const v8::FunctionCallbackInfo<Value>& args) {
    Isolate* isolate = args.GetIsolate();
    args.GetReturnValue().Set(String::NewFromUtf8(isolate, "Hello World!"));
}
 
void Test(const v8::FunctionCallbackInfo<v8::Value>& args) {
    Isolate* isolate = args.GetIsolate();
 
    // Number 类型的声明
    Local<Number> retval = v8::Number::New(isolate, 1000);
 
    // String 类型的声明
    Local<String> str = v8::String::NewFromUtf8(isolate, "Hello World!");
 
    // Object 类型的声明
    Local<Object> obj = v8::Object::New(isolate);
    // 对象的赋值
    obj->Set(v8::String::NewFromUtf8(isolate, "arg1"), str);
    obj->Set(v8::String::NewFromUtf8(isolate, "arg2"), retval);
 
    // Function 类型的声明并赋值
    Local<FunctionTemplate> tpl = v8::FunctionTemplate::New(isolate, MyFunction);
    Local<Function> fn = tpl->GetFunction();
    // 函数名字
    fn->SetName(String::NewFromUtf8(isolate, "theFunction"));
    obj->Set(v8::String::NewFromUtf8(isolate, "arg3"), fn);
 
    // Boolean 类型的声明
    Local<Boolean> flag = Boolean::New(isolate, true);
    obj->Set(String::NewFromUtf8(isolate, "arg4"), flag);
 
    // Array 类型的声明
    Local<Array> arr = Array::New(isolate);
    // Array 赋值
    arr->Set(0, Number::New(isolate, 1));
    arr->Set(1, Number::New(isolate, 10));
    arr->Set(2, Number::New(isolate, 100));
    arr->Set(3, Number::New(isolate, 1000));
    obj->Set(String::NewFromUtf8(isolate, "arg5"), arr);
 
    // Undefined 类型的声明
    Local<Value> und = Undefined(isolate);
    obj->Set(String::NewFromUtf8(isolate, "arg6"), und);
 
    // null 类型的声明
    Local<Value> null = Null(isolate);
    obj->Set(String::NewFromUtf8(isolate, "arg7"), null);
 
    // 返回给 JavaScript 调用时的返回值
    args.GetReturnValue().Set(obj);
}
 
void init(Local <Object> exports, Local<Object> module) {
    NODE_SET_METHOD(exports, "getTestValue", Test);
}
 
NODE_MODULE(returnValue, init)

所有的 addon 都需要一个初始化的函数,如下面的代码:

void Initialize(Local<Object> exports); NODE_MODULE(module_name, Initialize)

1
2
void Initialize(Local<Object> exports);
NODE_MODULE(module_name, Initialize)

Initialize 是初始化的函数,module_name 是编译后产生的二进制文件名,上述代码的模块名为 returnValue

上述代码通过 node-gyp 编译后(编译过程官方文档 C/C Addons 有详细的介绍),可以通过如下的方式调用。

// returnValue.node 这个文件就是编译后产生的文件,通过 NODE_MODULE(returnValue, init) 决定的文件名 const returnValue = require('./build/Release/returnValue.node'); console.log(returnValue.getTestValue());

1
2
3
// returnValue.node 这个文件就是编译后产生的文件,通过 NODE_MODULE(returnValue, init) 决定的文件名
const returnValue = require('./build/Release/returnValue.node');
console.log(returnValue.getTestValue());

运行结果如下:

分分快三计划 2

  只有GetRoot是真正生成null值的地方,注意第二个参数 I::kNullValueRootIndex ,这是一个静态整形值,除去null还有其他几个,所有的类似值定义如下:

参考资料

  • Type conversions from JavaScript to C in V8
  • node addon
  • v8 types documentation
  • node-gyp
  • gyp user documentation
  • nan

    1 赞 1 收藏 1 评论

  那个内部方法,就是完全体的核心,看似简单,实则跟厕所里的石头一样,又臭又硬。因为从这里开始,就要进入宏的地狱了。

#include <node.h> #include <v8.h> using namespace v8; void Test(const v8::FunctionCallbackInfo<v8::Value>& args) { Isolate* isolate = args.GetIsolate(); // 声明变量 Local<Number> retval = v8::Number::New(isolate, 1000); } void init(Local <Object> exports, Local<Object> module) { NODE_SET_METHOD(exports, "getTestValue", Test); } NODE_MODULE(returnValue, init)

  分分快三计划,实际上根本不存在一个正规的null类来生成一个对应的对象,而只是把一个特定的地址当成一个null值。

下面这段代定义了一个 Number 类型的值,其中 Test 函数中声明的 isolate 变量代表着 V8 虚拟机中的堆内存,当创建新变量的时候就需要用到它,接下来的一行代码就通过 isolate 声明了一个 Number 类型的变量。

3、arraysize(loaders_bootstrapper_args) => 参数长度,就是4

  • JavaScript 的基本类型是不可变类型,变量都是指向一个不可变的内存单元,var a = 10,则 a 指向的内存单元中包含的值为 5,重新赋值 a = 100,没有改变这个内存单元的值,而是使得 a 指向了另外一个内存单元,其中的值为 100。如果声明两个变量 x,y 的值都为 10,则他们指向的是同一个内存单元。
  • 函数的传参都是传值,而不是传引用,当在 JavaScript 中调用 C 的函数时,如果参数是基本类型则每次都是把这个值拷贝过去,改变参数的值不会影响原来的值。
  • 使用 Local 声明基本类型的变量都是对内存单元的引用,因为第一条原因不可能改变引用的值使其指向另外一个内存单元,因此不存在变量的重新赋值。

分分快三计划 3

看了 V8 类型 API 文档 你会发现对于基本的 JavaScript 类型,只有变量的声明而没有变量的赋值。最初想可能觉得这个非常的奇怪,可是仔细想一想后发现这个是合理的。主要由以下几点原因:

分分快三计划 4

虽然在 Node.js 官方网站有很多的关于怎么使用这些 API 的文档,但是在 JavaScript 和 C 之间传递数据是一件非常麻烦的事情,C 是强类型语言(”1024” 是字符串类型而不是整数类型),而 JavaScript 却总是默认的帮我们做一些类型转换。

  这里的参数分别为:

数据流向 javaScript -> C

上面的 demo 展示了怎样在在 C 定义 JavaScript 类型,数据的是从 C 流向 JavaScript,反过来数据也需要从 javaScript 流向 C ,也就是调用 C 函数的时候需要传入一些参数。

下面的代码展示了参数个数判断,参数类型判断,以及参数类型装换成 V8 类型的过程,包括基本类型以及 Object, Array, Fuction。

#include <node.h> #include <v8.h> #include <iostream> using namespace v8; using namespace std; void GetArgument(const FunctionCallbackInfo<Value>& args) { Isolate* isolate = args.GetIsolate(); // 参数长度判断 if (args.Length() < 2) { isolate->ThrowException(Exception::TypeError( String::NewFromUtf8(isolate, "Wrong number of arguments"))); return; } // 参数类型判断 if (!args[0]->IsNumber() || !args[1]->IsNumber()) { //抛出错误 isolate->ThrowException(Exception::TypeError( String::NewFromUtf8(isolate, "argumnets must be number"))); } if (!args[0]->IsObject()) { printf("I am not Objectn"); } if (!args[0]->IsBoolean()) { printf("I am not Booleann"); } if (!args[0]->IsArray()) { printf("I am not Arrayn"); } if (!args[0]->IsString()) { printf("I am not Stringn"); } if (!args[0]->IsFunction()) { printf("I am not Functionn"); } if (!args[0]->IsNull()) { printf("I am not Nulln"); } if (!args[0]->IsUndefined()) { printf("I am not Undefinedn"); } // js Number 类型转换成 v8 Number 类型 Local<Number> value1 = Local<Number>::Cast(args[0]); Local<Number> value2 = Local<Number>::Cast(args[1]); double value = value1->NumberValue() value2->NumberValue(); // js String 类型转换成 v8 String 类型 Local<String> str = Local<String>::Cast(args[2]); String::Utf8Value utfValue(str); cout<<string(*utfValue)<<endl; // js Array 类型转换成 v8 Array 类型 Local<Array> input_array = Local<Array>::Cast(args[3]); printf("%d, %f %fn", input_array->Length(), input_array->Get(0)->NumberValue(), input_array->Get(1)->NumberValue()); // js Object 类型转换成 v8 Object 类型 Local<Object> obj = Local<Object>::Cast(args[4]); // 根据 key 获取对象中的值 Local<Value> a = obj->Get(String::NewFromUtf8(isolate, "a")); Local<Value> b = obj->Get(String::NewFromUtf8(isolate, "b")); // js Array 类型转换成 v8 Array 类型 Local<Array> c = Local<Array>::Cast(obj->Get(String::NewFromUtf8(isolate, "c"))); cout<<a->NumberValue()<<" "<<b->NumberValue()<<endl; printf("%d, %f %fn", c->Length(), c->Get(0)->NumberValue(), c->Get(1)->NumberValue()); // js String 类型转换成 v8 String 类型 Local<String> cString = Local<String>::Cast(c->Get(2)); String::Utf8Value utfValueD(cString); cout<<string(*utfValueD)<<endl; // 根据 key 获取对象中的值 Local<Object> d = Local<Object>::Cast(obj->Get(String::NewFromUtf8(isolate, "d"))); Local<String> dString1 = Local<String>::Cast(d->Get(String::NewFromUtf8(isolate, "m"))); String::Utf8Value utfValued1(dString1); cout<<string(*utfValued1)<<endl; // 根据 key 获取对象中的值 Local<String> dString2 = Local<String>::Cast(d->Get(String::NewFromUtf8(isolate, "n"))); String::Utf8Value utfValued2(dString2); cout<<string(*utfValued2)<<endl; // js Booelan 类型转换成 v8 Boolean 类型 Local<Boolean> FlagTrue = Local<Boolean>::Cast(args[5]); cout<<"Flag: "<<FlagTrue->BooleanValue()<<endl; // js Function 类型转换成 v8 Function 类型 Local<Function> cb = Local<Function>::Cast(args[8]); const unsigned argc = 2; Local<Value> argv[2]; argv[0] = a; argv[1] = b; cb->Call(Null(isolate), argc, argv); args.GetReturnValue().Set(value); } void Init(Local <Object> exports, Local <Object> module) { NODE_SET_METHOD(module, "exports", GetArgument); } NODE_MODULE(argumentss, Init)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#include <node.h>
#include <v8.h>
#include <iostream>
 
using namespace v8;
using namespace std;
 
void GetArgument(const FunctionCallbackInfo<Value>& args) {
    Isolate* isolate = args.GetIsolate();
 
    // 参数长度判断
    if (args.Length() < 2) {
        isolate->ThrowException(Exception::TypeError(
            String::NewFromUtf8(isolate, "Wrong number of arguments")));
        return;
    }
 
    // 参数类型判断
    if (!args[0]->IsNumber() || !args[1]->IsNumber()) {
        //抛出错误
        isolate->ThrowException(Exception::TypeError(
            String::NewFromUtf8(isolate, "argumnets must be number")));
    }
 
    if (!args[0]->IsObject()) {
        printf("I am not Objectn");
    }
 
    if (!args[0]->IsBoolean()) {
        printf("I am not Booleann");
    }
 
    if (!args[0]->IsArray()) {
        printf("I am not Arrayn");
    }
 
    if (!args[0]->IsString()) {
        printf("I am not Stringn");
    }
 
    if (!args[0]->IsFunction()) {
        printf("I am not Functionn");
    }
 
    if (!args[0]->IsNull()) {
        printf("I am not Nulln");
    }
 
    if (!args[0]->IsUndefined()) {
        printf("I am not Undefinedn");
    }
 
    // js Number 类型转换成 v8 Number 类型
    Local<Number> value1 = Local<Number>::Cast(args[0]);
    Local<Number> value2 = Local<Number>::Cast(args[1]);
    double value = value1->NumberValue() value2->NumberValue();
 
    // js String 类型转换成 v8 String 类型
    Local<String> str = Local<String>::Cast(args[2]);
    String::Utf8Value utfValue(str);
    cout<<string(*utfValue)<<endl;
 
    // js Array 类型转换成 v8 Array 类型
    Local<Array> input_array = Local<Array>::Cast(args[3]);
    printf("%d, %f %fn", input_array->Length(), input_array->Get(0)->NumberValue(), input_array->Get(1)->NumberValue());
 
    // js Object 类型转换成 v8 Object 类型
    Local<Object> obj = Local<Object>::Cast(args[4]);
 
    // 根据 key 获取对象中的值
    Local<Value> a = obj->Get(String::NewFromUtf8(isolate, "a"));
    Local<Value> b = obj->Get(String::NewFromUtf8(isolate, "b"));
 
    // js Array 类型转换成 v8 Array 类型
    Local<Array> c = Local<Array>::Cast(obj->Get(String::NewFromUtf8(isolate, "c")));
    cout<<a->NumberValue()<<"   "<<b->NumberValue()<<endl;
    printf("%d, %f %fn", c->Length(), c->Get(0)->NumberValue(), c->Get(1)->NumberValue());
 
    // js String 类型转换成 v8 String 类型
    Local<String> cString = Local<String>::Cast(c->Get(2));
    String::Utf8Value utfValueD(cString);
    cout<<string(*utfValueD)<<endl;
 
    // 根据 key 获取对象中的值
    Local<Object> d = Local<Object>::Cast(obj->Get(String::NewFromUtf8(isolate, "d")));
    Local<String> dString1 = Local<String>::Cast(d->Get(String::NewFromUtf8(isolate, "m")));
    String::Utf8Value utfValued1(dString1);
    cout<<string(*utfValued1)<<endl;
 
    // 根据 key 获取对象中的值
    Local<String> dString2 = Local<String>::Cast(d->Get(String::NewFromUtf8(isolate, "n")));
    String::Utf8Value utfValued2(dString2);
    cout<<string(*utfValued2)<<endl;
 
    // js Booelan 类型转换成 v8 Boolean 类型
    Local<Boolean> FlagTrue = Local<Boolean>::Cast(args[5]);
    cout<<"Flag: "<<FlagTrue->BooleanValue()<<endl;
 
    // js Function 类型转换成 v8 Function 类型
    Local<Function> cb = Local<Function>::Cast(args[8]);
    const unsigned argc = 2;
    Local<Value> argv[2];
    argv[0] = a;
    argv[1] = b;
    cb->Call(Null(isolate), argc, argv);
 
    args.GetReturnValue().Set(value);
}
 
void Init(Local <Object> exports, Local <Object> module) {
    NODE_SET_METHOD(module, "exports", GetArgument);
}
 
NODE_MODULE(argumentss, Init)

通过 node-gyp 编译后,可以通过如下的方式调用。

const getArguments = require('./build/Release/arguments'); console.log(getArguments(2, 3, 'Hello Arguments', [1, 2, 3], { a: 10, b: 100, c: [23, 22, "我是33"], d: { m: '我是22', n: '我是23' } }, true, null, undefined, function myFunction(...args) { console.log('I am Function!'); console.log(...args); console.log('I am Function!'); }));

1
2
3
4
5
6
7
8
9
10
11
12
13
const getArguments = require('./build/Release/arguments');
 
console.log(getArguments(2, 3, 'Hello Arguments', [1, 2, 3], {
        a: 10,
        b: 100,
        c: [23, 22, "我是33"],
        d: { m: '我是22', n: '我是23' }
    }, true, null, undefined,
    function myFunction(...args) {
        console.log('I am Function!');
        console.log(...args);
        console.log('I am Function!');
    }));

运行结果如下:

分分快三计划 5

关于其他的类型,我这里就就不一一介绍,V8 文档里面都有对应的 API。

  这博客是越来越难写了,参考资料少,难度又高,看到什么写什么吧!

我非常喜欢使用 Node.js,但是当涉及到计算密集型的场景时 Node.js 就不能够很好地胜任了。而在这样的情况下 C 是一个很好的选择,非常幸运 Node.js 官方提供了 C/C Addons 的机制让我们能够使用 V8 API 把 Node.js 和 C 结合起来。

分分快三计划 6

NAN

由于 V8 的 API 还没有彻底稳定下来,所以对于不同版本的 Node.js 类型相关的 API 会发生变化,而 NAN 帮我们做了封装,在编码的时候不需要关心版本问题,只需要引入相应的头文件即可。

引入头文件后,可以如下使用方式:

v8::Local<v8::Primitive> Nan::Undefined() v8::Local<v8::Primitive> Nan::Null()

1
2
v8::Local<v8::Primitive> Nan::Undefined()
v8::Local<v8::Primitive> Nan::Null()
V8_INLINE static internal::Object** GetRoot(v8::Isolate* isolate,int index) {
    // 获取当前isolate地址并进行必要的空间指针偏移
    // static const int kIsolateRootsOffset = kExternalMemoryLimitOffset   kApiInt64Size   kApiInt64Size   kApiPointerSize   kApiPointerSize;
    uint8_t* addr = reinterpret_cast<uint8_t*>(isolate)   kIsolateRootsOffset;
    // 根据上面的数字以及当前操作系统指针大小进行偏移
    // const int kApiPointerSize = sizeof(void*);  // NOLINT
    return reinterpret_cast<internal::Object**>(addr   index * kApiPointerSize);
}

JavaScript 的基本类型包括 String,Number,Boolean,null,undefined,V8 使用类继承的方式来定义这类型,这些类型都继承了 Primitive 类,而 Primitive 继承了 Value,v8 也支持整型(包括 Int32Uint32),而所有的类型定义都可以从 V8 类型文档中看到,除了基本的类型,还有 Object,Array,Map 等类型的定义。

分分快三计划 7

基本类型的继承关系如下图:
分分快三计划 8

  转换成人话如下:

在 V8 中所有 JavaScript 值都是被放在 Local 对象中,通过这个对象指定了 JavaScript 运行时的内存单元。

  通过简单计算,这个差值是72(16进制的48),跟第一次偏移量大小一致,这里根本不关心指针指向什么东西,所以字符无效也没事。

原文出处: 慎里,2016-09-20   

  验证的话就很简单了,随意的在node启动代码里加一段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <node.h>
#include <v8.h>
 
using namespace v8;
 
void Test(const v8::FunctionCallbackInfo<v8::Value>& args) {
    Isolate* isolate = args.GetIsolate();
    // 声明变量
    Local<Number> retval = v8::Number::New(isolate, 1000);
}
 
void init(Local <Object> exports, Local<Object> module) {
    NODE_SET_METHOD(exports, "getTestValue", Test);
}
 
NODE_MODULE(returnValue, init)
auto t = Null(env->isolate());
t->IsNull();

  然而实际上没有这么简单粗暴,V8对null、undefined(实际上还包括了true、false、空字符串)都做了特殊的处理。

  看起来很简单,这里的null_value又是一个坑,如下:

static const int kUndefinedValueRootIndex = 4;
static const int kTheHoleValueRootIndex = 5;
static const int kNullValueRootIndex = 6;
static const int kTrueValueRootIndex = 7;
static const int kFalseValueRootIndex = 8;
static const int kEmptyStringRootIndex = 9;
Oddball* Heap::null_value() { return Oddball::cast(roots_[kNullValueRootIndex]); }

  最后对这个地址进行强转,返回一个Local<Primitive>类型的null对象。

  这里简单的过一遍,首先是测试代码:

分分快三计划 9

分分快三计划 10

  这两步,还原了object->IsNull(Isolate* isolate)究竟是个什么东西,整理后如下:

  每一个生成的null虽然是个废物,但是爸爸很厉害,父类Value有一个方法IsNull专门检测当前类是否是null值。

  这个方法就对应了标题,指针偏移。

2、loaders_bootstrapper => 函数体

  看起来就像JS里面的call方法,其中函数参数包括context、null、形参数量、形参,当时看到Null觉得比较好奇,就仔细的看了一下实现。

 

  你以为这就完了???nonono,这个Oddball::cast(Object* object)又要搞事,如下:

 

  因为调试模式对于宏的跳转十分不友好,所以只能一个一个的把宏复制到本地,然后进行拼接,看看最后出来的是什么。这里仅仅给出一系列的截图,看看什么是宏的地狱:

  最后返回一个地址,这个地址就是null,强转成Local<Primitive>也只是为了垃圾回收与类型区分,实际上并不关心这个指针指向什么,因为null本身不存在任何方法可以调用,大多数情况下也只是用来做变量重置。

Oddball* Oddball::cast(Object* object) {              
    SLOW_DCHECK(object->IsOddball());             
    return reinterpret_cast<type*>(object);       
}   
bool Value::FullIsNull() const {
    // 通过这个可以获取到当前的isolate实例
    i::Handle<i::Object> object = Utils::OpenHandle(this);
    bool result = false;
    // 判断object是否为空值
    if (!object->IsSmi()) {
        // 内部方法
        result = object->IsNull(i::HeapObject::cast(*object)->GetIsolate());
    }
    // 调用快速检测与返回结果进行比对
    DCHECK_EQ(result, QuickIsNull());
    return result;
}

分分快三计划 11

auto test = Null(env->isolate());

------------------------------------------------------------------------------------------------分割线-------------------------------------------------------------------------------------------

分分快三计划 12

  回到故事的起点,是我在研究LoadEnvironment函数的时候发现的。上一篇博客其实就是在讲这个方法,包装完函数名、函数体,最后一步就是配合函数参数来执行函数了,代码如下:

Local<Primitive> Null(Isolate* isolate) {
    typedef internal::Object* S;
    typedef internal::Internals I;
    // 检测当前V8引擎实例是否存活
    I::CheckInitialized(isolate);
    // 核心方法
    S* slot = I::GetRoot(isolate, I::kNullValueRootIndex);
    // 类型强转 直接是Primitive类而不是继承
    return Local<Primitive>(reinterpret_cast<Primitive*>(slot));
}

  方法源码如下:

 

  第二次偏移后,得到的null地址为:

 

  这三个宏定义了isolate->heap()->null_value()是个什么东西,整理后如下:

  如果还不明白,我这个灵魂画手会让你明白,超级简单:

  抛去一切上面无关的因素,最终判断条件其实就是那一行代码:Heap::null_value() { Oddball::cast(roots_[kNullValueRootIndex]); }

分分快三计划 13

  参数是啥并不重要,进入方法,源码如下:

分分快三计划 14

  正推过去很简单,指定地址的值就是null,如果反推的话,那么想想也很简单,判断当前类的地址是否与指定地址相等。但是在源码里,这个过程可以说是相当的恶心了……

bool Value::IsNull() const {
#ifdef V8_ENABLE_CHECKS
  return FullIsNull();
#else
  return QuickIsNull();
#endif
}

分分快三计划 15

// Bootstrap internal loaders
Local<Value> bootstrapped_loaders;
if (!ExecuteBootstrapper(env, loaders_bootstrapper,
                        arraysize(loaders_bootstrapper_args),
                        loaders_bootstrapper_args,
                        &bootstrapped_loaders)) {
return;
}

  在没有看源码之前,我以为是这样的:

return reinterpret_cast<internal::Object**>(addr   index * kApiPointerSize);

  众多周知,在JavaScript中有几个基本类型,包括字符串、数字、布尔、null、undefined、Symbol,其中大部分都可以在我之前那篇博客(

  就这样,只用了很小的空间便生成了一个null值,并且每一次获取都会返回同一个值。

class Null : public Primitive {
public:
    // Type testing.
    bool IsNull() const { return true; }
    // ...
}

  这个length长达511,定义非常非常多的特殊值,初始化方式也是宏,这里仅仅调出null的定义:

  这个方法其实很简单,但是实现的方式非常有意思,源码如下:

  第一次指针偏移后,addr的地址为:

1、env => 当前V8引擎的环境变量,包含Isolate、context等。

5、&bootstrapped_loaders => 一个局部变量指针

  简单讲,roots_在V8引擎初始化时已经预存了所有特殊值的地址,这里直接取this的地址与root_中保存的null值地址进行比较,最后得出结果。

  这个方法非常简单:

  敢于用这个方法,是因为对于每一个V8引擎来说isolate对象是独一无二的,所以在当前引擎下,获取到的isolate地址也是唯一的。

分分快三计划 16

  因此如果是null值,其内置类型必然为Object。

  发现没,SLOW_DCHECK,大写 下划线分割,又是一个宏,真的是无穷无尽,不过这个宏只是检测表达式是否为真。

Object* roots_[kRootListLength];

  上面的数字就是区分这几个类型的关键所在,继续进入GetRoot方法:

4、loaders_bootstrapper_args => 参数数组,包括process对象及3个C 内部方法

  而这个roots_定义也是很魔性,来源于heap.h,简单的一行:

  然后看局部变量的调试框,当前isolate的地址如下:

static bool ExecuteBootstrapper(Environment* env, Local<Function> bootstrapper,
                                int argc, Local<Value> argv[],
                                Local<Value>* out) {
  bool ret = bootstrapper->Call(
      env->context(), Null(env->isolate()), argc, argv).ToLocal(out);
  if (!ret) {
    env->async_hooks()->clear_async_id_stack();
  }

  return ret;
}

  虽然解释的差不多了,但是还是有必要做一个补充,就是关于一个类是否为null值的判断。

  这个Oddball是继承于HeapObject,而HeapObject继承于Object,这里只是简单判断当前类是否来源于Object,在上面生成null值的最后转换有这么一行代码:

  因为宏调试很不直观,也很不方便,这里就不贴图了。

bool Object::IsNull(Isolate* isolate) const {     
    return this == isolate->heap()->null_value();          
}

本文由分分快三计划发布,转载请注明来源

关键词: 分分快三计划 C++ 类型转换 Node.js C/C++