New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Function#caller and Function#arguments to Annex B #562

Open
claudepache opened this Issue May 10, 2016 · 35 comments

Comments

Projects
None yet
10 participants
@claudepache
Contributor

claudepache commented May 10, 2016

... because I doubt that any browser vendor would kill them.

I have written a strawman here: https://github.com/claudepache/es-legacy-function-reflection

Some notes:

  • Function#caller and Function#arguments are specced as configurable getters on Function.prototype rather than properties of individual functions, following Firefox's example.
  • Moreover, a basic realm check is performed (step 3 of GetTopMostExecutionContextIfLeakable), so that after those properties are deleted, they cannot be recovered from another realm.
  • As arguments.caller is not supported by any browser, I think that step 9 of CreateUnmappedArgumentsObject can be removed.
  • arguments.callee is currently specced in ECMA262 as unavailable for sloppy-mode functions with nonsimple parameter list. Question: Should Function#arguments and Function#caller be disabled for those functions as well?
@claudepache

This comment has been minimized.

Show comment
Hide comment
@claudepache

claudepache May 11, 2016

Contributor

In fact, arguments.callee is already specced as Step 24 of CreateMappedArgumentsObject, and it was already specced in ES3. Restricting that issue to Function#caller and Function#arguments

Contributor

claudepache commented May 11, 2016

In fact, arguments.callee is already specced as Step 24 of CreateMappedArgumentsObject, and it was already specced in ES3. Restricting that issue to Function#caller and Function#arguments

@claudepache claudepache changed the title from Add Function#caller, Function#arguments, and arguments.callee to Annex B to Add Function#caller and Function#arguments to Annex B May 11, 2016

@claudepache

This comment has been minimized.

Show comment
Hide comment
@claudepache

claudepache May 11, 2016

Contributor

Different browsers have different semantics for Function#caller.

Consider the function:

function f() {
    return f.caller
}

Consider the testcases:

// the caller is a sloppy-mode function
(function g() { 
    return f(); 
})();

// the caller is a strict-mode function
(function h() { 
    "use strict";
    return f(); 
})();

// the caller is a built-in function
[1,2].reduce(f);

Here are the results:

Browser caller is sloppy (g) caller is strict (h) caller is builtin (reduce)
Safari 9 g() throw a TypeError reduce()
Webkit g() null reduce()
Firefox 46 g() throw a TypeError null
Chrome 50 g() null null
Edge 13 g() null reduce()

The question is: Do we pick the semantics of Firefox or the one of Chrome?

Contributor

claudepache commented May 11, 2016

Different browsers have different semantics for Function#caller.

Consider the function:

function f() {
    return f.caller
}

Consider the testcases:

// the caller is a sloppy-mode function
(function g() { 
    return f(); 
})();

// the caller is a strict-mode function
(function h() { 
    "use strict";
    return f(); 
})();

// the caller is a built-in function
[1,2].reduce(f);

Here are the results:

Browser caller is sloppy (g) caller is strict (h) caller is builtin (reduce)
Safari 9 g() throw a TypeError reduce()
Webkit g() null reduce()
Firefox 46 g() throw a TypeError null
Chrome 50 g() null null
Edge 13 g() null reduce()

The question is: Do we pick the semantics of Firefox or the one of Chrome?

@bterlson

This comment has been minimized.

Show comment
Hide comment
@bterlson

bterlson May 11, 2016

Member

The goal here is to specify the absolute minimum observable semantics required to run web code.

Edge 15 throws a TypeError for case h, so we should probably go with that since it's not likely to break the web.

If caller is built-in, null seems best. I'd prefer a TypeError but that seems unlikely to be web compatible?

Function#arguments and Function#caller should be disabled for functions with non-simple parameter lists (ie. not have the own properties, so look up to throwers). Spidermonkey seems to do this now so we can probably get away with this too?

Member

bterlson commented May 11, 2016

The goal here is to specify the absolute minimum observable semantics required to run web code.

Edge 15 throws a TypeError for case h, so we should probably go with that since it's not likely to break the web.

If caller is built-in, null seems best. I'd prefer a TypeError but that seems unlikely to be web compatible?

Function#arguments and Function#caller should be disabled for functions with non-simple parameter lists (ie. not have the own properties, so look up to throwers). Spidermonkey seems to do this now so we can probably get away with this too?

@littledan

This comment has been minimized.

Show comment
Hide comment
@littledan

littledan May 11, 2016

Member

I'm not sure if divergence among browsers is enough to prove that picking one of them will not cause issues for users of a particular browser. Ideally, to make a decision, we would have some more data about how often a case occurs (infrequent cases are more OK to change semantics) as we got for __defineGetter__. It would also be nice to do some analysis by searching through existing codebases to see how caller is used and examine whether any issues are apparent.

Member

littledan commented May 11, 2016

I'm not sure if divergence among browsers is enough to prove that picking one of them will not cause issues for users of a particular browser. Ideally, to make a decision, we would have some more data about how often a case occurs (infrequent cases are more OK to change semantics) as we got for __defineGetter__. It would also be nice to do some analysis by searching through existing codebases to see how caller is used and examine whether any issues are apparent.

@bterlson

This comment has been minimized.

Show comment
Hide comment
@bterlson

bterlson May 11, 2016

Member

@littledan which of the proposed semantics are you worried about? I guess the type error for accessing caller when caller is strict is most concerning. I doubt that non-simple parameter lists not supporting this is worrying from a compat perspective.

Member

bterlson commented May 11, 2016

@littledan which of the proposed semantics are you worried about? I guess the type error for accessing caller when caller is strict is most concerning. I doubt that non-simple parameter lists not supporting this is worrying from a compat perspective.

@allenwb

This comment has been minimized.

Show comment
Hide comment
@allenwb

allenwb May 11, 2016

Member

The effect of f.caller when the caller of f is a strict mode function was specified in ES5.1: http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.5.4

Note that the specified behavior was to throw a TypeError.

That was removed from the ES6 spec. after Mark Miller and I convinced ourselves that it was no longer necessary because of other ES6 spec. changes. But I don't remember the details right now. It is probably captured somewhere in bugs.ecmascript.org.

Member

allenwb commented May 11, 2016

The effect of f.caller when the caller of f is a strict mode function was specified in ES5.1: http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.5.4

Note that the specified behavior was to throw a TypeError.

That was removed from the ES6 spec. after Mark Miller and I convinced ourselves that it was no longer necessary because of other ES6 spec. changes. But I don't remember the details right now. It is probably captured somewhere in bugs.ecmascript.org.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic May 11, 2016

Member

I vaguely recall us just hoping browsers would just leave that property off (f.hasOwnProperty("caller") === false).

It seems like nobody does that though, either defining a property with value null, or a throwing getter, if I am reading @claudepache's table right.

Member

domenic commented May 11, 2016

I vaguely recall us just hoping browsers would just leave that property off (f.hasOwnProperty("caller") === false).

It seems like nobody does that though, either defining a property with value null, or a throwing getter, if I am reading @claudepache's table right.

@bterlson

This comment has been minimized.

Show comment
Hide comment
@bterlson

bterlson May 11, 2016

Member

I vaguely recall we had a different semantics prior to putting throwers on Function.prototype (and that the change came somewhat late). I wonder if making that change made it so we shoul dhave added the ES5.1 15.3.5.4 semantics back?

Member

bterlson commented May 11, 2016

I vaguely recall we had a different semantics prior to putting throwers on Function.prototype (and that the change came somewhat late). I wonder if making that change made it so we shoul dhave added the ES5.1 15.3.5.4 semantics back?

@allenwb

This comment has been minimized.

Show comment
Hide comment
@allenwb
Member

allenwb commented May 11, 2016

@allenwb

This comment has been minimized.

Show comment
Hide comment
@allenwb

allenwb May 11, 2016

Member

The bigger question is what is the result of:

// the caller is a sloppy-mode function
function g() { 
    return f(0); 
};

function f(n) {
   var caller1 = f.caller;
   if (n==0) f(1);
   var caller2 = f.caller;
   if (n==0) console.log(caller1===caller2);
}

Does it log true or false? Presumably depends upon whether or not there is a single state slot in the function object. I think we talked about this at a TC39 meeting but nobody really want to put energy into fullyspecifying this highly deprecated "feature" so it didn't go into Annex B.

It probably isn't interoperable for cases like the above now. I suspect that no implementation would really want to waste time changing their implementation to make it inter-operable for such cases. What value comes from specifying it?

Member

allenwb commented May 11, 2016

The bigger question is what is the result of:

// the caller is a sloppy-mode function
function g() { 
    return f(0); 
};

function f(n) {
   var caller1 = f.caller;
   if (n==0) f(1);
   var caller2 = f.caller;
   if (n==0) console.log(caller1===caller2);
}

Does it log true or false? Presumably depends upon whether or not there is a single state slot in the function object. I think we talked about this at a TC39 meeting but nobody really want to put energy into fullyspecifying this highly deprecated "feature" so it didn't go into Annex B.

It probably isn't interoperable for cases like the above now. I suspect that no implementation would really want to waste time changing their implementation to make it inter-operable for such cases. What value comes from specifying it?

@WebReflection

This comment has been minimized.

Show comment
Hide comment
@WebReflection

WebReflection May 11, 2016

possibly no value, but that "quiz" is easy to solve. Recursion makes the
caller the function itself: least surprise.

The answer is false then, and since that's unlikely real-world code,
nobody gonna suffer from such answer.

It'd be worst/unreasonable practice, in my opinion, to ask twice for a
fn.caller, and in the same function body, if you're expecting the same
result.

Just my 2 cents

On Thu, May 12, 2016 at 12:11 AM, Allen Wirfs-Brock <
notifications@github.com> wrote:

The bigger question is what is the result of:

// the caller is a sloppy-mode functionfunction g() {
return f(0);
};
function f(n) {
var caller1 = f.caller;
if (n==0) f(1);
var caller2 = f.caller;
if (n==0) console.log(caller1===caller2);
}

Does it log true or false? Presumably depends upon whether or not there is
a single state slot in the function object. I think we talked about this at
a TC39 meeting but nobody really want to put energy into fullyspecifying
this highly deprecated "feature" so it didn't go into Annex B.

It probably isn't interoperable for cases like the above now. I suspect
that no implementation would really want to waste time changing their
implementation to make it inter-operable for such cases. What value comes
from specifying it?


You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub
#562 (comment)

WebReflection commented May 11, 2016

possibly no value, but that "quiz" is easy to solve. Recursion makes the
caller the function itself: least surprise.

The answer is false then, and since that's unlikely real-world code,
nobody gonna suffer from such answer.

It'd be worst/unreasonable practice, in my opinion, to ask twice for a
fn.caller, and in the same function body, if you're expecting the same
result.

Just my 2 cents

On Thu, May 12, 2016 at 12:11 AM, Allen Wirfs-Brock <
notifications@github.com> wrote:

The bigger question is what is the result of:

// the caller is a sloppy-mode functionfunction g() {
return f(0);
};
function f(n) {
var caller1 = f.caller;
if (n==0) f(1);
var caller2 = f.caller;
if (n==0) console.log(caller1===caller2);
}

Does it log true or false? Presumably depends upon whether or not there is
a single state slot in the function object. I think we talked about this at
a TC39 meeting but nobody really want to put energy into fullyspecifying
this highly deprecated "feature" so it didn't go into Annex B.

It probably isn't interoperable for cases like the above now. I suspect
that no implementation would really want to waste time changing their
implementation to make it inter-operable for such cases. What value comes
from specifying it?


You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub
#562 (comment)

@claudepache

This comment has been minimized.

Show comment
Hide comment
@claudepache

claudepache May 12, 2016

Contributor

@allenwb Good point

The bigger question is what is the result of:

// the caller is a sloppy-mode function
function g() {
return f(0);
};

function f(n) {
var caller1 = f.caller;
if (n==0) f(1);
var caller2 = f.caller;
if (n==0) console.log(caller1===caller2);
}
Does it log true or false? Presumably depends upon whether or not there is a single state slot in the function object.

Good point. Fortunately, it logs true in all current mainstream browsers.

What value comes from specifying it?

It is useful to make sure that implementations does not do unadvisable things; and it may be easier or safer to achieve that by a simple specification.

Contributor

claudepache commented May 12, 2016

@allenwb Good point

The bigger question is what is the result of:

// the caller is a sloppy-mode function
function g() {
return f(0);
};

function f(n) {
var caller1 = f.caller;
if (n==0) f(1);
var caller2 = f.caller;
if (n==0) console.log(caller1===caller2);
}
Does it log true or false? Presumably depends upon whether or not there is a single state slot in the function object.

Good point. Fortunately, it logs true in all current mainstream browsers.

What value comes from specifying it?

It is useful to make sure that implementations does not do unadvisable things; and it may be easier or safer to achieve that by a simple specification.

@WebReflection

This comment has been minimized.

Show comment
Hide comment
@WebReflection

WebReflection May 12, 2016

if it's about setting it up at the beginning and dropping it at the end (or
restoring the previous) then true would be reasonable result, yet I don't
think it makes any sense to ask for a caller down the road, specially not
after a recursive call.

A non-issue to solve, probably not even needed to be specified?

On Thu, May 12, 2016 at 8:46 AM, Claude Pache notifications@github.com
wrote:

@allenwb https://github.com/allenwb Good point

The bigger question is what is the result of:

// the caller is a sloppy-mode function
function g() {
return f(0);
};

function f(n) {
var caller1 = f.caller;
if (n==0) f(1);
var caller2 = f.caller;
if (n==0) console.log(caller1===caller2);
}
Does it log true or false? Presumably depends upon whether or not there is
a single state slot in the function object.

Good point. Fortunately, it logs true in all current mainstream browsers.

What value comes from specifying it?

It is useful to make sure that implementations does not do unadvisable
things; and it may be easier or safer to achieve that by a simple
specification.


You are receiving this because you commented.
Reply to this email directly or view it on GitHub
#562 (comment)

WebReflection commented May 12, 2016

if it's about setting it up at the beginning and dropping it at the end (or
restoring the previous) then true would be reasonable result, yet I don't
think it makes any sense to ask for a caller down the road, specially not
after a recursive call.

A non-issue to solve, probably not even needed to be specified?

On Thu, May 12, 2016 at 8:46 AM, Claude Pache notifications@github.com
wrote:

@allenwb https://github.com/allenwb Good point

The bigger question is what is the result of:

// the caller is a sloppy-mode function
function g() {
return f(0);
};

function f(n) {
var caller1 = f.caller;
if (n==0) f(1);
var caller2 = f.caller;
if (n==0) console.log(caller1===caller2);
}
Does it log true or false? Presumably depends upon whether or not there is
a single state slot in the function object.

Good point. Fortunately, it logs true in all current mainstream browsers.

What value comes from specifying it?

It is useful to make sure that implementations does not do unadvisable
things; and it may be easier or safer to achieve that by a simple
specification.


You are receiving this because you commented.
Reply to this email directly or view it on GitHub
#562 (comment)

@claudepache

This comment has been minimized.

Show comment
Hide comment
@claudepache

claudepache May 12, 2016

Contributor

@bterlson

Function#arguments and Function#caller should be disabled for functions with non-simple parameter lists (ie. not have the own properties, so look up to throwers). Spidermonkey seems to do this now so we can probably get away with this too?

Are you sure? I just tested this in Firefox Nightly web console:

function f(x = 1) { return f.caller }
(function g(x = 1) { return f() })(); // returns: function g()

In case there is confusion about the absence of own properties: Firefox doesn't have own properties anymore on individual functions, but it doesn't mean that the functionality is gone. Instead, all logic has been displaced inside the Function.prototype.{caller,arguments} accessors.

Contributor

claudepache commented May 12, 2016

@bterlson

Function#arguments and Function#caller should be disabled for functions with non-simple parameter lists (ie. not have the own properties, so look up to throwers). Spidermonkey seems to do this now so we can probably get away with this too?

Are you sure? I just tested this in Firefox Nightly web console:

function f(x = 1) { return f.caller }
(function g(x = 1) { return f() })(); // returns: function g()

In case there is confusion about the absence of own properties: Firefox doesn't have own properties anymore on individual functions, but it doesn't mean that the functionality is gone. Instead, all logic has been displaced inside the Function.prototype.{caller,arguments} accessors.

@rossberg

This comment has been minimized.

Show comment
Hide comment
@rossberg

rossberg May 12, 2016

Member

In V8, these properties have a purely "best effort" implementation. Whether they work, and with what specific semantics, depends on a variety of static and dynamic factors, like optimisation levels. IOW, there is not even a consistent "Chrome semantics".

Changing this would introduce significant overhead and complexity, in particular wrt Function#arguments.

We hence see zero -- or even negative -- value in elevating these (mis)features to the standard, even if it's just Annex B. We can serve the community better by spending our resources elsewhere.

Member

rossberg commented May 12, 2016

In V8, these properties have a purely "best effort" implementation. Whether they work, and with what specific semantics, depends on a variety of static and dynamic factors, like optimisation levels. IOW, there is not even a consistent "Chrome semantics".

Changing this would introduce significant overhead and complexity, in particular wrt Function#arguments.

We hence see zero -- or even negative -- value in elevating these (mis)features to the standard, even if it's just Annex B. We can serve the community better by spending our resources elsewhere.

@bterlson

This comment has been minimized.

Show comment
Hide comment
@bterlson

bterlson May 12, 2016

Member

@claudepache I am not sure! Confirmed your example is correct. I wish I had the sample I was using before as js.exe was the only one throwing on it. Alas, it is missing. I also updated today so possibly it's a recent change. Anyway, I would still hope we could not do this.

@rossberg-chromium would you agree that there is some semantics here required to run the web? If so, that is what we should spec. If not, you (and all of us) should just remove this functionality. You can always choose when to fix bugs (or, not to fix them) :)

Member

bterlson commented May 12, 2016

@claudepache I am not sure! Confirmed your example is correct. I wish I had the sample I was using before as js.exe was the only one throwing on it. Alas, it is missing. I also updated today so possibly it's a recent change. Anyway, I would still hope we could not do this.

@rossberg-chromium would you agree that there is some semantics here required to run the web? If so, that is what we should spec. If not, you (and all of us) should just remove this functionality. You can always choose when to fix bugs (or, not to fix them) :)

@rossberg

This comment has been minimized.

Show comment
Hide comment
@rossberg

rossberg May 13, 2016

Member

@bterlson, not necessarily. They are probably used primarily for debugging and other diagnostics, rather than actual programmatic logic. In that case, a best-effort implementation is still useful, despite the reliable intersection semantics being practically empty.

Member

rossberg commented May 13, 2016

@bterlson, not necessarily. They are probably used primarily for debugging and other diagnostics, rather than actual programmatic logic. In that case, a best-effort implementation is still useful, despite the reliable intersection semantics being practically empty.

@claudepache

This comment has been minimized.

Show comment
Hide comment
@claudepache

claudepache May 17, 2016

Contributor

They are probably used primarily for debugging and other diagnostics, rather than actual programmatic logic.

In that case, they could probably be neutered (by returning null or a fake empty Argument object) or plainly removed, as they are already inoperative for strict-mode functions and implementations provide better diagnostic tools. Some volunteer to try that?

Otherwise, if they are bound to remain, I think that a minimal specification, allowing implementation-defined behaviour at some well-guarded places if desired, is useful. For example, here are two points that could be improved relatively to the current situation:

  • Some implementations give unwarranted access to builtin functions through the .caller property. If we could limit leaks to sloppy-mode functions, it would be great.
  • Some implementations use an own property for sloppy-mode functions but not for the other ones (relying on the inheritance from Function.prototype), and that may lead to confusions:
function B() {}
var C = (function() { 
    "use strict"
    return class C extends B {} 
})()
C.arguments // will return B.arguments ... oops
Contributor

claudepache commented May 17, 2016

They are probably used primarily for debugging and other diagnostics, rather than actual programmatic logic.

In that case, they could probably be neutered (by returning null or a fake empty Argument object) or plainly removed, as they are already inoperative for strict-mode functions and implementations provide better diagnostic tools. Some volunteer to try that?

Otherwise, if they are bound to remain, I think that a minimal specification, allowing implementation-defined behaviour at some well-guarded places if desired, is useful. For example, here are two points that could be improved relatively to the current situation:

  • Some implementations give unwarranted access to builtin functions through the .caller property. If we could limit leaks to sloppy-mode functions, it would be great.
  • Some implementations use an own property for sloppy-mode functions but not for the other ones (relying on the inheritance from Function.prototype), and that may lead to confusions:
function B() {}
var C = (function() { 
    "use strict"
    return class C extends B {} 
})()
C.arguments // will return B.arguments ... oops
@rossberg

This comment has been minimized.

Show comment
Hide comment
@rossberg

rossberg May 17, 2016

Member

They are probably used primarily for debugging and other diagnostics,
rather than actual programmatic logic.

In that case, they could probably be neutered (by returning null or a
fake empty Argument object) or plainly removed, as they are already
inoperative for strict-mode functions and implementations provide better
diagnostic tools. Some volunteer to try that?

Given the turmoil we are seeing around tail call elimination, we might not
(yet) get away with a regression like that.

At the same time, there is no good reason to encourage new code to use
these features. Nor for us to waste time on them. Any useful spec text will
cause both.

Otherwise, if they are bound to remain, I think that a minimal

specification, allowing implementation-defined behaviour at some
well-guarded places if desired, is useful. For example, here are two points
that could be improved relatively to the current situation:

Some implementations give unwarranted access to builtin functions
through the .caller property. If we could limit leaks to sloppy-mode
functions, it would be great.

Some implementations use an own property for sloppy-mode functions but
not for the other ones (relying on the inheritance from
Function.prototype), and that may lead to confusions:

function B() {}var C = (function() {
"use strict"
return class C extends B {}
})()C.arguments // will return B.arguments ... oops

Yeah, but there is nothing that could be done about this other than poison
pills -- which we just got rid of in ES6, and don't want back. Hence this
seems like exactly the kind of issue that the spec cannot fix anyway.

Why are you saying that access to built-in callers is unwarranted? Not
saying that they should be accessible, but I don't see why they must
not
be either.

Member

rossberg commented May 17, 2016

They are probably used primarily for debugging and other diagnostics,
rather than actual programmatic logic.

In that case, they could probably be neutered (by returning null or a
fake empty Argument object) or plainly removed, as they are already
inoperative for strict-mode functions and implementations provide better
diagnostic tools. Some volunteer to try that?

Given the turmoil we are seeing around tail call elimination, we might not
(yet) get away with a regression like that.

At the same time, there is no good reason to encourage new code to use
these features. Nor for us to waste time on them. Any useful spec text will
cause both.

Otherwise, if they are bound to remain, I think that a minimal

specification, allowing implementation-defined behaviour at some
well-guarded places if desired, is useful. For example, here are two points
that could be improved relatively to the current situation:

Some implementations give unwarranted access to builtin functions
through the .caller property. If we could limit leaks to sloppy-mode
functions, it would be great.

Some implementations use an own property for sloppy-mode functions but
not for the other ones (relying on the inheritance from
Function.prototype), and that may lead to confusions:

function B() {}var C = (function() {
"use strict"
return class C extends B {}
})()C.arguments // will return B.arguments ... oops

Yeah, but there is nothing that could be done about this other than poison
pills -- which we just got rid of in ES6, and don't want back. Hence this
seems like exactly the kind of issue that the spec cannot fix anyway.

Why are you saying that access to built-in callers is unwarranted? Not
saying that they should be accessible, but I don't see why they must
not
be either.

@allenwb

This comment has been minimized.

Show comment
Hide comment
@allenwb

allenwb May 17, 2016

Member

On May 17, 2016 5:56 AM, Claude Pache notifications@github.com wrote:

Some implementations give unwarranted access to builtin functions through the .caller property. If we could limit leaks to sloppy-mode functions, it would be great.

This is a spec. violation if the built-in is implemented via a strict ES function

Member

allenwb commented May 17, 2016

On May 17, 2016 5:56 AM, Claude Pache notifications@github.com wrote:

Some implementations give unwarranted access to builtin functions through the .caller property. If we could limit leaks to sloppy-mode functions, it would be great.

This is a spec. violation if the built-in is implemented via a strict ES function

@rossberg

This comment has been minimized.

Show comment
Hide comment
@rossberg

rossberg May 17, 2016

Member

On 17 May 2016 at 15:36, Allen Wirfs-Brock notifications@github.com wrote:

On May 17, 2016 5:56 AM, Claude Pache notifications@github.com wrote:

Some implementations give unwarranted access to builtin functions
through the .caller property. If we could limit leaks to sloppy-mode
functions, it would be great.

This is a spec. violation if the built-in is implemented via a strict ES
function

Allen, in what sense is "implemented via a strict ES function" a meaningful
characterisation within the spec? How would that be observable?

Member

rossberg commented May 17, 2016

On 17 May 2016 at 15:36, Allen Wirfs-Brock notifications@github.com wrote:

On May 17, 2016 5:56 AM, Claude Pache notifications@github.com wrote:

Some implementations give unwarranted access to builtin functions
through the .caller property. If we could limit leaks to sloppy-mode
functions, it would be great.

This is a spec. violation if the built-in is implemented via a strict ES
function

Allen, in what sense is "implemented via a strict ES function" a meaningful
characterisation within the spec? How would that be observable?

@claudepache

This comment has been minimized.

Show comment
Hide comment
@claudepache

claudepache May 17, 2016

Contributor

Some implementations use an own property for sloppy-mode functions but
not for the other ones (relying on the inheritance from
Function.prototype), and that may lead to confusions:

function B() {}var C = (function() {
    "use strict"
    return class C extends B {}
})()C.arguments // will return B.arguments ... oops

Yeah, but there is nothing that could be done about this other than poison
pills -- which we just got rid of in ES6, and don't want back. Hence this
seems like exactly the kind of issue that the spec cannot fix anyway.

No, something can be done, and Firefox has done it: remove those own properties on every individual functions, including sloppy-mode ones, and define "arguments" and "caller" accessor properties on Function.prototype that take different action according to the received this value.

Contributor

claudepache commented May 17, 2016

Some implementations use an own property for sloppy-mode functions but
not for the other ones (relying on the inheritance from
Function.prototype), and that may lead to confusions:

function B() {}var C = (function() {
    "use strict"
    return class C extends B {}
})()C.arguments // will return B.arguments ... oops

Yeah, but there is nothing that could be done about this other than poison
pills -- which we just got rid of in ES6, and don't want back. Hence this
seems like exactly the kind of issue that the spec cannot fix anyway.

No, something can be done, and Firefox has done it: remove those own properties on every individual functions, including sloppy-mode ones, and define "arguments" and "caller" accessor properties on Function.prototype that take different action according to the received this value.

@zenparsing

This comment has been minimized.

Show comment
Hide comment
@zenparsing

zenparsing May 17, 2016

Contributor

At the same time, there is no good reason to encourage new code to use
these features. Nor for us to waste time on them. Any useful spec text will
cause both.

Plus one's aren't very helpful, I know. But +1 all the same (particularly to the second point).

Contributor

zenparsing commented May 17, 2016

At the same time, there is no good reason to encourage new code to use
these features. Nor for us to waste time on them. Any useful spec text will
cause both.

Plus one's aren't very helpful, I know. But +1 all the same (particularly to the second point).

@claudepache

This comment has been minimized.

Show comment
Hide comment
@claudepache

claudepache May 17, 2016

Contributor

At the same time, there is no good reason to encourage new code to use
these features. Nor for us to waste time on them. Any useful spec text will
cause both.

Plus one's aren't very helpful, I know. But +1 all the same (particularly to the second point).

Minus ones are nevertheless useful to point to actions that people don’t want to be taken.

I have updated the proposed spec in order to allow some implementation-defined behaviour, while keeping safety and soundness for non-sloppy functions. In particular, V8 is allowed to keep its nondeterministic semantics in order to discourage their use :-)

Contributor

claudepache commented May 17, 2016

At the same time, there is no good reason to encourage new code to use
these features. Nor for us to waste time on them. Any useful spec text will
cause both.

Plus one's aren't very helpful, I know. But +1 all the same (particularly to the second point).

Minus ones are nevertheless useful to point to actions that people don’t want to be taken.

I have updated the proposed spec in order to allow some implementation-defined behaviour, while keeping safety and soundness for non-sloppy functions. In particular, V8 is allowed to keep its nondeterministic semantics in order to discourage their use :-)

@allenwb

This comment has been minimized.

Show comment
Hide comment
@allenwb

allenwb May 17, 2016

Member

On May 17, 2016 8:58 AM, rossberg-chromium notifications@github.com wrote:

On 17 May 2016 at 15:36, Allen Wirfs-Brock notifications@github.com wrote:

On May 17, 2016 5:56 AM, Claude Pache notifications@github.com wrote:

Allen, in what sense is "implemented via a strict ES function" a meaningful
characterisation within the spec? How would that be observable?

See https://tc39.github.io/ecma262/#sec-built-in-function-objects
If a built-in is implemented using a strict node ECMAScript function object then the clause 16.1 rules about caller apply.

Member

allenwb commented May 17, 2016

On May 17, 2016 8:58 AM, rossberg-chromium notifications@github.com wrote:

On 17 May 2016 at 15:36, Allen Wirfs-Brock notifications@github.com wrote:

On May 17, 2016 5:56 AM, Claude Pache notifications@github.com wrote:

Allen, in what sense is "implemented via a strict ES function" a meaningful
characterisation within the spec? How would that be observable?

See https://tc39.github.io/ecma262/#sec-built-in-function-objects
If a built-in is implemented using a strict node ECMAScript function object then the clause 16.1 rules about caller apply.

@rossberg

This comment has been minimized.

Show comment
Hide comment
@rossberg

rossberg May 18, 2016

Member

Allen, in what sense is "implemented via a strict ES function" a
meaningful
characterisation within the spec? How would that be observable?

See https://tc39.github.io/ecma262/#sec-built-in-function-objects
If a built-in is implemented using a strict node ECMAScript function
object then the clause 16.1 rules about caller apply.

I still don't understand how this is observable. An implementation can
always pretend that all its built-ins are exotic, regardless of their
actual implementation details. AFAICS, there is no way a program can tell
their "true nature".

Member

rossberg commented May 18, 2016

Allen, in what sense is "implemented via a strict ES function" a
meaningful
characterisation within the spec? How would that be observable?

See https://tc39.github.io/ecma262/#sec-built-in-function-objects
If a built-in is implemented using a strict node ECMAScript function
object then the clause 16.1 rules about caller apply.

I still don't understand how this is observable. An implementation can
always pretend that all its built-ins are exotic, regardless of their
actual implementation details. AFAICS, there is no way a program can tell
their "true nature".

@claudepache

This comment has been minimized.

Show comment
Hide comment
@claudepache

claudepache May 23, 2016

Contributor

After reflection, I understand that putting this in Annex B may not be desirable.

However, there is still room for improvement w.r.t. the current situation, maybe by putting more constraints in Section 16.2 Forbidden Extensions. Concretely:

  • forbid to leak anything else than sloppy-mode ECMAScript function (functions with an [[ECMAScriptCode]] internal slot and for which [[Strict]] is false);
  • if an implementation choose to implement a given builtin using an ECMAScript function, it must be a strict-mode one.

And, in order to minimise API surface and avoid the issue of function objects inheriting from others, the following may be nice (implemented by Firefox):

  • disallow to add caller and arguments own properties on individual function objects; instead, only corresponding deletable accessor properties on Function.prototype are allowed.
Contributor

claudepache commented May 23, 2016

After reflection, I understand that putting this in Annex B may not be desirable.

However, there is still room for improvement w.r.t. the current situation, maybe by putting more constraints in Section 16.2 Forbidden Extensions. Concretely:

  • forbid to leak anything else than sloppy-mode ECMAScript function (functions with an [[ECMAScriptCode]] internal slot and for which [[Strict]] is false);
  • if an implementation choose to implement a given builtin using an ECMAScript function, it must be a strict-mode one.

And, in order to minimise API surface and avoid the issue of function objects inheriting from others, the following may be nice (implemented by Firefox):

  • disallow to add caller and arguments own properties on individual function objects; instead, only corresponding deletable accessor properties on Function.prototype are allowed.
@rossberg

This comment has been minimized.

Show comment
Hide comment
@rossberg

rossberg May 23, 2016

Member

I like the first two suggestions. I'm less thrilled about the latter, because that means that all functions, including strict ones, will have these properties (even if they throw on some). That seems strictly worse than the own property approach, where they only show up on non-sloppy functions in some inheritance corner cases.

Member

rossberg commented May 23, 2016

I like the first two suggestions. I'm less thrilled about the latter, because that means that all functions, including strict ones, will have these properties (even if they throw on some). That seems strictly worse than the own property approach, where they only show up on non-sloppy functions in some inheritance corner cases.

@claudepache

This comment has been minimized.

Show comment
Hide comment
@claudepache

claudepache May 23, 2016

Contributor

I'm less thrilled about the latter, because that means that all functions, including strict ones, will have these properties (even if they throw on some).

This is already the case according to the current spec, because the AddRestrictedFunctionProperties() operation is applied to %FunctionPrototype%, see CreateIntrisics().

One could just kill AddRestrictedFunctionProperties() instead; but really, I don’t think that it is in any way better than the current inherit-from-sloppy-mode-function hazard.

Contributor

claudepache commented May 23, 2016

I'm less thrilled about the latter, because that means that all functions, including strict ones, will have these properties (even if they throw on some).

This is already the case according to the current spec, because the AddRestrictedFunctionProperties() operation is applied to %FunctionPrototype%, see CreateIntrisics().

One could just kill AddRestrictedFunctionProperties() instead; but really, I don’t think that it is in any way better than the current inherit-from-sloppy-mode-function hazard.

@claudepache

This comment has been minimized.

Show comment
Hide comment
@claudepache

claudepache May 24, 2016

Contributor

disallow to add caller and arguments own properties on individual function objects; instead, only corresponding deletable accessor properties on Function.prototype are allowed.

Alternatively, a getter might be installed on each sloppy-mode function, which checks its this-value and refuses to work when used as inherited. I don’t think it is better, but I don’t care much.

More importantly, currently some implementations violate the spec, as they define, on each sloppy-mode function, an arguments and a caller property, and make them as nonwritable and nonconfigurable; but yet their value does observably change over time (6.1.7.3).

Contributor

claudepache commented May 24, 2016

disallow to add caller and arguments own properties on individual function objects; instead, only corresponding deletable accessor properties on Function.prototype are allowed.

Alternatively, a getter might be installed on each sloppy-mode function, which checks its this-value and refuses to work when used as inherited. I don’t think it is better, but I don’t care much.

More importantly, currently some implementations violate the spec, as they define, on each sloppy-mode function, an arguments and a caller property, and make them as nonwritable and nonconfigurable; but yet their value does observably change over time (6.1.7.3).

@claudepache

This comment has been minimized.

Show comment
Hide comment
@claudepache

claudepache May 26, 2016

Contributor

More importantly, currently some implementations violate the spec, as they define, on each sloppy-mode function, an arguments and a caller property, and make them as nonwritable and nonconfigurable; but yet their value does observably change over time (6.1.7.3).

Just opened:

Contributor

claudepache commented May 26, 2016

More importantly, currently some implementations violate the spec, as they define, on each sloppy-mode function, an arguments and a caller property, and make them as nonwritable and nonconfigurable; but yet their value does observably change over time (6.1.7.3).

Just opened:

@evilpie

This comment has been minimized.

Show comment
Hide comment
@evilpie

evilpie Dec 23, 2016

Contributor

Ping, can we move this forward? Firefox has been shipping getters on Function.prototype without any problems. @claudepache @bterlson

Contributor

evilpie commented Dec 23, 2016

Ping, can we move this forward? Firefox has been shipping getters on Function.prototype without any problems. @claudepache @bterlson

@bterlson

This comment has been minimized.

Show comment
Hide comment
@bterlson

bterlson Dec 23, 2016

Member

@evilpie sure, if it's web compatible I'm fine moving "caller" and "arguments" to proto getters.

I understand the desire to not specify actual machinery in Annex B and the biggest advantage of putting this text in Forbidden Extensions is that an implementation might one day remove these properties entirely.

I like @claudepache's 3 updates, but I wonder if the specification that built-in functions implemented in ECMAScript be strict mode functions should go in 9.3 Built-in Function Objects instead? Disallowing "arguments" and "caller" own properties and forbidding leaking of non-sloppy function state seem like fine 16.2 Forbidden Extensions additions. @evilpie thoughts? Want to write a (needs-consensus) PR?

Member

bterlson commented Dec 23, 2016

@evilpie sure, if it's web compatible I'm fine moving "caller" and "arguments" to proto getters.

I understand the desire to not specify actual machinery in Annex B and the biggest advantage of putting this text in Forbidden Extensions is that an implementation might one day remove these properties entirely.

I like @claudepache's 3 updates, but I wonder if the specification that built-in functions implemented in ECMAScript be strict mode functions should go in 9.3 Built-in Function Objects instead? Disallowing "arguments" and "caller" own properties and forbidding leaking of non-sloppy function state seem like fine 16.2 Forbidden Extensions additions. @evilpie thoughts? Want to write a (needs-consensus) PR?

@chicoxyzzy

This comment has been minimized.

Show comment
Hide comment
@chicoxyzzy

chicoxyzzy Jan 2, 2017

Contributor

was arguments.caller removed only in Firefox Nightlies or it was removed from SpiderMonkey completely?

Contributor

chicoxyzzy commented Jan 2, 2017

was arguments.caller removed only in Firefox Nightlies or it was removed from SpiderMonkey completely?

@evilpie

This comment has been minimized.

Show comment
Hide comment
@evilpie

evilpie Jan 2, 2017

Contributor

@chicoxyzzy We completely removed arguments.caller in bug 1324208 and unless we see regressions in the wild (unlikely because it threw before), we will ship Firefox 53 (current Nightly) on 2017-04-18.

@bterlson Sorry, seems like I ignored your request. I will take a shot at documenting this.

Contributor

evilpie commented Jan 2, 2017

@chicoxyzzy We completely removed arguments.caller in bug 1324208 and unless we see regressions in the wild (unlikely because it threw before), we will ship Firefox 53 (current Nightly) on 2017-04-18.

@bterlson Sorry, seems like I ignored your request. I will take a shot at documenting this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment