1

概括

我已经查看了 SpiderMonkey 'shell' 应用程序用于创建 ctypes JavaScript 对象的代码,但我不是新手 C 程序员。由于现代构建系统发出的不同程度的疯狂,我似乎无法追踪实际将程序与所需功能联系起来的代码或命令。


方法.疯狂

Mozilla Devs 的这个 js-ctypes 实现是一个很棒的补充。从它的概念开始,脚本就主要用于对更严格和更健壮的应用程序进行控制。SpiderMonkey 项目中 js-ctypes 的出现,使 JavaScript 能够站起来,并被视为一种成熟的面向对象的快速应用程序开发语言,高出各种古老的应用程序开发语言(如 Microsoft 的 VB6)设定的“标准”。


让我们开始?

我用这个配置构建了 SpiderMonkey:./configure --enable-ctypes --with-system-nspr

然后成功执行:make && make install

js shell 工作正常,全局 ctypes javascript 对象已在该 shell 中验证可操作。

使用How to embed the JavaScript Engine -MDN中第一个源代码清单中的代码,我尝试通过在第 66 行插入以下代码来实例化 JavaScript ctypes 对象:

    /* Populate the global object with the ctypes object. */
    if (!JS_InitCTypesClass(cx, global))
        return NULL;
    /*

我编译时使用: g++ $(./js-config --cflags --libs) hello.cpp -o hello

它编译时带有一些警告:

hello.cpp: In function ‘int main(int, const char**)’:
hello.cpp:69:16: warning: converting to non-pointer type ‘int’ from NULL [-Wconversion-null]
hello.cpp:80:20: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
hello.cpp:89:17: warning: NULL used in arithmetic [-Wpointer-arith]

但是当你运行应用程序时:

./hello: symbol lookup error: ./hello: undefined symbol: JS_InitCTypesClass

而且

JS_InitCTypesClass 在 'dist/include/jsapi.h' 中声明为 extern,但该函数驻留在 'ctypes/CTypes.cpp' 中,其中包括自己的头文件 'CTypes.h' 并在某些时候由某些命令在 'make' 期间编译产生'./CTypes.o'。正如我之前所说,我对 C 代码还不是新手,我真的不知道在这里做什么。

请给出或给出一个使 js-ctypes 对象在嵌入中起作用的通用示例。

4

2 回答 2

1

黑客

我已经想到,由于头文件中的条件定义以及分散的库和头文件位置,链接失败。好吧...我试图在命令行上定义 JS_HAS_CTYPES 但如果它确实有效,那肯定是不够的。

我决定,因为 SpiderMonkey shell 有自己独特的 makefile,并且已经可以访问我试图捕获的功能,只需将 js.cpp 重命名为 js.cpp.tmp 并让我的代码站在它的位置,几乎可以工作.

该文件编译良好,应用程序执行时没有引发运行时链接错误,但代码('JSNativeObject' ctypes)几乎完全失败了 JS_InitCTypesClass。看到我的链接错误早已被遗忘,我立即查看了 make 的输出,看看我是否可以“刷”编译代码,然后......我们有一个宾果游戏!


汇编

将 shell/js.cpp 恢复到其原始目标后,我将 hello.cpp 移动到 spidermonkey 的根源目录并开始更正由 makefile 创建的相对路径以及执行明显不适合的构造的删除我的应用程序的存在或相关性。

虽然以下命令似乎呈现了一个可操作的二进制文件,但作者无法给出此列表的正确性或完整性的相关性。

c++ -o hello.o -c  -Idist/system_wrappers_js -include config/gcc_hidden.h \
-DEXPORT_JS_API -DOSTYPE=\"Linux3.2\" -DOSARCH=Linux -I. -Idist/include \
-Idist/include/nsprpub  -I/usr/include/nspr -fPIC  -fno-rtti \
-fno-exceptions -Wall -Wpointer-arith -Woverloaded-virtual -Wsynth \
-Wno-ctor-dtor-privacy -Wno-non-virtual-dtor -Wcast-align \
-Wno-invalid-offsetof -Wno-variadic-macros -Werror=return-type -pedantic \
-Wno-long-long -fno-strict-aliasing -pthread -pipe  -DNDEBUG -DTRIMMED -Os \
-freorder-blocks -fomit-frame-pointer -DJS_HAS_CTYPES -DMOZILLA_CLIENT \
-include js-confdefs.h -MD -MF .deps/hello.pp hello.cpp;

c++ -o hello -fno-rtti -fno-exceptions -Wall -Wpointer-arith \
-Woverloaded-virtual -Wsynth -Wno-ctor-dtor-privacy \
-Wno-non-virtual-dtor -Wcast-align -Wno-invalid-offsetof \
-Wno-variadic-macros -Werror=return-type -pedantic \
-Wno-long-long -fno-strict-aliasing -pthread -pipe  -DNDEBUG \
-DTRIMMED -Os -freorder-blocks -fomit-frame-pointer hello.o \
-lpthread -Wl,-rpath-link,/bin -Wl,-rpath-link,/usr/local/lib \
-Ldist/bin -Ldist/lib -L/usr/lib -lplds4 -lplc4 -lnspr4 \
-lpthread -ldl editline/libeditline.a libjs_static.a -ldl;

上面列出的两个命令被放入一个名为“mkhello”的可执行 shell 脚本中,该脚本保存在根源目录中。

据我所知,这是一种两阶段的编译方法。出于什么原因我不确定,但解释似乎很有教育意义。想法?

编辑请参阅下面的评论以了解“两阶段编译方法”的解释。


代码:hello.cpp

/*
 * This define is for Windows only, it is a work-around for bug 661663.
 */
#ifdef _MSC_VER
# define XP_WIN
#endif

/* Include the JSAPI header file to get access to SpiderMonkey. */
#include "jsapi.h"


/* The class of the global object. */
static JSClass global_class = {
    "global", JSCLASS_GLOBAL_FLAGS,
    JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
    JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
    JSCLASS_NO_OPTIONAL_MEMBERS
};

/* The error reporter callback. */
void reportError(JSContext *cx, const char *message, JSErrorReport *report)
{
    fprintf(stderr, "%s:%u:%s\n",
            report->filename ? report->filename : "<no filename=\"filename\">",
            (unsigned int) report->lineno,
            message);
}

int main(int argc, const char *argv[])
{
    /* JSAPI variables. */
    JSRuntime *rt;
    JSContext *cx;
    JSObject  *global;

    /* Create a JS runtime. You always need at least one runtime per process. */
    rt = JS_NewRuntime(8 * 1024 * 1024);
    if (rt == NULL)
        return 1;

    /* 
     * Create a context. You always need a context per thread.
     * Note that this program is not multi-threaded.
     */
    cx = JS_NewContext(rt, 8192);
    if (cx == NULL)
        return 1;
    JS_SetOptions(cx, JSOPTION_VAROBJFIX | JSOPTION_JIT | JSOPTION_METHODJIT);
    JS_SetVersion(cx, JSVERSION_LATEST);
    JS_SetErrorReporter(cx, reportError);

    /*
     * Create the global object in a new compartment.
     * You always need a global object per context.
     */
    global = JS_NewCompartmentAndGlobalObject(cx, &global_class, NULL);
    if (global == NULL)
        return 1;

    /*
     * Populate the global object with the standard JavaScript
     * function and object classes, such as Object, Array, Date.
     */
    if (!JS_InitStandardClasses(cx, global))
        return 1;

    /* Populate the global object with the ctypes object. */
    if (!JS_InitCTypesClass(cx, global))
        return NULL;
    /*

    /* Your application code here. This may include JSAPI calls
     * to create your own custom JavaScript objects and to run scripts.
     *
     * The following example code creates a literal JavaScript script,
     * evaluates it, and prints the result to stdout.
     *
     * Errors are conventionally saved in a JSBool variable named ok.
     */
    char *script = "ctypes.open";
    jsval rval;
    JSString *str;
    JSBool ok;
    const char *filename = "noname";
    uintN lineno = 0;

    ok = JS_EvaluateScript(cx, global, script, strlen(script),
                           filename, lineno, &rval);
    if (rval == NULL | rval == JS_FALSE)
        return 1;

    str = JS_ValueToString(cx, rval);
    printf("%s\n", JS_EncodeString(cx, str));

    /* End of your application code */

    /* Clean things up and shut down SpiderMonkey. */
    JS_DestroyContext(cx);
    JS_DestroyRuntime(rt);
    JS_ShutDown();
    return 0;
}

结论

$ ./mkhello
# ...
# error free garbage scrolls....
$ ./hello
function open() {
    [native code]
}

按照此示例为 SpiderMonkey 嵌入提供 js-ctypes。您可能需要也可能不需要按顺序重新创建这些步骤,但从我目前的角度来看,强烈建议您这样做。

于 2012-02-27T06:35:33.907 回答
1

问题:大多数平台分销商对代码开发人员给出的建议一无所知。因此,在大多数(如果不是所有)托管该库的系统上,都没有为 mozjs185 启用 js-ctypes。这留下了一些问题需要您解决。

在您配置了 --enable-ctypes 和 --with-sytem-nspr 之后make,然后make install(可能需要是最后一个命令的 root)

您的系统上可能有两个版本的 libmozjs185.so。一个启用了 ctypes(位于 /usr/local/lib),一个没有启用 ctypes(位于 /usr/lib)。

You, as per the question, want to link against the library with ctypes. So that's what you do by specifying: -L/usr/local/lib -lnspr4 -lmozjs185 to your compiler. It will compile ok. But when the application runs, the os library loader will load the first instance of the library that it finds. Unfortunately this will likely be the library located in /usr/lib and this version likely does not have ctypes enabled. That's where you'll run into this [solved] problem: g++ Undefined Symbol Error using shared library

The bottom line is this: multiple versions of the same library is creating one hell of an issue. The best way to provide js-ctypes to a spidermonkey embedding is therefore linking in the ctypes enabled static version of mozjs185 to your program unless you want to write a library for dealing with multiple platform libary loaders, or create your own (rebranded version of mozjs185) which I have covered in great detail over @ SpiderMonkey Build Documentation - MDN

To perform the static linking you will need to use these parameters with g++: -lnspr4 -lpthread -lmozjs185-1.0 provided that you have built and installed the development package correctly. This is the 'best' (platform independant) way to provide js-ctypes to a spidermonkey embedding. Although this does increase the size of your application by at least 3.5 MB. If you have built the debug version it could be more than 15 times larger.

于 2012-03-31T02:41:01.280 回答