ES3.1-strict spec bug & proposed fix: method calls on primitives.

Brendan Eich brendan at
Thu Jan 8 21:14:15 PST 2009

HI Mark,

The fix of allowing primitive Reference base type is ok for  
implementations such as SpiderMonkey, which already does this for the  
built-in methods on String.prototype, Number.prototype, and  
Boolean.prototype to avoid the overhead of wrappers when calling  
native methods on string, number, and boolean primitive values.

Practical implementations do not use anything like a Reference type,  
instead specializing lvalue and rvalue contexts to preserve reference  
bases in VM registers.

FWIW, this is what ES4 did too. Types string and String shared a  
prototype object; calls on a primitive |this| to methods inherited  
from the shared prototype entailed no coercion or wrapping. ES4 did  
not use Reference internal types with non-primitive bases to specify  
this binding. So the fix is harmonious from the start.

  On Jan 8, 2009, at 7:43 PM, Mark S. Miller wrote:

> We'd change the spec of GetValue and delete to do a ToObject() on  
> its base in order to look up the property. However, this coercion of  
> the base is ephemeral. The wrapper cannot escape, and ToObject has  
> no observable side effects, so it is just an explanatory device  
> equivalent to saying: "If the base is a string, we lookup the  
> property on String.prototype. If it's a number, ..."

In the face of multiple global objects, the spec must say *which*  
original value of String, e.g., is used, or really, how the right  
global object is found. It should be found by following the scope  
chain to the last object, the global for the currently active code  
(not the same as the global for the caller or grand caller or top  
level script that's being evaluated).

Web browser embedding example:

* Window A script source:

String.prototype.trim = function () { return this.replace(/^\s*(\S*)\s* 
$/, "$1"); }

function wraptrim(s) { return s.trim(); }

* Window B script source (assume window A is denoted by winA here):

alert(winA.wraptrim(" hello   "));

> Ok, what it there is no inherited accessor? In ES3.1-nonstrict, this  
> should remain a no op. However, there are two equivalent means of  
> explaining this no op behavior: 1) mutating an unobservable wrapper,  
> or 2) a failed assignment. Recall that nonstrict failed assignments  
> are silent, so either explanation suffices. However, if we use  
> explanation #2, we give ourselves license to have this fail with a  
> thrown error in strict code.

A strict error for attempting to set an ad-hoc property could be  
helpful. All else equal, when using primitives and objects  
interchangeably in generic code, the silent case is less likely to be  
helpful in my experience.

Generic programming with primtiives more often wants to *read*  
undefined when getting a non-existent property, and "object detect"  
without having to try/catch. This common use case is not served by  
strict mode in ES3.1, right? You'd have to change code to try/catch  
around such probes.


More information about the Es-discuss mailing list