Cocos2dx 3.X jsb加载JavaScript源码文件分析

本文详细解析了Cocos2dx JavaScript Bindings (JSB) 中加载JavaScript源码的过程,包括核心函数require的实现原理,以及如何通过ScriptingCore组件执行和编译JavaScript文件。
Cocos2dx 3.X jsb加载JavaScript源码文件分析:
1、其实js和lua差不多,lua中通过require函数加载lua文件,js中也是通过一个函数require,加载js源码文件,而且思路差不多,那这个函数是如何执行的呢?

2、require函数
在ScriptingCore.cpp文件中有个registerDefaultClasses函数,这个函数注册了一些C++ 函数供javascript调用,require就是其中之一:
void registerDefaultClasses(JSContext* cx, JS::HandleObject global) {
………不需要的东西,都去掉了。
// register some global functions
//这里就是重点,注册了一个require函数,实际调用的是
    /**
     * run a script from script :)
     */
    static bool executeScript(JSContext *cx, uint32_t argc, jsval *vp);
函数,找到源头,我们可以去看下代码,具体如何实现得了。

JS_DefineFunction(cx, global, "require", ScriptingCore::executeScript, 1, JSPROP_READONLY | JSPROP_PERMANENT);

//顺便说下这个,这个log如果是jsb可以直接使用,但不能在h5上面使用,所以最好还是使用cc.log
    JS_DefineFunction(cx, global, "log", ScriptingCore::log, 0, JSPROP_READONLY | JSPROP_PERMANENT);
  
}
3、
//这个函数是我们的重点
bool ScriptingCore::executeScript(JSContext *cx, uint32_t argc, jsval *vp)
{
    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);//获取参数
    if (argc >= 1) {
        JSString* str = JS::ToString(cx, JS::RootedValue(cx, args.get(0)));
        JSStringWrapper path(str);
        bool res = false;
        if (argc == 2 && args.get(1).isString()) {
            JSString* globalName = args.get(1).toString();
            JSStringWrapper name(globalName);

            JS::RootedObject debugObj(cx, ScriptingCore::getInstance()->getDebugGlobal());
            if (debugObj) {
                res = ScriptingCore::getInstance()->runScript(path.get(), debugObj);
            } else {
                JS_ReportError(cx, "Invalid global object: %s", name.get());
                return false;
            }
        } else {
            JS::RootedObject glob(cx, JS::CurrentGlobalOrNull(cx));
            res = ScriptingCore::getInstance()->runScript(path.get(), glob);
        }
        return res;
    }
    args.rval().setUndefined();
    return true;
}

------》》其实最终都是执行这个path就是我们传进来,也就是想要加载的js文件
ScriptingCore::getInstance()->runScript(path.get(), glob);
4、这个函数就是真正加载的函数:
bool ScriptingCore::runScript(const char *path, JS::HandleObject global, JSContext* cx)
{
    if (cx == NULL) {
        cx = _cx;
    }
 //进行编译
    compileScript(path,global,cx);
    JS::RootedScript script(cx, getScript(path));
    bool evaluatedOK = false;
if (script) {
//编译完后,就可以调用JS_ExecuteScript函数执行了。
        JS::RootedValue rval(cx);
        JSAutoCompartment ac(cx, global);
        evaluatedOK = JS_ExecuteScript(cx, global, script, &rval);
        if (false == evaluatedOK) {
            cocos2d::log("(evaluatedOK == JS_FALSE)");
            JS_ReportPendingException(cx);
        }
    }
 
    return evaluatedOK;
}
5、编译函数
void ScriptingCore::compileScript(const char *path, JSObject* global, JSContext* cx)
{
    if (!path) {
        return;
    }
    
if (getScript(path)) { //如果已经编译过,则直接返回,这里有一个缓存
// name ~> JSScript map
static std::unordered_map<std::string, JSScript*> filename_script;
        return;
    }

    cocos2d::FileUtils *futil = cocos2d::FileUtils::getInstance();

    if (global == NULL) {
        global = _global.ref().get();
    }
    if (cx == NULL) {
        cx = _cx;
    }
    JSAutoCompartment ac(cx, global);

    JS::RootedScript script(cx);
    JS::RootedObject obj(cx, global);

    // a) check jsc file first  //先测试字节码,我们一般把js文件弄成字节码,防止被人使用
    std::string byteCodePath = RemoveFileExt(std::string(path)) + BYTE_CODE_FILE_EXT;

    // Check whether '.jsc' files exist to avoid outputing log which says 'couldn't find .jsc file'.
    if (futil->isFileExist(byteCodePath))
    { //如果找到字节码文件,则进行JS_DecodeScript解码
        Data data = futil->getDataFromFile(byteCodePath);
        if (!data.isNull())
        {
            script = JS_DecodeScript(cx, data.getBytes(), static_cast<uint32_t>(data.getSize()), nullptr);
        }
    }

    // b) no jsc file, check js file, 如果没有jsc字节码文件,则找js文件
    if (!script)
    {
        /* Clear any pending exception from previous failed decoding.  */
        ReportException(cx);

        std::string fullPath = futil->fullPathForFilename(path);
   
        JS::CompileOptions op(cx);
        op.setUTF8(true);
        op.setFileAndLine(fullPath.c_str(), 1);

        bool ok = false;
//这里对不同平台有个不同的处理,主要是存放文件的方式不同,然后调用
JS::Compile编译即可。
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
        std::string jsFileContent = futil->getStringFromFile(fullPath);
        if (!jsFileContent.empty())
        {
            ok = JS::Compile(cx, obj, op, jsFileContent.c_str(), jsFileContent.size(), &script);
        }
#else
        ok = JS::Compile(cx, obj, op, fullPath.c_str(), &script);
#endif
        if (ok) {
            filename_script[fullPath] = script;
        }
    }
    else {
        filename_script[byteCodePath] = script;
    }
}







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值