JavaScriptは非常に簡単な言語です。たとえ初心者であっても、魔法のようにさまざまなことが実現できます。しかしその仕様は複雑で、「悪用」すれば多くの人がびっくりするようなコードを書くことができます。
今回は、JavaScriptを悪用して難解プログラミングに挑んでみましょう。
JavaScriptと難解プログラミング
難解プログラミング言語というのは、わざと人間が理解できないように作られたプログラミング言語のことです。難解プログラミング言語としてはBrainf*ckやWhitespaceなどが有名です。これらの存在はほぼジョークであり、実用するわけではありません。
JavaScriptは難解プログラミング言語からは程遠いように思えます。しかし仕様をちょっと悪用すれば、非常に難解なプログラミング言語として活用することができます。
例えば、以下のコードは驚くべきことにJavaScriptとしてvalidです。
[]
[
(![]+[])[+[]]+
([![]]+[][[]])[+!+[]+[+[]]]+
(![]+[])[!+[]+!+[]]+
(![]+[])[!+[]+!+[]]
]
[
([][(![]+[])[+[]]+
([![]]+[][[]])[+!+[]+[+[]]]+
(![]+[])[!+[]+!+[]]+
(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+
(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+
(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+
(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+
(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+
(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+
([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+
(!![]+[])[+!+[]]
]
([
([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+
(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+
([][[]]+[])[+!+[]]+
(![]+[])[!+[]+!+[]+!+[]]+
(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+
(![]+[])[!+[]+!+[]]+
(!![]+[])[!+[]+!+[]+!+[]]+
(+(+!+[]+[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+[!+[]+!+[]]+[+[]])+[])[+!+[]]+
(![]+[])[!+[]+!+[]]+
(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+
(+![]+[![]]+([]+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+
(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+
[+[]]+[]+
(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]
])
()
いったい何がどうvalidなのかはわかりませんが、これを実行するとコンソール上に「0」と表示されます。確かにJavaScriptとして正しいようです。
このようにして、JavaScriptを難解プログラミング言語として使用することができます。
[]()!+
JavaScriptでは、[]()!+の6文字さえ使えれば、理論上どんなプログラムでも書くことができます。
その基本的なアイデアは配列の活用にあります。
JavaScriptにおいて配列は様々な特性を持ちます。まず、当然配列は配列として扱うことができます。そして配列はオブジェクトであるので、truthyでもあります。またブラケット記法を用いてオブジェクトのプロパティにアクセスができます。
これらの仕組みと、暗黙の型変換を用いれば、様々なことが実現できます。
true, false, 0, 1
まずは簡単なところから行ってみましょう!
配列はリテラル[]で空の配列を生成することができます。そして空の配列はオブジェクト型であるので、truthyです(注意!空の配列はfalsyではありません!)。
これにnot演算子(!)をつければ暗黙の型変換によりfalseを生成できます。
![]
さらにこのfalseにnot演算子をつければtrueになります。
!![]
このあたりはまだ簡単ですね。
次に数値の0を生成してみましょう。数値の0はプラス単項演算子を用いた暗黙の型変換により作ります。
まずは答えを出しましょう。
+[]
プラス単項演算子は値を数値に変換します。そのためにはまず文字列への変換が入ります。そして配列の文字列化にはjoinメソッドが使われるので、空配列の文字列表現は””です。空文字列は0に変換されるので、これで0を得ることができます。
次に1を生成してみます。今度も同様にプラス単項演算子を用います。trueがプラス単項演算子で1に変換される特性を使います。
+!+[]
+[]で0を作ります。そして!0でtrueを作ります(0はfalsyなの否定でtrueになります)。そしてそのtrueをプラス単項演算子で数値に変換することにより、1ができます。
1が作れたのであとは他の数値も作れます。
2を作ってみましょう。プラス二項演算子はtrueを1、falseを0に変換します。つまりtrue + trueを作れば2を作れます。
!+[]+!+[]
これで2になります。あとの数字を作るには足していくだけですね。
文字列
今度は文字列を作ってみましょう。
文字列作成のアイデアとしては、「true」「false」などを文字列化して、その中から文字を取り出すというものが考えられます。使える文字は限られますが、一番簡単な方法です。
まずfalseの一文字目の”f”を取り出したいと思います。falseの作り方はすでに学んだので、あとはそれを文字列化して文字を取り出すだけです。
文字列化にはプラス二項演算子が使えます。プラス二項演算子はオブジェクトを文字列型に変換します。そして真偽値型に文字列を足すと文字列になります。この性質を使います。
まずfalseを”false”に変換します。
![]+[]
![]でfalseですね。そして[]を足すと、[]は配列=オブジェクトなので、文字列に変換されます。空配列なので””になります。つまりfalse + “”なので、結果は”false”になります。
そして文字列のn番目にはブラケット記法を用いてアクセスできます。”false”の0番目にアクセスしたければ、!([]+[])[0]とすればいいわけです、この0も+[]に置き換えて:
(![]+[])[+[]]
これで”f”が取れました!true、falseの他の文字についても同様です。
true、falseに含まれていない文字については、なんとか頑張って生成するか、文字コードからの生成を用います。
undefined, NaN
値の文字列表現から文字を取り出せることがわかりました。そうなると普段は役立たずのundefinedなども欲しくなってきますね。例えば「i」なんてtrueにもfalseにも入ってませんから。
undefinedは配列の中の値の存在しないインデックスにアクセスすれば得ることができます。[]の中の[+[]]番目とかですね。さらにブラケット内の値は強制的に文字列に変換されるため、+は省略できます。つまり以下のようになります:
[][[]]
NaNもやってみましょう。プラス単項演算子を用いますが、プラス単項演算子に適当な文字列を与えるとNaNになります。たとえば+”false”はNaNです。
“false”は先ほど作りましたね。これにプラス単項演算子をつければよさそうです。
+(![]+[])
しかしもうちょっと短い方法があります。+[false]のほうが短くなります。これはプラス単項演算子がオブジェクトを文字列に変換するため、[false]は[false].join()が実行され、”false”になるからです。
[false]は[![]]でかけるため、以下のようになります。
+[![]]
これでNaNも作れました。
任意のコード
ここまでの知識があれば、あとは任意のコードが実行できます。任意のコード実行ができるとオモチャとしてはつまらなくなるので、これをやっておひらきにしましょう。
任意のコード実行にはFunctionオブジェクトのコンストラクタを使います。Functionオブジェクトのコンストラクタに文字列を渡すと、evalしてそれを実行する関数を返してくれます。つまり、なんでも実行できます。
しかしどこからFunctionオブジェクトのコンストラクタを?
実はこれはものすごく単純な方法で取得でき、配列のメソッドからコンストラクタを取得します。JavaScriptにおいてメソッドはただの関数であるのと、各オブジェクトはコンストラクタを保持しています。よって配列からFunctionオブジェクトのコンストラクタを取得できます。
どのメソッドでもいいのですが、できれば短く済むものがいいですね。fillメソッドなんてのはどうでしょう。fillメソッドからコンストラクタを取り出し、コードを入れ込みます。
[]["fill"]["constructor"](YOUR_CODE_HERE)
オブジェクトのプロパティにはブラケット記法でアクセスできるので、配列オブジェクトのfillメソッドのconstructorへのアクセスはこれでできます。
そしてこれで得られた関数を実行します。
[]["fill"]["constructor"](YOUR_CODE_HERE)()
あとはこれをひとつひとつ分解していくだけです。fはfalseから、iはundefinedから、lはfalseから、cはFunctionから……とひとつひとつ頑張って取ってきます。
[]
[
(![]+[])[+[]]+
([![]]+[][[]])[+!+[]+[+[]]]+
(![]+[])[!+[]+!+[]]+
(![]+[])[!+[]+!+[]]
]
[
([][(![]+[])[+[]]+
([![]]+[][[]])[+!+[]+[+[]]]+
(![]+[])[!+[]+!+[]]+
(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+
(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+
(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+
(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+
(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+
(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+
([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+
(!![]+[])[+!+[]]
]
(YOUR_CODE_HERE)
()
そしてYOUR_CODE_HEREのところに好きなコードを書きます。例えばconsole.log(0)なら以下のようになります:
[]
[
(![]+[])[+[]]+
([![]]+[][[]])[+!+[]+[+[]]]+
(![]+[])[!+[]+!+[]]+
(![]+[])[!+[]+!+[]]
]
[
([][(![]+[])[+[]]+
([![]]+[][[]])[+!+[]+[+[]]]+
(![]+[])[!+[]+!+[]]+
(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+
(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+
(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+
(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+
(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+
(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+
([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+
(!![]+[])[+!+[]]
]
([
([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+
(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+
([][[]]+[])[+!+[]]+
(![]+[])[!+[]+!+[]+!+[]]+
(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+
(![]+[])[!+[]+!+[]]+
(!![]+[])[!+[]+!+[]+!+[]]+
(+(+!+[]+[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+[!+[]+!+[]]+[+[]])+[])[+!+[]]+
(![]+[])[!+[]+!+[]]+
(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+
(+![]+[![]]+([]+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+
(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+
[+[]]+[]+
(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]
])
()
これで冒頭に出てきた謎のコードの意味がわかりましたね!
さいごに
JavaScriptを用いた難解プログラミング、いかがだったでしょうか。
お遊びながらもJavaScriptの細かい仕様について知ることができ、意外と楽しかったのではないでしょうか。ここで得た知識は現実ではあまり役に立ちませんが、JavaScriptを理解するための小さな一歩となれば、と思います。
難解プログラミングと言いながらも、文字列で任意コードを生成しての実行という抜け道ができてしまったので、最後は面白くなかったかもしれませんが、全体的に頭の体操になったのではないでしょうか。
こんなことをしなくてもJavaScriptでは普通にプログラムを書けるので、人生においてこのようなとんでもないコードを見ることはないと思いますが、「こんなこともできるんだよ」ということを覚えておけば、隠し芸的に使えるかもしれません。
それでは、よいJavaScriptライフを。