免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1246 | 回复: 0
打印 上一主题 下一主题

利用SpiderMonkey进行嵌入式开发——学习总结 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-10-29 17:40 |只看该作者 |倒序浏览
利用SpiderMonkey进行嵌入式开发——学习总结
关键词: SpiderMonkey                                          
利用SpiderMonkey进行嵌入式开发——学习总结
许峰 2007/07/30
最近在学习javascript引擎SpiderMonkey,学了一个星期了,终于有点眉目,现将学习经验记录下来,已被后用。
一下将逐步记录我学习的过程。
1、下载源文件以及编译

http://ftp.mozilla.org/pub/mozilla.org/js/
下面有一个文件js-1.60.tar.gz, 这个就是SpiderMonkey的源代码
将源码下载后,解压缩。比如我下载到 /home/xufeng/work/jsEngine 下面,解压后就产生一个文件夹js,源文件全部在js/src中,进入js/src,进行编译(请参考README)。
以上所用的命令有:
$tar xvzf js-1.60.tar.gz
$cd js/src
$make -f Makefile.ref
2.运行一个实例程序
编译过后就会生成javascript引擎的静态库和动态库,分别是libjs.a和libjs.so,在js/src/Linux_All_DBG.OBJ/目录下面.进入这个目录就可以看见.如果要使用javascript引擎,只要在头文件中包含jsapi.h 同时在编译的时候链接这些库就可以了.
在Linux_ALL_DBG.OBJ目录下面还有一个可执行程序 js, 运行这个程序将产生类似shell的东西,可以在下面运行javascript代码.
3.利用javascript引擎进行嵌入式开发
先写一个最简单的程序,用来执行一段javascript代码(javascript代码也是很简单).这个程序也基本上展示了最主要的几个API.
程序如下:
/**
*程序 test1.c
*执行一段javascript代码
*/
  
#include "jsapi.h"
/* EXIT_FAILURE and EXIT_SUCCESS */
#include "stdlib.h"
/* strlen */
#include "string.h"
static void usage();
int main(int argc,const char* argv[])
{
    /* pointer to our runtime object */
    if(argc!=2){
            usage();
            exit(-1);
    }
    JSRuntime *runtime = NULL;
    /* pointer to our context */
    JSContext *context = NULL;
    /* pointer to our global JavaScript object */
    JSObject *global = NULL;
    /* script to run (should return 100) */
    const char *script = argv[1];
    /* JavaScript value to store the result of the script */
    jsval rval;
    /* create new runtime, new context, global object */
    if ((!(runtime = JS_NewRuntime(1024L * 1024L)))
        || (!(context = JS_NewContext(runtime, 8192)))
        || (!(global = JS_NewObject(context, NULL, NULL, NULL)))
        )
        return EXIT_FAILURE;
    /* set global object of context and initialize standard ECMAScript
     *      objects (Math, Date, ...) within this global object scope */
    if (!JS_InitStandardClasses(context, global))
         return EXIT_FAILURE;
    /* now we are ready to run our script */
    if (!JS_EvaluateScript(context, global, script, strlen(script), "script", 1, &rval))
         return EXIT_FAILURE;
    printf("the script's result is \n%d\n",JSVAL_TO_INT(rval));
    /* clean up */
    JS_DestroyContext(context);
    JS_DestroyRuntime(runtime);
    JS_ShutDown();
    return EXIT_SUCCESS;
}
void usage()
{
        printf("example1 script_content\n");
        printf("for example:./example1 \"var a=1;b=2;a+b\"\n");
}
/**
*程序结束
*/
以上程序写好以后就要编译了,编译的时候要注意增加一些编译参数。这里我写了一个简单的Makefile
########################################################################################################
ROSS_COMPILE_PREFIX =
CROSS_COMPILE_PATH   =
CC      =  $(CROSS_COMPILE_PREFIX)gcc
CFLAGS  = -DXP_UNIX -DJS_THREADSAFE -g -O0 -Wall -W -pedantic -std=c99 -I$(HOME)/work/jsEngine/js/src/
LDFLAGS = -L$(HOME)/work/jsEngine/js/src/Linux_All_DBG.OBJ/
LIBS = -ljs
DFLAGS  =  -g -DDEBUG
CLINK  = -c -o
RM  = rm -f
CFILES = test1.c
OBJ  = $(patsubst %.c,%.o, $(CFILES))
EXECUTABLE = $(patsubst %.c,%, $(CFILES))
DEPENDENCY = $(patsubst %.c,%.d, $(CFILES))
all: $(DEPENDENCY) $(EXECUTABLE)
indent :
        find -name '*.[ch]'|xargs indent -kr -cli4 -i4 -nut
doc:
        doxygen
include $(DEPENDENCY)
%: %.o
        $(CC) $$@.$$$$;\
        sed 's,\($*\)\.o:,\1.o
[email=$@:,g']$@:,g'[/email]
  
[email=$@;\]$@;\[/email]
        $(RM)
[email=$@.$$$$]$@.$$$$[/email]
;
clean:
        $(RM) $(EXECUTABLE) *.o *.bak *.c~ *.h~ *.d
########################################################################################################
需要注意的是CFLAGS和DFLAGS,针对自己的路径要做修改。
-ljs -- 链接的时候增加libjs.so库
-DXP_UNIX -- 告诉编译器define XP_UNIX,使得javascript引擎知道操作系统是Unix或类似于Unix系统(这个参数是必需的,否则编译出错,我就在这里碰了跟头)
-O0 -- 不让编译器进行优化
有了以上步骤就可以进行编译运行了。初步的工作已经完成。
执行javascript代码的主要过程有:
1. 创建一个Runtime, API为 JS_NewRuntime()
2. 创建一个Context, API为 JS_NewContext()
3. 创建一个对象Object, API为 JS_NewObject()
4. 初始化全局对象 JS_InitStandardClasses()
5. 执行javascript代码 JS_EvaluateScript()
6. 销毁上下文Context JS_DestroyContext()
7. 销毁Runtime JS_DestroyRuntime()
以上就是主要的过程, 接下来的一些深入的工作都是基于此的.
4. 利用javascript引擎来解析一个javascript文件
在上面的程序中,javascript语句是当作命令行参数传给程序的, 有点的时候觉得不太方便, 于是将所有的javascript写道一个文件中, 然后解析这个文件, 执行语句. 执行一个文件中的javascript语句一般有两种方法:其一是定义FILE *fp, 利用 fp 将这个文件所有字节读出来,然后作为字符串传递给JS_EvaluateScript() API,得到结果;第二种是先编译这个js文件,然后再执行已经编译的文件。以下将给出这两种方法的实例。
方法一:
/**
*程序 test2.c
*利用FILE *fp文件指针执行js文件的代码
*/
#include "jsapi.h"
JSClass global_class =
{
    "global",
0,
JS_PropertyStub,
JS_PropertyStub,
JS_PropertyStub,
JS_PropertyStub,
JS_EnumerateStub,
JS_ResolveStub,
JS_ConvertStub,
JS_FinalizeStub,
JSCLASS_NO_OPTIONAL_MEMBERS
};
int main()
{
    JSString* jss;
    char buf[5120];
    int len;
    jsval rval;
    JSRuntime *rt;
    JSContext *cx;
    JSObject *globalObj;
   
    rt = JS_NewRuntime(1024L*1024L);
    if (!rt)return -1;
    cx = JS_NewContext(rt, 8L*1024L);
    if (!cx)return -1;
   
    if (!(globalObj = JS_NewObject (cx, &global_class, NULL, NULL)))return -1;
    JS_InitStandardClasses (cx, globalObj);
   
    FILE* fp;
if (!(fp = fopen ("test.js", "r")))return -1;
    len = fread (buf, 1, 5120, fp);
    fclose (fp);
    if (len
/**
*程序结束
*/
javascript文件内容如下:
/**
*test.js
*/
var i = 1;
var j = 2;
i+j;
/**
*End
*/
好了,运行这个程序,将得到如下结果:
The result is: 3
方法二:
/**
*程序 test3.c
*先编译js文件,再执行文件
*/
/* EXIT_FAILURE and EXIT_SUCCESS */
#include "stdlib.h"
/* strlen */
#include "string.h"
/* get SpiderMonkey API declarations */
#include "sys/types.h"
#include "sys/stat.h"
#include "jsapi.h"
/*Function prototypes definition*/
JSBool JsFileExecute(JSContext *ctx, const char *file);
int usage(const char *progname);
JSClass JsGlobalObjectClass = {
"System",
0,
JS_PropertyStub,
JS_PropertyStub,
JS_PropertyStub,
JS_PropertyStub,
JS_EnumerateStub,
JS_ResolveStub,
JS_ConvertStub,
JS_FinalizeStub,
JSCLASS_NO_OPTIONAL_MEMBERS
};
int main(int argc, char **argv)
{
JSRuntime *rt = NULL;
JSContext *ctx = NULL;
JSObject *obj = NULL;
char *file = NULL;
if (argc
if(JsFileExecute(ctx, file))
    {
     printf("File execute successfully.\n");
}
else
{
     printf("File execute failed.\n");
}
return 0;
}
int usage(const char *progname)
{
printf("Usage: %s \n", progname);
exit(-1);
}
JSBool JsFileExecute(JSContext *ctx, const char *file)
{
    JSScript *script = NULL;
    JSString *jss;
    JSBool retval;
    jsval ret;
    JSObject *global = JS_GetGlobalObject(ctx);
   
    script = JS_CompileFile(ctx, global, file);
    if(script == NULL)
    {
     return JS_FALSE;
}
retval = JS_ExecuteScript(ctx, global, script, &ret);
jss = JS_ValueToString (ctx, ret);
printf("The result is: %s\n", JS_GetStringBytes(jss));
JS_DestroyScript(ctx, script);
    return retval;
}
/**
*End
*/
这种方法先是用JS_CompileFile() API来编译文件,得到一个script,然后再JS_ExecuteScript() 来运行script,我觉得这种方法更加好一些。
运行的时候,js文件还是用的test.js,其源码同上。
5. 在程序中增加类
javascript是面向对象的,一个类常有构造函数、属性和方法等,在下面的程序中我们构建一个类,并逐步增加类的属性和方法。   
在如下程序中,定义了一个PeopleClass类,然后用
static JSFunctionSpec PeopleMethods[] = {
    {"print", PeoplePrint, 0, 0, 0},
{NULL}
};
来定义类的方法,如果有更多的方法也可以在这里定义,其中 "print" 是将在javascript中采用的方法名,PeoplePrint是在程序中实现的类的方法,后面的参数请参考API说明。
/**
*程序 test4.c
*/
#include "stdio.h"
#include "jsapi.h"
static JSBool PeoplePrint(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool JsFileExecute(JSContext *ctx, const char *file);
int usage(const char *progname);
typedef struct
{
    char name[16];
    char addr[64];
    char tel[10];
}PeopleInfo;
static PeopleInfo pinfo={"xufeng", "Chongqing", "123456"};
JSClass global_class = {
     "global",
  0,
  JS_PropertyStub,
  JS_PropertyStub,
  JS_PropertyStub,
  JS_PropertyStub,
  JS_EnumerateStub,
  JS_ResolveStub,
  JS_ConvertStub,
  JS_FinalizeStub,
  JSCLASS_NO_OPTIONAL_MEMBERS
};
static JSClass PeopleClass = {
  "people",
  0,
  JS_PropertyStub,
  JS_PropertyStub,
  JS_PropertyStub,
  JS_PropertyStub,
  JS_EnumerateStub,
  JS_ResolveStub,
  JS_ConvertStub,
  JS_FinalizeStub,
  JSCLASS_NO_OPTIONAL_MEMBERS
};
static JSFunctionSpec PeopleMethods[] = {
    {"print", PeoplePrint, 0, 0, 0},
{NULL}
};
int main(int argc, char **argv)
{
JSRuntime *rt;
    JSContext *cx;
    JSObject *globalObj,*PeopleObj;
    char *file = NULL;
if (argc
static JSBool PeoplePrint(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    fprintf(stdout,"My Name is %s.\nMy Addr is %s.\nMy telephone is %s\n", pinfo.name, pinfo.addr, pinfo.tel);
    return JS_TRUE;
}
JSBool JsFileExecute(JSContext *ctx, const char *file)
{
    JSScript *script = NULL;
    JSBool retval;
    jsval ret;
    JSObject *global = JS_GetGlobalObject(ctx);
   
    script = JS_CompileFile(ctx, global, file);
    if(script == NULL)
    {
     return JS_FALSE;
}
retval = JS_ExecuteScript(ctx, global, script, &ret);
JS_DestroyScript(ctx, script);
    return retval;
}
int usage(const char *progname)
{
printf("Usage: %s \n", progname);
exit(-1);
}
/**
*End
*/
运行的时候,在javascript中增加对象的使用,修改后的test.js内容如下:
/**
*javascript start
*/
people.print();
/**
*END
*/   
编译后运行:./test4 test.js
程序将会产生如下结果:
My Name is xufeng.
My Addr is Chongqing.
My telephone is 123456
File execute successfully.
接下来我们增加类的属性,增加属性以及定义属性的Getter和Setter函数。
程序如下:
/**
*程序 test5.c
*增加对象的属性
*增加错误报警函数  
*/
#include "stdio.h"
#include "stdlib.h"
#include "jsapi.h"
#include "sys/types.h"
#include "sys/stat.h"
enum PEOPLE {NAME, ADDRESS, TEL};
static JSBool GetPeopleProperty (JSContext *cx, JSObject *obj, jsval id, jsval *vp);
static JSBool SetPeopleProperty (JSContext *cx, JSObject *obj, jsval id, jsval *vp);
static JSBool PeoplePrint(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
static void JsErrorHandler(JSContext *ctx, const char *msg, JSErrorReport *er);
int usage(const char *progname);
JSBool JsFileExecute(JSContext *ctx, const char *file);
typedef struct
{
    char name[16];
    char addr[64];
    char tel[10];
}PeopleInfo;
static PeopleInfo pinfo={"xufeng", "Chongqing", "123456"};
JSClass global_class = {
     "global",
  0,
  JS_PropertyStub,
  JS_PropertyStub,
  JS_PropertyStub,
  JS_PropertyStub,
  JS_EnumerateStub,
  JS_ResolveStub,
  JS_ConvertStub,
  JS_FinalizeStub,
  JSCLASS_NO_OPTIONAL_MEMBERS
};
static JSClass PeopleClass = {
  "people",
  0,
  JS_PropertyStub,
  JS_PropertyStub,
  GetPeopleProperty,
  SetPeopleProperty,
  JS_EnumerateStub,
  JS_ResolveStub,
  JS_ConvertStub,
  JS_FinalizeStub,
  JSCLASS_NO_OPTIONAL_MEMBERS
};
static JSPropertySpec PeopleProperties[] = {
{"name", NAME, JSPROP_ENUMERATE},
{"address", ADDRESS, JSPROP_ENUMERATE},
{"tel", TEL, JSPROP_ENUMERATE},
{NULL}
};
static JSFunctionSpec PeopleMethods[] = {
    {"print", PeoplePrint, 0, 0, 0},
{NULL}
};
int main(int argc, char **argv)
{
    JSRuntime *rt;
    JSContext *cx;
    JSObject *globalObj,*PeopleObj;
    char *file = NULL;
if (argc
static void JsErrorHandler(JSContext *ctx, const char *msg, JSErrorReport *er)
{
    printf("JS Error: %s\nFile: %s:%u\n", msg, er->filename, er->lineno);
}
static JSBool GetPeopleProperty (JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    if (JSVAL_IS_INT(id)) {
        switch (JSVAL_TO_INT(id)) {
            case NAME:
                *vp=STRING_TO_JSVAL (JS_NewStringCopyZ (cx,pinfo.name));
                break;
            case ADDRESS:
                *vp=STRING_TO_JSVAL (JS_NewStringCopyZ (cx,pinfo.addr));
                break;
            case TEL:
             *vp=STRING_TO_JSVAL (JS_NewStringCopyZ (cx,pinfo.tel));
                break;
        }
    }
    return JS_TRUE;
}
static JSBool SetPeopleProperty (JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    if (JSVAL_IS_INT(id)) {
        switch (JSVAL_TO_INT(id)) {
            case NAME:
                strncpy(pinfo.name, JS_GetStringBytes(JS_ValueToString(cx, *vp)), 15);
                break;
            case ADDRESS:
                strncpy(pinfo.addr, JS_GetStringBytes(JS_ValueToString(cx, *vp)), 63);
                break;
            case TEL:
             strncpy(pinfo.tel, JS_GetStringBytes(JS_ValueToString(cx, *vp)), 9);
                break;
        }
    }
    return JS_TRUE;
}
static JSBool PeoplePrint(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    fprintf(stdout,"My Name is %s.\nMy Addr is %s.\nMy telephone is %s\n", pinfo.name, pinfo.addr, pinfo.tel);
    return JS_TRUE;
}
int usage(const char *progname)
{
printf("Usage: %s \n", progname);
exit(-1);
}
JSBool JsFileExecute(JSContext *ctx, const char *file)
{
    JSScript *script = NULL;
    JSBool retval;
    jsval ret;
    JSObject *global = JS_GetGlobalObject(ctx);
   
    script = JS_CompileFile(ctx, global, file);
    if(script == NULL)
    {
     return JS_FALSE;
}
retval = JS_ExecuteScript(ctx, global, script, &ret);
JS_DestroyScript(ctx, script);
    return retval;
}
/**
*END
*/
接下来修改test.js,修改后如下:
/**
*javascript start
*/
people.print();
people.name = "John";
people.address = "Beijing";
people.tel = "00099988";
people.print();
/**
*END
*/
编译运行 ./test5 test.js 得到如下结果:
My Name is xufeng.
My Addr is Chongqing.
My telephone is 123456
My Name is John.
My Addr is Beijing.
My telephone is 00099988
File execute successfully.
注意:在上面程序中增加了一个错误报警函数 JsErrorHandler(),然后 JS_SetErrorReporter(cx, JsErrorHandler);设置这个ErrorReporter,只要编译javascript出错了,这个函数就会运行,打印出出错的位置。
工作就做到了这里,希望对你有点用处。   
附:
将ECMAScript值转换为本地值,一般采用以下函数:
JS_ValueToNumber, JS_ValueToInt32, JS_ValueToECMAInt32, JS_ValueToECMAUint32,
JS_ValueToBoolean, JS_ValueToUint16, JS_ValueToString, JS_ValueToObject,
JS_ValueToFunction
将本地值转化为ECMAScript值,一般采用以下函数:
JS_NewNumberValue, JS_NewJS_NewStringCopyZ, JS_UCStringCopyZ, JS_NewArrayObject
and macros from the ..._TO_JSVAL family: BOOLEAN_TO_JSVAL, STRING_TO_JSVAL,
OBJECT_TO_JSVAL
内存使用:
可以设置两种内存使用限制。通过JS_NewRuntime可以限制每个runtime堆的大小,通过JS_NewContext可以限制每个
context栈的大小。
利用spidermonkey来执行一个javascript文件有两种方法,第一种是将这个文件读出来然后作为字符串传递给spidermonkey,第二种是告诉spidermonkey去编译这个文件,然后再执行已经编译的文件。


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/35985/showart_410091.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP