Android 接入 QuickJS(二)- 源码阅读

引言

我们接入 QuickJS 引擎的目的是为了使用引擎能力,那么应该从哪里获取 QuickJS 支持那些能力呢,比较好的一个来源就是 QuickJS 源码。

相关文章

Android 接入 QuickJS(一)

源码阅读

为了对 QuickJS 引擎有稍微深入的了解,我们需要阅读源码以加深对源码的理解,并且通过源码的阅读找寻当下我们使用引擎可能会用到的 API。

目前我个人是通过几个维度来阅读和分析源码,它们分别是头文件阅读与分析 / 源码 Test Code 阅读 / 引擎核心实现源码阅读。经过这三种维度的源码阅读后会对整体的引擎代码有一个初步了解。有了这些基础后就可以粗略选择出来可能要使用的 API 接口。

头文件

由于 QuickJS 是用 C 语言写的,我们在使用的时候可以优先查看代码通过头文件暴露了那些方法。

这里我以比较重要的 quickjs.h 为例,分析 QuickJS 引擎都提供了那些能力。

从上至下阅读,首先可以看到 QuickJS 对各种 reference 的 tag 定义,如 JS_TAG_INT = 0 / JS_TAG_BOOL = 1 等。

继续往下可以看到 QuickJS 对核心结构体的定义,如 JSValue / JSValueUnion 等。

然后在这个头文件里声明了一些常用的方法,如 #define JS_VALUE_GET_TAG(v) 获取 JSValue 的 tag 等。

最后是一些方法的定义,如 JSRuntime *JS_NewRuntime(void); 产生 JSRuntime 的结构等。

有了这个整体头文件的分析,可以大致对 QuickJS 引擎有一个初步的了解。经过这一宏观的了解后就可以根据 QuickJS 源码中提供的测试代码进行阅读来进一步的熟悉 QuickJS 的架构和基本用法。

Example Code

hello.c

通过编译 QuickJS 的源码会生成一个 test 文件 hello.c 通过对这个文件的阅读,我们可以了解最典型的 QuickJS 引擎用法。

c 复制代码
int main(int argc, char **argv)
{
  JSRuntime *rt;
  JSContext *ctx;
  rt = JS_NewRuntime();
  js_std_set_worker_new_context_func(JS_NewCustomContext);
  js_std_init_handlers(rt);
  ctx = JS_NewCustomContext(rt);
  js_std_add_helpers(ctx, argc, argv);
  js_std_eval_binary(ctx, qjsc_hello, qjsc_hello_size, 0);
  js_std_loop(ctx);
  JS_FreeContext(ctx);
  JS_FreeRuntime(rt);
  return 0;
}

通过上述代码可以分析出,在使用 QuickJS 引擎时,首先要创建一个 JSRuntime 环境,然后通过这个 Runtime 去生成对应的 JSContext 后续的执行则依赖这个 JSContext 去使用。

由于 QuickJS 引擎初衷有一部分是为嵌入式设备使用,因此从 js_std_eval_binary(ctx, qjsc_hello, qjsc_hello_size, 0); 这个方法里也可以看出 QuickJS 是支持直接通过二进制字节码运行脚本代码的。

这个 hello.c 文件驱动的方法都是通过调用 quickjs-libc.h 实现的。通过研究这个文件的实现类 quickjs-libc.c 可以看到大部分的典型用法。

如 js_std_eval_binary 方法中最终是调用 JS_EvalFunction 执行输入的字节码二进制文件,在 js_std_add_helpers 方法中完成对应方法的注入等。

point.c

这个 example 主要是讲解如何自定义自己的 JS Class 以及在 JS 脚本中使用,通过自定义实现,可以在 JS 脚本中使用我们使用 C 语言定义的结构体等。

关于本 example 的使用会在接下来的文章(API 详解)中详细解析,同时也会在该文中解析各个 API 的使用场景和注意事项。

Source Code

经过上述两种方式的阅读后,可以再通过阅读真实的 QuickJS 源码加深对引擎的理解。

阅读源码时,有时候会因为源码的体积或者架构比较复杂感到无从下手,根据我个人的经验,最好的阅读方式是带着问题去阅读和学习更加快速。

现在就以 JS_EvalFunction 为例,看下 QuickJS 引擎是如何执行 Fucntion 的实例的。

阅读代码可以发现这个方法内部调用了 JS_EvalFunctionInternal 进行下一步的执行,其中多添加了一个参数是 ctx->global_obj ,在这个方法的内部首先判断这个输入的 fun_obj 是 JS_TAG_FUNCTION_BYTECODE 还是 JS_TAG_MODULE 类型,如果是 JS_TAG_FUNCTION_BYTECODE 则可以正常执行 JS_CallFree 并且拿到结果。如果是 JS_TAG_MODULE 则会首先拿到对应的 PTR 然后对这个 PTR 进行一系列的操作,最终完成这个 module 的加载。

小结

本篇文章主要从三个维度介绍了如何阅读 QuickJS 的源码,希望通过这些方式可以加深大家对 QuickJS 的了解。

接下来将根据实际代码分析如何使用 QuickJS 引擎在自己的工程中实战。

相关推荐
Digitally23 分钟前
如何将文件从 iPhone 传输到 Android(新指南)
android·ios·iphone
whysqwhw1 小时前
OkHttp深度架构缺陷分析与演进规划
android
用户7093722538511 小时前
Android14 SystemUI NotificationShadeWindowView 加载显示过程
android
DolphinDB1 小时前
如何在C++交易系统中集成高性能回测与模拟撮合
c++
筏.k2 小时前
C++ 网络编程(14) asio多线程模型IOThreadPool
网络·c++·架构
木叶丸2 小时前
跨平台方案该如何选择?
android·前端·ios
顾林海3 小时前
Android ClassLoader加载机制详解
android·面试·源码
用户2018792831673 小时前
🎨 童话:Android画布王国的奇妙冒险
android
爱喝茶的小茶3 小时前
周赛98补题
开发语言·c++·算法
OpenC++3 小时前
【C++】备忘录模式
c++·设计模式·备忘录模式