Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Node.js源码-global&process的创建、初始化 #10

Open
tsy77 opened this issue Jul 26, 2018 · 0 comments
Open

Node.js源码-global&process的创建、初始化 #10

tsy77 opened this issue Jul 26, 2018 · 0 comments

Comments

@tsy77
Copy link
Owner

tsy77 commented Jul 26, 2018

global和process是node中大家经常用到的两个对象,那么这两个对象又是如何被创建的,我们在我们的代码中为什么能够使用呢?本篇文章将为大家揭晓答案。

global

global对象在src/node.cc中的被创建,在bootstrap/node.js中被初始化。

创建

在src/node.cc的LoadEnvironment方法中,有以下几行代码是用来创建global对象的。

// Add a reference to the global object
  Local<Object> global = env->context()->Global();
  
  ......
  // Expose the global object as a property on itself
  // (Allows you to set stuff on `global` from anywhere in JavaScript.)
  global->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "global"), global);

其中env->context()->Global()获取了当前context中的Global全局对象,global->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "global"), global)将全局对象本身挂载在其global对象上,这样全局对象中有了一个global对象指向了全局对象本身,我们在该context中可以直接使用global对全局对象的引用进行访问。

以上程序之后,这样我们就可以在context的任何地方对全局对象进行操作。

初始化

初始化在bootstrap/node.js中进行,其在上述逻辑执行后被执行,所以可以直接操作全局对象。

初始化代码分为以下几个部分:

1.为global挂载process、Symbol.toStringTag、buffer等属性

//为global挂载Symbol.toStringTag、buffer等属性
setupGlobalVariables();

function setupGlobalVariables() {
    // global.toString()时访问
    Object.defineProperty(global, Symbol.toStringTag, {
      value: 'global',
      writable: false,
      enumerable: false,
      configurable: true
    });
    global.process = process;
    const util = NativeModule.require('util');

    function makeGetter(name) {
      return util.deprecate(function() {
        return this;
      }, `'${name}' is deprecated, use 'global'`, 'DEP0016');
    }

    function makeSetter(name) {
      return util.deprecate(function(value) {
        Object.defineProperty(this, name, {
          configurable: true,
          writable: true,
          enumerable: true,
          value: value
        });
      }, `'${name}' is deprecated, use 'global'`, 'DEP0016');
    }

    Object.defineProperties(global, {
      GLOBAL: {
        configurable: true,
        get: makeGetter('GLOBAL'),
        set: makeSetter('GLOBAL')
      },
      root: {
        configurable: true,
        get: makeGetter('root'),
        set: makeSetter('root')
      }
    });

    // This, as side effect, removes `setupBufferJS` from the buffer binding,
    // and exposes it on `internal/buffer`.
    NativeModule.require('internal/buffer');

    global.Buffer = NativeModule.require('buffer').Buffer;
    process.domain = null;
    process._exiting = false;
  }

这里将process挂载在global上,这也是为什么我们可以在我们的用户代码直接访问process对象的原因。同时注意Symbol.toStringTag属性会在访问global.toString()时访问。

2.初始化timeout、console、URL

const browserGlobals = !process._noBrowserGlobals;
if (browserGlobals) {
  setupGlobalTimeouts();
  setupGlobalConsole();
  setupGlobalURL();
}

function setupGlobalTimeouts() {
    const timers = NativeModule.require('timers');
    global.clearImmediate = timers.clearImmediate;
    global.clearInterval = timers.clearInterval;
    global.clearTimeout = timers.clearTimeout;
    global.setImmediate = timers.setImmediate;
    global.setInterval = timers.setInterval;
    global.setTimeout = timers.setTimeout;
  }

这里以setupGlobalTimeouts为例,主要是在global上挂载setImmediate等timeout相关的方法。

3.为global加上'assert', 'async_hooks', 'buffer'等属性

const {
  addBuiltinLibsToObject
} = NativeModule.require('internal/modules/cjs/helpers');
// 为global加上'assert', 'async_hooks', 'buffer'等属性
addBuiltinLibsToObject(global);

这里调用的是internal/modules/cjs/helpers下的addBuiltinLibsToObject方法,代码如下:

const builtinLibs = [
  'assert', 'async_hooks', 'buffer', 'child_process', 'cluster', 'crypto',
  'dgram', 'dns', 'domain', 'events', 'fs', 'http', 'http2', 'https', 'net',
  'os', 'path', 'perf_hooks', 'punycode', 'querystring', 'readline', 'repl',
  'stream', 'string_decoder', 'tls', 'trace_events', 'tty', 'url', 'util',
  'v8', 'vm', 'zlib'
];

if (typeof process.binding('inspector').open === 'function') {
  builtinLibs.push('inspector');
  builtinLibs.sort();
}

function addBuiltinLibsToObject(object) {
  // Make built-in modules available directly (loaded lazily).
  builtinLibs.forEach((name) => {
    // Goals of this mechanism are:
    // - Lazy loading of built-in modules
    // - Having all built-in modules available as non-enumerable properties
    // - Allowing the user to re-assign these variables as if there were no
    //   pre-existing globals with the same name.

    const setReal = (val) => {
      // Deleting the property before re-assigning it disables the
      // getter/setter mechanism.
      delete object[name];
      object[name] = val;
    };

    Object.defineProperty(object, name, {
      get: () => {
        const lib = require(name);

        // Disable the current getter/setter and set up a new
        // non-enumerable property.
        delete object[name];
        Object.defineProperty(object, name, {
          get: () => lib,
          set: setReal,
          configurable: true,
          enumerable: false
        });

        return lib;
      },
      set: setReal,
      configurable: true,
      enumerable: false
    });
  });
}

其中builtinLibs主要包含buffer、assert等常用的对象或方法。

process

process在src/env.cc的Environment::Start方法中被创建和初始化,同时在bootstrap/node.js也初始化了process对象。Environment::Start的调用函数是src/node.cc中的inline函数Start。

创建

在Environment::Start中初始化process的逻辑如下:

auto process_template = FunctionTemplate::New(isolate());
  process_template->SetClassName(FIXED_ONE_BYTE_STRING(isolate(), "process"));

  auto process_object =
      process_template->GetFunction()->NewInstance(context()).ToLocalChecked();
  // 初始化时声明的Persistent handle,Persistent v8::Object process_object
  // 这里利用process_object.Reset(),最终调用v8的PersistentBase<T>::Reset给Persistent handle重新赋值
  set_process_object(process_object);

这里面主要做了如下几件事:

1.声明还输模版process_template,设置其类名为process
2.获取对象实例process_object
3.调用set_process_object,将上述实例process_object赋值给初始化时声明的Persistent handle(Persistent v8::Object process_object),也就是process_object这个持久化的handle指向新创建的process_object实例。

这里需要注意的是set_process_object是从哪里来的呢?

在env-inl.h中有下面的宏定义:

#define V(PropertyName, TypeName)                                             \
  inline v8::Local<TypeName> Environment::PropertyName() const {              \
    return StrongPersistentToLocal(PropertyName ## _);                        \
  }                                                                           \
  inline void Environment::set_ ## PropertyName(v8::Local<TypeName> value) {  \
    PropertyName ## _.Reset(isolate(), value);                                \
  }
  ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V)
#undef V

#define ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V)                           \
                                \
  V(process_object, v8::Object)                                               \
  V(promise_reject_handled_function, v8::Function)                            \
  V(promise_reject_unhandled_function, v8::Function)                          \
  V(promise_wrap_template, v8::ObjectTemplate)                                \
  V(push_values_to_array_function, v8::Function)                              \
  V(randombytes_constructor_template, v8::ObjectTemplate)                     \
  V(script_context_constructor_template, v8::FunctionTemplate)                \
  V(script_data_constructor_function, v8::Function)                           \
  V(secure_context_constructor_template, v8::FunctionTemplate)                \
  V(shutdown_wrap_template, v8::ObjectTemplate)                               \
  V(tcp_constructor_template, v8::FunctionTemplate)                           \
  V(tick_callback_function, v8::Function)                                     \
  V(timers_callback_function, v8::Function)                                   \
  V(tls_wrap_constructor_function, v8::Function)                              \
  V(tty_constructor_template, v8::FunctionTemplate)                           \
  V(udp_constructor_function, v8::Function)                                   \
  V(vm_parsing_context_symbol, v8::Symbol)                                    \
  V(url_constructor_function, v8::Function)                                   \
  V(write_wrap_template, v8::ObjectTemplate)

从这里我们可以看出,set_process_object在这里定义,其真实调用的是process_object_.Reset方法,那么v8::Local:: Reset方法又做了什么呢?

void PersistentBase<T>::Reset(Isolate* isolate, const Local<S>& other) {
  TYPE_CHECK(T, S);
  Reset();
  if (other.IsEmpty()) return;
  this->val_ = New(isolate, other.val_);
}

这里Reset方法在PersistentBase类下,这里顾名思义PersistentBase是Persistent handle的基类,调用Reset方法也就是重新给其赋值。

那么开始的Persistent handle process_object又是怎么来的呢?我们看env.h中的如下代码:

#define V(PropertyName, TypeName)                                             \
  inline v8::Local<TypeName> PropertyName() const;                            \
  inline void set_ ## PropertyName(v8::Local<TypeName> value);
  ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V)
#undef V

其中ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES这个宏我们上面见过,就是调用了多次V,只不过这里的V代表V8的handle声明,用在process_object上其实就是如下代码:

inline v8::Local<v8::Object> process_object() const

也就是声明了Persistent handle process_object,也就有了我们后面的利用PersistentBase::Reset进行赋值的操作了。

初始化

process的初始化出现在src/env.cc和bootstrap/node.js。

在src/env.cc中,其实调用的是node.cc中的SetupProcessObject方法,初始化代码比较长,下面摘出了比较具有代表性的代码:

Local<Object> process = env->process_object();

  auto title_string = FIXED_ONE_BYTE_STRING(env->isolate(), "title");
  // 设置process的存取器
  CHECK(process->SetAccessor(env->context(),
                             title_string,
                             ProcessTitleGetter,
                             ProcessTitleSetter,
                             env->as_external()).FromJust());

  // process.version
  // 参数为 obj, name, value
  READONLY_PROPERTY(process,
                    "version",
                    FIXED_ONE_BYTE_STRING(env->isolate(), NODE_VERSION));

这里首先获取从env中获取了process_object,然后设置了一个process.title的存取器,用来获取和设置title,这里的title获取调用了uv_get_process_title,实际上就是开辟了堆内存,copy了一份process_argv[0],也就是入口main函数的argv[0];接着使用READONLY_PROPERTY这个宏设置了process的只读属性version。READONLY_PROPERTY宏定义如下所示:

#define READONLY_PROPERTY(obj, name, value)                                   \
  do {                                                                        \
    obj->DefineOwnProperty(env->context(),                                    \
                           FIXED_ONE_BYTE_STRING(isolate, name),              \
                           value, ReadOnly).FromJust();                       \
  } while (0)

在bootstrap/node.js中初始化process的代码如下:

setupProcessObject();

// do this good and early, since it handles errors.
setupProcessFatal();

// 国际化
setupProcessICUVersions();

......

Object.defineProperty(process, 'argv0', {
  enumerable: true,
  configurable: false,
  value: process.argv[0]
});
process.argv[0] = process.execPath;

......

function setupProcessObject() {
    // set_push_values_to_array_function, node.cc
    process._setupProcessObject(pushValueToArray);

    function pushValueToArray() {
      for (var i = 0; i < arguments.length; i++)
        this.push(arguments[i]);
    }
}

......

function setupProcessFatal() {
    const {
      executionAsyncId,
      clearDefaultTriggerAsyncId,
      clearAsyncIdStack,
      hasAsyncIdStack,
      afterHooksExist,
      emitAfter
    } = NativeModule.require('internal/async_hooks');

    process._fatalException = function(er) {
      
      ......
      
      return true;
    };
}

这里主要初始化了process的set_push_values_to_array_function、execPath、错误处理以及国际化版本等。

总结

本文主要讲解了global、process的创建、初始化,以及为什么我们在我们的用户代码中可以对他们进行直接的访问。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant