您的位置:首页 > Web前端 > JavaScript

2. javascript 引擎Rhino源代码分析 简单代码分析

2015-11-08 21:30 609 查看
1. 简介
        本例子通过执行最简单的javascript:   var result = 1;
            来分析Rhino如何解析,转换,编译,执行。。。
  2. 基本测试代码

public static void main(String[] args){
//Context 用来存储对应线程的数据,一个线程只对应唯一的context
Context ctx = Context.enter();
//scope表示一组javascript对象,保存执行javascript需要的全部标准对象和全局函数
Scriptable scope = ctx.initSafeStandardObjects();
//设置js优化级别,有-1到9,其中 -1表示直接解释执行;
ctx.setOptimizationLevel(-1);
try{
//传入并执行javascript代码
ctx.evaluateString(scope, "var result=1;", "", 1, null);
}catch(Exception e){
e.printStackTrace();
}finally{
Context.exit();
}
}


    3.  AST抽象语法树转换过程

        解析器解析及转换AST: 

        

//具体见Context.compileImpl()方法;
Parser p = new Parser(compilerEnv, compilationErrorReporter);
AstRoot ast = p.parse(sourceString, sourceName, lineno);
        


       4. IRFactory将ast语法树转换成为内部表现形式

       ast语法树转换成为IR内部表现形式代码: 

//具体见Context.compileImpl()方法;
IRFactory irf = new IRFactory(compilerEnv, compilationErrorReporter);
ScriptNode tree = irf.transformTree(ast);

       主要转换核心代码在irf.transformTree(ast);:

        

switch (node.getType()) {
case Token.SCRIPT:
return transformScript((ScriptNode)node);
case Token.NAME:
return transformName((Name)node);
case Token.NUMBER:
return transformNumber((NumberLiteral)node);
...
...
}


        最终 转换后的结果存放在Decompiler的sourceBuffer数组:

        


    5. 编译生成byteCode虚拟字节码

    主要生成byteCode字节码:

    

//具体见Context.compileImpl()方法;
Object bytecode = compiler.compile(compilerEnv,tree, tree.getEncodedSource(),returnFunction);


    转换后的虚拟字节码:

MaxStack = 2
[0] LINE : 1
[3] REG_STR_C0
[4] BINDNAME
[5] ONE
[6] REG_STR_C0
[7] SETNAME
[8] POP
[9] RETURN_RESULT
注: 以上格式为   [pc计数器]   指令码

    6. 解释器顺序执行byteCode指令

        6.1  代码分析

    6.1.1 Context的evaluateString()方法执行上述解析,编译为字节码后,通过script.exec()方法解释执行

    

Script script = compileString(source, sourceName, lineno,securityDomain);
if (script != null) {
return script.exec(this, scope);
} else {
return null;
}
              script实例主要数据有:

               itsMaxStack: 当前script最大栈深

itsICode:  存储虚拟指令的字节数组

itsStringTable: 变量表字符串数组,当前例子中  itStringTable[0] = "result"

         6.1.2  解释器通过Interpreter.interpret()循环执行指令

        

private static Object interpretLoop(Context cx, CallFrame frame, Object throwable){
...
for...
switch (op) {
...
}
}


6.2 栈桢分析:

        接下来详细讲述Rhino如何循环执行上面的指令数组

初始化栈桢前:

        


初始化栈桢如下所示:



        6.2.1  [0] LINE : 1

             从指令节中取2个字节,并转为int(行码) 

   其处理代码:

  

case Icode_LINE :
frame.pcSourceLineStart = frame.pc;
if (frame.debuggerFrame != null) {
int line = getIndex(iCode, frame.pc);
frame.debuggerFrame.onLineChange(cx, line);
}
frame.pc += 2;
continue Loop;
当前栈桢示意图:

   


    6.2.2 [3] REG_STR_C0

          将变量表第0个数组(result变量名)保存到stringReg字符串中

其处理代码:

case Icode_REG_STR_C0:
stringReg = strings[0];
continue Loop;
栈桢示意图:



6.2.3  [4] BINDNAME

从scope作用域取出名为stringReg的Scriptable,压入栈顶stack[] 如果当前scope找不到,会到当前scope的prototype链上找,可见: ScriptRuntime.bind()方法;

其处理代码:

   

case Token.BINDNAME :
stack[++stackTop] = ScriptRuntime.bind(cx, frame.scope, stringReg);
continue Loop;


栈桢示意图:



6.2.4  [5] ONE

将DOUBLE_MARK标记压入栈顶,并将值 1 存入 sDbl数组中



6.2.5 [6] REG_STR_C0

同 6.2.2



6.2.6 [7] SETNAME

包含STRICT与非STRICT模式,弹出并取出栈顶对象,如果为DOUBLE_MARK,取出并转为数值,再从栈顶取一个对象,通过ScriptRuntime.setName()赋值给result.



6.2.7  [8] POP

从栈顶弹出一个对象



6.2.8 [9] RETURN_RESULT



作者:  阿钿
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息