以前に QuickJS を WebAssembly に変換して ブラウザ上で使う というポストを書いたのですが、 その後、もっと良いガイド Running self-hosted QuickJS in a browser があったので、こちらの方法でやり直します。
この方法であれば、 https://github.com/justjake/quickjs-emscripten を使うことで、emcc コマンドが不要。 自分で emscripten をインストールしなくてよいので助かる。
$ node --version
v18.17.1
$ npm --version
10.8.3
これは Running self-hosted QuickJS in a browser の手順通り実行しただけです。
$ mkdir qjs
$ cd qjs
$ npm install quickjs-emscripten webpack webpack-cli
あとは・・・全く同じ手順なので、省略します。
package.json に記載された各ライブラリのバージョン:
{
"dependencies": {
"quickjs-emscripten": "^0.31.0",
"webpack": "^5.95.0",
"webpack-cli": "^5.1.4"
}
}
npx webpack すると dist/ 以下に ライブラリ生成されます。
ls -l -h するとこんな感じです:
507K 0393610d054d74f0601f.wasm
241 1.quickjs.js
31K 226.quickjs.js
2.1K 318.quickjs.js
13K 672.quickjs.js
6.1K 964.quickjs.js
4.0K quickjs.js
この Node.js プロジェクトのルートに index.html を作成します。
<html>
<head>
<script src="dist/quickjs.js"></script>
</head>
<body>
Hello, QuickJS!
<script>
getQuickJS().then((QuickJS)=>{
const vm = QuickJS.newContext();
const world = vm.newString("World");
vm.setProp(vm.global, "name", world);
world.dispose();
const result = vm.evalCode(`"Hello, " + name + "!"`);
if( result.error ){
result.error.dispose();
} else {
console.log(vm.dump(result.value));
result.value.dispose();
}
vm.dispose();
});
</script>
</body>
</html>
そして web server を起動 python3 -m http.server 8000 ブラウザで http://localhost:8000/ を開きます。
ブラウザのコンソールを見ると Hello, World! と表示されているはずです。
これでこの QuickJS のコンテキストに name という文字列の変数を用意することができたのはわかりました。
こちらのサンプルを参考にしたらなんとかできました。 https://github.com/suchipi/js-sandbox-demo/blob/4f631c905968153f9b5c8351ef03036d26ad1b28/src/shortlived-vm.ts
const vm = QuickJS.newContext();
const myobj = vm.newObject();
const world = vm.newString("World");
vm.setProp(myobj, "name", world);
vm.setProp(vm.global, "myobj", myobj);
myobj.dispose();
world.dispose();
const result = vm.evalCode(`"Hello, " + myobj.name + "!"`);
実行すると ブラウザのコンソールに Hello, World! と表示されます。
ここ https://github.com/justjake/quickjs-emscripten/blob/main/doc/README.md の Exposing APIs のセクションにやり方が書いてあります。
const greetingF = vm.newFunction("greeting", (...args)=> {
const nativeArgs = args.map(vm.dump)
if( nativeArgs.length > 0 ){
return vm.newString("Hello, " + nativeArgs[0] + "!");
} else {
return vm.newString("Hello!");
}
});
vm.setProp(vm.global, "greeting", greetingF);
greetingF.dispose();
const result = vm.evalCode(`greeting("Pikachu")`);
実行すると ブラウザのコンソールに Hello, Pickachu! と表示されます。
完成した index.html を載せます。
<html>
<head>
<script src="dist/quickjs.js"></script>
</head>
<body>
Hello, QuickJS!
<script>
getQuickJS().then((QuickJS)=>{
const vm = QuickJS.newContext();
/*
const world = vm.newString("World");
vm.setProp(vm.global, "name", world);
world.dispose();
const result = vm.evalCode(`"Hello, " + name + "!"`);
*/
/*
const myobj = vm.newObject();
const world = vm.newString("World");
vm.setProp(myobj, "name", world);
vm.setProp(vm.global, "myobj", myobj);
myobj.dispose();
world.dispose();
const result = vm.evalCode(`"Hello, " + myobj.name + "!"`);
*/
const greetingF = vm.newFunction("greeting", (...args)=> {
const nativeArgs = args.map(vm.dump)
if( nativeArgs.length > 0 ){
return vm.newString("Hello, " + nativeArgs[0] + "!");
} else {
return vm.newString("Hello!");
}
});
vm.setProp(vm.global, "greeting", greetingF);
greetingF.dispose();
const result = vm.evalCode(`greeting("Pikachu")`);
if( result.error ){
result.error.dispose();
} else {
console.log(vm.dump(result.value));
result.value.dispose();
}
vm.dispose();
});
</script>
</body>
</html>
以上です。