精华内容
下载资源
问答
  • 一直在学习C++,也想阅读点开源的C++项目,发现网上对Google V8评价不错,于是上Github上找到了源代码,但在学习中遇到一个js数组排序的问题,下面这篇文章主要给大家介绍了通过V8源码说说一个关于JS数组排序的诡异...
  • 易语言1V8源码

    2014-02-09 21:48:15
    易语言1v8多开器材源码
  • 前面已经把V8源码download下来和build的过程都讲过了。这里不做过多赘述,只说说在win7下搭建环境与的的一些问题。 0x01 系统环境 名称 版本 位数 备注 操作系统 Windows 7 Professional 6.1.7601, Service...

    0x00 前言

    前面已经把V8源码download下来和build的过程都讲过了。这里不做过多赘述,只说说在win7下搭建环境与的的一些问题。

    0x01 系统环境

    名称 版本 位数 备注
    操作系统 Windows 7 Professional 6.1.7601, Service Pack 1 64 bit \
    Python 2.7.13 64 bit \

    没有全局代理的情况下

    0x02 设置代理

    打开cmd.exe 配置git代理

    配置poweshell脚本cipd.ps1的代理,这里在WebClient对象中加入了代理服务器地址http://127.0.0.2:8080

    try {
      echo "Downloading CIPD client for $Platform from $URL..."
      $FiddlerWP = New-Object System.Net.WebProxy "http://127.0.0.2:8080"
      $wc = (New-Object System.Net.WebClient)
      $wc.Headers.Add("User-Agent", $UserAgent)
      $wc.Proxy = $FiddlerWP
      try {
        $wc.DownloadFile($URL, $TmpPath)
      } catch {
        throw "Failed to download the file, check your network connection"
      }
    

    0x03 禁止depot_tools更新

    先谢郭嘉,git可以设置代理,然而,depot_tools和cipd这两个需要翻墙更新,搞了半天没有跟到发起连接的代码位置。这里先说明什么是cipd,再给出禁用更新的方法。

    CIPD(Chrome Infrastructure Package Deployment)

    CIPD 是包部署工具。由包注册和一个可以创建,更新,下载和安装的命令行客户端两部分组成。了解inux的童鞋是不是有点感觉了。对,就是一个apt-get一样的包管理工具,只不过是跟pip,npm,brew等负责chrome相关的包管理。【传送门

    cipd的包结构

    CIPD package具有一个package name(例如“ infra / tools / foo”)和一个内容寻址实例的列表(例如“ bec8e88201949be06b06174178c2f62b81e4008e”),其中软件包名称中的斜杠形成了软件包的层次结构,而一个实例是一个ZIP文件。

    depot_tools

    depot_tools 是用于Chromium开发的需要的一系列工具集合。这个定义相当于屁都没说。【传送门

    Chromium使用一个脚本集合,即dept_tools, 用于管理Chromium源码仓库与Chromium开发过程之间的交互。主要包括如下工具集:

    • gclient:管理svn和git checkout的工具。类似于repo_tool.

    其他子项就不一一介绍了,有个印象就成了,打算深入了解depot_tools的童鞋,可以使用此【传送门】。

    gclient

    gclient是一个python脚本,用于管理模块化依赖项的工作区,每个依赖项都是用独立的不同的的svn或git 拉取(checkout)。

    gclient这个要单独拉出来说说,大概意思是,项目比较大,用了很多第三方库,这些库有的是svn有的是git,不好傻瓜式管理。就拍脑袋搞了一个封装,管你用的什么,gclient可以完成仓库管理的各种功能,快活的很。

    特性:

    • 根据操作系统OS指定依赖
    • 根据不同仓库的父子依赖关系指定依赖
    • 变量可用于表示抽象概念
    • 提供一个checkout运行后即可调用的Hook
    • .gclient和DEPS这些python脚本,可以方便地增加和修改额外配置数据

    .gclient 文件

    一个master 文件。实际上是一个python脚本。它指定以下变量:

    • solutions: 指定需要拉取项目的字段数组。
    • hooks:checkout的钩子
    • target_os: OS以依赖列表
    • cache_dir: 主要针对bots,多个工作集使用单个git缓存. 参见 gclient.py --cache-dir

    每个项目在solutions数组中定义,同时包含可选的DEPS文件。.gclient文件通过gclient config <url>生成,或者手写。每个solustions条目是包含一些变量的字段:

    • name:checkout的路径
    • url:用于fetch/clone的远程仓库
    • custom_deps: 可选,定制的依赖
    • custom_vars: 可选,定制的变量
    • safesync_url:可选

    禁用depot_tools的方法

    1. 修改gclient.bat文件

    setlocal下面增加一行set DEPOT_TOOLS_UPDATE=0,如下

    @echo off
    :: Copyright (c) 2012 The Chromium Authors. All rights reserved.
    :: Use of this source code is governed by a BSD-style license that can be
    :: found in the LICENSE file.
    setlocal
    
    echo "--------->turn off the update<------"
    set DEPOT_TOOLS_UPDATE=0
    :: Shall skip automatic update?
    IF "%DEPOT_TOOLS_UPDATE%" == "0" GOTO :CALL_GCLIENT
    
    :: Synchronize the root d irectory before deferring control back to gclient.py.
    call "%~dp0update_depot_tools.bat" %*
    
    :CALL_GCLIENT
    :: Ensure that "depot_tools" is somewhere in PATH so this tool can be used
    :: standalone, but allow other PATH manipulations to take priority.
    set PATH=%PATH%;%~dp0
    
    :: Defer control.
    IF "%GCLIENT_PY3%" == "1" (
      :: TODO(1003139): Use vpython3 once vpython3 works on Windows.
      python3 "%~dp0gclient.py" %*
    ) ELSE (
      python "%~dp0gclient.py" %*
    )
    

    这样gclient就不会更新报错了

    D:\worksp\git>depot_tools\gclient sync  --force
    "--------->turn off the update<------"
    [P11288 09:27:15.448 client.go:309 W] RPC failed transiently. Will retry in 1s    {"error":"failed to send request: Post
    https://chrome-infra-packages.appspot.com/prpc/cipd.Repository/ResolveVersion: proxyconnect tcp: tls: first record does
    ot look like a TLS handshake", "host":"chrome-infra-packages.appspot.com", "method":"ResolveVersion", "service":"cipd.Re
    ository", "sleepTime":"1s"}
    [P11288 09:27:15.453 client.go:309 W] RPC failed transiently. Will retry in 1s    {"error":"failed to send request: Post
    https://chrome-infra-packages.appspot.com/prpc/cipd.Repository/ResolveVersion: proxyconnect tcp: tls: first record does
    ot look like a TLS handshake", "host":"chrome-infra-packages.appspot.com", "method":"ResolveVersion", "service":"cipd.Re
    ository", "sleepTime":"1s"}
    [P11288 09:27:15.458 client.go:309 W] RPC failed transiently. Will retry in 1s    {"error":"failed to send request: Post
    https://chrome-infra-packages.appspot.com/prpc/cipd.Repository/ResolveVersion: proxyconnect tcp: tls: first record does
    ot look like a TLS handshake", "host":"chrome-infra-packages.appspot.com", "method":"ResolveVersion", "service":"cipd.Re
    ository", "sleepTime":"1s"}
    终止批处理操作吗(Y/N)? y
    
    D:\worksp\git>depot_tools\gclient sync  --force
    "--------->turn off the update<------"
    

    做事还得做绝,发现fetch还会更新,继续修改fetch.bat, 直接注释掉更新

    @echo off
    :: Copyright (c) 2013 The Chromium Authors. All rights reserved.
    :: Use of this source code is governed by a BSD-style license that can be
    :: found in the LICENSE file.
    setlocal
    
    :: Synchronize the root directory before deferring control back to gclient.py.
    :: call "%~dp0\update_depot_tools.bat"
    
    :: Ensure that "depot_tools" is somewhere in PATH so this tool can be used
    :: standalone, but allow other PATH manipulations to take priority.
    set PATH=%PATH%;%~dp0
    
    :: Defer control.
    IF "%GCLIENT_PY3%" == "1" (
      vpython3 "%~dp0\fetch.py" %*
    ) ELSE (
      vpython "%~dp0\fetch.py" %*
    )
    

    2. 使用环境变量

    据说在环境变量中设置DEPOT_TOOLS_UPDATE为0 即可。第一种方法亲测有效,这个仅供参考。

    0x04 fetch v8

    执行如下命令

    depot_tools\fetch v8
    

    提示没有权限

    Running: 'C:\Users\xxxx\Scripts\python.exe' 'D:\worksp\git\depot_tools\gclient.py' sync -
    -with_branch_heads
    Running: git submodule foreach 'git config -f $toplevel/.git/config submodule.$name.ignore all'
    Traceback (most recent call last):
      File "D:\worksp\git\depot_tools\\fetch.py", line 318, in <module>
        sys.exit(main())
      File "D:\worksp\git\depot_tools\\fetch.py", line 313, in main
        return run(options, spec, root)
      File "D:\worksp\git\depot_tools\\fetch.py", line 307, in run
        return checkout.init()
      File "D:\worksp\git\depot_tools\\fetch.py", line 158, in init
        cwd=wd)
      File "D:\worksp\git\depot_tools\\fetch.py", line 111, in run_git
        return git_common.run(*cmd, **kwargs)
      File "D:\worksp\git\depot_tools\git_common.py", line 700, in run
        return run_with_stderr(*cmd, **kwargs)[0]
      File "D:\worksp\git\depot_tools\git_common.py", line 766, in run_with_stderr
        proc = subprocess2.Popen(cmd, **kwargs)
      File "D:\worksp\git\depot_tools\subprocess2.py", line 159, in __init__
        % (str(e), kwargs.get('cwd'), args[0]))
    OSError: Execution failed with error: [Error 267] .
    Check that D:\worksp\git\v8 or E:\Program Files\Git\cmd\git.exe exist and have execution permission.
    

    另一个坑

    fetch.py脚本不知道为何在win32环境下会使用git.bat命令而不用git,导致刚开始就报错

    >depot_tools\fetch v8
     --spec 'solutions = [
      {
        "url": "https://chromium.googlesource.com/v8/v8.git",
        "managed": False,
        "name": "v8",
        "deps_file": "DEPS",
        "custom_deps": {},
      },
    ]
    '
    v8 (ERRO
    R)
    ----------------------------------------
    [0:00:00] Started.
    ----------------------------------------
    Traceback (most recent call last):
      File "D:\worksp\git\depot_tools\metrics.py", line 267, in print_notice_and_exit
        yield
      File "D:\worksp\git\depot_tools\gclient.py", line 3183, in <module>
        sys.exit(main(sys.argv[1:]))
      File "D:\worksp\git\depot_tools\gclient.py", line 3168, in main
        return dispatcher.execute(OptionParser(), argv)
      File "D:\worksp\git\depot_tools\subcommand.py", line 252, in execute
        return command(parser, args[1:])
      File "D:\worksp\git\depot_tools\gclient.py", line 2725, in CMDsync
        ret = client.RunOnDeps('update', args)
      File "D:\worksp\git\depot_tools\gclient.py", line 1763, in RunOnDeps
        patch_refs=patch_refs, target_branches=target_branches)
      File "D:\worksp\git\depot_tools\gclient_utils.py", line 914, in flush
        reraise(e[0], e[1], e[2])
      File "D:\worksp\git\depot_tools\gclient_utils.py", line 991, in run
        self.item.run(*self.args, **self.kwargs)
      File "D:\worksp\git\depot_tools\gclient.py", line 933, in run
        file_list)
      File "D:\worksp\git\depot_tools\gclient_scm.py", line 132, in RunCommand
        return getattr(self, command)(options, args, file_list)
      File "D:\worksp\git\depot_tools\gclient_scm.py", line 516, in update
        mirror = self._GetMirror(url, options, revision_ref)
      File "D:\worksp\git\depot_tools\gclient_scm.py", line 957, in _GetMirror
        if not self.cache_dir:
      File "D:\worksp\git\depot_tools\gclient_scm.py", line 218, in cache_dir
        return git_cache.Mirror.GetCachePath()
      File "D:\worksp\git\depot_tools\git_cache.py", line 305, in GetCachePath
        ['cache.cachepath']).strip()
      File "E:\Program Files\Python\lib\subprocess.py", line 567, in check_output
        process = Popen(stdout=PIPE, *popenargs, **kwargs)
      File "E:\Program Files\Python\lib\subprocess.py", line 711, in __init__
        errread, errwrite)
      File "E:\Program Files\Python\lib\subprocess.py", line 959, in _execute_child
        startupinfo)
    WindowsError: [Error 2]
    

    直接修改git_cache.py文件,使git_exe变量赋值为'git',代码如下:

    class Mirror(object):
      git_exe = 'git' if sys.platform.startswith('win') else 'git'
      gsutil_exe = os.path.join(
        os.path.dirname(os.path.abspath(__file__)), 'gsutil.py')
      cachepath_lock = threading.Lock()
    

    继续fetch,可以看到已经能正常下载了

    1>git config
    1>________ running 'git -c core.deltaBaseCacheLimit=2g clone --no-checkout --progress https://chromium.googlesource.com/v
    8/v8.git D:\worksp\git\_gclient_v8_prggkr' in 'D:\worksp\git'
    1>Cloning into 'D:\worksp\git\_gclient_v8_prggkr'...
    1>remote: Sending approximately 581.51 MiB ...
    1>remote: Counting objects: 7675, done
    1>remote: Finding sources: 100% (15/15)
    1>Receiving objects:  13% (96685/723669), 41.43 MiB | 268.00 KiB/s
    

    0x05问题

    1. gclient工具目前只支持python2

    如果使用python3的话会报错,换成python2的环境即可。

    Warning: gclient doesn't yet support Python 3 and may not work correctly.
    

    0x06 参考文献

    http://www.cclk.cc/2017/08/03/webrtc/webrtc学习1-全平台编译/
    https://blog.csdn.net/yyinhai/article/details/52955608
    https://blog.csdn.net/yyinhai/article/details/54016892
    https://skylerlee.github.io/codelet/2017/03/08/build-v8/

    展开全文
  • V8 源码 win 系统下编译方法以及 V8 dll 化方法  微博前端 2012-9-5 19:26发布 4回复 V8 是个很好的 JS 引擎,我们可以用它做很多好玩的事情,比如包装一些系统API给JS调用。 在使用它之前,我们...
    V8 源码 win 系统下编译方法以及 V8 dll 化方法 
    微博前端 2012-9-5 19:26发布
    V8 是个很好的 JS 引擎,我们可以用它做很多好玩的事情,比如包装一些系统API给JS调用。
    在使用它之前,我们的首要任务是编译它。
    非 win 平台编译 v8 很容易,win 下就麻烦些。
    下文简要说明 win 下如何编译 v8 引擎。

    先决条件:
    安装VS,我整的是 VS2008。

    V8 项目已经改由 GYP 生成了,
    如果你看了 google 搜索出的资料,那么恭喜你!折腾各个包的路径去和 Scone 去吧!!

    现今为止正确方法如下:

    1. 得到 V8 项目源码
    svn checkout http://t.cn/zWgRq3D

    2. V8 源码 build/README.txt 文件看一下。

    To generate Visual Studio solution and project files on Windows:
    ----------------------------------------------------------------

    On Windows an additional third party component is required. This is cygwin in
    the same version as is used by the Chromium project. This can be checked out
    from the Chromium repository. From the root of the V8 project do the following:

    > svn co http://t.cn/zWgRq3s third_party/cygwin

    To run GYP Python is required and it is recommended to use the same version as
    is used by the Chromium project. This can also be checked out from the Chromium
    repository. From the root of the V8 project do the following:

    > svn co http://t.cn/zWgRq1P third_party/python_26

    Now generate Visual Studio solution and project files for the ia32 architecture:

    > third_party\python_26\python build/gyp_v8

    Now open build\All.sln in Visual Studio.

    步骤就是:
    1、在 V8 源码目录下建立 third_party 。

    2、在此目录下建立 python_26 和 cygwin 目录。

    3、在以上目录中分别

    4、得到 gyp :
    svn co http://t.cn/zWgRq3F build/gyp
    将其放在 V8 源码目录/build/gyp 目录下。

    4、进入 cmd ,定位到 V8 源码目录,执行 :
    third_party\python_26\python ..\..\build\gyp_v8
    生成工程文件 All.sln

    5、VS2008 中打开此工程,执行 bulid。


    资料连接:
    https://developers.google.com/v8/


    第三方 VS 工程中引入 V8 需要注意下面几点:
    1、把上面编译好的lib加入到 "工程/属性/配置属性/连接器/命令行" 的附加依赖项中,如:
    D:\project\v8_test\v8_project\build\Release\lib\v8_base.lib
    D:\project\v8_test\v8_project\build\Release\lib\preparser_lib.lib
    D:\project\v8_test\v8_project\build\Release\lib\v8_nosnapshot.lib
    D:\project\v8_test\v8_project\build\Release\lib\v8_snapshot.lib
    ws2_32.lib
    winmm.lib
    依赖中必须存在winmm.lib、ws2_32.lib。
    代码中 #pragma comment(lib,"ws2_32.lib") 等等也可以 ……

    2、把v8原码目录中的 include 目录指定到 "工程/属性/配置属性/C++/常规/附加包含目录"中,如:
    D:\project\v8_test\v8_project\include

    此外 http://t.cn/zWgRq1v 里面有 hello world 实例可用来测试是否引入成功。

    当 V8 是静态库时,如果 v8 版本修正,我们编译新的 v8 后还要编译使用 v8 的工程源码。使它们成为一个执行文件。这样有时候很麻烦。
    此时可能需要的是采用动态编译,让 V8 成为一个动态链接库(dll),以后 V8 更新,就单独编译它,覆盖老的链接库就好了,而不用再重新编译我们的工程。

    V8 dll 化编译要点:
    1、单独生成 v8_nosnapshot 为 v8_nosnapshot.lib 因为 v8_base 依赖 v8_nosnapshot.lib 。
    2、修改 v8_base 属性 "工程/属性/配置属性/常规/配置类型" 为 动态库.dll
    3、在 v8_base 属性 "工程/属性/配置属性/C++/预处理器/预处理定义" 内添加 "BUILDING_V8_SHARED" 定义项
    4、在 v8_base 属性 "工程/属性/配置属性/连接器/命令行" 的附加依赖项中加入:
    D:\project\v8_test\v8_project\build\Release\lib\v8_nosnapshot.lib
    ws2_32.lib
    winmm.lib
    5、生成 v8_base 。

    V8 dll 的使用:
    生成的 v8_base 动态链接库项目中存在两个文件 :
    v8_base.lib
    v8_base.dll
    其中 v8_base.lib 是在 VS 工程中隐式调用 v8_base.dll 使用的,工程编译后可不需要。
    隐式调用 v8_base.dll 方法为:
    1、将以上两个文件放置到你的工程文件夹中。
    2、在 v8_base 属性 "工程/属性/配置属性/连接器/命令行" 的附加依赖项中加入 v8_base.lib 。
    3、把v8原码目录中的 include 目录指定到 "工程/属性/配置属性/C++/常规/附加包含目录"中,如:
    D:\project\v8_test\v8_project\include
    * 当然,此时你还需要在工程中 #include "v8.h"
    4、bulid 工程。
    5、编译完成后将 v8_base.dll 放置到 exe 所在目录即可。

    当然,你也可以不用隐式调用,使用 LoadLibrary(" v8_base.dll") 在代码动态调用也可以。
    展开全文
  • V8源码编译出来的可执行程序名为d8。d8作为V8引擎在命令行中可以使用的交互shell存在。平常V8的shell是在浏览器中可以看到的。Google官方已经不记得d8这个名字的由来,但是做为"delveloper she...

    0x00 前言

    常听人说烦恼即菩提,我们凡人遇到烦恼只是深感烦恼,不见菩提。快乐是在心里,不假外求,求即往往不得,转为烦恼。 ------ 梁实秋

    0x01 什么是 d8

    V8源码编译出来的可执行程序名为d8。d8作为V8引擎在命令行中可以使用的交互shell存在。平常V8的shell是在浏览器中可以看到的。Google官方已经不记得d8这个名字的由来,但是做为"delveloper shell"的缩写,用首字母d和8结合,恰到好处。

    0x02 d8接口

    console.log函数

    解决js脚本执行结果从shell中输出显示

    $ cat test.js
    console.log('Hello world!');
    
    $ out.gn/x64.optdebug/d8 test.js
    Hello world!
    

    read/readline函数

    在d8命令行中,接入到js中,可以使js读取文件内容。

    d8> const license = read('LICENSE');
    d8> license
    "This license applies to all parts of V8 that are not externally
    maintained libraries.  The externally maintained libraries used by V8
    are:
    … (etc.)"
    

    load函数

    在d8命令行中,load一个外部的js文件,并可以随后访问该文件中声明的内容。

    $ cat util.js
    function greet(name) {
      return 'Hello, ' + name;
    }
    
    $ d8
    d8> load('util.js');
    d8> greet('World!');
    "Hello, World!"
    

    arguments变量

    与d8的命令一起使用的自定义参数可以在shell中使用。只需要把这些参数放在--后面。

    out.gn/x64.optdebug/d8 -- hi
    

    shell中使用参数

    d8> arguments[0]
    "hi"
    

    0x03 参考文献

    https://v8.dev/docs/d8

    展开全文
  • 摘要本文从 V8 源码的角度分析为什么 Javascript 语言中的函数是一等公民。首先会介绍一等公民的概念,然后对比一下 C 语言函数和 Javascript 函数的底层表示,以便理解为什么说 Javascript 函数是一等公民。什么是...

    03c3c0aa1a16edfab3faf6f79ad9100d.png

    摘要

    本文从 V8 源码的角度分析为什么 Javascript 语言中的函数是一等公民。首先会介绍一等公民的概念,然后对比一下 C 语言函数和 Javascript 函数的底层表示,以便理解为什么说 Javascript 函数是一等公民。

    什么是编程语言中的一等公民

    In computer science, a programming language is said to have first-class functions if it treats functions as first-class citizens. This means the language supports passing functions as arguments to other functions, returning them as the values from other functions, and assigning them to variables or storing them in data structures.

    以上内容来自维基百科,也就是说,在编程语言中,一等公民可以作为函数参数,可以作为函数返回值,也可以赋值给变量。

    C 语言函数的底层表示

    以下代码,可复制粘贴在 https://tool.lu/coderunner/ 运行。

    #include <stdio.h>
    
    int times10 (int small) {
        return small * 10;
    }
    
    int main(int argc, const char * argv[]) {
        int small = 1;
        int large = times10(small);
        printf("result is %dn", large);
        return 0;
    }

    代码逻辑非常简单,定义了 times10 函数,函数的功能是将入参乘 10 后返回。main 函数调用 times10 函数,代码运行结果在 Xcode 中如下:

    e8854c78f998241380b60f6b4a1b50ea.png

    times10 函数对应的 X64 汇编如下:

    0x100000f30 <+0>:  pushq  %rbp
    0x100000f31 <+1>:  movq   %rsp, %rbp  ; 前两行是套路,保存栈寄存器
    0x100000f34 <+4>:  movl   %edi, -0x4(%rbp) ; edi 寄存器存的是入参
    0x100000f37 <+7>:  imull  $0xa, -0x4(%rbp), %eax ; 乘 10 后的结果存入 eax 
    0x100000f3b <+11>: popq   %rbp ; 最后两行也是套路,恢复栈寄存器
    0x100000f3c <+12>: retq

    C 语言是编译型语言,编译器会将 C 语言的函数体编译成机器码,反汇编后就是上面看到的汇编语言函数。

    main 函数对应的 X64 汇编如下:

    0x100000f40 <+0>:  pushq  %rbp
    0x100000f41 <+1>:  movq   %rsp, %rbp
    0x100000f44 <+4>:  subq   $0x20, %rsp
    0x100000f48 <+8>:  movl   $0x0, -0x4(%rbp)
    0x100000f4f <+15>: movl   %edi, -0x8(%rbp)
    0x100000f52 <+18>: movq   %rsi, -0x10(%rbp)
    0x100000f56 <+22>: movl   $0x1, -0x14(%rbp)
    0x100000f5d <+29>: movl   -0x14(%rbp), %edi
    0x100000f60 <+32>: callq  0x100000f30         ; 调用 times10
    0x100000f65 <+37>: leaq   0x3a(%rip), %rdi          
    0x100000f6c <+44>: movl   %eax, -0x18(%rbp)
    0x100000f6f <+47>: movl   -0x18(%rbp), %esi
    0x100000f72 <+50>: movb   $0x0, %al
    0x100000f74 <+52>: callq  0x100000f86         ; 调用 printf     
    0x100000f79 <+57>: xorl   %esi, %esi
    0x100000f7b <+59>: movl   %eax, -0x1c(%rbp)
    0x100000f7e <+62>: movl   %esi, %eax
    0x100000f80 <+64>: addq   $0x20, %rsp
    0x100000f84 <+68>: popq   %rbp
    0x100000f85 <+69>: retq

    在地址为 0x100000f60 的这行汇编里

    0x100000f60 <+32>: callq  0x100000f30         ; 调用 times10

    main 函数调用 times10 函数,从生成的汇编来看,编译完成后 times10 这个函数名称对应的是地址,也就是说 C 语言的函数名在编译后将不复存在,它对应的是地址。 看到这里,可以看出 C 语言函数和 JavaScript 函数的区别,由于 C 语言函数体对应机器码,函数名称对应地址,所以 C 语言不支持为函数添加属性。

    对照编程语言一等公民的定义,虽然 C 语言的函数不能直接做为参数传递,也不能直接做为结果返回,但通过函数指针,可以完成这一切。所以 C 语言的函数“勉强”是一等公民,笔者的第一份工作是 C 语言程序员,C 语言程序员比较关注底层实现,基本不会讨论也不会在意 C 语言函数到底是不是一等公民。写这段文字的目的是为了对比 JavaScript 语言函数的底层表示,见下文。

    JavaScript 语言函数的底层表示

    V8 会将 JavaScript 函数编译成 C++ 类 JSFunction 的实例,JSFunction 声明代码如下:

    // JSFunction describes JavaScript functions.
    class JSFunction : public JSObject {
        public:
            // [prototype_or_initial_map]:
            DECL_ACCESSORS(prototype_or_initial_map, Object)
            // [shared]: The information about the function that
            // can be shared by instances.
            DECL_ACCESSORS(shared, SharedFunctionInfo)
            static const int kLengthDescriptorIndex = 0;
            static const int kNameDescriptorIndex = 1;
            // Home object descriptor index when function has a [[HomeObject]] slot.
            static const int kMaybeHomeObjectDescriptorIndex = 2;
            // [context]: The context for this function.
            inline Context context();
            inline bool has_context() const;
            inline void set_context(Object context);
            inline JSGlobalProxy global_proxy();
            inline NativeContext native_context();
            inline int length();
            static Handle<Object> GetName(Isolate* isolate, Handle<JSFunction> function);
            static Handle<NativeContext> GetFunctionRealm(Handle<JSFunction> function);
            // [code]: The generated code object for this function.  Executed
            // when the function is invoked, e.g. foo() or new foo(). See
            // [[Call]] and [[Construct]] description in ECMA-262, section
            // 8.6.2, page 27.
            inline Code code() const;
            inline void set_code(Code code);
            inline void set_code_no_write_barrier(Code code);
            // 源码太长,复制粘贴到此结束
    }
    

    从代码的第一行注释:

    // JSFunction describes JavaScript functions.

    可知,JavaScript 函数在 V8 中是 JSFunction 的实例,JSFunction 源码很长,这里举例佐证 JavaScript 函数是 V8 中的一个 C++ 对象。

    JavaScript 函数的 toString 方法,可以输出一个函数的实现代码。比如:

    a = _ => console.log(_)
    a.toString() // 输出 "_ => console.log(_)"
    

    但当对内置对象的方法调用 toString 时,比如:

    Math.max.toString() // 输出 "function max() { [native code] }"
    

    并没有输出函数的实现代码,而且输出的字符串中 native code 是从哪里来的呢?这个问题困扰了笔者 3 年,下面,我们一起看下 JavaScript 函数的 toString 方法在 V8 中的实现,源码如下:

    // ES6 section 19.2.3.5 Function.prototype.toString ( )
    BUILTIN(FunctionPrototypeToString) {
        HandleScope scope(isolate);
        Handle<Object> receiver = args.receiver();
        if (receiver->IsJSBoundFunction()) {
            return *JSBoundFunction::ToString(Handle<JSBoundFunction>::cast(receiver));
        }
        if (receiver->IsJSFunction()) {
            return *JSFunction::ToString(Handle<JSFunction>::cast(receiver));
        }
        // 源码太长,而且本文中的例子在上面的 return 已经返回,故后面省略,对源码感兴趣的朋友请点击上面的源码链接
    }
    

    BUILTIN 是 C++ 定义的宏,C++ 预处理阶段后,上面的代码会变成 C++ 类的一个方法。Math.max 是 JSFunction 的实例,receiver->IsJSFunction() 为true,会执行 JSFunction 的 ToString 类方法,源码如下:

    // static
    Handle<String> JSFunction::ToString(Handle<JSFunction> function) {
        Isolate* const isolate = function->GetIsolate();
        Handle<SharedFunctionInfo> shared_info(function->shared(), isolate);
        // Check if {function} should hide its source code.
        if (!shared_info->IsUserJavaScript()) {
            return NativeCodeFunctionSourceString(shared_info);
        }
        // 源码太长,而且本文中的例子在上面的 return 已经返回,故后面省略,对源码感兴趣的朋友请点击上面的源码链接
    }
    

    Math.max 是 V8 内置函数,不是由用户定义的,!shared_info->IsUserJavaScript() 结果是 true,执行 NativeCodeFunctionSourceString 函数。源码如下:

    Handle<String> NativeCodeFunctionSourceString(
        Handle<SharedFunctionInfo> shared_info) {
        Isolate* const isolate = shared_info->GetIsolate();
        IncrementalStringBuilder builder(isolate);
        builder.AppendCString("function ");
        builder.AppendString(handle(shared_info->Name(), isolate));
        builder.AppendCString("() { [native code] }");
        return builder.Finish().ToHandleChecked();
    }
    

    我们终于看到了期待的字符串 native code:

    builder.AppendCString("() { [native code] }");
    

    梳理一下 JavaScript 函数 toString 方法的调用链路:BUILTIN(FunctionPrototypeToString) -> JSFunction::ToString -> NativeCodeFunctionSourceString。可见 JavaScript 函数对应 V8 JSFunction 的实例,JavaScript 函数的 toString 方法对应 V8 的 JSFunction::ToString 方法。

    JavaScript 函数的 name 属性,可以获取函数名称,比如:

    a = _ => console.log(_)
    a.name // 输出函数名 "a"
    

    JavaScript 函数 name 属性的实现过程中,调用了 JSFunction 的 GetName 方法,源码如下:

    // static
    Handle<Object> JSFunction::GetName(Isolate* isolate,
                                       Handle<JSFunction> function) {
        if (function->shared().name_should_print_as_anonymous()) {
            return isolate->factory()->anonymous_string();
        }
        return handle(function->shared().Name(), isolate);
    }
    

    JavaScript 函数的 length 属性,可以获取参数个数,比如:

    a = _ => console.log(_)
    a.length // 输出 1
    

    JavaScript 函数 length 属性的实现过程中,调用了 JSFunction 的 length 方法,源码如下:

    int JSFunction::length() { return shared().length(); }
    

    举例佐证到此为止,可见 JavaScript 函数在 V8 中是 JSFunction 的实例,既然是 C++ 对象,JavaScript 函数当然可以做为参数传递给其它函数,也可以做为函数的返回值。理解了 JavaScript 函数是 C++ 对象,也很容易理解 JavaScript 函数式编程中的一些写法,比如:

    const isNumber = _ => !isNaN(_);
    function isPrice (price) {
        return [Boolean, isNumber].every(fn => {
            return fn(price)
        })
    }
    isPrice ('123') // 返回 true
    

    isPrice 校验用户输入的字符串是否是一个价格,Boolean 和 isNumber 这两个 JavaScript 函数都是 V8 中 JSFunction 的实例,那么在 V8 看来,[Boolean, isNumber].every 相当于遍历数组,并且数组中的每一个对象都最 JSFunction。如果把 every 换成 reduce,稍加改造便可实现类似 Vue 过滤器 filter 的功能。

    const add3 = num => num + 3;
    const mul2 = num => num * 2;
    function numberReduce (num) {
        return [add3, mul2].reduce((acc, fn) => {
            return fn(acc)
        }, num)
    }
    numberReduce(3) // 返回 (3 + 3) * 2 = 12
    

    在 V8 中 JSFunction 继承自 JSObject,如下:

    // JSFunction describes JavaScript functions.
    class JSFunction : public JSObject {
        // 略
    }
    

    JSObject 的声明如下:

    // The JSObject describes real heap allocated JavaScript objects with
    // properties.
    // Note that the map of JSObject changes during execution to enable inline
    // caching.
    class JSObject : public JSReceiver {
        public:
            static bool IsUnmodifiedApiObject(FullObjectSlot o);
            V8_EXPORT_PRIVATE static V8_WARN_UNUSED_RESULT MaybeHandle<JSObject> New(
            Handle<JSFunction> constructor, Handle<JSReceiver> new_target,
            Handle<AllocationSite> site);
            static MaybeHandle<NativeContext> GetFunctionRealm(Handle<JSObject> object);
            // 9.1.12 ObjectCreate ( proto [ , internalSlotsList ] )
            // Notice: This is NOT 19.1.2.2 Object.create ( O, Properties )
            static V8_WARN_UNUSED_RESULT MaybeHandle<JSObject> ObjectCreate(
                    Isolate* isolate, Handle<Object> prototype);
        // 源码太长,略。。。
    }
    

    V8 会将 JavaScript 对象编译成 JSObject 的实例,从 JavaScript 层面看来,函数(Function)和对象(Object)的关系是你中有我,我中有你,互相依偎,唇齿相依,如下图:

    ddc10b8659a589ec655c04f3eca1b5ab.png

    从 V8 源码来看,JavaScript 函数是 JSFunction 的实例,JavaScript 对象是 JSObject 的实例,JSObject 是 JSFunction 的父类,所以 JavaScript 函数具备 JavaScript 对象拥有的绝大部分功能,对象能做的事情,函数也可以做,从这个角度也可以理解 JavaScript 的函数是一等公民。

    总结

    C 语言编译器把 C 语言函数编译成机器码,V8 把 JavaScript 函数编译成 C++ 对象,C++ 对象是 C++ 世界中当之无愧的一等公民,JavaScript 函数当然也是一等公民。

    展开全文
  • HashTable是v8中哈希表的实现,HashTable继承Array。HashTable提供一些公共的逻辑,供后续子类使用。我们看一下他 大内存布局。 然后看一下类的定义。HashTable是个模板类,prefix_size是,element_size是哈希表中...
  • 科普 | 编译 V8 源码

    2017-03-12 20:31:00
    2017-02-13justjavac象尘说 对于JavaScript程序员来说,可以瞧一瞧justjavac给大家写的科普类读物,V8引擎的分析,“也许你不懂C++”,但是你可以了解一下,总是好的。 本系列得到了justjavac的...V8源码已...
  • Dictionary我们应该不陌生,他是HashTable的基类。我们看一下定义 // 字典基类,prefix大小为两个指针元素,数组中每个元素大小是3个指针 class DictionaryBase: public HashTable<2, 3> {};...
  • V8 JavaScript Engine V8 is Google's open source JavaScript engine. V8 implements ECMAScript as specified in ECMA-262. V8 is written in C++ and is used in Google Chrome, the open source browser from ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,384
精华内容 553
关键字:

v8源码