掌握V8的架构和使用过程
V8内部架构相当复杂,但是使用还是很方便的,因为V8把所有功能实现封装到内部的命名空间中,只给使用者提供了相当简洁的外部封装类,这些类都在一个叫做v8的命名空间中,全局函数封装在V8命名空间中(注意大小写有别)。V8引擎使用上的便捷性体现在多个方面:
l 大量采用模板类,统一了所有脚本对象的内存管理和垃圾回收。
l 所有脚本对象不允许直接在堆或栈上构造,这通过私有的构造函数体现出来。对象的创建都使用静态的New方法,对象的类型转换都使用CAST方法。
l 所有对象通过Handle<>模板类的封装来自动管理对象的生命周期,Handle分成Local和Persistent两种。Local在内部栈上管理对象,一旦离开栈作用范围(Scope),这些对象被自动释放。Persistent管理所有永久对象,对象被自动加上引用计数,一旦引用计数为0,永久对象将被放入垃圾回收队列,在合适的时候被系统自动回收。
l 提供了足够使用的回调函数机制来控制V8的运行,通过设置对象模板的访问函数和拦截函数来使用自定义功能。
l 不需要特殊的初始化和反初始化过程,一个集成了V8库的进程可以在任何时机直接使用脚本引擎。
l 脚本执行都需要特定的上下文,每个上下文相当于一个独立的脚本环境。可以随时从一个上下文转移到另一个上下文,也能随时从其它上下文返回到当前上下文。
使用V8执行脚本代码最基本的过程:
1. 创建一个上下文对象(Context)
2. 可选设置上下文对应的全局永久对象
3. 通过函数模板创建函数原型、函数对象或者对象模板
4. 通过对象模板创建对象实例
5. 编译脚本代码后执行
实际上上述几个步骤中不是每个都必须。如果执行的脚本代码中全部使用了V8的内部对象和函数,没有使用外部自定义对象,那么只需要1和5两个步骤就够了。中间步骤实际上是为了在脚本环境中创建自定义的类、函数或者对象。
不使用自定义对象的例子:
使用自定义对象不再提供例子,可以上http://code.google.com/intl/zh-CN/apis/v8/embed.html 站点或者Google开源网站直接查看V8嵌入的例子和应用示例,这不是本文的重点。
在我实现的引擎中,必须把微软脚本引擎规范与V8的功能一一对应,主要涉及到的对应有:
1. 每个引擎组件实例需要对应一个Context实例
2. 每个命名项需要对应一个Context实例
3. 脚本引擎中的Global对象和每个命名项对象各自对应Context的全局对象
4. 脚本代码中产生的COM对象必须创建一个永久Object对象并缓存
5. 所有COM对象的属性和方法必须添加对应的访问回调函数和拦截回调函数
6. V8中创建的事件回调函数必须创建一个对应的COM对象反馈给客户端
7. 函数调用的每个参数和返回值必须与VARIANT类型相互转换
8. Jscript特有的对象和函数必须添加到新创建的每个Context中
9. V8中的索引化访问(如a[2] = ‘abc’;)必须转换成COM中的枚举器访问或者属性化访问
10. 所有脚本运行中间产生的COM对象与V8永久对象的映射关系必须缓存,当V8永久对象被回收时自动释放缓存的COM对象