安全的脚本开发是多数引擎类语言所追求的目标,像java就试图消灭了指针和崩溃的问题,但没有成功。
说道脚本的安全无外乎两大部分,内存指针的使用安全和数据安全,
指针和内存的使用是c语言的一大优势,传递指针其实是数据集中管理的编程理念的体现。
假设一个指针被创建后永远不会被删除,
那么程序内的所有函数都在这块内存内操作数据并传递数据,
假设非常理想的情况下,这样的数据管理的效率将是最高的。
任何数据都不会存在复制传递的开销,
也不会有多个数据copy的一致性问题。
以java为例消灭指针之后也并没有什么不便利的地方,因为java本来就不是针对大数据流优化的语言,
对于new出来的对象在没有使用之后自动释放即可。
但引擎类的脚本就不能如此的简单,因为脚本不能预判要释放的对象,
所以要对数据做统一的管理和限制。
那么不得不说即便消灭了指针java也不是安全的语言因为经常会出现数据越界的情况,
例如api参数规定是1到5,输入6就会不知道该如何处理而程序崩溃。
这类问题也多半是可以处理但要看开发成本,每个变量逻辑数值的边界是更具体逻辑息息相关,
质量控制能到这样的地步是很不容易的事情。还有些状况是无法处理的例如输入asic字符串却
错误的输入了uft8的编码,这样造成的乱码和api的崩溃是通过代码检查是很难做到的,只能在
逻辑结构上加以预防。
总的来说脚本的安全指针只是原因之一,数据越界,数值错误这类问题是引擎无法解决的,
或者解决起来成本远远大于普通的开发成本。
对于脚本来说还有一大天敌就是异步接口,所谓异步接口就是被调用的接口功能的完成在另一个
线程,导致当前线程所使用的数据出现严重的逻辑错误。
假设有一个join函数功能是加入某个家族,这个函数是一个异步函数,也就是函数调用之后就会立即返回,
真正的加入功能是在另一个线程内完成的。
void fun() { ... join(); ... }
通常会使用回调函数的方式或者事件等通知的方式告知是否加入成功。
如果存在另一个函数判断是否在家族内isin()就保不齐会设计出这样的代码
void fun() { while(isin()) { join(); } }
这种异步数据的逻辑错误也是引擎无能为力的,
会空载大量的引擎资源而且不仔细检查代码还很难发现。
虽然可以对join加上报警或者日志,
如果调用错误过多,像上述代码就会有大量加入已经加入的家族导致加入失败的错误。
如果只是单纯的set数值就会产生大量重复的set。
上述错误是在脚本调用异步函数的数据问题,
做为异步调用本身也存在的极大的风险,
虽然事件或者通知的概念很诱人,但事件本身就把逻辑程序拆分的支离破碎。
例如一段程序加入家族并且发送礼包异步函数
void fun() { join() } void eventjoin() { givebox() }
void fun() { if(join()) { givebox() } }
这时会想到linux下的fork()语句,奇葩的运行在两个进程空间但逻辑可读并且看起来连续。
多线程引擎的存在就是力图解决在脚本中这种异步函数调用所引起的逻辑问题,
虽然现在看起来还没有什么更好的办法。