Re: Strategies for standardizing mistakes

On Oct 14, 2009, at 5:04 PM, Jim Blandy wrote:

> One could characterize the difference by saying that Mozilla has  
> "reluctant properties" whereas WebKit has "reluctant values".  :)
>
> In other words, in WebKit, 'document.all' has a value --- a value  
> that can be assigned to other variables, stored in data structures,  
> and so on without changing its behavior --- but which is hard to get  
> a grip on.

I'm not sure "reluctant value" is a good way to summarize the  
behavior. The term we use is "object that masquerades as undefined".  
The value returned by document.all has a small set of behaviors that  
are exhibited by the undefined value, but are not allowed by the spec  
to values of object type.

> Whereas, in Mozilla, 'document' sort-of-has and sort-of-doesn't-have  
> a property named 'all', depending on how you look at it.
>
> It could just be organizational bias, but reluctant properties  
> strike me as the more bounded form of insanity.

Before you conclude that, let's consider the impact of host object  
properties that return different values based on their syntactic  
context. From the informal operational semantics given form ECMAScript  
syntactic constructs, one might conclude that certain source-to-source  
program transforms are sound, in the sense that they cannot possibly  
alter the behavior of the program. Consider these two functions. I  
will use %EXPR% as a metasyntactic variable, indicating any valid  
JavaScript expression that can appear as the right-hand side of an  
assignment:

function testFunc() {
     var tmp;
     func nested(e) { tmp = e; }
     nested(%EXPR%);
     return tmp;
}

function testFunc() {
     var tmp = %EXPR%;
     return tmp;
}

You might think this is a valid transformation no matter what %EXPR%  
is, even if it involves hot objects. But this transformation is  
unsound in Mozilla, for example if %EXPR% is document.all. In that  
case, the first function will return the all collection and the second  
will return undefined. Now, you might think this is kind of obscure,  
and not a practically important transform. Sure, source-to-source  
transformations could in theory factor out unneeded closures, but  
would they really. However, consider this pair of functions:

function testFunc() {
     var tmp = %EXPR%;
     return tmp;
}

function testFunc() {
     return %EXPR%;
}

You might think these are surely equivalent in all respects.  
Converting in either direction seems like a really basic transform,  
something that many ECMAScript program rewriters are likely to do. But  
again, converting in either direction would be unsound for Mozlla, if  
%EXPR% is document.all. The first function would return undefined, the  
second all collection.

Going beyond what Mozilla does, let's say host object property or  
method access can vary in arbitrary ways based on syntactic context.  
Then *no* source-to-source transform is sound, not even changing  
whitespace or stripping comments, unless you know the behavior of all  
host objects the code will deal with.

It's true that an object which ToBoolean() converts as false violates  
some assumptions, as does an object that compares == to undefined or  
null. There are some otherwise valid identities for a value known to  
be an object that this would break (though that knowledge would have  
to come in some way other than 'typeof'). But I would claim this is a  
more bounded form of insanity than the idea of expressions that have  
different values depending on their surrounding source context. The  
former requires just a limited number of additional permitted host  
object exceptions. Tools that understand JavaScript object semantics  
would just need a finite set of changes to consider the possibility of  
objects that masquerade as undefined. The latter, if truly allowed by  
the spec, makes source-to-source transformers, even something as  
simple as a pretty-printer, potentially unsound. That seems like a  
much less bounded form of insanity.

(It's been raised that debugging APIs may have behavior that depends  
on the calling context. That may be true, but exposing debugging APIs  
directly to normal code would violate important assumptions. For  
example, the spec was tweaked to prevent exposing strict callers to  
their non-strict callees, but it's commonplace for debugging APIs to  
expose the full call stack. So I don't think the existence of  
debugging APIs is a good argument that calling-context-sensitive host  
objects are permissible in general. Also, looking at calling context  
for the sake of performance optimizations does not create these kinds  
of problems if observable behavior remains the same, so it's not a  
helpful analogy.)

In any case, both the Mozilla and WebKit solutions for undetectable  
document.all were pragmatic approaches to a problem with no perfect  
solutions. Each has its downsides. I just wanted to explain some of  
the less obvious consequences of Mozilla's approach, and of this kind  
of mechanism in general.

Regards,
Maciej

Received on Thursday, 15 October 2009 14:24:00 UTC