本文是《google.loader 代码欣赏》系列的一部分。
这段代码原本是非常好读懂的,但是当所有的函数名和变量名都用小写的a到z之间的一个字母代替以后,就变得不好读起来,尤其是这段令人抓狂的代码,
function k(c,a,b,d,e,f){
this.a=c;
this.g=a;
this.f=b;
this.d=d;
this.e=e;
this.b=f
}
不死一堆脑细胞是很难摸得到门路的。
其实,这说明,这个代码一定是经过编译的。说编译可能有点不是很准确,说“转化”或者更恰当些。为什么要这么做呢?
Symbol表
在整个代码的最后一句
g(“google_exportSymbol”,g);
泄露了些原因。
Symbol是什么?在Windows的世界(或者几乎所有的编译语言,或曰高级语言的世界里),都少不了Symbol这个东西。在编译的时候,所有的函数名,变量的名字,都会被一个地址或者一个标记代替,写进二进制文件里面,所以如果从一个.exe文件反编译出来,还是可以看得出执行的语句逻辑的(用汇编的形式),却永远无法获取原来的源代码。而Symbol文件,就是连接编译好的二进制程序,和源代码的桥梁,包含了关于函数,变量的名字以及源代码的文件名以及行数的信息。而Symbol文件编译以后,一般不和执行文件一起发布,需要另外去找。比方说当年调试IIS的时候,就把IIS的Symbol拿过来,再把源代码拿过来,用Windbg就可以开工了。。。这是以前的古老的Windows时代一个程序员要做的事情(对了,现在所有的Windows公开的Symbol可以在这里获得)。Symbol简直就是Windows世界里的解开二进制文件的一把神秘的钥匙。
Google Loader的Symbol库
既然有编译,就一定要有一张自己存着的对应表格。我猜测这个表格应该是这样样子的:
function “google_exportSymbol” => function “g”, @d:\my\code\uds\uds.js line #122
function “setApiKeyLookupMap” => function “p”, @d:\my\code\uds\uds.js line #102
function “load” => “o”, @d:\my\code\uds\uds.js line #77
function “writeLoadTag” => “m”, @d:\my\code\uds\uds.js line #115
var “google_exportSymbol”::”funcNameString” => “c”, @d:\my\code\uds\uds.js line#122
var “google_exportSymbol”::”function” => “a”, @d:\my\code\uds\uds.js line #122
需要说明的是,我没有原来的代码,也彻底不知道这个Symbol表是不是存在,甚至在这里开玩笑一样的假设原来的代码放在一台Windows的机器上面(显然这不是很可能),但我希望就是表达这么一个意思,就是说,如果有编译,就应该附属产生这样的一个Symbol表,只不过没有公布而已。如果没有这个表,debug会是一件和我们直接看源代码一样痛苦的过程。
DLL的exportSymbols
如果大家熟悉Windows的DLL文件结构的话,就会发现exportSymbols和importSymbols是一个常见的概念。就是说,虽说在DLL内部所有的函数的名字已经被编译的面目全非,就像这里的函数g()
,函数q()
一样,但是为了方便外界的调用,还是把一个标准的接口给输出(export)出去。打个比方,一个Windows XP上的c:\windows\system32\user32.dll就export了732个之多的函数(函数名是字符串,比如ActivateKeyboardLayout
,实际的函数确实一个地址为:7E42D32Ah
的EntryPoint。
微软的世界和Google的世界
一个微软的世界的基石应该是PE(Portal Exexcutable)的文件格式(也就是.EXE, .DLL所使用的格式),这些格式让DOS操作系统可以”load”到内存里面执行。
那么在Google的世界里面先出现了一个loader(就好比DOS的loader),然后作为load的基础函数出现了这么一个google_exportSymbols
,这是巧合呢,还是意味着什么呢?
且听下回分解。
注:第二次(其实是不知道多少次)声明,以上纯属个人的主观臆断,就好像听到了“咕咚”的声音就惊呼“咕咚来了”的猴子一样。或许是真的“咕咚”来了,或许仅仅是一个熟了的果子掉到湖里而已。是哪一种?只有时间告诉我们。
有些意思啦….一点一点分析下去…
其实这样的处理在javascript一般就是所谓的packer
(http://dean.edwards.name/packer/)
一般的packer会先做一个jsmin的工作,然后才会有局部变量名称的混淆工作,这样的事对于Java或者.Net算是常见…打开一个jar包…全是a,b,c,d的名称。估计google的这个js也做的类似的处理.不知道用了什么样的工具.不过这样的处理是较容易出问题的.比如dojo中所用的那个packer总是遭人置疑.
(http://www.crockford.com/javascript/jsmin.html)
这里的google_exportSymbol函数,好象用来把内部的函数透过适当的名称空间暴露出来一起…有些javascript包中支持对于库中的函数改名,也是采用同样的方法.
其实不只是windows是如此。
符号表是编译器中常用的技术,不论是什么平台的。
g函数的执行结果是将第一个参数串对应的JS对象赋第二个参数值.
g(“google.load”,o);==>google.load = o;
g(“google.setOnLoadCallback”,q);==>google.setOnLoadCallback=q;
g(“google.loader.writeLoadTag”,m);==>google.loader.writeLoadTag = m;
g(“google.loader.setApiKeyLookupMap”,p);==>google.loader.setApiKeyLookupMap=p;
g(“google_exportSymbol”,g);==>google_exportSymbol=g;
部分函数有了实名,应该更容易看懂了些.
ps:昨天把g函数看错了,汗!
老大不要分析这个了吧
那只是一个混淆工具处理过的结果
function k(c,a,b,d,e,f){
this.a=c;
this.g=a;
this.f=b;
this.d=d;
this.e=e;
this.b=f
}
不用讲的这么神秘吧
说到Symbol,年前我的机器出现过蓝屏,为了查明原因,下载了Symbol,很快就搞定.这里看见你说到Google也应用Sysmbol,说明了Javascript的潜能还有待挖掘!