Search code examples
javascriptjsfuck

Call methods in flow way for arrays using jsfuck convention


This is continuation of this question but for ARRAYS. Suppose I want to call array function with 2 parameters e.g.

console.log(
  [3,4,5].slice(1,2)
)

In first step I reduce characters set to jsfuck convention where we have 6 available characters: []()!+ (and for clarity: with a-z A-Z letters surrounded by " (JS strings) and numbers - which are easy to convert to those 6 chars):

console.log(
  [3,4,5]["slice"](1,2)
)

The problem here was comma (forbidden character) but we can overcome this problem by use following clever technique discovered by trincot:

console.log(
  [1]["concat"](2)["reduce"]([]["slice"]["bind"]([3,4,5]))
)

But the new question arise:

It is possible to call sequence of functions with 2 (or more) parameters without nesting them (which is imposed by above technique) but in "flow" way where we call next function in right side eg.: [3,4,5].slice(1,2).concat(6).. (without using 'eval' like solution where string is interpreted as code) ? (for function with one parameter it is possible e.g.: [3,4,5].slice(1).concat(6) )


Solution

  • The existing technique to wrap the subject of the method inside an array, which is mentioned here, was intended specifically for strings, turning "string" into ["string"]. The trick was using .split() without arguments. After applying that trick, it was not so difficult to use the other little tricks to apply the method that was desired, and allow chaining on the result.

    Of course, now that the subject is an array, we cannot use .split().

    It turns out that you can use the following method to make it happen for arrays:

    console.log(
      [3, 4, 5].reduce([].constructor).slice(-1)
    )

    This is rather inefficient, as reduce will actually iterate the subject array, destroying the result of the previous iteration, and so only the result of one (the last) iteration determines the result. You get the four arguments, which are passed to the callback, in an array, using the Array constructor (here [].constructor). As the fourth argument is the original array, we could pop it of: but that would leave us with the original array. So we slice it: that way we get the nested array.

    Using this with the other tricks, you get this expression for executing [3,4,5].slice(1,2):

    console.log(
      [3].concat(4).concat(5).reduce([].constructor)
         .slice(-1)
         .concat([[1].concat(2)])
         .reduce([].fill.apply.bind([].slice))
    )

    All the method references can be written in ["method"] notation so that you are left with producing these string literals with JSF:

    console.log(
      [3]["concat"](4)["concat"](5)["reduce"]([]["constructor"])
         ["slice"](-1)
         ["concat"]([[1]["concat"](2)])
         ["reduce"]([]["fill"]["apply"]["bind"]([]["slice"]))
    )

    Caveat

    This solution relies on the reduce callback being called. Be aware that it is not called when the subject array has only one element. In that case reduce just returns the original array, which is undesired. Also, this method will produce an exception when the subject array is empty.