zengl v1.2.1的版本,该版本修复v1.2.0中的函数调用的BUG,v1.2.0中当函数定义在函数调用之后时,函数调用就找不到正确的函数入口地址,该版本通过增加ZL_R_DT_LDFUNID指令操作数类型来解决此BUG...

    zengl v1.2.1的版本,该版本修复v1.2.0中的函数调用的BUG,v1.2.0中当函数定义在函数调用之后时,函数调用就找不到正确的函数入口地址,该版本通过增加ZL_R_DT_LDFUNID指令操作数类型来解决此BUG,函数调用时会先在指令中存放函数ID,在链接替换时再将函数ID替换为伪地址,最后再将伪地址替换为汇编的真实位置。另外增加一个API接口函数:zenglApi_Stop,用户可以通过此接口在中途停止脚本。最后将所有代码转为unix换行符,以适应github

    github地址:https://github.com/zenglong/zengl_language/  在该页面右侧有个Download Zip按钮可以下载源码,或者可以通过Fork来copy一份,github提供了强大的在线源码浏览功能,可以直接在浏览器中查看源代码。

    百度盘地址:http://pan.baidu.com/share/link?shareid=130544612&uk=940392313 该页面是一个rar压缩包,其实是作者在上面的Download Zip按钮下载下来的压缩包解压后再重命名打包为rar格式,所以和github中的代码没有区别。

    sourceforge的项目地址:https://sourceforge.net/projects/zengl/files/  访问该地址可以看到zengl_lang_v1.2.1.rar的压缩包

    对于linux用户按照linux_gcc目录中的usage.txt文档说明,先make clean,再make即可。

    对应windows用户,需要进入windows_vs2008目录下的zenglrun或zengl中,运行其中的zenglrun.vcproj或zengl.vcproj ,再将zenglrun测试项目设为启动项目,将zenglrun的调试命令参数设为test.zl,接着运行即可。(因为github过滤了很多vs2008的文件,所以zengl_lang_v1.2.1.sln双击无效果!)

    可以将该版本的test.zl测试脚本放到上一个版本v1.2.0中,运行一下,就可以发现BUG(此BUG是在将zengl嵌入到采集器时发现的),你会发现脚本无限循环下去,就是因为函数调用时的跳转地址没找对造成的。在test.zl中有如下代码:

clsTestOther.testOther();

class clsTestOther
    m;
    n;
    fun testOther()
        print 'i am in testOther';
        print 'hello zengl';
    endfun
endclass

    testOther这个类函数定义在函数调用语句clsTestOther.testOther()的下方时,就会出现这个问题,当函数或类函数定义在函数调用语句的上方时就不会有这个问题,原因需要查看zengl_assemble.c中的源代码:

ZL_VOID zengl_AsmGenCodes(ZL_VOID * VM_ARG,ZL_INT nodenum)
{
............................ //省略N行代码
    case ZL_ST_ASM_CODE_INFUNCALL: //如果是函数调用,就输出相应的汇编代码
............................ //省略N行代码
        else
                    run->AddInst(VM_ARG,compile->gencode_struct.pc++,nodenum,
                        ZL_R_IT_JMP,ZL_R_DT_NONE,0,
                        ZL_R_DT_LDADDR,compile->SymFunTable.funs[tmpFunID].LDAdr
); //对应汇编指令 类似 "JMP adr%d" 如果找到函数的信息 ..........
        break;
............................ //省略N行代码
}

    在上面的代码生成funcall函数调用时,是通过生成JMP跳转指令来跳转到目标函数的位置,而JMP指令在v1.2.0版本中使用的是函数表中该函数的LDAdr伪地址,而这个伪地址只有在生成fun函数的汇编指令时才会根据实际情况生成,当函数定义在函数调用的前方时,会先生成函数定义的汇编代码,同时产生正确的伪地址,这样在生成funcall函数调用的JMP指令时就可以得到正确的跳转伪地址,而当函数定义在函数调用后面时,因为在生成funcall时,该函数还没生成汇编代码,也就没有正确的伪地址,所以就会导致跳转出错。

    解决方法是在生成funcall函数调用的JMP跳转指令时,不直接使用伪地址,而是使用函数的ID值(每个函数的ID值都是唯一的),在链接器进行替换时,当发现函数ID时,就先将函数ID替换为正确的伪地址(因为在链接器工作时,所有的汇编工作已经完成了,所有函数的伪地址都有了),再将伪地址替换为真实的汇编位置,这样就不会出现跳转出错的情况:

    zengl_assemble.c中的修改情况如下:

ZL_VOID zengl_AsmGenCodes(ZL_VOID * VM_ARG,ZL_INT nodenum)
{
............................ //省略N行代码
    case ZL_ST_ASM_CODE_INFUNCALL: //如果是函数调用,就输出相应的汇编代码
............................ //省略N行代码
        else
                   run->AddInst(VM_ARG,compile->gencode_struct.pc++,nodenum,
                        ZL_R_IT_JMP,ZL_R_DT_NONE,0,
                        ZL_R_DT_LDFUNID,tmpFunID); //对应汇编指令 类似 "JMP funid%d" 如果找到函数的信息 ..........
        break;
............................ //省略N行代码
}

    zengl_ld.c中的修改情况如下:

/*
    将所有的伪地址替换为真实的汇编代码位置,从而完成链接工作
*/

ZL_VOID zengl_LDAddrListReplaceAll(ZL_VOID * VM_ARG)
{
............................ //省略N行代码

        for(j=1;j<=2;j++)
        {
            if(j==1)
                op_data = &run->inst_list.insts[i].dest;
            else
                op_data = &run->inst_list.insts[i].src;
            
            if(op_data->type == ZL_R_DT_LDFUNID) //如果是函数ID,则将函数ID先转为该函数的伪地址,再由该伪地址得到真实的汇编位置
            {
                op_data->val.num = compile->SymFunTable.funs[op_data->val.num].LDAdr;
                op_data->type = ZL_R_DT_LDADDR;

            }
            if(op_data->type == ZL_R_DT_LDADDR)
            {
                if(compile->LDAddrList.addr[op_data->val.num].isvalid == ZL_FALSE)
                    compile->exit(VM_ARG,ZL_ERR_CP_LD_INVALID_ADDR_INDEX,op_data->val.num,op_data->val.num);
                op_data->val.num = compile->LDAddrList.addr[op_data->val.num].loc;
                op_data->type = ZL_R_DT_NUM;
            }
        }

............................ //省略N行代码
}

    最后还增加了一个zenglApi_Stop的API接口。用户可以通过此接口在中途停止脚本,作者在做采集器时,采集器所嵌入的zengl脚本在运行时会进行抓包工作,有时抓包时间太长时,用户可能需要中途停止脚本运行,就可以通过此接口来实现,其实就是给解释器添加一个isUserWantStop的成员,在该接口中将isUserWantStop设为ZL_TRUE,那么在循环执行汇编指令时,发现该isUserWantStop成员为ZL_TRUE了就会跳出汇编指令循环。

    zenglApi.c中的修改情况如下:

/*API接口,用户可以通过此接口在中途停止脚本*/
ZL_EXPORT ZL_EXP_VOID zenglApi_Stop(ZL_EXP_VOID * VM_ARG)
{
    ZENGL_RUN_TYPE * run = &((ZENGL_VM_TYPE *)VM_ARG)->run;
    if(VM_ARG == ZL_NULL)
        return;
    if(run->isDestroyed)
        return;
    if(run->isinRunning)
    {
        run->isUserWantStop = ZL_TRUE;
    }
}

    另外,github对dos换行符支持不好,所以将所有代码转为unix换行符。

    OK,到这里,休息,休息一下 o(∩_∩)o~~
上下篇

下一篇: zengl v1.2.2 正式发布版及智能采集器源代码

上一篇: zengl v1.2.0 嵌入式编程语言

相关文章

zengl v1.4.1 及zengl_SDL新增的Android工程

zengl编程语言v0.0.11实现循环控制结构

zengl v1.9.0 增加self特殊类名,增加zenglApi_SetDefLookupHandle等接口

zengl编程语言v0.0.8第二代语法解析函数

开发自己的编程语言-zengl编程语言v0.0.1词法扫描器的实现

zengl编程语言v0.0.23,SDL游戏开发,类定义