QuickJS 初探

QuickJS 的 Windows Build: https://github.com/mengmo/QuickJS-Windows-Build
可以用 Release 里面给的编译好的 DLL,下载下来用 mingw64 编译是一样的。

如何在只有 DLL 的情况下生成 MSVC 需要的 LIB 文件?

打开 VS2015/2017/2019 自带的 Developer PowerShell,进入 DLL 的文件夹,运行

dumpbin /exports libquickjs.dll > t.txt

删除“ ordinal hint RVA      name”及之前的所有行;删除“ Summary”及之后的所有行,然后运行如下 Python 脚本:

f = open('t.txt')
fw = open('libquickjs.def', 'w')
fw.write('LIBRARY libquickjs.dll\nEXPORTS\n')
lines = f.readlines()
for line in lines:
    line = line.strip()
    line_arr = line.split()
    def_line = "{}\[email protected]\t{}\n".format(line_arr[3], line_arr[0])
    fw.write(def_line)
f.close()
fw.close()

然后在 Developer PowerShell 里执行

lib /def:libquickjs.def /machine:x64 /out:libquickjs.lib

即可得到 LIB 文件。

C++ 适配化改造

要在自己的项目里使用 QuickJS ,只需要 quickjs.h 和 quickjs-libc.h 两个头文件。 quickjs.h 里使用了指派初始化这一 C 支持 C++ 不支持的语法,需要改造。

首先,在第31行的 #ifdef __cplusplus 处引入 types_traits:

#ifdef __cplusplus
#include <type_traits>
extern "C" {
#endif

218行开始的一些宏使用了指派初始化,修改:

/* C++ doesn't support nested designators */
#ifndef __cplusplus
#define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .int32 = val }, tag }
#define JS_MKPTR(tag, p) (JSValue){ (JSValueUnion){ .ptr = p }, tag }
#define JS_NAN (JSValue){ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 }
#else
static inline JSValue JS_MKVAL(int64_t tag, int32_t val) {
    JSValue ret;
    ret.tag = tag;
    ret.u.int32 = val;
    return ret;
}
static inline JSValue JS_MKPTR(int64_t tag, void* p) {
    JSValue ret;
    ret.tag = tag;
    ret.u.ptr = p;
    return ret;
}
static inline JSValue JS_NAN() {
    JSValue ret;
    ret.tag = JS_TAG_FLOAT64;
    ret.u.float64 = JS_FLOAT64_NAN;
    return ret;
}
#endif  /* __cplusplus */

1006行也是(Fabrice Bellard 大神还贴心地告诉你 C++ 不喜欢指派初始化器):

/* Note: c++ does not like nested designators */
#ifndef __cplusplus
#define JS_CFUNC_DEF(name, length, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_generic, { .generic = func1 } } } }
#define JS_CFUNC_MAGIC_DEF(name, length, func1, magic) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, magic, .u = { .func = { length, JS_CFUNC_generic_magic, { .generic_magic = func1 } } } }
#define JS_CFUNC_SPECIAL_DEF(name, length, cproto, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_ ## cproto, { .cproto = func1 } } } }
#define JS_ITERATOR_NEXT_DEF(name, length, func1, magic) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, magic, .u = { .func = { length, JS_CFUNC_iterator_next, { .iterator_next = func1 } } } }
#define JS_CGETSET_DEF(name, fgetter, fsetter) { name, JS_PROP_CONFIGURABLE, JS_DEF_CGETSET, 0, .u = { .getset = { .get = { .getter = fgetter }, .set = { .setter = fsetter } } } }
#define JS_CGETSET_MAGIC_DEF(name, fgetter, fsetter, magic) { name, JS_PROP_CONFIGURABLE, JS_DEF_CGETSET_MAGIC, magic, .u = { .getset = { .get = { .getter_magic = fgetter }, .set = { .setter_magic = fsetter } } } }
#define JS_PROP_STRING_DEF(name, cstr, prop_flags) { name, prop_flags, JS_DEF_PROP_STRING, 0, .u = { .str = cstr } }
#define JS_PROP_INT32_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_INT32, 0, .u = { .i32 = val } }
#define JS_PROP_INT64_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_INT64, 0, .u = { .i64 = val } }
#define JS_PROP_DOUBLE_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_DOUBLE, 0, .u = { .f64 = val } }
#define JS_PROP_UNDEFINED_DEF(name, prop_flags) { name, prop_flags, JS_DEF_PROP_UNDEFINED, 0, .u = { .i32 = 0 } }
#define JS_OBJECT_DEF(name, tab, len, prop_flags) { name, prop_flags, JS_DEF_OBJECT, 0, .u = { .prop_list = { tab, len } } }
#define JS_ALIAS_DEF(name, from) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, .u = { .alias = { from, -1 } } }
#define JS_ALIAS_BASE_DEF(name, from, base) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, .u = { .alias = { from, base } } }
#else
static inline JSCFunctionListEntry JS_CFUNC_DEF(const char* name, uint8_t length, JSCFunction func1) {
  JSCFunctionListEntry ret;
  ret.name = name;
  ret.prop_flags = JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE;
  ret.def_type = JS_DEF_CFUNC;
  ret.magic = 0;
  ret.u.func.length = length;
  ret.u.func.cproto = JS_CFUNC_generic;
  ret.u.func.cfunc.generic = func1;
  return ret;
}
using JSCFuncMagic = std::add_pointer_t<JSValue(JSContext*, JSValue, int, JSValue*, int)>;
static inline JSCFunctionListEntry JS_CFUNC_MAGIC_DEF(const char* name, uint8_t length, JSCFuncMagic func1, int16_t magic) {
  JSCFunctionListEntry ret;
  ret.name = name;
  ret.prop_flags = JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE;
  ret.def_type = JS_DEF_CFUNC;
  ret.magic = magic;
  ret.u.func.length = length;
  ret.u.func.cproto = JS_CFUNC_generic_magic;
  ret.u.func.cfunc.generic_magic = func1;
  return ret;
}
#define CXX_JSCFENUM_GEN(cproto) JS_CFUNC_ ## cproto
static inline JSCFunctionListEntry JS_CFUNC_SPECIAL_DEF(const char* name, uint8_t length, JSCFunctionEnum cproto, JSCFunction func1) {
  JSCFunctionListEntry ret;
  ret.name = name;
  ret.prop_flags = JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE;
  ret.def_type = JS_DEF_CFUNC;
  ret.magic = 0;
  ret.u.func.length = length;
  ret.u.func.cproto = cproto;
  ret.u.func.cfunc.generic = func1;
  return ret;
}
using JSCFuncIter = std::add_pointer_t<JSValue(JSContext*, JSValueConst, int, JSValueConst*, int*, int)>;
static inline JSCFunctionListEntry JS_ITERATOR_NEXT_DEF(const char* name, uint8_t length, JSCFuncIter func1, int16_t magic) {
  JSCFunctionListEntry ret;
  ret.name = name;
  ret.prop_flags = JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE;
  ret.def_type = JS_DEF_CFUNC;
  ret.magic = magic;
  ret.u.func.length = length;
  ret.u.func.cproto = JS_CFUNC_iterator_next;
  ret.u.func.cfunc.iterator_next = func1;
  return ret;
}
using JSCFuncGetter = std::add_pointer_t <JSValue(JSContext*, JSValueConst)>;
using JSCFuncSetter = std::add_pointer_t <JSValue(JSContext*, JSValueConst, JSValueConst)>;
static inline JSCFunctionListEntry JS_CGETSET_DEF(const char* name, JSCFuncGetter fgetter, JSCFuncSetter fsetter) {
  JSCFunctionListEntry ret;
  ret.prop_flags = JS_PROP_CONFIGURABLE;
  ret.def_type = JS_DEF_CGETSET;
  ret.magic = 0;
  ret.u.getset.get.getter = fgetter;
  ret.u.getset.set.setter = fsetter;
  return ret;
}
using JSCFuncGetterMagic = std::add_pointer_t <JSValue(JSContext*, JSValueConst)>;
using JSCFuncSetterMagic = std::add_pointer_t <JSValue(JSContext*, JSValueConst, JSValueConst)>;
static inline JSCFunctionListEntry JS_CGETSET_MAGIC_DEF(const char* name, JSCFuncGetterMagic fgetter, JSCFuncSetterMagic fsetter, int16_t magic) {
  JSCFunctionListEntry ret;
  ret.prop_flags = JS_PROP_CONFIGURABLE;
  ret.def_type = JS_DEF_CGETSET;
  ret.magic = magic;
  ret.u.getset.get.getter = fgetter;
  ret.u.getset.set.setter = fsetter;
  return ret;
}
static inline JSCFunctionListEntry JS_PROP_STRING_DEF(const char* name, const char* cstr, uint8_t prop_flags) {
  JSCFunctionListEntry ret;
  ret.name = name;
  ret.prop_flags = prop_flags;
  ret.def_type = JS_DEF_PROP_STRING;
  ret.magic = 0;
  ret.u.str = cstr;
  return ret;
}
static inline JSCFunctionListEntry JS_PROP_INT32_DEF(const char* name, int32_t val, uint8_t prop_flags) {
  JSCFunctionListEntry ret;
  ret.name = name;
  ret.prop_flags = prop_flags;
  ret.def_type = JS_DEF_PROP_INT32;
  ret.magic = 0;
  ret.u.i32 = val;
  return ret;
}
static inline JSCFunctionListEntry JS_PROP_INT64_DEF(const char* name, int64_t val, uint8_t prop_flags) {
  JSCFunctionListEntry ret;
  ret.name = name;
  ret.prop_flags = prop_flags;
  ret.def_type = JS_DEF_PROP_INT64;
  ret.magic = 0;
  ret.u.i64 = val;
  return ret;
}
static inline JSCFunctionListEntry JS_PROP_DOUBLE_DEF(const char* name, double val, uint8_t prop_flags) {
  JSCFunctionListEntry ret;
  ret.name = name;
  ret.prop_flags = prop_flags;
  ret.def_type = JS_DEF_PROP_DOUBLE;
  ret.magic = 0;
  ret.u.f64 = val;
  return ret;
}
static inline JSCFunctionListEntry JS_PROP_UNDEFINED_DEF(const char* name, uint8_t prop_flags) {
  JSCFunctionListEntry ret;
  ret.name = name;
  ret.prop_flags = prop_flags;
  ret.def_type = JS_DEF_PROP_UNDEFINED;
  ret.magic = 0;
  ret.u.i32 = 0;
  return ret;
}
static inline JSCFunctionListEntry JS_OBJECT_DEF(const char* name, struct JSCFunctionListEntry* tab, int len, uint8_t prop_flags) {
  JSCFunctionListEntry ret;
  ret.name = name;
  ret.prop_flags = prop_flags;
  ret.def_type = JS_DEF_OBJECT;
  ret.u.prop_list.tab = tab;
  ret.u.prop_list.len = len;
  ret.magic = 0;
  return ret;
}
static inline JSCFunctionListEntry JS_ALIAS_DEF(const char* name, const char* from) {
  JSCFunctionListEntry ret;
  ret.name = name;
  ret.prop_flags = JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE;
  ret.def_type = JS_DEF_ALIAS;
  ret.magic = 0;
  ret.u.alias.name = from;
  ret.u.alias.base = -1;
  return ret;
}
static inline JSCFunctionListEntry JS_ALIAS_BASE_DEF(const char* name, const char* from, int base) {
  JSCFunctionListEntry ret;
  ret.name = name;
  ret.prop_flags = JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE;
  ret.def_type = JS_DEF_ALIAS;
  ret.magic = 0;
  ret.u.alias.name = from;
  ret.u.alias.base = base;
  return ret;
}
#endif  /* __cplusplus */

之后使用过程中应该不会再报错。

基本用法

// #include <cstring>
#include <fstream>
#include "quickjs-libc.h"

int main() {
  // 初始化 Runtime 和 Context
  JSRuntime* rt = JS_NewRuntime();
  JSContext* ctx = JS_NewContext(rt);
  // 可选:启用 ES6 Module
  // JS_SetModuleLoaderFunc(rt, nullptr, js_module_loader, nullptr);
  // 启用 console 等基础设施
  js_std_add_helpers(ctx, 0, nullptr);
  // 可选:启用 BigNumber 支持(解决0.1 + 0.2不等于0.3的千古难题)
  // JS_AddIntrinsicBigFloat(ctx);
  // JS_AddIntrinsicBigDecimal(ctx);
  // JS_AddIntrinsicOperators(ctx);
  // JS_EnableBignumExt(ctx, 1);
  // 从文件读取:
  size_t buf_len = 0;
  uint8_t* buf = js_load_file(ctx, &buf_len, "test.js");
  // 最后一项设为 JS_EVAL_TYPE_GLOBAL 则关闭 Module 功能,ES6 Module 和 C Module 都不能访问。
  if (JS_IsException(JS_Eval(ctx, reinterpret_cast<const char*>(buf), buf_len, "test.js", JS_EVAL_TYPE_MODULE))) {
    js_std_dump_error(ctx);
  }
  js_free(ctx, buf);
  // 从内存读取:
  // const char* hello = "console.log(\"hello\");";
  // if (JS_IsException(JS_Eval(ctx, hello, strlen(hello), "<hello>", JS_EVAL_TYPE_MODULE))) {
    // js_std_dump_error(ctx);
  // }
  // 调用一次让 Context 执行即可。
  js_std_loop(ctx);
  // Clean-up,注意先后顺序
  JS_FreeContext(ctx);
  JS_FreeRuntime(rt);
  return 0;
}

把 JavaScript 编译成字节码

#include <fstream>
#include "quickjs-libc.h"

int main() {
  JSRuntime* rt = JS_NewRuntime();
  JSContext* ctx = JS_NewContext(rt);
  // 视情况设置 context,略
  size_t buf_len = 0;
  uint8_t* buf = js_load_file(ctx, &buf_len, "test.js");
  // 关键:启用编译
  int eval_flags = JS_EVAL_FLAG_COMPILE_ONLY;
  // 如果是模块要用JS_EVAL_TYPE_MODULE,否则JS_EVAL_TYPE_GLOBAL
  eval_flags |= JS_EVAL_TYPE_GLOBAL;
  JSValue obj = JS_Eval(ctx, reinterpret_cast<const char*>(buf), buf_len, "test.js", eval_flags);
  if (JS_IsException(obj)) {
    js_std_dump_error(ctx);
  }
  // 不需要 js_std_loop 来执行文件
  js_free(ctx, buf);
  // 设置输出标志,如果用于大端序需要字节反转
  int flags = JS_WRITE_OBJ_BYTECODE;
  // flags |= JS_WRITE_OBJ_BSWAP;
  // 获取字节码
  size_t bytecode_size = 0;
  uint8_t* bytecode = JS_WriteObject(ctx, &bytecode_size, obj, flags);
  if (!bytecode) {
    js_std_dump_error(ctx);
  }
  std::ofstream file("test.bytecode", std::ios::out | std::ios::binary | std::ios::trunc);
  file.write(reinterpret_cast<const char*>(bytecode), bytecode_size);
  file.close();
  // Clean-up
  JS_FreeValue(ctx, obj);
  js_free(ctx, bytecode);
  JS_FreeContext(ctx);
  JS_FreeRuntime(rt);
  return 0;
}

读取字节码并执行

#include <fstream>
#include <sstream>
#include <string>
#include "quickjs-libc.h"

int main() {
  // 读取字节码
  std::ifstream file("test.bytecode", std::ios::in | std::ios::binary);
  std::stringstream ss;
  ss << file.rdbuf();
  std::string bytecode(ss.str());
  file.close();

  JSRuntime* rt = JS_NewRuntime();
  JSContext* ctx = JS_NewContext(rt);
  // 如果要加载编译成字节码的 module 需要先设置以下两行
  // js_std_init_handlers(rt);
  // JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);
  // 然后按依赖顺序用 js_std_eval_binary 加载字节码, module 的字节码用JS_EVAL_TYPE_MODULE,实际代码的用JS_EVAL_TYPE_GLOBAL
  // 视情况设置 context,略
  js_std_eval_binary(ctx, reinterpret_cast<const uint8_t*>(bytecode.data()), bytecode.size(), JS_EVAL_TYPE_GLOBAL);
  js_std_loop(ctx);
  // Clean-up,如果启用了 js_std_init_handlers 需要成对 free 掉
  // js_std_free_handlers(rt);
  JS_FreeContext(ctx);
  JS_FreeRuntime(rt);
}

C++ 调用 JavaScript 函数

来自:https://linuxtut.com/en/16cdbc69d4fd4a3dccbf/

核心:JS_GetPropertyStr(),JS_Call()

#include <cstring>
#include <iostream>
#include "quickjs-libc.h"

int main() {
  const char* js_func = "function add(x, y) { return x + y; }";
  JSRuntime* rt = JS_NewRuntime();
  JSContext* ctx = JS_NewContext(rt);
  if (JS_IsException(JS_Eval(ctx, js_func, strlen(js_func), "<hello>", JS_EVAL_TYPE_GLOBAL))) {
    js_std_dump_error(ctx);
    JS_FreeContext(ctx);
    JS_FreeRuntime(rt);
  }
  // 不需要 js_std_loop 来执行文件
  JSValue global = JS_GetGlobalObject(ctx);
  JSValue add = JS_GetPropertyStr(ctx, global, "add");
  JSValue argv[] = { JS_NewInt32(ctx, 5), JS_NewInt32(ctx, 3) };
  JSValue js_ret = JS_Call(ctx, add, global, sizeof(argv) / sizeof(JSValue), argv);
  int32_t ret;
  JS_ToInt32(ctx, &ret, js_ret);
  std::cout << ret << std::endl;  // 8
  // Clean-up,注意所有 JSValue 都需要清理
  JS_FreeValue(ctx, global);
  JS_FreeValue(ctx, add);
  JS_FreeValue(ctx, argv[0]);
  JS_FreeValue(ctx, argv[1]);
  JS_FreeValue(ctx, js_ret);
  JS_FreeContext(ctx);
  JS_FreeRuntime(rt);
  return 0;
}

如果不清理干净所有 JSValue,会出现类似 Assertion `list_empty (& rt-> gc_obj_list)' failed. 的报错。

JavaScript 调用 C 函数

来自:https://linuxtut.com/en/16cdbc69d4fd4a3dccbf/

核心:JSValue JSCFunction(JSContext* ctx, JSValueConst jsThis, int argc, JSValueConst* argv),JS_SetPropertyStr()函数

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include "quickjs-libc.h"

// 定义一个输出函数
JSValue MyPrint(JSContext *ctx, JSValueConst jsThis, int argc, JSValueConst *argv) {
  for (int i = 0; i < argc; i++) {
    if (i > 0) {
      fputc(' ', stdout);
    }
    const char* str = JS_ToCString(ctx, argv[i]);
    if (!str) {
      return JS_EXCEPTION;
    }
    fputs(str, stdout);
    JS_FreeCString(ctx, str);
  }
  fputc('\n', stdout);
  return JS_UNDEFINED;
}

int main() {
  const char* js_code = "myprint(\"hello\", \"world\");";
  JSRuntime *rt = JS_NewRuntime();
  JSContext *ctx = JS_NewContext(rt);
  // 把 C 函数绑定到 JS Context 的 global 上
  JSValue global = JS_GetGlobalObject(ctx);
  JS_SetPropertyStr(ctx, global, "myprint", JS_NewCFunction(ctx, MyPrint, "myprint", 1));
  if (JS_IsException(JS_Eval(ctx, js_func, strlen(js_func), "<hello>", JS_EVAL_TYPE_GLOBAL))) {
    js_std_dump_error(ctx);
    JS_FreeContext(ctx);
    JS_FreeRuntime(rt);
  }
  js_std_loop(ctx);
  JS_FreeValue(ctx, global);
  JS_FreeContext(ctx);
  JS_FreeRuntime(rt);
  return 0;
}

在 C++ 层暴露一个类给 JavaScript

来自:https://linuxtut.com/en/16cdbc69d4fd4a3dccbf/

示例:将 std:mt19937 随机数生成器暴露给 QuickJS,有一个 generate() 方法,每调用一次生成一个随机数。

JavaScript 代码:

const mt = new Mt19937();
for (let i = 0; i < 10; ++i) {
  console.log(mt.generate());  //random number(BigInt)To output
}

C++ 代码:

#include <random>
#include <type_traits>
#include "quickjs-libc.h"

// Class ID
static JSClassID Mt19937ClassID;

// 构造函数
JSValue Mt19937Constrctor(JSContext* ctx, JSValueConst jsThis, int argc, JSValueConst* argv) {
  JSValue obj = JS_NewObjectClass(ctx, Mt19937ClassID);
  bool fail = false;
  if (argc == 0) {
    // JS_SetOpaque 用于在 JS object 里放一个指针
    JS_SetOpaque(obj, new std::mt19937);
  }
  else {
    fail = true;
  }
  if (fail) {
    JS_FreeValue(ctx, obj);
    return JS_EXCEPTION;
  }
  return obj;
}

// 析构函数(用于GC)
void Mt19937Finalizer(JSRuntime* rt, JSValue val) {
  // JS_GetOpaque 取出 JS_SetOpaque 放进去的指针
  std::mt19937* p = static_cast<std::mt19937*>(JS_GetOpaque(val, Mt19937ClassID));
  delete p;
}

// generate 方法
JSValue Mt19937Generate(JSContext* ctx, JSValueConst jsThis, int argc, JSValueConst* argv) {
  std::mt19937* p = static_cast<std::mt19937*>(JS_GetOpaque(jsThis, Mt19937ClassID));
  return JS_NewBigUint64(ctx, (*p)());
}

// 方法列表
const JSCFunctionListEntry randFuncs[] = {
  JS_CFUNC_DEF("generate", 1, Mt19937Generate)
};

// 类定义
JSClassDef Mt19937Class { "mt19937", Mt19937Finalizer };

int main() {
  JSRuntime* rt = JS_NewRuntime();
  JSContext* ctx = JS_NewContext(rt);
  js_std_add_helpers(ctx, 0, nullptr);
  // 初始化 Class ID
  JS_NewClassID(&Mt19937ClassID);
  // 注册类
  JS_NewClass(ctx, Mt19937ClassID, &Mt19937Class);
  // 设置类的原型
  JSValue Mt19937Proto = JS_NewObject(ctx);
  JS_SetPropertyFunctionList(ctx, Mt19937Proto, Mt19937Funcs, std::extent_v<decltype(Mt19937Funcs)>);
  JS_SetClassProto(ctx, Mt19937ClassID, Mt19937Proto);
  // 添加到 global
  JSValue global = JS_GetGlobalObject(ctx);
  JS_SetPropertyStr(ctx, global, "Mt19937", JS_NewCFunction2(ctx, Mt19937New, "Mt19937", 1, JS_CFUNC_constructor, 0));
  // 执行和清理略
  return 0;
}

编写一个 C Module

来自:https://linuxtut.com/en/16cdbc69d4fd4a3dccbf/

以上面的 class 做例子,把这个 class 塞进一个 module 里。

JavaScript 代码:

import { Mt19937 } from "rand";
const mt = new Mt19937();
for (let i = 0; i < 10; ++i) {
  console.log(mt.generate());  //random number(BigInt)To output
}

C++ 代码:

#include <random>
#include <type_traits>
#include "quickjs-libc.h"

// Class ID……
// 构造函数……
// 析构函数……
// 方法和方法列表……
// 类定义……

// Module 列表
const JSCFunctionListEntry randFuncs[] = {
  // 之前 C++ 适配化里定义的 CXX_JSCFENUM_GEN 宏是这么用的
  JS_CFUNC_SPECIAL_DEF("Mt19937", 1, CXX_JSCFENUM_GEN(constructor), Mt19937Constrctor)
};

// 把模块里的初始化都放在这里,用于注册模块的时候 QuickJS 回调
int initRand(JSContext* ctx, JSModuleDef* m) {
  JS_NewClassID(&Mt19937ClassID);
  JS_NewClass(JS_GetRuntime(ctx), Mt19937ClassID, &Mt19937Class);
  JSValue Mt19937Proto = JS_NewObject(ctx);
  JS_SetPropertyFunctionList(ctx, Mt19937Proto, Mt19937Funcs, std::extent_v<decltype(Mt19937Funcs)>);
  JS_SetClassProto(ctx, Mt19937ClassID, Mt19937Proto);
  return JS_SetModuleExportList(ctx, m, randFuncs, std::extent_v<decltype(randFuncs)>);
}

int main() {
  JSRuntime* rt = JS_NewRuntime();
  JSContext* ctx = JS_NewContext(rt);
  js_std_add_helpers(ctx, 0, nullptr);
  // 注册模块
  JSModuleDef* m = JS_NewCModule(ctx, "rand", initRand);
  JS_AddModuleExportList(ctx, m, randFuncs, 1);
  // 执行和清理略
  return 0;
}

骚动时节的少女们啊:性,纯洁与爱

乙女どもよ。 - CHiCO with HoneyWorks

《荒乙》明明是7月番,现在却都已经12月了。“故懒癌者无药医也”的梗也快说腻了,但是毕竟灵感这东西不是随时都能有的,况且说白了不过是写出来自娱自乐,时效性什么的也就无所谓了——更何况我哪怕不是写出来自娱自乐的东西也从来没注意过时效性。

继续阅读“骚动时节的少女们啊:性,纯洁与爱”

写于退货 AirPods Pro 之后

2020年8月,我在拼多多用1380的价格拿下了AirPods Pro,在这个价格下,以下提到的所有问题都可以忍受了,真香。另外,“廉价的蓝莓味”已经确定是部分批次的偶发问题,新买的这个没有那么大的味道。以下是2019年12月1日发布的原文:

在两周焦急的等待以及三天不甚理想的使用之后,我最终还是退货了 AirPods Pro 。

继续阅读“写于退货 AirPods Pro 之后”