A Proposal For Formalizing __noSuchMethod__ in ECMAScript 3.1.
Introduction & History:
Many modern dynamic languages use a generic mechanism for delegation
of methods which are not understood by the target object. These
include Smalltalk ("doesNotUnderstand"), Objective-C ("forward::") and
Ruby ("method_missing").
At Technical Pursuit, Inc. we found this mechanism to be extremely
useful and in 2003 requested that this functionality be added to the
SpiderMonkey open source JavaScript engine. It was implemented that
year, as detailed here:
https://bugzilla.mozilla.org/show_bug.cgi?id=196097
(many thanks again to Brendan Eich)
This is currently a SpiderMonkey-only extension and we are proposing
that it be incorporated into ECMAScript 3.1.
Rationale:
Dynamic delegation is an extremely powerful tool in designing
lightweight applications whose power comes from avoiding over-
specification of getters, setters, and similar boilerplate. The
addition of getters and setters has helped keep boilerplate to a
minimum, but delegation through noSuchMethod can provide even more
capability.
As an example, Ruby on Rails uses this mechanism extensively to
provide powerful object querying when no such actual methods exist on
the target object:
smithEmps = Employee.find_by_last_name('Smith')
where 'find_by_last_name' isn't a real method on the Employee type,
but is dynamically redispatched after triggering the 'method_missing'
method.
Current mechanism:
As it is currently defined in Spidermonkey, the 'noSuchMethod'
functionality is implemented using the syntax for 'Mozilla-specific
extensions' - that is, as '__noSuchMethod__'.
It is a property on Object.prototype that can be reimplemented
'further down' the prototype chain. Here is a 'trivial' implementation
that pops an alert() when any object in the system sends a message to
an object that the target does not understand:
Object.prototype.__noSuchMethod__ =
function (methodName, sentArgs)
{
var targetObj;
alert('No such method:\n' +
'Target Object: ' + this + '\n' +
'Target Object Constructor: ' + this.constructor + '\n' +
'Method Name: ' + methodName + '\n' +
'Args: ' + sentArgs.join(', ') + '\n' +
'Call context (arguments object) callee: ' + arguments.callee +
'\n' +
'Call context (arguments object) callee\'s caller: ' +
arguments.callee.caller);
};
There are several things to note here which are of importance for this
mechanism to be truly useful:
1. The methodName is supplied to this method. This is required to
determine the actual method name of the method being dispatched.
2. The 'sent arguments' are supplied to this method. This is important
so that redispatching can occur, since obtaining the actual argument
object(s) to a method was removed from ECMAScript long ago (i.e.
'arguments.caller' - not to be confused with 'arguments.callee.caller'
- was removed in ECMAScript E3).
3. The actual 'arguments' object to this backstop hook are supplied
and are accessible. This is important to be able to produce debugging
backtraces, if necessary, since 'arguments.callee.caller' is still
with us.
Reference Implementation:
In the ECMA committee, there has been strong desire for new language
features to have a 'reference implementation' for testing and
validation purposes. This language feature already has a reference
implementation in a very widely used JavaScript engine.
Conclusion:
We respectfully submit this codification of a very powerful, already
existing and long-tested language feature to the ECMAScript committee
for inclusion in ECMAScript 3.1. We would also volunteer for any work
that needs to be done to codify this in the specification if that
would assist in making this a reality.
Cheers,
- Bill Edney and Scott Shattuck
- Technical Pursuit, Inc.