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源码-node_javascript.cc #8

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

Node.js源码-node_javascript.cc #8

tsy77 opened this issue Jul 22, 2018 · 0 comments

Comments

@tsy77
Copy link
Owner

tsy77 commented Jul 22, 2018

上一篇讲node运行的文章中,我们提到了LoadEnvironment的LoadersBootstrapperSource方法,其从node_javascript.cc中获取loader文件内容的ascII码,node_javascript.cc在out/Debug/gen中,它是如何产生的呢?

本篇文章就是来介绍node_javascript.cc是如何产生的以及其中的内容。

node_js2c

下面是node.gyp定义的node_js2c目标:

{
      'target_name': 'node_js2c',
      'type': 'none',
      'toolsets': ['host'],
      'actions': [
        {
          'action_name': 'node_js2c',
          'process_outputs_as_sources': 1,
          'inputs': [
            '<@(library_files)',
            './config.gypi',
            'tools/check_macros.py'
          ],
          'outputs': [
            '<(SHARED_INTERMEDIATE_DIR)/node_javascript.cc',
          ],
          'conditions': [
            [ 'node_use_dtrace=="false" and node_use_etw=="false"', {
              'inputs': [ 'src/notrace_macros.py' ]
            }],
            [ 'node_use_perfctr=="false"', {
              'inputs': [ 'src/noperfctr_macros.py' ]
            }],
            [ 'node_debug_lib=="false"', {
              'inputs': [ 'tools/nodcheck_macros.py' ]
            }],
            [ 'node_debug_lib=="true"', {
              'inputs': [ 'tools/dcheck_macros.py' ]
            }]
          ],
          'action': [
            'python',
            'tools/js2c.py',
            '<@(_outputs)',
            '<@(_inputs)',
          ],
        },
      ],
    }

inputs

我们可以看到inputs中主要有三个输入,library_files、./config.gypi、tools/check_macros.py。

library_files

其中library_files包含如下文件:

'library_files': [
      'lib/internal/bootstrap/loaders.js',
      'lib/internal/bootstrap/node.js',
      'lib/async_hooks.js',
      'lib/assert.js',
      'lib/buffer.js',
      
      ......
      
      'deps/node-inspect/lib/internal/inspect_client.js',
      'deps/node-inspect/lib/internal/inspect_repl.js',
      'deps/acorn/dist/acorn.js',
      'deps/acorn/dist/walk.js',
    ],

基本上是lib和dep中的一些.js文件。

./config.gypi

./config.gypi主要定义了一些target_defaults(作用域.gyp文件中所有的targets)和一些变量。

tools/check_macros.py

宏定义:

macro CHECK(x) = do { if (!(x)) (process._rawDebug("CHECK: x == true"), process.abort()) } while (0);
macro CHECK_EQ(a, b) = CHECK((a) === (b));
macro CHECK_GE(a, b) = CHECK((a) >= (b));
macro CHECK_GT(a, b) = CHECK((a) > (b));
macro CHECK_LE(a, b) = CHECK((a) <= (b));
macro CHECK_LT(a, b) = CHECK((a) < (b));
macro CHECK_NE(a, b) = CHECK((a) !== (b));

outputs

outputs很简单,在debug模式下就是out/Debug/node_javascript.cc。

action

'action': [
            'python',
            'tools/js2c.py',
            '<@(_outputs)',
            '<@(_inputs)',
          ]

翻译成指令就是:

python tools/js2c.py $(outputs) $(inputs)

js2c.py

下面我们来看下js2c.py里做了什么?

def main():
  natives = sys.argv[1]
  source_files = sys.argv[2:]
  JS2C(source_files, [natives])

调用了JS2C,并将inputs中的所有文件路径作为参数传进去。

我们来看下JS2C:

def JS2C(source, target):
  modules = []
  consts = {}
  macros = {}
  macro_lines = []

  for s in source:
    if (os.path.split(str(s))[1]).endswith('macros.py'):
      macro_lines.extend(ReadLines(str(s)))
    else:
      modules.append(s)

  # Process input from all *macro.py files
  // 拿到宏定义
  (consts, macros) = ReadMacros(macro_lines)

  # Build source code lines
  definitions = []
  initializers = []

  for name in modules:
    lines = ReadFile(str(name))
    // 替换宏定义
    lines = ExpandConstants(lines, consts)
    lines = ExpandMacros(lines, macros)

    deprecated_deps = None

    # On Windows, "./foo.bar" in the .gyp file is passed as "foo.bar"
    # so don't assume there is always a slash in the file path.
    if '/' in name or '\\' in name:
      split = re.split('/|\\\\', name)
      if split[0] == 'deps':
        if split[1] == 'node-inspect' or split[1] == 'v8':
          deprecated_deps = split[1:]
        split = ['internal'] + split
      else:
        split = split[1:]
      name = '/'.join(split)

    # if its a gypi file we're going to want it as json
    # later on anyway, so get it out of the way now
    if name.endswith(".gypi"):
      lines = re.sub(r'#.*?\n', '', lines)
      lines = re.sub(r'\'', '"', lines)
    name = name.split('.', 1)[0]
    var = name.replace('-', '_').replace('/', '_')
    key = '%s_key' % var
    value = '%s_value' % var

    definitions.append(Render(key, name))
    definitions.append(Render(value, lines))
    initializers.append(INITIALIZER.format(key=key, value=value))

    if deprecated_deps is not None:
      name = '/'.join(deprecated_deps)
      name = name.split('.', 1)[0]
      var = name.replace('-', '_').replace('/', '_')
      key = '%s_key' % var
      value = '%s_value' % var

      definitions.append(Render(key, name))
      definitions.append(Render(value, DEPRECATED_DEPS.format(module=name)))
      initializers.append(INITIALIZER.format(key=key, value=value))

  # Emit result
  output = open(str(target[0]), "w")
  output.write(TEMPLATE.format(definitions=''.join(definitions),
                               initializers=''.join(initializers)))
  output.close()

这里一共做了如下几件事:

1.拿到宏定义
2.循环遍历文件
	·宏替换
	·获得所有定义的字符串代码
	·获得所有初始化的字符串代码
	·字符串替换

render

def Render(var, data):
  # Treat non-ASCII as UTF-8 and convert it to UTF-16.
  if any(ord(c) > 127 for c in data):
    template = TWO_BYTE_STRING
    data = map(ord, data.decode('utf-8').encode('utf-16be'))
    data = [data[i] * 256 + data[i+1] for i in xrange(0, len(data), 2)]
    data = ToCArray(data)
  else:
    template = ONE_BYTE_STRING
    data = ToCString(data)
  return template.format(var=var, data=data)

判断文件中字符的ascII码是否超过127,超过的字符被转成UTF-16。

node_javascript.cc

node_javascript.cc主要有以下几部分组成:

1.各个模块key、value对应的结构体的定义

static const uint8_t raw_internal_bootstrap_loaders_key[] = { 105,110,116,101,114,110,97,108,47,98,111,111,116,115,116,114,97,112,47,108,
111,97,100,101,114,115 };
static struct : public v8::String::ExternalOneByteStringResource {
  const char* data() const override {
    return reinterpret_cast<const char*>(raw_internal_bootstrap_loaders_key);
  }
  size_t length() const override { return arraysize(raw_internal_bootstrap_loaders_key); }
  void Dispose() override { /* Default calls `delete this`. */ }
  v8::Local<v8::String> ToStringChecked(v8::Isolate* isolate) {
    return v8::String::NewExternalOneByte(isolate, this).ToLocalChecked();
  }
} internal_bootstrap_loaders_key;

static const uint8_t raw_internal_bootstrap_loaders_value[] = { 47,47,32,84,104,105,115,32,102,105,108,101,32,99,114,101,97,116,101,115,
32,116,104,101,32,105,110,116,101,114,110,97,108,32,109,111,100,117,108,101,
32,38,32,98,105,110,100,105,110,103,32,108,111,97,100,101,114,115,32,117,
 };
static struct : public v8::String::ExternalOneByteStringResource {
  const char* data() const override {
    return reinterpret_cast<const char*>(raw_internal_bootstrap_loaders_value);
  }
  size_t length() const override { return arraysize(raw_internal_bootstrap_loaders_value); }
  void Dispose() override { /* Default calls `delete this`. */ }
  v8::Local<v8::String> ToStringChecked(v8::Isolate* isolate) {
    return v8::String::NewExternalOneByte(isolate, this).ToLocalChecked();
  }
} internal_bootstrap_loaders_value;

我们可以看到数组和两个struct,其中raw_internal_bootstrap_loaders_key和raw_internal_bootstrap_loaders_value分别记录bootstrap_loaders的key和value(文件内容),两个结构体internal_bootstrap_loaders_key和internal_bootstrap_loaders_value均有方法ToStringChecked,而ToStringChecked其实会去找data()方法,也就是说internal_bootstrap_loaders_value.ToStringChecked()便会返回对应的ascII码。

2.初始化函数定义(initializers)

void DefineJavaScript(Environment* env, v8::Local<v8::Object> target) {
  CHECK(target->Set(env->context(),
                  internal_bootstrap_loaders_key.ToStringChecked(env->isolate()),
                  internal_bootstrap_loaders_value.ToStringChecked(env->isolate())).FromJust());

这里主要是将各个模块的key、value挂载在exports对象中,可以在.cpp或者.js中取得文件内容进行执行等操作。

总结

本文主要介绍node_javascript.cc的产生和内容,这其实也是node中获取native模块最关键的地方。到此为止,已经介绍了node中builtin和native模块的由来,大家也可以和上一篇文章的中所提到的getBinding串起来了。

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