TL; DR
JavaScriptファイルをプリコンパイルしておける機能。
ただし、まだ実験段階なので2019年5月時点で実用は出来ません。
サポートしているのは**Firefox Nightly**版のみ。
BinaryASTとは
BinaryASTは、JavaScriptの初回ロードを高速化します。
初回ロードの高速化というと、ダウンロード速度の方をイメージしてしまいますが、
ダウンロード後(あるいはキャッシュ読み込み後)のブラウザでのパース作業を高速化します。
ASTについて
ブラウザで実行する通常のJavaScriptコードの場合、ソースはコードの構文構造を記述する**抽象構文木(Abstract Syntax Tree, AST)**と呼ばれる中間表現に解析されます。
この表現はバイトコードまたはネイティブのマシンコードにコンパイルして実行することができます。
const hoge = 3 + 4;
{
"type": "Program",
"body": [
{
"type": "VariableDeclaration",
"declarations": [
{
"type": "VariableDeclarator",
"id": {
"type": "Identifier",
"name": "hoge"
},
"init": {
"type": "BinaryExpression",
"operator": "+",
"left": {
"type": "Literal",
"value": 3,
"raw": "3"
},
"right": {
"type": "Literal",
"value": 4,
"raw": "4"
}
}
}
],
"kind": "const"
}
],
"sourceType": "script"
}
JavaScriptコードからASTへの変換は以下のページで試せます。
http://esprima.org/demo/parse.html
高速化の仕組み
BinaryASTは、元のJavaScriptコードとそれに関連付けられたデータ構造をバイナリ形式で表現します。
これによりクライアントでの上記パース作業を簡略化しようという仕組みです。
モバイル端末などのローエンドなクライアントほど効果が出ます。
pre-encode
エンコーダーはRustで実装されているようです。試しにエンコードしてみます。
お試しなのでサクッとDockerで実行します。
docker run --rm -it ubuntu
apt-get update
apt-get upgrade
apt-get install curl
curl https://sh.rustup.rs -sSf | sh
1) Proceed with installation (default)
source ~/.cargo/env
apt-get install nodejs npm
apt-get install git
git clone https://github.com/binast/binjs-ref
cd binjs-ref
cargo run --bin binjs_encode -- --help
cargo run --bin binjs_decode -- --help
ここまでで環境準備は整いました。
エンコードを試してみます。
const hoge = 3 + 4;
console.log(hoge);
cargo run --bin binjs_encode -- -i hoge.js -o output
cd output
ll
total 28
drwxr-xr-x 2 root root 4096 May 19 04:33 ./
drwxr-xr-x 15 root root 4096 May 19 04:33 ../
-rw-r--r-- 1 root root 379 May 19 04:33 hoge.binjs
-rw-r--r-- 1 root root 10 May 19 04:33 hoge.grammar
-rw-r--r-- 1 root root 39 May 19 04:33 hoge.js
-rw-r--r-- 1 root root 10 May 19 04:33 hoge.strings
-rw-r--r-- 1 root root 10 May 19 04:33 hoge.tree
binjsファイルが出来ていますね。
BINJS[GRAMMAR]identity;(IdentifierExpression0LiteralNumericExpression(AssertedDeclaredName2AssertedScriptGlobalScope BinaryExpression"BindingIdentifier CallExpression&ExpressionStatement
Script,StaticMemberExpression&VariableDeclaration$VariableDeclarator[STRINGS]identity;P
hogeconsolelog+
constconst lexical[TREE]identity;Z @@
デコーダーも試してみましょう。
cargo run --bin binjs_decode -- ./output/hoge.binjs decode_hoge.js
スペースや改行は消されましたが、戻っていますね。
行末のセミコロンも消されているので、minifyされているんでしょうか。
const hoge=3+4;console.log(hoge)
ついでにダンプも実行してみます。
cargo run --bin binjs_dump -- ./output/hoge.binjs
ダンプ結果
Attempting to decode as multipart.
0 : 10 # Script {
: # .scope
1 : 06 # AssertedScriptGlobalScope {
: # .declaredNames
2 : 02 # list (length=1) [
3 : 04 # AssertedDeclaredName {
: # .name
4 : 00 # string="hoge"
: # .kind
5 : 0a # string="const lexical"
: # .isCaptured
6 : 00 # bool=false
: # }
: # ]
: # .hasDirectEval
7 : 00 # bool=false
: # }
: # .directives
8 : 00 # list (length=0) []
: # .statements
9 : 04 # list (length=2) [
10 : 14 # VariableDeclaration {
: # .kind
11 : 08 # string="const"
: # .declarators
12 : 02 # list (length=1) [
13 : 16 # VariableDeclarator {
: # .binding
14 : 0a # BindingIdentifier {
: # .name
15 : 00 # string="hoge"
: # }
: # .init
16 : 08 # BinaryExpression {
: # .operator
17 : 06 # string="+"
: # .left
18 : 02 # LiteralNumericExpression {
: # .value
19 : 00 00 00 00 00 00 08 40 # float=3
: # }
: # .right
27 : 02 # LiteralNumericExpression {
: # .value
28 : 00 00 00 00 00 00 10 40 # float=4
: # }
: # }
: # }
: # ]
: # }
36 : 0e # ExpressionStatement {
: # .expression
37 : 0c # CallExpression {
: # .callee
38 : 12 # StaticMemberExpression {
: # .object
39 : 00 # IdentifierExpression {
: # .name
40 : 02 # string="console"
: # }
: # .property
41 : 04 # string="log"
: # }
: # .arguments
42 : 02 # list (length=1) [
43 : 00 # IdentifierExpression {
: # .name
44 : 00 # string="hoge"
: # }
: # ]
: # }
: # }
: # ]
: # }
サーバーへの配置
binast-cf-worker を見る感じ、リクエストヘッダーのAccept
にapplication/javascript-binast
が含まれていれば、Content-Type
でapplication/javascript-binast
を指定した.binjsファイルを返せば良さそうです。
上記のサンプルでは、クライアント判定NGの場合は通常の.jsファイルを返すようにしていますが、今回はお試しなので、.binjsファイルなら無理やりapplication/javascript-binast
にしてみます。
また、**httpsでないとbinASTは有効にならない**ようですので、ローカルでテストするときは注意。
とりあえずオレオレ証明書で回避しておきます。
docker run --rm --name binjstest -v ~/output:/usr/share/nginx/html:ro -d -p 8080:80 nginx
docker exec -it binjstest bash
apt-get update
apt-get install openssl
openssl genrsa 2048 > server.key
openssl req -new -key server.key > server.csr
openssl x509 -days 3650 -req -signkey server.key < server.csr > server.crt
mkdir /etc/nginx/ssl
mv server* /etc/nginx/ssl
apt-get install vim
vim /etc/nginx/conf.d/default.conf
vim /etc/nginx/mime.types
nginx -s reload
server {
listen 80 ssl;
server_name localhost;
ssl_certificate_key /etc/nginx/ssl/server.key;
ssl_certificate /etc/nginx/ssl/server.crt;
:
types {
text/html html htm shtml;
text/css css;
text/xml xml;
image/gif gif;
image/jpeg jpeg jpg;
application/javascript js;
application/javascript-binast binjs;
:
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<script type="text/javascript" src="hoge.binjs"></script>
</head>
<body>
</body>
</html>
クライアント準備
現在BinaryASTをサポートしているのは**Firefox Nightly**版のみとのことで、インストールします。
about:configにて以下を設定します。
dom.script_loader.binast_encoding.domain.restrict
をfalse
dom.script_loader.binast_encoding.enabled
をtrue
hoge.htmlをブラウザで表示
以下のエラー。えぇ、プレビュー版はconstは使えないのかよ!
SyntaxError: BinAST Parsing Error: Const is not supported in this preview release hoge.binjs
constをvarに変えて再度エンコード。デプロイして再確認。
やっとできました。ちゃんとBinary表現になっていますね。結果も正しく動いています。
まとめ
情報が少ない中、何とかここまでたどり着けました。
今回は短いソースなので、ファイルサイズも逆に大きくなってしまいましたが、次回は大きなファイルでどれだけ変わるかやってみようと思います。(そこが重要)
ちなみに、公式デモサイトである下記は、正しくmime-typeを返していない為binjsになっていないようです。(2019/05/23)
https://serve-binjs.that-test.site/
参考元: https://blog.cloudflare.com/binary-ast/
2019/06/07 追記
公式デモサイトが修正されたようで、正しくbinjsで取得されるようになりました。