分类: C/C++
2009-01-14 16:29:28
最近在学习javascript引擎SpiderMonkey,学了一个星期了,终于有点眉目,现将学习经验记录下来,已被后用。
一下将逐步记录我学习的过程。
1、下载源文件以及编译
在 下面有一个文件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.
程序如下:
|
以上程序写好以后就要编译了,编译的时候要注意增加一些编译参数。这里我写了一个简单的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) $< $(LIBS) -o $@ $(LDFLAGS)
%.o: %.c
$(CC) -c $< -o$@ $(CFLAGS)
%.d: %.c
$(CC) -MM $(CFLAGS) $< >$@.$$$$;\
sed 's,\($*\)\.o:,\1.o < >
$(RM) ;
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文件,然后再执行已经编译的文件。以下将给出这两种方法的实例。
方法一:
|
|
好了,运行这个程序,将得到如下结果:
The result is: 3
方法二:
|
这种方法先是用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说明。
|
运行的时候,在javascript中增加对象的使用,修改后的test.js内容如下:
|
编译后运行:./test4 test.js
程序将会产生如下结果:
My Name is xufeng.
My Addr is Chongqing.
My telephone is 123456
File execute successfully.
接下来我们增加类的属性,增加属性以及定义属性的Getter和Setter函数。
程序如下:
|
接下来修改test.js,修改后如下:
|
编译运行 ./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去编译这个文件,然后再执行已经编译的文件。