集成spiderMonkey
乐正浩宕
2023-12-01
集成spiderMonkey
文档 :https://developer.mozilla.org/En/SpiderMonkey/JSAPI_User_Guide
https://developer.mozilla.org/en/How_to_embed_the_JavaScript_engine
一、hello world基本流程:
JS_NewRuntime
JS_NewContext
JS_SetOptions
JS_SetErrorReporter
JS_NewCompartmentAndGlobalObject ------创建global对象
JS_InitStandardClasses ---------------- 把运行时内置的函数、对象、常量等作为global object的属性导出
ok = JS_EvaluateScript(cx, global, script, strlen(script), filename, lineno, &rval); ----解释执行
if (rval == NULL | rval == JS_FALSE) // 判断运行状态
return 1;
str = JS_ValueToString(cx, rval); // 处理运行结果
二、暴漏c的函数(本地的wrapper是作为js的回调挂上去的)
本地wrapper的基本格式要求 typedef JSBool (*JSNative)(JSContext *cx, uintN argc, jsval *vp);
1、返回值是 JSBool(JS_TRUE 正常得到结果,JS_FALSE执行时出现异常)
2、第一个参数是 JSContext
3、第二个参数是 argc(js代码中传进来的参数个数)
4、第三个参数是 jsval *, 可以用来取得参数、this、返回值等
5、调用成功时 JS_SET_RVAL + return JS_TRUE
调用失败时 JS_ReportError + return JS_FALSE
三、常用技术
1、JS_ARGV(cx, vp) 指向参数数组的第一个元素
2、JS_RVAL(cx, vp) 指向返回值
3、JS_SET_RVAL(cx, vp, value) 设置返回值
4、JS_THIS_OBJECT(cx, vp) 取this对象
5、JS_THIS(cx, vp) jsVal版本的this
6、JS_CALLEE(cx, vp) 被调用的函数对象
四、例子
JSBool myjs_rand(JSContext *cx, uintN argc, jsval *vp)
{
JS_SET_RVAL(cx, vp, DOUBLE_TO_JSVAL(rand()));
return JS_TRUE;
}
/* A wrapper for the srand() function, from the C standard library.
This example shows how to handle optional arguments. */
JSBool myjs_srand(JSContext *cx, uintN argc, jsval *vp)
{
uint32 seed;
// 把输入的参数转变格式,存入输出中, 参数依次是cx,调用者传入参数个数,传入的参数,输出的格式,输出地址
if (!JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "/u", &seed))
return JS_FALSE;
/* If called with no arguments, use the current time as the seed. */
if (argc == 0)
seed = time(NULL);
srand(seed);// 初始化一个种子
JS_SET_RVAL(cx, vp, JSVAL_VOID); /* return undefined */
return JS_TRUE;
}
/* A wrapper for the system() function, from the C standard library.
This example shows how to report errors. */
JSBool myjs_system(JSContext *cx, uintN argc, jsval *vp)
{
JSString* str;
char *cmd;
int rc;
if (!JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "s", &str))
return JS_FALSE;
cmd = JS_EncodeString(cx, str); // js string 转成 c的char *
rc = system(cmd);
JS_free(cx, cmd);// 不用了,释放掉
if (rc != 0) {
/* Throw a JavaScript exception. */
JS_ReportError(cx, "Command failed with exit code %d", rc);// 抛出异常给js,js中可以用try catch检测到
return JS_FALSE;
}
JS_SET_RVAL(cx, vp, JSVAL_VOID); /* return undefined 没有返回值时也要显示的设置一下*/
return JS_TRUE;
}
//加入自定义函数
JSFunctionSpec my_functions[] = {
JS_FS("myprint", myprint, 0, 0, 0), // 依次是name, notive func, argc, tags, extra
JS_FS_END
};
JS_DefineFunctions(cx, objParent, my_functions);
五、暴漏对象 JS_InitClass + JS_DefineProperties
顺序:
1、属性id枚举
2、getter, constructer, destructer
3、class的template
4、class的函数表
5、JS_InitClass
6、JS_DefineProperties
网上找的一个例子:
***************************************************************************** getter, constructer, destructer******
// 属性Getter函数
enum{ EQFRONT, EQBACK, EQSIZE, EQEMPTY, EQARRAY};
JSBool MyClassGetter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
std::deque<int>* pQ = (std::deque<int>*)JS_GetPrivate(cx, obj);
if(!JSVAL_IS_INT(id)) return JS_TRUE;
switch(JSVAL_TO_INT(id))
{
case EQFRONT:
if(pQ->empty()) return JS_FALSE;
*vp = INT_TO_JSVAL( pQ->front() );
break;
case EQBACK:
if(pQ->empty()) return JS_FALSE;
*vp = INT_TO_JSVAL( pQ->back() );
break;
case EQSIZE:
*vp = INT_TO_JSVAL( pQ->size() );
break;
case EQEMPTY:
*vp = BOOLEAN_TO_JSVAL( pQ->empty() );
break;
case EQARRAY: //数组操作
{
//用JS_NewArrayObject生成数组
JSObject *objA = JS_NewArrayObject(cx, 0, NULL);
jsint idx = 0;
for(std::deque<int>::iterator itr=pQ->begin(); itr!=pQ->end(); ++itr)
{
jsval v = INT_TO_JSVAL(*itr);
//用JS_SetElement加入指定索引的数据
JS_SetElement(cx, objA, idx++, &v);
}
*vp = OBJECT_TO_JSVAL(objA);
break;
}
break;
}
return JS_TRUE;
}
// "构造"
JSBool MyClassConstructor(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval)
{
std::deque<int> *pQ = new std::deque<int>();
JS_SetPrivate(cx, obj, pQ);
return JS_TRUE;
}
// "析造"
void MyClassDestructor(JSContext *cx, JSObject *obj)
{
std::deque<int>* pQ = (std::deque<int>*)JS_GetPrivate(cx, obj);
delete pQ;
}
******************************************class template*****************************************
// 定义类
JSClass myClass={
"MyClass", // name
JSCLASS_HAS_PRIVATE, // tags
JS_PropertyStub, JS_PropertyStub, // 添加、删除属性的回调 默认是 JS_PropertyStub
MyClassGetter, JS_PropertyStub, // get set方法
JS_EnumerateStub, // 遍历属性
JS_ResolveStub, // 解析不存在的属性
JS_ConvertStub, // 转成原子类型
MyClassDestructor // 析构函数
};
*******************************************class的函数表***************************************
// 类方法
JSBool QPush(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval)
{
std::deque<int>* pQ = (std::deque<int>*)JS_GetPrivate(cx, obj);
if(JSVAL_IS_INT(argv[0]))
{
pQ->push_back(JSVAL_TO_INT(argv[0]));
}
return JS_TRUE;
}
JSBool QPop(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval)
{
std::deque<int>* pQ = (std::deque<int>*)JS_GetPrivate(cx, obj);
if(!pQ->empty())
{
pQ->pop_front();
}
return JS_TRUE;
}
JSFunctionSpec myClassMethod[]={
JS_FS("push", QPush, 1, 0, 0), // 依次是name, notive func, argc, tags, extra
JS_FS("pop", QPop, 0, 0, 0),
JS_FS_END
};
*****************************************JS_InitClass****************************
JSObject * newobj = JS_InitClass(cx, objParent, NULL, // cx, global obj, prototype,
&myClass, MyClassConstructor, 0, // 类构成模板,构造函数,构造函数参数个数
NULL, myClassMethod, // prototype中的属性和方法
NULL, NULL); // 构造函数的属性和方法
**************************************JS_DefineProperties************************
JSPropertySpec myClassProp[]={
{"front", EQFRONT, JSPROP_READONLY}, // names, c实现中的唯一id, flags, get函数, set函数
{"back", EQBACK, JSPROP_READONLY},
{"size", EQSIZE, JSPROP_READONLY},
{"empty", EQEMPTY, JSPROP_READONLY},
{"array", EQARRAY, JSPROP_READONLY},
{0}
};
JS_DefineProperties(cx, newobj, myClassProp);