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()
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)
上一篇讲node运行的文章中,我们提到了LoadEnvironment的LoadersBootstrapperSource方法,其从node_javascript.cc中获取loader文件内容的ascII码,node_javascript.cc在out/Debug/gen中,它是如何产生的呢?
本篇文章就是来介绍node_javascript.cc是如何产生的以及其中的内容。
node_js2c
下面是node.gyp定义的node_js2c目标:
inputs
我们可以看到inputs中主要有三个输入,library_files、./config.gypi、tools/check_macros.py。
library_files
其中library_files包含如下文件:
基本上是lib和dep中的一些.js文件。
./config.gypi
./config.gypi主要定义了一些target_defaults(作用域.gyp文件中所有的targets)和一些变量。
tools/check_macros.py
宏定义:
outputs
outputs很简单,在debug模式下就是out/Debug/node_javascript.cc。
action
翻译成指令就是:
js2c.py
下面我们来看下js2c.py里做了什么?
调用了JS2C,并将inputs中的所有文件路径作为参数传进去。
我们来看下JS2C:
这里一共做了如下几件事:
render
判断文件中字符的ascII码是否超过127,超过的字符被转成UTF-16。
node_javascript.cc
node_javascript.cc主要有以下几部分组成:
1.各个模块key、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)
这里主要是将各个模块的key、value挂载在exports对象中,可以在.cpp或者.js中取得文件内容进行执行等操作。
总结
本文主要介绍node_javascript.cc的产生和内容,这其实也是node中获取native模块最关键的地方。到此为止,已经介绍了node中builtin和native模块的由来,大家也可以和上一篇文章的中所提到的getBinding串起来了。