该版本修复了zenglrun_makeInfoString函数(定义在zenglrun_func.c文件中)和zengl_makeInfoString函数(定义在zengl_main.c文件中)在生成格式化字符串时,如果格式化的字符串较长时,在64位gcc和clang中会出现乱码或者段错误的Bug...

    页面导航: 项目下载地址:

    zengl language v1.7.4 源代码的相关地址:https://github.com/zenglong/zengl_language/  对应的tag标签为:v1.7.4

zengl v1.7.4:

    该版本修复了zenglrun_makeInfoString函数(定义在zenglrun_func.c文件中)和zengl_makeInfoString函数(定义在zengl_main.c文件中)在生成格式化字符串时,如果格式化的字符串较长时,在64位gcc和clang中会出现乱码或者段错误的Bug:

/*生成格式化信息字符串*/
ZL_CHAR * zenglrun_makeInfoString(ZL_VOID * VM_ARG,ZENGL_RUN_INFO_STRING_TYPE * infoStringPtr , ZL_CONST ZL_CHAR * format , ZENGL_SYS_ARG_LIST arglist)
{
	ZENGL_RUN_TYPE * run = &((ZENGL_VM_TYPE *)VM_ARG)->run;
	ZENGL_SYS_ARG_LIST tmp_arglist;
	ZL_INT retcount = -1;
	ZENGL_SYS_ARG_END(tmp_arglist);
	if(infoStringPtr->str == ZL_NULL)
	{
		infoStringPtr->size = ZL_INFO_STRING_SIZE;
		infoStringPtr->str = run->memAlloc(VM_ARG,infoStringPtr->size * sizeof(ZL_CHAR),&infoStringPtr->mempool_index);
	}
	do
	{
		// 在64位系统中, GCC和clang会将va_list指向某种特殊的结构,该结构中存储了可变参数位置信息,
		// 直接将va_list传递给vsnprintf的话,相当于将特殊结构的指针传递过去,vsnprintf根据指针,会修改该结构指向的参数位置,那么下一次再次执行
		// vsnprintf时,虽然va_list指针没变化,但是va_list对应的结构,实际指向的可能是一个无效的参数。因此,需要先使用va_copy生成一个拷贝,
		// 再将va_list的拷贝(包含了特殊结构的拷贝)传递给vsnprintf,
		// 32位系统,不需要va_copy也可以正常工作,因为32位系统中的va_list是一个简单的栈指针,直接指向了可变参数位置,
		// 32位中,va_list传递给vsnprintf时,本身就是以栈指针拷贝的方式传递的,所以32位系统中没有va_copy也可以正常工作,
		// 不过,为了让代码能够在32位和64位中都正常运作,就统一使用va_copy的方式来处理,在VS2008之类的没有
		// va_copy的环境中,会将va_copy定义为memcpy
		ZENGL_SYS_ARG_COPY(tmp_arglist, arglist);
		retcount = ZENGL_SYS_SPRINTF_ARG_NUM((infoStringPtr->str + infoStringPtr->cur),
											 (infoStringPtr->size - infoStringPtr->count),format,tmp_arglist);
		ZENGL_SYS_ARG_END(tmp_arglist);
		if(retcount >= 0 && retcount < (infoStringPtr->size - infoStringPtr->count))
		{
			infoStringPtr->count += retcount;
			infoStringPtr->cur = infoStringPtr->count;
			infoStringPtr->str[infoStringPtr->cur] = ZL_STRNULL;
			return infoStringPtr->str;
		}

		infoStringPtr->size += ZL_INFO_STRING_SIZE;
		infoStringPtr->str = run->memReAlloc(VM_ARG,infoStringPtr->str,infoStringPtr->size * sizeof(ZL_CHAR),&infoStringPtr->mempool_index);
	} while(ZL_TRUE);
	return ZL_NULL;
}


    以及修复zenglrun_InstDataStringPoolAdd函数(定义在zenglrun_func.c文件中),在添加的指令操作数是字符串,且字符串的长度较长时,会出现的字符串截断和发生ZL_ERR_RUN_INST_DATA_STR_POOL_ADD_I_OUT_OF_BOUNDS错误的Bug:

/*
	指令操作数字符串池添加字符串
*/
ZL_INT zenglrun_InstDataStringPoolAdd(ZL_VOID * VM_ARG , ZL_CHAR * src)
{
	ZENGL_RUN_TYPE * run = &((ZENGL_VM_TYPE *)VM_ARG)->run;
	ZL_INT len;
	ZL_INT i,j;
	if(!run->InstData_StringPool.isInit)
		run->initInstDataStringPool(VM_ARG);
	if(src == ZL_NULL)
		return -1;
	len = ZENGL_SYS_STRLEN(src);
	// 这里使用while循环语句,循环增加size,直到size能够满足字符串的大小时才跳出循环
	// 之前用的if语句,size只会增加一次,从而导致指令操作数中,较长的字符串会被截断,
	// 并发生 ZL_ERR_RUN_INST_DATA_STR_POOL_ADD_I_OUT_OF_BOUNDS 错误
	while(run->InstData_StringPool.count >= run->InstData_StringPool.size ||
		run->InstData_StringPool.count + len + 1 > run->InstData_StringPool.size)
	{
		run->InstData_StringPool.size += ZL_R_INST_DATA_STRING_POOL_SIZE;
		run->InstData_StringPool.ptr = run->memReAlloc(VM_ARG,run->InstData_StringPool.ptr,
												run->InstData_StringPool.size * sizeof(ZL_CHAR),
												&run->InstData_StringPool.mempool_index);
		ZENGL_SYS_MEM_SET(run->InstData_StringPool.ptr + (run->InstData_StringPool.size - ZL_R_INST_DATA_STRING_POOL_SIZE),0,
						ZL_R_INST_DATA_STRING_POOL_SIZE * sizeof(ZL_CHAR));
	}
	for(i=run->InstData_StringPool.count,j=0;
		i<run->InstData_StringPool.size && j<len;i++,j++)
	{
		run->InstData_StringPool.ptr[i] = src[j];
	}
	if(i >= run->InstData_StringPool.size)
		run->exit(VM_ARG,ZL_ERR_RUN_INST_DATA_STR_POOL_ADD_I_OUT_OF_BOUNDS);
	else
		run->InstData_StringPool.ptr[i] = ZL_STRNULL;
	i = run->InstData_StringPool.count;
	run->InstData_StringPool.count += len + 1;
	return i;
}


    在zengl_main函数里(定义在zengl_main.c中),添加检测errorFullString错误信息的代码:

/*编译器入口函数*/
ZL_INT zengl_main(ZL_VOID * VM_ARG,ZL_CHAR * script_file,ZENGL_EXPORT_VM_MAIN_ARGS * vm_main_args)
{
	..........................................
	compile->KeywordCompleteDetect(VM_ARG);
	compile->buildAST(VM_ARG);		//组建AST抽象语法树
	compile->buildSymTable(VM_ARG); //组建符号表
	compile->buildAsmCode(VM_ARG);	//组建汇编代码
	compile->LDAddrListReplaceAll(VM_ARG); //将所有的伪地址替换为真实的汇编代码位置,从而完成链接工作

	/**
	 * 在编译过程中,如果调用了解释器的函数时,例如buildAsmCode组建汇编代码,向解释器写入虚拟汇编指令时
	 * 如果发生错误,是不会立即退出的,只会将错误信息写入到errorFullString中,因此,这里,如果检测到
	 * 错误,就退出编译器,并将错误信息传递给用户自定义函数(如果定义了的话)
	 */
	if(compile->errorFullString.str != ZL_NULL && compile->errorFullString.count > 0)
	{
		compile->exit(VM_ARG, ZL_ERR_RUN_ERROR_EXIT_WHEN_IN_COMPILE);
	}

end:

	..........................................
}


    当编译器调用解释器的方法发生错误时,虚拟机如果是以zenglApi_Run的方式运行时,并不会立即退出,而只是将错误信息写入到errorFullString中,因此,需要在上面的zengl_main函数中添加相关的检测代码,这样当发生ZL_ERR_RUN_INST_DATA_STR_POOL_ADD_I_OUT_OF_BOUNDS之类的错误时,就能够及时退出编译器,并将错误信息显示出来。

    测试脚本:test_scripts/v1.7.4/test.zl:

use builtin;

contents = '
年代:魏晋  作者:陶渊明

《归园田居 其一》

少无适俗韵,性本爱丘山。
误落尘网中,一去三十年。(误落 一作:误入)
羁鸟恋旧林,池鱼思故渊。
开荒南野际,守拙归园田。
方宅十余亩,草屋八九间。
榆柳荫后檐,桃李罗堂前。
暧暧远人村,依依墟里烟。
狗吠深巷中,鸡鸣桑树颠。(颠 通 巅)
户庭无尘杂,虚室有余闲。
久在樊笼里,复得返自然。 

《归园田居 其二》

野外罕人事,穷巷寡轮鞅。
白日掩荆扉,虚室绝尘想。
时复墟曲中,披草共来往。(墟曲中 一作:墟曲人)
相见无杂言,但道桑麻长。
桑麻日已长,我土日已广。
常恐霜霰至,零落同草莽。 

《归园田居 其三》

种豆南山下,草盛豆苗稀。
晨兴理荒秽,带月荷锄归。
道狭草木长,夕露沾我衣。
衣沾不足惜,但使愿无违。

《归园田居 其四》

久去山泽游,浪莽林野娱。
试携子侄辈,披榛步荒墟。
徘徊丘垄间,依依昔人居。
井灶有遗处,桑竹残朽株。
借问采薪者,此人皆焉如?
薪者向我言,死没无复余。
一世异朝市,此语真不虚。
人生似幻化,终当归空无。

《归园田居 其五》

怅恨独策还,崎岖历榛曲。
山涧清且浅,遇以濯吾足。
漉我新熟酒,双鸡招近局。
日入室中暗,荆薪代明烛。
欢来苦夕短,已复至天旭。

《饮酒 其一》

衰荣无定在,彼此更共之。
邵生瓜田中,宁似东陵时!
寒暑有代谢,人道每如兹。
达人解其会,逝将不复疑;
忽与一樽酒,日夕欢相持。

《饮酒 其二》

积善云有报,夷叔在西山。
善恶苟不应,何事空立言!
九十行带索,饥寒况当年。
不赖固穷节,百世当谁传。

《饮酒 其三》

道丧向千载,人人惜其情。
有酒不肯饮,但顾世间名。
所以贵我身,岂不在一生?
一生复能几,倏如流电惊。
鼎鼎百年内,持此欲何成!

《饮酒 其四》

栖栖失群鸟,日暮犹独飞。
徘徊无定止,夜夜声转悲。
厉响思清远,去来何依依。
因值孤生松,敛翮遥来归。
劲风无荣木,此荫独不衰。
托身已得所,千载不相违。

《饮酒 其五》

结庐在人境,而无车马喧。
问君何能尔?心远地自偏。
采菊东篱下,悠然见南山。
山气日夕佳,飞鸟相与还。
此中有真意,欲辨已忘言。(辨 通:辩) 

《桃花源诗》

嬴氏乱天纪,贤者避其世。
黄绮之商山,伊人亦云逝。
往迹浸复湮,来径遂芜废。
相命肆农耕,日入从所憩。
桑竹垂馀荫,菽稷随时艺;
春蚕收长丝,秋熟靡王税。
荒路暧交通,鸡犬互鸣吠。
俎豆独古法,衣裳无新制。
童孺纵行歌,班白欢游诣。
草荣识节和,木衰知风厉。
虽无纪历志,四时自成岁。
怡然有馀乐,于何荣智慧!
奇踪隐五百,一朝敞神界。
淳薄既异源,旋复还幽蔽。
借问游方士,焉测尘嚣外。
愿言蹑清风,高举寻吾契。

《桃花源诗》这篇作品为陶渊明晚年所作。作品以虚构的方式,描绘了一幅没有战乱,没有压迫,没有剥削,人人劳动、平等自由,道德淳朴、宁静和睦的社会生活图景——桃花源,寄托了作者美好的社会理想。这个理想中的美好社会,与当时黑暗的现实社会形成鲜明的对比,从而表现出作者对现实社会的不满和否定,同时在一定程度上也反映了广大人民追求美好生活的愿望。
';

print '\ncontents length:' + bltGetStrLen(contents) + '\n';
print contents;


    当前版本的执行结果如下:

zengl@zengl-ubuntu:~/zengl/zengl_language/linux$ ./zengl test_scripts/v1.7.4/test.zl 
run(编译执行中)...

contents length:3634


年代:魏晋  作者:陶渊明

《归园田居 其一》

少无适俗韵,性本爱丘山。
误落尘网中,一去三十年。(误落 一作:误入)
羁鸟恋旧林,池鱼思故渊。
开荒南野际,守拙归园田。
方宅十余亩,草屋八九间。
榆柳荫后檐,桃李罗堂前。
暧暧远人村,依依墟里烟。
狗吠深巷中,鸡鸣桑树颠。(颠 通 巅)
户庭无尘杂,虚室有余闲。
久在樊笼里,复得返自然。 

《归园田居 其二》

野外罕人事,穷巷寡轮鞅。
白日掩荆扉,虚室绝尘想。
时复墟曲中,披草共来往。(墟曲中 一作:墟曲人)
相见无杂言,但道桑麻长。
桑麻日已长,我土日已广。
常恐霜霰至,零落同草莽。 

《归园田居 其三》

种豆南山下,草盛豆苗稀。
晨兴理荒秽,带月荷锄归。
道狭草木长,夕露沾我衣。
衣沾不足惜,但使愿无违。

《归园田居 其四》

久去山泽游,浪莽林野娱。
试携子侄辈,披榛步荒墟。
徘徊丘垄间,依依昔人居。
井灶有遗处,桑竹残朽株。
借问采薪者,此人皆焉如?
薪者向我言,死没无复余。
一世异朝市,此语真不虚。
人生似幻化,终当归空无。

《归园田居 其五》

怅恨独策还,崎岖历榛曲。
山涧清且浅,遇以濯吾足。
漉我新熟酒,双鸡招近局。
日入室中暗,荆薪代明烛。
欢来苦夕短,已复至天旭。

《饮酒 其一》

衰荣无定在,彼此更共之。
邵生瓜田中,宁似东陵时!
寒暑有代谢,人道每如兹。
达人解其会,逝将不复疑;
忽与一樽酒,日夕欢相持。

《饮酒 其二》

积善云有报,夷叔在西山。
善恶苟不应,何事空立言!
九十行带索,饥寒况当年。
不赖固穷节,百世当谁传。

《饮酒 其三》

道丧向千载,人人惜其情。
有酒不肯饮,但顾世间名。
所以贵我身,岂不在一生?
一生复能几,倏如流电惊。
鼎鼎百年内,持此欲何成!

《饮酒 其四》

栖栖失群鸟,日暮犹独飞。
徘徊无定止,夜夜声转悲。
厉响思清远,去来何依依。
因值孤生松,敛翮遥来归。
劲风无荣木,此荫独不衰。
托身已得所,千载不相违。

《饮酒 其五》

结庐在人境,而无车马喧。
问君何能尔?心远地自偏。
采菊东篱下,悠然见南山。
山气日夕佳,飞鸟相与还。
此中有真意,欲辨已忘言。(辨 通:辩) 

《桃花源诗》

嬴氏乱天纪,贤者避其世。
黄绮之商山,伊人亦云逝。
往迹浸复湮,来径遂芜废。
相命肆农耕,日入从所憩。
桑竹垂馀荫,菽稷随时艺;
春蚕收长丝,秋熟靡王税。
荒路暧交通,鸡犬互鸣吠。
俎豆独古法,衣裳无新制。
童孺纵行歌,班白欢游诣。
草荣识节和,木衰知风厉。
虽无纪历志,四时自成岁。
怡然有馀乐,于何荣智慧!
奇踪隐五百,一朝敞神界。
淳薄既异源,旋复还幽蔽。
借问游方士,焉测尘嚣外。
愿言蹑清风,高举寻吾契。

《桃花源诗》这篇作品为陶渊明晚年所作。作品以虚构的方式,描绘了一幅没有战乱,没有压迫,没有剥削,人人劳动、平等自由,道德淳朴、宁静和睦的社会生活图景——桃花源,寄托了作者美好的社会理想。这个理想中的美好社会,与当时黑暗的现实社会形成鲜明的对比,从而表现出作者对现实社会的不满和否定,同时在一定程度上也反映了广大人民追求美好生活的愿望。

run finished(编译执行结束)
zengl@zengl-ubuntu:~/zengl/zengl_language/linux$ 


    如果在zenglrun_makeInfoString函数中,不使用va_copy拷贝tmp_arglist,而像之前的版本那样,直接使用arglist进行vsnprintf操作的话:

/*生成格式化信息字符串*/
ZL_CHAR * zenglrun_makeInfoString(ZL_VOID * VM_ARG,ZENGL_RUN_INFO_STRING_TYPE * infoStringPtr , ZL_CONST ZL_CHAR * format , ZENGL_SYS_ARG_LIST arglist)
{
	ZENGL_RUN_TYPE * run = &((ZENGL_VM_TYPE *)VM_ARG)->run;
	ZENGL_SYS_ARG_LIST tmp_arglist;
	ZL_INT retcount = -1;
	ZENGL_SYS_ARG_END(tmp_arglist);
	if(infoStringPtr->str == ZL_NULL)
	{
		infoStringPtr->size = ZL_INFO_STRING_SIZE;
		infoStringPtr->str = run->memAlloc(VM_ARG,infoStringPtr->size * sizeof(ZL_CHAR),&infoStringPtr->mempool_index);
	}
	do
	{
		.............................................
		// ZENGL_SYS_ARG_COPY(tmp_arglist, arglist);
		retcount = ZENGL_SYS_SPRINTF_ARG_NUM((infoStringPtr->str + infoStringPtr->cur),
			(infoStringPtr->size - infoStringPtr->count),format,arglist);
		// ZENGL_SYS_ARG_END(tmp_arglist);
		if(retcount >= 0 && retcount < (infoStringPtr->size - infoStringPtr->count))
		{
			infoStringPtr->count += retcount;
			infoStringPtr->cur = infoStringPtr->count;
			infoStringPtr->str[infoStringPtr->cur] = ZL_STRNULL;
			return infoStringPtr->str;
		}

		infoStringPtr->size += ZL_INFO_STRING_SIZE;
		infoStringPtr->str = run->memReAlloc(VM_ARG,infoStringPtr->str,infoStringPtr->size * sizeof(ZL_CHAR),&infoStringPtr->mempool_index);
	} while(ZL_TRUE);
	return ZL_NULL;
}


    在64位gcc和clang的编译环境下,得到的就会是乱码(由于没有对va_list类型的arglist变量执行va_copy操作,64位环境中,当第二次执行vsnprintf时,就会指向错误的参数位置):

zengl@zengl-ubuntu:~/zengl/zengl_language/linux$ ./zengl test_scripts/v1.7.4/test.zl 
run(编译执行中)...

contents length:3634

UH��H���
run finished(编译执行结束)
zengl@zengl-ubuntu:~/zengl/zengl_language/linux$ 


    以上就是当前版本相关的内容,当前版本也只在windows,linux和mac OSX中进行过基本的测试。

结束语:

    每样东西的背后,都有它的故事

——  《夸世代》
 
上下篇

下一篇: zengl v1.8.0 缓存内存中的编译数据,跳过编译过程

上一篇: zengl v1.7.3 获取数组之类的内存块中非NONE成员的数量

相关文章

zengl编程语言 中序

zengl编程语言v0.0.5构建符号表汇编代码和虚拟机

zengl编程语言v0.0.12函数的实现

zengl v1.9.1 函数参数可以使用负数作为默认值

zengl v1.2.4 脚本加密,单目负号,VC6编译,新增API,BUG修复

zengl v1.5.0 移植到zenglOX系统