Archive

Archive for December, 2012

[摘要] The V8 Myth: Why JavaScript is not a Worthy Competitor

December 25th, 2012 No comments

本文是内容摘要。原文见这里。作者的主页在这里

以下是摘要:

  • JavaScript是无类型的(untyped)语言,代码规模相对较小,以源代码形式发布,在运行动态编译;ActionScript是有类型的(typed),代码规模较大,在编译后以二进制字节码的形式发布。
  • JavaScript在语言本质上的一些特性导致了它不适合用来开发重量级的应用。清醒一些吧。
  • 动态分析是静态分析的很好的补充,但是无法完全替代静态分析。ActionScript代码经过一个AOT(Ahead-of-Time)编译器充分的分析和优化,而JavaScript的即时编译器无法承受这种优化的开销。
  • 你大概听说V8(一个新的JavaScript VM)使用了“类型推断”技术,但是你可能不知道一个JIT是无法承受AOT使用的 modular analysis 所带来的开销的。
  • 我粗略的测试了一下ActionScript工具链和JavaScript工具链。前者得到的代码速度是后者的三倍以上。当然这个比较不够严谨。
  • 所以,不要操心JavaScript了,来关注ActionScript吧,这个才是21世纪最值得拥有的脚本语言。

摘要结束

原文作者原先是美国马里兰大学程序语言研究组的研究人员,后来加入了Adobe公司程序语言研究小组。这篇博客写于2012年1月,距今差不多快1年——对于互联网技术而言是很长的一段时间。文章很短,评论很多,语言之间的对比和吐槽总是很有人气 🙂

今天看来,文中提到的JavaScript的一些问题依然存在,相信其中还有一些研究的机会,博主已经看到了一些相关的研究成果发表了。有兴趣研究的同学要抓紧时间了 🙂

Project Idea: 增强C++ IDE的宏展开能力

December 23rd, 2012 No comments

这个想法源自前天在阅读IonMonkey代码时候寻找UnrootedScript类型定义的时候。UnrootedScript这个类型的名字是用C++中的宏定义展开生成的。这种利用宏拼接字符串的技术在编译器的代码中比较的常见。例如编译器的设计和实现中,一般都会有一个或多个代码的中间表示(IR),每个中间表示都由若干种不同的节点组成。这些节点的实例构成程序的抽象表示。编译器在生成机器码的过程中需要访问和遍历抽象表示中的节点。对于每个节点,往往需要有一个 is_NODENAME() 或 visit_NODENAME() 类似的函数,这类函数写起来费时费力,并且当中间表示的数据结构修改时需要同步的修改代码中多处地方,这种关联应当尽量避免。

宏定义中字符串拼接的定义和代码实例见GNU的C预处理器文档3.5节“Concatenation”[1]。

Eclipse/CDT 中已经内建了对于宏定义展开的部分支持[2]。以下面的代码为例:

在这段代码中,变量“cd”和变量“abcd”都是通过宏拼贴技术生成的。拼贴变量“cd”的宏INT被包含在一个“#ifdef”条件语句中,而拼贴出变量“abcd”的宏INT2没有。对于宏INT2这样的确定性的宏展开,Eclipse/CDT能够识别出来“abcd”变量是由INT2定义的,并在鼠标移动至对应变量(“abcd”)上面时给出相关的提示;对于包含在预处理条件语句中的宏定义而言,Eclipse/CDT会尝试分析条件跳转语句中的真值,如果可以静态的确定“#ifdef”语句的结果(如上例所示),Eclipse/CDT也会同样的给出定义的提示。

这已经非常的智能了。但是当移除掉上例中第二行的“#define DEBUG”语句之后,Eclipse/CDT就不能够确定DEBUG是否在别处被定义过,因此就无法给出“cd”变量的定义信息。这也就是导致之前Eclipse无法提示出“UnrootedScript”类型定义的原因。

为了解决这个问题,我的想法是在CDT目前的“准确”推断提示系统的基础上,添加一个“可能”推断提示系统,在遇到类型定义的时候记录下来,当“准确”提示系统无法得到定义信息的时候就将“可能”提示系统的定义信息(集合)展示给用户。

进一步的,“可能”推断提示系统可以通过用户的点击反馈来“反向”的推断和求解阻碍“精确”推断提示系统的变量(表达式)的值,从而进一步的改善提示系统的提示能力。例如在上例中,移除第二行的“#define DEBUG”语句之后,“可能”推断提示系统记录了INT宏的两个不同的定义,并在处理第16行INT宏引用时,同时计算出来“cd”和“dc”两种变量定义的结果,在第17行时就可以给出“cd”的定义值了。当用户点击了“cd”的定义提示浮窗时,推断提示系统就可以推断“DEBUG”宏已经被定义,进而可以确定宏CONCAT是第五行的定义结果。

[1]: http://gcc.gnu.org/onlinedocs/cpp/Concatenation.html

[2]: http://www.eclipse.org/recommenders/

UnrootedScript in SpiderMonkey

December 19th, 2012 No comments

在 SpiderMonkey 中,JSScript 是比较常见的一个数据结构,它封装了一个 JavaScript 脚本。而 UnrootedScript 是在 SpiderMonkey 代码中经常作为函数返回值出现的类型。奇怪的是,使用 Eclipse IDE 找不到该类型的定义;使用 grep 搜索也只能找到该类型的使用,找不到该类型的定义(这个类型的使用非常广泛,眼睛都看花了);直接使用 Google 搜索,也找不到这个类型。

最后,在 Mozilla 的 Bugzilla 上看到 bug 817818,才意识到:这个类型是用宏定义拼出来的。

以下这段代码来自于 Mozilla-central/js/src/vm/Root.h:

在 Mozilla-central/js/src/JSScript.h 文件中,有一行对应的宏引用:

通过这种方式完成了 UnrootedScript 的定义。
类似的定义还有:

生成了以下类型:

评注:以前在 GCC 的代码中也见到了不少这样的技巧,用 C/C++ 中的宏拼出来很多的代码,使得源代码看起来简洁,代码行数更少。但是要看懂这样的代码也需要更多的耐心和技巧,对于初学者而言不是很友好。内部实现的文档稀缺,加之目前IDE中的智能提示系统(CCS)还搞不定这么复杂的宏展开,使得初学者的学习门槛更高了。

如何得到SpiderMonkey引擎的字节码(bytecode)

December 5th, 2012 No comments

最简单的方法是用SpiderMonkey[4]自带的jsshell[1]工具。使用debug模式编译之后,通过“-D”参数就可以获得JavaScript脚本对应的bytecode了。示例(假设你编译的目录是build-debug):

得到的结果如下:

— SCRIPT tests/js1_8_5/shell.js:1 —
00000: 10 getgname “version”
{“interp”: 1}
00005: 10 typeof
{“interp”: 1}
00006: 10 string “undefined”
{“interp”: 1}
00011: 10 ne
{“interp”: 1}
00012: 10 ifeq 32 (+20)
{}
00017: 12 callgname “version”
{“interp”: 1}
00022: 12 undefined
{“interp”: 1}
00023: 12 notearg
{“interp”: 1}
00024: 12 uint16 185
{“interp”: 1}
00027: 12 notearg
{“interp”: 1}
00028: 12 call 1
{“interp”: 1}
00031: 12 pop
{“interp”: 1}
00032: 12 stop
{“interp”: 1}
— END SCRIPT tests/js1_8_5/shell.js:1 —

注意只有debug模式才会输出,release/optimize模式的jsshell会忽略该选项。

可以通过Mozilla的wiki学习如何下载[2]和编译[3]源代码。

[1]: Introduction_to_the_JavaScript_shell

[2]: Getting_SpiderMonkey_source_code

[3]: SpiderMonkey/Build_Documentation

[4]: JavaScript:New_to_SpiderMonkey

 

 

 

 

 

SpiderMonkey内部的字符串表示

December 4th, 2012 No comments

在SpiderMonkey的代码中经常能够看到JSAtom这一个数据结构。它并不是定义在 js/src/jsatom.h 中,而是在js/src/vm/String.h中。SpiderMonkey为了能够快速的实现字符串的复制、比较操作,使用了一系列的C++对象。具体实现在String.h的注释中有描述: