脚本引擎小PK: SpiderMonkey vs V8(二)

脚本引擎小PK: SpiderMonkey vs V8(二)

应用脚本引擎

要运行前文中的两段脚本代码,必须实现全局函数myprint和类MyClass,其中,MyClasspush()pup()方法。

首先,我们来看看各自的框架代码

SpiderMonkey

1.                #define XP_WIN

2.                extern "C"{

3.                #include "jsapi.h"

4.                }

5.                // argv[1]指定脚本文件

6.                int main(int argc, char* argv[])

7.                {

8.                    // 需要一个runtime和一个以上的上下文

9.                    JSRuntime *rt = JS_NewRuntime(0x 400000L );// 参数指定最大BYTE

10.                JSContext *cx = JS_NewContext(rt, 8192); //8192是栈大小

11.             

12.                // 设置JS上下文参数

13.                JS_SetOptions(cx, JSOPTION_VAROBJFIX);// 尽量兼容ECMAScript

14.                JS_SetVersion(cx, JSVERSION_LATEST);  // 支持最新版本JS

15.             

16.                // 定义全局变量

17.                JSObject *globalObj = JS_NewObject(cx, NULL, 0, 0); //生成全局对象

18.                JS_InitStandardClasses(cx, globalObj); //向全局对象注册标准方法和属性

19.             

20.                // 编译JS文件,得到编译后的脚本对象

21.                JSScript * jsp = JS_CompileFile(cx, globalObj, argv[1]);

22.                if(jsp == NULL){

23.                    std::cout << "编译js文件出错!" << std::endl;

24.                    return -1;

25.                }

26.               

27.                // 执行脚本对象

28.                jsval rval;

29.                JS_ExecuteScript(cx, globalObj, jsp, &rval);

30.               

31.                // 销毁脚本对象

32.                JS_DestroyScript(cx, jsp);

33.               

34.                // 关闭JS环境

35.                JS_DestroyContext(cx);

36.                JS_DestroyRuntime(rt);

37.                JS_ShutDown();

38.                return 0;

39.            }

V8

1.                //这里用的是DLL版本,如果是静态版本就不用下面这句了

2.                #include <iostream>

3.                #include <string>

4.                #include <fstream>

5.                #define USING_V8_SHARED 1

6.                #include "v8.h"

7.                using namespace v8;

8.                // argv[1]指定脚本文件

9.                int main(int argc, char* argv[])

10.            {

11.                // V8不支持直接读文件,只好自己把脚本文件内容讲到字符串里去了-_-

12.                std::ifstream ifstm(argv[1]);

13.                if(!ifstm) return -1;

14.                std::string s( std::istreambuf_iterator<char>(ifstm.rdbuf()),

15.                    std::istreambuf_iterator<char>());

16.             

17.                HandleScope handle_scope; // 管理Handle,在它生命周期内的Handle都归它管

18.                Handle<ObjectTemplate> global = ObjectTemplate::New(); //准备一个全局对象模板

19.             

20.                Persistent<Context> context = Context::New(NULL,global); //建立上下文

21.                Context::Scope context_scope(context);//管理上下文

22.             

23.                Handle<String> source_obj = String::New(s.c_str(), s.length()); //源码字符串

24.                Handle<Script> script = Script::Compile(source_obj);//编译

25.                Handle<Value> result = script->Run();//运行

26.               

27.                context.Dispose();

28.                return 0;

29.            }

上面的这些代码已经可以解析标准JS脚本了,你可以把我们试验用的《脚本一》里最后一句myprint注释掉,然后用上面的程序来运行。

 

 

实现全局函数myprint

myprint是宿主程序向脚本提供的一个全局函数,用于向控制台输出传给它的所有参数,这样我们就可以直接在脚本里输出信息了。

SpiderMonkey

1.                //-----------------自定义函数---------------------

2.                JSBool myprint(JSContext* cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

3.                {

4.                    for(uintN i=0; i<argc; i++)

5.                    {

6.                        JSString *str = JS_ValueToString(cx, argv[i]);

7.                        std::cout << JS_GetStringBytes(str) << '   ' ;

8.                    }

9.                    return JS_TRUE;

10.            }

11.            void addMyFunc(JSContext* cx, JSObject *objParent) //加入自定义函数

12.            {

13.                JSFunctionSpec my_functions[] = {

14.                    JS_FS("myprint", myprint, 0, 0, 0),

15.                    JS_FS_END

16.                };

17.                JS_DefineFunctions(cx, objParent, my_functions);

18.            }

在编译之前加上addMyFunc函数就可以为脚本加入myprint函数了。如:

1.                ...

2.                // 定义全局变量

3.                JSObject *globalObj = JS_NewObject(cx, NULL, 0, 0); //生成全局对象

4.                JS_InitStandardClasses(cx, globalObj); //向全局对象注册标准方法和属性

5.                //------加入自定义函数------

6.                addMyFunc(cx, globalObj);

7.                //------------------------

8.                // 编译JS

9.                JSScript * jsp = JS_CompileFile(cx, globalObj, argv[1]);

10.            ...

V8

1.                //-----------------自定义函数---------------------

2.                Handle<Value> MyPrint( const Arguments& args) //myprint函数实体,打印所有参数

3.                {

4.                    HandleScope handle_scope; //管理这里的Handle(函数退出时释放循环里的Handle<String> s)

5.                    for(int i=0; i<args.Length(); i++)

6.                    {

7.                        Handle<String> s = args[i]->ToString();

8.                        std::cout << *(String::AsciiValue(s)) << std::endl;

9.                    }

10.                return Undefined();

11.            }

12.             

13.            void addMyFunc(Handle<ObjectTemplate> global) //加入自定义函数

14.            {

15.                global->Set(String::New("myprint"), FunctionTemplate::New(MyPrint)); //自定义一个myprint函数

16.            }

向全局对象模板注册这个函数就可以在脚本中使用myprint了,如:

1.                HandleScope handle_scope; // 管理Handle,在它生命周期内的Handle都归它管

2.                Handle<ObjectTemplate> global = ObjectTemplate::New(); //全局对象

3.                addMyFunc(global); //为全局对象加上自定义函数

加入类MyClass

这里,打算把MyClass实现成一个队列类,方法有:pushpop;属性有:frontbackemptysizearray。直接使用std::deque作为队列实现的底层支持,下面开始分别用SpiderMonkeyV8的方式对它进行包装。

SpiderMonkey

1.                // 属性Getter函数

2.                enum{ EQFRONT, EQBACK, EQSIZE, EQEMPTY, EQARRAY};

3.                JSBool MyClassGetter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

4.                {

5.                    std::deque<int>* pQ = (std::deque<int>*)JS_GetPrivate(cx, obj);

6.                    if(!JSVAL_IS_INT(id)) return JS_TRUE;

7.                    switch(JSVAL_TO_INT(id))

8.                    {

9.                    case EQFRONT:

10.                    if(pQ->empty()) return JS_FALSE;

11.                    *vp = INT_TO_JSVAL( pQ->front() );

12.                    break;

13.                case EQBACK:

14.                    if(pQ->empty()) return JS_FALSE;

15.                    *vp = INT_TO_JSVAL( pQ->back() );

16.                    break;

17.                case EQSIZE:

18.                    *vp = INT_TO_JSVAL( pQ->size() );

19.                    break;

20.                case EQEMPTY:

21.                    *vp = BOOLEAN_TO_JSVAL( pQ->empty() );

22.                    break;

23.                case EQARRAY: //数组操作

24.                    {

25.                        //JS_NewArrayObject生成数组

26.                        JSObject *objA = JS_NewArrayObject(cx, 0, NULL);

27.                        jsint idx = 0;

28.                        for(std::deque<int>::iterator itr=pQ->begin(); itr!=pQ->end(); ++itr)

29.                        {

30.                            jsval v = INT_TO_JSVAL(*itr);

31.                            //JS_SetElement加入指定索引的数据

32.                            JS_SetElement(cx, objA, idx++, &v);

33.                        }

34.                        *vp = OBJECT_TO_JSVAL(objA);

35.                        break;

36.                    }

37.                    break;

38.                }

39.                return JS_TRUE;

40.            }

41.            // "构造"

42.            JSBool MyClassConstructor(JSContext *cx, JSObject *obj, uintN argc,

43.                jsval *argv, jsval *rval)

44.            {

45.                std::deque<int> *pQ = new std::deque<int>();

46.                JS_SetPrivate(cx, obj, pQ);

47.                return JS_TRUE;

48.            }

49.            // "析造"

50.            void MyClassDestructor(JSContext *cx, JSObject *obj)

51.            {

52.                std::deque<int>* pQ = (std::deque<int>*)JS_GetPrivate(cx, obj);

53.                delete pQ;

54.            }

55.            // 类方法

56.            JSBool QPush(JSContext *cx, JSObject *obj, uintN argc,

57.                    jsval *argv, jsval *rval)

58.            {

59.                std::deque<int>* pQ = (std::deque<int>*)JS_GetPrivate(cx, obj);

60.                if(JSVAL_IS_INT(argv[0]))

61.                {

62.                    pQ->push_back(JSVAL_TO_INT(argv[0]));

63.                }

64.                return JS_TRUE;

65.            }

66.            JSBool QPop(JSContext *cx, JSObject *obj, uintN argc,

67.                    jsval *argv, jsval *rval)

68.            {

69.                std::deque<int>* pQ = (std::deque<int>*)JS_GetPrivate(cx, obj);

70.                if(!pQ->empty())

71.                {

72.                    pQ->pop_front();

73.                }

74.                return JS_TRUE;

75.            }

76.            // 定义类

77.            JSClass myClass={

78.                    "MyClass",

79.                    JSCLASS_HAS_PRIVATE,

80.                    JS_PropertyStub, JS_PropertyStub, //add del

81.                    MyClassGetter, JS_PropertyStub, //get set

82.                    JS_EnumerateStub,

83.                    JS_ResolveStub,

84.                    JS_ConvertStub,

85.                    MyClassDestructor

86.            };

87.            JSPropertySpec myClassProp[]={

88.                {"front", EQFRONT, JSPROP_READONLY},

89.                {"back", EQBACK, JSPROP_READONLY},

90.                {"size", EQSIZE, JSPROP_READONLY},

91.                {"empty", EQEMPTY, JSPROP_READONLY},

92.                {"array", EQARRAY, JSPROP_READONLY},

93.                {0}

94.            };

95.            JSFunctionSpec myClassMethod[]={

96.                JS_FS("push", QPush, 1, 0, 0),

97.                JS_FS("pop", QPop, 0, 0, 0),

98.                JS_FS_END

99.            };

100.        void addMyClass(JSContext* cx, JSObject *objParent) //加入自定义类

101.        {

102.            JSObject * newobj = JS_InitClass(cx, objParent, NULL,

103.                &myClass, MyClassConstructor, 0,

104.                NULL, myClassMethod,

105.                NULL, NULL);

106.            JS_DefineProperties(cx, newobj, myClassProp);

107.        }

V8

1.                // “构造

2.                Handle<Value> MyClassConstructor( const Arguments& args)

3.                {

4.                    Handle<Object> obj = args.This();

5.                    HandleScope handle_scope;

6.                    testq *ptr = new testq;

7.                    obj->SetInternalField(0, External::New(ptr));

8.                    return obj;

9.                }

10.            // 类属性Getter函数

11.            Handle<Value> GetQFront(Local<String> property,

12.                                      const AccessorInfo &info)

13.            {

14.                HandleScope handle_scope;

15.                Handle<Object> self = info.Holder();

16.                std::deque<int>* pQ = (std::deque<int>*) self->GetPointerFromInternalField(0);

17.                return Undefined();

18.            }

19.             

20.            Handle<Value> GetQBack(Local<String> property,

21.                                      const AccessorInfo &info)

22.            {

23.                HandleScope handle_scope;

24.                Local<Object> self = info.Holder();

25.                std::deque<int>* pQ = (std::deque<int>*) self->GetPointerFromInternalField(0);

26.                return Integer::New(pQ->back());

27.            }

28.            Handle<Value> GetQSize(Local<String> property,

29.                                      const AccessorInfo &info)

30.            {

31.                HandleScope handle_scope;

32.                Local<Object> self = info.Holder();

33.                std::deque<int>* pQ = (std::deque<int>*) self->GetPointerFromInternalField(0);

34.                return Integer::New(pQ->size());

35.            }

36.            Handle<Value> GetQEmpty(Local<String> property,

37.                                      const AccessorInfo &info)

38.            {

39.                HandleScope handle_scope;

40.                Local<Object> self = info.Holder();

41.                std::deque<int>* pQ = (std::deque<int>*) self->GetPointerFromInternalField(0);

42.                return Boolean::New(pQ->empty());

43.            }

44.            Handle<Value> GetQArray(Local<String> property,

45.                                      const AccessorInfo &info)

46.            {

47.                HandleScope handle_scope;

48.                Local<Object> self = info.Holder();

49.                std::deque<int>* pQ = (std::deque<int>*) self->GetPointerFromInternalField(0);

50.                Handle<Array> array = Array::New(pQ->size());

51.                int i=0;

52.                for(std::deque<int>::iterator itr=pQ->begin(); itr!=pQ->end(); ++itr)

53.                {

54.                    array->Set(Integer::New(i++), Integer::New(*itr));

55.                }

56.                return array;

57.            }

58.            // 类方法

59.            Handle<Value> QPush( const Arguments& args)

60.            {

61.                HandleScope handle_scope;

62.                Local<Object> self = args.Holder();

63.                Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));

64.                std::deque<int>* ptr = (std::deque<int>*)wrap->Value();

65.                ptr->push_back( args[0]->Int32Value() );

66.                return Undefined();

67.            }

68.            Handle<Value> QPop( const Arguments& args)

69.            {

70.                HandleScope handle_scope;

71.                Local<Object> self = args.Holder();

72.                Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));

73.                std::deque<int>* ptr = (std::deque<int>*)wrap->Value();

74.                ptr->pop_front();

75.                return Undefined();

76.            }

77.            //加入自定义类

78.            void addMyClass(Handle<ObjectTemplate> global)

79.            {

80.                Handle<FunctionTemplate> myc = FunctionTemplate::New(MyClassConstructor);

81.             

82.                HandleScope handle_scope;

83.                myc->SetClassName(String::New("MyClass"));

84.                global->Set(String::New("MyClass"), myc);

85.               

86.                Handle<ObjectTemplate> proto = myc->PrototypeTemplate();

87.                proto->Set("push", FunctionTemplate::New(QPush));

88.                proto->Set("pop", FunctionTemplate::New(QPop));

89.             

90.                Handle<ObjectTemplate> inst = myc->InstanceTemplate();

91.                inst->SetInternalFieldCount(1);

92.                inst->SetAccessor(String::New("front"), GetQFront);

93.                inst->SetAccessor(String::New("back"), GetQBack);

94.                inst->SetAccessor(String::New("size"), GetQSize);

95.                inst->SetAccessor(String::New("empty"), GetQEmpty);

96.                inst->SetAccessor(String::New("array"), GetQArray);

97.            }

同样,把这个addMyClass函数和之前的addMyFunc放到一起就可以为脚本加入一个MyClass类了。

速度测试

脚本所需的类和方法已经装备完毕,接下来就可以做我们的速度测试了。

下面的测试速度是在我的电脑上,Core2 T7100, 1G 内存上10次测试取平均的结果,编译环境为VC2005 ExpressRelease方式。单位为秒。

脚本引擎小PK: SpiderMonkey vs V8(二)_第1张图片

从图上可以直观地看出,V8的速度要比SpiderMonkey快,尤其是脚本一,V8的速度是SpiderMonkey10倍还多。

总结

就执行速度而言,V8具有压倒性的优势,这和Google的官方宣传是一致的。

大 小方面,在VCRelease下,V8的动态编译库大小为 1.39MSpiderMonkey的动态编译库大小为708kSpiderMonkey 胜出。静态编译?嗯,还是算了吧,偶编译的静态V8.lib足足有 100M ,和程序链接时要花费10秒钟的时间,调试程序太郁闷了~~

对于编程简易程度来说,两方各有优势,这个因人而异,我觉得SpiderMonkey的概念更简洁一点;V8TemplateScopeHandle等东东还是需要一点时间去理解的。而且还有一点,SpiderMonkey的官方文档比V8的丰富。

如 果各位细心的话,可以发现V8版的addMyClass没有析构函数,也就是说在脚本里建立出来的MyClass是存在内存泄漏的!我花了很长时间, 查了N多资料也没找到如何在V8GC工作时回收InternalField里的数据(经试验,PersistentMakeWeak方法无效)。目前 的解决方法只有为类提供一个destory函数,在脚本中显式调用来回收,不过这样就享受不了垃圾回收了。如果各位有更好的方法,请留言提出哦,非常感 谢!

最后我们还得提到IE的脚本引擎,调用IE脚本引擎的优点是操作系统集成,所以不用附带一个DLL或链接一大块脚本引擎,这样发布的软件会小一点点,而且ActiveX Scripting技术(请参考本站相关文章)对于熟悉COM编程的人来说编写代码也很简单。但是~~缺点也很明显,速度不是慢,而是相当慢~~本来偶还想用它也运行一下《脚本一》的代码作为参考的,结果等了一分种无响应后郁闷地强制退出~~

 

你可能感兴趣的:(脚本引擎小PK: SpiderMonkey vs V8(二))