views:

420

answers:

9

This is an SO challenge

I would like to know how someone would get an invalid formal parameters in a function without the arguments object to as simulate not knowing the format of the parameter destructuring assignment. This is not an ECMAScript question and only pertains to JavaScript.

Your mySolution cannot access arguments or test. You are provided with an args array which contains the parameter names. You must return an object which has a property for every parameter which is the parameter that was passed to the function. In short, results[prop] must === test[prop]. Your solution shouldn't rely on bugs or security holes as they may not be present in the future. The solution to this problem of which I have in mind does not rely on any bugs.

(function () {
    function mySolution ({
     var,
     this,
     function,
     if,
     return,
     true
    }) {
     // prohbit reference to arguments and the test object
     var test = arguments = null,

     args = ['var', 'this', 'function', 'if', 'return', 'true'],
     results = {};

     // put your solution here

     return results;
    };
    var test = {
     "var"     : {},
     "this"    : {},
     "function": {},
     "if"      : {},
     "return"  : {},
     "true"    : {}
    },
    results = mySolution(test),
    pass = true;

    for (var prop in test)
     if (test.hasOwnProperty(prop))
      if (results[prop] !== test[prop])
       pass = false;

    alert(pass ? "PASS" : "FAIL")
}());

Here's one of the two possible solutions that I would have accepted:

(function () {
    function mySolution ({
     var,
     this,
     function,
     if,
     return,
     true
    }) {
     // prohbit reference to arguments and the test object
     var test = arguments = null,

     args = ['var', 'this', 'function', 'if', 'return', 'true'],
     results = {};

     var i = args.length;
     while (i--) {
      results[args[i]] = eval("function::" + args[i]);
      // function::[args[i]] won't work unless you eval() it
     }

     return results;
    };
    var test = {
     "var"     : {},
     "this"    : {},
     "function": {},
     "if"      : {},
     "return"  : {},
     "true"    : {}
    },
    results = mySolution(test),
    pass = true;

    for (var prop in test)
     if (test.hasOwnProperty(prop))
      if (results[prop] !== test[prop])
       pass = false;

    alert(pass ? "PASS" : "FAIL")
}());

The solution works by using the default function:: namespace in combination with eval() scope.

For example: foo.function::bar and foo.function::['bar'] are the same thing foo.bar.

A: 

Homework? Here's a hint: use eval.

John Millikin
No, this is not homework. And no, you can't eval("var") to get the value of the `var` parameter.
Eli Grey
If this isn't homework, then why the weird restrictions on what variables may be accessed, or what functions may be used? State your needs clearly.
John Millikin
You _may_ use eval, it's just that you _CAN'T_ use eval as it won't help.
Eli Grey
To add to that, there is one __one__ restriction, not multiple restrictions. The one restriction is that you can't access `arguments`.
Eli Grey
A: 

I can only think of one way to achieve this and even that one way relies on both - deprecated callee.caller and an already fixed FF peculiarity of eval being able to execute code in a context of a specified function.

What's interesting is that I think eval was "fixed" before function({...}){} extension was introduced, but I'm not totally sure.

I reduced test case slightly, but preserving an actual idea, of course.

I first tried accessing arguments off of a caller itself, but it looks like <fn>.arguments references same object as arguments within function context; nulling arguments object essentially destroyed object referred to by arguments property as well.

I also thought about evaling stack string from an error object (to get test values), but that would not solve anything, as test values are objects, not primitives.

(function () {
  function mySolution () {
    var test = arguments = null;
    return eval('test', (function(){ return arguments.callee.caller; })());
  };
  var test = {
    "var"   : {},
    "this"  : {},
    "function": {},
    "if"    : {},
    "return"  : {},
    "true"  : {}
  },
  results = mySolution(test),
  pass = true;

  for (var prop in test)
    if (test.hasOwnProperty(prop))
        if (results[prop] !== test[prop])
            pass = false;

  alert(pass ? "PASS" : "FAIL");
})();
kangax
`arguments.callee.caller` isn't deprecated last I checked, just non-standard. The deprecated thing is `arguments.caller`. Your solution doesn't solve the challenge that is stated in my question though. My question involves actually getting the values of the formal parameters (ie. `function(a){return a}` gets the `a` formal parameter).
Eli Grey
Also, please test your solution as it actually doesn't work in non-fixed or fixed JS engines. Given `someFunction` (which declares a private variable, `a`), eval("a", someFunction) doesn't return the private variable, "a". It returns `someFunction.a` if the property exists.
Eli Grey
It does work and, yes, I did test it. Which "non-fixed or fixed JS engines" are you talking about? I haven't seen implementations that return `someFunction.a` when calling `eval('a', someFunction)`, so I don't know where you're getting it from. Mozilla, on the other hand, had extension to evaluate code in a scope of a given function (i.e. using function's internal [[Scope]]) See https://bugzilla.mozilla.org/show_bug.cgi?id=442333.
kangax
Well you still are missing the point. Even if this does solve it, I nulled `test` for a reason, so you don't access it. If your solution works with destructuring assignment ({a,b,c}) in the arguments, I will make this as an answer.
Eli Grey
I believe it does work in FF3.0.x
kangax
+2  A: 

Tested PASS in FireFox 3.0.13! I "cheated", by altering the Object prototype:

<html>
<head>
<title></title>
<script>
(function () {
    function mySolution ({
        var,
        this,
        function,
        if,
        return,
        true
    }) {
        // prohbit reference to arguments and the test object
        var test = arguments = null,

        args = ['var', 'this', 'function', 'if', 'return', 'true'],
        results = {};

        // put your solution here
        Object.prototype._hasOwnProperty = Object.prototype.hasOwnProperty;
        Object.prototype.hasOwnProperty =
function(prop) {
 results[prop] = this[prop];
 return this._hasOwnProperty(prop);
}

        return results;
    };
    var test = {
        "var"     : {},
        "this"    : {},
        "function": {},
        "if"      : {},
        "return"  : {},
        "true"    : {}
    },
    results = mySolution(test),
    pass = true;

    for (var prop in test)
        if (test.hasOwnProperty(prop))
                if (results[prop] !== test[prop])
                        pass = false;

    alert(pass ? "PASS" : "FAIL")
}());

</script>
</head>
<body>
<!-- Put the body of your page below this line -->

<!-- Put the body of your page above this line -->
</body>
</html>

Does this count? I guess it probably doesn't. =p

RMorrisey
Also tested in IE7, but the script doesn't work at all (even if I delete my solution and just use the provided code on its own)
RMorrisey
It doesn't count but it sure is a creative solution! I guess I'm going to have to make `results` be `null` to :P
Eli Grey
Do I at least get a vote in case nobody else gets it, since I got the test to pass? ;)
RMorrisey
Woo, I got a vote =)
RMorrisey
A: 

Aha! I found a better answer, this time. (I have to admit that I got the general idea from kangax's answer, though). Tested PASS in FF 3.0.13:

<html>
<head>
<title></title>
<script>
(function () {
    function mySolution ({
        var,
        this,
        function,
        if,
        return,
        true
    }) {
        // prohbit reference to arguments and the test object
        var test = arguments = null,

        args = ['var', 'this', 'function', 'if', 'return', 'true'],
        results = {};

        // put your solution here
        var o = eval('arguments', mySolution)[0];
        for(var prop in o) {
         results[prop] = o[prop];
        }

        return results;
    };
    var test = {
        "var"     : {},
        "this"    : {},
        "function": {},
        "if"      : {},
        "return"  : {},
        "true"    : {}
    },
    results = mySolution(test),
    pass = true;

    for (var prop in test)
        if (test.hasOwnProperty(prop))
                if (results[prop] !== test[prop])
                        pass = false;

    alert(pass ? "PASS" : "FAIL")
}());

</script>
</head>
<body>
<!-- Put the body of your page below this line -->

<!-- Put the body of your page above this line -->
</body>
</html>
RMorrisey
I'm not really sure why this works... it seems like maybe the eval() function has a special case for the 'arguments' property, where it doesn't use the same property that is used within the method itself? Or maybe the "arguments" being assigned the value "null" is actually not the method arguments array?
RMorrisey
I tested in firefox 3.5.2 and it doesnt works "TypeError: eval('arguments', mySolution) is null"
Cleiton
This does seem like the kind of thing that could be a bug in FF, fixed in a later version =(
RMorrisey
As I already explained, this was a rather serious security hole in Mozilla (being able to evaluate string in a scope of arbitrary function). 3.0.13 still "works" (as RMorrisey noted), but 3.5 already doesn't. I think they fixed it shortly after 3.0.
kangax
A: 

Attempt #3; again, tested PASS in FF 3.0.13

<html>
<head>
<title></title>
<script>
(function () {
    function mySolution ({
        var,
        this,
        function,
        if,
        return,
        true
    }) {
        // prohbit reference to arguments and the test object
        var test = arguments = null,

        args = ['var', 'this', 'function', 'if', 'return', 'true'],
        results = {};

        // put your solution here
        var o = mySolution[0];
        for (var prop in o) {
           results[prop] = o[prop];
        }

        return results;
    };
    var test = {
        "var"     : {},
        "this"    : {},
        "function": {},
        "if"      : {},
        "return"  : {},
        "true"    : {}
    },
    results = mySolution(test),
    pass = true;

    for (var prop in test)
        if (test.hasOwnProperty(prop))
                if (results[prop] !== test[prop])
                        pass = false;

    alert(pass ? "PASS" : "FAIL")
}());

</script>
</head>
<body>
<!-- Put the body of your page below this line -->

<!-- Put the body of your page above this line -->
</body>
</html>
RMorrisey
failed in firefox 3.5.2
Cleiton
You gotta be kidding me!
RMorrisey
A: 

Tried lots of ways. Kind of given up. But if you can't break the system, change the system. MY solution:


(function () {
    function mySolution ({
        var,
        this,
        function,
        if,
        return,
        true
    }) {
        // prohbit reference to arguments and the test object
        var test = arguments = null,

        args = ['var', 'this', 'function', 'if', 'return', 'true'],
        results = {};

        // put your solution here
/********** MY SOLUTION STARTS ******************/
     return null;
    }
    function mySolution ({
        var,
        this,
        function,
        if,
        return,
        true
    }) {
        // new function does not prohbit reference to arguments and the test object
        //var test = arguments = null,

        args = ['var', 'this', 'function', 'if', 'return', 'true'],
        results = {};
     for(var i =0; i < args.length; i++) {
      results[args[i]] = arguments[0][args[i]];
     }
/********** MY SOLUTION ENDS ******************/
        return results;
    };
    var test = {
        "var"     : {},
        "this"    : {},
        "function": {},
        "if"      : {},
        "return"  : {},
        "true"    : {}
    },
    results = mySolution(test),
    pass = true;

    for (var prop in test)
        if (test.hasOwnProperty(prop))
                if (results[prop] !== test[prop])
                        pass = false;

    alert(pass ? "PASS" : "FAIL")
}());
Ashish
Well of course `arguments[0]` has the object but the whole purpose of the challenge is to see if you can get the invalid parameters without replicating the deconstruction.
Eli Grey
hey can you give a hint on how to go about solving this!!
Ashish
A: 
(function () {
    function mySolution ({ var, this, function, if, return, true }) {
    // prohbit reference to arguments and the test object
    var test = arguments = null, args = ['var', 'this', 'function', 'if', 'return','true'], results = {};
    //LAME...
    };
    mySolution=function(a){var results=a;
    //LAME...
    return results;
};
var test = {
      "var" : {},
      "this" : {},
      "function": {},
      "if" : {},
      "return" : {},
      "true" : {} }, results = mySolution(test), pass = true;
 for (var prop in test)
        if (test.hasOwnProperty(prop))
            if (results[prop] !== test[prop]) pass = false;
 alert(pass ? "PASS" : "FAIL") }());
artificialidiot
+3  A: 

For 100 points


(function () {
    function mySolution ({
        var,
        this,
        function,
        if,
        return,
        true
    }) {
        // prohbit reference to arguments and the test object
        var test = arguments = null,

        args = ['var', 'this', 'function', 'if', 'return', 'true'],
        results = {};

        // put your solution here
     var getEscUnicode = function(str) {
      var ret = "";
      for(var j = 0; j < str.length; j++) {
       var temp = parseInt(str.charCodeAt(j)).toString(16).toUpperCase();
       for(var i=0; i < 5 - temp.length; i++) {
        temp = "0" + temp;
       }
       ret = ret + "\\u" + temp;
      }
      return ret;

     }
     for(var i = 0; i < args.length; i++) {
      results[args[i]] = eval(getEscUnicode(args[i]));
     }
        return results;
    };
    var test = {
        "var"     : {},
        "this"    : {},
        "function": {},
        "if"      : {},
        "return"  : {},
        "true"    : {}
    },
    results = mySolution(test),
    pass = true;

    for (var prop in test)
        if (test.hasOwnProperty(prop))
                if (results[prop] !== test[prop])
                        pass = false;

    alert(pass ? "PASS" : "FAIL")
}());

Ashish
Congratulations, you actually figured out one of the two possible solutions to the challenge! Your solution relies on what I think is a bug and so if nobody finds the other solution by tomorrow I will mark this as the accepted answer.
Eli Grey
A: 

results = mySolution[0];

or:

for (var arg in args) results[args[arg]] = mySolution[0][args[arg]];

or:

results = eval('arguments', (function(){ return arguments.callee.caller; })())[0];

or even:

results = (function () { return arguments.callee.caller.arguments[0]; })();

All work on Firefox 3.0.

  • Fails on Firefox 3.5: mySolution[0] is undefined (deprecated?)
  • Fails on Firefox 3.5: arguments.callee.caller.arguments equals our local scope variable "arguments" (it shouldn't. possible bug in FF 3.5)
  • Fails on IE7 and IE8: syntax error at "function mySolution ({"
  • Fails on Opera 10.0: syntax error at "function mySolution ({"
  • Fails on Google Chrome: syntax error at "function mySolution ({"
  • Fails on Safari 3.2: syntax error at "function mySolution ({"

As you can see, even the original script is absolutely browser specific. It is definitively Firefox-only!

Is this really a JavaScript problem or a browser specific JavaScript problem?

This "challenge" is broken.

Havenard
As stated this is a JavaScript problem, not an ECMAScript problem. ECMAScript doesn't have destructuring assignment, JavaScript does. Only one browser supports _JavaScript_ and I it's pretty easy to guess which browser that is.Also, things like `mySolution[0]` don't work in Firefox 3.5 as they were a huge security bug. The solution should work in current JavaScript engines (again, I'm referring to JS engines, which is limited to SpiderMonkey and Rhino).
Eli Grey