JSFuck

2019/03/05更新

「非実用ネタ」のカテゴリでは、プログラミングに関する実用性の無いジョークやネタをまとめています。

目次

JSFuckとは

JSFuckとは、任意のJavaScriptのコードを[]()!+6文字だけで記述するという遊びである。わずか8種類の記号だけで任意のプログラムを記述可能な代表的難解プログラミング言語「Brainfuck」と似たようなことをJavaScriptで行なうことからこの名がある。なお、名称に卑語が含まれるため、表記や発音の際には注意を要する。

JSFuckの基本形

JavaScriptのコードをどうやって、6文字だけで記述するのか。JSFuckの基本的なアイデアは、以下の形である。

[]['constructor']['constructor']('コード')()

これは以下のようにして単なる無名関数の実行を変形して得られたものである。

// 通常の無名関数の実行
(function(){コード})()

// Functionコンストラクタを使用することで、コードを文字列で記述できる。
Function('コード')()

// Functionは、任意のクラスのconstructorである。
Array.constructor('コード')()

// Arrayは、任意の配列リテラルのconstructorである。
[].constructor.constructor('コード')()

// プロパティアクセスに.~ではなく[~]を使用する。
[]['constructor']['constructor']('コード')()

この形を用いることで、単なる無名関数の実行を[]()の4つの記号と文字列だけで行なうことができるのである。

文字列の作成

そこで、次は任意の文字列を[]()!+の6文字で作成することを考える。もしそれができれば、前述の基本形に当てはめることで任意のコードが実行可能となる。

まず、数字は以下の要領で作れる。

+[]              // 0
+!+[]            // 1
+!+[]+!+[]       // 2
+!+[]+!+[]+!+[]  // 3
  …
+!+[]+[+[]]      // 10
+!+[]+[+!+[]]    // 11
  …

例えば、0は、単項演算子+によって空配列[]を数値化して作る。また、1は、0を!で真偽値trueに変換し、もう一度+で数値化して作る。比較的小さい数は、こうして作った1を数値として足し合わせていけばよい。大きい数は、文字列として各桁の数字を足して作る。文字列連結を行なうには、足し合わせる前に[~]で囲って配列にすればよい。こうすることで、暗黙的に[~].join()が呼ばれて文字列化される。

次に、特殊値の文字列化と添え字による文字列アクセスにより、以下のようなアルファベットが比較的簡単に作れる。

(!+[]+[])[N]    // 'true'[N] → t、r、e
(![]+[])[N]     // 'false'[N] → f、a、l、s
([][[]]+[])[N]  // 'undefined'[N] → u、n、d、i

さらに、ネイティブメソッドを文字列化することで、以下のようなアルファベットと記号が得られる。

([][(![]+[])[+[]]+([][[]]+[])[+!+[]+!+[]+!+[]+!+[]+!+[]]+(![]+[])[+!+[]+!+[]]+(![]+[])[+!+[]+!+[]]]+[])[N]
// ([]['false'[0]+'undefined'[5]+'false'[2]+'false'[2]]+[])[N]
// ([]['f'+'i'+'l'+'l']+[])[N]
// ([].fill+'')[N]
// → 'function fill() { [native code] }' (c、o、v、(、)、{、}、[、]、スペース)

ここまでで、まだ全てのアルファベットは揃っていないが、基本形を使うために必要な「constructor」を始め、「return」や「alert()」などの文字列はすでに作ることができる。例えば、alert(1)を実行するコードは以下となる。

[][([][(![]+[])[+[]]+([][[]]+[])[+!+[]+!+[]+!+[]+!+[]+!+[]]+(![]+[])[+!+[]+!+[]]+(![]+[])[+!+[]+!+[]]]+[])[+!+[]+!+[]+!+[]]+([][(![]+[])[+[]]+([][[]]+[])[+!+[]+!+[]+!+[]+!+[]+!+[]]+(![]+[])[+!+[]+!+[]]+(![]+[])[+!+[]+!+[]]]+[])[+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+([][[]]+[])[+!+[]]+(![]+[])[+!+[]+!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([][[]]+[])[+!+[]+!+[]+!+[]+!+[]+!+[]]+(![]+[])[+!+[]+!+[]]+(![]+[])[+!+[]+!+[]]]+[])[+!+[]+!+[]+!+[]]+(!+[]+[])[+[]]+([][(![]+[])[+[]]+([][[]]+[])[+!+[]+!+[]+!+[]+!+[]+!+[]]+(![]+[])[+!+[]+!+[]]+(![]+[])[+!+[]+!+[]]]+[])[+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]][([][(![]+[])[+[]]+([][[]]+[])[+!+[]+!+[]+!+[]+!+[]+!+[]]+(![]+[])[+!+[]+!+[]]+(![]+[])[+!+[]+!+[]]]+[])[+!+[]+!+[]+!+[]]+([][(![]+[])[+[]]+([][[]]+[])[+!+[]+!+[]+!+[]+!+[]+!+[]]+(![]+[])[+!+[]+!+[]]+(![]+[])[+!+[]+!+[]]]+[])[+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+([][[]]+[])[+!+[]]+(![]+[])[+!+[]+!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([][[]]+[])[+!+[]+!+[]+!+[]+!+[]+!+[]]+(![]+[])[+!+[]+!+[]]+(![]+[])[+!+[]+!+[]]]+[])[+!+[]+!+[]+!+[]]+(!+[]+[])[+[]]+([][(![]+[])[+[]]+([][[]]+[])[+!+[]+!+[]+!+[]+!+[]+!+[]]+(![]+[])[+!+[]+!+[]]+(![]+[])[+!+[]+!+[]]]+[])[+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[+!+[]+!+[]]+(!+[]+[])[+!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]+(!+[]+[])[+[]]+([][(![]+[])[+[]]+([][[]]+[])[+!+[]+!+[]+!+[]+!+[]+!+[]]+(![]+[])[+!+[]+!+[]]+(![]+[])[+!+[]+!+[]]]+[])[+!+[]+[]+(+!+[]+!+[]+!+[])]+(+!+[])+([][(![]+[])[+[]]+([][[]]+[])[+!+[]+!+[]+!+[]+!+[]+!+[]]+(![]+[])[+!+[]+!+[]]+(![]+[])[+!+[]+!+[]]]+[])[+!+[]+[]+(+!+[]+!+[]+!+[]+!+[])])()

さらに足りない文字列は、以下のようにして作っていく。

このようにして、非常に手間ではあるものの、[]()!+の6文字だけで任意の文字を作ることが可能なため、基本形にあてはめることで[]()!+の6文字だけで任意のコードが実行可能となる。

ここまで見てきて分かる通り、JSFuckは単なるプログラミング上の遊びであるため、実用性は無い。ただし、JSFuckを理解することは、JavaScriptそのものの理解を深めることにつながると考えることはできる。

発展

将来のECMAScriptの草案として考えられているパイプライン演算子「|>」を使うと、[]+|>の5文字で任意のコードが書けるようになるという(外部リンク参照)。

外部リンク