Closed Bug 966477 Opened 11 years ago Closed 9 years ago

The caller property on functions can sometimes -- contra ES5 -- return a strict mode function

Categories

(Core :: JavaScript Engine, defect)

defect
Not set
normal

Tracking

()

RESOLVED FIXED

People

(Reporter: Waldo, Assigned: Waldo)

References

Details

ES5's algorithm for getting the caller property off functions is a little vague about mechanics, but it clearly wants any caller access that'd return a strict mode function to fail.  We don't always do that.

js> var Sandbox = Components.utils.Sandbox;
js> var s1 = new Sandbox("http://example.com/");
js> s1.eval("function f() { return arguments.callee.caller; }");
js> var s2 = new Sandbox("http://example.com/");
js> s2.f = s1.f;
function f() { return arguments.callee.caller; }
js> s2.eval('function g() { "use strict"; return f(); } g()')
function g() { "use strict"; return f(); }

Not so good if we ever look into optimizing out strict mode code from having to be tracked in the stack.

I'm likely to fix this in passing while making arguments/caller into accessors, seeing as I have to half-rewrite those algorithms when I do that.  We'll see.
Is this the cause of https://gist.github.com/rwaldron/8720084#file-jquery-js-L40 ?
Flags: needinfo?(jwalden+bmo)
The current structure is so:

  // computed caller is the right callable from the stack
  if computed caller is a wrapper && wrapper type has a security policy:
    return null as caller
  elif computed caller is not a wrapper:
    if function is strict:
      throw a TypeError

The first arm is fine.  The problem is the test nested in the second arm, should be performed in the remaining possibility not covered by this if-elif: computed caller is a wrapper, and wrapper type *doesn't* have a security policy.

It seems unlikely to me jquery's hitting this lapse in our logic.  Far, far more likely is that they're hitting (not a wrapper && function is strict) causing a TypeError, correctly, and not realizing it's the specified behavior.

(Yes, the implication is that a library can't be strict mode code, if it makes callbacks to user code, and it's allowed for that user code to walk its stack.  And vice versa, if the library wants to walk the stack for debugging purposes, and the user code on the stack is strict.)
Flags: needinfo?(jwalden+bmo)
Fixed now (probably by bug 969478, the referent in comment 0):

js> function test() {
  var Sandbox = Components.utils.Sandbox;
  var s1 = new Sandbox("http://example.com/");
  s1.eval("function f() { return arguments.callee.caller; }");
  var s2 = new Sandbox("http://example.com/");
  s2.f = s1.f;
  try {
    s2.eval('function g() { "use strict"; return f(); } g()');
    print("FAIL");
  } catch (e) {
    if (e instanceof s1.TypeError) {
      print("PASS");
    } else {
      print("FAIL");
    }
  }
} test();
PASS

Oddly, xpcshell doesn't print anything when an uncaught exception occurs (testcase: |throw 5|), so the test in comment 0 is somewhat-not-usable: instead of printing a function it shouldn't be able to access, it prints *nothing* and silently swallows the exception.  So it's necessary to catch it to distinguish proper failure from some other exception, or silent failure.
Status: ASSIGNED → RESOLVED
Closed: 9 years ago
Depends on: 969478
Resolution: --- → FIXED
You need to log in before you can comment on or make changes to this bug.