- From: Maciej Stachowiak <mjs@apple.com>
- Date: Thu, 15 Oct 2009 07:23:25 -0700
- To: Jim Blandy <jimb@mozilla.com>
- Cc: es-discuss Steen <es-discuss@mozilla.org>, "Mark S. Miller" <erights@google.com>, public-script-coord@w3.org, Simon Pieters <simonp@opera.com>
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