[Harmony Proxies] Default trap implementations should be non-normative

# Sean Eagan (13 years ago)

Problem:

There have been two main justifications for having the default trap implementations:

  • To prove ES implementability
  • To provide a starting point to slightly tweak a trap's behavior.

However, all that is required to achieve these goals is that default trap implementations exist, not that they be normatively specified within the spec. We have already seen a number of questions around the consistency of default trap implementations with the internal method pseudocode e.g. [1], [2]. It is likely that such issues will keep popping up even after ES.next is finalized. If default trap implementations are included within the spec, any such issues found will remain unfixed until the next spec. Also, proxies have entirely separate internal methods than the standard implementations, which creates two problems:

  1. proxies do not inherit alternate internal method implementations.

For example, functions have an alternate [[Get]] implementation (15.3.5.4), and this is not reflected in the default "get" behavior of function proxies. Also, array proxies could be useful as well, and a similar issue would be encountered there since they override the [[DefineOwnProperty]] internal method.

  1. duplication of internal method behavior in the spec

The only thing that should be different about the internal method behavior of proxies is that the proxy's traps should be used when they exist, instead of the default behavior. So why should we duplicate the logic that is the same for both proxies and non-proxies by having separate internal method implementations which subsequently call default trap implementations which are just intended to match the behavior of the standard implementations. This duplication causes unnecessary burden in maintaining the spec, introduces inconsistencies, and most importantly makes the spec less clear since the reader needs to determine which proxy semantics are just duplicating the standard semantics, and which are different.

Solution:

Encapsulate the trapping behavior in an internal [[Trap]] method:

[[Trap]] (Name, Arguments, Throw)

When the [[Trap]] internal method of O is called with trap name Name, arguments list Arguments, and Boolean flag Throw, the following steps are taken:

  1. If O does not have a [[Handler]] internal property return [false].
  2. Let handler be the value of the [[Handler]] internal property of O.
  3. Let trap be the result of calling the [[Get]] internal method of handler with argument Name.
  4. If trap is undefined, return a List whose first element is false.
  5. If IsCallable(trap) is false, then a. If Throw is true throw a TypeError exception. b. return a List whose first element is false.
  6. Return trap.
  7. Return a List whose first element is true, and whose second element is the result of calling the [[Call]] internal method of trap providing handler as the this value and Arguments appended with O as the arguments.

Then update the proxy semantics [3] such that proxies just use the standard internal method implementations which are updated to call [[Trap]]. For example, the standard [[Get]] implementation ( 8.12.3 ) which corresponds to the optional "get" trap, could be updated to support proxies by merely prepending these two steps:

  1. Let hasTrap be the first element and trapReturn be the second element of the result of calling the [[Trap]] internal method of O with arguments "get", a List with only element P, and false.
  2. If hasTrap is true, return trapReturn.

The function [[Get]] implementation (15.3.5.4) can prepend the same steps only replacing O with F. Generally, it will be slightly more complicated than prepending steps, but not by much. For example, the standard [[Delete]] implementation ( 8.12.7 ) which corresponds to the required "delete" trap, could be updated to the following:

[[Delete]] (P, Throw)

When the [[Delete]] internal method of a trapping proxy O is called with property name P and the Boolean flag Throw the following steps are taken:

  1. Let hasTrap be the first element and success be the second element of the result of calling the [[Trap]] internal method of O with arguments "delete", a List with only element P, and true.
  2. If hasTrap is true a. Let success be ToBoolean(success)
  3. Else a. Let desc be the result of calling the [[GetOwnProperty]] internal method of O with property name P. b. If desc is undefined, then return true. c. Let success be the value of desc.[[Configurable]]. d. If success is true, remove the own property with name P from O.
  4. If success is false, then a. If Throw, throw a TypeError exception b. Return false.
  5. Return true.

[1] strawman:proxy_set_trap [2] esdiscuss/2011-May/014717 [3] harmony:proxies_semantics

Thanks, Sean Eagan

# Sean Eagan (13 years ago)

On Tue, May 31, 2011 at 12:04 PM, Sean Eagan <seaneagan1 at gmail.com> wrote:

  1. If O does not have a [[Handler]] internal property return [false].
  2. Let handler be the value of the [[Handler]] internal property of O.
  3. Let trap be the result of calling the [[Get]] internal method of handler with argument Name.
  4. If trap is undefined, return a List whose first element is false.
  5. If IsCallable(trap) is false, then    a.  If Throw is true throw a TypeError exception.    b.  return a List whose first element is false.
  6. Return trap.
  7. Return a List whose first element is true, and whose second element is the result of calling the [[Call]] internal method of trap providing handler as the this value and Arguments appended with O as the arguments.

Sorry, ignore step 7, forgot to remove it.

Thanks, Sean Eagan

# Sean Eagan (13 years ago)

Noticed a few other typos in the [[Trap]] steps, here's a better version:

  1. Let noTrapReturn be a List with the elements false and undefined.
  2. If O does not have a [[Handler]] internal property return noTrapReturn.
  3. Let handler be the value of the [[Handler]] internal property of O.
  4. Let trap be the result of calling the [[Get]] internal method of handler with argument Name.
  5. If trap is undefined, return noTrapReturn.
  6. If IsCallable(trap) is false, then a. If Throw is true throw a TypeError exception. b. return noTrapReturn.
  7. Return a List whose first element is true, and whose second element is the result of calling the [[Call]] internal method of trap providing handler as the this value and Arguments appended with O as the arguments.

On Tue, May 31, 2011 at 12:08 PM, Sean Eagan <seaneagan1 at gmail.com> wrote:

On Tue, May 31, 2011 at 12:04 PM, Sean Eagan <seaneagan1 at gmail.com> wrote:

  1. If O does not have a [[Handler]] internal property return [false].
  2. Let handler be the value of the [[Handler]] internal property of O.
  3. Let trap be the result of calling the [[Get]] internal method of handler with argument Name.
  4. If trap is undefined, return a List whose first element is false.
  5. If IsCallable(trap) is false, then    a.  If Throw is true throw a TypeError exception.    b.  return a List whose first element is false.
  6. Return trap.
  7. Return a List whose first element is true, and whose second element is the result of calling the [[Call]] internal method of trap providing handler as the this value and Arguments appended with O as the arguments.

Sorry, ignore step 7, forgot to remove it.

Thanks, Sean Eagan

Thanks, Sean Eagan

# Tom Van Cutsem (13 years ago)

I agree that when it comes time to write the ES.next spec language, we should refactor the existing built-in methods such that it's not actually necessary to duplicate their implementation for specifying the default behavior of the fundamental Proxy traps.

The current proxies:semantics page indeed falls back on default behavior as specified in ECMAScript itself. At the very least, in the actual spec, we should probably refer to the built-in implementation rather than to the "meta-circular" behavior. Your [[Trap]] proposal is another good suggestion to keep in mind.

Whatever refactoring we apply, we should make absolutely sure that the built-in normative behavior of non-proxy values remains unchanged.

Cheers, Tom

2011/5/31 Sean Eagan <seaneagan1 at gmail.com>