SpiderMonkey是個歷史悠久的Javascript引擎,有不少應用的實例。以下就介紹一下一些應用,及簡單地使用C來嵌入SpiderMonkey。
應用列表
Mozilla網站上有提供一份應用的列表:
https://developer.mozilla.org/En/SpiderMonkey/FOSS
這裡面包涵了一些程式語言的支援例如C++、OCaml、Python等,還有一個可以C程式碼產生器,用來將javascript函數與C語言函數橋接。另外有一些應用及延伸的計畫,包括在GNOME及wxWidgets環境中使用Javascript,以及一些功能延伸。
Wikipedia也有一些介紹:
http://en.wikipedia.org/wiki/SpiderMonkey_(JavaScript_engine)#Usage
重要的應用像是在一些遊戲環境中作為模組開發語言、adobe用在acrobat reader、Flash Media Server、JSFL等。
JSLibs
在語言延伸中,我對這個比較有興趣:
http://code.google.com/p/jslibs/
JSLibs是一個已經非常成熟的專案,可以透過他在Console中執行Javascript,而且他提供了許多函數與模組的支援,例如:
* zlib 壓縮
* SQLite 資料庫
* FastCGI CGI介面,可以提供網頁程式處理的服務
* NSPR (Netscape Portable Runtime) 平台中立的函式庫
* ODE (Open Dynamics Engine) 動態模擬
* libpng, libjpeg, librsvg 圖形、影像處理轉換
* SDL 動畫函式庫(類似DirectDraw)
* libiconv 文字編碼轉換
* OpenGL, OpenAL 3D動畫、3D音效
* ogg vorbis 音樂
* libTomCrypt 加解密
* libffi 呼叫外部程式(例如dll中的函數)
另外,他也有處理binary格式、Buffer、執行緒、檔案及Socket I/O等等功能,非常強大,這應該是擴充SpiderMonkey的功能中最完整的方案了。
使用SpiderMonkey
接下來嘗試一下怎樣簡單地把SpiderMonkey整合到Console環境下執行的C語言程式,其實是以前作練習的例子,使用DEV-C++配合MINGW來編譯。在這之前,要先編譯好SpiderMonkey,並設定好include及lib目錄,並且設定好Linker要去Link的lib檔。(可以透過DEV-C++的Project設定來做)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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
};
JSBool js_print(JSContext *, JSObject *, uintN , jsval *, jsval *);
JSFunctionSpec myjs_global_functions[] = {
JS_FS("print",js_print,1,0,0),
JS_FS_END
};
globalfunc.h(定義了一個陽春的global物件結構,以及一個print函數,會加到global物件中)
#include "globalfunc.h"
JSBool js_print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
if(argc>0) {
JSString *msg;
msg = JS_ValueToString(cx, argv[0]);
printf("%s",JS_GetStringBytes(msg));
return JS_TRUE;
}else{
return JS_FALSE;
}
}
globalfunc.c(print函數實作)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "jsapi.h"
extern JSClass global_class;
extern JSBool js_print(JSContext *, JSObject *, uintN, jsval *, jsval *);
extern JSFunctionSpec myjs_global_functions[];
main.h(宣告)
#include "main.h"
void reportError(JSContext *cx, const char *message, JSErrorReport *report)
{
fprintf(stderr, "%s:%u:%s\n",
report->filename ? report->filename : "<no filename>",
(unsigned int) report->lineno,
message);
}
int main(int argc, char* argv[])
{
if(argc<2) {
fputs("you must pass script filename as 1st argument.", stderr);
return 1;
}
JSRuntime *rt;
JSContext *cx;
JSObject *global;
char *filename = (char*) argv[1];
FILE *myFile;
JSBool ok;
myFile = fopen(filename, "r");
if (myFile == NULL) {
perror("Error!");
return 1;
}
rt = JS_NewRuntime(8L * 1024L * 1024L);
if (rt == NULL) {
fputs("Error!: runtime starting failed.\n", stderr);
return 1;
}
cx = JS_NewContext(rt, 8192);
if (cx == NULL) {
fputs("Error!: context starting failed.\n", stderr);
return 1;
}
JS_SetOptions(cx, JSOPTION_VAROBJFIX);
JS_SetVersion(cx, JSVERSION_1_5);
JS_SetErrorReporter(cx, reportError);
global = JS_NewObject(cx, &global_class, NULL, NULL);
if (global == NULL) {
fputs("Error!: global object creating failed.\n", stderr);
return 1;
}
if (!JS_InitStandardClasses(cx, global)) {
fputs("Error!: global object init failed.\n", stderr);
return 1;
}
if (!JS_DefineFunctions(cx,global,myjs_global_functions)) {
fputs("Error!: customized global functions loading failed.\n", stderr);
return 1;
}
char* script;
int iSize;
size_t size_r;
jsval rval;
long lineno = 1;
fseek(myFile,0,SEEK_END);
iSize = ftell(myFile);
rewind(myFile);
script = (char*) malloc(sizeof(char)*iSize);
if(script==NULL) {
printf("Memory Error\n");
fclose(myFile);
JS_DestroyContext(cx);
JS_DestroyRuntime(rt);
JS_ShutDown();
return 1;
}
size_r = fread(script,sizeof(char),iSize,myFile);
ok = JS_EvaluateScript(cx, global, script, size_r, filename, lineno, &rval);
fclose(myFile);
free(script);
JS_DestroyContext(cx);
JS_DestroyRuntime(rt);
JS_ShutDown();
return 0;
}
main.c(主程式)
其實從main.c就可以看出,關鍵的幾個東西就是JSAPI的JSRuntime、JSContext、JSObject,先建立runtime環境,然後建立context,然後加入global物件,最後把print函數加到global物件中。接下來從程式參數讀入要執行的Javascript檔案名稱,讀取檔案內容字串,最後使用JS_EvaluateScript函數來執行這個程式。Javascript檔案裡面用到print()函數時,就會輸出訊息到console。基本的結構其實主要就是這幾個物件/函數做出來的。
接下來用javascript檔案測試一下:
function pyth(x,y) {
return sqrt(x*x+y*y);
}
function sqrt(x) {
return x*x;
}
print(pyth(2,3)+"\n");
print("我踏月色而來。\n");
執行完會顯示:
169
我踏月色而來。
更詳細的使用,還是要參考SpiderMonkey的文件,包括一些教學文件、指引、範例還有API文件等等,都可以在http://www.mozilla.org/js/spidermonkey/以及https://developer.mozilla.org/en/SpiderMonkey找到。
您好,我目前正照著你的 source code 在 ubuntu 上 compile ,不過似乎一直無法成功?!我用的是 spidermonkey 1.8.5
不知道是否能請你幫我解惑!我卡在這一段一直錯誤!
JSBool js_print(JSContext *cx, uintN argc, jsval *argv)
{
if(argc>0) {
JSString *msg;
msg = JS_ValueToString(cx, argv[0]);
printf("%s",JS_EncodeString(cx, msg));
return JS_TRUE;
}else{
return JS_FALSE;
}
}
錯誤訊息是 Segmentation fault
然後錯誤一直卡在這一行:msg = JS_ValueToString(cx, argv[0]);
似乎是這個值有錯:argv[0]
誒...這是三年多前的東西...我要再重建編譯環境才能模擬了
我好像總是卡在某些api function 執行不正確!
例如:
if (!JS_EvaluateScript(context, JS_GetGlobalObject(context), script, strlen(script), "script", 1, &rval))
return EXIT_FAILURE;
cout << "JSVAL_IS_INT(rval): " << JSVAL_IS_INT(rval) << endl;
cout << "JSVAL_TO_INT(rval): " << JSVAL_TO_INT(rval) << endl;
script = "var x=10; x*x;"
那 JSVAL_TO_INT(rval) 應該要等於 100, 可是 output 是 201 ~"~