47
\$\begingroup\$

This is not a challenge but a question, I figured it was on topic because of

Non-challenge questions that are related to solving programming puzzles or a particular type of challenge are also on topic.

Now on to the question:

Is it possible to write any JavaScript code with only 5 letters? JSFuck already does this with 6 symbols !+[]() but I wonder if the ! character is needed.

JSFuck works with a combination of casting to string (by adding an empty array), casting to number (by writing a + in front) and casting to boolean by negating. For example:

[]        \\ Empty array
+[]       \\ Cast to number -> 0
!+[]      \\ Negate -> true
!+[]+[]   \\ Cast to string -> "true"

From this string we can extract all it's letters using the square brackets with a number inside, and any number can be made by adding true together that many times.

Like this a lot of letters can be found and can be concatenated to strings. The most important string to be able to create is "constructor" because it can be used to get the Function from any function, and this object can be used to execute strings as JavaScript:

[]["find"]                          \\ the function Array.prototype.find
[]["find"]["constructor"]           \\ the Function object
[]["find"]["constructor"](string)() \\ same as eval(string)

As you can see, ! has 2 uses here:

  • Creating numbers to select letters from strings.
  • Casting to boolean to get "true" and "false".

The first one of these 2 can also be done using the ++ incrementor, not directly on 0, but it can be used on elements inside an array:

+[]          \\ 0
[+[]]        \\ [0]
[+[]][+[]]   \\ [0][0] -> 0
++[+[]][+[]] \\ ++[0][0]-> 1
++[[]][+[]]  \\ also works because ++ casts to number

So all numbers can be created without !.

The second one is more difficult. The importance of "true" and "false" lays in the letters "r" and "s", which both appear in "constructor". I have already found all the other letters of "constructor" by means of "undefined", "Infinity", "NaN" and by casting functions to strings.

So the ultimate question: (How) can you create booleans, or the letters "r" and "s" in JavaScript by only using +[]() ?

The letter "l" might also help. It can be obtained form null but I havent been able to get that value with those 5 symbols. It can for example be used to get booleans if we already have "s":

[]["includes"]()       \\ false
[+[]]["includes"](+[]) \\ true

The letter "l" and "k" together would give access to "r":

([]+[])["link"]() \\ "<a href="undefined"></a>"

Any way to get a boolean, null or any of the letters r s l k would be very useful!

A library of what we have:

Array.prototype.find: [][(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])])+(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])])+(([][[]]+[])[++[[]][+[]]])+(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])])]

Infinity: +((++[[]][+[]]+[])+(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])])+(++[[]][+[]]+[])+(+[])+(+[])+(+[]))

NaN: +[][[]]

undefined: [][[]]

0: +[]

1: ++[[]][+[]]

2: (++[[]][+[]])+(++[[]][+[]])

3: (++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])

4: (++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])

5: (++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])

6: (++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])

7: (++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])

8: (++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])

9: (++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])

a: (+[][[]]+[])[++[[]][+[]]]

c: ([][(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])])+(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])])+(([][[]]+[])[++[[]][+[]]])+(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])])]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])]

d: ([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])]

e: ([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])]

f: ([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])]

i: ([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])]

n: ([][[]]+[])[++[[]][+[]]]

o: ([][(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])])+(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])])+(([][[]]+[])[++[[]][+[]]])+(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])])]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])]

t: (+((++[[]][+[]]+[])+(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])])+(++[[]][+[]]+[])+(+[])+(+[])+(+[]))+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])]

u: ([][[]]+[])[+[]]

v: ([][(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])])+(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])])+(([][[]]+[])[++[[]][+[]]])+(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])])]+[])[(++[[]][+[]])+(++[[]][+[]])+[]+((++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]]))]

y: (+((++[[]][+[]]+[])+(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])])+(++[[]][+[]]+[])+(+[])+(+[])+(+[]))+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])]

I: (+((++[[]][+[]]+[])+(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])])+(++[[]][+[]]+[])+(+[])+(+[])+(+[]))+[])[+[]]

N: (+[][[]]+[])[+[]]

" ": ([][(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])])+(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])])+(([][[]]+[])[++[[]][+[]]])+(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])])]+[])[+(++[[]][+[]]+[]+((++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])))]

(: ([][(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])])+(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])])+(([][[]]+[])[++[[]][+[]]])+(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])])]+[])[+(++[[]][+[]]+[]+((++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])))]

): ([][(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])])+(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])])+(([][[]]+[])[++[[]][+[]]])+(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])])]+[])[+(++[[]][+[]]+[]+((++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])))]

{: ([][(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])])+(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])])+(([][[]]+[])[++[[]][+[]]])+(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])])]+[])[+(++[[]][+[]]+[]+((++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])))]

}: ([][(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])])+(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])])+(([][[]]+[])[++[[]][+[]]])+(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])])]+[])[+((++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+[]+((++[[]][+[]])+(++[[]][+[]])))]

.: (+(++[[]][+[]]+[]+(++[[]][+[]])+([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])]+(++[[]][+[]]+[]+(+[])+(+[])))+[])[++[[]][+[]]]

,: [[]][([][(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])])+(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])])+(([][[]]+[])[++[[]][+[]]])+(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])])]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])]+([][(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])])+(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])])+(([][[]]+[])[++[[]][+[]]])+(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])])]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])]+([][[]]+[])[++[[]][+[]]]+([][(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])])+(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])])+(([][[]]+[])[++[[]][+[]]])+(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])])]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])]+(+[][[]]+[])[++[[]][+[]]]+(+((++[[]][+[]]+[])+(([][[]]+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])])+(++[[]][+[]]+[])+(+[])+(+[])+(+[]))+[])[(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])+(++[[]][+[]])]]([[]])+[]
\$\endgroup\$
  • \$\begingroup\$ This is very closely related to codegolf.stackexchange.com/q/11690/194 , and if that question had a JS answer I would have voted to close. As it is, an answer to this question is likely to translate directly into an answer to the earlier question, but the difference makes it sufficiently borderline that I don't want to close unilaterally. \$\endgroup\$ – Peter Taylor Mar 12 '16 at 13:14
  • 29
    \$\begingroup\$ Very nice question. I'm absolutely in favour of question about esoteric programming and unconventional computational models, but be prepared for some people close voting, because currently this doesn't quite fit the scope people agree on on meta. I'd love for this to set a precedent for such questions though. :) \$\endgroup\$ – Martin Ender Mar 12 '16 at 13:48
  • 1
    \$\begingroup\$ Comments are not for extended discussion; this conversation has been moved to chat. \$\endgroup\$ – Alex A. Mar 12 '16 at 22:12
  • 4
    \$\begingroup\$ Questions like this make me wish there was a feature to give a bounty to a question. \$\endgroup\$ – xnor Mar 13 '16 at 8:03
  • 1
    \$\begingroup\$ I got eval in 2453 chars with window allowed. \$\endgroup\$ – CalculatorFeline Mar 14 '16 at 3:43
23
\$\begingroup\$

After brainstorming, the result seems to be that, on modern browsers at least, there’s no way to do this.

I’ll try to summarize the entire process, adding some reasoning about why we’ve exhausted our options in any given domain before moving on. Then, barring some kind of amazing new insight (like, a corner case of JavaScript syntax everyone is forgetting about) it’ll be pretty clear that there’s no way to get the remaining letters.

Literals

The only immediate literals you can make with +()[] are the nested empty arrays [], [[]], [[[]]], etc. From there, we can start casting values using +:

  • +[] gets zero, which Jens’s trick expands to arbitrary positive integers using ++.

  • []+[] is "". In fact, []+x gets us a string representation of x in general.

[]’s next use is indexing. Indexing an object out-of-bounds ([][[]]) gets you undefined. Casting that to a string and indexing the result gets you the letters d e f i n u; casting it to an integer first using + gets you NaN, from which the letters a N follow.

Using the ++ trick on any non-integer value reached so far either gives NaN or an error. Also, none of the objects we can create are callable (yet), so () doesn’t help (except for grouping).

The remaining tricks up our sleeve are casting and indexing. So the question is: which strings can we create using the characters 0123456789adefinuN that either

  • are number literals we can round-trip cast to integer to get new strings, or
  • are property names of objects we can already reach?

Number literals

As an example of the second option, we can make the string "1e1000", and then get Infinity from +"1e1000", and casting that back to string gets us the letters y and I.

Also, we can make "11e100", cast to number and back to string, to get "1.1e+101", from which we extract . and +.

Using that ., in turn, we can make the string ".0000001", cast it to number and back, to get "1e-7", winning us -.

That’s basically all floats will get you: there aren’t any more interesting values other than Infinity and NaN, and there aren’t any more characters used in their usual string representations other than -+.0123456789e.

Properties

So we have the letters -+.0123456789adefinuyIN. Which properties can we reach? Let’s ask JavaScript.

>>> R = /^[-+.0123456789adefinuyIN]+$/
>>> [Array, Object, String, Number].reduce((h, f) => {
        h[f.name] = Object.getOwnPropertyNames(f.prototype).filter(x => x.match(R));
        return h }, {})

{ Array: [ 'find' ], Object: [], String: [], Number: [] }

Only [].find, which Jens already found. Let’s cast that to a string, reap all of its letters, and try again. The string representation is a bit different across browsers. On Chrome and Edge, "function find() { [native code] }" contains acdefinotuv()[]{} and a space; our full alphabet is now +-.()[]{}0123456789INacdefinotuvy. On Firefox, there are more spaces and newlines, but the letters are the same.

We repeat our search:

>>> R = /^[+-.()\[\]{}0123456789INacdefinotuvy]+$/
>>> [Array, Object, String, Number, Function].reduce((h, f) => {
        h[f.name] = Object.getOwnPropertyNames(f.prototype).filter(x => x.match(R));
        return h }, {})

{ Array: [ 'concat', 'find' ],
  Object: [],
  String: [ 'concat' ],
  Number: [],
  Function: [] }

String.prototype.concat is deprecated: it does exactly what + does, which we can already do. So we got Array.prototype.concat and Array.prototype.find. What can we do with them?

Functions

concat() lets us create, for the first time, longer arrays. [[]].concat([[]]) is [[], []], and casting that to a string gets us ",". (This doesn’t help us find new properties.) But .concat doesn’t modify our values, and it can never return null or anything like that.

Calling find() doesn’t help us either: the MDN documentation says

The find() method returns a value in the array, if an element in the array satisfies the provided testing function. Otherwise undefined is returned.

Both of those we can already do using indexing.


And from here, there’s nowhere else to go. If you doubt anything I’ve written, let me know in the comments.

\$\endgroup\$
  • 1
    \$\begingroup\$ My personal approaches I've worked with in the past hours alone have yielded all the feasible null returning functions: String.prototype.match, RegExp.exec, and Array.prototype.includes. Finding all of these impossible to form, unless there is an odd way to form a regex I do not know about, I have also concluded that there is no possible way to do this. \$\endgroup\$ – Conor O'Brien Mar 13 '16 at 16:21
  • \$\begingroup\$ Nice analysis! This is probably the correct answer but i'm still hoping for some trick... probably false hope though :) \$\endgroup\$ – Jens Renders Mar 13 '16 at 16:48
  • \$\begingroup\$ If we could get the letters for catch and throw, could we get the letters of the error? That is 'hwr'. \$\endgroup\$ – Rɪᴋᴇʀ Mar 13 '16 at 17:08
  • 3
    \$\begingroup\$ Even if we construct the strings "catch" and "throw", which we currently can’t, we’d need something eval-like to use those as keywords, which is our goal in the first place. \$\endgroup\$ – Lynn Mar 13 '16 at 17:19
  • \$\begingroup\$ Negative numbers are possible using - and number casting, but that's not very helpful. \$\endgroup\$ – CalculatorFeline Mar 14 '16 at 15:19
15
\$\begingroup\$

The 3 functions in Lynn's answer weren't that useless. But the strict mode in ECMAScript 5 foiled my plan.

There is a quirk in the older versions of JavaScript / ECMAScript. If a method is called without an object, the global object window is assumed. So we can do this:

a = {f:function(){return this}};
a.f();                            // Returns a.
g = a.f;
g();                              // Returns window.
window.g();                       // Also returns window.

This is still true for modern browsers, but only if the function isn't defined in strict mode. And all built-in functions (with native code) seemed to be in strict mode. In older browsers when there isn't the strict mode yet, this also works for built-in functions.

Assume we are using older browsers. Then if we want window, we have to find a built-in function that returns something containing this. Within the only choices we had, there is the function Array.prototype.concat doing exactly that. We can test it like this:

Number.prototype.concat = Array.prototype.concat;
1..concat(2);                     // Returns [1, 2]
concat = Array.prototype.concat;
window.concat(2);                 // Returns [window, 2]
concat(2)                         // TypeError in modern browsers while
                                  //   returning the same thing in older ones.
concat.bind(window)(2)            // A workaround in modern browsers.

So basically it doesn't care whether the object it is called upon is an array (but it must be an object for the least). It just wraps it in an array if not.

If we had window, firstly we can get the string [object Window] by casting it to a string. With the new character b, we can get r and s using the following two lines respectively, and every character we didn't have in constructor:

window["atob"]("cuaa")[0]
window["atob"]("cyaa")[0]

But the other problem is to remove the object reference from [].concat. Wrapping it in an array and extracting doesn't work, because [].concat already means []["concat"]. The only way I know which could possibly be constructed using +[]() is to return it from a function. Array.prototype.find seemed to be able to do that:

[[]["concat"]]["find"](x=>1)      // Returns Array.prototype.concat, where x=>1 can
                                  //   be replaced with any always truthy function.

We had always truthy functions. Array.prototype.concat and String.prototype.concat both return truthy if the object is window. If we use the later one, we made use of all of the three available functions.

But, unfortunately, Array.prototype.find doesn't exist in the old browser we are using. At least I didn't find one that works. And I didn't find another way removing the object reference.

The complete code that is testable in modern browsers that returns r and s, with the .bind(window) workaround:

[[]["concat"]]["find"](""["concat"].bind(window)).bind(window)()[0]["ato"+([]+[[]["concat"]]["find"](""["concat"].bind(window)).bind(window)()[0])[2]]("cuaa")[0];
[[]["concat"]]["find"](""["concat"].bind(window)).bind(window)()[0]["ato"+([]+[[]["concat"]]["find"](""["concat"].bind(window)).bind(window)()[0])[2]]("cyaa")[0]
\$\endgroup\$
  • \$\begingroup\$ Cool info. What browsers have you tried? \$\endgroup\$ – Lynn Mar 13 '16 at 22:07
  • \$\begingroup\$ @Lynn Not many. Mostly Firefox 3.6.0 and 25.0. I read from here and here that find came much later than strict mode so finding something working is unlikely. I asked about Edge because I thought it may have a chance preferring backward compatibility to following the standard. I also tried Konqueror for the same reason. And some command line browsers, but none of them even support JavaScript. \$\endgroup\$ – jimmy23013 Mar 14 '16 at 0:04
  • \$\begingroup\$ I tried Safari 7.1 and 8, and some random supposedly default browsers on phones on a browser screenshot website. None works so far. \$\endgroup\$ – jimmy23013 Mar 14 '16 at 0:43
  • \$\begingroup\$ @jimmy23013 Try Safari 5.0 or 5.1. According to Can I use, Partial support in older Safari refers to strict mode still accepting a lot of JS that should be considered invalid. Though find wasn't implemented yet, maybe it was partially?... If only it was in their list... \$\endgroup\$ – mbomb007 Sep 23 '16 at 21:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.