Open4

nccc: Node.jsで絵が出るまで頑張る会

okuokuokuoku

前にChez SchemeでやったのをNode.jsでやってみる。

https://zenn.dev/okuoku/scraps/164730a000cecb

問題になりそうなのは:

  • ポインタとして BigInt を使いたいけどできるのか問題。過去には普通にJavaScript的な Number を使ってたけど、これはポインタの値域が53ビットを越えると危いので。また、 Duktape はそもそも BigInt をサポートしていないのでどうしようもない。。Hermes、XS、QuickJSはサポートしている。
  • macOS上のNode.jsはスクリプトをメインスレッドで実行するのか問題。UIスレッドはメインスレッドでなければならない。
okuokuokuoku

モジュールのlookupでいきなりつまづく

Node.jsは標準ベースの ECMA Script Module(ESM)と伝統的なCommonJSモジュールの両方をサポートしているがサポート範囲が絶妙に異なっている。

  • ESMをESMなプログラム(= .mjs)からESMとしてロードする場合は拡張子を省略できない。
  • ESMの import 構文は ネイティブモジュールをロードできない。 ESMからネイティブモジュールをロードするには、CommonJSの require をimportしてきて動的ロードとして使う。
  • ESMをESMとしてロードする場合は、Node.js標準の node_modules 検索パスは使われない。このパスは CommonJS 用。

いろいろ考えたけど、殆どのケースではESMで十分なので、Node.js固有のコードだけCommonJS形式にして、他をESM(= 相対パスでファイル名を直接指定)ということにした。

https://github.com/okuoku/nccc/commit/38db8445f44c777a047040e8246d0dfb8798de81

https://github.com/okuoku/em2native-tests/commit/99ce995a3dca1a7f37db6ff894f13d1fb1a71bd8

import { createRequire } from 'module';
                                                                                                                                                              
// Node.js currently does not support loading native module
// using ES6 module syntax
const require = createRequire(import.meta.url);
const nccc = require("node-nccc");
const debug_prefix = require("nccc-debug-prefix");
 
export default {
    nccc: nccc,
    debug_prefix: debug_prefix
};

こういう感じで、 createRequire を使うとCommonJSの require を召喚できる。

okuokuokuoku

モジュールローダーの作成

node-nccc モジュールは dlfcn_opendlfcn_get を提供しているので、それを使ってモジュールローダーを書く。(unloadは無い。動的ライブラリをアンロードする安全な方法は無いため。)

https://github.com/okuoku/nccc/blob/38db8445f44c777a047040e8246d0dfb8798de81/common/ncccdlfcn.c

dlfcn_open: [path] => [res ptr]
dlfcn_close: [handle name] => [res ptr]

これらをJavaScript的なクロージャーにするにはネイティブモジュールに実装した make_nccc_call を使う。

https://github.com/okuoku/nccc/blob/38db8445f44c777a047040e8246d0dfb8798de81/javascript/node-nccc/node-nccc.c#L402-L404

https://github.com/okuoku/em2native-proto/blob/25550e323bd55518bbd4d0daaaac4fcf1b041a13/runtime/ncccutil.mjs#L59-L64

まぁこの辺は過去のコードのコピペで良いかな。

okuokuokuoku

問題なく絵は出た

ちゃんとメインスレッドで動いているようだ。まだC-WebGLの関数を直接使っているが、これをまたWebGLにマップすれば前みたいにEmscriptenなプログラムも動かせるだろう。

const initr = yfrm_init();
const ctx_cwgl = yfrm_cwgl_ctx_create(1280, 720, 0, 1);

cwgl_viewport(ctx_cwgl, 0, 0, 1280, 720);

let cur = 0.0;
const evq = ncccutil.malloc(256 * 4);

const COLOR_BUFFER_BIT = 0x4000;
function fill(ctx){
    yfrm_frame_begin0(ctx);
    cwgl_clearColor(ctx, cur, cur, cur, cur);
    cwgl_clear(ctx, COLOR_BUFFER_BIT);
    yfrm_frame_end0(ctx);
}

function step(ctx){
    yfrm_wait0(0);
    const r = yfrm_query0(0, evq, 256 * 4);
    console.log("EVENT:", r);
    cur += 0.05;
    if(cur > 1.0){
        cur -= 1.0;
    }
    console.log(cur);
    fill(ctx);
}

for(;;){
    step(ctx_cwgl);
}